source: PSPA/madxPSPA/testing/MadWindowsCompileClient.pl @ 476

Last change on this file since 476 was 430, checked in by touze, 11 years ago

import madx-5.01.00

File size: 15.0 KB
Line 
1#!/usr/bin/perl
2
3# assume a Perl script in charge of compiling MAD on the Windows platform
4# is waiting on an agreed-upon socket => the Server must be started before
5# the Client, if not the program should return an error otherwise it would
6# wait for ever!
7
8# accept an argument manual-trigger or automatic-trigger, the former meaning we
9# immediately trigger the compilation, the later meaning the program will listen
10# to port 7075 for a request emanating from the automatic build and test procedure.
11
12#$debug = 'no'; # global variable also used by notify() subroutine and others
13# this program only for tests => set $debug='yes' from 10 June 2009
14$debug = 'yes';
15
16
17if ($#ARGV!=0){
18    die "expect one argument: 'now' or 'wait-for-trigger'\n";
19} else {
20    if ($ARGV[0] eq 'now'){
21        $mode = 'now';
22    } else {
23        if ($ARGV[0] eq 'wait-for-trigger') {
24            $mode = 'wait-for-trigger';
25        } else {
26            die "incorrect argument: should be either 'now' or 'wait-for-trigger'\n";
27        }
28    }
29   
30}
31
32
33# KILL ITSELF IN CASE ALREADY RUNNING
34
35my @check = `ps -aef | grep MadWindowsCompileClient.pl`;
36foreach $line (@check){
37    chop $line;
38    $line =~ /^\w+[\s\t]+(\d+)/; # format of output: username pid parent ...
39    my $pid = $1; # pid of the process being considered
40    my $myPid = $$; # $$ is the pid of this process (special perl variable!#@?)
41   
42    if ($pid ne $myPid){
43        # check this is indeed a perl command
44        if ($line =~ /\/usr\/bin\/perl[\s\t]/) {
45            # `kill -9 $pid`; # kill process, unless this is this process's pid
46            # no: instead should kill itself
47            if ($mode eq 'now') {
48                my $warning = "MadWindowsCompileClient.pl already running => abort new process.\n";
49                print $warning;
50                notify($warning);
51            } # otherwise don't print the message otherwise the cron job
52            # will send an e-mail
53            exit;
54        } else {
55            # skip
56        }
57    } # later-on should send a signal that this process would either accept
58    # to kill itself, or reject in case it is already engaged in a compilation
59    # on the Windows platform => before dying, close the listening socket port
60}
61
62
63
64# fork process to spawn a branch that will periodically refresh the AFS kerberos tokens
65my $child_pid = fork();
66
67if (not defined $child_pid){
68    notify("no system resources to fork process => exit");
69    exit;
70}
71
72if ($child_pid==0){
73    # this is the child process
74    # refresh the AFS token every 6 hours. Otherwise the token
75    # would expire after 25 hours.
76    # (note that this trick works for up to 10 days according to IT support)
77    my $start = localtime;
78    # the recipe for refresing AFS/Kerberos tokens through a child process is known to work
79    # up to ten days. As a consequence, this process should die gracefully before ten days
80    # of running and wait for the acrontab to relaunch it with a time of grain of 5 min
81    # Hence the NFS client that triggs the remote compilation should have a time-out of the
82    # same order.
83    my $counter = 0;
84    while(1){
85        $counter ++; # increment every 6 hours
86        if ($counter > (4*5)) { # life-expectancy set to 5 days (could as well try with 9 days)
87            # time to die gracefully and let the acrontab restart a new process with
88            # fresh AFS and Kerberos tokens. In the meantime, the trigger should be able
89            # to wait for the client socket port to reappear...
90            # should also kill the parent process...
91            my $parent_pid = getppid();
92            kill 9, $parent_pid;
93            exit;
94        }
95        my $now = localtime;
96        sleep 21600; # 6 hours
97        `/usr/sue/bin/kinit -R`;
98        `/usr/bin/aklog`;
99        # check if the child process' parent is dead. If so, should kill itself
100        my $parent_pid = getppid(); # get parent's pid
101        $cnt = kill 0, $parent_pid;
102        if ($cnt == 0){
103            exit;
104        }
105    }
106
107}
108
109# else ...
110
111if ($child_pid){
112    # non-zero pid means we are in the parent process, which received the child's pid
113
114
115
116    # output of this program on stdout: SUCCESS or FAILURE:<message>
117
118    # this program must run on a machine such as abcopl1 on which NFS mounted partitions
119    # can be viewed through Samba by the remote Windows machine.
120
121    my $windowsHost = 'abpc10788';
122    # $windowsHost = 'abcopl1'; # 29 september 2009 - for test purposes
123
124    $executablesAfsWebFolder = "/afs/cern.ch/user/n/nougaret/www/mad/windows-binaries"; # global
125
126    $madForWindowsSambaFolder = "/user/nougaret/MAD-X-WINDOWS/madX"; 
127    # problem: won't be seen on pcslux99!!! => cannot automate fully !!!
128    # => for the time-being this process will need to be launched manually.
129
130    # where binaries are delivered on the web for subsequent retreival by users
131
132    $madWindowsCompilationDir = $madForWindowsSambaFolder; # global used by other routines
133    $madWindowsDeliveryDir = "/afs/cern.ch/user/n/nougaret/www/mad/windows-binaries"; # global
134    # also used by other routines
135    @windowsTargets = ('madx.exe','madxp.exe','mpars.exe'); # Windows/DOS deliverables
136    # above is global as used by other routines as well
137
138    use IO::Socket::INET;
139    use MIME::Lite;
140    use Sys::Hostname;
141
142
143    # two local and remote ports for communication with the Windows host in the two directions
144    $socketPortWindows = 7070; # agreed-up with client (>1024 for non-root)
145    $socketPortLinux = 7071; # could be the same as above
146    # one local port to listen for requests emanating from the automated build and test process
147    my $listeningPort = 7075;
148
149
150    my $thisLinuxHost = hostname;
151
152
153    # before asking the Windows host to trigger the compilation, we must first make sure that the
154    # Samba folder MAD-X-WINDOWS/madX contains the latest CVS (more precisely latest released tagged
155    # version - for the time being, we'll simply pick-up the latest contents of the repository)
156    # ... well the `cvs update` is actually carried-out in sub updateMadForWindowsSambaFolder() !!!
157
158    # wait to be waken-up (or start right now if mode is 'now')
159
160    INFINITE_LOOP: while(1){
161
162        my $invokeCompilation = 0; # default = do nothing
163
164        if ($mode eq 'wait-for-trigger'){
165            my $listeningSocket = new IO::Socket::INET(
166                                                       LocalHost => $thisLinuxHost,
167                                                       LocalPort => $listeningPort,
168                                                       Proto => 'tcp',
169                                                       Listen => 1,
170                                                       Reuse => 1,
171                                                       ) or die "Can't bind : $@\n";
172
173            unless ($listeningSocket) {
174                notify("failed to open TCP socket $listeningPort on $thisLinuxHost to receive command => will die\n");
175                die 'failed to open TCP socket $listeningPort on $thisLinuxHost';
176            }
177
178            my $receive = $listeningSocket->accept();
179            while (<$receive>){
180                if (/^Trigger Windows compilation$/){
181                    # sent triggering signal via socket to Windows compilation server
182                    $invokeCompilation = 1;
183                    last;
184                }
185            }
186            close $listeningSocket;
187        } # else mode is 'now' and we should trigger the remote compilation right now
188       
189       
190        if ($mode eq 'now'){
191            $invokeCompilation = 1 ;
192        }
193
194
195        if ($invokeCompilation == 1){
196            notify("MadWindowsCompileClient.pl will now forward the compilation request to the Windows host machine.");
197
198            updateMadForWindowsSambaFolder();
199
200
201
202            # $thisLinuxHost = 'abcopl1';
203            # print "the Linux box is '$thisLinuxHost'\n";
204
205            my $sock = new IO::Socket::INET ( 
206                                              PeerAddr => $windowsHost,
207                                              PeerPort => $socketPortWindows,
208                                              Proto => 'tcp'
209                                              ); 
210
211            unless ($sock) {
212                notify("Could not create socket $socketPortWindows to connect to $windowsHost => will die\n");
213                die "Could not create socket: $!\n" unless $sock; 
214            }
215
216            print "will now send message to port $socketPortWindows of $windowsHost\n";
217       
218            print $sock "$thisLinuxHost asks: Compile MAD for Windows!\n";
219       
220            $startTime = localtime; # global
221            $endTime; # global, will be set later-on
222       
223            close($sock);
224       
225            # now wait for the message signalling that the compilation completed
226
227
228
229            my $clientSock = new IO::Socket::INET(
230                                                  LocalHost => $thisLinuxHost,
231                                                  LocalPort => $socketPortLinux,
232                                                  Proto => 'tcp',
233                                                  Listen => 1,
234                                                  Reuse => 1
235                                                  );
236
237            die "Could not create client socket: $!\n" unless $clientSock;
238           
239            print "$thisLinuxHost accepts messages sent through socket $socketPortLinux\n";
240            my $newClientSock = $clientSock->accept();
241
242
243           
244            while (<$newClientSock>){
245                print $_;
246                if (/Compilation completed/){
247                    $endTime = localtime;
248                    print "OK: the compilation completed on Windows side\n";
249                    checkWindowsCompilationOutcome();
250                    print "=> installed the executables in the AFS web folder\n";
251                    last INFINITE_LOOP; # leave the while loop
252                }
253
254                # should leave loop (timeout) in case there's no reply by the Windows-side server,
255                # in which case, the executables will need to be delivered manually
256
257            }
258       
259            close ($clientSock);
260           
261        } # if $invokeCompilation == 1
262
263        if ($mode eq 'now') {
264            # should compile only once and then leave the infinite loop to complete the program
265            last;
266        } else {
267            print "now wait for wake-up by next compilation-triggering signal\n";
268        }
269
270        # debug:
271        my $whereAmI = `pwd`;
272        notify("at the end of the compilation, the Linux box client is in '$whereAmI'\n");
273
274    } # while(1): wait forever to be woken-up by automated build-and-test program
275
276    # do we really need to kill the child process?
277    # in principle not, but the child would commit suicide only 6 hours later due to loop duration
278    kill 9, $child_pid;
279
280} # this is the parent process (not the child forked process refreshing AFS/Kerberos tokens)
281
282
283sub checkWindowsCompilationOutcome {
284    my $initialDir = `pwd`;
285    # check the delivery directory contents
286    foreach $target (@windowsTargets){
287        # check that the executable has been created within the last hour
288        my $ls = `ls -l $madWindowsCompilationDir/$target`;
289
290        # debug
291        notify("for target '$target', we see : '$ls'");
292
293        # pick the date and time at which the executables have been created
294        $ls =~ /(\w{3})[\s\t]+(\d{1,2})[\s\t]+(\d+:\d+)[\s\t]/ ;
295
296        my $month = $1;
297        my $day = $2;
298        my $time = $3;
299
300        my $now = localtime;
301        print "now=$now\n";
302
303        $now =~ /^\w{3}[\s\t]+(\w{3})[\s\t]+(\d{1,2})[\s\t]+(\d+:\d+:)\d+/ ;
304        # forget about the year...
305
306        my $monthNow = $1;
307        my $dayNow = $2;
308        my $time = $3;
309
310        # debug
311        notify("monthNow is '$monthNow', month is '$month', dayNow is '$dayNow', day is '$day'");
312#       if (0){ # for the time being, always deliver the executables, without checking anything
313        if (($monthNow != $month)||($dayNow != $day)){
314            print "Mistmatch of day and month => executables were not created\n";
315        } else {
316            # now check that compilation occured within on hour from now
317           
318            # now install the executables in the AFS web folder
319            my $source = "$madWindowsCompilationDir/$target";
320            my $destination = "$madWindowsDeliveryDir/$target";
321            my $result = `cp $source $destination`;
322           
323            # debug:
324            notify("just copied '$source' into '$destination' => outcome = '$result'");
325
326        }
327
328
329    } # for each $target (@windowsTargets)
330
331    # if everything ok...
332
333
334
335
336    # now notify that the Windows executables are ready
337    my $grepVersion = `grep myversion $madWindowsCompilationDir/madxd.h`; # hard-coded !?
338
339    # debug:
340    notify("now grep my version in '$madWindowsCompilationDir/madx.h'");
341
342    $grepVersion =~ /MAD-X (\d+\.\d+\.\d+)/;
343    $madVersion = $1; # global, also used in subroutine 'deliverHtmlPage';
344
345    deliverHtmlPage();
346   
347
348    if ($debug eq 'no') {
349        my $msg = MIME::Lite->new(
350                                  From => 'Jean-Luc.Nougaret@cern.ch',
351                                  To => 'mad-windows-watchers@cern.ch',
352#                                 To => 'Jean-Luc.Nougaret@cern.ch',
353                                  Subject => 'MAD-X for Windows updated',
354                                  Data => "Dear colleagues,\n\nPlease take note that MAD-X version $madVersion is now available on Windows.\n\nThe new releases are available for download on the new Web page:\nhttps://test-mad-automation.web.cern.ch/test-mad-automation/windows-binaries/executables.htm\n\nRegards,\nJean-Luc"
355                                  );
356        $msg->send;
357    } else {
358        notify("MAD-X for Windows has been updated");
359    }
360   
361    chdir($initialDir);
362
363} # subroutine checkWindowsCompilationOutcome
364
365
366sub deliverHtmlPage {
367   
368    # at this stage, the Windows binaries have been delivered to the
369    # AFS web folder already
370
371    my $htmlFile = "$executablesAfsWebFolder/executables.htm"; 
372    # only for Windows?
373
374    my $contents =''; # blank at first
375
376    # grep size of the binaries located in the AFS web folder
377    my @binaries = `ls -l $executablesAfsWebFolder/*.exe`;
378
379#    my $nBinaries = scalar(@binaries);
380#    notify("in '$executablesAfsWebFolder', 'found $nBinaries'");
381       
382    $contents .= "Version $madVersion compiled with Lahey Fortran and Microsoft Visual C++:\n";
383    $contents .= "<table width=\"75%\" border=\"0\">\n";
384    my $oddOrEven = 'even'; # to colorize successive lines differently
385    foreach $binary (@binaries){
386        chop $binary; # end of line
387#       notify("line:$binary");
388#       notify("in '$executablesAfsWebFolder', 'found $binary'");
389# -rw-r--r--  1 nougaret pz  658664 Oct  1 12:06 /afs/cern.ch/user/n/nougaret/www/mad/windows-binaries/mpars.exe
390        $binary =~ /(\d+)[\s\t]+(\w{3})[\s\t]+(\d{1,2})[\s\t]+(\d+:\d+)[\s\t]+[^\s]+\/(\w+\.exe)$/;
391        my $size = $1;
392        my $megabytes = $size / 1000000;
393        my $month = $2;
394        my $day = $3;
395        my $time = $4;
396        my $executable = $5;
397#       notify("size='$size',exec='$executable',descr='$description{$executable}'");
398        $description{'madx.exe'} = "standard version";
399        $description{'madxp.exe'} = "version including PTC";
400        $description{'mpars.exe'} = "\"parser-only\" version";
401        if ($oddOrEven eq 'odd'){
402            $oddOrEven = 'even';
403        } else {
404            $oddOrEven = 'odd';
405        }
406        $contents .= "<tr class=\"$oddOrEven\"><td>Download</td><td><a href=\"./$executable\">$executable</a></td><td>($megabytes Megabytes)</td><td>for the $description{$executable}.</td></tr>\n";
407    }
408    $contents .= "</table>\n";
409
410    # create web page in the correct AFS web folder location
411    my $html = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">';
412    $html .= "<html>\n";
413    $html .= "<head>\n";
414    $html .= "<title>MAD-X downloadable executables</title>\n";
415    $html .= "<link rel=stylesheet href=\"../MadTestWebStyle.css\" type=\"text/css\">"; # CSS stylesheet one level up
416    $html .= "</head>\n";
417    $html .= "<!-- generated by Windows compilation script -->\n";
418    $html .= "<body>\n";
419    $html .= "<p>Windows compilation started $startTime, ended $endTime</p>\n";
420    $html .= $contents;
421    $html .= "</body>\n";
422    $html .= "</html>\n";
423    open(OUTHTML, ">$htmlFile");
424    print OUTHTML $html;
425    close OUTHTML;
426
427    # debug
428    notify("created file '$htmlFile'");
429   
430    # now move HTML file into the AFS target web folder
431   
432}
433
434
435sub updateMadForWindowsSambaFolder{
436    my $localDir = `pwd`;
437    chdir($madForWindowsSambaFolder);
438    # ideally we should do a complete clean-up here.
439    print "invoke CVS update in $madForWindowsSambaFolder. Ideally should do a complete clean-up before\n";
440    `cvs update`;
441    $cvsStatus = `cvs status`;
442    if ($debug eq 'yes'){
443        notify("outcome of `cvs update`: $cvsStatus");
444    }
445    chdir ($localDir); # back to where we were before entering the sub
446
447}
448
449
450sub notify{
451    if ($debug eq 'yes') {
452        my $message = $_[0];
453        my $msg = MIME::Lite->new(
454                                  From => 'MAD-X Windows compilation robot',
455                                  ReplyTo => 'Jean-Luc.Nougaret@cern.ch',
456                                  To => 'Jean-Luc.Nougaret@cern.ch',
457                                  Subject => 'automatic notification',
458                                  Data => $message
459                                  );
460        $msg->send;   
461    } # else do nothing
462
463}
Note: See TracBrowser for help on using the repository browser.