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@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@0: # Каталоги devi@0: "path_labmaker" => "/var/labmaker/", devi@0: "path_classes" => "/var/labmaker/classes/", devi@0: "lablogs_path" => "/var/labmaker/lablogs/", devi@0: "courses_path" => "/var/labmaker/courses/", devi@0: "outpath" => "/var/labmaker/out/", 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@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@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@0: lablogs => $Config{"lablogs_path"}."/". 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@0: sub lm_report devi@0: { devi@0: devi@0: print "Not implemented yet\n"; devi@0: exit(1); devi@0: devi@0: =cut comment devi@0: devi@0: Дальше идут скрипты, код которых нужно реализовать здесь. devi@0: Как минимум. devi@0: devi@0: =cut devi@0: devi@0: my $lm_script_make_report_web = <<'SCRIPT'; devi@0: devi@0: #!/bin/sh devi@0: devi@0: ####cd /home/devi/lm devi@0: ./lm do copy-lablogs devi@0: ##./lm-report --input /home/murk/.labmaker --output /var/www/lm/murk.html devi@0: ##exit devi@0: devi@0: COURSE=ug-h devi@0: DATE=2005-04-25 devi@0: SUFF=".linux.nt" devi@0: #MACHINES="m01 m02 m03 m04 m05 m06 m07 m08 m09 m10 m11 m12 m13 m14 m15" devi@0: #MACHINES="m1 m2 m3 m4 m5 m6 m7 f1 f2 f3 f4 f5" devi@0: MACHINES="m01 m02 m03 m04 m05 m06 m07 m08" devi@0: #MACHINES="m1" devi@0: USERS="user root" devi@0: WEBDIR=/var/www/lm devi@0: devi@0: for u in $USERS devi@0: do devi@0: for m in $MACHINES devi@0: do devi@0: e=utf-8 devi@0: [ "${m##f}" = "$m" ] || e=koi8-r devi@0: #e=koi8-r devi@0: mkdir -p $WEBDIR/$DATE/$m devi@0: cp share/*.ico share/*.css $WEBDIR/$m devi@0: #echo Processing Lablogs/$COURSE/$DATE/$m$SUFF/$u/ devi@0: ./lm-report\ devi@0: --input Lablogs/$COURSE/$DATE/$m$SUFF/$u/ \ devi@0: --diffs "Lablogs/$COURSE/$DATE/$m$SUFF/$u/ Lablogs/$COURSE/$DATE/$m$SUFF/root/"\ devi@0: --output /var/www/lm/$DATE/$m/$u.html \ devi@0: --encoding $e devi@0: done devi@0: done devi@0: devi@0: devi@0: SCRIPT devi@0: devi@0: my $lm_script_make_report_all = <<'SCRIPT'; devi@0: devi@0: #!/bin/sh devi@0: WEBDIR=/var/www/lm devi@0: devi@0: find . -type d -maxdepth 5 -mindepth 5 |\ devi@0: while read dir devi@0: do devi@0: subdir=${dir##Lablogs/} devi@0: e=utf-8 devi@0: echo $dir | grep -qi bsd && e=koi8-r devi@0: echo $dir | grep -qi /f && e=koi8-r devi@0: devi@0: mkdir -p $WEBDIR/${subdir%/*} devi@0: cp share/*.ico share/*.css $WEBDIR/${subdir%/*} devi@0: #echo Processing Lablogs/$COURSE/$DATE/$m$SUFF/$u/ devi@0: ./lm-report\ devi@0: --input $dir \ devi@0: --diffs "${dir%/user}/root $dir"\ devi@0: --output $WEBDIR/$subdir.html \ devi@0: --encoding $e devi@0: done devi@0: devi@0: SCRIPT 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