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