lilalo

view lm @ 20:6cabb6a4b4f5

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