lilalo

changeset 25:ba4d6515b8fd v_0_2_3

Выполнен шаг (3) в плане (N05) по построению распределённой системы lilalo.


Агент l3-агент в реальном времени анализирует скрипты в указанном ему
каталоге и по мере обнаружения новых завершённых команд записывает их
в кэш-файл.

Данные о том, докуда разобран каждый скрипт-файл сохраняются во временном
файле, для того чтобы при перезапуске агента он мог продолжить разбор
с того места, где он был остановлен в прошлый раз, а не копировал
данные в кэш-файл повторно.

Агент запускается для каждого пользователя системы.
Если агент обнаружил свою копию работающую от имени того же пользователя,
он автоматически завершается.
Поиск копии агента выполняется так:
просматривается pid-файл агента - если его нет, считается, что и агент не запущен
(Внимание! Не удаляйте pid-файл!! Работа нескольких агентов от имени одного
пользователя может быть некорректной!)
Если он есть, выполняется проверка, действительно ли процесс с таким идентификатором
это l3-агент текущего пользователя. Если нет, pid-файл удаляется, и агент запускается.

Нормальное завершение агента, работающего в режиме демона, выполняется
с помощью сигнала TERM. При завершении агент автоматически стирает свой pid-файл.



Добавлены атрибуты команды, хранящие информацию о участке бинарного файла скрипта,
соответствующей команды:
raw_start - начало блока команды
raw_output_start - начало вывода команды
raw_end - окончание вывода
raw_file - имя бинарного файла

Файлы:
(могут меняться с помощью конфигурационных параметров)

~/.labmaker/.cache.dat
~/.labmaker/cache.xml
~/.labmaker/l3-agent.pid

Конфигурационные параметры:
cache_stat Имя файла с информацией о текущей позиции разбора
в каждом файле

mode Режим, в котором работает агент.
Допустимые значения:
daemon - в режиме непрерывного опроса каталога
Программа не завершается после окончания анализа,
а ждёт появления новых данных
normal - однократный анализ каталога.
Программа завершается после окончания анализа данных

daemon_sleep_interval Интервал через который агент просматривает каталог скриптов
в поисках новых данных

detach Нужно ли выполнять отключение от терминала при работе в режиме демона?
(строго говоря, если процесс не отключился от терминала,
то и в режиме демона он работать не может. Здесь имеется в виду
режим непрерывного опроса каталога)

agent_pidfile Путь к файлу, который будет хранить идентификатор процесса агента.

l3-agent Имя, под которым будет известен процесс l3-agent

Незначительные исправления:
* убрана отладочная информация о new_commands и new_files из frontend'а
author devi
date Thu Nov 03 17:49:56 2005 +0200 (2005-11-03)
parents 4b86595adb4b
children 916661a89335
files l3-agent l3-frontend l3config.pm
line diff
     1.1 --- a/l3-agent	Wed Nov 02 19:25:39 2005 +0200
     1.2 +++ b/l3-agent	Thu Nov 03 17:49:56 2005 +0200
     1.3 @@ -5,6 +5,7 @@
     1.4  #
     1.5  
     1.6  use strict;
     1.7 +use POSIX;
     1.8  use Term::VT102;
     1.9  use Text::Iconv;
    1.10  use Data::Dumper;
    1.11 @@ -21,6 +22,13 @@
    1.12  our %Commands_Stat;		# Statistics about commands usage
    1.13  our %Files_Stat;		# Statistics about commands usage
    1.14  
    1.15 +our %Script_Files;		# Информация о позициях в скрипт-файлах, 
    1.16 +				# до которых уже выполнен разбор
    1.17 +				# и информация о времени модификации файла
    1.18 +				# 	$Script_Files{$file}->{size}
    1.19 +				# 	$Script_Files{$file}->{tell}
    1.20 +
    1.21 +our $Killed =0;			# В режиме демона -- процесс получил сигнал о завершении
    1.22  
    1.23  sub init_variables;
    1.24  sub main;
    1.25 @@ -34,6 +42,10 @@
    1.26  sub print_command_lines;
    1.27  sub printq;
    1.28  
    1.29 +sub save_cache_stat;
    1.30 +sub load_cache_stat;
    1.31 +
    1.32 +
    1.33  sub load_diff_files
    1.34  {
    1.35  	my @pathes = @_;
    1.36 @@ -183,7 +195,8 @@
    1.37  	my $lab_scripts_mask = $_[1];
    1.38  
    1.39  	my $cline_re_base = qq'
    1.40 -			(?:\\^?([0-9]*C?))					# exitcode
    1.41 +			(
    1.42 +			(?:\\^?([0-9]*C?))			# exitcode
    1.43  			(?:_([0-9]+)_)?				# uid
    1.44  			(?:_([0-9]+)_)				# pid
    1.45  			(...?)					# day
    1.46 @@ -193,6 +206,7 @@
    1.47  			.\\[50D.\\[K				# killing symbols
    1.48  			(.*?([\$\#]\\s?))			# prompt
    1.49  			(.*)					# command line
    1.50 +			)
    1.51  			';
    1.52  	#my $cline_re = qr/$cline_re_base(?:$cline_re_base|$)/x;
    1.53  	#my $cline_re = qr/(?:$cline_re_base)*$cline_re_base$/x;
    1.54 @@ -221,9 +235,20 @@
    1.55  
    1.56  	for $file (@lab_scripts){
    1.57  		#printf "\t%i %3.2f\n", $ii, (100*$ii++/$files_number) if $Config{"verbose"} =~ /y/;
    1.58 +		
    1.59 +		# Пропускаем файл, если он не изменялся со времени нашего предудущего прохода
    1.60 +		my $size = (stat($file))[7];
    1.61 +		next if ($Script_Files{$file} && $Script_Files{$file}->{size} && $Script_Files{$file}->{size} >= $size);
    1.62  
    1.63  		open (FILE, "$file");
    1.64  		binmode FILE;
    1.65 +		
    1.66 +		# Переходим к тому месту, где мы окончили разбор
    1.67 +		seek (FILE, $Script_Files{$file}->{tell}, 0) if $Script_Files{$file}->{tell};
    1.68 +		$Script_Files{$file}->{size} = $size;
    1.69 +		$Script_Files{$file}->{tell} = 0 unless $Script_Files{$file}->{tell};
    1.70 +
    1.71 +
    1.72  		$file =~ m@.*/(.*?)-.*@;
    1.73  		
    1.74  		my $tty = $1;
    1.75 @@ -231,6 +256,7 @@
    1.76  		my %cl;
    1.77  		my $last_output_length=0;
    1.78  		while (<FILE>) {
    1.79 +			
    1.80  			$commandlines_processed++;
    1.81  				# time
    1.82  
    1.83 @@ -247,7 +273,7 @@
    1.84  
    1.85  				# Previous command
    1.86  				my %last_cl = %cl;
    1.87 -				my $err = $1 || "";
    1.88 +				my $err = $2 || "";
    1.89  
    1.90  
    1.91  =cut 
    1.92 @@ -380,11 +406,14 @@
    1.93  	raw_start (*)
    1.94  		Начало блока командной строки в файле бинарного представления
    1.95  	
    1.96 +	raw_output_start (*)
    1.97 +		Начало блока вывода
    1.98 +	
    1.99  	raw_end (*)
   1.100  		Конец блока командной строки в файле бинарного представления
   1.101  
   1.102  	raw_cline (*)
   1.103 -		Необработанная командная строка в бинарном виде
   1.104 +		Необработанная командная строка (без приглашения) в бинарном виде
   1.105  	
   1.106  	raw_data (*)
   1.107  		Бинарное представление команды и результатов её выполнения
   1.108 @@ -402,17 +431,24 @@
   1.109  =cut
   1.110  
   1.111  				# Parse new command 
   1.112 -				$cl{"uid"} = $2;
   1.113 +				$cl{"uid"} = $3;
   1.114  				$cl{"euid"} = $cl{"uid"};	# Если в команде обнаружится sudo, euid поменяем на 0
   1.115 -				$cl{"pid"} = $3;
   1.116 -				$cl{"day"} = $4;
   1.117 -				$cl{"lab"} = $5;
   1.118 -				$cl{"hour"} = $6;
   1.119 -				$cl{"min"} = $7;
   1.120 -				$cl{"sec"} = $8;
   1.121 -				$cl{"fullprompt"} = $9;
   1.122 -				$cl{"prompt"} = $10;
   1.123 -				$cl{"raw_cline"} = $11;	
   1.124 +				$cl{"pid"} = $4;
   1.125 +				$cl{"day"} = $5;
   1.126 +				$cl{"lab"} = $6;
   1.127 +				$cl{"hour"} = $7;
   1.128 +				$cl{"min"} = $8;
   1.129 +				$cl{"sec"} = $9;
   1.130 +				$cl{"fullprompt"} = $10;
   1.131 +				$cl{"prompt"} = $11;
   1.132 +				$cl{"raw_cline"} = $12;	
   1.133 +
   1.134 +				{
   1.135 +				use bytes;
   1.136 +				$cl{"raw_start"} = tell (FILE) - length($1);
   1.137 +				$cl{"raw_output_start"} = tell FILE;
   1.138 +				}
   1.139 +				$cl{"raw_file"} = $file;
   1.140  
   1.141  				$cl{"err"} = 0;
   1.142  				$cl{"output"} = "";
   1.143 @@ -454,6 +490,7 @@
   1.144  				}
   1.145  
   1.146  				# Error code
   1.147 +				$last_cl{"raw_end"} = $cl{"raw_start"};
   1.148  				$last_cl{"err"}=$err;
   1.149  				$last_cl{"err"}=130 if $err eq "^C";
   1.150  
   1.151 @@ -484,11 +521,16 @@
   1.152  				if (!$Config{"lab"} || $cl{"lab"} eq $Config{"lab"}) {
   1.153  					# Changing encoding 
   1.154  					for (keys %last_cl) {
   1.155 +						next if /raw/;
   1.156  						$last_cl{$_} = $converter->convert($last_cl{$_})
   1.157  							if ($Config{"encoding"} && 
   1.158  							$Config{"encoding"} !~ /^utf-8$/i);
   1.159  					}
   1.160  					push @Command_Lines, \%last_cl;	
   1.161 +
   1.162 +					# Сохранение позиции в файле, до которой выполнен
   1.163 +					# успешный разбор
   1.164 +					$Script_Files{$file}->{tell} = $last_cl{raw_end};
   1.165  				}	
   1.166  				next;
   1.167  			}
   1.168 @@ -620,12 +662,14 @@
   1.169  sub print_command_lines
   1.170  {
   1.171  	my $output_filename=$_[0];
   1.172 -	open(OUT, ">", $output_filename)
   1.173 +	my $mode = ">";
   1.174 +	$mode =">>" if $Config{mode} eq "daemon";
   1.175 +	open(OUT, $mode, $output_filename)
   1.176  		or die "Can't open $output_filename for writing\n";
   1.177  
   1.178  
   1.179  
   1.180 -	print OUT "<livelablog>\n";
   1.181 +	#print OUT "<livelablog>\n";
   1.182  
   1.183  	my $cl;
   1.184  	my $in_range=0;
   1.185 @@ -708,6 +752,10 @@
   1.186  		# Начинаем вывод команды
   1.187  		print OUT "<command>\n";
   1.188  		print OUT "<time>",$cl->{time},"</time>\n";
   1.189 +		print OUT "<raw_start>",$cl->{raw_start},"</raw_start>\n";
   1.190 +		print OUT "<raw_output_start>",$cl->{raw_output_start},"</raw_output_start>\n";
   1.191 +		print OUT "<raw_end>",$cl->{raw_end},"</raw_end>\n";
   1.192 +		print OUT "<raw_file>",$cl->{raw_file},"</raw_file>\n";
   1.193  		print OUT "<tty>",$cl->{tty},"</tty>\n";
   1.194  		print OUT "<out_class>",$out_class,"</out_class>\n";
   1.195  		print OUT "<prompt>";
   1.196 @@ -739,10 +787,31 @@
   1.197  
   1.198  	}
   1.199  
   1.200 -	print OUT "</livelablog>\n";
   1.201 +	#print OUT "</livelablog>\n";
   1.202  	close(OUT);
   1.203 +	save_cache_stat();
   1.204  }
   1.205  
   1.206 +sub save_cache_stat
   1.207 +{
   1.208 +	open (CACHE, ">$Config{cache_stat}");
   1.209 +	for my $f (keys %Script_Files) {
   1.210 +		print CACHE "$f\t",$Script_Files{$f}->{size},"\t",$Script_Files{$f}->{tell},"\n";
   1.211 +	}
   1.212 +	close(CACHE);
   1.213 +}
   1.214 +
   1.215 +sub load_cache_stat
   1.216 +{
   1.217 +	if (open (CACHE, "$Config{cache_stat}")) {
   1.218 +		while(<CACHE>) {
   1.219 +			my ($f, $size, $tell) = split /\t/;
   1.220 +			$Script_Files{$f}->{size} = $size;
   1.221 +			$Script_Files{$f}->{tell} = $tell;
   1.222 +		}
   1.223 +		close(CACHE);
   1.224 +	};
   1.225 +}
   1.226  
   1.227  =cut
   1.228  sub print_command_lines2
   1.229 @@ -806,6 +875,11 @@
   1.230  
   1.231  main();
   1.232  
   1.233 +sub process_was_killed
   1.234 +{
   1.235 +	$Killed = 1;
   1.236 +}
   1.237 +
   1.238  sub main
   1.239  {
   1.240  $| = 1;
   1.241 @@ -817,10 +891,64 @@
   1.242  	load_diff_files($lab_log);
   1.243  }
   1.244  
   1.245 -load_command_lines($Config{"input"}, $Config{"input_mask"});
   1.246 -sort_command_lines;
   1.247 -process_command_lines;
   1.248 -print_command_lines($Config{"cache"});
   1.249 +if ($Config{"mode"} ne "daemon") {
   1.250 +	load_command_lines($Config{"input"}, $Config{"input_mask"});
   1.251 +	sort_command_lines;
   1.252 +	process_command_lines;
   1.253 +	print_command_lines($Config{"cache"});
   1.254 +} 
   1.255 +else {
   1.256 +	if (open(PIDFILE, $Config{agent_pidfile})) {
   1.257 +		my $pid = <PIDFILE>;
   1.258 +		close(PIDFILE);
   1.259 +		if ( ! -e "/proc/$pid" || !`grep $Config{"l3-agent"} /proc/$pid/cmdline && grep "uid:.*\b$<\b" /proc/$pid/status`) {
   1.260 +			print "Removing stale pidfile\n";
   1.261 +			unlink $Config{agent_pidfile};
   1.262 +				or die "Can't remove stale pidfile ". $Config{agent_pidfile}. " : $!";
   1.263 +		}
   1.264 +		else {
   1.265 +			print "l3-agent is already running\n";
   1.266 +			exit(0);
   1.267 +		}
   1.268 +	}
   1.269 +	if ($Config{detach} =~ /^y/i) {
   1.270 +		#$Config{verbose} = "no";
   1.271 +		my $pid = fork;
   1.272 +		exit if $pid;
   1.273 +		die "Couldn't fork: $!" unless defined ($pid);
   1.274 +
   1.275 +		open(PIDFILE, ">", $Config{agent_pidfile})
   1.276 +			or die "Can't open pidfile ". $Config{agent_pidfile}. " for wrting: $!";
   1.277 +		print PIDFILE $$;
   1.278 +		close(PIDFILE);
   1.279 +
   1.280 +		for my $handle (*STDIN, *STDOUT, *STDERR) {
   1.281 +			open ($handle, "+<", "/dev/null")
   1.282 +				or die "can't reopen $handle to /dev/null: $!"
   1.283 +		}
   1.284 +
   1.285 +		POSIX::setsid()
   1.286 +			or die "Can't start a new session: $!";
   1.287 +
   1.288 +		$0 = $Config{"l3-agent"};
   1.289 +		
   1.290 +		$SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&process_was_killed;
   1.291 +	}
   1.292 +	while (not $Killed) {
   1.293 +		@Command_Lines = ();
   1.294 +		@Command_Lines_Index = ();
   1.295 +		load_cache_stat();
   1.296 +		load_command_lines($Config{"input"}, $Config{"input_mask"});
   1.297 +		if (@Command_Lines) {
   1.298 +			sort_command_lines;
   1.299 +			process_command_lines;
   1.300 +			print_command_lines($Config{"cache"});
   1.301 +		}
   1.302 +		sleep($Config{"daemon_sleep_interval"} || 1);
   1.303 +	}
   1.304 +	
   1.305 +	unlink $Config{agent_pidfile};
   1.306 +}
   1.307  
   1.308  }
   1.309  
     2.1 --- a/l3-frontend	Wed Nov 02 19:25:39 2005 +0200
     2.2 +++ b/l3-frontend	Thu Nov 03 17:49:56 2005 +0200
     2.3 @@ -306,15 +306,6 @@
     2.4  				$Result{"body"} .= "</table>\n";
     2.5  				$Result{"body"} .= "</td></tr></table>";
     2.6  			}
     2.7 -			else {
     2.8 -				$Result{"body"} .= "<table width='$Config{comment_width}'>".
     2.9 -						"<tr><td width='5'/><td>";
    2.10 -				$Result{"body"} .= "<table class='note' width='100%'>";
    2.11 -				$Result{"body"} .= "commands ".join(" ",@new_commands)."<br/>";
    2.12 -				$Result{"body"} .= "files ".join(" ",@new_files)."<br/>";
    2.13 -				$Result{"body"} .= "</table>\n";
    2.14 -				$Result{"body"} .= "</td></tr></table>";
    2.15 -			}
    2.16  		}
    2.17  
    2.18  		# Вывод очередной команды окончен
     3.1 --- a/l3config.pm	Wed Nov 02 19:25:39 2005 +0200
     3.2 +++ b/l3config.pm	Thu Nov 03 17:49:56 2005 +0200
     3.3 @@ -40,6 +40,7 @@
     3.4  		"encoding"			=> 	"utf-8",
     3.5  
     3.6  		"cache"				=>	"/tmp/report.xml",
     3.7 +		"cache_stat"			=>	"/tmp/.report.dat",
     3.8  
     3.9  		"output"			=>	"/tmp/report.html",
    3.10  		#"output"			=>	"report.xml",
    3.11 @@ -55,7 +56,14 @@
    3.12  		
    3.13  		comment_width			=>	"300",
    3.14  		time_width			=>	"60",
    3.15 +		
    3.16 +		"mode"				=>	"daemon",		# daemon | normal
    3.17 +		"daemon_sleep_interval"		=>	"1",
    3.18 +		"detach"			=>	"yes",
    3.19 +		"agent_pidfile"			=> 	"$ENV{HOME}/.labmaker/l3-agent.pid",
    3.20  
    3.21 +		"l3-agent"			=>	"l3-agent", 
    3.22 +		
    3.23  		"course-name" => "", 
    3.24  		"course-code" => "", 
    3.25  		"course-date" => "",