Commit 259c9475 authored by Leigh Stoller's avatar Leigh Stoller

Rework the vlag tag reservation code so that it works properly from

within an elabinelab.

snmpit -A -C can now take a list of vlanids to work on
(reserve,unreserve) and -A can take a block of tags to try, as for
coordinating tags with another protogeni CM.

Add a new elabinelab proxy version until my changes make it out.
parent bfa21396
......@@ -58,6 +58,7 @@ sub doSetOpenflowController($$$);
sub doSetOpenflowListener($$$);
sub doEnableOpenflowListener($$);
sub doReserveVlanTags($$@);
sub doUnReserveVlanTags($$@);
#
# Defaults
......@@ -177,12 +178,11 @@ my @SAVEARGV = @ARGV;
my %opt = ();
Getopt::Long::Configure("no_ignore_case");
GetOptions(\%opt,
'a','c','d','e','b','B=s@','g','h','i=s@','l+','m=s@','M','n', 'A',
'N=s@','o=s@','p=s','q','r','s', 'S=s@','t','E=s','T=s','u=s','U','v=s','w',
'a','c','d','e','b','B=s@','g','h','i=s@','l+','m=s@','M','n', 'A', 'C',
'N=s@','p=s','q','r','s', 'S=s@','t','E=s','T=s','u=s','U','v=s','w',
'y=s','x=s','z=s','F','L=s','O', 'D', 'R', 'f', 'X', 'Z', 'vlan_tag=i',
'of-disable=s', 'of-enable=s', 'of-controller=s', 'of-listener=s',
'redirect-err');
# Unused: f,j
'o=s@{1,1}', 'redirect-err');
if ($opt{h}) {
exit &usage;
......@@ -220,7 +220,8 @@ my $pid;
my $eid;
my $experiment;
my @ports;
my @optvlanids = ();
my @optvlanids = ();
my %optvlantags = ();
my $equaltrunking = 0;
my $this_user;
my $ofconnstr; # Openflow connection string, for controller
......@@ -255,18 +256,36 @@ if ($opt{m} || $opt{o}) {
($pid, $eid) = (shift @ARGV, shift @ARGV);
}
}
if ($opt{t} || $opt{r} || $opt{D} || $opt{R} || $opt{X} || $opt{A}) {
if ($opt{t} || $opt{r} || $opt{D} || $opt{R} || $opt{X} || $opt{A} || $opt{C}) {
#
# Options that take 'pid eid'
#
if (@ARGV < 2) {
tberror "pid/eid reqired!";
exit &usage;
} else {
($pid, $eid) = (shift @ARGV, shift @ARGV);
if (!defined($pid)) {
if (@ARGV < 2) {
tberror "pid/eid reqired!";
exit &usage;
} else {
($pid, $eid) = (shift @ARGV, shift @ARGV);
}
}
if (@ARGV) {
@optvlanids = @ARGV;
if ($opt{A} || ($opt{C} && !($opt{r} || $opt{o}))) {
# Allow a comma separated list of tags per lan.
foreach my $arg (@ARGV) {
if ($arg =~ /^[-\w]*,\d*/) {
my @tmp = split(',', $arg);
my $id = shift(@tmp);
push(@optvlanids, $id);
$optvlantags{$id} = \@tmp;
}
else {
push(@optvlanids, $arg);
}
}
}
else {
@optvlanids = @ARGV;
}
}
} elsif ($opt{d} || $opt{e} || $opt{a} || $opt{p} || $opt{u} || $opt{m}
|| $opt{U} || $opt{b}) {
......@@ -351,6 +370,9 @@ if ($opt{b}) { push @commands, ["portstatus"]; }
if ($opt{F}) { push @commands, ["synchleader"]; }
if ($opt{A}) { push @commands, ["reservetags"]; }
# This option combines with reset/remove
if ($opt{C} && !($opt{r} || $opt{o})) { push @commands, ["unreservetags"]; }
#
# Commands that can appear once, and take an argument
#
......@@ -676,7 +698,7 @@ COMMAND: foreach my $command (@commands) {
}
last;
};
(/^tables$/ || /^reservetags$/) && do {
(/^tables$/ || /^reservetags$/ || /^unreservetags$/) && do {
# Grab all stacks that any ports in the experiment are members
# of.
# (We need the entire stack, since the VLAN may have to traverse
......@@ -720,6 +742,14 @@ COMMAND: foreach my $command (@commands) {
if (!$quiet);
next COMMAND;
}
if ($ELABINELAB) {
#
# Do not worry about the local state. Always go to outer
# boss to make sure that the vlans are removed.
#
@devicenames = getTestSwitches();
last;
}
@vlans = filterPlannedVlans(@vlans);
if (!@vlans) {
print "snmpit: $pid/$eid has VLANs, but none have been " .
......@@ -985,6 +1015,10 @@ COMMAND: foreach my $command (@commands) {
$exitval += doReserveVlanTags($experiment,\@stacks,@vlans);
last;
}; # /reservetags/ && do
/^unreservetags$/ && do {
$exitval += doUnReserveVlanTags($experiment,\@stacks,@vlans);
last;
}; # /reservetags/ && do
/synctables/ && do {
$exitval += syncVlansFromTables($experiment,\@stacks);
last;
......@@ -1787,7 +1821,8 @@ sub doReset($@) {
# Hand over to outer boss.
#
if ($ELABINELAB) {
return RemoteDoReset($experiment, scalar(@optvlanids) == 0, @vlans);
return RemoteDoReset($experiment,
scalar(@optvlanids) == 0, $opt{C}, @vlans);
}
my $errors = 0;
......@@ -1807,6 +1842,8 @@ sub doReset($@) {
}
foreach my $vlan (@existant_vlans) {
setVlanTag($vlan, 0);
clearReservedVlanTag($vlan)
if ($opt{C});
VLan->RecordVLanDeletion($vlan);
}
}
......@@ -2132,8 +2169,10 @@ sub doReserveVlanTags($$@) {
my $errors = 0;
my @stacknames = map { $_->{STACKID} } @$stacks;
print STDERR "@stacknames\n";
if ($ELABINELAB) {
return RemoteDoReserveVlanTags($experiment, \%optvlantags, @vlanids);
}
#
# First do sanity checks on the entire set of vlans
#
......@@ -2150,6 +2189,10 @@ sub doReserveVlanTags($$@) {
$errors++;
next;
}
elsif (@planned == 0) {
# Default to the provided stack. Correct thing to do?
@planned = @stacknames;
}
my $stack = $planned[0];
if (! (grep {$_ eq $stack} @stacknames)) {
......@@ -2168,29 +2211,121 @@ sub doReserveVlanTags($$@) {
my @assigned = ();
foreach my $vlan (values(%vlans)) {
my $vlanid = $vlan->id();
my $stack = $vstacks{"$vlanid"};
my $tag = getReservedVlanTag($vlanid);
my $vlanid = $vlan->id();
my $stack = $vstacks{"$vlanid"};
my $isblock = 0;
my @tags = ();
# Do nothing if tag already assigned.
next
if ($tag);
#
# Option to assign a specific tag. This needs to be better.
#
if (exists($optvlantags{$vlanid})) {
@tags = @{ $optvlantags{$vlanid} };
# In block mode (multiple tags) do not signal an error on failure.
# Caller will figure it out.
$isblock = (scalar(@tags) > 1 ? 1 : 0);
}
elsif ($vlan->GetReservedVlanTag()) {
# Wanted any tag, but lan has a tag.
next;
}
again:
if (@tags) {
$next_vlan_tag = pop(@tags);
}
#
# If we can assign a tag, remember we did so that we can
# undo whatever we did, if there is an error.
#
$tag = $stack->newVlanNumber($vlanid);
if ($tag <= 0) {
my $tag = $stack->newVlanNumber($vlanid);
if ($tag) {
push(@assigned, [$vlan,$tag]);
}
elsif (!$isblock) {
print STDERR "Could not pre-reserve tag for $vlan\n";
$errors++;
last;
}
push(@assigned, $vlan);
goto again
if (@tags);
}
if ($errors) {
foreach my $vlan (@assigned) {
clearReservedVlanTag($vlan->id());
foreach my $ref (@assigned) {
my ($vlan, $tag) = @{ $ref };
$vlan->ClearReservedVlanTag($tag);
}
}
return $errors;
}
sub doUnReserveVlanTags($$@) {
my $experiment = shift;
my $stacks = shift;
my @vlanids = @_;
my %vlans = ();
my %vstacks = ();
my $errors = 0;
my @stacknames = map { $_->{STACKID} } @$stacks;
if ($ELABINELAB) {
return RemoteDoUnReserveVlanTags($experiment, \%optvlantags, @vlanids);
}
#
# First do sanity checks on the entire set of vlans
#
foreach my $id (@vlanids) {
my $vlan = VLan->Lookup($id);
if (!defined($vlan)) {
die("Could not locate vlan $id in the DB\n");
}
$vlans{"$id"} = $vlan;
my @planned = getPlannedStacksForVlans($id);
if (@planned > 1) {
print STDERR "$vlan crosses multiple stacks. Cannot reserve tag\n";
$errors++;
next;
}
elsif (@planned == 0) {
# Default to the provided stack. Correct thing to do?
@planned = @stacknames;
}
my $stack = $planned[0];
if (! (grep {$_ eq $stack} @stacknames)) {
print STDERR "$vlan is in stack $stack, but not in given stacks!\n";
$errors++;
next;
}
#
# Not allowed to clear the reservation if the lan exists.
#
if ($stack_ids{$stack}->vlanExists($id)) {
print STDERR
"$vlan exists on stack $stack; cannot clear reserved tag\n";
$errors++;
next;
}
}
return $errors
if ($errors);
#
# Now clear the reservations.
#
foreach my $vlan (values(%vlans)) {
my $vlanid = $vlan->id();
if (exists($optvlantags{$vlanid})) {
foreach my $tag (@{ $optvlantags{$vlanid} }) {
$vlan->ClearReservedVlanTag($tag);
}
}
else {
$vlan->ClearReservedVlanTag();
}
}
return $errors;
......@@ -2378,7 +2513,11 @@ sub doDeleteVlan($@) {
#
if (defined($experiment)) {
foreach my $vlan_name (@vlan_names) {
my $vlan = VLan->Lookup($experiment, $vlan_name);
my $vlan = VLan->Lookup($vlan_name);
if (defined($vlan) &&
!$experiment->SameExperiment($vlan->GetExperiment())) {
die("$vlan is not in the correct experiment\n");
}
if (!defined($vlan)) {
die("VLan object for $vlan_name does not exist\n");
}
......@@ -2400,7 +2539,7 @@ sub doDeleteVlan($@) {
if ($ELABINELAB) {
if (defined($experiment)) {
foreach my $vlan (values(%vlans)) {
if (RemoteDeleteVlan($vlan) == 0) {
if (RemoteDeleteVlan($vlan, $opt{C}) == 0) {
if ($vlan->Destroy() != 0) {
print STDERR "*** Could not destroy $vlan\n";
}
......@@ -2415,7 +2554,7 @@ sub doDeleteVlan($@) {
foreach my $vid (values(%vlan_ids)) {
push(@vidlist, $vid);
}
$errors = RemoteDoReset(undef, 0, @vidlist);
$errors = RemoteDoReset(undef, 0, $opt{C}, @vidlist);
}
return $errors;
}
......@@ -2462,15 +2601,23 @@ sub doDeleteVlan($@) {
#
next
if ($errors && exists($notdeleted{$vlan_name}));
# Always delete from the vlans table.
VLan->RecordVLanDeletion($vlan_id) == 0
or $errors++;
next
if (!exists($vlans{$vlan_name}));
if (!exists($vlans{$vlan_name})) {
if ($opt{C}) {
VLan::ClearReservedVlanTag($vlan_name) == 0
or $errors++;
}
next;
}
my $vlan = $vlans{$vlan_name};
$vlan->ClearReservedVlanTag()
if ($opt{C});
if ($vlan->IsManual()) {
$vlan->Destroy() == 0
or $errors++;
......
This diff is collapsed.
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2004-2010 University of Utah and the Flux Group.
# Copyright (c) 2004-2011 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -12,8 +12,10 @@ package snmpit_remote;
use Exporter;
@ISA = ("Exporter");
@EXPORT = qw( RemoteDoVlansFromTables RemoteDoReset RemoteDoPortControl
RemoteDoTrunking RemoteDoList RemoteMakeVlan RemoteDeleteVlan);
@EXPORT = qw(RemoteDoVlansFromTables RemoteDoReset RemoteDoPortControl
RemoteDoTrunking RemoteDoList RemoteMakeVlan RemoteDeleteVlan
RemoteDoReserveVlanTags RemoteDoUnReserveVlanTags);
# Must come after package declaration!
use lib '@prefix@/lib';
......@@ -24,6 +26,7 @@ use libtblog qw(tbdie);
use snmpit_lib;
use Lan;
use libxmlrpc;
use Data::Dumper;
use RPC::XML;
# Configure variables
......@@ -74,7 +77,7 @@ sub commonTail($$)
my $args = {"op" => $op, "args" => $arg};
my ($obj) = RPC::XML::smart_encode($args);
my $response = libxmlrpc::CallMethod0("elabinelab", "vlansv2",
my $response = libxmlrpc::CallMethod0("elabinelab", "vlansv3",
{"xmldoc" => $obj->as_string()});
# sklower is temporarily going to way violate layering
......@@ -84,7 +87,7 @@ sub commonTail($$)
if (($config{"verbose"} || $response->{"code"}) &&
defined($response->{"output"}) && $response->{"output"} ne "") {
print $response->{"output"};
print STDERR $response->{"output"};
}
return $response;
}
......@@ -223,27 +226,30 @@ sub RemoteMakeVlan($$@)
return 0;
}
sub RemoteDeleteVlan($)
sub RemoteDeleteVlan($$)
{
my $vlan = shift();
my $cleartags = shift();
return RemoteDoReset(undef, 0, ($vlan->id()));
return RemoteDoReset(undef, 0, $cleartags, ($vlan->id()));
}
#
# Ask outer boss to tear down a bunch of vlans for an experiment.
#
sub RemoteDoReset($$@)
sub RemoteDoReset($$$@)
{
my $experiment = shift();
my $cleartrunks = shift();
my $cleartags = shift();
my @vlans = @_;
return 0
if (! @vlans);
my $arg = {};
$arg->{"cleartrunks"} = $cleartrunks;
$arg->{"cleartrunks"} = ($cleartrunks ? 1 : 0);
$arg->{"cleartags"} = ($cleartags ? 1 : 0);
$arg->{"vlans"} = [ @vlans ];
my $response = commonTail("destroy", $arg);
......@@ -251,6 +257,8 @@ sub RemoteDoReset($$@)
if ($response->{"code"});
foreach my $vlan (@vlans) {
clearReservedVlanTag($vlan)
if ($cleartags);
VLan->RecordVLanDeletion($vlan);
}
return 0;
......@@ -368,5 +376,136 @@ sub RemoteDoList(@)
return @list;
}
#
# Ask outer boss to reserve vlan tags
#
sub RemoteDoReserveVlanTags($$@)
{
my $experiment = shift();
my $optvlantags= shift();
my @vlanids = @_;
my $vlantable = {};
my %vlans = ();
return 0
if (! @vlanids);
my $prefix = "" ;
TBGetSiteVar("federation/localprefix",\$prefix);
foreach my $vlanid (@vlanids) {
my $vlan = VLan->Lookup($vlanid);
return -1
if (!defined($vlan));
$vlans{"$vlanid"} = $vlan;
}
foreach my $id (keys(%vlans)) {
my $vlan = $vlans{"$id"};
$vlantable->{$id} = {};
$vlantable->{$id}->{"virtual"} = $vlan->vname();
$vlantable->{$id}->{"stack"} = $vlan->GetClass();
$vlantable->{$id}->{"tags"} = $optvlantags->{$id}
if (exists($optvlantags->{$id}));
}
return 0
if (! keys(%$vlantable));
my $errors = 0;
my $response = commonTail("reserve", $vlantable);
return 1
if ($response->{"code"} != RESPONSE_SUCCESS());
my $xmlback = $response->{"value"};
if (defined($xmlback) && $xmlback ne "") {
foreach my $vlres (split ',', $xmlback) {
my ($vlanid, $tagstr) = split '#', $vlres;
my @tags = split(':', $tagstr);
if (!exists($vlans{$vlanid})) {
print STDERR "Bad vlanid $vlanid in returned vlan map\n";
$errors++;
next;
}
my $vlan = $vlans{$vlanid};
# This should never fail ...
foreach my $tag (@tags) {
if (! $vlan->ReserveVlanTag($tag)) {
print STDERR "could not reserve vlan tag $tag for $vlan\n";
$errors++;
}
}
}
}
else {
$errors = 1;
}
return $errors;
}
#
# Ask outer boss to unreserve vlan tags
#
sub RemoteDoUnReserveVlanTags($$@)
{
my $experiment = shift();
my $optvlantags= shift();
my @vlanids = @_;
my $vlantable = {};
my %vlans = ();
return 0
if (! @vlanids);
my $prefix = "" ;
TBGetSiteVar("federation/localprefix",\$prefix);
foreach my $vlanid (@vlanids) {
my $vlan = VLan->Lookup($vlanid);
return -1
if (!defined($vlan));
$vlans{"$vlanid"} = $vlan;
}
foreach my $id (keys(%vlans)) {
my $vlan = $vlans{"$id"};
$vlantable->{$id} = {};
$vlantable->{$id}->{"virtual"} = $vlan->vname();
$vlantable->{$id}->{"stack"} = $vlan->GetClass();
$vlantable->{$id}->{"tags"} = $optvlantags->{$id}
if (exists($optvlantags->{$id}));
}
return 0
if (! keys(%$vlantable));
my $errors = 0;
my $response = commonTail("unreserve", $vlantable);
return 1
if ($response->{"code"} != RESPONSE_SUCCESS());
foreach my $id (keys(%vlans)) {
my $vlan = $vlans{"$id"};
if (exists($optvlantags->{$id})) {
foreach my $tag (@{ $optvlantags->{$id} }) {
if ($vlan->ClearReservedVlanTag($tag) != 0) {
print STDERR
"could not clear reserved vlan tag for $vlan\n";
$errors++;
}
}
}
else {
if ($vlan->ClearReservedVlanTag() != 0) {
print STDERR "could not clear reserved vlan tag for $vlan\n";
$errors++;
}
}
}
return $errors;
}
# End with true
1;
......@@ -404,6 +404,7 @@ sub newVlanNumber($$) {
my $self = shift;
my $vlan_id = shift;
my %vlans;
my $limit;
$self->debug("stack::newVlanNumber $vlan_id\n");
if ($self->{ALLVLANSONLEADER}) {
......@@ -412,15 +413,29 @@ sub newVlanNumber($$) {
%vlans = $self->findVlans();
}
my $number = $vlans{$vlan_id};
# XXX temp, see doMakeVlans in snmpit.in
if ($::next_vlan_tag)
{ $number = $::next_vlan_tag; $::next_vlan_tag = 0; return $number; }
# Vlan exists, so tell caller a new number/vlan is not needed.
if (defined($number)) { return 0; }
my @numbers = sort values %vlans;
$self->debug("newVlanNumbers: numbers ". "@numbers" . " \n");
# XXX temp, see doMakeVlans in snmpit.in
if ($::next_vlan_tag) {
$number = $::next_vlan_tag;
$::next_vlan_tag = 0;
#
# Reserve this number in the table. If we can actually
# assign it (tables locked), then we call it good.
#
if ((grep {$_ == $number} @numbers) ||
!defined(reserveVlanTag($vlan_id, $number))) {
print STDERR "desired vlan tag for $vlan_id already in use!\n";
# Indicates no tag assigned.
return 0;
}
return $number;
}
#
# See if there is a number already pre-assigned in the lans table.
# But still make sure that the number does not conflict with an
......@@ -430,14 +445,14 @@ sub newVlanNumber($$) {
if ($number) {
if (grep {$_ == $number} @numbers) {
print STDERR "reserved vlan tag for $vlan_id already in use!\n";
return -1;
return 0;
}
return $number;
}
$number = $self->{MIN_VLAN}-1;
my $lim = $self->{MAX_VLAN};
while (++$number < $lim) {
$limit = $self->{MAX_VLAN};
while (++$number < $limit) {
if (!(grep {$_ == $number} @numbers)) {
#
# Reserve this number in the table. If we can actually
......@@ -451,8 +466,7 @@ sub newVlanNumber($$) {
$self->debug("Failed to reserve tag $number for vlan $vlan_id\n");
}
}
# Distinguish between already allocated and error.
return -1;
return 0;
}
#
......@@ -485,7 +499,7 @@ sub createVlan($$$;$$$) {
#
my ($res, $devicename, $device);
$vlan_number = $self->newVlanNumber($vlan_id);
if ($vlan_number <= 0) { last LOCKBLOCK;}
if ($vlan_number == 0) { last LOCKBLOCK;}
print "Creating VLAN $vlan_id as VLAN #$vlan_number on stack " .
"$self->{STACKID} ... \n";
if ($self->{ALLVLANSONLEADER}) {
......
......@@ -2,7 +2,7 @@
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2010 University of Utah and the Flux Group.
# Copyright (c) 2000-2011 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
......@@ -532,9 +532,11 @@ sub doSwapout($) {
#
if ($type != MODIFY ||
($type == MODIFY && $ELABINELAB && Lan->GotTrunks($experiment))) {
# Kill tag reservations on actual swapout.
my $tagopt = ($type != MODIFY ? "-C" : "");
TBDebugTimeStamp("snmpit started");
print STDERR "Removing VLANs.\n";
if (system("snmpit --redirect-err -r $pid $eid")) {
if (system("snmpit --redirect-err $tagopt -r $pid $eid")) {
tberror({type => 'secondary', severity => SEV_SECONDARY,
error => ['vlan_reset_failed']},
"Failed to reset VLANs");
......@@ -556,7 +558,7 @@ sub doSwapout($) {
}
if (@stale) {
print "Removing stale vlans @stale\n";
system("snmpit --redirect-err -f ".
system("snmpit --redirect-err -f -C ".
join(" ", map("-o $_", @stale)));
if ($?) {
tberror({type => 'summary', severity => SEV_SECONDARY,
......@@ -969,7 +971,8 @@ sub doSwapin($) {
if ($ELABINELAB || !$syncvlans) {
if (@diff) {
print "Removing obsolete vlans @diff\n";
system("snmpit --redirect-err -f ". join(" ", map("-o $_", @diff)));
system("snmpit --redirect-err -f -C ".
join(" ", map("-o $_", @diff)));
if ($?) {
tberror({type => 'summary', severity => SEV_SECONDARY,
error => ['vlan_setup_failed']},
......
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