Commit f76a76dc authored by Leigh Stoller's avatar Leigh Stoller

Initial "logical wire" support for MLE.

parent 4161f851
#!/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 Interface;
......@@ -138,6 +138,7 @@ sub mac($) { return field($_[0], 'mac'); }
sub IP($) { return field($_[0], 'IP'); }
sub role($) { return field($_[0], 'role'); }
sub type($) { return field($_[0], 'interface_type'); }
sub logical($) { return field($_[0], 'logical'); }
sub mask($) { return field($_[0], 'mask'); }
sub uuid($) { return field($_[0], 'uuid'); }
# Wires table
......@@ -277,7 +278,7 @@ sub Create($$$)
my $node_id = $node->node_id();
my $MAC = $argref->{'MAC'};
my $MAC = $argref->{'MAC'} || $argref->{'mac'};
my $IP = $argref->{'IP'};
my $mask = $argref->{'mask'};
my $card = $argref->{'card'};
......@@ -294,6 +295,7 @@ sub Create($$$)
my $cable = $argref->{'cable'};
my $length = $argref->{'length'};
my $iface = $argref->{'iface'};
my $logical = $argref->{'logical'};
$iface = "eth$card"
if (!defined($iface));
......@@ -307,6 +309,8 @@ sub Create($$$)
if (!defined($duplex));
$max_speed = 0
if (!defined($max_speed));
$logical = 0
if (!defined($logical));
if (!defined($uuid)) {
$uuid = NewUUID();
if (!defined($uuid)) {
......@@ -345,7 +349,7 @@ sub Create($$$)
}
if (!DBQueryWarn("insert into interfaces set ".
" node_id='$node_id', " .
" node_id='$node_id', logical='$logical', " .
" card=$card, port=$port, role='$ifrole', ".
" mac='$MAC', IP='$IP', mask='$mask', " .
" interface_type='$iftype', iface='$iface', " .
......@@ -374,7 +378,7 @@ sub Create($$$)
$cable_len .= ", len=$length";
}
if (!DBQueryWarn("insert into wires set".
" type='$wire_type', " .
" type='$wire_type', logical='$logical', " .
" node_id1='$node_id', card1=$card, port1=$port, " .
" node_id2='$switch_id', card2='$switch_card', " .
" port2='$switch_port' $cable_len")) {
......@@ -392,6 +396,30 @@ sub Create($$$)
return Interface->Lookup($node_id, $card, $port);
}
#
# Create a fake object, as for the mapper (assign_wrapper) during debugging.
#
sub MakeFake($$$)
{
my ($class, $node, $argref) = @_;
my $nodeid = $node->node_id();
$argref->{'node_id'} = $nodeid;
my $self = {};
$self->{"DBROW"} = $argref;
bless($self, $class);
# Cache by card,port and by iface
my $iface = $argref->{'iface'};
my $card = $argref->{'card'};
my $port = $argref->{'port'};
$all_interfaces{"$nodeid:$card:$port"} = $self;
$all_interfaces{"$nodeid:$iface"} = $self;
return $self;
}
#
# Lookup by iface
#
......@@ -789,6 +817,158 @@ sub Update($$)
return Refresh($self);
}
# _Always_ make sure that this 1 is at the end of the file...
##############################################################################
#
# This class exists entirely for the mapper, to make it easy to track
# and display logical wires and interfaces while the mapper is running.
#
package Interface::LogicalWire;
use libdb;
use Node;
use libtestbed;
use English;
use overload ('""' => 'Stringify');
#
# Create a pair of logical interfaces that will later be wired together
# at layer 1.
#
sub Create($$$$$$)
{
my ($class, $impotent, $nodeA, $portA, $nodeB, $portB) = @_;
#
# See if one is the switch; the switch is node_id2 in the wires table.
#
my $pnodeA = Node->Lookup($nodeA);
if (!defined($pnodeA)) {
print STDERR "Could not lookup '$nodeA'\n";
return undef;
}
my $pnodeB = Node->Lookup($nodeB);
if (!defined($pnodeB)) {
print STDERR "Could not lookup '$nodeB'\n";
return undef;
}
my $interfaceA = Interface->LookupByIface($pnodeA, $portA);
if (!defined($interfaceA)) {
print STDERR "Could not lookup '$pnodeA:$portA'\n";
return undef;
}
my $interfaceB = Interface->LookupByIface($pnodeB, $portB);
if (!defined($interfaceB)) {
print STDERR "Could not lookup '$pnodeB:$portB'\n";
return undef;
}
#
# Create interfaces, but not for things that are marked as
# switches since we do not put interfaces for switches into
# the interfaces table.
#
if (!$pnodeA->isswitch()) {
#
# Create a logical interface. Note we have to force a hash copy.
#
my %argref = %{ $interfaceA->{'DBROW'} };
my $argref = \%argref;
# Update the card and iface.
# XXX Need a better way to allocate the card numbers.
$argref->{'card'} = $interfaceA->card() + 200;
$argref->{'iface'} = "eth" . $argref->{'card'};
$argref->{'logical'} = 1;
if (!$impotent) {
$interfaceA = Interface->Create($pnodeA, $argref);
return undef
if (!defined($interfaceA));
}
else {
# Fake things up.
$interfaceA = Interface->MakeFake($pnodeA, $argref);
}
}
if (!$pnodeB->isswitch()) {
#
# Create a logical interface. Note we have to force a hash copy.
#
my %argref = %{ $interfaceB->{'DBROW'} };
my $argref = \%argref;
# Update the card and iface.
# XXX Need a better way to allocate the card numbers.
$argref->{'card'} = $interfaceB->card() + 200;
$argref->{'iface'} = "eth" . $argref->{'card'};
$argref->{'logical'} = 1;
if (!$impotent) {
$interfaceB = Interface->Create($pnodeB, $argref);
return undef
if (!defined($interfaceB));
}
else {
# Fake things up.
$interfaceB = Interface->MakeFake($pnodeB, $argref);
}
}
if (!$impotent) {
my $node_id1 = ($pnodeA->isswitch() ?
$pnodeB->node_id() : $pnodeA->node_id());
my $node_id2 = ($pnodeB->isswitch() ?
$pnodeA->node_id() : $pnodeB->node_id());
my $card1 = $interfaceA->card();
my $port1 = $interfaceA->port();
my $card2 = $interfaceB->card();
my $port2 = $interfaceB->port();
#
# The wire is not active yet. When snmpit tuns and actually
# wires things up at layer 1, it will update this wire to
# reflect that. Use the 'Unused' type to indicate it is not
# active.
#
if (!DBQueryWarn("insert into wires set".
" type='Unused', logical=1, " .
" node_id1='$node_id1',card1=$card1,port1=$port1, " .
" node_id2='$node_id2',card2=$card2,port2=$port2")) {
}
}
my $self = {};
$self->{"nodeA"} = $nodeA;
$self->{"portA"} = $portA;
$self->{"lportA"} = $interfaceA->iface();
$self->{"nodeB"} = $nodeB;
$self->{"portB"} = $portB;
$self->{"lportB"} = $interfaceB->iface();
bless($self, $class);
return $self;
}
sub nodeA($) { return $_[0]->{'nodeA'}; }
sub portA($) { return $_[0]->{'portA'}; }
sub lportA($) { return $_[0]->{'lportA'}; }
sub nodeB($) { return $_[0]->{'nodeB'}; }
sub portB($) { return $_[0]->{'portB'}; }
sub lportB($) { return $_[0]->{'lportB'}; }
#
# Stringify for output.
#
sub Stringify($)
{
my ($self) = @_;
my $nodeA = $self->nodeA();
my $nodeB = $self->nodeB();
my $portA = $self->portA();
my $portB = $self->portB();
my $lportA = $self->lportA();
my $lportB = $self->lportB();
return "[LogicalWire: $nodeA:$portA:$lportA/$nodeB:$portB:$lportB]";
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -5006,6 +5006,8 @@ sub InterpLinks($)
my $firstmember = $vpath->firstmember();
my $lastmember = $vpath->lastmember();
$self->printdb("Adding link $virtlan, implemented by $vpath\n");
$self->AddLinkToSolution($vlink, 0, 0,
$firstmember->_pnode(),
$firstmember->_pport(),
......@@ -5391,26 +5393,54 @@ sub InterpLinksAux($)
if ($virtlan->_layer() == 1) {
#
# Just a wire.
# A layer one link is a "wire" between two interfaces
# attached to a layer one switch; snmpit deals with
# wires by creating the links on the layer one switch.
#
$protolink = ProtoLan->Create($experiment, $lan,
$self->impotent() ||
$self->alloconly());
$protovlans{$lan} = $protolink;
#
# The layer one link when it is in existence, is just like
# a wire between the end points. But of course the wire
# is dynamic, only in existence because the layer one
# switch hooks them up to each other. While this
# connection is in existence, we want to create a
# "logical" wire so that the link at the next layer up
# sees that wire (as for snmpit).
#
my $logicalwire =
Interface::LogicalWire->Create($self->impotent(),
$nodeA, $portA,
$nodeB, $portB);
return -1
if (!defined($logicalwire));
$self->printdb("Created $logicalwire for $virtlan\n");
#
# Hmm, until we have a layer one switch, lets say that
# a layer one link is just a vlan between the nodes, and
# the ports are put into trunk mode so that tagged vlans
# can operate over it.
# We should generalize this somehow.
# There is no reasonable place to store the association of
# logical interface/wire with the physical interfaces.
# Do it in the member settings.
#
$protolink->SetType("wire");
$protolink->SetRole("link");
my $ifaceA =
$protolink->AddInterface($nodeA, $vnodeA,
$vportA, $portA);
my $ifaceB =
$protolink->AddInterface($nodeB, $vnodeB,
$vportB, $portB);
$protolink->AddInterface($nodeA, $vnodeA,
$vportA, $portA, undef,
{'logical_iface' =>
$logicalwire->lportA()});
$protolink->AddInterface($nodeB, $vnodeB,
$vportB, $portB, undef,
{'logical_iface' =>
$logicalwire->lportB()});
#
# Stash this into segment so we can find it later, as when
# doing the link at the next layer up.
#
$virtlan->_logicalwire($logicalwire);
next;
}
elsif ($virtlan->_layer() == 2 && $virtlan->_vpath()) {
......@@ -5461,14 +5491,39 @@ sub InterpLinksAux($)
next;
}
#
# Okay, now make sure that if the link is layer 2 and
# is implemented by a layer 1 link, the layer 1 link
# is already done. Otherwise, push the link back on
# the list and keep going.
# Special handing for layer two links implemented by a path.
#
if ($virtlan->_implemented_by() && 0) {
if ($virtlan->_implemented_by()) {
#
# Make sure the underlying links have been processed.
# Push back on the list if not.
#
my $vpath = $virtlan->_implemented_by();
my @links = $vpath->virtlanlist();
next;
foreach my $link (@links) {
if (!exists($protovlans{$link->vname()})) {
push(@plinks, $plink);
goto loop;
}
}
#
# Find the first and last links (the segments) of the path.
# The logical wires created by those segments are the wires
# we want to use below.
#
# XXX Not sure this makes sense if the path has more then
# 2 segments in it.
#
my $firstlink = shift(@links);
my $lastlink = pop(@links);
my $firstwire = $firstlink->_logicalwire();
my $lastwire = $lastlink->_logicalwire();
$self->printdb("$virtlan; $firstwire, $lastwire\n");
$portA = $firstwire->lportA();
$portB = $lastwire->lportB();
}
if ($virtlan->usevirtiface()) {
my $protovlan;
......@@ -5610,6 +5665,22 @@ sub InterpLinksAux($)
$protolink->AddInterface($nodeB, $vnodeB, $vportB, $portB);
$protovlans{$lan} = $protolink;
}
#
# If this link is implemented by a path, link them up so we
# encode the ordering in the lans table (for snmpit).
#
if ($virtlan->_implemented_by()) {
my $vpath = $virtlan->_implemented_by();
my @links = $vpath->virtlanlist();
# If the proto link has an underlying proto vlan, that is
# the actual dependency.
my $protodep = $protolink->link() || $protolink;
foreach my $link (@links) {
$protovlans{$link->vname()}->SetLink($protodep);
}
}
}
else {
# If the trivial link has all simulated members, we
......
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