Commit 3146be4a authored by Leigh B Stoller's avatar Leigh B Stoller
Browse files

Remove these file since Weibin has properly merged them.

parent 22c26e12
#!/usr/bin/perl -w
#
# EMULAB-LGPL
# Copyright (c) 2000-2011 University of Utah and the Flux Group.
# All rights reserved.
#
#
# snmpit - A tool for setting up VLANs on SNMP-controllable switches
#
#
# Configure variables
#
use lib '@prefix@/lib';
my $TESTMODE = @TESTMODE@;
my $ELABINELAB = @ELABINELAB@;
my $NOSTACKMIB = @NOSTACKMIB@;
my $MAINSITE = @TBMAINSITE@;
my $TB = '@prefix@';
use libdb;
use User;
use Experiment;
use new_snmpit_lib;
use snmpit_remote;
use libtblog;
use EmulabFeatures;
use Port;
use Lan;
use English;
use Getopt::Long;
use strict;
# Protos
sub parseStatusString($);
sub debug($);
sub doListVlans($);
sub doListPorts($);
sub doPortStatus($@);
sub doGetStats($);
sub doVlansFromTables($$@);
sub syncVlansFromTables($$);
sub doSyncVlansWithDB($);
sub doReset($@);
sub doMakeVlan($$@);
sub doDeleteVlan($@);
sub doVlanNumber($$);
sub doPortControl($$@);
sub doRecreateVlans($);
sub doTrunkEnable($$$@);
sub doTrunkDisable($$);
sub doRestorePortStatus($@);
sub doSynchLeader($);
sub CreateOneVlan($$$@);
sub doOpenflowEnable($$);
sub doOpenflowDisable($$);
sub doSetOpenflowController($$$);
sub doSetOpenflowListener($$$);
sub doEnableOpenflowListener($$);
sub doReserveVlanTags($$@);
sub doUnReserveVlanTags($$@);
#
# Defaults
#
my $debug = 0;
my $quiet = 0;
my $impotent = 0;
my $minOpenflowListenerPort = 5000;
my $maxOpenflowListenerPort = 65535;
######################################################################
# Step 1 - Process command-line arguments
#
# We have a fairly complex set of command line arguments, and we
# need to make sure that the user only specifies one command at a
# time.
######################################################################
sub usage {
print << "END";
Usage: $0 [-h] [-v level] [-q] [-n] [-i device] [-S stack]
[-l] [-s] [-g] [-O]
[-m name pid eid [ports]]
[-T port name]
[-U port]
[-o name pid eid]
[-N name]
[-r pid eid]
[-t pid eid]
[-d ports] [-e ports] [-a ports]
[-p <10|100|1000> ports] [-u <half|full> ports]
[-c]
[--redirect-err]
[--of-disable vlanid pid eid]
[--of-enable vlanid pid eid]
[--of-controller vlanid pid eid tcp:ip:port]
[--of-listener vlanid pid eid]
General:
-h Display this help message
-v <level> Verbose mode
-q Quiet mode
-n Test mode - don't actually make any changes
-i <device> Operate on <device>, overriding default device list. Can be
given multiple times
-S <stack> Operate on the given stack, instead of the default of the
experimental network
--redirect-err Redirect STDERR to STDOUT, for easier capturing in logfiles
VLAN Control:
-t <pid> <eid> Create all VLANs for an experiment
-r <pid> <eid> Remove all VLANs for an experiment
(you can provide an optional list of vlan ids)
-l List all VLANs associated with experiments.
A second -l will list ALL vlans.
-w Used with -l, includes device-specific VLAN number
-M Used with -l, print MAC addresses instead of port numbers
-O Used with -l, list only orphaned VLANs
-L <out#in[,o2#i2,...]> stylized -l for snmpit.proxy of specific vlans
-m <name> <pid> <eid> [ports]
Create a new VLAN with name <name>, if it doesn't exist,
and put [ports] in it. The new VLAN will be
associated with the priject and experiment given
-y <type> When used with -m, the new VLAN becomes a private VLAN
of type <type>
-x <primary> When used with -y, assocates the new private VLAN with
the primary VLAN named <primary>
-z <port> Used with -y and -x, to specify which port is to be used
with the private VLAN
-o <name> <pid> <eid>
Delete the VLAN with name <name>, which is associated with
the given experiment
-N <name> Print out the VLAN number for the named VLAN
-c Delete ALL VLANs, and recreate from the database. ** USE
WITH EXTREME CAUTION **
-F Create all vlans in the given stack on the leader for
use in leader->{ALLVLANSONLEADER} (Internal use only)
--of-enable <vlanid> <pid> <eid>
Enable Openflow on VLAN with the id <vlanid> that is
associated with the given experiment
--of-disable <vlanid> <pid> <eid>
Disable Openflow on VLAN with the id <vlanid> that is
associated with the given experiment
--of-controller <vlanid> <pid> <eid> tcp:ip:port
Set controller for Openflow-enabled VLAN with the
id <vlanid> that is associated with the given
experiment
--of-listener <vlanid> <pid> <eid>
Enable listener for Openflow-enabled VLAN with the
id <vlanid> that is associated with the given
experiment, snmpit will print out the listener
connection string
Port Control:
-s List all ports, and show configuration information
-g Get port statistics
-d <ports> Disable <ports>
-e <ports> Enable <ports>
-a <ports> Enable auto-negotiation of port speed/duplex
-p <10|100> <ports> Set speed of <ports> to 10 or 100 Mbps
-u <half|full> <ports> Set duplex of <ports> to half or full
-T <port> <names> Enable trunking on the given <port>, and allow VLANs
with the given <names> across it
-E <port> <names> Like -T, but "Equal" mode; PVID is also tagged
-U <port> Turn off trunking for the given <port>
-b <ports> Print out port status for a set of ports
-B <statstring> Pass in a stat string from -b to restore status
-D <pid> <eid> Disable all control net ports for an experiment
-R <pid> <eid> (Re)enable all control net ports for an experiment
More than one operation can be specified - However, beware that the order in
which operations will occur is undefined, and some combinations of operations
(ie. -d and -e) are non-sensical.
END
return 1;
}
my @SAVEARGV = @ARGV;
my %opt = ();
Getopt::Long::Configure("no_ignore_case");
GetOptions(\%opt,
'a','c','d','e','b','B=s@','g','h','i=s@','l+','m=s@','M','n', 'A', 'C',
'N=s@','p=s','q','r','s', 'S=s@','t','E=s','T=s','u=s','U','v=s','w',
'y=s','x=s','z=s','F','L=s','O', 'D', 'R', 'f', 'X', 'Z', 'vlan_tag=i',
'of-disable=s', 'of-enable=s', 'of-controller=s', 'of-listener=s',
'o=s@{1,1}', 'redirect-err', 'blockmode', 'syncvlans', 'impotent',
'shadow');
if ($opt{h}) {
exit &usage;
}
#
# If requested, redirect STDERR to STDOUT - this makes it easier to capture
# error output in logfiles
#
if ($opt{'redirect-err'}) {
open(STDERR, ">&STDOUT");
}
if ($opt{'impotent'}) {
$impotent = 1;
}
if ($opt{v}) {
$debug = $opt{v};
print "Debug level is $debug\n";
}
if ($opt{q}) {
$quiet = 1;
if ($opt{m} || $opt{o} || $opt{t} || $opt{r} || $opt{X} || $opt{T} ||
$opt{E} || $opt{U} || $opt{u} || $opt{d} || $opt{p} || $opt{e} ||
$opt{'of-enable'} || $opt{'of-disable'} || $opt{'of-controller'} ||
$opt{'of-listener'}) {
my $nulldev;
open($nulldev, ">>/dev/null");
select $nulldev; # supresses print foo(); without explicit fileglob
}
}
#if ($opt{F}) { push @commands, ["synchleader"]; }
# Values that may have been passed on the command line
#
my $pid;
my $eid;
my $experiment;
my @ports;
my @optvlanids = ();
my %optvlantags = ();
my $equaltrunking = 0;
my $this_user;
my $ofconnstr; # Openflow connection string, for controller
our $next_vlan_tag; # XXX see doMakeVlan for explanation
my %stack_ids = ();
my $optdev; # when -i only specify a single device
#
# Move this initialization ahead from its previous location so that
# the ports converters can be used when parsing command line args.
#
# snmpit_lib fills out some hashes for speed of lookup later. Initialize
# them now
#
new_snmpit_lib::init($debug);
if ($opt{i} && @{$opt{i}} == 1) {
$optdev = $opt{i}->[0];
}
#
# Verify user and get his DB uid for later.
#
if ($UID) {
$this_user = User->ThisUser();
if (! defined($this_user)) {
die("*** $0:\n".
" You ($UID) do not exist!\n");
}
}
#
# Some operations have mandatory agruments - for others, make sure that
# the user didn't give any extraneous arguments
#
if ($opt{m} || $opt{o}) {
if (@ARGV < 2 ||
($opt{m} && ($ARGV[0] =~ /:/ || $ARGV[1] =~ /:/)) ||
($opt{o} && ($ARGV[0] =~ /^\d+$/ || $ARGV[1] =~ /^\d+$/))) {
if (! $opt{f}) {
tberror "pid/eid reqired!";
exit(usage());
}
$pid = VLAN_PID();
$eid = VLAN_EID();
print "Using internal experiment $pid/$eid for operations\n";
}
else {
($pid, $eid) = (shift @ARGV, shift @ARGV);
}
}
if ($opt{'syncvlans'} || $opt{l} || $opt{F}) {
$pid = VLAN_PID();
$eid = VLAN_EID();
print "Using internal experiment $pid/$eid for operations\n";
}
if ($opt{t} || $opt{r} || $opt{D} || $opt{R} || $opt{X} ||
$opt{A} || ($opt{C} && !$opt{o})) {
#
# Options that take 'pid eid'
#
if (!defined($pid)) {
if (@ARGV < 2) {
tberror "pid/eid reqired!";
exit &usage;
} else {
($pid, $eid) = (shift @ARGV, shift @ARGV);
}
}
if (@ARGV) {
if ($opt{A} || ($opt{C} && !($opt{r} || $opt{o}))) {
# Allow a comma separated list of tags per lan.
foreach my $arg (@ARGV) {
if ($arg =~ /^[-\w]*,\d*/) {
my @tmp = split(',', $arg);
my $id = shift(@tmp);
push(@optvlanids, $id);
$optvlantags{$id} = \@tmp;
}
else {
push(@optvlanids, $arg);
}
}
}
else {
@optvlanids = @ARGV;
}
}
} elsif ($opt{d} || $opt{e} || $opt{a} || $opt{p} || $opt{u} || $opt{m}
|| $opt{U} || $opt{b}) {
#
# Options that take a list of ports
#
@ports = @ARGV;
} elsif ($opt{T} || $opt{E}) {
#
# Options that take both a port and a list of VLANs - we require at least
# one VLAN to be given
#
if ($opt{E}) { $opt{T} = $opt{E}; $equaltrunking = 1;}
elsif (!@ARGV) {
tberror "At least one VLAN required";
exit &usage;
}
@optvlanids = @ARGV;
#
# Set the @ports array so that we'll do proper permission checking on it
#
@ports = ($opt{T});
} elsif ($opt{'of-controller'}) {
#
# Options that take the pid eid and connection string
#
if (@ARGV < 2) {
tberror "pid/eid reqired!";
exit &usage;
} else {
($pid, $eid) = (shift @ARGV, shift @ARGV);
}
if (@ARGV < 0) {
tberror "Controller connection string reqired!";
exit &usage;
} else {
$ofconnstr = shift @ARGV;
}
} elsif ($opt{'of-enable'} || $opt{'of-disable'} || $opt{'of-listener'}) {
#
# Options that need pid eid
#
if (@ARGV < 1) {
tberror "pid/eid reqired!";
exit &usage;
} else {
($pid, $eid) = (shift @ARGV, shift @ARGV);
}
} else {
#
# Everything else
#
if (@ARGV) {
tberror({type => 'primary', severity => SEV_ERROR,
error => ['too_many_arguments']},
"Too many arguments!");
exit &usage;
}
}
#
# Save the ports given in command line for future comparison
#
my @optports = @ports;
#
# Determine which operation we're performing. This is just for convenience,
# so that we can use switch-like constructs later. While we're at it, we
# pull out any arguments that were given in the $opt{} values.
#
my @commands;
#
# Simple commands
#
if ($opt{l}) { push @commands, ["listvlans"]; }
if ($opt{L}) { push @commands, ["listvlans"]; }
if ($opt{s}) { push @commands, ["listports"]; }
if ($opt{g}) { push @commands, ["getstats"]; }
if ($opt{t}) { push @commands, ["tables"]; }
if ($opt{r}) { push @commands, ["reset"]; }
if ($opt{X}) { push @commands, ["synctables"]; }
if ($opt{c}) { push @commands, ["recreate"]; }
if ($opt{U}) { push @commands, ["trunkdisable"]; }
if ($opt{b}) { push @commands, ["portstatus"]; }
if ($opt{F}) { push @commands, ["synchleader"]; }
if ($opt{A}) { push @commands, ["reservetags"]; }
if ($opt{'syncvlans'}) { push @commands, ["syncvlans"]; }
# This option combines with reset/remove
if ($opt{C} && !($opt{r} || $opt{o})) { push @commands, ["unreservetags"]; }
#
# Commands that can appear once, and take an argument
#
if ($opt{d}) { push @commands, ["portcontrol","disable"]; }
if ($opt{e}) { push @commands, ["portcontrol","enable"]; }
if ($opt{a}) { push @commands, ["portcontrol","auto"]; }
if ($opt{D}) { push @commands, ["expcnetcontrol", "disable"]; }
if ($opt{R}) { push @commands, ["expcnetcontrol", "enable"]; }
if ($opt{T}) { push @commands, ["trunkenable", $opt{T}]; }
if ($opt{'of-enable'}) { push @commands, ["ofenable", $opt{'of-enable'}]; }
if ($opt{'of-disable'}) { push @commands, ["ofdisable", $opt{'of-disable'}]; }
if ($opt{'of-controller'}) { push @commands, ["ofcontroller", $opt{'of-controller'}]; }
if ($opt{'of-listener'}) { push @commands, ["oflistener", $opt{'of-listener'}]; }
#
# Commands that can occur more than once
#
if ($opt{m}) {
foreach my $name (@{$opt{m}}) {
push @commands, ["make",$name];
}
}
if ($opt{o}) {
foreach my $name (@{$opt{o}}) {
push @commands, ["remove",$name];
}
}
if ($opt{N}) {
foreach my $name (@{$opt{N}}) {
push @commands, ["vlannumber",$name];
}
}
if ($opt{B}) {
foreach my $statstring (@{$opt{B}}) {
push @commands, ["restorestatus",$statstring];
# Set up the @ports variable so that we get permissions checking. Note,
# though, that we re-parse the strings again later, this is just for
# permissions
my %args = parseStatusString($statstring);
if (!$args{port}) {
tbdie "No port given in status string";
}
if ($args{port}) {
push @ports, convertPortsFromStrings($args{port}, $optdev);
}
}
}
#
# Commands that require 'translation' of their arguments
#
if ($opt{p}) {
#
# We'll put the argument in the form needed by the portControl function
#
if ($opt{p} =~ /^1000/) {
push @commands, ["portcontrol","1000mbit"];
} elsif ($opt{p} =~ /^100/) {
push @commands, ["portcontrol","100mbit"];
} elsif ($opt{p} =~ /^10/) {
push @commands, ["portcontrol","10mbit"];
} else {
tbreport(SEV_ERROR, 'bad_data', 'port_speed', $opt{p});
die "Bad port speed: $opt{p}. Valid values are 10, 100, and 1000\n";
}
}
if ($opt{u}) {
#
# We'll put the argument in the form needed by the portControl function
#
if ($opt{u} =~ /half/) {
push @commands, ["portcontrol","half"];
} elsif ($opt{u} =~ /full/) {
push @commands, ["portcontrol","full"];
} else {
die "Bad port duplex: $opt{u}. Valid values are full and half\n";
}
}
#
# If this is an operation on an experiment, make sure that they have permission
# to modify that experiment
#
if ($pid && $eid) {
#
# First, make sure the experiment exists
#
$experiment = Experiment->Lookup($pid,$eid);
if (!defined($experiment)) {
die "There is no experiment $eid in project $pid\n";
}
# XXX Do not make this check when -f is used; called in user context.
if (defined($this_user) && !$opt{f} &&
!$experiment->AccessCheck($this_user, TB_EXPT_MODIFY)) {
die "You do not have permission to modify experiment $pid/$eid\n";
}
}
#
# See if we use the alternate version of snmpit from Rob.
#
if ($MAINSITE && $TB eq "/usr/testbed" && !$opt{Z}) {
my $group = (defined($experiment) ? $experiment->GetGroup() : undef);
$EmulabFeatures::verbose = 0;
my $newsnmpit =
(EmulabFeatures->FeatureEnabled("RobSnmpit",
$this_user, $group, $experiment));
if ($newsnmpit) {
# Rob, change this path:
my $newpath = "$TB/bin/snmpit";
print STDERR "Invoking alternate snmpit from $newpath\n";
unshift(@SAVEARGV, "-Z");
exec $newpath, @SAVEARGV;
die("*** $0:\n".
" Could not exec $newpath: $!");
}
}
if (!@commands) {
tbreport(SEV_ERROR, 'no_operation');
die "No operation given\n";
}
#
# Options that affect other commands
#
#
# User-supplied switch lists
#
my @supplied_switches = ();
my @supplied_stacks = ();
my $supplied_switches = 0; # Whether -i or -S was given
if ($opt{i}) {
$supplied_switches = 1;
push @supplied_switches, @{$opt{i}};
}
if ($opt{S}) {
foreach my $stack (@{$opt{S}}) {
if ($ELABINELAB) {
# We are going to pass the stack argument through in the rpc call.
push(@supplied_stacks, $stack);
}
else {
$supplied_switches = 1;
my @switches = getSwitchesInStack($stack);
if (@switches) {
push @supplied_stacks, $stack;
push @supplied_switches, @switches;
} else {
tbdie({type => 'primary', severity => SEV_ERROR,
error => ['invalid_switch_stack', $stack]},
"No such switch stack: $stack");
}
}
}
}
@supplied_switches = uniq(@supplied_switches);
@supplied_stacks = uniq(@supplied_stacks);
#
# Arguments for making private VLANs
#
# Build up a list of extra arguments to be passed to createVlan()
my @pvlanArgs = ();
if ($opt{y}) {
#
# Make sure the private VLAN type they gave is valid, and make sure they
# gave the other required arugments for certain types
#
if ($opt{y} ne "primary" && $opt{y} ne "isolated" &&
$opt{y} ne "community") {
die "Unknown private VLAN type $opt{y}\n";
}
@pvlanArgs = $opt{y};
if ($opt{y} ne "primary") {
if (!$opt{x} || !$opt{z}) {
tberror "-x and -z must be given when -y is $opt{y}!";
exit &usage;
}
#
# Fix up ports given in the module/port format, like we do below for
# ports from @ARGV
#
my ($c, $p) = Port->ParseCardPortString($opt{z});
if (defined($c) && defined($p)) {
if (defined($optdev)) {
$opt{z} = Port->Triple2Iface(Port->Tokens2TripleString($optdev, $c, $p));
if (!defined($opt{z})) {
tbdie "Port $c:$p not found";
}
} else {
tbdie "The module/port format is only legal if exactly one -i " .
"argument has been given";
}
}
push @pvlanArgs,$opt{x},$opt{z};
}