Commit df048b11 authored by Leigh B Stoller's avatar Leigh B Stoller

Add support for experimental networks on the control network.

So what if your testbed has a control network but no experimental
network? In the past you were SOL, but with this commit you can now
create links and lans on the control network that look just like
an experimental network link/lan.

To make this work, ptopgen sports a new option (-C) that will put the
control network links and wires and switches into the ptop file.

libvtop generally operates as normal, but need to arrange for the
physical ports to be put into dual tag/trunk mode, where the native
vlan is the Control network. This is done with by setting attributes
on the lan table entry that indicate dual and what to use for the
native vlan. snmpit looks for these attributes.

There are a couple of places that use the stack name (Control or
Experiment) to determine if a vlan is control or experimental. This is
not longer truu, and so need to use an attribute in the lan table
entry. 

The last bit of the puzzle is that snmpit has to be careful when
disabling trunking on these ports. When this happens, all vlans are
cleard from the ports (by the device layer), including the Control
network itself, which would make the node unreachable. I had to add
some special cases to watch for that, and return the ports to the
control network.

To turn this on, create a ControlNetVlans and enable it. The mapper
looks for this and passes the -C argument to ptopgen.

Nothing special in the NS file, except you have to turn on vlan
encapsulation; tb-set-vlan-emulation vlan

No delay nodes, but linkdelays work okay. Works for openvz containers
as well.
parent 545e72de
...@@ -1950,6 +1950,13 @@ sub Instantiate($) ...@@ -1950,6 +1950,13 @@ sub Instantiate($)
return -1; return -1;
} }
} }
# These are always class=Experiment.
if (exists($self->{"ATTRS"}->{'class'})) {
if ($lan->SetAttribute("class", "Experimental")) {
$lan->Destroy();
return -1;
}
}
# Members ... # Members ...
foreach my $key (sort(keys(%{ $self->{"MEMBERS"} }))) { foreach my $key (sort(keys(%{ $self->{"MEMBERS"} }))) {
...@@ -2373,6 +2380,28 @@ sub GetLan($) { return $_[0]->{'LAN'}; } ...@@ -2373,6 +2380,28 @@ sub GetLan($) { return $_[0]->{'LAN'}; }
sub GetExperiment($) { return $_[0]->GetLan()->GetExperiment(); } sub GetExperiment($) { return $_[0]->GetLan()->GetExperiment(); }
sub vlanid($) { return $_[0]->lanid(); } sub vlanid($) { return $_[0]->lanid(); }
#
# Lookup a vlan in the internal holding experiment.
#
#
# Lookup an internal vlan (in the vlan-holding experiment).
#
sub LookupInternal($$)
{
my ($class, $vname) = @_;
require Experiment;
my $experiment = Experiment->Lookup(VLAN_PID(), VLAN_EID());
return undef
if (!defined($experiment));
my $lan = Lan->Lookup($experiment, $vname);
return undef
if (!defined($lan));
return VLan->Lookup($lan->lanid());
}
# #
# Create a new VLan object and return it. No members yet ... which means # Create a new VLan object and return it. No members yet ... which means
# it has to be locked so that snmpit does not try to do anything with it. # it has to be locked so that snmpit does not try to do anything with it.
...@@ -3436,15 +3465,24 @@ sub StaleVlanList($$$) ...@@ -3436,15 +3465,24 @@ sub StaleVlanList($$$)
my $exptidx = $experiment->idx(); my $exptidx = $experiment->idx();
my @result = (); my @result = ();
# XXX: Hack - want to catch all non-Control VLANs #
# We want to find all experiment lans, but we now support experiment
# vlans on the control stack (using vlan encap). So the stack no longer
# tells, we need to look at the "class" of the lan.
#
my $query_result = my $query_result =
DBQueryWarn("select id from vlans ". DBQueryWarn("select v.id,a.attrvalue from vlans as v ".
"where exptidx=$exptidx and stack!='Control'"); "left join lan_attributes as a on a.lanid=v.id and ".
" a.attrkey='class' ".
"where v.exptidx=$exptidx");
return -1 return -1
if (!$query_result); if (!$query_result);
while (my ($id) = $query_result->fetchrow_array()) { while (my ($id,$class) = $query_result->fetchrow_array()) {
push(@result, $id); # No class means Experimental; we were not setting this properly
# for a long time.
push(@result, $id)
if (!defined($class) || $class eq "Experimental");
} }
@$pref = @result; @$pref = @result;
return 0; return 0;
......
#!/usr/bin/perl -wT #!/usr/bin/perl -wT
# #
# EMULAB-COPYRIGHT # EMULAB-COPYRIGHT
# Copyright (c) 2010 University of Utah and the Flux Group. # Copyright (c) 2010-2012 University of Utah and the Flux Group.
# All rights reserved. # All rights reserved.
# #
package libptop_new; package libptop_new;
...@@ -48,6 +48,7 @@ my $genimode = 0; ...@@ -48,6 +48,7 @@ my $genimode = 0;
my $delaycap_override = undef; my $delaycap_override = undef;
my $multiplex_override = undef; my $multiplex_override = undef;
my $debug = 0; my $debug = 0;
my $usecontrol = 0;
my $default_longitude = undef; my $default_longitude = undef;
my $default_latitude = undef; my $default_latitude = undef;
...@@ -145,6 +146,9 @@ sub ProcessArgs($) ...@@ -145,6 +146,9 @@ sub ProcessArgs($)
if (defined($options{"c"})) { if (defined($options{"c"})) {
$delaycap_override = $options{"c"}; $delaycap_override = $options{"c"};
} }
if (defined($options{"C"})) {
$usecontrol = 1;
}
if (defined($options{"g"})) { if (defined($options{"g"})) {
$genimode = 1; $genimode = 1;
$print_shared = 1; $print_shared = 1;
...@@ -170,7 +174,7 @@ sub LookupNodes() ...@@ -170,7 +174,7 @@ sub LookupNodes()
} }
# Bulk lookup on nodes table # Bulk lookup on nodes table
$dbresult = DBQueryFatal("select * from nodes where role='testnode' or role='testswitch' or role='widearea_switch' or role='testnodefoo'"); $dbresult = DBQueryFatal("select * from nodes where role='testnode' or role='testswitch' or role='widearea_switch' or role='testnodefoo' or role='ctrlswitch'");
while ($row = $dbresult->fetchrow_hashref()) { while ($row = $dbresult->fetchrow_hashref()) {
my $node = libptop::pnode->Create($row); my $node = libptop::pnode->Create($row);
if (defined($node)) { if (defined($node)) {
...@@ -635,7 +639,8 @@ sub LookupLinks() ...@@ -635,7 +639,8 @@ sub LookupLinks()
$link->dest()); $link->dest());
} }
$link->addTrunk($sourcebw, $destbw); $link->addTrunk($sourcebw, $destbw);
} elsif ($type eq 'Node') { } elsif ((!$usecontrol && $type eq 'Node') ||
($usecontrol && $type eq 'Control')) {
# Add a switch/node or node/node link. # Add a switch/node or node/node link.
my $node1 = $nodeList{$node_id1}; my $node1 = $nodeList{$node_id1};
my $node2 = $nodeList{$node_id2}; my $node2 = $nodeList{$node_id2};
...@@ -1004,8 +1009,13 @@ sub calculateSwitch($) ...@@ -1004,8 +1009,13 @@ sub calculateSwitch($)
{ {
my ($self) = @_; my ($self) = @_;
my $role = $self->node()->role(); my $role = $self->node()->role();
return ($role eq 'testswitch' || $role eq 'widearea_switch' if ($usecontrol) {
|| ($role eq 'testnodefoo' && $self->node()->isswitch())); return ($role eq 'ctrlswitch' && $self->node()->isswitch());
}
else {
return ($role eq 'testswitch' || $role eq 'widearea_switch'
|| ($role eq 'testnodefoo' && $self->node()->isswitch()));
}
} }
sub islocal($) sub islocal($)
...@@ -2121,7 +2131,12 @@ sub processSwitchNode($$$$) ...@@ -2121,7 +2131,12 @@ sub processSwitchNode($$$$)
$self->add_type($basetype."-".$switchRef->name()); $self->add_type($basetype."-".$switchRef->name());
$self->add_type($basetype."-".$self->bw()); $self->add_type($basetype."-".$self->bw());
my $ifaceType = $iface_type{"$node:$nodeif"}; my $ifaceType = $iface_type{"$node:$nodeif"};
$self->add_type($basetype."-".$ifaceType); if (defined($ifaceType)) {
$self->add_type($basetype."-".$ifaceType);
}
else {
print STDERR "No interface type for $node:$nodeif\n";
}
} }
sub processNodeNode($) sub processNodeNode($)
......
...@@ -764,17 +764,14 @@ sub LoadPhysInfo($) ...@@ -764,17 +764,14 @@ sub LoadPhysInfo($)
# #
my %node_type_linkbw = (); my %node_type_linkbw = ();
# XXX: PlanetLab hack - PlanetLab 'control' interfaces are also
# 'experimental' interfaces! We probably need a way to express
# this in the interfaces table or interface_types
#
$query_result = $query_result =
DBQueryWarn("select distinct i.interface_type,n.type ". DBQueryWarn("select distinct i.interface_type,n.type ".
" from interfaces as i ". " from interfaces as i ".
"left join nodes as n on n.node_id=i.node_id ". "left join nodes as n on n.node_id=i.node_id ".
"where i.role='" . TBDB_IFACEROLE_EXPERIMENT . "' ". "where n.type is not null and ".
" or (n.type='pcplabphys' and i.role='" . " (i.role='" . TBDB_IFACEROLE_EXPERIMENT . "' or ".
TBDB_IFACEROLE_CONTROL . "')"); " i.role='" . TBDB_IFACEROLE_CONTROL . "')");
return -1 return -1
if (!$query_result); if (!$query_result);
...@@ -5446,55 +5443,6 @@ sub InterpLinks($) ...@@ -5446,55 +5443,6 @@ sub InterpLinks($)
} }
} }
#
# Check the portbw to make sure they are all set; if the user is
# letting assign pick the interfaces (zero bandwidth), then portbw
# is still zero, and it needs to be set to the default speed for
# that interface.
#
foreach my $virtlan (values(%{ $self->vlans() })) {
foreach my $member ($virtlan->memberlist()) {
# Will this happen?
next
if (!exists($self->solution_portmap()->{$member}));
# XXX NSE checks I do not understand.
next
if (! (exists($self->portbw()->{$member}) &&
exists($self->solution_v2p()->{$member->vnode()})));
# Skip anything that is implemented by a path; speed set elsewhere.
next
if (defined($virtlan->_implemented_by()));
# Skip anything that was turned into a vinterface; the
# port speed will get set elsewhere.
next
if (exists($self->solution_vifacemap()->{$member}));
my $iface = $self->solution_portmap()->{$member};
my $pnodename = $self->solution_v2p()->{$member->vnode()};
my $speed = $self->portbw()->{$member};
# Already set.
next
if ($speed);
my $interface = Interface->LookupByIface($pnodename, $iface);
if (!defined($interface)) {
tberror("Could not lookup interface $pnodename:$iface\n");
return -1;
}
$speed =
$self->interfacespeedmbps($interface->type(), "ethernet");
# portbw() is in bps.
$self->portbw()->{$member} = $speed * 1000;
$self->printdb("Setting portbw: $pnodename:$iface $speed\n");
}
}
# Write the vlans to the DB. # Write the vlans to the DB.
$self->UploadVlans() == 0 $self->UploadVlans() == 0
or return -1; or return -1;
...@@ -5959,6 +5907,8 @@ sub InterpLinksAux($) ...@@ -5959,6 +5907,8 @@ sub InterpLinksAux($)
if (defined($protovlan)) { if (defined($protovlan)) {
$vlan_type = "emulated"; $vlan_type = "emulated";
$vlan_role = "link/lan"; $vlan_role = "link/lan";
$virtifaceA->_vlanname($protovlan->vname());
$virtifaceB->_vlanname($protovlan->vname());
} }
else { else {
$vlan_type = "vlan"; $vlan_type = "vlan";
...@@ -5966,6 +5916,8 @@ sub InterpLinksAux($) ...@@ -5966,6 +5916,8 @@ sub InterpLinksAux($)
$protolink->SetAttribute("link/lan", $lan); $protolink->SetAttribute("link/lan", $lan);
$protolink->SetAttribute("switchpath", "$pathA") $protolink->SetAttribute("switchpath", "$pathA")
if (defined($pathA)); if (defined($pathA));
$virtifaceA->_vlanname($lan);
$virtifaceB->_vlanname($lan);
} }
$protolink->SetType($vlan_type); $protolink->SetType($vlan_type);
$protolink->SetRole($vlan_role); $protolink->SetRole($vlan_role);
...@@ -6142,6 +6094,7 @@ sub InterpLinksAux($) ...@@ -6142,6 +6094,7 @@ sub InterpLinksAux($)
if (!defined($virtiface)); if (!defined($virtiface));
$portA = $virtiface->viface(); $portA = $virtiface->viface();
$virtiface->_vlanname($protovlan->vname());
} }
# #
# We need to reserve the shared bandwidth. # We need to reserve the shared bandwidth.
...@@ -6935,6 +6888,9 @@ sub NewVirtIface($$$$;$) ...@@ -6935,6 +6888,9 @@ sub NewVirtIface($$$$;$)
# Record this vinterface mapping. # Record this vinterface mapping.
$self->solution_vifacemap()->{$member} = $virtiface; $self->solution_vifacemap()->{$member} = $virtiface;
$member->_virtiface($virtiface); $member->_virtiface($virtiface);
$virtiface->_member($member);
$virtiface->_trunk(undef);
$virtiface->_speed(undef);
# #
# For veth and vlan interfaces, we need to set the characteristics # For veth and vlan interfaces, we need to set the characteristics
...@@ -6946,17 +6902,15 @@ sub NewVirtIface($$$$;$) ...@@ -6946,17 +6902,15 @@ sub NewVirtIface($$$$;$)
# #
if (defined($pport) && $isvdev && if (defined($pport) && $isvdev &&
(!$pnode->_sharedhost() || $member->_needtrunk())) { (!$pnode->_sharedhost() || $member->_needtrunk())) {
my $speed = $self->interfacespeedmbps(physinterfacetype($pnode,$pport),
"ethernet");
my $trunk = ($type eq "vlan" ? 1 : 0);
$self->printdb(" Setting port speed/trunk for $pnodename: ". # If vlan encap, turn on trunking.
"$pport:$speed/$trunk\n"); $virtiface->_trunk(($type eq "vlan") ? 1 : 0);
DBQueryWarn("update interfaces set " . # Also set the speed, although if this is a control interface,
" current_speed='$speed',trunk=$trunk " . # it will utlimately be ignore.
"where node_id='$pnodename' and iface='$pport'") $virtiface->_speed(
or return -1 if (!($self->impotent() || $self->alloconly())); $self->interfacespeedmbps(physinterfacetype($pnode,$pport),
"ethernet"));
} }
# #
...@@ -7905,8 +7859,10 @@ sub UploadVlans($) ...@@ -7905,8 +7859,10 @@ sub UploadVlans($)
my $args = {}; my $args = {};
$args->{'current_speed'} = $current_speed $args->{'current_speed'} = $current_speed
if (defined($current_speed)); if (defined($current_speed));
$args->{'trunk'} = 1 if (defined($trunk_mode)) {
if (defined($trunk_mode)); $args->{'trunk'} = 1;
$args->{'trunk_mode'} = $trunk_mode;
}
$interface->Update($args) == 0 $interface->Update($args) == 0
or return -1; or return -1;
} }
...@@ -8014,23 +7970,117 @@ sub UploadVlans($) ...@@ -8014,23 +7970,117 @@ sub UploadVlans($)
next next
if (defined($virtlan->_implemented_by())); if (defined($virtlan->_implemented_by()));
# Skip anything that was turned into a vinterface; the my $iface;
# port speed will get set elsewhere. my $pnodename;
next my $interface;
if (exists($self->solution_vifacemap()->{$member})); my $speed;
my $trunk;
my $trunk_vlan;
my $args = {};
my $iface = $self->solution_portmap()->{$member}; if (exists($self->solution_vifacemap()->{$member})) {
my $pnodename = $self->solution_v2p()->{$member->vnode()}; #
my $speed = $self->portbw()->{$member}; # Need to set the underlying physical interface, but
# that info is not in portbw cause the member is not a
# XXX - the following converts from bps to Mbps # physical interface.
$speed = $speed / 1000; #
my $virtiface = $self->solution_vifacemap()->{$member};
$iface = $virtiface->iface();
$pnodename = $virtiface->node_id();
$speed = $virtiface->_speed();
$trunk = $virtiface->_trunk();
$self->printdb("Interface speed: $pnodename:$iface $speed\n"); $interface = Interface->LookupByIface($pnodename, $iface);
if (!defined($interface)) {
DBQueryWarn("update interfaces set current_speed='$speed' ". tberror("No such interface $pnodename:$iface\n");
"where node_id='$pnodename' and iface='$iface'") return -1;
or return -1 if (!($self->impotent() || $self->alloconly())); }
#
# Another wrinkle; if this was a trunked vlan on the control
# network, we have to mark the vlan as dual mode and say
# what the native vlan is. This is how snmpit knows what to
# do.
#
if ($trunk && $interface->role() eq TBDB_IFACEROLE_CONTROL()) {
$trunk_vlan = VLan->LookupInternal("Control");
if (!defined($trunk_vlan)) {
tberror("Could not lookup control vlan\n");
return -1;
}
if (!$self->impotent()) {
my $vlan = VLan->Lookup($self->experiment(),
$virtiface->_vlanname());
if (!defined($vlan)) {
tberror("Could not lookup vlan for $virtiface\n");
return -1;
}
$vlan->SetAttribute("trunk_mode", "dual");
$vlan->SetAttribute("trunk_vlan", $trunk_vlan->lanid());
}
}
#
# Do not mess with control interface speeds.
#
if ($interface->role() eq TBDB_IFACEROLE_CONTROL()) {
$speed = 0;
}
}
else {
$iface = $self->solution_portmap()->{$member};
$pnodename = $self->solution_v2p()->{$member->vnode()};
$speed = $self->portbw()->{$member};
$trunk = 0;
$interface = Interface->LookupByIface($pnodename, $iface);
if (!defined($interface)) {
tberror("No such interface $pnodename:$iface\n");
return -1;
}
#
# This happens when the user asks assign to give it the
# default speed ("*" bw in the NS file). Need to assign it
# from the interface info.
#
if (! $speed) {
my $interface = Interface->LookupByIface($pnodename,
$iface);
if (!defined($interface)) {
tberror("No such interface $pnodename:$iface\n");
return -1;
}
$speed = $self->interfacespeedmbps($interface->type(),
"ethernet");
}
else {
# XXX - the following converts from bps to Mbps
# since portbw() is in bps.
$speed = $speed / 1000;
}
}
my $dbgline = "Interface: $pnodename:$iface";
if ($speed) {
$dbgline .= " speed:$speed";
$args->{'current_speed'} = $speed;
}
if ($trunk) {
$dbgline .= " trunk:$trunk";
$args->{'trunk'} = 1;
if (defined($trunk_vlan)) {
$args->{'trunk_mode'} = "dual";
$dbgline .= ":dual(" . $trunk_vlan->lanid() . ")";
}
}
$dbgline .= "\n";
if (keys(%{$args})) {
$self->printdb($dbgline);
$interface->Update($args) == 0
or return -1 if (!($self->impotent() ||
$self->alloconly()));
}
} }
} }
......
...@@ -54,7 +54,7 @@ sub usage () ...@@ -54,7 +54,7 @@ sub usage ()
print STDERR " -A - Tell ptopgen all nodes are free; only with -n\n"; print STDERR " -A - Tell ptopgen all nodes are free; only with -n\n";
exit($WRAPPER_FAILED); exit($WRAPPER_FAILED);
} }
my $optlist = "dvunfprqczxm:ko:altzZA"; my $optlist = "dvunfprqczxm:ko:altzZAC";
my $verbose = 0; my $verbose = 0;
my $debug = 0; my $debug = 0;
my $fixmode = 0; my $fixmode = 0;
...@@ -77,6 +77,7 @@ my $warnings = 0; ...@@ -77,6 +77,7 @@ my $warnings = 0;
my $maxrun = 3; # Maximum number of times we run assign. my $maxrun = 3; # Maximum number of times we run assign.
my $gotlock = 0; my $gotlock = 0;
my $userspec = 0; my $userspec = 0;
my $usecontrol = 0;
my $use_old_ptopgen = 0; my $use_old_ptopgen = 0;
my $vtop; my $vtop;
...@@ -84,6 +85,7 @@ my $vtop; ...@@ -84,6 +85,7 @@ my $vtop;
# Configure variables # Configure variables
# #
my $TB = "@prefix@"; my $TB = "@prefix@";
my $MAINSITE = @TBMAINSITE@;
my $DBNAME = "@TBDBNAME@"; my $DBNAME = "@TBDBNAME@";
my $TBOPS = "@TBOPSEMAIL@"; my $TBOPS = "@TBOPSEMAIL@";
my $ASSIGN = "$TB/libexec/assign"; my $ASSIGN = "$TB/libexec/assign";
...@@ -201,6 +203,9 @@ if (defined($options{"q"})) { ...@@ -201,6 +203,9 @@ if (defined($options{"q"})) {
if (defined($options{"c"})) { if (defined($options{"c"})) {
$clear = 1; $clear = 1;
} }
if (defined($options{"C"})) {
$usecontrol = 1;
}
if (defined($options{"l"})) { if (defined($options{"l"})) {
if ($XERCES) { if ($XERCES) {
$userspec = 1; $userspec = 1;
...@@ -247,6 +252,19 @@ my $newassign = ...@@ -247,6 +252,19 @@ my $newassign =
EmulabFeatures->FeatureEnabled("NewAssign", EmulabFeatures->FeatureEnabled("NewAssign",
$this_user, $this_user,
$experiment->GetGroup(), $experiment); $experiment->GetGroup(), $experiment);
if (!$usecontrol) {
$usecontrol =
EmulabFeatures->FeatureEnabled("ControlNetVlans",
$this_user,
$experiment->GetGroup(), $experiment);
if ($usecontrol) {
chat("Telling ptopgen to use control network vlans\n");
}
}
if ($usecontrol && $MAINSITE) {
$debug = 1;
$verbose = 1;
}
libvtop::Init($this_user, $experiment->GetGroup(), $experiment); libvtop::Init($this_user, $experiment->GetGroup(), $experiment);
...@@ -566,6 +584,8 @@ sub RunAssign($$) ...@@ -566,6 +584,8 @@ sub RunAssign($$)
if ($updating && $experiment->elabinelab()); if ($updating && $experiment->elabinelab());
$ptopargs .= "-m $mfactor " $ptopargs .= "-m $mfactor "
if (defined($mfactor)); if (defined($mfactor));
$ptopargs .= "-C "
if ($usecontrol);
$ptopargs .= "-v " $ptopargs .= "-v "
if ($vtop->virtnodecount()); if ($vtop->virtnodecount());
$ptopargs .= "-r " $ptopargs .= "-r "
......
...@@ -67,7 +67,7 @@ sub usage() ...@@ -67,7 +67,7 @@ sub usage()
exit(-1); exit(-1);
} }
my $optlist = "s:e:m:vp:rSan:c:uxg:h1:l:zZ"; my $optlist = "s:e:m:vp:rSan:c:uxg:h1:l:zZC";
my $mfactor; my $mfactor;
my $virtstuff = 0; my $virtstuff = 0;
my $widearea = 0; my $widearea = 0;
...@@ -81,6 +81,7 @@ my $genimode = $NO_GENI; ...@@ -81,6 +81,7 @@ my $genimode = $NO_GENI;
my $useshared = 0; my $useshared = 0;
my $component_name = undef; my $component_name = undef;
my $randomize = 0; my $randomize = 0;
my $usecontrol= 0;
my $OURDOMAIN = "@OURDOMAIN@"; my $OURDOMAIN = "@OURDOMAIN@";
my $MAINSITE = @TBMAINSITE@; my $MAINSITE = @TBMAINSITE@;
...@@ -200,6 +201,9 @@ if (defined($options{"u"})) { ...@@ -200,6 +201,9 @@ if (defined($options{"u"})) {
if (defined($options{"c"})) { if (defined($options{"c"})) {
$delaycap_override = $options{"c"}; $delaycap_override = $options{"c"};
} }
if (defined($options{"C"})) {
$usecontrol = 1;
}
if (defined($options{"n"})) { if (defined($options{"n"})) {
if ($options{"n"} =~ /(\d*),(\d*)/) { if