Commit e401c0f1 authored by Leigh B. Stoller's avatar Leigh B. Stoller
Browse files

Rob and I talked about regression testing last week, and we decided

that depending on different versions of assign to find the exact same
solution on the same top/ptop, is asking for trouble. And in fact, the
new assign is slightly different and the solutions do not match.

So the idea we came up with is to first run the old version, and then
fixnode the results for the input to the new version. assign should
run cleanly and the results will be the same. Then reverse the
situation and run the new version, then fixnode those results for a
run of the old version. Then compare all the results.
parent c170e6e4
......@@ -85,7 +85,7 @@ sub usage ()
"implies -n\n";
exit($WRAPPER_FAILED);
}
my $optlist = "vutnfprzxm:";
my $optlist = "vutnfprzxm:k";
my $verbose = 0;
my $fixmode = 0;
my $updating = 0;
......@@ -93,9 +93,12 @@ my $toponly = 0;
my $impotent = 0;
my $precheck = 0;
my $prepass = 0;
my $mfactor;
my $regression=0;
my $warnings = 0;
my $mfactor;
my $regression = 0;
my $noassign = 0; # Only with regression mode, use previous solution.
my $noregfree = 0; # Only with regression mode, leave physical state at end.
my $usecurrent = 0; # Only with regression mode, use current solution.
#
# Configure variables
......@@ -195,8 +198,10 @@ if (defined($options{"r"})) {
fatal("Cannot use regression mode on main DB");
}
$regression = 1;
$regression++
$usecurrent = 1
if (defined($options{"z"}));
$noregfree = 1
if (defined($options{"k"}));
}
my $pid = $ARGV[0];
my $eid = $ARGV[1];
......@@ -858,19 +863,20 @@ LoadPhysInfo();
#
LoadExperiment();
if ($regression) {
print STDERR "Freeing reserved nodes in regression mode\n";
system("export NORELOAD=1; $NFREE -x -a $pid $eid") == 0
or fatal("Could not release nodes in regression mode");
}
#
# If updating, load current experiment resources. We have to be careful
# of how this is merged in with the (new) desired topology. See below.
#
if ($updating || $regression) {
if ($updating || $regression || $usecurrent) {
LoadCurrent()
if ($updating);
if ($updating || $usecurrent);
if ($regression) {
print STDERR "Freeing reserved nodes in regression mode\n";
system("export NORELOAD=1; $NFREE -x -a $pid $eid") == 0
or fatal("Could not release nodes in regression mode");
}
print STDERR "Resetting DB before updating.\n";
$experiment->RemovePhysicalState();
}
......@@ -1005,7 +1011,7 @@ sub RunAssign ()
my %subnodes = ();
# Debugging hack for regression mode. Avoid really long assign runs.
if ($regression > 1) {
if ($noassign) {
if (! -e "assign.log") {
print "No existing assign results file!\n";
return -1;
......@@ -3193,10 +3199,27 @@ sub InitPnode($$)
# Now call os_select.
#
if (defined($osid)) {
printdb("os_select $osid $pnode\n");
system("os_select $osid $pnode") == 0 or
fatal(" os_select $osid $pnode failed!\n");
if ($impotent) {
printdb(" pretending to os_select $osid\n");
}
else {
printdb(" os_select $osid\n");
# osselect wants an osinfo object.
my $tmposinfo = OSinfo->Lookup($osid);
if (!defined($tmposinfo)) {
fatal("Could not map $osid to osinfo object");
}
my $pnodeobject = Node->Lookup($pnode);
if (! defined($pnodeobject)) {
fatal("No such pnode $pnode in nodes table!");
}
if ($pnodeobject->OSSelect($tmposinfo, "def_boot_osid", $verbose)) {
fatal("OSSelect($pnode,$tmposinfo) failed\n");
}
}
}
# Clear this after os_select.
if ($regression) {
DBQueryFatal("update nodes set state_timestamp=0, ".
......@@ -5580,13 +5603,14 @@ sub LoadCurrent()
my $query_result =
DBQueryFatal("select r.vname,r.node_id,n.phys_nodeid, ".
" nt.isvirtnode,nt.isremotenode,nt.isplabdslice,r.erole,".
" nt.isvirtnode,nt.isremotenode,nt.isplabdslice,r.erole,".
"r.simhost_violation,nt.type from reserved as r ".
"left join nodes as n on n.node_id=r.node_id ".
"left join node_types as nt on nt.type=n.type ".
"where r.pid='$pid' and r.eid='$eid'");
while (my ($vname,$reserved,$physnode,$isvirt,$isremote,$isplab,$erole,$simhost_violation,$node_type) =
while (my ($vname,$reserved,$physnode,$isvirt,$isremote,
$isplab,$erole,$simhost_violation,$node_type) =
$query_result->fetchrow_array) {
#
......@@ -5595,6 +5619,19 @@ sub LoadCurrent()
fatal("Cannot update widearea nodes yet!")
if ($isremote && !nodetypeisdedicatedremote($node_type)
&& !$isplab);
if ($regression) {
#
# In regression mode, we just store the p2v mapping for fixnode.
#
if ($isvirt) {
$fixed_nodes{$vname} = $physnode;
}
else {
$fixed_nodes{$vname} = $reserved;
}
next;
}
LoadPhysNode($reserved);
......@@ -5659,7 +5696,8 @@ sub LoadCurrent()
# Allow for the user to "move" a node. Yuck!
$fixed_nodes{$vname} = $reserved
if (!defined($fixed_nodes{$vname}) && $fix_current_resources);
if (!defined($fixed_nodes{$vname}) &&
$fix_current_resources);
$reserved_pcount++;
}
}
......@@ -5712,12 +5750,27 @@ sub FinalizeRegression($)
chomp($cwd = `/bin/pwd`);
if (!$error) {
print STDERR "Saving physical state in regression mode\n";
if (system("/bin/rm -rf $pid-$eid.pstate")) {
print STDERR "Could not clean physical state directory\n";
exit(1);
}
if ($experiment->BackupPhysicalState("$cwd/$pid-$eid.pstate", 1)
!= 0) {
print STDERR "Could not save physical state!\n";
exit(1);
}
# Load the current resources in so we can generate a new vtop
# file with all resources fixed.
$fix_current_resources = 1;
$updating = 1;
LoadCurrent();
$topfile = "$pid-$eid.fixed";
CreateTopFile();
}
return 0
if ($noregfree);
print STDERR "Removing physical state in regression mode\n";
if ($experiment->RemovePhysicalState() != 0) {
print STDERR "Could not remove physical state!\n";
......
......@@ -617,15 +617,31 @@ sub LoadCurrentResources($)
$self->counters()->{'reserved_virtcount'} = 0;
$self->counters()->{'reserved_physcount'} = 0;
return 0
if (!$self->fixcurrent());
$self->printdb("Loading fixed nodes\n");
$self->printdb("Loading current resources" .
($self->regression() ? " in regression mode" : "") . "\n");
my @nodelist = $self->experiment()->NodeList(0, 1);
return 0
if (!@nodelist);
if ($self->regression()) {
#
# In regression mode, we just store the p2v mapping for fixnode.
#
foreach my $pnode (@nodelist) {
my $vname = $pnode->vname();
my $node_id = $pnode->node_id();
if ($pnode->isvirtnode()) {
$self->fixednodes()->{$vname} = $pnode->phys_nodeid();
}
else {
$self->fixednodes()->{$vname} = $node_id;
}
}
return 0;
}
foreach my $pnode (@nodelist) {
my $vname = $pnode->vname();
my $node_id = $pnode->node_id();
......@@ -645,7 +661,6 @@ sub LoadCurrentResources($)
return -1;
}
if ($pnode->isvirtnode()) {
$self->fixednodes()->{$vname} = $pnode->node_id();
$self->counters()->{'reserved_virtcount'}++;
# Get the underlying physical node.
......@@ -654,17 +669,23 @@ sub LoadCurrentResources($)
tberror("Cannot map $pnode to its real physnode");
return -1;
}
my $ppnode_id = $ppnode->node_id();
$self->fixednodes()->{$vname} = $ppnode_id
if ($self->fixcurrent());
#
# Record the mappings.
#
$self->current_v2v()->{$vname} = $pnode;
$self->current_v2p()->{$vname} = $ppnode;
$self->current_v2v()->{$vname} = $pnode->node_id();
$self->current_v2p()->{$vname} = $ppnode->node_id();
push(@{ $self->current_p2v()->{$ppnode->node_id()} }, $vname);
# Mark the node as unused until later.
$pnode->_reuse("unused");
$ppnode->_reuse("unused");
$self->printdb("current v2p: $node_id ($ppnode_id) -> $vname\n");
}
else {
#
......@@ -675,13 +696,15 @@ sub LoadCurrentResources($)
return -1;
}
else {
$self->fixednodes()->{$vname} = $pnode->node_id();
$self->fixednodes()->{$vname} = $pnode->node_id()
if ($self->fixcurrent());
$self->counters()->{'reserved_physcount'}++;
#
# Record the mapping.
#
$self->current_v2p()->{$vname} = $pnode;
$self->current_v2p()->{$vname} = $pnode->node_id();
push(@{ $self->current_p2v()->{$node_id} }, $vname);
# Mark the node as unused until later.
$pnode->_reuse("unused");
......@@ -2719,7 +2742,7 @@ sub InterpNodes($)
# No changes once it goes into reboot.
;
}
elsif (defined($virtnode) && $virtnode->isvirtnode()) {
elsif (defined($virtnode) && $virtnode->_isvirtnode()) {
#
# A new virtual node on an existing physical node
# does not force the physnode to be rebooted; we can
......@@ -2927,6 +2950,16 @@ sub AllocNodes($)
$self->newreserved()->{$nodeid} = $nodeid;
$pnode->SetAllocState(TBDB_ALLOCSTATE_RES_INIT_DIRTY())
if (!$self->impotent());
#
# Fix all of the nodes assigned to the pnode.
#
foreach my $vname (@{ $self->solution_p2v()->{$nodeid} }) {
$self->fixednodes()->{$vname} = $nodeid;
# And add to the results for the next vtop print.
$self->addfixed("$vname $nodeid");
}
}
}
......
......@@ -50,7 +50,7 @@ sub usage ()
"implies -n\n";
exit($WRAPPER_FAILED);
}
my $optlist = "dvunfprqczxm:";
my $optlist = "dvunfprqczxm:k";
my $verbose = 0;
my $debug = 0;
my $fixmode = 0;
......@@ -61,10 +61,13 @@ my $prepass = 0;
my $mfactor;
my $regression = 0;
my $noassign = 0; # Only with regression mode, use previous solution.
my $noregfree = 0; # Only with regression mode, leave physical state at end.
my $usecurrent = 0; # Only with regression mode, use current solution.
my $quiet = 0;
my $clear = 0;
my $warnings = 0;
my $maxrun = 3; # Maximum number of times we run assign.
my $vtop;
#
# Configure variables
......@@ -95,6 +98,8 @@ sub debug($);
sub chat($);
sub RunAssign($$);
sub FinalizeRegression($);
sub AssignLoop();
sub MapperWrapper();
# un-taint path
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
......@@ -153,10 +158,13 @@ if (defined($options{"r"})) {
fatal("Cannot use regression mode on main DB");
}
$regression = 1;
$clear = 1;
$clear = 1;
$fixmode = 1;
TBDebugTimeStampsOn();
$noassign = 1
$usecurrent = 1
if (defined($options{"z"}));
$noregfree = 1
if (defined($options{"k"}));
}
if (defined($options{"q"})) {
$quiet = 1;
......@@ -183,6 +191,10 @@ if (!TBAdmin() &&
fatal("You do not have permission to map this experiment!");
}
# multiplex_factor default.
$mfactor = $experiment->multiplex_factor()
if (!defined($mfactor) && defined($experiment->multiplex_factor()));
#
# These are the flags to the vtop creation code.
#
......@@ -198,111 +210,130 @@ $vtopflags |= $libvtop::VTOP_FLAGS_IMPOTENT
$vtopflags |= $libvtop::VTOP_FLAGS_REGRESSION
if ($regression);
chat("Starting the new and improved mapper wrapper.\n");
MapperWrapper();
if ($regression) {
if (0) {
$updating = 1;
$fixmode = 1;
$clear = 0;
$vtopflags |=
($libvtop::VTOP_FLAGS_UPDATE|$libvtop::VTOP_FLAGS_FIXNODES);
TBDebugTimeStamp("Create libvtop started");
my $vtop = libvtop->Create($experiment, $vtopflags);
if (!defined($vtop)) {
fatal("Could not create vtop structure for $experiment");
MapperWrapper();
}
FinalizeRegression(0);
}
TBDebugTimeStamp("Create libvtop ended");
exit(0);
#
# If updating, load current experiment resources. We have to be careful
# of how this is merged in with the (new) desired topology.
#
if ($updating) {
TBDebugTimeStamp("LoadCurrentResources started");
$vtop->LoadCurrentResources() == 0
or fatal("Could not load current resources into mapper");
TBDebugTimeStamp("LoadCurrentResources ended");
}
if (!$impotent && ($updating || $clear)) {
if ($regression || $clear) {
chat("Freeing reserved nodes in regression mode\n");
system("export NORELOAD=1; $NFREE -x -a $pid $eid") == 0
or fatal("Could not release nodes in regression mode");
}
chat("Clearing physical state before updating.\n");
$experiment->RemovePhysicalState();
exit(0)
if ($clear && !$regression);
sub MapperWrapper()
{
chat("Starting the new and improved mapper wrapper.\n");
TBDebugTimeStamp("Create libvtop started");
$vtop = libvtop->Create($experiment, $vtopflags);
if (!defined($vtop)) {
fatal("Could not create vtop structure for $experiment");
}
TBDebugTimeStamp("Create libvtop ended");
#
# If updating, load current experiment resources. We have to be
# careful of how this is merged in with the (new) desired
# topology.
#
if ($updating || $usecurrent) {
TBDebugTimeStamp("LoadCurrentResources started");
$vtop->LoadCurrentResources() == 0
or fatal("Could not load current resources into mapper");
TBDebugTimeStamp("LoadCurrentResources ended");
}
if (!$impotent && ($updating || $clear)) {
if ($clear) {
chat("Freeing reserved nodes ...\n");
system("export NORELOAD=1; $NFREE -x -a $pid $eid") == 0
or fatal("Could not release nodes.");
}
chat("Clearing physical state before updating.\n");
$experiment->RemovePhysicalState();
exit(0)
if ($clear && !$regression);
}
TBDebugTimeStamp("vtopgen started");
$vtop->CreateVtop() == 0
or fatal("Could not create vtop for $experiment");
TBDebugTimeStamp("vtopgen finished");
AssignLoop();
return 0;
}
TBDebugTimeStamp("vtopgen started");
$vtop->CreateVtop() == 0
or fatal("Could not create vtop for $experiment");
TBDebugTimeStamp("vtopgen finished");
# multiplex_factor default.
$mfactor = $experiment->multiplex_factor()
if (!defined($mfactor) && defined($experiment->multiplex_factor()));
#
# The assign loop.
#
my $currentrun = 1;
my $canceled = 0;
my $tried_precheck = 0;
# Admission control counts
my %admission_control = ();
# XXX plab hack - only run assign once on plab topologies, since they're easy
# to map and the physical topology does not change frequently.
if ($vtop->plabcount() && $vtop->plabcount == $vtop->virtnodecount()) {
$maxrun = 2;
}
TBDebugTimeStamp("mapper loop started");
while (1) {
chat("Assign run $currentrun\n");
my $prefix = ($debug || $regression ? "$pid-$eid" : "$pid-$eid-$$");
sub AssignLoop()
{
my $currentrun = 1;
my $canceled = 0;
my $tried_precheck = 0;
# Admission control counts
my %admission_control = ();
#
# When precheck is on, we only do one run in impotent mode and exit.
#
if ($precheck) {
$prefix .= ".empty";
$impotent = 1;
chat("Trying assign on an empty testbed.\n");
# XXX plab hack - only run assign once on plab topologies, since
# they're easy to map and the physical topology does not change
# frequently.
if ($vtop->plabcount() && $vtop->plabcount == $vtop->virtnodecount()) {
$maxrun = 2;
}
#
# RunAssign returns 0 if successful.
# returns -1 if failure, but assign says to stop trying.
# returns 1 if failure, but assign says to try again.
# returns 2 if we made some forward progress.
#
my $retval = RunAssign($precheck, $prefix);
TBDebugTimeStamp("mapper loop started");
while (1) {
chat("Assign run $currentrun\n");
# Success!
last
if ($retval == 0);
my $prefix = ($debug || $regression ? "$pid-$eid" : "$pid-$eid-$$");
if ($retval < 0 || $regression) {
#
# Failure in assign.
# When precheck is on, we only do one run in impotent mode and exit.
#
FinalizeRegression(1)
if ($regression);
if ($precheck) {
$prefix .= ".empty";
$impotent = 1;
chat("Trying assign on an empty testbed.\n");
}
fatal({type => 'primary', severity => SEV_ERROR,
error => ['unretriable_assign_error']},
"Unretriable error. Giving up.");
}
#
# RunAssign returns 0 if successful.
# returns -1 if failure, but assign says to stop trying.
# returns 1 if failure, but assign says to try again.
# returns 2 if we made some forward progress.
#
my $retval = RunAssign($precheck, $prefix);
# Success!
last
if ($retval == 0);
if ($retval < 0 || $regression) {
#
# Failure in assign.
#
FinalizeRegression(1)
if ($regression);
fatal({type => 'primary', severity => SEV_ERROR,
error => ['unretriable_assign_error']},
"Unretriable error. Giving up.");
}
#
# When precheck is off, we do a precheck run if the first try fails
# to find a solution. This avoids looping on an unsolvable topology.
#
if (!$precheck && !$tried_precheck) {
chat("Trying assign on an empty testbed to verify mapability.\n");
my $save_impotent = $impotent;
$impotent = 1;
my $retval = RunAssign(1, $prefix . ".empty");
if ($retval != 0) {
fatal({type=>'extra', cause=>'user', severity=>SEV_ERROR,
#
# When precheck is off, we do a precheck run if the first try fails
# to find a solution. This avoids looping on an unsolvable topology.
#
if (!$precheck && !$tried_precheck) {
chat("Trying assign on an empty testbed to verify mapability.\n");
my $save_impotent = $impotent;
$impotent = 1;
my $retval = RunAssign(1, $prefix . ".empty");
if ($retval != 0) {
fatal({type=>'extra', cause=>'user', severity=>SEV_ERROR,
error=>['infeasible_resource_assignment']},
"This experiment cannot be instantiated on this ".
"testbed. You have most likely asked for hardware ".
......@@ -311,26 +342,25 @@ while (1) {
"interfaces. You will need to modify this experiment ".
"before it can be swapped in - re-submitting the ".
"experiment as-is will always result in failure.");
}
chat("Assign succeeded on an empty testbed.\n");
$impotent = $save_impotent;
$tried_precheck = 1;
}
chat("Assign succeeded on an empty testbed.\n");
$impotent = $save_impotent;
$tried_precheck = 1;
}
if ($currentrun >= $maxrun && $retval != 2) {
fatal({type => 'primary', severity => SEV_ERROR,
error => ['reached_assign_run_limit']},
"Reached run limit. Giving up.");
}
if ($currentrun >= $maxrun && $retval != 2) {
fatal({type => 'primary', severity => SEV_ERROR,
error => ['reached_assign_run_limit']},
"Reached run limit. Giving up.");
}
chat("Waiting 5 seconds and trying again...\n");
sleep(5);
$currentrun++;
chat("Waiting 5 seconds and trying again...\n");
sleep(5);
$currentrun++;
}
TBDebugTimeStamp("mapper loop finished");
return 0;
}
TBDebugTimeStamp("mapper loop finished");
FinalizeRegression(0)
if ($regression);
exit(0);
#
# The guts of an assign run.
......@@ -436,10 +466,10 @@ sub RunAssign($$)
}
# Run assign
# my $cmd = "assign-dev";
# my $args = "-P -x $ptopfile -y $vtopfile";
my $cmd = "assign";
my $args = "-P $ptopfile $vtopfile";
my $cmd = "assign-dev";
my $args = "-P -x $ptopfile -y $vtopfile";
# my $cmd = "assign";
# my $args = "-P $ptopfile $vtopfile";
$args = "-uod -c .75 $args"
if ($vtop->virtnodecount() || $vtop->simnodecount());
$args = "-n $args"
......@@ -447,7 +477,7 @@ sub RunAssign($$)
$args = "-s 123456 $args"
if ($regression);
# The prepass speeds up assign on big tops with virtual nodes.
# The prepass speeds up assign on big topos with virtual nodes.
if ($prepass) {
$cmd = "assign_prepass";