Commit 9b7f535e authored by Leigh B Stoller's avatar Leigh B Stoller
Browse files

ProtoGeni stitching and vlan tag reservation changes.

* Get rid of all use of component_hops; this was our original syntax
  before the stitching path stuff was nailed down.

* Allow a vlan tag to be requested in the link statement:

    <link client_id="link0" vlantag="765">
      <interface_ref client_id="geni1:if0" />
  
* Support vlan tag requests in the stiching path part:

    <vlanRangeAvailability>765</vlanRangeAvailability>
    <suggestedVLANRange>765</suggestedVLANRange>

  This is the only support at the moment; none of the range stuff is
  done. Further, if you really want things to work, make sure all the
  hops have the same vlan tag cause we don't do vlan translation
  internally or at our edge points.

* Utah only change in the mapper; when trying to use a shared vlan
  whose tag is great then 1000, demand the "highvlan" feature on the
  nodes in the lan. Only some of our switches to high numbered vlans.
parent 97a610b1
......@@ -1473,6 +1473,7 @@ sub GetTicketAuxAux($$$$$$$$$)
#
my $linknum = 1;
my %linkexistsmap = ();
my %vlan_reservations = ();
foreach my $linkref (GeniXML::FindNodes("n:link",
$rspec)->get_nodelist()) {
......@@ -1482,10 +1483,11 @@ sub GetTicketAuxAux($$$$$$$$$)
"n:interface_ref",
$linkref)->get_nodelist();
my %managers = ();
my %hops = ();
my $ifacenum = 1;
my $vindex = 0;
my $trivial_ok = 1;
# Avoid multiple insertions for this setting.
my $sharedvlansetting = 0;
if (!defined($lanname)) {
$response = GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
......@@ -1519,14 +1521,26 @@ sub GetTicketAuxAux($$$$$$$$$)
}
#
# Look for hops list; optional.
# Look for a vlan tag reservation request.
#
if (GeniXML::FindNodes("n:component_hop", $linkref)) {
%hops = map { GeniXML::GetNodeId($_) => $_ }
GeniXML::FindNodes("n:component_hop",
$linkref)->get_nodelist();
if (my $vlan_tag = GeniXML::GetText("vlantag", $linkref)) {
if (! ($vlan_tag =~ /^\d*/)) {
$response =
GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Bad vlan tag for $lanname");
goto bad;
}
my $result =
ReserveLocalVlanTag($slice_experiment, $lanname, $vlan_tag);
if (GeniResponse::IsResponse($result)) {
$response = $result;
goto bad;
}
# Record newly reserved tags for rollback.
if (ref($result)) {
$vlan_reservations{$lanname} = $result;
}
}
#
# Ick. Before we create the virt_lan_lans entry, we have to check
......@@ -1720,6 +1734,25 @@ sub GetTicketAuxAux($$$$$$$$$)
# Stash for later. We need to allocate a vlan tag, and
# this stores the min/max vlan numbers we have to use.
$stitching_paths{$lanname}->{'network'} = $network;
$stitching_paths{$lanname}->{'edgeurn'} = $edgeurn;
#
# Look for a vlan tag reservation request. We only
# support a single tag, not a range.
#
my $vlan_tag = GetSuggestedVlanFromHop($edgehop);
if ($vlan_tag) {
my $result = ReserveLocalVlanTag($slice_experiment,
$lanname, $vlan_tag);
if (GeniResponse::IsResponse($result)) {
$response = $result;
goto bad;
}
# Record newly reserved tags for rollback.
if (ref($result)) {
$vlan_reservations{$lanname} = $result;
}
}
#
# Stick in a reference to the fake node.
......@@ -1754,6 +1787,7 @@ sub GetTicketAuxAux($$$$$$$$$)
$iface_vport = $external_vportmap{$node_nickname};
$external_vportmap{$node_nickname} += 1;
$external_linkmap{$lanname} = $linkref;
goto stitch;
}
else {
......@@ -1839,17 +1873,18 @@ sub GetTicketAuxAux($$$$$$$$$)
# Watch for shared lans; we have to add the lan entries for them.
my $shared_vlan = GeniXML::GetSharedLanName($linkref);
if (defined($shared_vlan)) {
if (defined($shared_vlan) && !$sharedvlansetting) {
# This is the magic for libvtop.
$virtexperiment->NewTableRow("virt_lan_settings",
{"vname" => $lanname,
"capkey" => "portvlan",
"capkey" => "sharedvlan",
"capval" => $shared_vlan});
# Clear all this.
$bandwidth = 0;
$latency = 0.0;
$lossrate = 0.0;
$sharedvlansetting = 1;
}
stitch:
......@@ -2240,6 +2275,10 @@ sub GetTicketAuxAux($$$$$$$$$)
GeniXML::SetText("vlantag", $linkref, $tag);
#
# Change all of the stitching hops to our edge point, since
# that is how our network is setup.
#
my @hoplist = @{ $stitching_paths{$linkname}->{'hoplist'} };
foreach my $hop (@hoplist) {
my $hopurn = GetHopLinkID($hop);
......@@ -2247,24 +2286,7 @@ sub GetTicketAuxAux($$$$$$$$$)
next
if (!defined($auth) || $auth ne $OURDOMAIN);
#
# Need to change a couple of fields buried down inside. Ick.
#
my $tmp = FindFirst("n:link", $hop);
$tmp = (FindFirst("n:switchingCapabilityDescriptor", $tmp) ||
FindFirst("n:switchingCapabilityDescriptors", $tmp))
if (defined($tmp));
$tmp = FindFirst("n:switchingCapabilitySpecificInfo", $tmp)
if (defined($tmp));
if (defined($tmp) &&
FindFirst("n:switchingCapabilitySpecificInfo_L2sc", $tmp)) {
$tmp = FindFirst("n:switchingCapabilitySpecificInfo_L2sc",
$tmp)
}
if (defined($tmp)) {
SetText("vlanRangeAvailability", $tmp, "$tag");
SetText("suggestedVLANRange", $tmp, "$tag");
}
SetVlanTagInHop($hop, $tag);
}
next;
}
......@@ -2273,14 +2295,20 @@ sub GetTicketAuxAux($$$$$$$$$)
# Chainmode
#
while ($retries) {
my $edgeurn = $stitching_paths{$linkname}->{'edgeurn'};
#
# Already have a reserved tag? This could happen if the other CM
# acted first and talked to this CM before we saw the ticket
# request. Or this is an update and we already have tags reserved.
# request. Or this is an update and we already have tags
# reserved.
#
# If we just reserved the tag above, we still need to talk
# to the other side.
#
my $tag = VLan::GetReservedVlanTag($slice_experiment, $linkname);
last
if (defined($tag));
if (defined($tag) && !exists($vlan_reservations{$linkname}));
my ($fh, $filename) = tempfile(UNLINK => 0);
if (!defined($fh)) {
......@@ -2290,7 +2318,8 @@ sub GetTicketAuxAux($$$$$$$$$)
}
print $fh GeniXML::Serialize($rspec);
close($fh);
system("$RESERVEVLANS '$slice_urn' '$linkname' $filename");
system("$RESERVEVLANS ".
"'$slice_urn' '$linkname' '$edgeurn' $filename");
if ($CHILD_ERROR) {
unlink($filename);
......@@ -2334,6 +2363,20 @@ sub GetTicketAuxAux($$$$$$$$$)
$tag = VLan::GetReservedVlanTag($slice_experiment, $linkname);
if (defined($tag)) {
GeniXML::SetText("vlantag", $linkref, $tag);
#
# Change all of the stitching hops to our edge point, since
# that is how our network is setup.
#
my @hoplist = @{ $stitching_paths{$linkname}->{'hoplist'} };
foreach my $hop (@hoplist) {
my $hopurn = GetHopLinkID($hop);
my ($auth,undef,undef) = GeniHRN::Parse($hopurn);
next
if (!defined($auth) || $auth ne $OURDOMAIN);
SetVlanTagInHop($hop, $tag);
}
last;
}
# This should not happen.
......@@ -2346,8 +2389,12 @@ sub GetTicketAuxAux($$$$$$$$$)
again:
$retries--;
}
if (!$retries) {
$response = GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not reserve vlan tag for $linkname");
goto bad;
}
}
print GeniXML::Serialize($rspec);
#
......@@ -2431,6 +2478,16 @@ sub GetTicketAuxAux($$$$$$$$$)
$slice_experiment->RemoveVirtualState()
if (defined($slice_experiment));
}
#
# Clear any new vlan tag reservations we made during this call.
#
foreach my $linkname (%vlan_reservations) {
my ($lanid, $vlan_tag) = @{ $vlan_reservations{$linkname} };
# If this is a new ticket, there is no lan object yet,
# so need to call this as a function instead of a method.
VLan::ClearReservedVlanTag($lanid, $vlan_tag);
}
if ($v2 && $level == 0) {
CleanupDeadSlice($slice, 1)
if (defined($slice));
......@@ -3436,7 +3493,8 @@ sub SliverWorkAux($$$$$$$)
my $ipAddress;
if (defined($vinterface)) {
$ipAddress = $vinterface->IP();
} else {
}
elsif (defined($interface)) {
$ipAddress = $interface->IP();
}
# Manifest goes back to the user.
......@@ -6118,5 +6176,127 @@ sub GetHopLinkID($)
return $result;
}
sub GetHopCapabilitySection($)
{
my ($hopref) = @_;
#
# Dig out the section we need from the hop.
#
my $tmp = FindFirst("n:link", $hopref);
$tmp = (FindFirst("n:switchingCapabilityDescriptor", $tmp) ||
FindFirst("n:switchingCapabilityDescriptors", $tmp))
if (defined($tmp));
$tmp = FindFirst("n:switchingCapabilitySpecificInfo", $tmp)
if (defined($tmp));
if (defined($tmp) &&
FindFirst("n:switchingCapabilitySpecificInfo_L2sc", $tmp)) {
$tmp = FindFirst("n:switchingCapabilitySpecificInfo_L2sc", $tmp)
}
return $tmp;
}
sub GetSuggestedVlanFromHop($)
{
my ($hopref) = @_;
my $capref = GetHopCapabilitySection($hopref);
return undef
if (!defined($capref));
my $tag = GeniXML::GetText("suggestedVLANRange", $capref);
return $tag;
}
sub SetVlanTagInHop($$)
{
my ($hopref, $tag) = @_;
my $capref = GetHopCapabilitySection($hopref);
return undef
if (!defined($capref));
GeniXML::SetText("vlanRangeAvailability", $capref, "$tag");
GeniXML::SetText("suggestedVLANRange", $capref, "$tag");
return 0;
}
sub ReserveLocalVlanTag($$$)
{
my ($experiment, $lanname, $tag) = @_;
my $createdvlan = 0;
my $vlan = VLan->Lookup($experiment, $lanname);
#
# Do we already have the link and tag?
#
if (defined($vlan)) {
if ($vlan->HasVlanTagReserved($tag)) {
# Indicates tag already reserved.
return 0;
}
#
# Not allowed to change the tag, sorry.
#
if ($vlan->GetReservedVlanTag()) {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Already have a tag for $lanname");
}
}
else {
#
# If this is an update to a ticket, there is no lan
# object, it is still uninstantiated.
#
my $curtag = VLan::GetReservedVlanTag($experiment, $lanname);
if (defined($curtag)) {
if ($curtag == $tag) {
# Indicates tag already reserved.
return 0;
}
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Already have a tag for $lanname");
}
}
#
# Lets see if the tag is available.
#
if (!VLan->VlanTagAvailable($tag)) {
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"vlan tag for $lanname not available");
}
#
# Reserve the tag. In order to do this, we have to create
# a lan entry long enough for snmpit to actually create the
# reservation. Then we kill it off until the ticket is actually
# redeemed.
#
if (!defined($vlan)) {
$vlan = VLan->Create($experiment, $lanname);
if (!defined($vlan)) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Internal Error creating vlan object");
}
$createdvlan = 1;
}
my $lanid = $vlan->lanid();
my $pid = $experiment->pid();
my $eid = $experiment->eid();
print STDERR "Trying to reserve vlan tag $tag for $lanname\n";
system("$SNMPIT -A $pid $eid $lanid,$tag");
if ($?) {
$vlan->Destroy()
if ($createdvlan);
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Error reserving vlan tag for $lanname");
}
$vlan->Destroy()
if ($createdvlan);
print STDERR "Reserved tag $tag for $lanname\n";
return [$lanid, $tag];
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -1820,31 +1820,98 @@ sub ReserveVlanTags($)
}
#
# Go through the component hops and find the one that refers to us.
# This is our external network point.
# Need to dig inside the stitching section to find the path.
#
my $stitching_path;
foreach my $ref (GeniXML::FindNodesNS("n:stitching",
$rspec,
$GeniXML::STITCH_NS)->get_nodelist()){
foreach my $path (GeniXML::FindNodes("n:path",
$ref)->get_nodelist()) {
my $path_id = GeniXML::GetText("id", $path);
if ($path_id eq $linkname) {
$stitching_path = $path;
last;
}
}
}
if (!defined($stitching_path)) {
$response = GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Could not find path in rspec");
goto done;
}
#
# Look at the hop list to find the edge point.
#
my @hoplist = GeniXML::FindNodes("n:hop", $stitching_path)->get_nodelist();
if (! @hoplist) {
$response = GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"No hops in the stitching path");
goto done;
}
if (!GeniHRN::IsValid(GeniCM::GetHopLinkID($hoplist[0]))) {
$response = GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Invalid URN in first hop in path");
goto done;
}
my ($hopauth) = GeniHRN::Parse(GeniCM::GetHopLinkID($hoplist[0]));
if ($hopauth ne $OURDOMAIN) {
# Reverse the list to make life easier.
@hoplist = reverse(@hoplist);
}
#
# Go through the hop list to find the edge point. This will
# be the first hop that is in a different domain.
#
my $hopref;
foreach my $ref (GeniXML::FindNodes("n:component_hop",
$linkref)->get_nodelist()) {
my $component_urn = GeniXML::GetNodeId($ref);
my ($domain,undef,undef) = GeniHRN::Parse($component_urn);
if (defined($domain) and $domain eq $me->domain()) {
$hopref = $ref;
my $lasthop;
foreach my $hop (@hoplist) {
my $hopurn = GeniCM::GetHopLinkID($hop);
next
if (! GeniHRN::IsValid($hopurn));
my ($auth,undef,undef) = GeniHRN::Parse($hopurn);
next
if (!defined($auth));
if ($auth ne $OURDOMAIN) {
$hopref = $lasthop;
last;
}
$lasthop = $hop;
}
if (!defined($hopref)) {
$response = GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Could not find hop in link");
$response = GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not find the edge hop");
goto done;
}
my $hop_urn = GeniXML::GetNodeId($hopref);
my (undef,undef,$network_id) = GeniHRN::Parse($hop_urn);
my $hopurn = GeniCM::GetHopLinkID($hopref);
my (undef,undef,$network_stuff) = GeniHRN::Parse($hopurn);
my (undef,undef,$network_id) = split('//', $network_stuff);
my $network = ExternalNetwork->Lookup($network_id);
if (!defined($network)) {
$response = GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"$hop_urn is not an external network");
"$hopurn is not an external network");
goto done;
}
#
# Special case; if the lan has a tag reserved already, the tag we
# got must be an exact match. This can happen if the ticket is
# being updated before redeem, at the other side.
#
if (my $t = VLan::GetReservedVlanTag($slice_experiment, $linkname)) {
if ((grep {$_ == $t} @{ $taglist })) {
$actualtag = $t;
goto gottag;
}
$response = GeniResponse->Create(GENIRESPONSE_SEARCHFAILED, undef,
"Already have a tag reserved");
goto done;
}
......@@ -1972,6 +2039,7 @@ sub ReserveVlanTags($)
goto done;
}
gottag:
print STDERR "Agreed on tag $actualtag\n";
GeniXML::SetText("vlantag", $linkref, $actualtag);
$response = GeniResponse->Create(GENIRESPONSE_SUCCESS,
GeniXML::Serialize($rspec));
......
......@@ -796,6 +796,14 @@ sub Release($$)
my @nodes = ();
my @vhosts = ();
#
# Release unused reserved vlantags.
#
$experiment->ClearUnusedReservedVlanTags();
#
# Release nodes.
#
foreach my $ref (GeniXML::FindNodes("n:node",
$self->rspec())->get_nodelist()) {
# Skip lan nodes; they are fake.
......
#!/usr/bin/perl -w
#
# GENIPUBLIC-COPYRIGHT
# Copyright (c) 2008-2011 University of Utah and the Flux Group.
# Copyright (c) 2008-2012 University of Utah and the Flux Group.
# All rights reserved.
#
use strict;
......@@ -15,7 +15,8 @@ use Data::Dumper;
#
sub usage()
{
print STDERR "Usage: reservevlans <slice_urn> <linkname> <rspec>\n";
print STDERR
"Usage: reservevlans <slice_urn> <linkname> <hopurn> <rspec>\n";
exit(1);
}
my $optlist = "";
......@@ -40,6 +41,7 @@ sub fatal($);
sub busy();
sub ReserveLocalTags(@);
sub ReserveRemoteTags($$@);
sub GetHopLinkID($);
#
# Turn off line buffering on output
......@@ -71,11 +73,12 @@ if (! getopts($optlist, \%options)) {
usage();
}
usage()
if (@ARGV != 3);
if (@ARGV != 4);
my $slice_urn = $ARGV[0];
my $linkname = $ARGV[1];
my $rspecfile = $ARGV[2];
my $hopurn = $ARGV[2];
my $rspecfile = $ARGV[3];
#
# Load the CM cert to act as caller context.
......@@ -138,28 +141,11 @@ if (!defined($other_manager)) {
fatal("Who is the other manager?");
}
#
# Go through the component hops and find the one that refers to us.
# This is our external network point.
#
my $hopref;
foreach my $ref (GeniXML::FindNodes("n:component_hop",
$linkref)->get_nodelist()) {
my $component_urn = GeniXML::GetNodeId($ref);
my ($domain,undef,undef) = GeniHRN::Parse($component_urn);
if (defined($domain) and $domain eq $me->domain()) {
$hopref = $ref;
last;
}
}
if (!defined($hopref)) {
fatal("Could not find hop in link");
}
my $hop_urn = GeniXML::GetNodeId($hopref);
my (undef,undef,$network_id) = GeniHRN::Parse($hop_urn);
my (undef,undef,$network_stuff) = GeniHRN::Parse($hopurn);
my (undef,undef,$network_id) = split('//', $network_stuff);
my $network = ExternalNetwork->Lookup($network_id);
if (!defined($network)) {
fatal("$hop_urn is not an external network");
fatal("$hopurn is not an external network");
}
# The bounds of the vlan tags we can use for this network point.
my $mintag = $network->min_vlan();
......@@ -180,15 +166,6 @@ my $vlan = VLan->Lookup($experiment, $linkname);
if (defined($vlan)) {
fatal("There is already a lan object for $linkname; $vlan");
}
#
# The point of this is to create the VLan object, just long enough to
# get a lanid and a tag assigned.
#
$vlan = VLan->Create($experiment, $linkname);
if (!defined($vlan)) {
fatal("Could not create vlan for $linkname");
}
my $lanid = $vlan->lanid();
#
# Setup a signal handler to clean things up.
......@@ -201,8 +178,33 @@ $SIG{HUP} = \&handler;
$SIG{TERM} = \&handler;
$SIG{INT} = \&handler;
# The other side returns suitable tags.
my @othertags = ();
#
# Special case; if the lan has a tag reserved already, we must
# try with that one tag and if the other side says no, we are done.
#
if (my $t = VLan::GetReservedVlanTag($experiment, $linkname)) {
my $tag = ReserveRemoteTags($other_manager, \@othertags, $t);
if ($tag) {
print STDERR "Agreed on tag $tag.\n";
exit(0);
}
# Tell caller we failed to reserve tags.
exit(-1)
}
#
# The point of this is to create the VLan object, just long enough to
# get a lanid and a tag assigned.