Commit 662972cd authored by Kirk Webb's avatar Kirk Webb

Refactor taintstate code and move final taint updates to stated.

Can't do the untainting for all cases in libosload*.  The untainting
is now hooked into stated, where we catch the nodes as they send
along their "RELOADDONE" events to update their taint state according
to the final state of their partitions.
parent 1de4e516
......@@ -49,7 +49,8 @@ LIB_SCRIPTS = libdb.pm Node.pm libdb.py libadminctrl.pm Experiment.pm \
emdb.pm emutil.pm Firewall.pm VirtExperiment.pm libGeni.pm \
libEmulab.pm EmulabConstants.pm TraceUse.pm \
EmulabFeatures.pm Port.pm BlockstoreType.pm Blockstore.pm \
IPBuddyAlloc.pm IPBuddyWrapper.pm Lease.pm Quota.pm
IPBuddyAlloc.pm IPBuddyWrapper.pm Lease.pm Quota.pm \
libTaintStates.pm
# Stuff installed on plastic.
USERSBINS = genelists.proxy dumperrorlog.proxy backup
......
......@@ -3742,97 +3742,44 @@ sub ClrTipAclUrl($)
"where node_id='$node_id'");
}
#
# Check to see if the node is tainted, or tainted in a
# particular way.
#
sub IsTainted($;$)
{
my ($self, $taint) = @_;
my $taint_states = $self->taint_states();
return 0
if (!defined($taint_states) || $taint_states eq "");
# Just looking to see if any taint is applied?
return 1
if (!defined($taint));
# Looking for a specific taint.
return grep {$_ eq $taint} split(',', $taint_states);
}
#
# Get the current set of taint states for the Node
#
# Stubs for calling "libTaintStates" common taint handling code
sub GetTaintStates($) {
my ($self) = @_;
require libTaintStates;
my $taint_states = $self->taint_states();
return ()
if (!defined($taint_states) || $taint_states eq "");
return split(',', $taint_states);
return libTaintStates::GetTaintStates($self);
}
sub IsTainted($;$) {
my ($self, $taint) = @_;
require libTaintStates;
#
# Explicitly set the taint states based on an input array of states.
# Squash any duplicates or empty/undefined entries.
#
return libTaintStates::IsTainted($self, $taint);
}
sub SetTaintStates($@) {
my ($self, @taint_states) = @_;
require libTaintStates;
my @newtstates = ();
my @validtstates = TB_TAINTSTATE_ALL();
foreach my $tstate (@taint_states) {
next if (!$tstate);
if (!grep {$_ eq $tstate} @validtstates) {
warn "Invalid taint state: $tstate\n";
return -1;
}
if (!grep {$_ eq $tstate} @newtstates) {
push @newtstates, $tstate;
}
}
return 0
if (!@newtstates);
return $self->Update({"taint_states" => join(',', @newtstates)});
return libTaintStates::SetTaintStates($self, @taint_states);
}
#
# Add a taint state to the node.
#
sub AddTaint($$)
{
sub AddTaintState($$) {
my ($self, $taint) = @_;
require libTaintStates;
if (!grep {$_ eq $taint} TB_TAINTSTATE_ALL()) {
warn "Invalid taint state: $taint\n";
return -1;
}
return 0
if ($self->IsTainted($taint));
my $taint_states = $self->taint_states();
if (!defined($taint_states) || $taint_states eq "") {
$taint_states = $taint;
}
else {
$taint_states .= ",$taint";
}
return libTaintStates::AddTaintState($self, $taint);
}
sub RemoveTaintState($;$) {
my ($self, $taint) = @_;
require libTaintStates;
return $self->Update({"taint_states" => $taint_states});
return libTaintStates::RemoveTaintState($self, $taint);
}
#
# Inherit the taint states from an OS. Take the union with whatever
# taint states are already set for the node.
#
sub InheritTaintStates($$) {
sub InheritTaintStates($$)
{
my ($self, $osinfo) = @_;
require OSinfo;
......@@ -3845,40 +3792,40 @@ sub InheritTaintStates($$) {
$osinfo = $tmp;
}
my $os_taint_states = $osinfo->taint_states();
my @taint_states = $osinfo->GetTaintStates();
return 0
if (!defined($os_taint_states) || $os_taint_states eq "");
my @taint_states = split(',', $os_taint_states);
my $node_taint_states = $self->taint_states();
if ($node_taint_states) {
push @taint_states, split(',', $node_taint_states);
}
if (!@taint_states);
push @taint_states, $self->GetTaintStates();
return $self->SetTaintStates(@taint_states);
}
#
# Remove a taint state (or all taint states) from the node.
# Synchornize the node's taint states based on the OSes listed
# for its partitions.
#
sub RemoveTaint($;$)
sub SyncTaintStates($)
{
my ($self, $taint) = @_;
my ($self) = @_;
require OSinfo;
return 0
if (!$self->IsTainted($taint));
my $node_id = $self->node_id();
my $taint_states = $self->taint_states();
return 0
if (!defined($taint_states) || $taint_states eq "");
my $query_result =
DBQueryWarn("select osid from partitions ".
"where node_id='$node_id' and osid is not null");
return -1
if (!$query_result);
if (defined($taint)) {
$taint_states = join(',',
grep {$_ ne $taint} split(',', $taint_states));
} else {
$taint_states = "";
my @taint_states = ();
while (my ($osid) = $query_result->fetchrow_array()) {
my $osinfo = OSinfo->Lookup($osid);
if (defined($osinfo) && $osinfo->IsTainted()) {
push @taint_states, $osinfo->GetTaintStates();
}
}
return $self->Update({"taint_states" => $taint_states});
return $self->SetTaintStates(@taint_states);
}
# _Always_ make sure that this 1 is at the end of the file...
......
......@@ -730,114 +730,36 @@ sub MapToImage($$)
return Image->Lookup($imageid);
}
#
# Check if the OS is tainted, or tainted in a
# particular way.
#
sub IsTainted($;$)
{
my ($self, $taint) = @_;
my $taint_states = $self->taint_states();
return 0
if (!defined($taint_states) || $taint_states eq "");
# Just looking to see if any taint is applied?
return 1
if (!defined($taint));
# Looking for a specific taint.
return grep {$_ eq $taint} split(',', $taint_states);
}
#
# Get the current set of taint states for the OS
#
sub GetTaintStates() {
# Stubs for calling "libTaintStates" common taint handling code
sub GetTaintStates($) {
my ($self) = @_;
require libTaintStates;
my $taint_states = $self->taint_states();
return ()
if (!defined($taint_states) || $taint_states eq "");
return split(',', $taint_states);
return libTaintStates::GetTaintStates($self);
}
sub IsTainted($;$) {
my ($self, $taint) = @_;
require libTaintStates;
#
# Explicitly set the taint states based on an input array of states.
# Squash any duplicates or empty/undefined entries.
#
return libTaintStates::IsTainted($self, $taint);
}
sub SetTaintStates($@) {
my ($self, @taint_states) = @_;
require libTaintStates;
my @newtstates = ();
my @validtstates = TB_TAINTSTATE_ALL();
foreach my $tstate (@taint_states) {
next if (!$tstate);
if (!grep {$_ eq $tstate} @validtstates) {
warn "Invalid taint state: $tstate\n";
return -1;
}
if (!grep {$_ eq $tstate} @newtstates) {
push @newtstates, $tstate;
}
}
return 0
if (!@newtstates);
return $self->Update({"taint_states" => join(',', @newtstates)});
return libTaintStates::SetTaintStates($self, @taint_states);
}
#
# Add a taint state to the OS.
#
sub AddTaint($$)
{
sub AddTaintState($$) {
my ($self, $taint) = @_;
require libTaintStates;
if (!grep {$_ eq $taint} TB_TAINTSTATE_ALL()) {
warn "Invalid taint state: $taint\n";
return -1;
}
return 0
if ($self->IsTainted($taint));
my $taint_states = $self->taint_states();
if (!defined($taint_states) || $taint_states eq "") {
$taint_states = $taint;
}
else {
$taint_states .= ",$taint";
}
return $self->Update({"taint_states" => $taint_states});
return libTaintStates::AddTaintState($self, $taint);
}
#
# Remove a taint state (or all taint states) from the OS.
#
sub RemoveTaint($;$)
{
sub RemoveTaintState($;$) {
my ($self, $taint) = @_;
require libTaintStates;
return 0
if (!$self->IsTainted($taint));
my $taint_states = $self->taint_states();
return 0
if (!defined($taint_states) || $taint_states eq "");
if (defined($taint)) {
$taint_states = join(',',
grep {$_ ne $taint} split(',', $taint_states));
} else {
$taint_states = "";
}
return $self->Update({"taint_states" => $taint_states});
return libTaintStates::RemoveTaintState($self, $taint);
}
# _Always_ make sure that this 1 is at the end of the file...
......
#!/usr/bin/perl -wT
#
# Copyright (c) 2014 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
#
# Common Emulab taint handling code. These functions are meant to
# work with DB object abstractions, such as 'OSinfo' and 'Node'
# objects. For an object abstraction to be compatible with this
# library, it needs to have a 'taint_states' DB column. This column
# must then be accessible via a taint_states() method, and must be
# defined as a SQL set with the same set member options as in the
# "nodes" and "os_info" tables. Finally, the object abstraction must
# have an Update() method, with the same semantics as those found in
# the Node and OSinfo objects.
#
package libTaintStates;
use strict;
use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw();
use EmulabConstants;
use English;
# Function prototypes
sub GetTaintStates($);
sub IsTainted($;$);
sub SetTaintStates($@);
sub AddTaint($$);
sub RemoveTaint($;$);
#
# Return the current set of taint states as an array.
#
sub GetTaintStates($)
{
my ($obj) = @_;
if (!ref($obj)) {
warn "First argument is not an object!\n";
return undef;
}
my $taint_states = $obj->taint_states();
return ()
if (!defined($taint_states) || $taint_states eq "");
return split(',', $taint_states);
}
#
# Check to see if the object is tainted, or tainted in a
# particular way.
#
sub IsTainted($;$)
{
my ($obj, $taint) = @_;
if (!ref($obj)) {
warn "First argument is not an object!\n";
return -1;
}
my @taint_states = GetTaintStates(@obj);
return 0
if (!@taint_states);
# Just looking to see if any taint is applied?
return 1
if (!defined($taint));
# Looking for a specific taint.
return grep {$_ eq $taint} @taint_states;
}
#
# Explicitly set the taint states based on an input array of states.
# Squash any duplicates or empty/undefined entries.
#
sub SetTaintStates($@)
{
my ($obj, @taint_states) = @_;
if (!ref($obj)) {
warn "First argument is not an object!\n";
return -1;
}
my %newtstates = ();
my %validtstates = map {$_ => 1} TB_TAINTSTATE_ALL();
foreach my $tstate (@taint_states) {
next if (!$tstate);
if (!exists($validtstates{$tstate})) {
warn "Invalid taint state: $tstate\n";
return -1;
}
$newtstates{$tstate} = 1;
}
my $upd_str = scalar(keys %newtstates) ?
join(',', keys %newtstates) : "NULL";
return $obj->Update({"taint_states" => $upd_str});
}
#
# Add a taint state to the object.
#
sub AddTaint($$)
{
my ($obj, $taint) = @_;
if (!ref($obj)) {
warn "First argument is not an object!\n";
return -1;
}
return -1
if (!defined($taint));
if (!grep {$_ eq $taint} TB_TAINTSTATE_ALL()) {
warn "Invalid taint state: $taint\n";
return -1;
}
return 0
if (IsTainted($obj, $taint));
my @taint_states = GetTaintStates($obj);
push @taint_states, $taint;
return SetTaintStates($obj, @taint_states);
}
#
# Remove a taint state (or all taint states).
#
sub RemoveTaint($;$)
{
my ($obj, $taint) = @_;
if (!ref($obj)) {
warn "First argument is not an object!\n";
return -1;
}
my @taint_states = GetTaintStates($obj);
return 0
if (!@taint_states);
my @ntstates = ();
if (defined($taint)) {
@ntstates = grep {$_ ne $taint} @taint_states;
}
return SetTaintStates($obj, @ntstates);
}
# Next line required by perl for modules
1;
......@@ -1161,6 +1161,7 @@ sub handleCtrlEvent($$) {
info("$node: Clearing reload info\n");
$nodeobj->ClearCurrentReload();
$nodeobj->FlushReserved();
$nodeobj->SyncTaintStates();
my $experiment = $nodeobj->Reservation();
if (defined($experiment) &&
$experiment->pid() eq NODERELOADING_PID &&
......
......@@ -258,7 +258,7 @@ sub osload ($$) {
# to zero all node disks, then capture that here too.
my $zeronode = 0;
if ($nodeobject->IsTainted()) {
$zeronode = 2; # use maximum firepower.
$zeronode = 2;
}
elsif ($zerofree) {
$zeronode = $zerofree;
......@@ -311,7 +311,6 @@ sub osload ($$) {
my $defosid;
my $maxwait = 0;
my @access_keys;
my @tstates = ();
#
# Most of the DB work related to images is determining what
......@@ -471,14 +470,10 @@ sub osload ($$) {
# until OS loading and disk zeroing have been
# performed.
my $osinfo = OSinfo->Lookup($osid);
if (defined($osinfo)) {
if ($osinfo->IsTainted()) {
# Save new/incoming taint states for later...
push @tstates, $osinfo->GetTaintStates();
$nodeobject->InheritTaintStates($osinfo) == 0 or
warn "Node $node could not inherit taint ".
"states from osid $osid\n";
}
if (defined($osinfo) && $osinfo->IsTainted()) {
$nodeobject->InheritTaintStates($osinfo) == 0 or
warn "Node $node could not inherit taint ".
"states from osid $osid\n";
}
my %part = (
......@@ -622,7 +617,6 @@ sub osload ($$) {
'reboot' => $reboot_required,
'wait' => $wait_required,
'zerofree' => $zeronode,
'tstates' => \@tstates,
'prepare' => $prepare,
'maxwait' => $maxwait,
'isremote' => $isremote,
......@@ -1044,20 +1038,6 @@ sub WaitTillReloadDone($$$$$@)
print STDERR "osload ($node): left reloading mode at ".`date`
if ($debug);
#
# Now reset tainting for the node. Start off
# with a clean slate. Next, apply any taint states
# previously identified and saved off for the
# still-existing and/or newly-loaded OSes. Doing this
# now allows us to clear any remnant taint states
# nullified by OS loading and/or disk zeroing.
#
$nodeobject->RemoveTaint();
my $tstates = $reload_info->{$node}{'tstates'};
if (@{$tstates}) {
$nodeobject->SetTaintStates(@{$tstates});
}
$count--;
$done{$node} = 1;
next;
......
......@@ -630,7 +630,7 @@ sub osload($$$) {
# Check to see if the node is tainted. If so, then the disk
# needs to be cleaned up (zeroed).
if ($nodeobject->IsTainted()) {
$nargs{'zerofree'} = 2; # use maximum firepower.
$nargs{'zerofree'} = 2;
}
# Wait, don't do this -- waitmode is global; nowait is per-node!
......@@ -1144,21 +1144,6 @@ sub WaitTillReloadDone($$$$$@)
}
else {
# success!
#
# Now reset tainting for the node. Start off
# with a clean slate. Next, apply any taint states
# previously identified and saved off for the
# still-existing and/or newly-loaded OSes. Doing this
# now allows us to clear any remnant taint states
# nullified by OS loading and/or disk zeroing.
#
$nodeobject->RemoveTaint();
my $tstates = $self->nodeinfo($nodeobject,'tstates');
if (@{$tstates}) {
$nodeobject->SetTaintStates(@{$tstates});
}
$count--;
$done{$node} = 1;
$typeobject->ReloadDone($nodeobject);
......@@ -2005,7 +1990,6 @@ sub UpdatePartitions($$)
#
my %partitions = ();
my $curmbrvers = 0;
my @tstates = ();
#
# XXX assumes a DOS MBR, but this is ingrained in the DB schema
......@@ -2111,14 +2095,10 @@ sub UpdatePartitions($$)
# existing states until OS loading and disk
# zeroing have been performed.
my $osinfo = OSinfo->Lookup($osid);
if (defined($osinfo)) {
if ($osinfo->IsTainted()) {
# Save new/incoming taint states for later...
push @tstates, $osinfo->GetTaintStates();
$nodeobject->InheritTaintStates($osinfo) == 0 or
warn "Node $node_id could not inherit taint ".
"states from osid $osid\n";
}
if (defined($osinfo) && $osinfo->IsTainted()) {
$nodeobject->InheritTaintStates($osinfo) == 0 or
warn "Node $node_id could not inherit taint ".
"states from osid $osid\n";
}
my %part = (
......@@ -2137,9 +2117,6 @@ sub UpdatePartitions($$)
}
}
# Store the taint states for this node object
$self->nodeinfo($nodeobject,'tstates',\@tstates);
#
# Now that we have processed all images, update the actual DB
# partitions table entries for this node.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment