lilalo

annotate lm @ 25:ba4d6515b8fd

Выполнен шаг (3) в плане (N05) по построению распределённой системы lilalo.


Агент l3-агент в реальном времени анализирует скрипты в указанном ему
каталоге и по мере обнаружения новых завершённых команд записывает их
в кэш-файл.

Данные о том, докуда разобран каждый скрипт-файл сохраняются во временном
файле, для того чтобы при перезапуске агента он мог продолжить разбор
с того места, где он был остановлен в прошлый раз, а не копировал
данные в кэш-файл повторно.

Агент запускается для каждого пользователя системы.
Если агент обнаружил свою копию работающую от имени того же пользователя,
он автоматически завершается.
Поиск копии агента выполняется так:
просматривается pid-файл агента - если его нет, считается, что и агент не запущен
(Внимание! Не удаляйте pid-файл!! Работа нескольких агентов от имени одного
пользователя может быть некорректной!)
Если он есть, выполняется проверка, действительно ли процесс с таким идентификатором
это l3-агент текущего пользователя. Если нет, pid-файл удаляется, и агент запускается.

Нормальное завершение агента, работающего в режиме демона, выполняется
с помощью сигнала TERM. При завершении агент автоматически стирает свой pid-файл.



Добавлены атрибуты команды, хранящие информацию о участке бинарного файла скрипта,
соответствующей команды:
raw_start - начало блока команды
raw_output_start - начало вывода команды
raw_end - окончание вывода
raw_file - имя бинарного файла

Файлы:
(могут меняться с помощью конфигурационных параметров)

~/.labmaker/.cache.dat
~/.labmaker/cache.xml
~/.labmaker/l3-agent.pid

Конфигурационные параметры:
cache_stat Имя файла с информацией о текущей позиции разбора
в каждом файле

mode Режим, в котором работает агент.
Допустимые значения:
daemon - в режиме непрерывного опроса каталога
Программа не завершается после окончания анализа,
а ждёт появления новых данных
normal - однократный анализ каталога.
Программа завершается после окончания анализа данных

daemon_sleep_interval Интервал через который агент просматривает каталог скриптов
в поисках новых данных

detach Нужно ли выполнять отключение от терминала при работе в режиме демона?
(строго говоря, если процесс не отключился от терминала,
то и в режиме демона он работать не может. Здесь имеется в виду
режим непрерывного опроса каталога)

agent_pidfile Путь к файлу, который будет хранить идентификатор процесса агента.

l3-agent Имя, под которым будет известен процесс l3-agent

Незначительные исправления:
* убрана отладочная информация о new_commands и new_files из frontend'а
author devi
date Thu Nov 03 17:49:56 2005 +0200 (2005-11-03)
parents 05d496f33d76
children 4d252e7dd478
rev   line source
devi@0 1 #!/usr/bin/perl
devi@0 2
devi@0 3
devi@0 4 use strict;
devi@0 5 use Inline::Files;
devi@0 6 use Data::Dumper;
devi@0 7 use Switch;
devi@0 8 use XML::Simple;
devi@0 9 use Getopt::Long;
devi@4 10 use utf8;
devi@0 11
devi@0 12 our $XMLClass;
devi@0 13 our $XMLCourse;
devi@0 14 our @Labs;
devi@0 15
devi@0 16 our %Machines; # Machines list from class.xml
devi@0 17 our @SelectedMachines; # Machines list given as the command line argument
devi@0 18
devi@0 19 our $Config_File = "labmaker.conf";
devi@0 20 our %Config = (
devi@0 21 "show_host" => "no",
devi@0 22
devi@3 23 # Вспомогательные программы
devi@23 24 #"l3-report" => "./lm-report",
devi@23 25 "l3-report" => "./l3-report",
devi@3 26
devi@0 27 # Каталоги
devi@13 28 "path_lilalo" => "/var/lilalo/",
devi@10 29 "path_classes" => "/var/lilalo/classes/",
devi@10 30 "path_lablogs" => "/var/lilalo/lablogs/",
devi@10 31 "courses_path" => "/var/lilalo/courses/",
devi@10 32 "outpath" => "/var/lilalo/out/",
devi@3 33 "path_web" => "/var/www/l3", # Путь к web-отчётам
devi@3 34 "path_share" => "./share/", # Путь к web-отчётам
devi@0 35
devi@0 36 # Файлы
devi@0 37 "runfile" => "lm.run",
devi@0 38 "logfile" => "lm.log",
devi@0 39
devi@0 40 "class" => "class", # Имя файла класса
devi@0 41 "class_suffix" => ".xml", # Cуффикс файла класса
devi@5 42 "classfile" => "",
devi@0 43
devi@0 44 "sshkey" => "$ENV{HOME}/.ssh/id_dsa.pub",
devi@0 45 "lmssh" => "./lm-ssh",
devi@0 46 "lminstall" => "./lm-install",
devi@17 47 "ssh_user" => "root",
devi@0 48 );
devi@0 49
devi@0 50 our %Run = (
devi@0 51 "lab" => ""
devi@0 52 );
devi@0 53
devi@0 54 our %Scripts;
devi@0 55
devi@0 56 sub load_class;
devi@0 57 sub load_config;
devi@0 58 sub load_course;
devi@0 59 sub load_scripts;
devi@0 60
devi@0 61 sub lm_next;
devi@0 62 sub lm_prev;
devi@0 63 sub lm_start;
devi@0 64 sub lm_stop;
devi@0 65 sub lm_set;
devi@0 66 sub lm_do;
devi@0 67 sub lm_report;
devi@0 68 sub lm_show_hosts;
devi@0 69 sub lm_show_labs;
devi@0 70
devi@0 71 sub load_run;
devi@0 72 sub save_run;
devi@0 73 sub print_log;
devi@0 74 sub print_usage_info;
devi@0 75 sub main();
devi@0 76
devi@0 77 main();
devi@0 78
devi@0 79 sub main()
devi@0 80 {
devi@0 81 binmode STDOUT, ":utf8";
devi@0 82
devi@0 83 if (! @ARGV) {
devi@0 84 print_usage_info();
devi@0 85 exit(0);
devi@0 86 }
devi@0 87
devi@0 88 load_config;
devi@0 89 load_run;
devi@0 90 load_scripts;
devi@0 91 load_class;
devi@0 92 load_course;
devi@0 93
devi@0 94 my $arg = join " ", @ARGV;
devi@0 95
devi@0 96 # Getting @SelectedMachines if any
devi@0 97 if ($arg =~ s/@(.*?)\s//) {
devi@0 98 my $machines = $1;
devi@0 99 my @list = split /,/, $machines;
devi@0 100 for my $interval (@list) {
devi@0 101 my ($first, $last) = split /-/, $interval;
devi@0 102
devi@0 103 push @SelectedMachines, $first;
devi@0 104 while ($first < $last) {
devi@0 105 push @SelectedMachines, ++$first;
devi@0 106 }
devi@0 107 }
devi@0 108 }
devi@0 109
devi@0 110 # Choose command to do
devi@0 111 switch ($arg) {
devi@0 112 case "next" { lm_next }
devi@0 113 case "prev" { lm_prev }
devi@0 114 case /set / { $arg =~ /set (.*)/; lm_set $1 }
devi@0 115 case "report" { lm_report }
devi@0 116 case "start" { lm_start }
devi@0 117 case "stop" { lm_stop }
devi@0 118 case "show hosts" { lm_show_hosts }
devi@0 119 case "show labs" { lm_show_labs }
devi@0 120 case /do / { $arg =~ /do (.*)/; lm_do "$1" }
devi@0 121 else { print_usage_info() }
devi@0 122 }
devi@0 123 save_run;
devi@0 124 exit(0);
devi@0 125 }
devi@0 126
devi@0 127 sub load_scripts
devi@0 128 {
devi@0 129 local $/;
devi@0 130 $_=<SCRIPTS>;
devi@0 131 %Scripts = ("empty-element", split (/###(.*)\n/));
devi@0 132 delete($Scripts{"empty-element"});
devi@0 133 }
devi@0 134
devi@0 135 sub load_config
devi@0 136 {
devi@0 137 my %file_config;
devi@0 138 my %argv_config;
devi@0 139 #read_config_file(\%file_config, $Config_File);
devi@0 140 GetOptions(\%argv_config, map "$_=s", keys %Config);
devi@0 141 %Config = (%Config, %file_config, %argv_config);
devi@0 142 }
devi@0 143
devi@0 144 sub load_course
devi@0 145 {
devi@0 146 $XMLCourse = XMLin($Config{"courses_path"}.$XMLClass->{"course"}.".xml", ForceArray => 1 )
devi@0 147 or die "Can't open file of the course ",$XMLClass->{"course"}," [with .xml extension]\n";
devi@0 148 # print Dumper($XMLCourse);
devi@0 149 for my $lab (@{$XMLCourse->{"module"}}) {
devi@0 150 push @Labs, $lab->{"code"};
devi@0 151 }
devi@0 152 }
devi@0 153
devi@0 154 sub load_class
devi@0 155 {
devi@0 156 my $classfile =
devi@5 157 $Config{"classfile"} ||
devi@0 158 $Config{"path_classes"}."/".$Config{"class"}.$Config{"class_suffix"};
devi@0 159 $XMLClass = XMLin($classfile , ForceArray => [ 'student' ] )
devi@0 160 or die "Can't open file of the class ",$classfile,"\n";
devi@0 161
devi@0 162 for my $student (@{$XMLClass->{"student"}}) {
devi@0 163 $Machines{$student->{"host"}} = {
devi@0 164 "name" => "$student->{firstname} $student->{surname}",
devi@0 165 "user" => "$student->{user}",
devi@0 166 "student" => $student,
devi@0 167 }
devi@0 168 }
devi@0 169 # print Dumper($XMLClass);
devi@0 170 # print Dumper(\%Machines);
devi@0 171 }
devi@0 172
devi@0 173
devi@0 174 sub lm_next
devi@0 175 {
devi@0 176 for(my $i=0; $i<=$#Labs; $i++){
devi@0 177 if ( $Labs[$i] eq $Run{"lab"} ) {
devi@0 178 if ($i < $#Labs) {
devi@0 179 lm_set($Labs[$i+1]);
devi@0 180 return ;
devi@0 181 } else {
devi@0 182 die "Lab ", $Run{"lab"}, " is the last. Switch to next lab is impossible"
devi@0 183 }
devi@0 184 }
devi@0 185
devi@0 186 }
devi@0 187 die "Lab ", $Run{"lab"}, " not found. Don't know which is next"
devi@0 188 }
devi@0 189
devi@0 190 sub lm_prev
devi@0 191 # Switch to previous lab
devi@0 192 {
devi@0 193 for(my $i=0; $i<=$#Labs; $i++){
devi@0 194 if ( $Labs[$i] eq $Run{"lab"} ) {
devi@0 195 if ($i > 0) {
devi@0 196 lm_set($Labs[$i-1]);
devi@0 197 return ;
devi@0 198 } else {
devi@0 199 die "Lab ", $Run{"lab"}, " is the first. Switch to previous lab is impossible"
devi@0 200 }
devi@0 201 }
devi@0 202
devi@0 203 }
devi@0 204 die "Lab ", $Run{"lab"}, " not found. Don't know which is previous"
devi@0 205 }
devi@0 206
devi@0 207 sub lm_set
devi@0 208 # Switch to $_[0] lab
devi@0 209 # FIXME
devi@0 210 {
devi@0 211 my $lab = shift;
devi@0 212 print "Current lab is $lab\n";
devi@0 213 $Run{"lab"} = "$lab";
devi@0 214 lm_do "setlab", $lab;
devi@0 215 }
devi@0 216
devi@0 217
devi@0 218 sub lm_start
devi@0 219 # Start new training day
devi@0 220 {
devi@0 221 print_log(`date`." STARTED\n");
devi@0 222 if ($Run{"lab"}) {
devi@0 223 lm_next;
devi@0 224 }
devi@0 225 else
devi@0 226 {
devi@0 227 # First lab in the course
devi@0 228 lm_set($Labs[0]);
devi@0 229 }
devi@0 230 }
devi@0 231
devi@0 232 sub lm_stop
devi@0 233 # Stop this training day
devi@0 234 {
devi@0 235 print_log(`date`." STOPPED\n");
devi@0 236 }
devi@0 237
devi@0 238
devi@0 239 sub lm_show_hosts
devi@0 240 # Show hosts used to run a commands
devi@0 241 {
devi@0 242 my $i=1;
devi@0 243 for my $m (sort keys %Machines) {
devi@0 244 if (!@SelectedMachines || grep /^$i$/, @SelectedMachines) {
devi@0 245 print "($i)","\t",$m,"\t",$Machines{$m}->{"name"},"\n";
devi@0 246 }
devi@0 247 $i++;
devi@0 248 }
devi@0 249 }
devi@0 250
devi@0 251 sub lm_show_labs
devi@0 252 # Show hosts used to run a commands
devi@0 253 {
devi@0 254 my $i=1;
devi@0 255 for my $lab (@Labs) {
devi@0 256 print $lab;
devi@0 257 print "*" if $lab eq $Run{"lab"};
devi@0 258 print "\n";
devi@0 259 }
devi@0 260 }
devi@0 261
devi@0 262 sub lm_do
devi@0 263 # Do the $_[0] command on all of the hosts
devi@0 264 {
devi@0 265 my $command = shift;
devi@0 266 my $arg = join " ", @_;
devi@0 267 my $i=1;
devi@0 268 for my $m (sort keys %Machines) {
devi@0 269 if (!@SelectedMachines || grep $_ eq $i, @SelectedMachines) {
devi@0 270 print "$m:\n" if $Config{"show_host"} =~ /y/i;
devi@0 271
devi@0 272 my %myenv = ( %Config,
devi@0 273 host => $m,
devi@0 274 dirs => "/root /home/".$Machines{$m}->{"user"},
devi@3 275 lablogs => $Config{"path_lablogs"}."/".
devi@0 276 $XMLClass->{"course"}."/".
devi@0 277 $XMLClass->{"date"}."/".
devi@0 278 "$m",
devi@0 279 lab => $arg,
devi@0 280
devi@0 281 email => $Machines{$m}->{"student"}->{"email"},
devi@0 282 company => $Machines{$m}->{"student"}->{"company"},
devi@0 283 center => $XMLClass->{"center"},
devi@0 284 course => $XMLClass->{"course"},
devi@0 285 date => $XMLClass->{"date"},
devi@0 286 name => $Machines{$m}->{"name"},
devi@0 287 coursepath => $XMLCourse->{"path"},
devi@0 288
devi@0 289 );
devi@0 290 if (grep { $_ eq $command} keys %Scripts) {
devi@0 291 $_=$Scripts{"$command"};
devi@0 292 s/\$(\w+)/$myenv{$1}/ge;
devi@0 293 open(SHELL, "|/bin/sh -s");
devi@0 294 binmode SHELL, ":utf8";
devi@0 295 print SHELL $_;
devi@0 296 close (SHELL);
devi@0 297 }
devi@0 298 else {
devi@0 299 my $res = `ssh $Config{"ssh_user"}\@$m $command`;
devi@0 300 if ($res) {
devi@0 301 my $count = ($res =~ s/(^)/$m: /mg);
devi@0 302 print $res;
devi@0 303 print "\n" if ($count > 1);
devi@0 304 }
devi@0 305 }
devi@0 306 }
devi@0 307 $i++;
devi@0 308 }
devi@0 309 }
devi@0 310
devi@0 311
devi@3 312
devi@3 313 =cut comment
devi@3 314
devi@3 315 lm report
devi@3 316
devi@3 317 Построить html представление для журналов текущего класса.
devi@3 318 Для построения используется скрипт l3-report.
devi@3 319
devi@3 320 =cut
devi@3 321
devi@0 322 sub lm_report
devi@0 323 {
devi@0 324
devi@3 325 my $webdir = $Config{"path_web"};
devi@3 326 my $course=$XMLClass->{"course"};
devi@3 327 my $date=$XMLClass->{"date"};
devi@3 328 my $encoding=$XMLClass->{"charset"};
devi@0 329
devi@4 330 my $center = $XMLClass->{"center"};
devi@4 331 my $instructor = $XMLClass->{"instructor"}->{"firstname"}." ".$XMLClass->{"instructor"}->{"surname"};
devi@4 332 my $course_name = $XMLCourse->{"fullname"}[0];
devi@4 333
devi@5 334
devi@5 335 # Собственно журналы
devi@5 336
devi@5 337 for my $student (@{$XMLClass->{"student"}}) {
devi@5 338 my $user = $student->{"user"};
devi@5 339 my $hostname = $student->{"host"};
devi@6 340 my $encoding = $student->{"charset"};
devi@6 341 my $student_name = $student->{"firstname"}." ".$student->{"surname"};
devi@5 342
devi@5 343 system("mkdir -p $webdir/$date/$hostname");
devi@5 344 system("cp ".$Config{"path_share"}."/*.{ico,css} $webdir/$date/$hostname");
devi@5 345 system($Config{"l3-report"}.
devi@5 346 " --input ".$Config{"path_lablogs"}."/$course/$date/$hostname/$user".
devi@5 347 " --diffs ".$Config{"path_lablogs"}."/$course/$date/$hostname/$user ".
devi@5 348 $Config{"path_lablogs"}."/$course/$date/$hostname/root".
devi@5 349 " --output $webdir/$date/$hostname/$user.html".
devi@6 350 " --course-name '$course_name'".
devi@6 351 " --course-code '$course'".
devi@6 352 " --course-date '$date'".
devi@6 353 " --course-center '$center'".
devi@6 354 " --course-student '$student_name'".
devi@6 355 " --course-trainer '$instructor'".
devi@5 356 " --encoding $encoding"
devi@5 357 );
devi@5 358 system($Config{"l3-report"}.
devi@5 359 " --input ".$Config{"path_lablogs"}."/$course/$date/$hostname/root".
devi@5 360 " --diffs ".$Config{"path_lablogs"}."/$course/$date/$hostname/root ".
devi@5 361 " --output $webdir/$date/$hostname/root.html".
devi@6 362 " --course-name '$course_name'".
devi@6 363 " --course-code '$course'".
devi@6 364 " --course-date '$date'".
devi@6 365 " --course-center '$center'".
devi@6 366 " --course-student '$student_name'".
devi@6 367 " --course-trainer '$instructor'".
devi@5 368 " --encoding $encoding"
devi@5 369 );
devi@5 370 }
devi@5 371
devi@4 372 # Индекс для данного класса
devi@4 373
devi@4 374 my $head;
devi@4 375
devi@4 376 $head="Журналы лабораторных работ";
devi@4 377 open(HTML, ">$webdir/$date/index.html")
devi@4 378 or die "Can't open $webdir/$date/index.html for writing";
devi@4 379 binmode HTML, ":utf8";
devi@4 380 print HTML <<HEAD;
devi@4 381 <html>
devi@4 382 <head>
devi@4 383 <meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
devi@4 384 <title>$head</title>
devi@4 385 </head>
devi@4 386 <body>
devi@4 387 <h1>$head</h1>
devi@4 388 <p>
devi@4 389 Курс: $course_name ($course)<br/>
devi@4 390 Начало: $date<br/>
devi@4 391 Учебный центр: $center <br/>
devi@4 392 Инструктор: $instructor <br/>
devi@4 393 </p>
devi@4 394 <table>
devi@4 395 HEAD
devi@4 396 for my $student (@{$XMLClass->{"student"}}) {
devi@4 397 my $user = $student->{"user"};
devi@4 398 my $hostname = $student->{"host"};
devi@4 399 print HTML "<tr>\n";
devi@4 400 print HTML "<td>",$student->{"firstname"}," ",$student->{"surname"},"</td>\n";
devi@4 401 print HTML "<td>",$hostname,"</td>\n";
devi@4 402 print HTML "<td><a href=\"$hostname/$user.html\">",$user,"</td>\n";
devi@4 403 print HTML "<td><a href=\"$hostname/root.html\">","root","</td>\n";
devi@4 404 print HTML "</tr>\n";
devi@4 405 }
devi@4 406 print HTML <<TAIL;
devi@4 407 </table>
devi@4 408 </html>
devi@4 409 TAIL
devi@4 410 close (HTML);
devi@4 411
devi@4 412
devi@4 413
devi@0 414 }
devi@0 415
devi@0 416 sub load_run
devi@0 417 {
devi@0 418 my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"};
devi@0 419 open (RUN, $runfile)
devi@0 420 or return;
devi@0 421 while (<RUN>) {
devi@0 422 chomp;
devi@0 423 my ($var, $val) = split /\s+/,$_,2;
devi@0 424 $Run{$var}=$val;
devi@0 425 }
devi@0 426 close (RUN);
devi@0 427 }
devi@0 428
devi@0 429 sub save_run
devi@0 430 {
devi@0 431 my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"};
devi@0 432 open (RN, "$runfile")
devi@0 433 or die "Can't save running state to $runfile";
devi@0 434 for my $var (keys %Run) {
devi@0 435 print RN $var,"\t",$Run{$var},"\n";
devi@0 436 }
devi@0 437 close (RN);
devi@0 438 }
devi@0 439
devi@0 440 sub print_log
devi@0 441 {
devi@0 442 my $logfile = $Config{"path_labmaker"}."/".$Config{"path_logfile"};
devi@0 443 open (LOG, ">>$logfile")
devi@0 444 or die "Can't open logfile $logfile for writing";
devi@0 445 print LOG @_;
devi@0 446 close (LOG);
devi@0 447 }
devi@0 448
devi@0 449
devi@0 450 sub print_usage_info
devi@0 451 {
devi@0 452 print "Usage:\n\n\t$0 [host-list] command\n";
devi@0 453 while (<USAGE>) {
devi@0 454 print $_;
devi@0 455 }
devi@0 456 }
devi@0 457
devi@0 458 __USAGE__
devi@0 459
devi@0 460 Commands:
devi@0 461
devi@0 462 next -- next lab
devi@0 463 prev -- prev lab
devi@0 464 set LAB -- set current lab to LAB
devi@0 465 start -- start this day training
devi@0 466 stop -- stop this day training
devi@0 467 show hosts -- show available hosts in the class
devi@0 468 show labs -- show available labs in the course
devi@0 469 do COMMAND -- do specified command on the hosts of hostlist
devi@0 470 report -- generate XML/HTML reports
devi@0 471
devi@0 472
devi@0 473 do commands:
devi@0 474
devi@0 475 install [PROFILE] -- install profile
devi@0 476
devi@0 477 Host list:
devi@0 478
devi@0 479 @N -- machine N
devi@0 480 @N1-N2 -- all of the machines from N1 to N2
devi@0 481 @N1,N2,N3 -- machine N1, N2 and N3
devi@0 482
devi@0 483 N* is numbers or domain names of the machines.
devi@0 484
devi@0 485 If host list is not specified,
devi@0 486 command is executed on all of the machines
devi@0 487
devi@0 488
devi@0 489
devi@0 490 __SCRIPTS__
devi@0 491 ###install
devi@0 492 cat $sshkey | $lmssh $ssh_user@$host /bin/sh -c '"mkdir -p ~/.ssh; cat >>~/.ssh/authorized_keys; chmod 600 ~/.ssh/authorized_keys"'
devi@0 493
devi@0 494 ###install-lm
devi@0 495 cat $lminstall | ssh $ssh_user@$host /bin/sh -s $dirs
devi@0 496
devi@0 497 ###copy-lablogs
devi@0 498 for i in $dirs
devi@0 499 do
devi@0 500 mkdir -p $lablogs/${i##*/}
devi@0 501 scp -q $ssh_user@$host:${i}/.labmaker/* $lablogs/${i##*/}
devi@0 502 done
devi@0 503
devi@0 504 ###setlab
devi@0 505 for i in $dirs
devi@0 506 do
devi@0 507 echo $lab | ssh $ssh_user@$host "cat > "${i}"/.labmaker/lab"
devi@0 508 done
devi@0 509
devi@0 510 ###makeout
devi@0 511 common=$course-$date
devi@0 512 personal=$course-$date-$email
devi@0 513 mkdir -p $outpath/${common}/{Lablogs,Docs}
devi@0 514 mkdir -p $outpath/${personal}/{Course,Files}
devi@0 515 cd $outpath/${personal}
devi@0 516 ln -s ../${common}/Lablogs .
devi@0 517 ln -s ../${common}/Docs .
devi@0 518 cd ~-
devi@15 519 export UG_PERSONAL=$outpath/${personal}/Course
devi@0 520 export UG_CENTER="$center"
devi@0 521 export UG_COURSE="$course"
devi@0 522 export UG_DATE="$date"
devi@0 523 export UG_STUDENT="$name"
devi@0 524 export UG_COMPANY="$company"
devi@0 525 cd $coursepath; make personal; cd ~-
devi@0 526
devi@0 527 ###watch
devi@0 528 cat taillast.pl | ssh $ssh_user@$host perl - /root/.labmaker