lilalo

view l3-frontend @ 27:098664cf339c

Выполнены шаги 4,5 в плане N05 по построению распределённой системы lilalo.
Шаг <6> в настоящее время не является необходимым.


Введено понятие сеанса.
Сеансом считается процедура работы с системой, начинающаяся с регистрации
в ней и зазаканчивающаяся разрегистрацией, и сопровождающаяся ведением одного
файла скрипта.
Одновременно с созданием скрипта (.script) создаётся соответствующий ему
файл с информацией о сеансе (.info).
Каждый сеанс имеет уникальный в пределах хоста идентификатор,
~local_session_id~, который впоследствии позволяет определить,
какие команды относятся к какому сеансу.

Добавлен backend-сервер, который получает данные от агентов и записывает
из в backend (в настойщий момент - в XML-файл).
Данные передаются по tcp-соединениям.
(Одновременно может работать несколько серверов.
Блокировка файла при записи пока что не выполняется ОСТОРОЖНО!!!!!!)

Агент периодически пытается отправить backend-серверу содержимое своего кэш-файла,
и если ему это удаётся, кэш файл очищается -- данные теперь хранятся в backend'е.

Взаимодействие агентов, backend-сервера и frontend'а
сейчас выполнеятся так:


+-------+
| |
| cache |
| |
+-^---+-+
| |
. ^ v . ^^ . +---------+ . ^^ .
/ \ tcp / \ | | / \ CGI
( agent )----->( backend- )-->| backend |-->( frontend )----->
\ / \ сервер / | | \ /
' . ' ' .. ' +---------+ ' .. '
^
|
+----+----+
| |
|*.script |
| *.info |
| |
+---------+

l3-frontend:
Теперь может выдавать результат работы на стандартный поток вывода.
Вместо имени файла нужно указать символ -

Добавлены файлы:

l3-backend - backend-сервер
l3-cgi - CGI-обвязка для l3-frontend'а

Новые конфигурационные параметры:
frontend_css Путь к файлу CSS, используемому в HTML-странице, которую генерирует frontend
frontend_google_ico Путь к иконке google
frontend_linux_ico Путь к иконке linux
frontend_freebsd_ico Путь к иконке freebsd
frontend_opennet_ico Путь к иконке opennet
frontend_local_ico Путь к иконке локальной документации

backend_address IP-адрес интерфейса, на котором работает backend-сервер
backend_port Порт, который слушает backend-сервер
backend_pidfile Путь к файлу, который хранит идентификатор процесса backend-сервера
backend_datafile Путь к файлу хранилищу (файлу backend)
author devi
date Mon Nov 07 11:24:49 2005 +0200 (2005-11-07)
parents ba4d6515b8fd
children 450b6ac9b657
line source
1 #!/usr/bin/perl -w
3 use lib '.';
4 use l3config;
6 our @Command_Lines;
8 # vvv Инициализация переменных выполняется процедурой init_variables
9 our @Day_Name;
10 our @Month_Name;
11 our @Of_Month_Name;
12 our %Search_Machines;
13 our %Elements_Visibility;
14 # ^^^
16 sub search_buy;
17 sub make_comment;
18 sub load_command_lines_from_xml;
19 sub print_command_lines;
20 sub init_variables;
21 sub main;
23 main();
25 sub main
26 {
27 $| = 1;
29 init_variables();
30 init_config();
32 load_command_lines_from_xml($Config{"backend_datafile"});
33 print_command_lines($Config{"output"});
34 }
37 sub search_by
38 {
39 my $sm = shift;
40 my $topic = shift;
41 $topic =~ s/ /+/;
43 return "<a href='". $Search_Machines{$sm}->{"query"}."$topic'><img width='16' height='16' src='".
44 $Search_Machines{$sm}->{"icon"}."' border='0'/></a>";
45 }
47 sub make_comment
48 {
49 my $commands = $_[0];
50 my $files = $_[1];
51 chomp $commands;
52 chomp $files;
53 return if (!$commands && !$files);
55 my $comment="";
57 # Commands
58 for my $command (split /\s+/,$commands) {
59 $command =~ s/'//g;
60 my $description="";
61 eval { $description=`mywi-client '$command'`; } ;
62 $description = join ("<br>\n", grep(/\([18]\)/, split(/\n/, $description)));
63 $description =~ s/.*?-//;
64 next if $description =~ /^\s*$/;
66 my $query=$command." ".$Config{"keywords"};
67 $query =~ s/\ /+/g;
68 my $search= search_by("opennet",$query).
69 search_by("local",$command).
70 search_by("google",$query);
72 $comment .= "<tr><td class='note_title'>$command</td>".
73 "<td class='note_search'>$search</td>".
74 "</tr><tr><td width='100%' colspan='2' class='note_text'>".
75 "$description</td></tr><tr/>";
76 }
78 # Files
79 for my $file (split /\s+/,$files) {
80 $file =~ s@.*/@@;
81 $file =~ s/'//g;
82 next if $file =~ /^\s*$/;
83 next if $file =~ /^-/;
85 my $description=`mywi '$file'`;
86 $description = join ("<br>\n", grep(/\(5\)/, split(/\n/, $description)));
87 next if $description =~ /^\s*$/;
89 my $query=$file." ".$Config{"files_keywords"};
90 $query =~ s/\ /+/g;
91 my $search= search_by("opennet",$query).
92 search_by("local",$file).
93 search_by("google",$query);
95 $comment .= "<tr><td class='note_title'>$file</td>".
96 "<td class='note_search'>$search</td>".
97 "</tr><tr><td width='100%' colspan='2' class='note_text'>".
98 "$description</td></tr><tr/>";
99 }
102 return $comment;
103 }
105 =cut
106 Процедура load_command_lines_from_xml выполняет загрузку разобранного lab-скрипта
107 из XML-документа в переменную @Command_Lines
109 Предупреждение!
110 Процедура не в состоянии обрабатывать XML-документ любой структуры.
111 В действительности файл cache из которого загружаются данные
112 просто напоминает XML с виду.
113 =cut
114 sub load_command_lines_from_xml
115 {
116 my $datafile = $_[0];
118 open (CLASS, $datafile)
119 or die "Can't open file of the class ",$datafile,"\n";
120 local $/;
121 $data = <CLASS>;
122 close(CLASS);
124 for $command ($data =~ m@<command>(.*?)</command>@sg) {
125 my %cl;
126 while ($command =~ m@<([^>]*?)>(.*?)</\1>@sg) {
127 $cl{$1} = $2;
128 }
129 push @Command_Lines, \%cl;
130 }
131 }
133 =cut
134 Процедура print_command_lines выводит HTML-представление
135 разобранного lab-скрипта.
137 Разобранный lab-скрипт должен находиться в массиве @Command_Lines
138 =cut
140 sub print_command_lines
141 {
142 my $output_filename=$_[0];
144 my $course_name = $Config{"course-name"};
145 my $course_code = $Config{"course-code"};
146 my $course_date = $Config{"course-date"};
147 my $course_center = $Config{"course-center"};
148 my $course_trainer = $Config{"course-trainer"};
149 my $course_student = $Config{"course-student"};
152 # Результат выполнения процедуры равен
153 # join("", @Result{header,body,stat,help,about,footer})
154 my %Result;
155 my $toc =""; # Хранит оглавление по дням
157 $Result{"body"} = "<table width='100%'>\n";
159 my $cl;
160 my $last_tty="";
161 my $last_day="";
162 my $in_range=0;
164 for my $cl (@Command_Lines) {
166 if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) {
167 $in_range=1;
168 next;
169 }
170 if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) {
171 $in_range=0;
172 next;
173 }
174 next if ($Config{"from"} && $Config{"to"} && !$in_range)
175 ||
176 ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ )
177 ||
178 ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0)
179 ||
180 ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
182 #my @new_commands=@{$cl->{"new_commands"}};
183 #my @new_files=@{$cl->{"new_files"}};
185 my $cl_class="cline";
186 my $out_class="output";
187 if ($cl->{"class"}) {
188 $cl_class = $cl->{"class"}."_".$cl_class;
189 $out_class = $cl->{"class"}."_".$out_class;
190 }
192 my @new_commands;
193 my @new_files;
194 @new_commands = split (/\s+/, $cl->{"new_commands"}) if defined $cl->{"new_commands"};
195 @new_files = split (/\s+/, $cl->{"new_files"}) if defined $cl->{"new_files"};
197 my $output="";
198 if ($Config{"head_lines"} || $Config{"tail_lines"}) {
199 # Partialy output
200 my @lines = split '\n', $cl->{"output"};
201 # head
202 my $mark=1;
203 for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) {
204 $output .= $lines[$i]."\n";
205 }
206 # tail
207 my $start=$#lines-$Config{"tail_lines"}+1;
208 if ($start < 0) {
209 $start=0;
210 $mark=0;
211 }
212 if ($start < $Config{"head_lines"}) {
213 $start=$Config{"head_lines"};
214 $mark=0;
215 }
216 $output .= $Config{"skip_text"}."\n" if $mark;
217 for (my $i=$start; $i<= $#lines; $i++) {
218 $output .= $lines[$i]."\n";
219 }
220 }
221 else {
222 # Full output
223 $output .= $cl->{"output"};
224 }
225 #$output .= "^C\n" if ($cl->{"err"} eq "130");
227 #
228 ##
229 ## Начинается собственно вывод
230 ##
231 #
233 # <command>
235 my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time});
236 # Добавляем спереди 0 для удобочитаемости
237 $min = "0".$min if $min =~ /^.$/;
238 $hour = "0".$hour if $hour =~ /^.$/;
239 $sec = "0".$sec if $sec =~ /^.$/;
241 $class=$cl->{"out_class"};
242 $class =~ s/output$//;
245 $Result{"body"} .= "<tr class='command'>\n";
248 # DAY CHANGE
249 if ( $last_day ne $day) {
250 #$Result{"body"} .= "<td colspan='6'><p></p><h3>День ",$day,"</h4></td></tr><tr>";
251 $Result{"body"} .= "<td colspan='6'><p></p><h3 id='day$day'>".$Day_Name[$wday]."</h4></td></tr><tr>";
252 $toc .= "<li><a href='#day$day'>".$Day_Name[$wday]."</a></li>\n";
253 $last_day=$day;
254 }
256 # CONSOLE CHANGE
257 if ( $last_tty ne $cl->{"tty"}) {
258 $Result{"body"} .= "<td colspan='6'><table><tr><td class='ttychange' width='140' align='center'>".$cl->{"tty"}."</td><td/></tr></table></td></tr><tr>";
259 $last_tty=$cl->{"tty"};
260 }
262 # TIME
263 if ($Config{"show_time"} =~ /^y/i) {
264 $Result{"body"} .= "<td valign='top' class='time' width='$Config{time_width}'><pre>".
265 $hour. ":". $min. ":". $sec.
266 "</td>";
267 } else {
268 $Result{"body"} .= "<td width='0'/>"
269 }
271 # COMMAND
272 $Result{"body"} .= "<td class='script'>\n";
273 $Result{"body"} .= "<pre class='${class}cline'>\n";
274 my $cline = $cl->{"cline"};
275 $cline =~ s/\n//;
276 $Result{"body"} .= $cl->{"prompt"}.$cl->{"cline"};
277 $Result{"body"} .= "</pre>\n";
279 my $last_command = $cl->{"last_command"};
280 if (!(
281 $Config{"suppress_editors"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"editors"}}) ||
282 $Config{"suppress_pagers"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"pagers"}}) ||
283 $Config{"suppress_terminal"}=~ /^y/i && grep ($_ eq $last_command, @{$Config{"terminal"}})
284 )) {
286 $Result{"body"} .= "<pre class='".$cl->{out_class}."'>";
287 $Result{"body"} .= $output;
288 $Result{"body"} .= "</pre>\n";
289 }
291 # DIFF
292 if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) {
293 $Result{"body"} .= "<table><tr><td width='5'/><td class='diff'><pre>";
294 $Result{"body"} .= $cl->{"diff"};
295 $Result{"body"} .= "</pre></td></tr></table>";
296 }
298 # COMMENT
299 if ( $Config{"show_comments"} =~ /^y/i) {
300 my $comment = make_comment(join(" ",@new_commands), join (" ",@new_files));
301 if ($comment) {
302 $Result{"body"} .= "<table width='$Config{comment_width}'>".
303 "<tr><td width='5'/><td>";
304 $Result{"body"} .= "<table class='note' width='100%'>";
305 $Result{"body"} .= $comment;
306 $Result{"body"} .= "</table>\n";
307 $Result{"body"} .= "</td></tr></table>";
308 }
309 }
311 # Вывод очередной команды окончен
312 $Result{"body"} .= "</td>\n";
313 $Result{"body"} .= "</tr>\n";
314 }
316 $Result{"body"} .= "</table>\n";
318 $Result{"stat"} = "<hr/>";
319 $Result{"stat"} .= "<h2 id='stat'>Статистика</h2>";
320 $Result{"stat"} .= "Статистическая информация о журнале<br/>";
321 $Result{"help"} .= "<hr/>";
322 $Result{"help"} .= "<h2 id='help'>Справка</h2>";
323 $Result{"help"} .= "$Html_Help<br/>";
324 $Result{"about"} .= "<hr/>";
325 $Result{"about"} .= "<h2 id='about'>О программе</h2>";
326 $Result{"about"} .= "$Html_About";
327 $Result{"footer"} .= "</body>\n";
328 $Result{"footer"} .= "</html>\n";
330 # Заголовок генерируется позже всего
331 # Тогда, когда известно уже, что должно быть написано в
332 # оглавлении
333 $Result{"header"} = <<HEADER;
334 <html>
335 <head>
336 <meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
337 <link rel='stylesheet' href='$Config{frontend_css}' type='text/css'/>
338 </head>
339 <body>
340 <script>
341 $Html_JavaScript
342 </script>
343 <h2>Журнал лабораторных работ</h2>
345 <p>
346 Выполнил $course_student<br/>
347 Проверил $course_trainer <br/>
348 Курс $course_name ($course_code),
349 $course_date<br/>
350 Учебный центр $course_center <br/>
351 </p>
353 <ul>
354 <li><a href='#log'>Журнал</a></li>
355 <ul>$toc</ul>
356 <li><a href='#stat'>Статистика</a></li>
357 <li><a href='#help'>Справка</a></li>
358 <li><a href='#about'>О программе</a></li>
359 </ul>
361 <h2 id="log">Журнал</h2>
362 HEADER
363 $Result{"header"} .= "<table class='visibility_form'><tr><td><form>\n";
364 for my $element (keys %Elements_Visibility)
365 {
366 my @e = split /\s+/, $element;
367 my $showhide = join "", map { "ShowHide('$_');" } @e ;
368 $Result{"header"} .= "<input type='checkbox' name='$e[0]' onclick=\"$showhide\" checked>".
369 $Elements_Visibility{$element}.
370 "</input><br>\n";
371 }
373 $Result{"header"} .= "</form></td></tr></table>\n";
375 if ($output_filename eq "-") {
376 print $Result{"header"}, $Result{"body"}, $Result{"stat"}, $Result{"help"}, $Result{"about"}, $Result{"footer"};
377 }
378 else {
379 open(OUT, ">", $output_filename)
380 or die "Can't open $output_filename for writing\n";
381 print OUT $Result{"header"}, $Result{"body"}, $Result{"stat"}, $Result{"help"}, $Result{"about"}, $Result{"footer"};
382 close(OUT);
383 }
384 }
391 sub init_variables
392 {
393 $Html_Help = <<HELP;
394 Справка по использованию журнала
395 HELP
397 $Html_About = <<ABOUT;
398 <p>
399 LiLaLo (L3) расшифровывается как Live Lab Log.<br/>
400 Программа разработана для повышения эффективности обучения<br/>
401 Unix/Linux-системам.<br/>
402 (c) Игорь Чубин, 2004-2005<br/>
403 </p>
404 ABOUT
405 $Html_About.='$Id$ </p>';
407 $Html_JavaScript = <<JS;
408 function getElementsByClassName(Class_Name)
409 {
410 var Result=new Array();
411 var All_Elements=document.all || document.getElementsByTagName('*');
412 for (i=0; i<All_Elements.length; i++)
413 if (All_Elements[i].className==Class_Name)
414 Result.push(All_Elements[i]);
415 return Result;
416 }
417 function ShowHide (name)
418 {
419 elements=getElementsByClassName(name);
420 for(i=0; i<elements.length; i++)
421 if (elements[i].style.display == "none")
422 elements[i].style.display = "";
423 else
424 elements[i].style.display = "none";
425 //if (elements[i].style.visibility == "hidden")
426 // elements[i].style.visibility = "visible";
427 //else
428 // elements[i].style.visibility = "hidden";
429 }
430 function filter_by_output(text)
431 {
433 var jjj=0;
435 elements=getElementsByClassName('command');
436 for(i=0; i<elements.length; i++) {
437 subelems = elements[i].getElementsByTagName('pre');
438 for(j=0; j<subelems.length; j++) {
439 if (subelems[j].className = 'output') {
440 var str = new String(subelems[j].nodeValue);
441 if (jjj != 1) {
442 alert(str);
443 jjj=1;
444 }
445 if (str.indexOf(text) >0)
446 subelems[j].style.display = "none";
447 else
448 subelems[j].style.display = "";
450 }
452 }
453 }
455 }
456 JS
458 %Search_Machines = (
459 "google" => { "query" => "http://www.google.com/search?q=" ,
460 "icon" => "google.ico" },
461 "freebsd" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=",
462 "icon" => "freebsd.ico" },
463 "linux" => { "query" => "http://man.he.net/?topic=",
464 "icon" => "linux.ico"},
465 "opennet" => { "query" => "http://www.opennet.ru/search.shtml?words=",
466 "icon" => "opennet.ico"},
467 "local" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=",
468 "icon" => "freebsd.ico" },
470 );
472 %Elements_Visibility = (
473 "note" => "замечания",
474 "diff" => "редактор",
475 "time" => "время",
476 "ttychange" => "терминал",
477 "wrong_output wrong_cline wrong_root_output wrong_root_cline"
478 => "команды с ошибками",
479 "interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline"
480 => "прерванные команды",
481 "tab_completion_output tab_completion_cline"
482 => "продолжение с помощью tab"
483 );
485 @Day_Name = qw/ Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота /;
486 @Month_Name = qw/ Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь /;
487 @Of_Month_Name = qw/ Января Февраля Марта Апреля Мая Июня Июля Августа Сентября Октября Ноября Декабря /;
488 }