Merged in Governor-Tarkin/swg-src (pull request #17)
[swg-src.git] / tools / build_cluster_controller.pl
blobafe019180fe5a739859d62eb4f82d893afac9715
1 #! /usr/bin/perl
2 # ======================================================================
3 # ======================================================================
5 use strict;
6 use warnings;
7 use Socket;
8 use File::Copy;
10 # ======================================================================
11 # Constants
12 # ======================================================================
14 use constant START_COMMUNICATION => "S";
15 use constant START_NOT_LOCKED_READY => "B";
16 use constant START_LOCKED_READY => "C";
17 use constant START_LOCKED_READY_UNAUTHORIZED_USER => "L";
18 use constant START_ERROR_AUTHORIZING => "K";
20 use constant END_COMMUNICATION => "E";
21 use constant END_SUCCESSFULL => "F";
23 use constant SUCCESSFULL_COMMAND => "P";
24 use constant FAILED_COMMAND => "U";
25 use constant UPDATE_BOOTLEG_STEP_OK => "G";
26 use constant UPDATE_BOOTLEG_STEP_FAILED => "H";
27 use constant UPDATE_BOOTLEG_SEND_DIRECTORY => "M";
28 use constant UPDATE_BOOTLEG_SEND_FILE => "N";
29 use constant UPDATE_BOOTLEG_FILES_FINISHED => "Q";
30 use constant SNAPSHOT_FAILED => "O";
31 use constant SNAPSHOT_SUCCESSFULL => "P";
33 use constant COMMAND_RESTART => "a";
34 use constant COMMAND_RESTART_LOGIN => "b";
35 use constant COMMAND_RESTART_NODES => "c";
36 use constant COMMAND_LOCK => "d";
37 use constant COMMAND_UNLOCK => "e";
38 use constant COMMAND_UPDATE_BOOTLEG => "f";
39 use constant COMMAND_CONTENT_SYNC => "g";
40 use constant COMMAND_SYNC_SPECIFIED_CHANGELISTS => "h";
41 use constant COMMAND_SNAPSHOT => "i";
42 use constant COMMAND_BOOTLEG_VERSION => "j";
43 use constant COMMAND_FREE_OBJECT_IDS => "k";
45 # ======================================================================
46 # Globals
47 # ======================================================================
49 my $buildCluster = "swo-dev9.station.sony.com";
50 my $port = "98452";
51 my $candela = "p:";
52 my $exitcode = 0;
54 my $name = $0;
55 $name =~ s/^(.*)\\//;
57 my $option;
58 my $command;
59 my $user;
61 # ======================================================================
62 # Subroutines
63 # ======================================================================
65 sub usage
67 print STDERR "\nUsage:\n";
68 print STDERR "\t$name [command(s)]\n\n".
69 "\t\t-restart :\n\t\t\t restart the build cluster (central node)\n".
70 "\t\t-restart-login :\n\t\t\t restart the Login server\n".
71 "\t\t-restart-nodes :\n\t\t\t restart all nodes of the build cluster\n".
72 "\t\t-lock :\n\t\t\t lock the build cluster (must be authorized user)\n".
73 "\t\t-unlock :\n\t\t\t unlock the build cluster (must be authorized user)\n".
74 "\t\t-update-bootleg <branch> :\n\t\t\t update the bootleg on the build cluster (p4 key check) - needs to be run in windows\n".
75 "\t\t-bootleg-version:\n\t\t\t check bootleg version on the build cluster\n".
76 "\t\t-free-object-ids :\n\t\t\t free object IDs in the database for the build cluster\n".
77 "\t\t-content-sync [changelist] :\n\t\t\t shut down, content sync to specific changelist (if none, content sync to head), bring up\n".
78 "\t\t-sync-specified-changelists <changelist [| changelist]> :\n\t\t\t shut down, sync to multiple specified changelists, bring up\n".
79 "\t\t-snap <schema> <branch> [dontsubmit] :\n\t\t\t free object IDs, make a snapshot, verifies before adding files to <branch> in perforce\n\t\t\t and submits unless [dontsubmit]\n".
80 "\t\t\t If <schema> does not exist, it is created otherwise it is overwritten\n".
81 "\n\tIf multiple commands are given, the build cluster will go down / come up only once around the commands (if necessary)\n";
82 die "\n";
85 sub exitFailed
87 $exitcode = 1;
88 goto FAIL;
91 sub perforceWhere
93 local $_;
95 # find out where a perforce file resides on the local machine
96 my $result;
98 open(P4, "p4 where $_[0] |");
99 $_ = <P4>;
100 chomp;
101 my @where = split;
102 $result = $where[2];
103 close(P4);
106 return $result;
109 sub checkarguments()
111 my @args = @ARGV;
113 while(@args)
115 my $elem = shift @args;
117 # check if the key is valid if the command requires one
118 if($elem =~ /^-snap$/)
120 $elem = shift @args;
121 &usage() if(!(defined $elem) || $elem =~ /^-/);
122 $elem = shift @args;
123 &usage() if(!(defined $elem) || $elem =~ /^-/);
124 # check for optional parameter
125 shift @args if((defined $args[0]) && $args[0] eq "dontsubmit");
127 elsif($elem =~ /^-update-bootleg$/)
129 $elem = shift @args;
130 &usage() if(!(defined $elem) || $elem =~ /^-/);
132 elsif($elem =~ /^-content-sync$/)
134 shift @args if(@args && !($args[0] =~ /^-/));
136 elsif($elem =~ /^-sync-specified-changelists$/)
138 $elem = shift @args;
139 &usage() if(!defined $elem || $elem =~ /^-/);
140 while(@args)
142 last if($args[0] =~ /^-/);
143 shift @args;
146 elsif(!($elem =~ /^-restart$/ || $elem =~ /^-restart-login$/ || $elem =~ /^-restart-nodes$/ || $elem =~ /^-lock$/ || $elem =~ /^-unlock$/ || $elem =~ /^-bootleg-version$/ || $elem =~ /^-free-object-ids$/ || $elem =~ /^-build_script_publish$/))
148 &usage();
153 sub openbuildsocket
155 socket(BUILD, PF_INET, SOCK_STREAM, getprotobyname('tcp')) || die "socket failed\n";
157 my $destination = inet_aton($buildCluster) || die "inet_aton failed\n";
158 my $paddr = sockaddr_in($port, $destination);
159 connect(BUILD, $paddr) || die "connect failed\n";
161 # unbuffer the socket
162 my $oldSelect = select(BUILD);
163 $| = 1;
164 select($oldSelect);
166 # put the socket into binary mode
167 binmode BUILD;
171 sub getuser
173 my $user;
175 open(P4, "p4 user -o |") || die "p4 user failed\n";
176 while(<P4>)
178 $user = $1 if(/^User:\s+(\S+)/);
180 close(P4);
182 die "Could not get perforce user\n" if(!defined $user);
184 return $user;
187 sub sendstartinfo
189 print STDERR "Contacting build cluster...\n";
190 print BUILD START_COMMUNICATION;
192 my $initializer = $user;
193 $initializer = "buildscript" if($user eq "build_script_publish");
195 my $length = length $initializer;
197 print BUILD pack("N", $length);
198 print BUILD $initializer;
200 my $returncode;
201 if(read(BUILD, $returncode, 1) != 1)
203 print STDERR "Problem contacting build server\n";
204 return 0;
207 if($returncode eq START_NOT_LOCKED_READY)
209 print STDERR "Build server is not locked and ready\n\n";
210 return 1;
212 elsif($returncode eq START_LOCKED_READY)
214 print STDERR "Build server is locked and ready\n\n";
215 return 1;
217 elsif($returncode eq START_LOCKED_READY_UNAUTHORIZED_USER)
219 print STDERR "Build server is locked (limited access for non-authoritative user)\n\n";
220 return 1;
222 elsif($returncode eq START_ERROR_AUTHORIZING)
224 print STDERR "problem authorizing $user for build server\n\n";
225 return 0;
227 else
229 print STDERR "Build server not ready\n\n";
230 return 0;
235 sub sendendinfo
237 print STDERR "Ending communication with build cluster...\n";
238 print BUILD END_COMMUNICATION;
240 my $returncode;
241 my $readreturn = read(BUILD, $returncode, 1);
242 if(!defined $readreturn || $readreturn != 1)
244 print STDERR "Build server communication ended abruptly\n";
245 return 0;
248 if($returncode eq END_SUCCESSFULL)
250 print STDERR "Build server communication ended successfully\n";
251 return 1;
253 else
255 print STDERR "Build server communication ended with errors\n";
256 return 0;
261 sub contentsync
263 my $changelist = "";
265 $changelist = shift @ARGV if(@ARGV && !($ARGV[0] =~ /^-/));
267 my $length = length $changelist;
269 print BUILD pack("N", $length);
270 print BUILD $changelist;
272 # Recieve any errors from the content sync
273 my $buffer;
274 return 0 if(read(BUILD, $buffer, 4) != 4);
275 $length = unpack("N", $buffer);
276 return 0 if(read(BUILD, $buffer, $length) != $length);
277 print $buffer;
279 return 1;
282 sub syncspecifiedchangelists
284 my $changelists = "";
286 while(@ARGV)
288 last if($ARGV[0] =~ /^-/);
290 my $elem = shift @ARGV;
291 $changelists .= "$elem ";
294 chomp $changelists;
296 if($changelists eq "")
298 print BUILD pack("N", 0);
299 print STDERR "You must specify changelist\(s\)\n";
300 return 0;
303 my $length = length $changelists;
305 print BUILD pack("N", $length);
306 print BUILD $changelists;
308 return 1;
311 sub endsubmit
313 print "Error running: $_[0]\n";
314 return 0;
317 sub submitopenfiles
319 my $dontsubmit = shift;
320 local $_;
321 my @files;
323 system("p4 revert -a > /dev/null");
325 open(P4, "p4 -ztag opened -c default |");
327 while (<P4>)
329 chomp;
330 push (@files, $_) if (s/^\.\.\. depotFile //);
333 close(P4);
335 if(!@files)
337 print STDERR "No changed files, nothing to submit\n";
338 return 1;
341 my $tmpfile = "submit.tmp";
343 # submit all the open files
344 open(TMP, ">" . $tmpfile);
346 print TMP "Change:\tnew\n";
347 print TMP "\nDescription:\n";
349 foreach (@_)
351 print TMP "\t", $_, "\n";
354 print TMP "\nFiles:\n";
355 foreach (@files)
357 print TMP "\t", $_, "\n";
360 close(TMP);
362 if ($dontsubmit)
364 open(P4, "p4 change -i < $tmpfile |") || return 0;
365 while(<P4>)
367 print STDERR "Successfully created changelist $1\n" if(/Change (\d+) created/);
369 close(P4);
371 else
373 open(P4, "p4 submit -i < $tmpfile |") || return 0;
374 while(<P4>)
376 print STDERR "Successfully submitted at changelist $1\n" if(/Change (\d+) submitted/);
378 close(P4);
381 return 0 if ($? != 0);
382 unlink($tmpfile);
383 return 1;
386 sub snapshot
388 my $dbSchema = shift @ARGV;
389 my $branch = shift @ARGV;
390 my $dontsubmit = 0;
391 my $snapshotLog = "";
392 my $buffer = "";
393 my $p4operation = "submit";
395 if (defined($ARGV[0]) && $ARGV[0] eq "dontsubmit")
397 $dontsubmit = 1;
398 $p4operation = "change";
399 shift @ARGV;
402 print BUILD pack("N", length $dbSchema);
403 print BUILD $dbSchema;
405 if(read(BUILD, $buffer, 1) != 1 || $buffer eq SNAPSHOT_FAILED)
407 print STDERR "Snapshot not created successfully on the build cluster\n";
408 return 0;
411 # Recieve files
412 my @worldSnapshots;
414 print STDERR "Snapshot generation complete.\n";
415 while(1)
417 return 0 if (read(BUILD, $buffer, 2*4) != 2*4);
418 my ($fileSize, $fileNameLength) = unpack("NN", $buffer);
420 # check if we are finished
421 last if($fileSize == 0 && $fileNameLength == 0);
423 my $localFileName;
424 return 0 if (read(BUILD, $localFileName, $fileNameLength) != $fileNameLength);
426 # first file sent will be the snapshot log
427 $snapshotLog = $localFileName if($snapshotLog eq "");
429 # add all ws files to the array
430 push @worldSnapshots, $localFileName if($localFileName =~ /\.ws$/);
432 # receive the binary bits for the file
433 print STDERR "Receiving $localFileName ($fileSize bytes)...";
434 unlink $localFileName;
435 open(F, ">" . $localFileName) || return endbootleginstall("could not open $localFileName for writing");
436 binmode(F);
437 while ($fileSize)
439 my $readSize = 16 * 1024;
440 $readSize = $fileSize if ($fileSize < $readSize);
441 my $readResult = read(BUILD, $buffer, $readSize);
442 return 0 if (!defined($readResult));
443 return 0 if ($readResult == 0);
444 print F $buffer;
445 $fileSize -= $readResult;
447 return 0 if ($fileSize != 0);
448 close(F);
449 print "done\n";
452 # Echo log to user
453 print STDERR "--- Start of snapshot log:\n";
454 system("cat $snapshotLog") == 0 || return 0;
455 print STDERR "--- End of snapshot log:\n";
457 # Only verify using STDIN if we are not being called by the build script
458 if($user ne "build_script_publish")
460 print STDERR "\nAre the world snapshots ok to do perforce $p4operation? (y/n)\n";
461 while(<STDIN>)
463 chomp;
465 if($_ eq "y" || $_ eq "Y")
467 last;
469 elsif($_ eq "n" || $_ eq "N")
471 return 1;
473 print STDERR "Please enter \'y\' or \'n\'\n";
477 # If we get here, we have decided to submit
478 print STDERR "Proceeding with $p4operation\n";
480 # Get a hash of the current world snapshots in perforce
481 my %ws;
482 open(FILES, "p4 files //depot/swg/$branch/data/sku.0/sys.client/built/game/snapshot/... |") || return endsubmit("p4 files");
483 while(<FILES>)
485 $ws{$1} = 1 if(/\/([^\/]+\.ws)#/);
487 close(FILES);
489 # Edit files and move to appropriate directory
490 system("p4 edit //depot/swg/$branch/data/sku.0/sys.client/built/game/snapshot/...") == 0 || return endsubmit("p4 edit snapshot dir");
492 foreach(@worldSnapshots)
494 system("p4 add //depot/swg/$branch/data/sku.0/sys.client/built/game/snapshot/$_") == 0 || return endsubmit("p4 add") if(!exists($ws{$_}));
495 copy($_, perforceWhere("//depot/swg/$branch/data/sku.0/sys.client/built/game/snapshot/$_")) || return endsubmit("moving *.ws to snapshot dir");
498 system("p4 edit //depot/swg/$branch/dsrc/sku.0/sys.client/built/game/snapshot/swg_object.txt") == 0 || return endsubmit("p4 edit swg_object.txt");
499 copy("swg_object.txt", perforceWhere("//depot/swg/$branch/dsrc/sku.0/sys.client/built/game/snapshot/swg_object.txt")) || return endsubmit("moving object file to swg_object.txt");
501 # create golddata text file
502 createGoldDataFile($dbSchema, $branch);
504 submitopenfiles($dontsubmit, "[automated]", "New snapshots for $branch from build_cluster_controller ($dbSchema)") || return endsubmit("p4 $p4operation");
506 return 1;
509 sub createGoldDataFile
511 my ($dbSchema, $branch) = @_;
512 my $goldDataFile = perforceWhere("//depot/swg/$branch/src/game/server/database/build/linux/golddata.txt");
514 system("p4 edit $goldDataFile");
516 open(GOLDDATA, "> $goldDataFile");
517 print GOLDDATA "$dbSchema\n";
518 close GOLDDATA;
520 system("p4 add $goldDataFile");
523 sub getbootlegversion
525 my $buffer;
526 return 0 if(read(BUILD, $buffer, 4) != 4);
527 my $length = unpack("N", $buffer);
528 return 0 if(read(BUILD, $buffer, $length) != $length);
530 if($length == 0)
532 print STDERR "Could not get build cluster bootleg version\n";
533 return 0;
536 print STDERR "Build cluster bootleg version is: $buffer\n";
537 return 1;
540 sub updatebootleg
542 my $branch = shift @ARGV;
544 # Get the number of the most recent bootleg
545 my $dir = "$candela/swo/builds/$branch";
547 my $buffer;
548 my $change = 0;
549 opendir DH, $dir or return 0;
550 foreach (readdir DH)
552 $change = $_ if(/^\d+$/ && -d ($dir."/".$_) && $_ > $change);
554 closedir DH;
555 return 0 if(!$change);
557 print STDERR "Most recent blessed bootleg is: $change\n";
559 # Send info to build cluster
560 print STDERR "Syncing build cluster to $change...\n";
561 print BUILD pack("N", $change);
562 return 0 if(read(BUILD, $buffer, 1) != 1 || $buffer ne UPDATE_BOOTLEG_STEP_OK);
563 print STDERR "Sync of build cluster complete.\n";
565 # Compress the server binaries
566 my $file = "servers_debug.tar.gz";
568 print STDERR "Compressing server binaries...\n";
569 system("tar --create --gzip --directory=$dir/$change/servers_debug --file=/tmp/$file .") == 0 || die "Failed to compress $dir/$change/servers_debug";
570 print STDERR "Compress server binaries complete.\n";
572 # Send file to build cluster
573 die "Can't find server zip file!\n" if (!-s "c:/cygwin/tmp/$file");
574 my $fileSize = -s "c:/cygwin/tmp/$file";
575 print STDERR "Sending file $file ($fileSize bytes)\n";
576 print BUILD pack("NN", $fileSize, length $file);
577 print BUILD $file;
578 open(F, "<c:/cygwin/tmp/$file");
579 binmode(F);
580 while ($fileSize)
582 my $buffer;
583 my $readSize = 16 * 1024;
584 $readSize = $fileSize if ($fileSize < $readSize);
585 my $readResult = read(F, $buffer, $readSize);
586 die "unexpected end of file" if (!defined($readResult));
587 die "did not read what we expected to" if ($readResult != $readSize);
588 print BUILD $buffer;
589 $fileSize -= $readResult;
591 die "copied all the bytes but not at EOF" if (!eof(F));
592 close(F);
594 # Cleanup
595 unlink "c:/cygwin/tmp/$file";
597 if(read(BUILD, $buffer, 1) != 1 || $buffer ne UPDATE_BOOTLEG_STEP_OK)
599 print "Failed while sending file.\n";
600 closedir DH;
601 return 0;
603 print "$file sent.\n";
605 print STDERR "Updating database on build cluster...\n";
606 return 0 if(read(BUILD, $buffer, 1) != 1 || $buffer ne UPDATE_BOOTLEG_STEP_OK);
607 print STDERR "Database update on build cluster complete.\n";
609 print STDERR "Syncing individual changelists on build cluster...\n";
610 my @syncChangelists;
611 open(SYNC, "$candela/SWO/builds/$branch/$change/sync.txt") || return 0;
612 while(<SYNC>)
614 chomp;
615 push @syncChangelists, $_;
617 close(SYNC);
618 print BUILD pack("N", length (join(" ", @syncChangelists)));
619 print BUILD join(" ", @syncChangelists);
620 return 0 if(read(BUILD, $buffer, 1) != 1 || $buffer ne UPDATE_BOOTLEG_STEP_OK);
621 print STDERR "Inidividual changelist sync complete.\n";
623 return 1;
627 # ======================================================================
628 # Main
629 # ======================================================================
631 &usage if(@ARGV == 0);
633 # Check to see if we're testing
634 if($ARGV[0] eq "vthakkar-box")
636 shift;
637 $buildCluster = "lin-vthakkar.station.sony.com";
640 $user = getuser();
641 $user = "build_script_publish" if(grep("-build_script_publish" eq $_, @ARGV));
643 checkarguments();
645 openbuildsocket();
647 sendstartinfo() || exitFailed();
649 while(@ARGV)
652 $option = shift @ARGV;
654 if($option eq "-restart")
656 print STDERR "Restarting build cluster...\n";
657 print BUILD COMMAND_RESTART;
659 elsif($option eq "-restart-login")
661 print STDERR "Restarting loginserver on build cluster...\n";
662 print BUILD COMMAND_RESTART_LOGIN;
664 elsif($option eq "-restart-nodes")
666 print STDERR "Restarting build cluster nodes...\n";
667 print BUILD COMMAND_RESTART_NODES;
669 elsif($option eq "-lock")
671 print STDERR "Locking build cluster...\n";
672 print BUILD COMMAND_LOCK;
674 elsif($option eq "-unlock")
676 print STDERR "Unlocking build cluster...\n";
677 print BUILD COMMAND_UNLOCK;
679 elsif($option eq "-update-bootleg")
681 print STDERR "Updating bootleg on build cluster...\n";
682 print BUILD COMMAND_UPDATE_BOOTLEG;
683 updatebootleg() || goto ERROR;
685 elsif($option eq "-content-sync")
687 print STDERR "Content syncing build cluster...\n";
688 print BUILD COMMAND_CONTENT_SYNC;
689 contentsync() || goto ERROR;
691 elsif($option eq "-sync-specified-changelists")
693 print STDERR "Syncing build cluster to changelists...\n";
694 print BUILD COMMAND_SYNC_SPECIFIED_CHANGELISTS;
695 syncspecifiedchangelists() || goto ERROR;
697 elsif($option eq "-snap")
699 print STDERR "Creating snapshot on build cluster...\n";
700 print BUILD COMMAND_SNAPSHOT;
701 snapshot() || goto ERROR;
703 elsif($option eq "-bootleg-version")
705 print STDERR "Checking bootleg version on build cluster...\n";
706 print BUILD COMMAND_BOOTLEG_VERSION;
707 getbootlegversion() || goto ERROR;
709 elsif($option eq "-free-object-ids")
711 print STDERR "Freeing object ids on build cluster...\n";
712 print BUILD COMMAND_FREE_OBJECT_IDS;
714 elsif($option eq "-build_script_publish")
716 next;
718 else
720 print STDERR "Error: cannot decipher option: $option\n";
721 goto FAIL;
724 ERROR:
725 my $success;
726 exitFailed() if(!read(BUILD, $success, 1));
727 if($success eq SUCCESSFULL_COMMAND)
729 print STDERR "Successfully completed $option\n\n";
731 else
733 print STDERR "Error encountered while running $option\n\n";
734 exitFailed();
739 FAIL:
740 sendendinfo();
742 close(BUILD);
744 exit($exitcode);