devi@0: #!/usr/bin/perl devi@0: devi@0: devi@0: use strict; devi@0: use Inline::Files; devi@0: use Data::Dumper; devi@0: use Switch; devi@0: use XML::Simple; devi@0: use Getopt::Long; devi@4: use utf8; devi@0: devi@0: our $XMLClass; devi@0: our $XMLCourse; devi@0: our @Labs; devi@0: devi@0: our %Machines; # Machines list from class.xml devi@0: our @SelectedMachines; # Machines list given as the command line argument devi@0: devi@0: our $Config_File = "labmaker.conf"; devi@0: our %Config = ( devi@0: "show_host" => "no", devi@0: devi@3: # Вспомогательные программы devi@3: "l3-report" => "./lm-report", devi@3: devi@0: # Каталоги devi@0: "path_labmaker" => "/var/labmaker/", devi@0: "path_classes" => "/var/labmaker/classes/", devi@3: "path_lablogs" => "/var/labmaker/lablogs/", devi@0: "courses_path" => "/var/labmaker/courses/", devi@0: "outpath" => "/var/labmaker/out/", devi@3: "path_web" => "/var/www/l3", # Путь к web-отчётам devi@3: "path_share" => "./share/", # Путь к web-отчётам devi@0: devi@0: # Файлы devi@0: "runfile" => "lm.run", devi@0: "logfile" => "lm.log", devi@0: devi@0: "class" => "class", # Имя файла класса devi@0: "class_suffix" => ".xml", # Cуффикс файла класса devi@5: "classfile" => "", devi@0: devi@0: "sshkey" => "$ENV{HOME}/.ssh/id_dsa.pub", devi@0: "lmssh" => "./lm-ssh", devi@0: "lminstall" => "./lm-install", devi@0: "ssh_user" => "r", devi@0: ); devi@0: devi@0: our %Run = ( devi@0: "lab" => "" devi@0: ); devi@0: devi@0: our %Scripts; devi@0: devi@0: sub load_class; devi@0: sub load_config; devi@0: sub load_course; devi@0: sub load_scripts; devi@0: devi@0: sub lm_next; devi@0: sub lm_prev; devi@0: sub lm_start; devi@0: sub lm_stop; devi@0: sub lm_set; devi@0: sub lm_do; devi@0: sub lm_report; devi@0: sub lm_show_hosts; devi@0: sub lm_show_labs; devi@0: devi@0: sub load_run; devi@0: sub save_run; devi@0: sub print_log; devi@0: sub print_usage_info; devi@0: sub main(); devi@0: devi@0: main(); devi@0: devi@0: sub main() devi@0: { devi@0: binmode STDOUT, ":utf8"; devi@0: devi@0: if (! @ARGV) { devi@0: print_usage_info(); devi@0: exit(0); devi@0: } devi@0: devi@0: load_config; devi@0: load_run; devi@0: load_scripts; devi@0: load_class; devi@0: load_course; devi@0: devi@0: my $arg = join " ", @ARGV; devi@0: devi@0: # Getting @SelectedMachines if any devi@0: if ($arg =~ s/@(.*?)\s//) { devi@0: my $machines = $1; devi@0: my @list = split /,/, $machines; devi@0: for my $interval (@list) { devi@0: my ($first, $last) = split /-/, $interval; devi@0: devi@0: push @SelectedMachines, $first; devi@0: while ($first < $last) { devi@0: push @SelectedMachines, ++$first; devi@0: } devi@0: } devi@0: } devi@0: devi@0: # Choose command to do devi@0: switch ($arg) { devi@0: case "next" { lm_next } devi@0: case "prev" { lm_prev } devi@0: case /set / { $arg =~ /set (.*)/; lm_set $1 } devi@0: case "report" { lm_report } devi@0: case "start" { lm_start } devi@0: case "stop" { lm_stop } devi@0: case "show hosts" { lm_show_hosts } devi@0: case "show labs" { lm_show_labs } devi@0: case /do / { $arg =~ /do (.*)/; lm_do "$1" } devi@0: else { print_usage_info() } devi@0: } devi@0: save_run; devi@0: exit(0); devi@0: } devi@0: devi@0: sub load_scripts devi@0: { devi@0: local $/; devi@0: $_=; devi@0: %Scripts = ("empty-element", split (/###(.*)\n/)); devi@0: delete($Scripts{"empty-element"}); devi@0: } devi@0: devi@0: sub load_config devi@0: { devi@0: my %file_config; devi@0: my %argv_config; devi@0: #read_config_file(\%file_config, $Config_File); devi@0: GetOptions(\%argv_config, map "$_=s", keys %Config); devi@0: %Config = (%Config, %file_config, %argv_config); devi@0: } devi@0: devi@0: sub load_course devi@0: { devi@0: $XMLCourse = XMLin($Config{"courses_path"}.$XMLClass->{"course"}.".xml", ForceArray => 1 ) devi@0: or die "Can't open file of the course ",$XMLClass->{"course"}," [with .xml extension]\n"; devi@0: # print Dumper($XMLCourse); devi@0: for my $lab (@{$XMLCourse->{"module"}}) { devi@0: push @Labs, $lab->{"code"}; devi@0: } devi@0: } devi@0: devi@0: sub load_class devi@0: { devi@0: my $classfile = devi@5: $Config{"classfile"} || devi@0: $Config{"path_classes"}."/".$Config{"class"}.$Config{"class_suffix"}; devi@0: $XMLClass = XMLin($classfile , ForceArray => [ 'student' ] ) devi@0: or die "Can't open file of the class ",$classfile,"\n"; devi@0: devi@0: for my $student (@{$XMLClass->{"student"}}) { devi@0: $Machines{$student->{"host"}} = { devi@0: "name" => "$student->{firstname} $student->{surname}", devi@0: "user" => "$student->{user}", devi@0: "student" => $student, devi@0: } devi@0: } devi@0: # print Dumper($XMLClass); devi@0: # print Dumper(\%Machines); devi@0: } devi@0: devi@0: devi@0: sub lm_next devi@0: { devi@0: for(my $i=0; $i<=$#Labs; $i++){ devi@0: if ( $Labs[$i] eq $Run{"lab"} ) { devi@0: if ($i < $#Labs) { devi@0: lm_set($Labs[$i+1]); devi@0: return ; devi@0: } else { devi@0: die "Lab ", $Run{"lab"}, " is the last. Switch to next lab is impossible" devi@0: } devi@0: } devi@0: devi@0: } devi@0: die "Lab ", $Run{"lab"}, " not found. Don't know which is next" devi@0: } devi@0: devi@0: sub lm_prev devi@0: # Switch to previous lab devi@0: { devi@0: for(my $i=0; $i<=$#Labs; $i++){ devi@0: if ( $Labs[$i] eq $Run{"lab"} ) { devi@0: if ($i > 0) { devi@0: lm_set($Labs[$i-1]); devi@0: return ; devi@0: } else { devi@0: die "Lab ", $Run{"lab"}, " is the first. Switch to previous lab is impossible" devi@0: } devi@0: } devi@0: devi@0: } devi@0: die "Lab ", $Run{"lab"}, " not found. Don't know which is previous" devi@0: } devi@0: devi@0: sub lm_set devi@0: # Switch to $_[0] lab devi@0: # FIXME devi@0: { devi@0: my $lab = shift; devi@0: print "Current lab is $lab\n"; devi@0: $Run{"lab"} = "$lab"; devi@0: lm_do "setlab", $lab; devi@0: } devi@0: devi@0: devi@0: sub lm_start devi@0: # Start new training day devi@0: { devi@0: print_log(`date`." STARTED\n"); devi@0: if ($Run{"lab"}) { devi@0: lm_next; devi@0: } devi@0: else devi@0: { devi@0: # First lab in the course devi@0: lm_set($Labs[0]); devi@0: } devi@0: } devi@0: devi@0: sub lm_stop devi@0: # Stop this training day devi@0: { devi@0: print_log(`date`." STOPPED\n"); devi@0: } devi@0: devi@0: devi@0: sub lm_show_hosts devi@0: # Show hosts used to run a commands devi@0: { devi@0: my $i=1; devi@0: for my $m (sort keys %Machines) { devi@0: if (!@SelectedMachines || grep /^$i$/, @SelectedMachines) { devi@0: print "($i)","\t",$m,"\t",$Machines{$m}->{"name"},"\n"; devi@0: } devi@0: $i++; devi@0: } devi@0: } devi@0: devi@0: sub lm_show_labs devi@0: # Show hosts used to run a commands devi@0: { devi@0: my $i=1; devi@0: for my $lab (@Labs) { devi@0: print $lab; devi@0: print "*" if $lab eq $Run{"lab"}; devi@0: print "\n"; devi@0: } devi@0: } devi@0: devi@0: sub lm_do devi@0: # Do the $_[0] command on all of the hosts devi@0: { devi@0: my $command = shift; devi@0: my $arg = join " ", @_; devi@0: my $i=1; devi@0: for my $m (sort keys %Machines) { devi@0: if (!@SelectedMachines || grep $_ eq $i, @SelectedMachines) { devi@0: print "$m:\n" if $Config{"show_host"} =~ /y/i; devi@0: devi@0: my %myenv = ( %Config, devi@0: host => $m, devi@0: dirs => "/root /home/".$Machines{$m}->{"user"}, devi@3: lablogs => $Config{"path_lablogs"}."/". devi@0: $XMLClass->{"course"}."/". devi@0: $XMLClass->{"date"}."/". devi@0: "$m", devi@0: lab => $arg, devi@0: devi@0: email => $Machines{$m}->{"student"}->{"email"}, devi@0: company => $Machines{$m}->{"student"}->{"company"}, devi@0: center => $XMLClass->{"center"}, devi@0: course => $XMLClass->{"course"}, devi@0: date => $XMLClass->{"date"}, devi@0: name => $Machines{$m}->{"name"}, devi@0: coursepath => $XMLCourse->{"path"}, devi@0: devi@0: ); devi@0: if (grep { $_ eq $command} keys %Scripts) { devi@0: $_=$Scripts{"$command"}; devi@0: s/\$(\w+)/$myenv{$1}/ge; devi@0: open(SHELL, "|/bin/sh -s"); devi@0: binmode SHELL, ":utf8"; devi@0: print SHELL $_; devi@0: close (SHELL); devi@0: } devi@0: else { devi@0: my $res = `ssh $Config{"ssh_user"}\@$m $command`; devi@0: if ($res) { devi@0: my $count = ($res =~ s/(^)/$m: /mg); devi@0: print $res; devi@0: print "\n" if ($count > 1); devi@0: } devi@0: } devi@0: } devi@0: $i++; devi@0: } devi@0: } devi@0: devi@0: devi@3: devi@3: =cut comment devi@3: devi@3: lm report devi@3: devi@3: Построить html представление для журналов текущего класса. devi@3: Для построения используется скрипт l3-report. devi@3: devi@3: =cut devi@3: devi@0: sub lm_report devi@0: { devi@0: devi@3: my $webdir = $Config{"path_web"}; devi@3: my $course=$XMLClass->{"course"}; devi@3: my $date=$XMLClass->{"date"}; devi@3: my $encoding=$XMLClass->{"charset"}; devi@0: devi@4: my $center = $XMLClass->{"center"}; devi@4: my $instructor = $XMLClass->{"instructor"}->{"firstname"}." ".$XMLClass->{"instructor"}->{"surname"}; devi@4: my $course_name = $XMLCourse->{"fullname"}[0]; devi@4: devi@5: devi@5: # Собственно журналы devi@5: devi@5: for my $student (@{$XMLClass->{"student"}}) { devi@5: my $user = $student->{"user"}; devi@5: my $hostname = $student->{"host"}; devi@5: my $encoding = $student->{"encoding"}; devi@5: devi@5: system("mkdir -p $webdir/$date/$hostname"); devi@5: system("cp ".$Config{"path_share"}."/*.{ico,css} $webdir/$date/$hostname"); devi@5: system($Config{"l3-report"}. devi@5: " --input ".$Config{"path_lablogs"}."/$course/$date/$hostname/$user". devi@5: " --diffs ".$Config{"path_lablogs"}."/$course/$date/$hostname/$user ". devi@5: $Config{"path_lablogs"}."/$course/$date/$hostname/root". devi@5: " --output $webdir/$date/$hostname/$user.html". devi@5: " --encoding $encoding" devi@5: ); devi@5: system($Config{"l3-report"}. devi@5: " --input ".$Config{"path_lablogs"}."/$course/$date/$hostname/root". devi@5: " --diffs ".$Config{"path_lablogs"}."/$course/$date/$hostname/root ". devi@5: " --output $webdir/$date/$hostname/root.html". devi@5: " --encoding $encoding" devi@5: ); devi@5: } devi@5: devi@4: # Индекс для данного класса devi@4: devi@4: my $head; devi@4: devi@4: $head="Журналы лабораторных работ"; devi@4: open(HTML, ">$webdir/$date/index.html") devi@4: or die "Can't open $webdir/$date/index.html for writing"; devi@4: binmode HTML, ":utf8"; devi@4: print HTML < devi@4: devi@4: devi@4: $head devi@4: devi@4: devi@4:

$head

devi@4:

devi@4: Курс: $course_name ($course)
devi@4: Начало: $date
devi@4: Учебный центр: $center
devi@4: Инструктор: $instructor
devi@4:

devi@4: devi@4: HEAD devi@4: for my $student (@{$XMLClass->{"student"}}) { devi@4: my $user = $student->{"user"}; devi@4: my $hostname = $student->{"host"}; devi@4: print HTML "\n"; devi@4: print HTML "\n"; devi@4: print HTML "\n"; devi@4: print HTML "\n"; devi@4: print HTML "\n"; devi@4: print HTML "\n"; devi@4: } devi@4: print HTML < devi@4: devi@4: TAIL devi@4: close (HTML); devi@4: devi@4: devi@4: devi@0: } devi@0: devi@0: sub load_run devi@0: { devi@0: my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"}; devi@0: open (RUN, $runfile) devi@0: or return; devi@0: while () { devi@0: chomp; devi@0: my ($var, $val) = split /\s+/,$_,2; devi@0: $Run{$var}=$val; devi@0: } devi@0: close (RUN); devi@0: } devi@0: devi@0: sub save_run devi@0: { devi@0: my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"}; devi@0: open (RN, "$runfile") devi@0: or die "Can't save running state to $runfile"; devi@0: for my $var (keys %Run) { devi@0: print RN $var,"\t",$Run{$var},"\n"; devi@0: } devi@0: close (RN); devi@0: } devi@0: devi@0: sub print_log devi@0: { devi@0: my $logfile = $Config{"path_labmaker"}."/".$Config{"path_logfile"}; devi@0: open (LOG, ">>$logfile") devi@0: or die "Can't open logfile $logfile for writing"; devi@0: print LOG @_; devi@0: close (LOG); devi@0: } devi@0: devi@0: devi@0: sub print_usage_info devi@0: { devi@0: print "Usage:\n\n\t$0 [host-list] command\n"; devi@0: while () { devi@0: print $_; devi@0: } devi@0: } devi@0: devi@0: __USAGE__ devi@0: devi@0: Commands: devi@0: devi@0: next -- next lab devi@0: prev -- prev lab devi@0: set LAB -- set current lab to LAB devi@0: start -- start this day training devi@0: stop -- stop this day training devi@0: show hosts -- show available hosts in the class devi@0: show labs -- show available labs in the course devi@0: do COMMAND -- do specified command on the hosts of hostlist devi@0: report -- generate XML/HTML reports devi@0: devi@0: devi@0: do commands: devi@0: devi@0: install [PROFILE] -- install profile devi@0: devi@0: Host list: devi@0: devi@0: @N -- machine N devi@0: @N1-N2 -- all of the machines from N1 to N2 devi@0: @N1,N2,N3 -- machine N1, N2 and N3 devi@0: devi@0: N* is numbers or domain names of the machines. devi@0: devi@0: If host list is not specified, devi@0: command is executed on all of the machines devi@0: devi@0: devi@0: devi@0: __SCRIPTS__ devi@0: ###install devi@0: cat $sshkey | $lmssh $ssh_user@$host /bin/sh -c '"mkdir -p ~/.ssh; cat >>~/.ssh/authorized_keys; chmod 600 ~/.ssh/authorized_keys"' devi@0: devi@0: ###install-lm devi@0: cat $lminstall | ssh $ssh_user@$host /bin/sh -s $dirs devi@0: devi@0: ###copy-lablogs devi@0: for i in $dirs devi@0: do devi@0: mkdir -p $lablogs/${i##*/} devi@0: scp -q $ssh_user@$host:${i}/.labmaker/* $lablogs/${i##*/} devi@0: done devi@0: devi@0: ###setlab devi@0: for i in $dirs devi@0: do devi@0: echo $lab | ssh $ssh_user@$host "cat > "${i}"/.labmaker/lab" devi@0: done devi@0: devi@0: ###makeout devi@0: common=$course-$date devi@0: personal=$course-$date-$email devi@0: mkdir -p $outpath/${common}/{Lablogs,Docs} devi@0: mkdir -p $outpath/${personal}/{Course,Files} devi@0: cd $outpath/${personal} devi@0: ln -s ../${common}/Lablogs . devi@0: ln -s ../${common}/Docs . devi@0: cd ~- devi@0: export UG_PERSONAL=${PWD}/$outpath/${personal}/Course devi@0: export UG_CENTER="$center" devi@0: export UG_COURSE="$course" devi@0: export UG_DATE="$date" devi@0: export UG_STUDENT="$name" devi@0: export UG_COMPANY="$company" devi@0: cd $coursepath; make personal; cd ~- devi@0: devi@0: ###watch devi@0: cat taillast.pl | ssh $ssh_user@$host perl - /root/.labmaker
",$student->{"firstname"}," ",$student->{"surname"},"",$hostname,"",$user,"","root","