mirror of
https://github.com/SWG-Source/swg-main.git
synced 2026-01-16 20:04:18 -05:00
1161 lines
34 KiB
Perl
Executable File
1161 lines
34 KiB
Perl
Executable File
#! /usr/bin/perl
|
|
|
|
use warnings;
|
|
use strict;
|
|
use Socket;
|
|
use POSIX qw(setsid);
|
|
|
|
# ======================================================================
|
|
# notes
|
|
# add serverop user
|
|
# remove dsrc from build cluster
|
|
# ======================================================================
|
|
|
|
# ======================================================================
|
|
# Constants
|
|
# ======================================================================
|
|
|
|
|
|
use constant START_COMMUNICATION => "S";
|
|
use constant START_NOT_LOCKED_READY => "B";
|
|
use constant START_LOCKED_READY => "C";
|
|
use constant START_LOCKED_READY_UNAUTHORIZED_USER => "L";
|
|
use constant START_ERROR_AUTHORIZING => "K";
|
|
|
|
use constant END_COMMUNICATION => "E";
|
|
use constant END_SUCCESSFULL => "F";
|
|
|
|
use constant SUCCESSFULL_COMMAND => "P";
|
|
use constant FAILED_COMMAND => "U";
|
|
use constant UPDATE_BOOTLEG_STEP_OK => "G";
|
|
use constant UPDATE_BOOTLEG_STEP_FAILED => "H";
|
|
use constant UPDATE_BOOTLEG_SEND_DIRECTORY => "M";
|
|
use constant UPDATE_BOOTLEG_FILES_FINISHED => "Q";
|
|
use constant UPDATE_BOOTLEG_SEND_FILE => "N";
|
|
use constant SNAPSHOT_FAILED => "O";
|
|
use constant SNAPSHOT_SUCCESSFULL => "P";
|
|
|
|
use constant COMMAND_RESTART => "a";
|
|
use constant COMMAND_RESTART_LOGIN => "b";
|
|
use constant COMMAND_RESTART_NODES => "c";
|
|
use constant COMMAND_LOCK => "d";
|
|
use constant COMMAND_UNLOCK => "e";
|
|
use constant COMMAND_UPDATE_BOOTLEG => "f";
|
|
use constant COMMAND_CONTENT_SYNC => "g";
|
|
use constant COMMAND_SYNC_SPECIFIED_CHANGELISTS => "h";
|
|
use constant COMMAND_SNAPSHOT => "i";
|
|
use constant COMMAND_BOOTLEG_VERSION => "j";
|
|
use constant COMMAND_FREE_OBJECT_IDS => "k";
|
|
|
|
# ======================================================================
|
|
# Globals
|
|
# ======================================================================
|
|
|
|
my $p4 = "/usr/local/bin/p4";
|
|
my $port = '98452';
|
|
my $snapShotOutputFile = "/tmp/swg_object.txt";
|
|
my $logfile = "build_cluster.log";
|
|
my $depotdir = "/swo/";
|
|
my $branch;
|
|
|
|
my @authorizedUsers =
|
|
(
|
|
"buildscript",
|
|
"gmcdaniel",
|
|
"ttyson",
|
|
"tcramer",
|
|
"sthomas"
|
|
);
|
|
|
|
my $user = "none";
|
|
my $keyuser = "none";
|
|
my $serverdown = 0;
|
|
my $email = 0;
|
|
my %nodes;
|
|
|
|
my $name = $0;
|
|
$name =~ s/^(.*)\\//;
|
|
|
|
# Access level:
|
|
# 0 - Cluster is locked and incorrect user is trying to access
|
|
# 1 - Cluster is unlocked
|
|
# 2 - Cluster is locked and user is valid
|
|
my $accesslevel = 0;
|
|
|
|
$ENV{"P4PORT"} = "aus-perforce1.station.sony.com:1666";
|
|
$ENV{"P4CLIENT"} = "serverop-swo-dev9";
|
|
|
|
# ======================================================================
|
|
# Subroutines
|
|
# ======================================================================
|
|
|
|
sub usage
|
|
{
|
|
print STDERR "\nUsage:\n";
|
|
print STDERR "\t$name branch [-d <log file directory>]\n\n".
|
|
"\t\tbranch : required perforce branch name (e.g. current or s7)\n".
|
|
"\t\t-d : daemonize script (e.g. -d /tmp/)\n";
|
|
die "\n";
|
|
}
|
|
|
|
sub prompt
|
|
{
|
|
print $_[0], "\n";
|
|
|
|
do
|
|
{
|
|
#print "Continue (y/n): ";
|
|
$_ = "y"; # <STDIN>;
|
|
return if (/^y/i);
|
|
die "Aborting\n" if (/^n/i);
|
|
} while (1);
|
|
}
|
|
|
|
sub asksystem
|
|
{
|
|
print "Execute: $_[0]\n";
|
|
do
|
|
{
|
|
#print "(y/n/A): ";
|
|
$_ = "y"; # <STDIN>;
|
|
return system($_[0]) if (/^y/i);
|
|
return 0 if (/^n/i);
|
|
die "Aborting\n" if (/^A/);
|
|
} while (1);
|
|
}
|
|
|
|
sub endFunc
|
|
{
|
|
writelog("Failed while running: $_[0]");
|
|
print STDERR "Failed while running: $_[0]\n";
|
|
return 0;
|
|
}
|
|
|
|
sub perforceWhere
|
|
{
|
|
local $_;
|
|
|
|
# find out where a perforce file resides on the local machine
|
|
my $result;
|
|
{
|
|
open(P4, "p4 where $_[0] |");
|
|
$_ = <P4>;
|
|
chomp;
|
|
my @where = split;
|
|
$result = $where[2];
|
|
close(P4);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
sub writelog
|
|
{
|
|
my $message = shift @_;
|
|
my ($sec, $min, $hr, $day, $mon, $yr) = localtime time;
|
|
my $timestamp = sprintf "%4s-%02s-%02s\t%02s:%02s:%02s", ($yr + 1900), ($mon + 1), $day, $hr, $min, $sec;
|
|
my $lock = locked() ? "locked" : "unlocked";
|
|
|
|
print LOG join("\t", $timestamp, $user, $lock, $message), "\n";
|
|
}
|
|
|
|
sub initcommunication
|
|
{
|
|
my $buffer;
|
|
|
|
$user = "none";
|
|
$keyuser = "none";
|
|
$accesslevel = 0;
|
|
|
|
return endFunc("Did not recieve enough bytes from socket (!= 4)") if (read(SOCKET, $buffer, 4) != 4);
|
|
|
|
my $length = unpack("N", $buffer);
|
|
|
|
return endFunc("Did not recieve enough bytes from socket (!= $length)") if(read(SOCKET, $buffer, $length) != $length);
|
|
|
|
if($buffer eq "buildscript")
|
|
{
|
|
writelog("Build script - bypassing key check");
|
|
$accesslevel = 2;
|
|
$user = "build_script";
|
|
print SOCKET START_LOCKED_READY;
|
|
return 1;
|
|
}
|
|
|
|
$user = $buffer;
|
|
my $isValidUser = grep($user eq $_, @authorizedUsers);
|
|
|
|
if(!locked())
|
|
{
|
|
# Cluster is unlocked
|
|
$accesslevel = ($isValidUser == 0) ? 1 : 2;
|
|
writelog("Communication initialized");
|
|
print SOCKET START_NOT_LOCKED_READY;
|
|
return 1;
|
|
}
|
|
elsif(!$isValidUser)
|
|
{
|
|
# Cluster is locked, and the and an unauthorized user is accessing
|
|
$accesslevel = 0;
|
|
writelog("Build locked and unauthorized user accessing");
|
|
print SOCKET START_LOCKED_READY_UNAUTHORIZED_USER;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
# Cluster is locked, and the user is valid
|
|
$accesslevel = 2;
|
|
writelog("Communication initialized");
|
|
print SOCKET START_LOCKED_READY;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
sub endcommunication
|
|
{
|
|
if(bringup() == 0)
|
|
{
|
|
writelog("Error bringing cluster back up");
|
|
print SOCKET FAILED_COMMAND;
|
|
return 0;
|
|
}
|
|
|
|
print STDERR "Communication ended\n";
|
|
writelog("Communication ended");
|
|
print SOCKET END_SUCCESSFULL;
|
|
|
|
$user = "none";
|
|
$keyuser = "none";
|
|
$accesslevel = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub lockcluster
|
|
{
|
|
if($accesslevel == 2)
|
|
{
|
|
print STDERR "Locking build cluster...\n";
|
|
asksystem("p4 counter swg-$branch-build-lockout 1");
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
print STDERR "Not authorized to lock build cluster...\n";
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
sub unlockcluster
|
|
{
|
|
if($accesslevel == 2)
|
|
{
|
|
print STDERR "Unlocking build cluster...\n";
|
|
asksystem("p4 counter swg-$branch-build-lockout 0");
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
print STDERR "Not authorized to unlock build cluster...\n";
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
sub locked
|
|
{
|
|
my $lock = `$p4 counter swg-$branch-build-lockout`;
|
|
chomp $lock;
|
|
return $lock;
|
|
}
|
|
|
|
sub bringdown
|
|
{
|
|
if($serverdown == 0)
|
|
{
|
|
system("mail -s \"[BUILDCLUSTER] Build cluster coming down for a $_[0]\" swo\@soe.sony.com < /dev/null") == 0 || return endFunc("send email") if($email);
|
|
|
|
# stop the build cluster
|
|
print STDERR "Bringing down the build cluster...\n";
|
|
writelog("Bringing down the build cluster...");
|
|
chdir("${depotdir}swg/$branch/exe/linux") || return endFunc("chdir");
|
|
asksystem("./stopserver");
|
|
$serverdown = 1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
sub bringup
|
|
{
|
|
if($serverdown == 1)
|
|
{
|
|
system("mail -s \"[BUILDCLUSTER] Build cluster coming back up\" swo\@soe.sony.com < /dev/null") == 0 || return endFunc("send email") if($email);
|
|
|
|
# bring the build cluster up normally
|
|
print STDERR "Bringing up the build cluster for $branch...\n";
|
|
writelog("Bringing up the build cluster for $branch...");
|
|
chdir("${depotdir}swg/$branch/exe/linux") || return endFunc("Cannot change to ${depotdir}swg/$branch/exe/linux - $!");
|
|
asksystem("echo start | debug/TaskManager -- \@buildTaskManager.cfg 2>&1 | tee taskManager.log &");
|
|
$serverdown = 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
sub restart
|
|
{
|
|
return endFunc("Incorrect access level - need > 0") if ($accesslevel < 1);
|
|
|
|
bringdown("restart") || errorcode("bringdown");
|
|
bringup() || errorcode("bringup");
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub restartlogin
|
|
{
|
|
return endFunc("Incorrect access level - need > 0") if ($accesslevel < 1);
|
|
|
|
bringdown("restart login") || errorcode("bringdown");
|
|
|
|
writelog("Bringing down LoginServer");
|
|
asksystem("killall -9 LoginServer");
|
|
|
|
asksystem("echo start | debug/LoginServer -- \@loginServer_buildCluster.cfg 2>&1 | tee loginServer.log &") == 0 || return endFunc("start LoginServer");
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub refreshnodes
|
|
{
|
|
writelog("Refreshing the list of nodes");
|
|
%nodes = ();
|
|
|
|
open(LOCAL, "/swg/swg/$branch/exe/linux/localOptions.cfg") || return endFunc("error opening localOptions.cfg");
|
|
while(<LOCAL>)
|
|
{
|
|
# Refresh all nodes, ignoring the head node
|
|
$nodes{$1} = $2 if(/^node(\d+)=(\S+)/ && $1 != 0);
|
|
}
|
|
close(LOCAL);
|
|
|
|
writelog("Completed refreshing the list of nodes");
|
|
}
|
|
|
|
sub bringnodesdown
|
|
{
|
|
return endFunc("Incorrect access level - need > 0") if ($accesslevel < 1);
|
|
|
|
writelog("Bringing down all nodes");
|
|
my $remotecmd = "killall TaskManager";
|
|
|
|
foreach my $node (sort keys %nodes)
|
|
{
|
|
writelog("Bringing down $nodes{$node}");
|
|
my $result = system("ssh -f serverop\@$nodes{$node} \"$remotecmd \"");
|
|
|
|
writelog("Error bringing down node $nodes{$node}") if($result != 0);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub bringnodesup
|
|
{
|
|
return endFunc("Incorrect access level - need > 0") if ($accesslevel < 1);
|
|
|
|
writelog("Bringing up all nodes");
|
|
my $remotecmd = "ulimit -c unlimited; cd /swo/swg/$branch/exe/linux; debug/TaskManager -- \@remote_taskmanager.cfg";
|
|
|
|
foreach my $node (sort keys %nodes)
|
|
{
|
|
writelog("Bringing up $nodes{$node}");
|
|
my $result = system("ssh -f serverop\@$nodes{$node} \"$remotecmd 2>&1 \" > logs/taskmanager-$nodes{$node}.log");
|
|
|
|
writelog("Error bringing up node $nodes{$node}") if($result != 0);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub restartnodes
|
|
{
|
|
return endFunc("Incorrect access level - need > 0") if ($accesslevel < 1);
|
|
|
|
refreshnodes() || errorcode("refreshnodelist");
|
|
bringnodesdown() || errorcode("bringnodesdown");
|
|
bringnodesup() || errorcode("bringnodesup");
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub endcontentsync
|
|
{
|
|
print SOCKET pack("N", 0);
|
|
return endFunc($_[0]);
|
|
}
|
|
|
|
sub runcontentsync
|
|
{
|
|
my ($change) = @_;
|
|
|
|
my $counter = `p4 counter swg-$branch-build-contentsync`;
|
|
chomp $counter;
|
|
|
|
return endFunc("Cannot content sync before $branch counter ($change < $counter)") if($change < $counter);
|
|
|
|
my $messageToController = "";
|
|
writelog("Content syncing from $counter to $change");
|
|
|
|
# Fix for a Malformed UTF-8 error in perl 5.8.0
|
|
my $oldLang;
|
|
if(exists $ENV{"LANG"})
|
|
{
|
|
$oldLang = $ENV{"LANG"};
|
|
$ENV{"LANG"} = "en_US";
|
|
}
|
|
|
|
open(CONTENTSYNC, "perl ${depotdir}swg/$branch/tools/ContentSync.pl 1 $branch $counter $change 2>&1 | ") || return endFunc("cannot open perl ContentSync.pl");
|
|
while(<CONTENTSYNC>)
|
|
{
|
|
if(/^could not determine content status of /)
|
|
{
|
|
$messageToController .= $_;
|
|
print STDERR $_;
|
|
chomp;
|
|
writelog($_);
|
|
}
|
|
}
|
|
close(CONTENTSYNC);
|
|
|
|
$ENV{"LANG"} = $oldLang if(defined $oldLang);
|
|
|
|
print SOCKET pack("N", length $messageToController);
|
|
print SOCKET $messageToController;
|
|
|
|
$change = $1 if($messageToController =~ /^could not determine content status of \S+ for changelist (\d+)/);
|
|
|
|
asksystem("p4 counter swg-$branch-build-contentsync $change") == 0 || return endFunc("Can't set counter swg-$branch-build-contentsync");
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
sub contentsync
|
|
{
|
|
return endcontentsync("Incorrect access level - need > 0") if($accesslevel < 1);
|
|
|
|
bringdown("content sync") || return endcontentsync("bringdown");
|
|
|
|
# issue the content sync
|
|
print STDERR "Content syncing...\n";
|
|
chdir("${depotdir}swg/$branch/exe/linux") || endcontentsync("chdir");
|
|
|
|
my $buffer;
|
|
return endcontentsync("Did not recieve enough bytes from socket (!= 4)") if (read(SOCKET, $buffer, 4) != 4);
|
|
|
|
my $length = unpack("N", $buffer);
|
|
my $end;
|
|
|
|
if($length != 0)
|
|
{
|
|
return endcontentsync("Did not recieve enough bytes from socket (!= $length)") if(read(SOCKET, $end, $length) != $length);
|
|
}
|
|
else
|
|
{
|
|
$end = `p4 counter change`;
|
|
chomp $end;
|
|
}
|
|
|
|
runcontentsync($end) || return endcontentsync("runcontentsync");
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub changelistsync
|
|
{
|
|
return endFunc("Incorrect access level - need > 0") if($accesslevel < 1);
|
|
|
|
bringdown("changelist sync") || errorcode("bringdown");
|
|
|
|
print STDERR "Syncing to changelists...\n";
|
|
my $buffer;
|
|
|
|
return endFunc("Did not recieve enough bytes from socket (!= 4)") if (read(SOCKET, $buffer, 4) != 4);
|
|
|
|
my $length = unpack("N", $buffer);
|
|
|
|
return endFunc("No changelists given") if ($length == 0);
|
|
|
|
return endFunc("Did not recieve enough bytes from socket (!= $length)") if (read(SOCKET, $buffer, $length) != $length);
|
|
|
|
my @cl = split / /, $buffer;
|
|
|
|
my $counter = `p4 counter swg-$branch-build-contentsync`;
|
|
chomp $counter;
|
|
|
|
foreach (sort { $a <=> $b } @cl)
|
|
{
|
|
if($_ >= $counter)
|
|
{
|
|
writelog("Syncing to individual changelist $_");
|
|
asksystem("$p4 sync //depot/swg/$branch/...\@$_,\@$_ > /dev/null 2>/dev/null") == 0 || return endFunc("Cannot sync //depot/swg/$branch/...\@$_,$_");
|
|
}
|
|
else
|
|
{
|
|
return endFunc("Cannot sync to a changelist less than contentsync ($_ < $counter)");
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub endsnap
|
|
{
|
|
print SOCKET SNAPSHOT_FAILED;
|
|
return 0;
|
|
}
|
|
|
|
sub makesnap
|
|
{
|
|
bringdown("snapshot") || errorcode("bringdown");
|
|
|
|
# get the name of the db schema to update
|
|
my $buffer;
|
|
my $dbSchema;
|
|
return endsnap("Did not recieve enough bytes from socket (!= 4)") if (read(SOCKET, $buffer, 4) != 4);
|
|
my $length = unpack("N", $buffer);
|
|
return endFunc("Did not recieve enough bytes from socket (!= $length)") if (read(SOCKET, $dbSchema, $length) != $length);
|
|
|
|
# create the gold schema user if not already created
|
|
createuser($dbSchema);
|
|
|
|
# bring the build cluster up with preloading
|
|
writelog("Bringing up the build cluster in preload mode");
|
|
print STDERR "Bringing up the build cluster in preload mode...\n";
|
|
prompt("unlinking localOptions.cfg");
|
|
unlink("localOptions.cfg");
|
|
prompt("symlinking localOptions.cfg to snapshotLocalOptions");
|
|
symlink("snapshotLocalOptions.cfg", "localOptions.cfg") || return endsnap();
|
|
prompt("bringing up the build cluster");
|
|
my $result = system("echo start | debug/TaskManager -- \@buildTaskManagerPublishMode.cfg 2>&1 | tee taskManager.log");
|
|
|
|
# restore the normal config file
|
|
writelog("Restoring the build cluster to normal mode");
|
|
prompt("unlinking localOptions.cfg");
|
|
unlink("localOptions.cfg");
|
|
prompt("linking localOptions.cfg to normalLocalOptions.cfg");
|
|
symlink("normalLocalOptions.cfg", "localOptions.cfg") || return endsnap();
|
|
|
|
# handle the cluster startup failing
|
|
# return endsnap() if ($result != 0);
|
|
|
|
# copy to the publish cluster
|
|
writelog("Copying to the publish database");
|
|
print STDERR "Copying to the publish database...\n";
|
|
chdir("${depotdir}swg/$branch/src/game/server/database/build/linux") || return endsnap();
|
|
|
|
my $copyBuildClusterLog = "${depotdir}swg/$branch/src/game/server/database/build/linux/copy_buildcluster.log";
|
|
my $copyComplete = 0;
|
|
$buffer = "";
|
|
unlink($copyBuildClusterLog);
|
|
open(DBLOG, "perl copy_buildcluster.pl --copybuildcluster --username=$dbSchema 2>&1 |") || return endsnap();
|
|
while(<DBLOG>)
|
|
{
|
|
$buffer .= $_;
|
|
$copyComplete = 1 if(/^Import terminated successfully/); # Note: ^IMP- and ^EXP- and ^ORA- warnings during import are normal operation
|
|
}
|
|
close(DBLOG);
|
|
my $copyReturn = $?;
|
|
|
|
open (DBLOG, ">$copyBuildClusterLog");
|
|
print DBLOG $buffer;
|
|
close (DBLOG);
|
|
|
|
if ($copyReturn != 0 || $copyComplete == 0)
|
|
{
|
|
print STDERR "failed to copy the build cluster - output is in $copyBuildClusterLog\n";
|
|
writelog("failed to copy the build cluster - output is in $copyBuildClusterLog");
|
|
return endsnap();
|
|
}
|
|
|
|
# Set the golddata schema name in the version_number table
|
|
updateversion($dbSchema);
|
|
|
|
# execute the snapshot query
|
|
writelog("Executing the world snapshot query");
|
|
print STDERR "Executing the world snapshot query...\n";
|
|
unlink($snapShotOutputFile);
|
|
chdir("../../queries") || return endsnap();
|
|
asksystem("sqlplus $dbSchema/changeme\@swodb \@world_snapshot.sql") == 0 || return endsnap();
|
|
|
|
# Here are the commands to run to do a new snapshot by hand
|
|
# cd /swg/swg/test/exe/linux
|
|
# rm -f *.ws snapshot.log
|
|
# debug/SwgGameServer -- \@servercommon.cfg -s GameServer javaVMName=none -s WorldSnapshot createWorldSnapshots=/swg/swg/test/dsrc/sku.0/sys.client/built/game/snapshot/swg_object.txt 2> snapshot.log
|
|
# p4 edit /swg/swg/test/data/sku.0/sys.client/built/game/snapshot/...
|
|
# mv *.ws /swg/swg/test/data/sku.0/sys.client/built/game/snapshot
|
|
|
|
# build the snapshot files "debug/SwgGameServer -- @snapshot.cfg"
|
|
writelog("Creating snapshot files");
|
|
chdir("${depotdir}swg/$branch/exe/linux/") || return endsnap();
|
|
system("rm -f *.ws snapshot.log");
|
|
system("debug/SwgGameServer -- \@servercommon.cfg -s GameServer javaVMName=none -s WorldSnapshot createWorldSnapshots=$snapShotOutputFile 2> snapshot.log") == 0 || return endsnap();
|
|
|
|
print SOCKET SNAPSHOT_SUCCESSFULL;
|
|
|
|
# Send snapshot log, swg_object.txt, then all ws back to controller
|
|
writelog("Transmitting snapshot files back to the controller");
|
|
system("cp $snapShotOutputFile ${depotdir}swg/$branch/exe/linux/swg_object.txt");
|
|
opendir DH, "${depotdir}swg/$branch/exe/linux/" or return endsnap();
|
|
my @files;
|
|
foreach(readdir DH)
|
|
{
|
|
push @files, $_ if($_ =~ /\.ws$/ && -s "${depotdir}swg/$branch/exe/linux/$_");
|
|
}
|
|
closedir DH;
|
|
|
|
@files = sort @files;
|
|
unshift @files, "snapshot.log", "swg_object.txt";
|
|
|
|
foreach (@files)
|
|
{
|
|
my $file = $_;
|
|
|
|
my $fileSize = -s "${depotdir}swg/$branch/exe/linux/$_";
|
|
print STDERR "Sending file $file ($fileSize bytes)\n";
|
|
writelog("Sending file $file ($fileSize bytes)");
|
|
print SOCKET pack("NN", $fileSize, length $file);
|
|
print SOCKET $file;
|
|
open(F, "<${depotdir}swg/$branch/exe/linux/$file");
|
|
binmode(F);
|
|
while ($fileSize)
|
|
{
|
|
my $buffer;
|
|
my $readSize = 16 * 1024;
|
|
$readSize = $fileSize if ($fileSize < $readSize);
|
|
my $readResult = read(F, $buffer, $readSize);
|
|
return endFunc("readResult not defined") if (!defined($readResult));
|
|
return endFunc("readResult not equal to readSize") if ($readResult != $readSize);
|
|
print SOCKET $buffer;
|
|
$fileSize -= $readResult;
|
|
}
|
|
return endFunc("copied all the bytes but not at EOF") if (!eof(F));
|
|
close(F);
|
|
|
|
print STDERR "$file sent.\n";
|
|
writelog("$file sent");
|
|
}
|
|
|
|
print SOCKET pack("NN", 0, 0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub endbootleginstall
|
|
{
|
|
print SOCKET UPDATE_BOOTLEG_STEP_FAILED;
|
|
return endFunc($_[0]);
|
|
}
|
|
|
|
sub bootleginstall
|
|
{
|
|
if($accesslevel == 2)
|
|
{
|
|
bringdown("exe update") || errorcode("bringdown");
|
|
|
|
writelog("Bringing down LoginServer");
|
|
asksystem("killall -9 LoginServer");
|
|
|
|
refreshnodes() || return endFunc("refreshnodes");
|
|
bringnodesdown() || return endFunc("bringnodesdown");
|
|
|
|
print STDERR "Installing most recent bootleg...\n";
|
|
|
|
my $buffer;
|
|
# Sync to bootleg number
|
|
return endbootleginstall("Did not recieve enough bytes from socket (!= 4)") if (read(SOCKET, $buffer, 4) != 4);
|
|
my $change = unpack("N", $buffer);
|
|
print STDERR "Syncing to $change...\n";
|
|
writelog("Syncing to $change...");
|
|
asksystem("p4 sync //depot/swg/$branch/...\@$change 2>&1") == 0 || return endbootleginstall("p4 sync failed");
|
|
writelog("Sync to $change complete");
|
|
print STDERR "Sync to $change complete\n";
|
|
print SOCKET UPDATE_BOOTLEG_STEP_OK;
|
|
|
|
# Get exes for bootleg
|
|
chdir("${depotdir}swg/$branch/exe/linux/") || return endbootleginstall("chdir");
|
|
writelog("Recieving files...");
|
|
|
|
return endbootleginstall("Did not recieve enough bytes from socket (!= 8)") if (read(SOCKET, $buffer, 2*4) != 2*4);
|
|
my ($fileSize, $fileNameLength) = unpack("NN", $buffer);
|
|
|
|
my $localFileName;
|
|
return endbootleginstall("Did not recieve enough bytes from socket (!= $fileNameLength)") if (read(SOCKET, $localFileName, $fileNameLength) != $fileNameLength);
|
|
|
|
# receive the binary bits for the file
|
|
print STDERR "Receiving $localFileName ($fileSize bytes)\n";
|
|
writelog("Receiving $localFileName ($fileSize bytes)");
|
|
unlink $localFileName;
|
|
open(F, ">" . $localFileName) || return endbootleginstall("could not open $localFileName for writing");
|
|
chmod (0755, $localFileName);
|
|
binmode(F);
|
|
while ($fileSize)
|
|
{
|
|
my $readSize = 16 * 1024;
|
|
$readSize = $fileSize if ($fileSize < $readSize);
|
|
my $readResult = read(SOCKET, $buffer, $readSize);
|
|
return endbootleginstall("socket to controller machine abruptly terminated ($fileSize bytes remained)") if (!defined($readResult));
|
|
return endbootleginstall("read incorrect amount ($fileSize bytes remained)") if ($readResult == 0);
|
|
print F $buffer;
|
|
$fileSize -= $readResult;
|
|
}
|
|
return endbootleginstall("copied wrong number of bytes") if ($fileSize != 0);
|
|
close(F);
|
|
|
|
writelog("Untarring servers file");
|
|
chdir("${depotdir}swg/$branch/exe/linux/debug");
|
|
my $result = system("tar -xzf ../$localFileName &> /dev/null");
|
|
return endbootleginstall("error untarring server files") if ($result != 0);
|
|
system("chmod -R 755 *");
|
|
|
|
print SOCKET UPDATE_BOOTLEG_STEP_OK;
|
|
writelog("Recieve successful");
|
|
|
|
# Update database
|
|
print STDERR "Updating database...\n";
|
|
writelog("Updating database");
|
|
|
|
chdir("${depotdir}swg/$branch/src/game/server/database/build/linux/") || endbootleginstall("Cannot change to database dir $!");
|
|
|
|
my $update_complete = 1;
|
|
$buffer = "";
|
|
open(DBLOG, "perl database_update.pl --delta --username=buildcluster --buildcluster |") || endbootleginstall("database_update failed");
|
|
while(<DBLOG>)
|
|
{
|
|
$buffer .= $_;
|
|
$update_complete = 0 if(/ERROR/);
|
|
}
|
|
close(DBLOG);
|
|
|
|
return endbootleginstall("Error while updating database:\n$buffer") if(!$update_complete);
|
|
print SOCKET UPDATE_BOOTLEG_STEP_OK;
|
|
writelog("Update database complete");
|
|
print STDERR "Update database complete\n";
|
|
|
|
writelog("Updating individual changelists from sync.txt");
|
|
return endFunc("Did not recieve enough bytes from socket (!= 4)") if (read(SOCKET, $buffer, 4) != 4);
|
|
my $length = unpack("N", $buffer);
|
|
writelog("Expecting individual changelist string of length " . $length);
|
|
return endFunc("No changelists given") if ($length == 0);
|
|
return endFunc("Did not recieve enough bytes from socket (!= $length)") if (read(SOCKET, $buffer, $length) != $length);
|
|
my @changelists = split / /, $buffer;
|
|
foreach (@changelists)
|
|
{
|
|
system("p4 sync //depot/swg/$branch/...\@$_,\@$_");
|
|
}
|
|
writelog("Done updating individual changelists from sync.txt");
|
|
print SOCKET UPDATE_BOOTLEG_STEP_OK;
|
|
|
|
writelog("Bringing back up LoginServer");
|
|
chdir("${depotdir}swg/$branch/exe/linux") || return endFunc("Cannot change to ${depotdir}swg/$branch/exe/linux - $!");
|
|
asksystem("echo start | debug/LoginServer -- \@loginServer_buildCluster.cfg 2>&1 | tee loginServer.log &") == 0 || return endbootleginstall("start LoginServer");
|
|
|
|
bringnodesup() || return endFunc("bringnodesup");
|
|
|
|
print STDERR "Bootleg update complete.\n";
|
|
writelog("Bootleg update complete");
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
print STDERR "Not authorized to update bootleg...\n";
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
sub getbootlegversion
|
|
{
|
|
print STDERR "Getting bootleg version\n";
|
|
my $bootlegver;
|
|
open(VER, perforceWhere("//depot/swg/$branch/tools/VersionNumber") . " -r /swg/swg/$branch/exe/linux/debug/lib/libsharedFoundation.so |");
|
|
while(<VER>)
|
|
{
|
|
$bootlegver = $1 if(/ \d+\.(\d+) (bootleg|publish)/);
|
|
}
|
|
close(VER);
|
|
|
|
if(!defined $bootlegver)
|
|
{
|
|
print SOCKET pack("N", 0);
|
|
return endFunc("Could not get VersionNumber from file");
|
|
}
|
|
|
|
print SOCKET pack("N", length $bootlegver);
|
|
print SOCKET $bootlegver;
|
|
print "Build cluster bootleg version is: $bootlegver\n";
|
|
writelog("Build cluster bootleg version is: $bootlegver");
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub snapobjectiderror
|
|
{
|
|
print SOCKET pack("N", 0);
|
|
errorcode("freeobjectids");
|
|
}
|
|
|
|
sub freeobjectids
|
|
{
|
|
print STDERR "Freeing object ids\n";
|
|
|
|
my $tmpfile = "freeobjectids.tmp";
|
|
open(TMPFILE, ">$tmpfile");
|
|
print TMPFILE "exec objectidmanager.rebuild_freelist\;";
|
|
print TMPFILE "delete from free_object_ids where end_id is null\;";
|
|
print TMPFILE "Commit\;";
|
|
close(TMPFILE);
|
|
|
|
my $buffer = "";
|
|
my $error = 0;
|
|
open(SQL, "sqlplus buildcluster/changeme\@swodb < freeobjectids.tmp |") || return endFunc("sqlplus buildcluster/changeme\@swodb < freeobjectids.tmp");
|
|
while(<SQL>)
|
|
{
|
|
$buffer .= $_;
|
|
$error = 1 if(/ERROR/);
|
|
}
|
|
close(SQL);
|
|
|
|
unlink($tmpfile);
|
|
|
|
if($error)
|
|
{
|
|
writelog("Error freeing object IDs");
|
|
writelog($buffer);
|
|
return endFunc("freeing object ids");
|
|
}
|
|
else
|
|
{
|
|
writelog("Successfully freed object ids");
|
|
print STDERR "Successfully freed object ids\n";
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
sub updateversion
|
|
{
|
|
my $dbSchema = uc shift;
|
|
|
|
print STDERR "Updating version [$dbSchema]\n";
|
|
|
|
my $tmpfile = "updateversion.tmp";
|
|
open(TMPFILE, ">$tmpfile");
|
|
print TMPFILE "UPDATE VERSION_NUMBER SET GOLDDATA='$dbSchema';\n";
|
|
print TMPFILE "COMMIT;\n";
|
|
close(TMPFILE);
|
|
|
|
my $buffer = "";
|
|
my $error = 0;
|
|
|
|
open(SQL, "sqlplus $dbSchema/changeme\@swodb < updateversion.tmp |") || return endFunc("sqlplus $dbSchema/changeme\@swodb < updateversion.tmp");
|
|
while(<SQL>)
|
|
{
|
|
$buffer .= $_;
|
|
$error = 1 if(/ERROR/);
|
|
}
|
|
close(SQL);
|
|
|
|
unlink($tmpfile);
|
|
|
|
if($error)
|
|
{
|
|
writelog("Error updating version");
|
|
writelog($buffer);
|
|
return endFunc("update version");
|
|
}
|
|
else
|
|
{
|
|
writelog("Successfully updated version");
|
|
print STDERR "Successfully updated version\n";
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
sub hasuser
|
|
{
|
|
my ($dbSchema, $dbUser, $dbPass) = @_;
|
|
|
|
print STDERR "Checking if user exists [$dbSchema]\n";
|
|
|
|
my $tmpfile = "hasuser.tmp";
|
|
open(TMPFILE, ">$tmpfile");
|
|
print TMPFILE "SELECT USERNAME FROM DBA_USERS WHERE USERNAME = '$dbSchema';\n";
|
|
close(TMPFILE);
|
|
my $found = 1;
|
|
|
|
open(SQL, "sqlplus $dbUser/$dbPass\@swodb < hasuser.tmp |") || return endFunc("sqlplus < hasuser.tmp");
|
|
while(<SQL>)
|
|
{
|
|
$found = 0 if(/no rows selected/);
|
|
}
|
|
close(SQL);
|
|
|
|
unlink($tmpfile);
|
|
|
|
if($found)
|
|
{
|
|
writelog("User found");
|
|
print STDERR "User found\n";
|
|
}
|
|
else
|
|
{
|
|
writelog("User not found");
|
|
print STDERR "User not found\n";
|
|
}
|
|
|
|
return $found;
|
|
}
|
|
|
|
sub createuser
|
|
{
|
|
if (!defined($ENV{BUILD_CLUSTER_USER}) || !defined($ENV{BUILD_CLUSTER_PASS}))
|
|
{
|
|
print STDERR "DBA user environment variables not set correctly for automatic user creation\n";
|
|
return;
|
|
}
|
|
|
|
my $dbSchema = uc shift;
|
|
my $dbUser = $ENV{BUILD_CLUSTER_USER};
|
|
my $dbPass = $ENV{BUILD_CLUSTER_PASS};
|
|
|
|
return if hasuser($dbSchema, $dbUser, $dbPass);
|
|
|
|
print STDERR "Creating new user [$dbSchema]\n";
|
|
|
|
my $tmpfile = "createuser.tmp";
|
|
open(TMPFILE, ">$tmpfile");
|
|
print TMPFILE "CREATE USER \"$dbSchema\" PROFILE \"DEFAULT\"\n";
|
|
print TMPFILE " IDENTIFIED BY \"changeme\" DEFAULT TABLESPACE \"DATA\"\n";
|
|
print TMPFILE " TEMPORARY TABLESPACE \"TEMP\"\n";
|
|
print TMPFILE " ACCOUNT UNLOCK;\n";
|
|
print TMPFILE "GRANT CREATE TYPE TO \"$dbSchema\";\n";
|
|
print TMPFILE "GRANT UNLIMITED TABLESPACE TO \"$dbSchema\";\n";
|
|
print TMPFILE "GRANT \"SWG_GENERAL\" TO \"$dbSchema\";\n";
|
|
close(TMPFILE);
|
|
|
|
my $buffer = "";
|
|
my $error = 0;
|
|
|
|
open(SQL, "sqlplus $dbUser/$dbPass\@swodb < createuser.tmp |") || return endFunc("sqlplus < createuser.tmp");
|
|
while(<SQL>)
|
|
{
|
|
$buffer .= $_;
|
|
$error = 1 if(/ERROR/);
|
|
}
|
|
close(SQL);
|
|
|
|
unlink($tmpfile);
|
|
|
|
if($error)
|
|
{
|
|
writelog("Error creating user");
|
|
writelog($buffer);
|
|
return endFunc("create user");
|
|
}
|
|
else
|
|
{
|
|
writelog("Successfully created user");
|
|
print STDERR "Successfully created user\n";
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
sub errorcode
|
|
{
|
|
writelog("Failed while running $_[0]");
|
|
print STDERR "Failed while running: $_[0]\n";
|
|
print SOCKET FAILED_COMMAND;
|
|
bringup();
|
|
endcommunication();
|
|
close(SOCKET);
|
|
goto BUILDLOOP;
|
|
}
|
|
|
|
# ======================================================================
|
|
# Main
|
|
# ======================================================================
|
|
usage() if(@ARGV != 1 && @ARGV != 3);
|
|
|
|
$branch = shift;
|
|
|
|
usage() unless ($branch =~ m/^(current|test|stage|live|x\d+|ep3|s\d+)$/);
|
|
|
|
# handle running as a daemon
|
|
# usage is $name branch [-d <directory where daemon log file should be placed>]
|
|
if (@ARGV == 2 && $ARGV[0] eq "-d")
|
|
{
|
|
open STDIN, "/dev/null";
|
|
open STDOUT, "/dev/null";
|
|
|
|
# run as a daemon
|
|
my $pid = fork;
|
|
die "$0: fork failed" if (!defined $pid);
|
|
|
|
# parent process should exit
|
|
exit 0 if ($pid != 0);
|
|
|
|
# create a new session
|
|
setsid || die "$0: setsid failed";
|
|
|
|
chdir($ARGV[1]) or die "cannot change directry to $ARGV[1]";
|
|
|
|
open STDERR, "/dev/null";
|
|
}
|
|
|
|
# ignore sigpipe
|
|
$SIG{'PIPE'} = 'IGNORE';
|
|
|
|
# open the log file
|
|
open(LOG, ">>$logfile");
|
|
my $oldSelect = select(LOG);
|
|
$| = 1;
|
|
select($oldSelect);
|
|
|
|
writelog("Starting running branch $branch");
|
|
|
|
# open the daemon socket
|
|
print STDERR "Opening socket\n";
|
|
writelog("Opening socket");
|
|
socket(LISTEN, PF_INET, SOCK_STREAM, getprotobyname('tcp')) || die "socket failed\n";
|
|
setsockopt(LISTEN, SOL_SOCKET, SO_REUSEADDR, 1) || die "setsockopt failed\n";
|
|
my $addr = sockaddr_in($port, INADDR_ANY);
|
|
bind(LISTEN, $addr) || die "bind failed\n";
|
|
listen(LISTEN, 1) || die "listen failed\n";
|
|
|
|
BUILDLOOP:
|
|
while (1)
|
|
{
|
|
print STDERR "Waiting on a connection...\n";
|
|
|
|
accept(SOCKET, LISTEN) || die "accept failed\n";
|
|
|
|
# make binary and unbuffer the socket
|
|
binmode(SOCKET);
|
|
$oldSelect = select(SOCKET);
|
|
$| = 1;
|
|
select($oldSelect);
|
|
|
|
my $mode;
|
|
goto FAIL if (read(SOCKET, $mode, 1) != 1);
|
|
|
|
if($mode eq START_COMMUNICATION)
|
|
{
|
|
print STDERR "Initializing communication\n";
|
|
writelog("Initializing communication");
|
|
initcommunication() || errorcode("initcommunication");
|
|
|
|
while(1)
|
|
{
|
|
goto FAIL if (read(SOCKET, $mode, 1) != 1);
|
|
|
|
# Restart the build cluster
|
|
if($mode eq COMMAND_RESTART)
|
|
{
|
|
writelog("Restarting");
|
|
restart() || errorcode("restart");
|
|
print SOCKET SUCCESSFULL_COMMAND;
|
|
}
|
|
# Restart the login server on the build cluster
|
|
elsif($mode eq COMMAND_RESTART_LOGIN)
|
|
{
|
|
writelog("Restarting login");
|
|
restartlogin() || errorcode("restart login");
|
|
print SOCKET SUCCESSFULL_COMMAND;
|
|
}
|
|
# Restart the nodes on the build cluster
|
|
elsif($mode eq COMMAND_RESTART_NODES)
|
|
{
|
|
writelog("Restarting nodes");
|
|
restartnodes() || errorcode("restart nodes");
|
|
print SOCKET SUCCESSFULL_COMMAND;
|
|
}
|
|
# Lock build cluster
|
|
elsif($mode eq COMMAND_LOCK)
|
|
{
|
|
writelog("Locking");
|
|
lockcluster() || errorcode("lock");
|
|
print SOCKET SUCCESSFULL_COMMAND;
|
|
}
|
|
# Unlock build cluster
|
|
elsif($mode eq COMMAND_UNLOCK)
|
|
{
|
|
writelog("Unlocking");
|
|
unlockcluster() || errorcode("unlock");
|
|
print SOCKET SUCCESSFULL_COMMAND;
|
|
}
|
|
# Bootleg
|
|
elsif($mode eq COMMAND_UPDATE_BOOTLEG)
|
|
{
|
|
writelog("Update bootleg");
|
|
bootleginstall() || errorcode("bootleginstall");
|
|
print SOCKET SUCCESSFULL_COMMAND;
|
|
}
|
|
# Content sync
|
|
elsif($mode eq COMMAND_CONTENT_SYNC)
|
|
{
|
|
writelog("Content syncing");
|
|
contentsync() || errorcode("contentsync");
|
|
print SOCKET SUCCESSFULL_COMMAND;
|
|
}
|
|
# Sync specific changelists
|
|
elsif($mode eq COMMAND_SYNC_SPECIFIED_CHANGELISTS)
|
|
{
|
|
writelog("Changelist syncing");
|
|
changelistsync() || errorcode("changelistsync");
|
|
print SOCKET SUCCESSFULL_COMMAND;
|
|
}
|
|
# Snapshot
|
|
elsif($mode eq COMMAND_SNAPSHOT)
|
|
{
|
|
writelog("Snapshot");
|
|
freeobjectids || snapobjectiderror("freeobjectids");
|
|
makesnap() || errorcode("makesnap");
|
|
print SOCKET SUCCESSFULL_COMMAND;
|
|
}
|
|
elsif($mode eq COMMAND_BOOTLEG_VERSION)
|
|
{
|
|
writelog("Checking bootleg version");
|
|
getbootlegversion() || errorcode("getbootlegversion");
|
|
print SOCKET SUCCESSFULL_COMMAND;
|
|
}
|
|
elsif($mode eq COMMAND_FREE_OBJECT_IDS)
|
|
{
|
|
writelog("Freeing object IDs");
|
|
freeobjectids() || errorcode("freeobjectids");
|
|
print SOCKET SUCCESSFULL_COMMAND;
|
|
}
|
|
elsif($mode eq END_COMMUNICATION)
|
|
{
|
|
print STDERR "Ending communication\n";
|
|
writelog("Ending communication");
|
|
endcommunication() || errorcode("endcommunication");
|
|
last;
|
|
}
|
|
else
|
|
{
|
|
writelog("Unrecognized command : $mode");
|
|
goto FAIL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errorcode("waiting for start message");
|
|
}
|
|
|
|
FAIL:
|
|
# done with the socket
|
|
close(SOCKET);
|
|
}
|
|
|
|
close(LOG);
|