Commit 7260d026 authored by Robert Ricci's avatar Robert Ricci
Browse files

Merge branch 'master' of...

Merge branch 'master' of git-public.flux.utah.edu:/flux/git/users/wbsun/emulab-devel into weibin-merge
parents 97dcfdfe f168c189
......@@ -778,6 +778,15 @@ foreach $node (@nodenames) {
push(@features, "FBSD-NSE:0.0");
}
#
# XXX: Temporary hack - don't march switches that are testnodes
# as having class 'switch' - assign treats those specially. We
# use the knowledge that 'real' switches don't hit this point!
#
if ($class eq "switch") {
$class = $type;
}
# Might be equal, which assign would sum as two, not one!
if ($type ne $class) {
push(@types, "$class:1");
......
......@@ -22,6 +22,7 @@ use libdb;
use libtestbed;
use Expect;
use Lan;
# CLI constants
......@@ -177,24 +178,16 @@ sub new($$$;$) {
bless($self, $class);
#
# Create the Expect object
# Lazy initialization of the Expect object is adopted, so
# we set the session object to be undef.
#
# We'd better set a long timeout on Apcon switch
# to keep the connection alive.
$self->{SESS} = $self->createExpectObject();
if (!$self->{SESS}) {
warn "WARNNING: Unable to connect via SSH to $self->{NAME}\n";
return undef;
}
$self->{SESS} = undef;
# TODO: may need this:
#$self->readifIndex();
$self->readTranslationTable();
return $self;
}
#
# Create an Expect object that spawns the ssh process
# to switch.
......@@ -270,11 +263,17 @@ my %PortIface=();
# Maps pcX:Y<==>pcX:iface
my %Ports=();
# Ports maps pcX:Y.port<==>switch:port
my %OldPorts=();
# Ports maps pcX:Y<==>switch:port
my %MorePortIface=();
# Maps node:card.port <==> node:iface
#
# This function fills in %Interfaces and %Ports
# They hold pcX:Y<==>MAC and pcX:Y<==>switch:port respectively
# They hold pcX:Y<==>MAC and pcX:Y.port<==>switch:port respectively
#
# XXX: Temp workround for portnum
#
......@@ -297,6 +296,10 @@ sub readTranslationTable($) {
$Interfaces{$mac} = $name;
$PortIface{$name} = $iface;
$PortIface{$iface} = $name;
$MorePortIface{"$_[0]:$_[1].$_[2]"} = $iface;
$MorePortIface{$iface} = "$_[0]:$_[1].$_[2]";
print "Interfaces: $mac <==> $name\n" if $self->{DEBUG} > 1;
}
......@@ -305,6 +308,8 @@ sub readTranslationTable($) {
"from wires;");
while ( my @row = $result->fetchrow_array()) {
my ($node_id1, $card1, $port1, $node_id2, $card2, $port2) = @row;
my $oldname = "$node_id1:$card1";
$name = "$node_id1:$card1.$port1";
print "Name='$name'\t" if $self->{DEBUG} > 2;
print "Dev='$node_id2'\t" if $self->{DEBUG} > 2;
......@@ -312,11 +317,88 @@ sub readTranslationTable($) {
print "switchport='$switchport'\n" if $self->{DEBUG} > 2;
$Ports{$name} = $switchport;
$Ports{$switchport} = $name;
if (exists($OldPorts{$oldname})) {
if ($OldPorts{$oldname} ne "") {
delete $OldPorts{$OldPorts{$oldname}};
}
$OldPorts{$oldname} = "";
} else {
$OldPorts{$oldname} = $switchport;
$OldPorts{$switchport} = $oldname;
}
print "Ports: '$name' <==> '$switchport'\n" if $self->{DEBUG} > 1;
}
}
#
# More robust version of convertPortFromNode2Dev
#
sub getRealSwitchPortFromPCPort($$) {
my $self = shift;
my $port = shift;
my $realport = "";
$self->debug("get real port on switch from PC port: $port\n");
if (exists($OldPorts{$port})) {
$realport = $OldPorts{$port};
if ($realport ne "") {
return $realport;
}
}
if (exists($Ports{$port})) {
$realport = $Ports{$port};
return $realport;
}
my $fullport = $port.".1";
if (exists($Ports{$fullport})) {
return $Ports{$fullport};
}
return undef;
}
#
# Try to guess if the given ports contains switch ports
# and refine it to be full port format.
#
sub refineVlanPorts($$@) {
my ($self, $vlanid, @givenports) = @_;
my @ifaces = getVlanIfaces($vlanid);
# Now we have to guess...
if ($#givenports == $#ifaces) {
#
# seems like all ports are here
#
my @fullports = ();
foreach my $iface (@ifaces) {
if (exists($MorePortIface{$iface})) {
push @fullports, $MorePortIface{$iface};
} else {
#
# iface doesn't exist, may God bless the givenports
# be new... new enough to be not in DB.
#
$self->debug("refine failed: Iface $iface not found\n");
return @givenports;
}
}
return @fullports;
}
$self->debug("refine failed: ports numbers not equal @givenports, @ifaces\n");
return @givenports;
}
##############################################################################
......@@ -364,7 +446,7 @@ sub parseNames($$)
foreach ( split ( /\n/, $raw ) ) {
if ( /^([A-I][0-9]{2}):\s+(\w+)\W*/ ) {
if ( $2 !~ /$CLI_UNNAMED_PATTERN/ ) {
$names{$1} = $2;
$names{$1} = $2;
}
}
}
......@@ -454,6 +536,20 @@ sub doCLICmd($$)
my $output = "";
my $exp = $self->{SESS};
if (!$exp) {
#
# Create the Expect object, lazy initialization.
#
# We'd better set a long timeout on Apcon switch
# to keep the connection alive.
$self->{SESS} = $self->createExpectObject();
if (!$self->{SESS}) {
warn "WARNNING: Unable to connect to $self->{NAME}\n";
return (1, "Unable to connect to switch $self->{NAME}.");
}
$exp = $self->{SESS};
}
$exp->clear_accum(); # Clean the accumulated output, as a rule.
$exp->send($cmd);
$exp->expect($CLI_TIMEOUT,
......@@ -464,6 +560,7 @@ sub doCLICmd($$)
$cmd = quotemeta($cmd);
if ( $output =~ /^($cmd)\n(ERROR:.+)\r\n[.\n]*$/ ) {
$self->debug("snmpit_apcon: Error in doCLICmd: $2\n");
return (1, $2);
} else {
return (0, $output);
......@@ -523,6 +620,7 @@ sub getPortName($$)
my $raw = $self->getRawOutput("show port info $port\r");
if ( $raw =~ /$port Name:\s+(\w+)\W*\n/ ) {
if ( $1 !~ /$CLI_UNNAMED_PATTERN/ ) {
$self->debug("snmpit_apcon: getPortName: $port as $1\n");
return $1;
}
}
......@@ -545,6 +643,8 @@ sub getNamedPorts($$)
}
}
$self->debug("snmpit_apcon: getNamedPorts: ".join(", ", @ports)."\n");
return \@ports;
}
......@@ -702,6 +802,8 @@ sub connectMulticast($$@)
my ($self, $src, @dsts) = @_;
my $cmd = "connect multicast $src".join("", @dsts)."\r";
$self->debug("snmpit_apcon: connectMulticast: $cmd\n");
return $self->doCLICmd($cmd);
}
......@@ -713,6 +815,8 @@ sub connectDuplex($$$)
my ($self, $src, $dst) = @_;
my $cmd = "connect duplex $src"."$dst"."\r";
$self->debug("snmpit_apcon: connectDuplex: $cmd\n");
return $self->doCLICmd($cmd);
}
......@@ -742,6 +846,9 @@ sub namePorts($$@)
# undo set name
if ( $rt ) {
$self->debug("snmpit_apcon: namePorts failed: ".join(", ", @ports)."\n");
for ($i--; $i >= 0; $i-- ) {
$self->doCLICmd(
"configure port name $ports[$i] $CLI_UNNAMED_NAME\r");
......@@ -853,8 +960,14 @@ sub setPortRate($$$)
sub convertPortFromNode2Dev($$) {
my $self = shift;
my $pcport = shift;
my $modport = $Ports{"$pcport.1"};
my $modport;
if ($pcport =~ /(.+):(.+)\.(.+)/) {
$modport = $Ports{"$pcport"};
} else {
$modport = $Ports{"$pcport.1"};
}
if (defined($modport)) {
return $self->convertPortFormat($modport);
}
......@@ -875,14 +988,28 @@ sub portControl ($$@) {
my $self = shift;
my $cmd = shift;
my @ports = @_;
my @pcports = @_;
$self->debug("portControl: $cmd -> (@ports)\n");
$self->debug("portControl: $cmd -> (@pcports)\n");
#my @fullports = $self->refineVlanPorts($vlan_id, @pcports);
#my @ports = map {$self->getRealSwitchPortFromPCPort($_)} @fullports;
my $errors = 0;
foreach my $port (@ports) {
$port = $self->convertPortFromNode2Dev($port);
my $rt = $self->setPortRate($port, $cmd);
foreach my $port (@pcports) {
my $swport = $self->getRealSwitchPortFromPCPort($port);
if (!defined($swport)) {
if (isSwitchPort($port)) {
next;
} else {
$self->debug("No such port: $port\n");
$errors++;
next;
}
}
$swport = $self->convertPortFormat($swport);
my $rt = $self->setPortRate($swport, $cmd);
if ($rt) {
if ($rt =~ /^ERROR: port rate unsupported/) {
#
......@@ -1049,8 +1176,13 @@ sub setPortVlan($$@) {
return 1;
}
my @ports = map {$self->convertPortFromNode2Dev($_)} @pcports;
#my @ports = map {$self->convertPortFromNode2Dev($_)} @pcports;
my @fullports = $self->refineVlanPorts($vlan_id, @pcports);
my @swports = map {$self->getRealSwitchPortFromPCPort($_)} @fullports;
$self->debug("$id: set ports in vlan: ".join(", ",@swports)."\n");
my @ports = map {$self->convertPortFormat($_)} @swports;
$self->lock();
# Check if ports are free
......@@ -1058,14 +1190,18 @@ sub setPortVlan($$@) {
if ($self->getPortName($port)) {
warn "ERROR: Port $port already in use.\n";
$self->unlock();
$self->debug("Port $port already in use in $_.\n");
return 1;
}
}
$self->debug("$id: ports is free\n");
my $errmsg = $self->namePorts($vlan_id, @ports);
if ($errmsg) {
warn "$errmsg";
$self->unlock();
$self->debug("$errmsg");
return 1;
}
......@@ -1073,6 +1209,7 @@ sub setPortVlan($$@) {
if ($rt) {
$self->unnamePorts(@ports);
warn "$id: ports connection failed. $msg\n";
$self->debug("$id: ports connection failed. $msg\n");
# We unnamed the ports so vlan doesn't exist now.
if (exists($emptyVlans{$vlan_id})) {
......@@ -1112,8 +1249,15 @@ sub delPortVlan($$@) {
$self->debug($self->{NAME} . "::delPortVlan $vlan_id ");
$self->debug("ports: " . join(",",@pcports) . "\n");
my @ports = map {$self->convertPortFromNode2Dev($_)} @pcports;
#my @ports = map {$self->convertPortFromNode2Dev($_)} @pcports;
my @fullports = $self->refineVlanPorts($vlan_id, @pcports);
my @swports = map {$self->getRealSwitchPortFromPCPort($_)} @fullports;
$self->debug("snmpit_apcon:delPortVlan: set ports in vlan: ".join(", ",@swports)."\n");
my @ports = map {$self->convertPortFormat($_)} @swports;
$self->lock();
# Remember all ports for empty check after remove
......
......@@ -15,7 +15,7 @@ use Exporter;
@ISA = ("Exporter");
@EXPORT = qw( macport portnum portiface Dev vlanmemb vlanid
getTestSwitches getControlSwitches getSwitchesInStack
getSwitchesInStacks
getSwitchesInStacks getVlanIfaces
getVlanPorts convertPortsFromIfaces convertPortFromIface
getExperimentTrunks setVlanTag setVlanStack
getExperimentVlans getDeviceNames getDeviceType
......@@ -33,7 +33,7 @@ use Exporter;
setPortEnabled setPortTagged
printVars tbsort getExperimentCurrentTrunks
getExperimentVlanPorts
uniq);
uniq isSwitchPort getPathVlanIfaces);
use English;
use libdb;
......@@ -160,6 +160,128 @@ sub ReadTranslationTable {
}
#
# Return an array of ifaces belonging to the VLAN
#
sub getVlanIfaces($) {
my $vlanid = shift;
my @ports = ();
my $vlan = VLan->Lookup($vlanid);
if (!defined($vlan)) {
die("*** $0:\n".
" No vlanid $vlanid in the DB!\n");
}
my @members;
if ($vlan->MemberList(\@members) != 0) {
die("*** $0:\n".
" Unable to load members for $vlan\n");
}
foreach my $member (@members) {
my $nodeid;
my $iface;
if ($member->GetAttribute("node_id", \$nodeid) != 0 ||
$member->GetAttribute("iface", \$iface) != 0) {
die("*** $0:\n".
" Missing attributes for $member in $vlan\n");
}
push(@ports, "$nodeid:$iface");
}
return @ports;
}
#
# Get real ifaces on switch node in a VLAN that implements a path
# that consists of two layer 1 connections and also has a switch as
# the middle node.
#
sub getPathVlanIfaces($$) {
my $vlanid = shift;
my $ifaces = shift;
my $vlan = VLan->Lookup($vlanid);
my $experiment = $vlan->GetExperiment();
my $pid = $experiment->pid();
my $eid = $experiment->eid();
my %ifacesonswitchnode = ();
# find the underline path of the link
my $query_result =
DBQueryWarn("select distinct implemented_by_path from ".
"virt_lans where pid='$pid' and eid='$eid' and vname='".
$vlan->vname()."';");
if (!$query_result || !$query_result->numrows) {
warn "Can't find VLAN $vlanid definition in DB.";
return -1;
}
# default implemented_by is empty
my ($path) = $query_result->fetchrow_array();
if (!$path || $path eq "") {
print "VLAN $vlanid is not implemented by a path\n" if $debug;
return -1;
}
# find the segments of the path
$query_result = DBQueryWarn("select segmentname, segmentindex from virt_paths ".
"where pid='$pid' and eid='$eid' and pathname='$path';");
if (!$query_result || !$query_result->numrows) {
warn "Can't find path $path definition in DB.";
return -1;
}
if ($query_result->numrows > 2) {
warn "We can't handle the path with more than two segments.";
return -1;
}
my @vlans = ();
VLan->ExperimentVLans($experiment, \@vlans);
while (my ($segname, $segindex) = $query_result->fetchrow())
{
foreach my $myvlan (@vlans)
{
if ($myvlan->vname eq $segname) {
my @members;
$vlan->MemberList(\@members);
foreach my $member (@members) {
my ($node,$iface);
$member->GetAttribute("node_id", \$node);
$member->GetAttribute("iface", \$iface);
if ($myvlan->IsMember($node, $iface)) {
my @pref;
$myvlan->PortList(\@pref);
# only two ports allowed in the vlan
if (@pref != 2) {
warn "Vlan ".$myvlan->id()." doesnot have exact two ports.\n";
return -1;
}
if ($pref[0] eq "$node:$iface") {
$ifacesonswitchnode{"$node:$iface"} = $pref[1];
} else {
$ifacesonswitchnode{"$node:$iface"} = $pref[0];
}
}
}
}
}
}
%$ifaces = %ifacesonswitchnode;
return 0;
}
#
# Returns an array of ports (in node:card form) used by the given VLANs
#
......@@ -172,27 +294,8 @@ sub getVlanPorts (@) {
my @ports = ();
foreach my $vlanid (@vlans) {
my $vlan = VLan->Lookup($vlanid);
if (!defined($vlan)) {
die("*** $0:\n".
" No vlanid $vlanid in the DB!\n");
}
my @members;
if ($vlan->MemberList(\@members) != 0) {
die("*** $0:\n".
" Unable to load members for $vlan\n");
}
foreach my $member (@members) {
my $nodeid;
my $iface;
if ($member->GetAttribute("node_id", \$nodeid) != 0 ||
$member->GetAttribute("iface", \$iface) != 0) {
die("*** $0:\n".
" Missing attributes for $member in $vlan\n");
}
push(@ports, "$nodeid:$iface");
}
my @ifaces = getVlanIfaces($vlanid);
push @ports, @ifaces;
}
# Convert from the DB format to the one used by the snmpit modules
return convertPortsFromIfaces(@ports);
......@@ -417,21 +520,64 @@ sub convertPortsFromIfaces(@) {
sub convertPortFromIface($) {
my ($port) = $_;
if ($port =~ /(.+):(.+)/) {
my ($node,$iface) = ($1,$2);
my $result = DBQueryFatal("SELECT card FROM interfaces " .
"WHERE node_id='$node' AND iface='$iface'");
my ($node,$iface) = ($1,$2);
my $result = DBQueryFatal("SELECT card, port FROM interfaces " .
"WHERE node_id='$node' AND iface='$iface'");
if (!$result->num_rows()) {
warn "WARNING: convertPortFromIface($port) - Unable to get card\n";
return $port;
}
my $card = ($result->fetchrow())[0];
my @row = $result->fetchrow();
my $card = $row[0];
my $cport = $row[1];
$result = DBQueryFatal("SELECT isswitch FROM node_types WHERE type IN ".
"(SELECT type FROM nodes WHERE node_id='$node')");
if (!$result->num_rows()) {
warn "WARNING: convertPortFromIface($port) -".
" Uable to decide if $node is a switch or not\n";
return $port;
}
if (($result->fetchrow())[0] == 1) {
#
# Should return the later one, but many places in snmpit
# and this file depend on the old format...
#
return "$node:$card";
#return "$node:$card.$cport";
}
return "$node:$card";
} else {
warn "WARNING: convertPortFromIface($port) - Bad port format\n";
return $port;
}
}
#
# If a port is on switch, some port ops in snmpit
# should be avoided.
#
sub isSwitchPort($) {
my $port = shift;
if ($port =~ /^(.+):(.+)/) {
my $node = $1;
my $result = DBQueryFatal("SELECT isswitch FROM node_types WHERE type IN ".
"(SELECT type FROM nodes WHERE node_id='$node')");
if (($result->fetchrow())[0] == 1) {
return 1;
}
}
return 0;
}
#
# Returns an array of all VLAN id's used by a given experiment.
# Optional list of vlan ids restricts operation to just those vlans,
......@@ -580,6 +726,13 @@ sub getInterfaceSettings ($) {
my ($interface) = @_;
#
# Switch ports are evil and we don't touch them.
#
if (isSwitchPort($interface)) {
return ();
}
$interface =~ /^(.+):(\d+)$/;
my ($node, $port) = ($1, $2);
if ((!defined $node) || (!defined $port)) {
......