Commit 8022c16a authored by Leigh B Stoller's avatar Leigh B Stoller
Browse files

Add vlan tag reservation support. This is the first phase, mostly

based on Keith's version. This is needed for protogeni so that we can
know the vlan tag before startsliver is called, and also for cross-cm
vlan creation. The next step is to add global reservations.
parent ecd7428e
......@@ -2196,6 +2196,7 @@ sub ready($) { return $_[0]->GetLan()->ready(); }
sub type($) { return $_[0]->GetLan()->type(); }
sub GetLan($) { return $_[0]->{'LAN'}; }
sub GetExperiment($) { return $_[0]->GetLan()->GetExperiment(); }
sub vlanid($) { return $_[0]->lanid(); }
#
# Create a new VLan object and return it. No members yet ... which means
......@@ -2488,6 +2489,50 @@ sub GetClass($)
return "Experimental";
}
# VLan reservation.
sub ReserveVlanTag($$)
{
my ($self, $tag) = @_;
DBQueryWarn("lock tables lans write, lan_attributes write")
or return undef;
my $query_result =
DBQueryWarn("select attrvalue from lan_attributes ".
"where attrkey='reservedvlantag' and attrvalue='$tag'");
$tag = undef
if (!$query_result || $query_result->numrows ||
$self->SetAttribute("reservedvlantag", $tag) != 0);
DBQueryWarn("unlock tables");
return $tag;
}
sub ClearReservedVlanTag($)
{
my ($self) = @_;
my $lanid = $self->lanid();
DBQueryWarn("delete from lan_attributes ".
"where attrkey='reservedvlantag' and lanid='$lanid'")
or return -1;
return 0;
}
sub GetReservedVlanTag($)
{
my ($self) = @_;
my $tag;
return undef
if ($self->GetAttribute("reservedvlantag",\$tag) != 0);
return $tag;
}
# Find out which stack a VLAN resides on
sub GetStack($) {
my ($self) = @_;
......
......@@ -55,6 +55,7 @@ sub doOpenflowDisable($$);
sub doSetOpenflowController($$$);
sub doSetOpenflowListener($$$);
sub doEnableOpenflowListener($$);
sub doReserveVlanTags($$@);
#
# Defaults
......@@ -173,7 +174,7 @@ END
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','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',
'y=s','x=s','z=s','F','L=s','O', 'D', 'R', 'f', 'X', 'vlan_tag=i',
'of-disable=s', 'of-enable=s', 'of-controller=s', 'of-listener=s',
......@@ -221,6 +222,7 @@ my $equaltrunking = 0;
my $this_user;
my $ofconnstr; # Openflow connection string, for controller
our $next_vlan_tag; # XXX see doMakeVlan for explanation
my %stack_ids = ();
#
# Verify user and get his DB uid for later.
......@@ -250,7 +252,7 @@ if ($opt{m} || $opt{o}) {
($pid, $eid) = (shift @ARGV, shift @ARGV);
}
}
if ($opt{t} || $opt{r} || $opt{D} || $opt{R} || $opt{X}) {
if ($opt{t} || $opt{r} || $opt{D} || $opt{R} || $opt{X} || $opt{A}) {
#
# Options that take 'pid eid'
#
......@@ -344,6 +346,7 @@ if ($opt{c}) { push @commands, ["recreate"]; }
if ($opt{U}) { push @commands, ["trunkdisable"]; }
if ($opt{b}) { push @commands, ["portstatus"]; }
if ($opt{F}) { push @commands, ["synchleader"]; }
if ($opt{A}) { push @commands, ["reservetags"]; }
#
# Commands that can appear once, and take an argument
......@@ -650,7 +653,7 @@ COMMAND: foreach my $command (@commands) {
}
last;
};
(/^tables$/) && do {
(/^tables$/ || /^reservetags$/) && 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
......@@ -843,7 +846,7 @@ COMMAND: foreach my $command (@commands) {
# Now, make the object for each stack that we discovered
#
my @stacks;
my %stack_ids;
%stack_ids = ();
foreach my $stack_id (keys %stacks) {
my ($stack_type, $supports_private, $single_domain, $community)
= getStackType($stack_id);
......@@ -955,6 +958,10 @@ COMMAND: foreach my $command (@commands) {
$exitval += doVlansFromTables($experiment,\@stacks,@vlans);
last;
}; # /tables/ && do
/^reservetags$/ && do {
$exitval += doReserveVlanTags($experiment,\@stacks,@vlans);
last;
}; # /reservetags/ && do
/synctables/ && do {
$exitval += syncVlansFromTables($experiment,\@stacks);
last;
......@@ -2090,6 +2097,82 @@ sub syncVlansFromTables($$) {
return -1;
}
#
# Reserve vlan tags.
#
sub doReserveVlanTags($$@) {
my $experiment = shift;
my $stacks = shift;
my @vlanids = @_;
my %vlans = ();
my %vstacks = ();
my $errors = 0;
my @stacknames = map { $_->{STACKID} } @$stacks;
print STDERR "@stacknames\n";
#
# 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;
}
my $stack = $planned[0];
if (! (grep {$_ eq $stack} @stacknames)) {
print STDERR "$vlan is in stack $stack, but not in given stacks!\n";
$errors++;
next;
}
$vstacks{"$id"} = $stack_ids{$stack};
}
return $errors
if ($errors);
#
# Now do the reservations. Have to deal with roll back on error.
#
my @assigned = ();
foreach my $vlan (values(%vlans)) {
my $vlanid = $vlan->id();
my $stack = $vstacks{"$vlanid"};
my $tag = getReservedVlanTag($vlanid);
# Do nothing if tag already assigned.
next
if ($tag);
#
# 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) {
print STDERR "Could not pre-reserve tag for $vlan\n";
$errors++;
last;
}
push(@assigned, $vlan);
}
if ($errors) {
foreach my $vlan (@assigned) {
clearReservedVlanTag($vlan->id());
}
}
return $errors;
}
#
# Create a vlan with name $vlan_name. It is not an error to try to create a
# VLAN that already exists, as this can be used to add ports to an existing
......
......@@ -33,7 +33,9 @@ use Exporter;
setPortEnabled setPortTagged
printVars tbsort getExperimentCurrentTrunks
getExperimentVlanPorts
uniq isSwitchPort getPathVlanIfaces);
uniq isSwitchPort getPathVlanIfaces
reserveVlanTag getReservedVlanTag clearReservedVlanTag
);
use English;
use libdb;
......@@ -488,6 +490,44 @@ sub setVlanStack($$) {
return 0;
}
#
# Update database to reserve a vlan tag. The tables will be locked to
# make sure we can get it.
#
sub reserveVlanTag ($$) {
my ($vlan_id, $tag) = @_;
if (!$vlan_id || !defined($tag)) {
return 0;
}
my $vlan = VLan->Lookup($vlan_id);
return 0
if (!defined($vlan));
return $vlan->ReserveVlanTag($tag);
}
sub clearReservedVlanTag ($) {
my ($vlan_id) = @_;
my $vlan = VLan->Lookup($vlan_id);
return -1
if (!defined($vlan));
return $vlan->ClearReservedVlanTag();
}
sub getReservedVlanTag ($) {
my ($vlan_id) = @_;
my $vlan = VLan->Lookup($vlan_id);
return 0
if (!defined($vlan));
return $vlan->GetReservedVlanTag();
}
#
# Given a list of VLANs, return only the VLANs that are beleived to actually
# exist on the switches
......
......@@ -2,7 +2,7 @@
#
# EMULAB-LGPL
# Copyright (c) 2000-2010 University of Utah and the Flux Group.
# Copyright (c) 2000-2011 University of Utah and the Flux Group.
# Copyright (c) 2004-2009 Regents, University of California.
# All rights reserved.
#
......@@ -397,8 +397,8 @@ sub setPortVlan($$@) {
# usage: newVlanNumber(self, vlan_identifier)
#
# returns a number in $self->{VLAN_MIN} ... $self->{VLAN_MAX}
# or zero indicating failure: either that the id exists,
# or the number space is full.
# or zero indicating that the id exists, or -1 indicating
# the number space is full.
#
sub newVlanNumber($$) {
my $self = shift;
......@@ -416,14 +416,43 @@ sub newVlanNumber($$) {
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");
#
# 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
# existing vlan.
#
$number = getReservedVlanTag($vlan_id);
if ($number) {
if (grep {$_ == $number} @numbers) {
print STDERR "reserved vlan tag for $vlan_id already in use!\n";
return -1;
}
return $number;
}
$number = $self->{MIN_VLAN}-1;
my $lim = $self->{MAX_VLAN};
do { ++$number }
until (!(grep {$_ == $number} @numbers) || ($number > $lim));
return $number <= $lim ? $number : 0;
while (++$number < $lim) {
if (!(grep {$_ == $number} @numbers)) {
#
# Reserve this number in the table. If we can actually
# assign it (tables locked), then we call it good. Else
# go around again.
#
if (reserveVlanTag($vlan_id, $number)) {
$self->debug("Reserved tag $number to vlan $vlan_id\n");
return $number;
}
$self->debug("Failed to reserve tag $number for vlan $vlan_id\n");
}
}
# Distinguish between already allocated and error.
return -1;
}
#
......@@ -456,7 +485,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}) {
......@@ -496,7 +525,7 @@ sub createVlan($$$;$$$) {
}
$self->unlock();
return $vlan_number;
return ($vlan_number <= 0 ? 0 : $vlan_number);
}
#
......
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