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