Commit 1e121d58 authored by Leigh Stoller's avatar Leigh Stoller

Add share/unshare vlan extrypoints to that an existing vlan can be

shared. This was requested at GEC 18.

When unsharing (or terminating) a slice that contains shared vlans,
the sharing is revoked from all slices using that vlan.
parent 686c632e
......@@ -5569,6 +5569,20 @@ sub SetupPortLans($)
tberror("Could not lock $portvlan for a long time!\n");
return -1;
}
#
# Once we get the lock, make sure the lan is actually still
# shared.
#
if ($portvlan->Refresh() != 0) {
tberror("Could not refresh $portvlan after locking!\n");
$portvlan->Unlock();
return -1;
}
if (! $portvlan->IsShared()) {
tberror("$portvlan is no longer shared!\n");
$portvlan->Unlock();
return -1;
}
#
# The idea here is to remove any members for this lan
......@@ -5639,13 +5653,13 @@ sub SetupPortLans($)
# When swapping out an experiment, need to clear the ports from the
# shared lans.
#
sub ClearPortLans($)
sub ClearPortLans($;$@)
{
my ($self) = @_;
my ($self, $nolock, @lans) = @_;
$nolock = 0 if (!defined($nolock));
require Lan;
my @lans;
if (Lan->ExperimentLans($self, \@lans) != 0) {
if (!@lans && Lan->ExperimentLans($self, \@lans) != 0) {
tberror("Could not get list of all lans for $self\n");
return -1;
}
......@@ -5660,8 +5674,8 @@ sub ClearPortLans($)
my $portvlan = Lan->Lookup($target_lanid);
if (!defined($portvlan)) {
tberror("Could not lookup portvlan $target_lanid\n");
return -1;
tbinfo("portvlan $target_lanid no longer exists. Skipping ...\n");
next;
}
#
......@@ -5670,11 +5684,34 @@ sub ClearPortLans($)
# timeout should not be too long; indicates an error if
# it is.
#
if ($portvlan->Lock(180) != 0) {
if (!$nolock && $portvlan->Lock(180) != 0) {
tberror("Could not lock $portvlan for a long time!\n");
return -1;
}
#
# Once we get the lock, make sure the lan is actually still
# shared.
#
if ($portvlan->Refresh() != 0) {
tberror("Could not refresh $portvlan after locking!\n");
$portvlan->Unlock()
if (!$nolock);
return -1;
}
#
# This does not need to be a fatal error since snmpit will not
# allow a shared vlan to removed. The only way to get here is
# if sharevlan -r -f is called, in which case the port was
# forcibly yanked out of the target vlan already, so we can just
# skip it.
#
if (! $portvlan->IsShared()) {
$portvlan->Unlock()
if (!$nolock);
next;
}
#
# The idea here is to remove any members for this lan
# from the target lan. Then sync the target.
......@@ -5682,7 +5719,8 @@ sub ClearPortLans($)
my @members;
if ($portvlan->MemberList(\@members) != 0) {
tberror("Could not get member list for $portvlan\n");
$portvlan->Unlock();
$portvlan->Unlock()
if (!$nolock);
return -1;
}
foreach my $member (@members) {
......@@ -5707,7 +5745,8 @@ sub ClearPortLans($)
# Delete the member.
if ($portvlan->DelMember($member)) {
tberror("Could not delete $member from $portvlan\n");
$portvlan->Unlock();
$portvlan->Unlock()
if (!$nolock);
return -1;
}
}
......@@ -5716,7 +5755,8 @@ sub ClearPortLans($)
#
my $experiment = $portvlan->GetExperiment();
if (!defined($experiment)) {
$portvlan->Unlock();
$portvlan->Unlock()
if (!$nolock);
return -1;
}
my $pid = $experiment->pid();
......@@ -5726,10 +5766,12 @@ sub ClearPortLans($)
print "Syncing target vlan $lanid in $experiment\n";
mysystem("$TB/bin/snmpit_test -f --redirect-err -X $pid $eid $lanid");
if ($?) {
$portvlan->Unlock();
$portvlan->Unlock()
if (!$nolock);
return -1;
}
$portvlan->Unlock();
$portvlan->Unlock()
if (!$nolock);
}
return 0;
}
......@@ -5775,6 +5817,32 @@ sub SharingVlans($)
return $query_result->numrows;
}
#
# List of shared vlans
#
sub SharedVlanList($$)
{
my ($self, $pref) = @_;
my @result = ();
my $idx = $self->idx();
my $query_result =
DBQueryWarn("select lanid from shared_vlans where exptidx='$idx'");
return -1
if (!$query_result);
while (my ($lanid) = $query_result->fetchrow_array()) {
my $vlan = VLan->Lookup($lanid);
if (!defined($vlan)) {
tberror("Could not lookup shared vlan $lanid\n");
return -1;
}
push(@result, $vlan);
}
@$pref = @result;
return 0;
}
#
# Do IP allocation for switch network fabrics that require it.
#
......
......@@ -579,9 +579,7 @@ sub RenewSliver()
# Return an XML-RPC boolean
my $coder = Frontier::RPC2->new();
return GeniResponse->Create(GENIRESPONSE_SUCCESS,
GeniResponse::value($response),
GeniResponse::value($response));
return GeniResponse->Create(GENIRESPONSE_SUCCESS, $coder->boolean(1));
}
sub Shutdown()
......@@ -1127,6 +1125,28 @@ sub PerformOperationalAction
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
elsif ($action eq 'geni_sharelan') {
if (!exists($options->{"geni_sharelan_lanname"})) {
return GeniResponse->MalformedArgsResponse("No lanname provided!");
}
if (!exists($options->{"geni_sharelan_token"})) {
return GeniResponse->MalformedArgsResponse("No token provided!");
}
$args->{'slice_urn'} = $slice->urn();
$args->{'token'} = $options->{"geni_sharelan_token"};
$args->{'lanname'} = $options->{"geni_sharelan_lanname"};
return GeniCMV2::ShareLan($args);
}
elsif ($action eq 'geni_unsharelan') {
if (!exists($options->{"geni_unsharelan_lanname"})) {
return GeniResponse->MalformedArgsResponse("No lanname provided!");
}
$args->{'slice_urn'} = $slice->urn();
$args->{'lanname'} = $options->{"geni_unsharelan_lanname"};
return GeniCMV2::UnShareLan($args);
}
return GeniResponse->Create(GENIRESPONSE_REFUSED, undef,
"Invalid operational action");
}
......
......@@ -75,6 +75,7 @@ use Compress::Zlib;
use File::Temp qw(tempfile);
use MIME::Base64;
use Digest::SHA1 qw(sha1_hex);
use POSIX ":sys_wait_h";
# Configure variables
my $TB = "@prefix@";
......@@ -119,6 +120,7 @@ my $ADDAUTHORITY = "$TB/sbin/protogeni/addauthority";
my $EMULAB_PEMFILE = "@prefix@/etc/genicm.pem";
my $TARINSTALL = "/usr/local/bin/install-tarfile";
my $IMAGE_SETUP = "$TB/sbin/image_setup";
my $SHAREVLAN = "$TB/sbin/sharevlan";
my $FWNAME = "fw";
my $API_VERSION = 1;
my $USELOCALPROJ = 0;
......@@ -916,7 +918,6 @@ sub GetTicketAuxAux($$$$$$$$$$)
"Hostname > 63 characters: $fullhostname");
goto bad;
}
#
# Check for disk_image request. Specified as a URN.
#
......@@ -5470,6 +5471,35 @@ sub CleanupDeadSlice($;$)
print STDERR "Could not clear portvlans\n";
return -1;
}
#
# If this experiment has shared a lan to others, then must
# revoke them. This will yank them out from underneath the
# slices using them, but thats the way it has to be.
#
if ($experiment->SharingVlans()) {
my @sharedvlans = ();
if ($experiment->SharedVlanList(\@sharedvlans)) {
print STDERR "Failed to get shared VLANs\n";
return -1;
}
while (@sharedvlans) {
my $vlan = shift(@sharedvlans);
my $vname = $vlan->vname();
print STDERR "Unsharing vlan $vname in $pid,$eid\n";
#
# This operation has to be done as an admin person.
#
GeniUtil::FlipToElabMan();
system("$WAP $SHAREVLAN -r -f $pid,$eid $vname");
my $ecode = $?;
FlipToUser($slice);
if ($ecode) {
print STDERR "Failed to unshare vlan!\n";
return -1;
}
}
}
system("$SNMPIT -C -r $pid $eid");
if ($?) {
......@@ -6313,8 +6343,6 @@ sub KillMonitor($)
return 0
if (!$pid);
print STDERR "Monitor in progress: process id $pid ...\n";
my $experiment = $slice->GetExperiment();
if (!defined($experiment)) {
......@@ -6322,6 +6350,21 @@ sub KillMonitor($)
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"No slice experiment");
}
#
# See if the process still exists.
#
if (kill(0, $pid) == 0) {
if ($!{ESRCH}) {
print STDERR "Monitor process $pid no longer exists.\n";
$experiment->SetCancelFlag(0);
$slice->ClearMonitorPid();
return 0;
}
# What does getting here mean?
}
print STDERR "Monitor in progress: process id $pid ...\n";
if ($experiment->canceled()) {
print STDERR "Cancel flag already set for $experiment\n";
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
......
......@@ -99,6 +99,8 @@ my $SNMPIT = "$TB/bin/snmpit";
my $CLONEIMAGE = "$TB/sbin/clone_image";
my $CREATEIMAGE = "$TB/bin/create_image";
my $DELETEIMAGE = "$TB/sbin/delete_image";
my $WAP = "$TB/sbin/withadminprivs";
my $SHAREVLAN = "$TB/sbin/sharevlan";
my $XMLLINT = "/usr/local/bin/xmllint";
my $PRERENDER = "$TB/libexec/vis/prerender";
my $EMULAB_PEMFILE = "@prefix@/etc/genicm.pem";
......@@ -736,6 +738,8 @@ sub GetSliver($)
my $slice_urn = $argref->{'slice_urn'};
my $credentials = $argref->{'credentials'};
sleep(100);
if (! (defined($credentials) && defined($slice_urn))) {
return GeniResponse->MalformedArgsResponse("Missing arguments");
}
......@@ -2709,5 +2713,154 @@ sub ListActiveSlivers($)
return GeniResponse->Create(GENIRESPONSE_SUCCESS, \@results);
}
#
# Emulab specific function to share and unshare vlans.
#
sub ShareLan($)
{
my ($argref) = @_;
return ShareLanAux($argref, 0);
}
sub UnShareLan($)
{
my ($argref) = @_;
return ShareLanAux($argref, 1);
}
sub ShareLanAux($$)
{
my ($argref, $revoke) = @_;
my $slice_urn = $argref->{'slice_urn'};
my $credentials = $argref->{'credentials'};
my $linkname = $argref->{'lanname'};
my $token = $argref->{'token'};
require Lan;
if (! (defined($credentials) &&
defined($slice_urn) && defined($linkname))) {
return GeniResponse->MalformedArgsResponse("Missing arguments");
}
if (! ($linkname =~ /^[-\w]*$/)) {
return GeniResponse->MalformedArgsResponse("Improper lanname argument");
}
if (!$revoke) {
if (!defined($token)) {
return GeniResponse->MalformedArgsResponse("Missing token");
}
if (! ($token =~ /^[-\w]*$/)) {
return GeniResponse->MalformedArgsResponse("Improper token");
}
}
my ($credential,$speaksfor) = GeniStd::CheckCredentials($credentials);
return $credential
if (GeniResponse::IsResponse($credential));
my $user = GeniCM::CreateUserFromCertificate($credential);
return $user
if (GeniResponse::IsResponse($user));
my ($slice, $aggregate) = Credential2SliceAggregate($credential);
return $slice
if (defined($slice) && GeniResponse::IsResponse($slice));
if (! (defined($slice) && defined($aggregate))) {
return GeniResponse->Create(GENIRESPONSE_SEARCHFAILED, undef,
"Slice does not exist");
}
main::AddLogfileMetaDataFromSlice($slice);
# If a monitor process is running, we are "busy".
if ($slice->monitor_pid()) {
return GeniResponse->MonitorResponse();
}
if ($slice_urn ne $slice->urn()) {
return GeniResponse->Create(GENIRESPONSE_FORBIDDEN(), undef,
"Credential does not match the URN");
}
#
# Lock the slice; we do not the user to mess with things.
#
if ($slice->Lock() != 0) {
return GeniResponse->BusyResponse();
}
my $experiment = $slice->GetExperiment();
if (!defined($experiment)) {
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"No local experiment for slice");
}
my $pid = $experiment->pid();
my $eid = $experiment->eid();
my $vlan = VLan->Lookup($experiment, $linkname);
if (!defined($vlan)) {
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"No such lan in sliver");
}
if ($vlan->IsShared() && !$revoke) {
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_SUCCESS, undef,
"Lan is already shared");
}
if (!$revoke) {
#
# This operation has to be done as an admin person.
#
GeniUtil::FlipToElabMan();
my $output = GeniUtil::ExecQuiet("$WAP $SHAREVLAN -o $pid,$eid ".
" $linkname $token");
my $ecode = $?;
GeniUtil::FlipToGeniUser();
if ($ecode) {
print STDERR "Failed to share vlan:\n";
print STDERR $output;
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_ERROR, undef, $output);
}
libtestbed::SENDMAIL($TBOPS, "$linkname has been shared",
"$linkname in $slice_urn has been\n".
"shared by $user\n\n".
"Slice: $slice\n".
"Experiment: $experiment\n",
$TBOPS);
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
#
# Revoking is a litte trickier since we have to worry about the
# experiments that are actually using the shared vlan. But the
# backend program is going to revoke access from all the experiments
# using those ports. Oh well.
#
# This operation has to be done as an admin person.
#
GeniUtil::FlipToElabMan();
my $output = GeniUtil::ExecQuiet("$WAP $SHAREVLAN -r -f $pid,$eid ".
" $linkname ");
my $ecode = $?;
GeniUtil::FlipToGeniUser();
if ($ecode) {
print STDERR "Failed to unshare vlan:\n";
print STDERR $output;
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
libtestbed::SENDMAIL($TBOPS, "$linkname has been unshared",
"$linkname in $slice_urn has been\n".
"unshared by $user\n\n".
"Slice: $slice\n".
"Experiment: $experiment\n",
$TBOPS);
$slice->UnLock();
return GeniResponse->Create(GENIRESPONSE_SUCCESS);
}
# _Always_ make sure that this 1 is at the end of the file...
1;
#! /usr/bin/env python
#
# Copyright (c) 2008-2013 University of Utah and the Flux Group.
#
# {{{GENIPUBLIC-LICENSE
#
# GENI Public License
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and/or hardware specification (the "Work") to
# deal in the Work without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Work, and to permit persons to whom the Work
# is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Work.
#
# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
# IN THE WORK.
#
# }}}
#
#
#
import sys
import pwd
import getopt
import os
import time
import re
ACCEPTSLICENAME=1
def Usage():
print "usage: " + sys.argv[ 0 ] + " [option...] lanname token "
print "usage: " + sys.argv[ 0 ] + " [option...] revoke lanname "
print "Options:"
BaseOptions()
pass
debug = 0
revoke = 0
execfile( "test-common.py" )
if len(REQARGS) == 2:
if REQARGS[0] == "revoke":
lanname = REQARGS[1]
revoke = 1
else:
lanname = REQARGS[0]
token = REQARGS[1]
pass
else:
Usage()
sys.exit(1)
pass
#
# Get a credential for myself, that allows me to do things at the SA.
#
mycredential = get_self_credential()
print "Got my SA credential"
#
# Lookup slice and get credential.
#
myslice = resolve_slice( SLICENAME, mycredential )
print "Asking for slice credential for " + SLICENAME
slicecredential = get_slice_credential( myslice, mycredential )
print "Got the slice credential"
#
# Create the image
#
params = {}
params["credentials"] = (slicecredential,)
params["slice_urn"] = myslice["urn"]
params["lanname"] = lanname
if revoke == 0:
params["token"] = token
print "Sharing the lan ..."
rval,response = do_method("cm", "ShareLan", params, version="2.0")
if rval:
Fatal("Could not share lan")
pass
pass
else:
print "Unsharing the lan ..."
rval,response = do_method("cm", "UnShareLan", params, version="2.0")
if rval:
Fatal("Could not unshare lan")
pass
pass
......@@ -102,6 +102,8 @@ elsif ($GENI_VERSION eq "2.0") {
"CreateImage" => \&GeniCMV2::CreateImage,
"DeleteImage" => \&GeniCMV2::DeleteImage,
"ListImages" => \&GeniCMV2::ListImages,
"ShareLan" => \&GeniCMV2::ShareLan,
"UnShareLan" => \&GeniCMV2::UnShareLan,
"ListActiveSlivers" => \&GeniCMV2::ListActiveSlivers,
};
}
......
......@@ -33,13 +33,14 @@ sub usage()
{
print STDERR "Usage: sharevlan [-o] eid lanname token\n";
print STDERR " sharevlan -r | -i token\n";
print STDERR " sharevlan -r eid lanname\n";
print STDERR " -r Revoke sharing instead\n";
print STDERR " -i Show members.\n";
print STDERR " -o Sharing is open to everyone.\n";
print STDERR " -l List all shared vlans\n";
exit(-1);
}
my $optlist = "hrolif";
my $optlist = "hrRolif";
my $revoke = 0;
my $open = 0;
my $list = 0;
......@@ -112,8 +113,9 @@ elsif (defined($options{'l'})) {
elsif (defined($options{'r'})) {
$revoke = 1;
usage()
if (@ARGV != 1);
if (defined($options{'R'})) {
$force = 1;
}
}
else {
usage()
......@@ -163,7 +165,7 @@ if ($info) {
my $token = $ARGV[0];
my $query_result =
DBQueryFatal("select lanid from shared_vlans where token='$token'");
if (!$query_result) {
if (!$query_result->numrows) {
fatal("No such shared vlan");
}
my ($lanid) = $query_result->fetchrow_array();
......@@ -208,24 +210,61 @@ if ($info) {
}
if ($revoke) {
my $token = $ARGV[0];
my ($vlan,$lanid);
my $busy = 0;
if (@ARGV == 1) {
my $token = $ARGV[0];
my $query_result =
DBQueryFatal("select lanid from shared_vlans where token='$token'");
if (!$query_result->numrows) {
fatal("No such shared vlan");
}
($lanid) = $query_result->fetchrow_array();
$vlan = VLan->Lookup($lanid);
if (!defined($vlan)) {
fatal("No such vlan $lanid");
}
}
elsif (@ARGV == 2) {
my $eid = $ARGV[0];
my $lanname = $ARGV[1];
my $experiment = Experiment->Lookup($eid);
if (!defined($experiment)) {
fatal("No such experiment $eid");
}
$vlan = VLan->Lookup($experiment, $lanname);
if (!defined($vlan)) {
fatal("No such lan in $experiment");
}
$lanid = $vlan->lanid();
}
else {
usage();
}
if (!$vlan->IsShared()) {
fatal("Lan is not shared");
}
#
# Do not allow if the lan is actually being used by other experiments.
# The lan is obviously shared, so we have to lock it.
# It should not spend much time locked though, so the
# timeout should not be too long; indicates an error if
# it is.
#
my $query_result =
DBQueryFatal("select lanid from shared_vlans where token='$token'");
if (!$query_result) {
fatal("No such shared vlan");
}
my ($lanid) = $query_result->fetchrow_array();
my $vlan = VLan->Lookup($lanid);
if (!defined($vlan)) {
fatal("No such vlan $lanid");
if ($vlan->Lock(180) != 0) {
fatal("Could not lock vlan $lanid");
}
#
# Do not allow if the lan is actually being used by other experiments.
#
my @members;
if ($vlan->MemberList(\@members) != 0) {
$vlan->Unlock();
fatal("Could not get member list for $vlan");
}
foreach my $member (@members) {
......@@ -244,21 +283,42 @@ if ($revoke) {
if (defined($experiment)) {
print $node->node_id() . ":$iface in ";
print $experiment->pid() . "/" . $experiment->eid() . " ";
print "is using this vlan!\n";
$busy++;
print "is using this vlan";
if ($force) {
print ", but we are going to forcibly revoke it!\n";
my $portlan = Lan->Lookup($experiment, $portlan_lanname);
if (!defined($portlan)) {
$vlan->Unlock();
fatal("Could not revoke sharing of $vlan by ".
"$portlan_lanname in $experiment cause ".
"portlan_lanname does not exist!\n");
}
if ($experiment->ClearPortLans(1, $portlan)) {
$vlan->Unlock();
fatal("Could not revoke sharing of $vlan by ".
"$portlan_lanname in $experiment!\n");
}
}
else {
print "!\n";
$busy++;
}
}
else {
print $node->node_id() . ":$iface is marked as in this vlan ";
print "but the experiment is gone!\n";
print $node->node_id() . ":$iface is marked as shared in this ";
print "vlan but the experiment is gone!\n";
}
}
}
if ($busy) {
print "*** Refusing to delete shared vlan until sharers give it up!\n";
$vlan->Unlock();
exit(1);
}
DBQueryFatal("delete from shared_vlans where token='$token'");
$vlan->Unlock();
DBQueryFatal("delete from shared_vlans where lanid='$lanid'");
exit(0);
}
......
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