lilalo
view lm @ 55:d3fcff5e3757
mywi ответы кэшируются + подсветка для записей с хинтами
| author | devi | 
|---|---|
| date | Thu Dec 22 11:56:06 2005 +0200 (2005-12-22) | 
| parents | e0db0f8bb482 | 
| children | 1e1422588716 | 
 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 	open (SCRIPTS, "$Config{l3scripts}")
   133 		or die "Cant open l3scripts file: ".$Config{l3scripts}.": $!\n";
   134 	binmode SCRIPTS, ":utf8";
   135 	local $/;
   136 	$_=<SCRIPTS>;
   137 	close(SCRIPTS);
   139 	%Scripts = ("empty-element", split (/###(.*)\n/));
   140 	delete($Scripts{"empty-element"});
   142 }
   144 sub load_config
   145 {
   146 	my %file_config;
   147 	my %argv_config;
   148 	#read_config_file(\%file_config, $Config_File);
   149 	GetOptions(\%argv_config, map "$_=s", keys %Config);
   150 	%Config = (%Config, %file_config, %argv_config);
   151 }
   153 sub load_course
   154 {
   155 	$XMLCourse = XMLin($Config{"courses_path"}.$XMLClass->{"course"}.".xml", ForceArray => 1 )  
   156 		or die "Can't open file of the course ",$XMLClass->{"course"}," [with .xml extension]\n";
   157 #	print Dumper($XMLCourse);
   158 	for my $lab (@{$XMLCourse->{"module"}}) {
   159 		push @Labs, $lab->{"code"};
   160 	}
   161 }
   163 sub load_class
   164 {
   165 	my $classfile =
   166 	$Config{"classfile"} || 
   167 	$Config{"path_classes"}."/".$Config{"class"}.$Config{"class_suffix"};
   168 	$XMLClass = XMLin($classfile , ForceArray => [ 'student' ] )  
   169 		or die "Can't open file of the class ",$classfile,"\n";
   171 	for my $student (@{$XMLClass->{"student"}}) {
   172 		$Machines{$student->{"host"}} = {
   173 			"name"	=> "$student->{firstname} $student->{surname}",
   174 			"firstname"	=> "$student->{firstname}",
   175 			"user"	=> "$student->{user}",
   176 			"student" => $student,
   177 		}	
   178 	}
   179 #	print Dumper($XMLClass);
   180 #	print Dumper(\%Machines);
   181 }
   184 sub lm_next
   185 {
   186 	for(my $i=0; $i<=$#Labs; $i++){
   187 		if ( $Labs[$i] eq $Run{"lab"} ) {
   188 			if ($i < $#Labs) {
   189 				lm_set($Labs[$i+1]);
   190 				return ;
   191 			} else {
   192 				die "Lab ", $Run{"lab"}, " is the last. Switch to next lab is impossible"
   193 			}
   194 		}
   196 	}
   197 	die "Lab ", $Run{"lab"}, " not found. Don't know which is next"
   198 }
   200 sub lm_prev
   201 # Switch to previous lab
   202 {
   203 	for(my $i=0; $i<=$#Labs; $i++){
   204 		if ( $Labs[$i] eq $Run{"lab"} ) {
   205 			if ($i > 0) {
   206 				lm_set($Labs[$i-1]);
   207 				return ;
   208 			} else {
   209 				die "Lab ", $Run{"lab"}, " is the first. Switch to previous lab is impossible"
   210 			}
   211 		}
   213 	}
   214 	die "Lab ", $Run{"lab"}, " not found. Don't know which is previous"
   215 }
   217 sub lm_set
   218 # Switch to $_[0] lab
   219 # FIXME
   220 {
   221 	my $lab = shift;
   222 	print "Current lab is $lab\n";
   223 	$Run{"lab"} = "$lab";
   224 	lm_do "setlab", $lab;
   225 }
   228 sub lm_start
   229 # Start new training day
   230 {
   231 	print_log(`date`." STARTED\n");
   232 	if ($Run{"lab"}) {
   233 		lm_next;
   234 	}
   235 	else
   236 	{
   237 		# First lab in the course
   238 		lm_set($Labs[0]);
   239 	}
   240 }
   242 sub lm_stop
   243 # Stop this training day
   244 {
   245 	print_log(`date`." STOPPED\n");
   246 }
   249 sub lm_show_hosts
   250 # Show hosts used to run a commands
   251 {
   252 	my $i=1;
   253 	for my $m (sort keys %Machines) {
   254 		if (!@SelectedMachines || grep /^$i$/, @SelectedMachines) {
   255 			print "($i)","\t",$m,"\t",$Machines{$m}->{"name"},"\n";
   256 		}	
   257 		$i++;
   258 	}
   259 }
   261 sub lm_show_labs
   262 # Show hosts used to run a commands
   263 {
   264 	my $i=1;
   265 	for my $lab (@Labs) {
   266 		print $lab;
   267 		print "*" if $lab eq $Run{"lab"};
   268 		print "\n";
   269 	}
   270 }
   272 sub lm_do
   273 # Do the $_[0] command on all of the hosts 
   274 {
   275 	my $command = shift;
   276 	my $arg = join " ", @_;
   277 	my $i=1;
   279 	my %myenv = ( %Config, 
   280 				lab	=> 	$arg,			
   281 				center 	=>	$XMLClass->{"center"},
   282 				course 	=>	$XMLClass->{"course"},
   283 				date 	=>	$XMLClass->{"date"},
   284 				stopdate 	=>	$XMLClass->{"stop-date"},
   285 				instructor 	=>	$XMLClass->{"instructor"}->{"firstname"}." ".$XMLClass->{"instructor"}->{"surname"},
   286 				manager 	=>	$XMLClass->{"manager"}->{"firstname"}." ".$XMLClass->{"manager"}->{"surname"},
   287 				coursepath =>	$XMLCourse->{"path"},
   288 			);
   290 	if (grep { $_ eq "PRE-$command"} keys %Scripts) {
   291 		$_=$Scripts{"PRE-$command"};
   292 		s/\$(\w+)/$myenv{$1}/ge;
   293 		open(SHELL, "|/bin/sh -s");
   294 		binmode SHELL, ":utf8";
   295 		print SHELL $_;
   296 		close (SHELL);
   297 	}
   300 	for my $m (sort keys %Machines) {
   301 		if (!@SelectedMachines || grep $_ eq $i, @SelectedMachines) {
   302 			print "$m:\n" if $Config{"show_host"} =~ /y/i;
   304 			%myenv = ( %myenv,
   305 				host 	=>	$m,
   306 				ipaddress	=> 	$Machines{$m}->{"ipaddress"},
   307 				dirs	=>	"/root /home/".$Machines{$m}->{"user"},
   308 				lablogs =>	$Config{"path_lablogs"}."/".
   309 						$XMLClass->{"course"}."/".
   310 						$XMLClass->{"date"}."/".
   311 						"$m",
   312 				email	=>	$Machines{$m}->{"student"}->{"email"},
   313 				company	=>	$Machines{$m}->{"student"}->{"company"},
   314 				name	=> 	$Machines{$m}->{"name"},
   315 				firstname	=> 	$Machines{$m}->{"firstname"},
   316 			);
   317 			if (grep { $_ eq $command} keys %Scripts) {
   318 				$_=$Scripts{"$command"};
   319 				s/\$(\w+)/$myenv{$1}/ge;
   320 				open(SHELL, "|/bin/sh -s");
   321 				binmode SHELL, ":utf8";
   322 				print SHELL $_;
   323 				close (SHELL);
   324 			}
   325 			else {
   326 				my $res = `ssh $Config{"ssh_user"}\@$m $command`;
   327 				if ($res) {
   328 					my $count = ($res =~ s/(^)/$m: /mg);
   329 					print $res;
   330 					print "\n" if ($count > 1);
   331 				}
   332 			}	
   333 		}	
   334 		$i++;
   335 	}
   337 	if (grep { $_ eq "POST-$command"} keys %Scripts) {
   338 		$_=$Scripts{"POST-$command"};
   339 		s/\$(\w+)/$myenv{$1}/ge;
   340 		open(SHELL, "|/bin/sh -s");
   341 		binmode SHELL, ":utf8";
   342 		print SHELL $_;
   343 		close (SHELL);
   344 	}
   345 }
   349 =cut comment
   351 lm report
   353 Построить html представление для журналов текущего класса.
   354 Для построения используется скрипт l3-report.
   356 =cut
   358 sub lm_report
   359 {
   361 	my $webdir = $Config{"path_web"};
   362 	my $course=$XMLClass->{"course"};
   363 	my $date=$XMLClass->{"date"};
   364 	my $encoding=$XMLClass->{"charset"};
   366 	my $center = $XMLClass->{"center"};
   367 	my $instructor = $XMLClass->{"instructor"}->{"firstname"}." ".$XMLClass->{"instructor"}->{"surname"};
   368 	my $course_name = $XMLCourse->{"fullname"}[0];
   371 	# Собственно журналы
   373 	for my $student (@{$XMLClass->{"student"}}) {
   374 		my $user = $student->{"user"};
   375 		my $hostname = $student->{"host"};
   376 		my $encoding = $student->{"charset"};
   377 		my $student_name = $student->{"firstname"}." ".$student->{"surname"};
   379 		system("mkdir -p $webdir/$date/$hostname");
   380 		system("cp ".$Config{"path_share"}."/*.{ico,css} $webdir/$date/$hostname");
   381 		system($Config{"l3-report"}.
   382 			" --input ".$Config{"path_lablogs"}."/$course/$date/$hostname/$user".
   383 			" --diffs ".$Config{"path_lablogs"}."/$course/$date/$hostname/$user ".
   384 				   $Config{"path_lablogs"}."/$course/$date/$hostname/root".
   385 			" --output $webdir/$date/$hostname/$user.html".
   386 			" --course-name '$course_name'".
   387 			" --course-code '$course'".
   388 			" --course-date '$date'".
   389 			" --course-center '$center'".
   390 			" --course-student '$student_name'".
   391 			" --course-trainer '$instructor'".
   392 			" --encoding $encoding"
   393 		);
   394 		system($Config{"l3-report"}.
   395 			" --input ".$Config{"path_lablogs"}."/$course/$date/$hostname/root".
   396 			" --diffs ".$Config{"path_lablogs"}."/$course/$date/$hostname/root ".
   397 			" --output $webdir/$date/$hostname/root.html".
   398 			" --course-name '$course_name'".
   399 			" --course-code '$course'".
   400 			" --course-date '$date'".
   401 			" --course-center '$center'".
   402 			" --course-student '$student_name'".
   403 			" --course-trainer '$instructor'".
   404 			" --encoding $encoding"
   405 		);
   406 	}
   408 	# Индекс для данного класса
   410 	my $head;
   412 	$head="Журналы лабораторных работ";
   413 	open(HTML, ">$webdir/$date/index.html")
   414 		or die "Can't open $webdir/$date/index.html for writing";
   415 	binmode HTML, ":utf8";
   416 	print HTML <<HEAD;
   417 	<html>
   418 	<head>
   419 	<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
   420 	<title>$head</title>
   421 	</head>
   422 	<body>
   423 	<h1>$head</h1>
   424 	<p>
   425 	Курс: $course_name ($course)<br/>
   426 	Начало: $date<br/>
   427 	Учебный центр: $center <br/>
   428 	Инструктор: $instructor <br/>
   429 	</p>
   430 	<table>
   431 HEAD
   432 	for my $student (@{$XMLClass->{"student"}}) {
   433 		my $user = $student->{"user"};
   434 		my $hostname = $student->{"host"};
   435 		print HTML "<tr>\n";
   436 		print HTML "<td>",$student->{"firstname"}," ",$student->{"surname"},"</td>\n";
   437 		print HTML "<td>",$hostname,"</td>\n";
   438 		print HTML "<td><a href=\"$hostname/$user.html\">",$user,"</td>\n";
   439 		print HTML "<td><a href=\"$hostname/root.html\">","root","</td>\n";
   440 		print HTML "</tr>\n";
   441 	}
   442 	print HTML <<TAIL;
   443 	</table>
   444 	</html>
   445 TAIL
   446 	close (HTML);
   450 }
   452 sub load_run
   453 {
   454 	my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"};
   455 	open (RUN, $runfile)
   456 		or return;
   457 	while (<RUN>) {
   458 		chomp;
   459 		my ($var, $val) = split /\s+/,$_,2;
   460 		$Run{$var}=$val;
   461 	}
   462 	close (RUN);	
   463 }
   465 sub save_run
   466 {
   467 	my $runfile = $Config{"path_labmaker"}."/".$Config{"path_runfile"};
   468 	open (RN, "$runfile")
   469 		or die "Can't save running state to $runfile";
   470 	for my $var (keys %Run) {
   471 		print RN $var,"\t",$Run{$var},"\n";
   472 	}
   473 	close (RN);	
   474 }
   476 sub print_log
   477 {
   478 	my $logfile = $Config{"path_labmaker"}."/".$Config{"path_logfile"};
   479 	open (LOG, ">>$logfile")
   480 		or die "Can't open logfile $logfile for writing";
   481 	print LOG  @_;
   482 	close (LOG);	
   483 }
   486 sub print_usage_info
   487 {
   488 	print "Usage:\n\n\t$0 [host-list] command\n";
   489 	print <<'USAGE';
   491 Commands:
   493 	next		-- next lab
   494 	prev		-- prev lab
   495 	set LAB		-- set current lab to LAB
   496 	start		-- start this day training
   497 	stop		-- stop this day training
   498 	show hosts	-- show available hosts in the class
   499 	show labs	-- show available labs in the course
   500 	do COMMAND	-- do specified command on the hosts of hostlist
   501 	report		-- generate XML/HTML reports
   504 do commands:
   506 	install [PROFILE] -- install profile 
   508 Host list:	
   510 	@N		-- machine N
   511 	@N1-N2		-- all of the machines from N1 to N2
   512 	@N1,N2,N3	-- machine N1, N2 and N3
   514 	N* is numbers or domain names of the machines.
   516 	If host list is not specified, 
   517 	command is executed on all of the machines
   519 USAGE
   520 }
