Commit 75c0fd91 authored by Mike Hibler's avatar Mike Hibler
Browse files

Merge remote branch 'central/master'

parents 48cde223 174337c0
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005-2010 University of Utah and the Flux Group.
# Copyright (c) 2005-2011 University of Utah and the Flux Group.
# All rights reserved.
#
package Experiment;
......@@ -3416,6 +3416,28 @@ sub ClearPortRegistration($)
return 0;
}
#
# The reserved_vlantags table is special, and needs to be cleared only
# at certain times. See tbswap.
#
sub ClearReservedVlanTags($)
{
my ($self) = @_;
# Must be a real reference.
return -1
if (! ref($self));
my $pid = $self->pid();
my $eid = $self->eid();
return -1
if (! DBQueryWarn("delete from reserved_vlantags ".
"where pid='$pid' and eid='$eid'"));
return 0;
}
#
# Does experiment have any program agents.
#
......
......@@ -497,7 +497,8 @@ sub CompareVlansWithSwitches($$$)
# This is bad; if one of these updates fails, we are screwed.
#
foreach my $table ("lans", "lan_attributes", "lan_members",
"lan_member_attributes", "ifaces") {
"lan_member_attributes", "ifaces",
"reserved_vlantags") {
DBQueryWarn("update $table set lanid=$oldid ".
"where lanid='$newid'")
or return -1;
......@@ -557,7 +558,8 @@ sub CompareVlansWithSwitches2($$)
# This is bad; if one of these updates fails, we are screwed.
#
foreach my $table ("lans", "lan_attributes", "lan_members",
"lan_member_attributes", "ifaces") {
"lan_member_attributes", "ifaces",
"reserved_vlantags") {
DBQueryWarn("update $table set lanid=$oldid ".
"where lanid='$newid'")
or return -1;
......@@ -1786,6 +1788,27 @@ sub Instantiate($)
}
}
#
# Check to see if there is an entry in reserved_vlantags table.
# If so, use that lanid instead of generating a new one. This is
# mostly for the benefit of ProtoGeni stitching, but is also handy
# for swapmod so we can reuse existing vlan tags and lanids.
#
if (!defined($idx)) {
my $exptidx = $experiment->idx();
my $query_result =
DBQueryWarn("select lanid from reserved_vlantags ".
"where exptidx='$exptidx' and vname='$vname'");
return -1
if (!defined($query_result));
if ($query_result->numrows) {
($idx) = $query_result->fetchrow_array();
}
}
# The new lan has the 'ready' bit set to zero.
my $lan = Lan->Create($experiment, $vname, $type, $idx, $link);
return -1
......@@ -1831,21 +1854,24 @@ sub Instantiate($)
my $vnode = $protomember->{'vnode'};
my $vport = $protomember->{'vport'};
my $iface = $protomember->{'iface'};
my $attributes = $protomember->{'attrs'};
my $interface = $lan->AddInterface($node, $vnode, $vport, $iface);
my $pport = $protomember->{'pport'};
my $member;
if (defined($pport)) {
$member = $lan->AddMember($node, $pport);
if (!defined($member)) {
$lan->Destroy();
return -1;
}
}
my $interface =
$lan->AddInterface($node, $vnode, $vport, $iface, $member);
if (!defined($interface)) {
$lan->Destroy();
return -1;
}
if (defined($attributes)) {
foreach my $attrkey (keys(%{$attributes})) {
my $attrvalue = $attributes->{$attrkey};
$interface->SetAttribute($attrkey, $attrvalue);
}
}
}
$self->{'INSTANCE'} = $lan;
return 0;
......@@ -1942,8 +1968,12 @@ sub Dump($)
my $vnode = $interface->{'vnode'};
my $vport = $interface->{'vport'};
my $iface = $interface->{'iface'};
my $pport = $interface->{'pport'};
$string .= "$vnode:$vport:$nodeid:$iface, ";
$string .= "$vnode:$vport:$nodeid:$iface";
$string .= ":$pport"
if (defined($pport));
$string .= ", ";
}
return $string;
}
......@@ -1953,7 +1983,7 @@ sub Dump($)
#
sub AddInterface($$$$$;$)
{
my ($self, $node, $vnode, $vport, $iface, $attributes) = @_;
my ($self, $node, $vnode, $vport, $iface, $pport) = @_;
if (!ref($node)) {
$node = Node->Lookup($node);
......@@ -1971,7 +2001,7 @@ sub AddInterface($$$$$;$)
"vnode" => $vnode,
"vport" => $vport,
"iface" => $iface,
"attrs" => $attributes,
"pport" => $pport,
};
return 0;
}
......@@ -2196,6 +2226,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 +2519,147 @@ sub GetClass($)
return "Experimental";
}
# VLan reservation for a specific lan in an experiment.
sub ReserveVlanTag($$;$)
{
my ($self, $tag, $checkonly) = @_;
my $lanid = $self->lanid();
my $vname = $self->vname();
my $pid = $self->pid();
my $eid = $self->eid();
my $exptidx = $self->exptidx();
$checkonly = 0 if (!defined($checkonly));
DBQueryWarn("lock tables lan_attributes write, lans write, ".
" reserved_vlantags write, vlans read")
or return undef;
#
# Use global table for all reservations.
#
my $query_result =
DBQueryWarn("select lanid from reserved_vlantags where tag='$tag'");
goto inuse
if (!$query_result || $query_result->numrows);
#
# But because of swapmod and syncvlansfromtables, must also check
# the vlans table since that is what is currently on the switches.
#
$query_result =
DBQueryWarn("select id from vlans where tag='$tag'");
goto inuse
if (!$query_result || $query_result->numrows);
# Just checking ...
goto isfree
if ($checkonly);
goto inuse
if (!DBQueryWarn("insert into reserved_vlantags set ".
" lanid='$lanid', tag='$tag', vname='$vname', ".
" pid='$pid', eid='$eid', exptidx='$exptidx', ".
" reserve_time=now()"));
isfree:
DBQueryWarn("unlock tables");
return $tag;
inuse:
DBQueryWarn("unlock tables");
return undef;
}
# Both a class and an instance method.
sub ClearReservedVlanTag($;$)
{
my ($arg, $tag) = @_;
my $lanid = (ref($arg) ? $arg->lanid() : $arg);
my $clause = (defined($tag) ? "and tag='$tag'" : "");
DBQueryWarn("delete from reserved_vlantags where lanid='$lanid' $clause")
or return -1;
return 0;
}
sub GetReservedVlanTags($)
{
my ($self) = @_;
my $lanid = $self->lanid();
my @result = ();
my $query_result =
DBQueryWarn("select tag from reserved_vlantags ".
"where lanid='$lanid'");
return ()
if (!$query_result || !$query_result->numrows);
while (my ($tag) = $query_result->fetchrow_array()) {
push(@result, $tag);
}
return @result;
}
# Lots of different ways to call this one.
sub GetReservedVlanTag($;$)
{
my ($arg, $linkname) = @_;
my $clause;
if (ref($arg) eq "Experiment") {
my $exptidx = $arg->idx();
$clause = "exptidx='$exptidx' and vname='$linkname'";
}
else {
my $lanid = $arg->lanid();
$clause = "lanid='$lanid'";
}
my $query_result =
DBQueryWarn("select tag from reserved_vlantags where $clause");
return undef
if (!$query_result || !$query_result->numrows);
my ($tag) = $query_result->fetchrow_array();
return $tag;
}
sub HasVlanTagReserved($$)
{
my ($arg, $tag) = @_;
my $lanid = (ref($arg) ? $arg->lanid() : $arg);
my $query_result =
DBQueryWarn("select tag from reserved_vlantags ".
"where lanid='$lanid' and tag='$tag'");
return 0
if (!$query_result);
return $query_result->numrows;
}
sub VlanTagAvailable($$)
{
my ($class, $tag) = @_;
my $query_result =
DBQueryWarn("select tag from reserved_vlantags where tag='$tag'");
return 0
if (!$query_result || $query_result->numrows);
$query_result =
DBQueryWarn("select id from vlans where tag='$tag'");
return 0
if (!$query_result || $query_result->numrows);
return 1;
}
# Find out which stack a VLAN resides on
sub GetStack($) {
my ($self) = @_;
......@@ -2545,7 +2717,7 @@ sub IsManual($)
#
# Check to see if we think the VLAN actually exists on any switches at the
# moment (ie. has a vlans table entry)
# moment (ie. has a vlans table entry).
#
sub CreatedOnSwitches() {
my ($self) = @_;
......
......@@ -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;
......@@ -78,15 +78,15 @@ DBQueryFatal("replace into emulab_locks set name='pool_daemon',value='0'");
# Check for experiments stuck in transition.
#
$query_result =
DBQueryFatal("select pid,eid,expt_locked from experiments ".
DBQueryFatal("select pid,eid,expt_locked,state from experiments ".
"where expt_locked is not NULL");
if ($query_result->numrows) {
print "The following experiments are stuck in transition.\n\n";
print "Pid Eid When\n";
print "------------ -------------------- ------------------------\n";
while (($pid,$eid,$locked) = $query_result->fetchrow_array) {
printf("%-12s %-20s %s\n", $pid, $eid, $locked);
while (($pid,$eid,$locked,$state) = $query_result->fetchrow_array) {
printf("%-12s %-20s %s %s\n", $pid, $eid, $locked, $state);
}
print "\n";
}
......
......@@ -1212,6 +1212,10 @@ sub MapNodes($$)
my $manifest = $resource->Manifest();
return -1
if (!defined($manifest));
my $rspec = $resource->Rspec();
return -1
if (!defined($rspec));
$manifest_string = GeniXML::Serialize($manifest, 1);
if ($verbose) {
......
......@@ -60,6 +60,7 @@ CREATE TABLE `geni_slices` (
`sa_uuid` varchar(40) NOT NULL default '',
`registered` tinyint(1) NOT NULL default '0',
`needsfirewall` tinyint(1) NOT NULL default '0',
`isplaceholder` tinyint(1) NOT NULL default '0',
PRIMARY KEY (`idx`),
UNIQUE KEY `hrn` (`hrn`),
UNIQUE KEY `uuid` (`uuid`)
......
......@@ -783,7 +783,7 @@ sub Start($$$)
}
my $reservation = $node->Reservation();
if (!defined($reservation)) {
print STDERR "$node no long belongs to $self\n";
print STDERR "$node no longer belongs to $self\n";
return -1;
}
if ($reservation->SameExperiment($experiment)) {
......
......@@ -39,6 +39,7 @@ use libtestbed qw(SENDMAIL);
use emutil;
use EmulabConstants;
use libEmulab;
use Lan;
use English;
use Data::Dumper;
use XML::Simple;
......@@ -74,6 +75,7 @@ my $TARFILES_SETUP = "$TB/bin/tarfiles_setup";
my $MAPPER = "$TB/bin/mapper";
my $VTOPGEN = "$TB/bin/vtopgen";
my $SNMPIT = "$TB/bin/snmpit";
my $RESERVEVLANS = "$TB/sbin/protogeni/reservevlans";
my $NEWGROUP = "$TB/bin/newgroup";
my $PRERENDER = "$TB/libexec/vis/prerender";
my $XMLLINT = "/usr/local/bin/xmllint";
......@@ -655,6 +657,9 @@ sub GetTicketAuxAux($$$$$$$$$)
my %nodemap = ();
my @nodeids = ();
my %lannodes = ();
# For stitching, keep track of external nodes and links.
my %external_nodemap = ();
my %external_linkmap = ();
#
# If this is a ticket update, we want to seed the namemap with
......@@ -747,8 +752,10 @@ sub GetTicketAuxAux($$$$$$$$$)
my $node;
# Let remote nodes pass through.
next
if (! GeniXML::IsLocalNode($ref));
if (! GeniXML::IsLocalNode($ref)) {
$external_nodemap{$node_nickname} = $ref;
next;
}
#
# Lan nodes are fake and do not go into the virt topo. Need
......@@ -1097,6 +1104,7 @@ sub GetTicketAuxAux($$$$$$$$$)
foreach my $ref (@interfaces) {
my $node_nickname = GeniXML::GetInterfaceNodeId($ref);
my $iface_id = GeniXML::GetInterfaceId($ref);
my ($iface_ref,$iface_name,$iface_vport);
if (!defined($iface_id)) {
$response =
......@@ -1134,15 +1142,65 @@ sub GetTicketAuxAux($$$$$$$$$)
next;
}
if (!exists($ifacemap{$node_nickname})) {
# Might be the other side. Skip for now; might bite later.
next
if (!exists($namemap{$node_nickname}));
$response =
GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"$lanname: No such virtual_node_id: ".
"$node_nickname");
goto bad;
#
# If the interface refers to a node at another site, then
# lets try to stitch together a vlan. Assign will fail if
# the user has specified something impossible.
#
if (!exists($namemap{$node_nickname}) &&
exists($external_nodemap{$node_nickname})) {
#
# I have completely punted on how we represent external
# links in the DB so that we know what to put into the
# virtual topology. For now, just hardwire the one test
# case.
#
my $noderef = $external_nodemap{$node_nickname};
my $other_cm = GeniXML::GetManagerId($noderef);
if (! ($other_cm eq "urn:publicid:IDN+" .
"myelab.testbed.emulab.net+authority+cm" ||
$other_cm eq "urn:publicid:IDN+" .
"emulab.net+authority+cm")) {
$response =
GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"$lanname: links to external node");
goto bad;
}
#
# Stick in a reference to the fake bbg node that
# corresponds to where the link comes in.
#
my $virtnode =
$virtexperiment->NewTableRow("virt_nodes",
{"vname" => $node_nickname,
"type" => "bbgenivm",
"osname" => 'BBGENIVM-FAKE',
"ips" => '', # deprecated
"cmd_line"=> '', # bogus
"fixed" => "bbg1"});
if (!defined($virtnode)) {
print STDERR "Error creating bbg node\n";
$response = GeniResponse->Create(GENIRESPONSE_ERROR);
goto bad;
}
# These nodes are technically shared.
$virtexperiment->NewTableRow("virt_node_desires",
{"vname" => $node_nickname,
"desire" => "pcshared",
"weight" => 0.95});
$virtexperiment->multiplex_factor(1);
$virtexperiment->encap_style("vlan");
$iface_name = "";
$iface_vport = 0;
goto stitch;
}
else {
$response =
GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"$lanname: No such virtual_node_id: ".
"$node_nickname");
goto bad;
}
}
#
......@@ -1155,8 +1213,8 @@ sub GetTicketAuxAux($$$$$$$$$)
"$node_nickname:$iface_id");
goto bad;
}
my $iface_ref = $ifacemap{$node_nickname}->{$iface_id}->{"rspec"};
my $iface_name = GeniXML::GetText("component_id", $iface_ref);
$iface_ref = $ifacemap{$node_nickname}->{$iface_id}->{"rspec"};
$iface_name = GeniXML::GetText("component_id", $iface_ref);
if (!defined($iface_name)) {
$iface_name = "";
}
......@@ -1172,8 +1230,9 @@ sub GetTicketAuxAux($$$$$$$$$)
$iface_name = "";
$trivial_ok = 1;
}
my $iface_vport = $ifacemap{$node_nickname}->{$iface_id}->{"vport"};
$iface_vport = $ifacemap{$node_nickname}->{$iface_id}->{"vport"};
stitch:
# XXX
my $ip = "10.10.${linknum}.${ifacenum}";
my $mask = "255.255.255.0";
......@@ -1348,12 +1407,20 @@ sub GetTicketAuxAux($$$$$$$$$)
$solution)->get_nodelist()) {
my $virtual_id = GeniXML::GetVirtualId($ref);
my $component_id = GeniXML::GetNodeId($ref);
if (!exists($nodemap{$virtual_id})) {
if (!(exists($nodemap{$virtual_id}) ||
exists($external_nodemap{$virtual_id}))) {
$response =
GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Mapper inserted nodes you did not want");
goto bad;
}
#
# If this was an external node we placed into the topo, then
# just skip it. Revisit this later.
#
next
if (exists($external_nodemap{$virtual_id}));
my $rspec = $nodemap{$virtual_id}->{'rspec'};
my $virtnode = $nodemap{$virtual_id}->{'virtnode'};
my $node = GeniUtil::LookupNode($component_id);
......@@ -1417,11 +1484,20 @@ sub GetTicketAuxAux($$$$$$$$$)
&& defined($virtual_port_id)) {
$virtual_node_id = $iface2node{$virtual_port_id};
}
my $component_id = GeniXML::GetText("component_id",
$iface_ref);
#
# If this was an external node we placed into the topo, skip
# it here, but we have to remember it cause we have to contact
# the other CM to coordinate the vlan reservation.
#
if (exists($external_nodemap{$virtual_node_id})) {
$external_linkmap{$nickname} = [$ref, $iface_ref];
next;
}
my $component_id = GeniXML::GetText("component_id", $iface_ref);
if (!defined($virtual_node_id) || !defined($virtual_port_id)) {
$response = GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Virtual node ID or virtual interface ID missing on interface");
"Virtual node ID or virtual interface ".
"ID missing on interface");
goto bad;
}
my $vportp = $vportmap{"$virtual_node_id:$virtual_port_id"};
......@@ -1438,6 +1514,7 @@ sub GetTicketAuxAux($$$$$$$$$)
}
}
}
# Store the virt topo again since we changed it above.
$virtexperiment->Dump();
if ($virtexperiment->Store()) {
......@@ -1445,6 +1522,53 @@ sub GetTicketAuxAux($$$$$$$$$)
goto bad;
}
#
# Now contact external CMs to coordinate vlans.
#
foreach my $linkname (keys(%external_linkmap)) {
my ($linkref, $ifaceref) = @{ $external_linkmap{$linkname} };
my $slice_urn = $slice->urn();
#
# 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.
#
my $tag = VLan::GetReservedVlanTag($slice_experiment, $linkname);
goto tagged
if (defined($tag));
my ($fh, $filename) = tempfile(UNLINK => 0);
if (!defined($fh)) {
print STDERR "Could not create temp file for rspec\n";
$response = GeniResponse->Create(GENIRESPONSE_ERROR);
goto bad;
}
print $fh GeniXML::Serialize($rspec);
close($fh);
system("$RESERVEVLANS '$slice_urn' '$linkname' $filename");
if ($?) {
unlink($filename);
$response = GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not reserve vlan tags for $linkname");
goto bad;
}
unlink($filename);
#
# Need to find out what vlan was assigned.
#
$tag = VLan::GetReservedVlanTag($slice_experiment, $linkname);
if (!defined($tag)) {
print STDERR "Did not find the reserved tag for $linkname\n";
$response = GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Error reserving vlan tag");
goto bad;
}
tagged:
GeniXML::SetText("vlantag", $linkref, $tag);
}
print GeniXML::Serialize($rspec);
#
......@@ -1730,6 +1854,9 @@ sub SliverWorkAux($$$$$$$)
my @freelinks= ();
my %iface2node = ();
my $needplabslice = 0;
# For stitching, keep track of external nodes and links.
my %external_nodemap = ();