lilalo

view lm @ 133:b9d00dd5596f

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