lilalo
view lm @ 114:658b4ea105c1
PS1 bug fixed
| author | igor | 
|---|---|
| date | Sun Mar 09 02:38:56 2008 +0200 (2008-03-09) | 
| 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 }
