lilalo

view lm @ 40:5e5878bd1b20

Скрипты lm вынесены в отдельный файл l3config
author devi
date Tue Nov 22 22:47:47 2005 +0200 (2005-11-22)
parents 466bea239d25
children 6452bfe549d1
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_next;
64 sub lm_prev;
65 sub lm_start;
66 sub lm_stop;
67 sub lm_set;
68 sub lm_do;
69 sub lm_report;
70 sub lm_show_hosts;
71 sub lm_show_labs;
73 sub load_run;
74 sub save_run;
75 sub print_log;
76 sub print_usage_info;
77 sub main();
79 main();
81 sub main()
82 {
83 binmode STDOUT, ":utf8";
85 if (! @ARGV) {
86 print_usage_info();
87 exit(0);
88 }
90 init_config();
91 #load_config;
92 load_run;
93 load_scripts;
94 load_class;
95 load_course;
97 my $arg = join " ", @ARGV;
99 # Getting @SelectedMachines if any
100 if ($arg =~ s/@(.*?)\s//) {
101 my $machines = $1;
102 my @list = split /,/, $machines;
103 for my $interval (@list) {
104 my ($first, $last) = split /-/, $interval;
106 push @SelectedMachines, $first;
107 while ($first < $last) {
108 push @SelectedMachines, ++$first;
109 }
110 }
111 }
113 # Choose command to do
114 switch ($arg) {
115 case "next" { lm_next }
116 case "prev" { lm_prev }
117 case /set / { $arg =~ /set (.*)/; lm_set $1 }
118 case "report" { lm_report }
119 case "start" { lm_start }
120 case "stop" { lm_stop }
121 case "show hosts" { lm_show_hosts }
122 case "show labs" { lm_show_labs }
123 case /do / { $arg =~ /do (.*)/; lm_do "$1" }
124 else { print_usage_info() }
125 }
126 save_run;
127 exit(0);
128 }
130 sub load_scripts
131 {
132 print join " ", %Config;
133 print "\n";
135 open (SCRIPTS, "$Config{l3scripts}")
136 or die "Cant open l3scripts file: ".$Config{l3scripts}.": $!\n";
137 binmode SCRIPTS, ":utf8";
138 local $/;
139 $_=<SCRIPTS>;
140 close(SCRIPTS);
142 %Scripts = ("empty-element", split (/###(.*)\n/));
143 delete($Scripts{"empty-element"});
145 }
147 sub load_config
148 {
149 my %file_config;
150 my %argv_config;
151 #read_config_file(\%file_config, $Config_File);
152 GetOptions(\%argv_config, map "$_=s", keys %Config);
153 %Config = (%Config, %file_config, %argv_config);
154 }
156 sub load_course
157 {
158 $XMLCourse = XMLin($Config{"courses_path"}.$XMLClass->{"course"}.".xml", ForceArray => 1 )
159 or die "Can't open file of the course ",$XMLClass->{"course"}," [with .xml extension]\n";
160 # print Dumper($XMLCourse);
161 for my $lab (@{$XMLCourse->{"module"}}) {
162 push @Labs, $lab->{"code"};
163 }
164 }
166 sub load_class
167 {
168 my $classfile =
169 $Config{"classfile"} ||
170 $Config{"path_classes"}."/".$Config{"class"}.$Config{"class_suffix"};
171 $XMLClass = XMLin($classfile , ForceArray => [ 'student' ] )
172 or die "Can't open file of the class ",$classfile,"\n";
174 for my $student (@{$XMLClass->{"student"}}) {
175 $Machines{$student->{"host"}} = {
176 "name" => "$student->{firstname} $student->{surname}",
177 "user" => "$student->{user}",
178 "student" => $student,
179 }
180 }
181 # print Dumper($XMLClass);
182 # print Dumper(\%Machines);
183 }
186 sub lm_next
187 {
188 for(my $i=0; $i<=$#Labs; $i++){
189 if ( $Labs[$i] eq $Run{"lab"} ) {
190 if ($i < $#Labs) {
191 lm_set($Labs[$i+1]);
192 return ;
193 } else {
194 die "Lab ", $Run{"lab"}, " is the last. Switch to next lab is impossible"
195 }
196 }
198 }
199 die "Lab ", $Run{"lab"}, " not found. Don't know which is next"
200 }
202 sub lm_prev
203 # Switch to previous lab
204 {
205 for(my $i=0; $i<=$#Labs; $i++){
206 if ( $Labs[$i] eq $Run{"lab"} ) {
207 if ($i > 0) {
208 lm_set($Labs[$i-1]);
209 return ;
210 } else {
211 die "Lab ", $Run{"lab"}, " is the first. Switch to previous lab is impossible"
212 }
213 }
215 }
216 die "Lab ", $Run{"lab"}, " not found. Don't know which is previous"
217 }
219 sub lm_set
220 # Switch to $_[0] lab
221 # FIXME
222 {
223 my $lab = shift;
224 print "Current lab is $lab\n";
225 $Run{"lab"} = "$lab";
226 lm_do "setlab", $lab;
227 }
230 sub lm_start
231 # Start new training day
232 {
233 print_log(`date`." STARTED\n");
234 if ($Run{"lab"}) {
235 lm_next;
236 }
237 else
238 {
239 # First lab in the course
240 lm_set($Labs[0]);
241 }
242 }
244 sub lm_stop
245 # Stop this training day
246 {
247 print_log(`date`." STOPPED\n");
248 }
251 sub lm_show_hosts
252 # Show hosts used to run a commands
253 {
254 my $i=1;
255 for my $m (sort keys %Machines) {
256 if (!@SelectedMachines || grep /^$i$/, @SelectedMachines) {
257 print "($i)","\t",$m,"\t",$Machines{$m}->{"name"},"\n";
258 }
259 $i++;
260 }
261 }
263 sub lm_show_labs
264 # Show hosts used to run a commands
265 {
266 my $i=1;
267 for my $lab (@Labs) {
268 print $lab;
269 print "*" if $lab eq $Run{"lab"};
270 print "\n";
271 }
272 }
274 sub lm_do
275 # Do the $_[0] command on all of the hosts
276 {
277 my $command = shift;
278 my $arg = join " ", @_;
279 my $i=1;
281 my %myenv = ( %Config,
282 lab => $arg,
283 center => $XMLClass->{"center"},
284 course => $XMLClass->{"course"},
285 date => $XMLClass->{"date"},
286 coursepath => $XMLCourse->{"path"},
287 );
289 if (grep { $_ eq "PRE-$command"} keys %Scripts) {
290 $_=$Scripts{"PRE-$command"};
291 s/\$(\w+)/$myenv{$1}/ge;
292 open(SHELL, "|/bin/sh -s");
293 binmode SHELL, ":utf8";
294 print SHELL $_;
295 close (SHELL);
296 }
299 for my $m (sort keys %Machines) {
300 if (!@SelectedMachines || grep $_ eq $i, @SelectedMachines) {
301 print "$m:\n" if $Config{"show_host"} =~ /y/i;
303 %myenv = ( %myenv,
304 host => $m,
305 dirs => "/root /home/".$Machines{$m}->{"user"},
306 lablogs => $Config{"path_lablogs"}."/".
307 $XMLClass->{"course"}."/".
308 $XMLClass->{"date"}."/".
309 "$m",
310 email => $Machines{$m}->{"student"}->{"email"},
311 company => $Machines{$m}->{"student"}->{"company"},
312 name => $Machines{$m}->{"name"},
313 );
314 if (grep { $_ eq $command} keys %Scripts) {
315 $_=$Scripts{"$command"};
316 s/\$(\w+)/$myenv{$1}/ge;
317 open(SHELL, "|/bin/sh -s");
318 binmode SHELL, ":utf8";
319 print SHELL $_;
320 close (SHELL);
321 }
322 else {
323 my $res = `ssh $Config{"ssh_user"}\@$m $command`;
324 if ($res) {
325 my $count = ($res =~ s/(^)/$m: /mg);
326 print $res;
327 print "\n" if ($count > 1);
328 }
329 }
330 }
331 $i++;
332 }
334 if (grep { $_ eq "POST-$command"} keys %Scripts) {
335 $_=$Scripts{"POST-$command"};
336 s/\$(\w+)/$myenv{$1}/ge;
337 open(SHELL, "|/bin/sh -s");
338 binmode SHELL, ":utf8";
339 print SHELL $_;
340 close (SHELL);
341 }
342 }
346 =cut comment
348 lm report
350 Построить html представление для журналов текущего класса.
351 Для построения используется скрипт l3-report.
353 =cut
355 sub lm_report
356 {
358 my $webdir = $Config{"path_web"};
359 my $course=$XMLClass->{"course"};
360 my $date=$XMLClass->{"date"};
361 my $encoding=$XMLClass->{"charset"};
363 my $center = $XMLClass->{"center"};
364 my $instructor = $XMLClass->{"instructor"}->{"firstname"}." ".$XMLClass->{"instructor"}->{"surname"};
365 my $course_name = $XMLCourse->{"fullname"}[0];
368 # Собственно журналы
370 for my $student (@{$XMLClass->{"student"}}) {
371 my $user = $student->{"user"};
372 my $hostname = $student->{"host"};
373 my $encoding = $student->{"charset"};
374 my $student_name = $student->{"firstname"}." ".$student->{"surname"};
376 system("mkdir -p $webdir/$date/$hostname");
377 system("cp ".$Config{"path_share"}."/*.{ico,css} $webdir/$date/$hostname");
378 system($Config{"l3-report"}.
379 " --input ".$Config{"path_lablogs"}."/$course/$date/$hostname/$user".
380 " --diffs ".$Config{"path_lablogs"}."/$course/$date/$hostname/$user ".
381 $Config{"path_lablogs"}."/$course/$date/$hostname/root".
382 " --output $webdir/$date/$hostname/$user.html".
383 " --course-name '$course_name'".
384 " --course-code '$course'".
385 " --course-date '$date'".
386 " --course-center '$center'".
387 " --course-student '$student_name'".
388 " --course-trainer '$instructor'".
389 " --encoding $encoding"
390 );
391 system($Config{"l3-report"}.
392 " --input ".$Config{"path_lablogs"}."/$course/$date/$hostname/root".
393 " --diffs ".$Config{"path_lablogs"}."/$course/$date/$hostname/root ".
394 " --output $webdir/$date/$hostname/root.html".
395 " --course-name '$course_name'".
396 " --course-code '$course'".
397 " --course-date '$date'".
398 " --course-center '$center'".
399 " --course-student '$student_name'".
400 " --course-trainer '$instructor'".
401 " --encoding $encoding"
402 );
403 }
405 # Индекс для данного класса
407 my $head;
409 $head="Журналы лабораторных работ";
410 open(HTML, ">$webdir/$date/index.html")
411 or die "Can't open $webdir/$date/index.html for writing";
412 binmode HTML, ":utf8";
413 print HTML <<HEAD;
414 <html>
415 <head>
416 <meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
417 <title>$head</title>
418 </head>
419 <body>
420 <h1>$head</h1>
421 <p>
422 Курс: $course_name ($course)<br/>
423 Начало: $date<br/>
424 Учебный центр: $center <br/>
425 Инструктор: $instructor <br/>
426 </p>
427 <table>
428 HEAD
429 for my $student (@{$XMLClass->{"student"}}) {
430 my $user = $student->{"user"};
431 my $hostname = $student->{"host"};
432 print HTML "<tr>\n";
433 print HTML "<td>",$student->{"firstname"}," ",$student->{"surname"},"</td>\n";
434 print HTML "<td>",$hostname,"</td>\n";
435 print HTML "<td><a href=\"$hostname/$user.html\">",$user,"</td>\n";
436 print HTML "<td><a href=\"$hostname/root.html\">","root","</td>\n";
437 print HTML "</tr>\n";
438 }
439 print HTML <<TAIL;
440 </table>
441 </html>
442 TAIL
443 close (HTML);
447 }
449 sub load_run
450 {
451 my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"};
452 open (RUN, $runfile)
453 or return;
454 while (<RUN>) {
455 chomp;
456 my ($var, $val) = split /\s+/,$_,2;
457 $Run{$var}=$val;
458 }
459 close (RUN);
460 }
462 sub save_run
463 {
464 my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"};
465 open (RN, "$runfile")
466 or die "Can't save running state to $runfile";
467 for my $var (keys %Run) {
468 print RN $var,"\t",$Run{$var},"\n";
469 }
470 close (RN);
471 }
473 sub print_log
474 {
475 my $logfile = $Config{"path_labmaker"}."/".$Config{"path_logfile"};
476 open (LOG, ">>$logfile")
477 or die "Can't open logfile $logfile for writing";
478 print LOG @_;
479 close (LOG);
480 }
483 sub print_usage_info
484 {
485 print "Usage:\n\n\t$0 [host-list] command\n";
486 print <<'USAGE';
488 Commands:
490 next -- next lab
491 prev -- prev lab
492 set LAB -- set current lab to LAB
493 start -- start this day training
494 stop -- stop this day training
495 show hosts -- show available hosts in the class
496 show labs -- show available labs in the course
497 do COMMAND -- do specified command on the hosts of hostlist
498 report -- generate XML/HTML reports
501 do commands:
503 install [PROFILE] -- install profile
505 Host list:
507 @N -- machine N
508 @N1-N2 -- all of the machines from N1 to N2
509 @N1,N2,N3 -- machine N1, N2 and N3
511 N* is numbers or domain names of the machines.
513 If host list is not specified,
514 command is executed on all of the machines
516 USAGE
517 }