All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

Commit 259c9475 authored by Leigh B Stoller's avatar Leigh B 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($$$;$$$) {