Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
emulab
emulab-devel
Commits
1ad5c4a6
Commit
1ad5c4a6
authored
Feb 15, 2011
by
Jonathon Duerig
Browse files
Merge branch 'master' of git-public.flux.utah.edu:/flux/git/emulab-devel
parents
1351043d
2136aec5
Changes
13
Hide whitespace changes
Inline
Side-by-side
db/Lan.pm.in
View file @
1ad5c4a6
...
...
@@ -3198,5 +3198,76 @@ sub SetRole($$)
return
$
self
->
GetLan
()->
SetRole
($
role
);
}
############################################################################
#
#
Another
convenience
package
,
for
external
references
.
#
package
ExternalNetwork
;
use
libdb
;
use
libtestbed
;
use
English
;
use
Lan
;
use
overload
(
'""'
=>
'Stringify'
);
#
#
Lookup
by
either
the
node
or
the
network
name
.
#
sub
Lookup
($$)
{
my
($
class
,
$
arg
)
=
@
_
;
#
Always
sanity
check
before
hitting
the
DB
.
return
undef
if
(
! ($arg =~ /^[-\w]*$/));
my
$
query_result
=
DBQueryWarn
(
"select * from external_networks "
.
"where node_id='$arg' or network_id='$arg'"
);
return
undef
if
(
!$query_result || !$query_result->numrows);
#
This
would
be
unusual
;
if
($
query_result
->
numrows
>
1
)
{
print
STDERR
"*** Multiple rows in external_networks for $arg
\n
"
;
return
undef
;
}
my
$
self
=
{};
$
self
->{
'DBROW'
}
=
$
query_result
->
fetchrow_hashref
();
bless
($
self
,
$
class
);
return
$
self
;
}
#
accessors
sub
field
($$)
{
return
((
! ref($_[0])) ? -1 : $_[0]->{'DBROW'}->{$_[1]}); }
sub
node_id
($)
{
return
field
($
_
[
0
],
'node_id'
);
}
sub
node_type
($)
{
return
field
($
_
[
0
],
'node_type'
);
}
sub
network_id
($)
{
return
field
($
_
[
0
],
'network_id'
);
}
sub
min_vlan
($)
{
return
field
($
_
[
0
],
'min_vlan'
);
}
sub
max_vlan
($)
{
return
field
($
_
[
0
],
'max_vlan'
);
}
#
#
Stringify
for
output
.
#
sub
Stringify
($)
{
my
($
self
)
=
@
_
;
my
$
node_id
=
$
self
->
node_id
();
my
$
network_id
=
$
self
->
network_id
();
return
"[External Network: $network_id,$node_id]"
;
}
#
#
Given
a
vlan
tag
,
is
it
okay
(
in
the
range
).
#
sub
VlanTagOkay
($$)
{
my
($
self
,
$
tag
)
=
@
_
;
return
($
tag
>=
$
self
->
min_vlan
()
&&
$
tag
<
$
self
->
max_vlan
()
?
1
:
0
);
}
#
_Always_
make
sure
that
this
1
is
at
the
end
of
the
file
...
1
;
os/frisbee.redux/client.c
View file @
1ad5c4a6
...
...
@@ -1693,7 +1693,8 @@ PlayFrisbee(void)
p
->
msg
.
join2
.
chunksize
=
MAXCHUNKSIZE
;
p
->
msg
.
join2
.
blocksize
=
MAXBLOCKSIZE
;
p
->
msg
.
join2
.
bytecount
=
p
->
msg
.
join
.
blockcount
*
MAXBLOCKSIZE
;
(
uint64_t
)
p
->
msg
.
join
.
blockcount
*
MAXBLOCKSIZE
;
}
CLEVENT
(
1
,
EV_CLIJOINREP
,
CHUNKSIZE
,
BLOCKSIZE
,
...
...
os/frisbee.redux/decls.h
View file @
1ad5c4a6
...
...
@@ -290,7 +290,7 @@ typedef struct {
int32_t
blockcount
;
int32_t
chunksize
;
int32_t
blocksize
;
int64_t
bytecount
;
u
int64_t
bytecount
;
}
join2
;
/*
...
...
protogeni/lib/GeniCM.pm.in
View file @
1ad5c4a6
...
...
@@ -1053,6 +1053,7 @@ sub GetTicketAuxAux($$$$$$$$$)
"n:interface_ref",
$linkref)->get_nodelist();
my %managers = ();
my %hops = ();
my $ifacenum = 1;
my $vindex = 0;
my $trivial_ok = 1;
...
...
@@ -1065,7 +1066,7 @@ sub GetTicketAuxAux($$$$$$$$$)
#
# Look for managers list; optional for now. If not specified then
# we assume the link is for thi
e
CM.
# we assume the link is for thi
s
CM.
#
if (GeniXML::FindNodes("n:component_manager", $linkref)) {
%managers = map { GetLinkManager($_) => $_ }
...
...
@@ -1080,6 +1081,16 @@ sub GetTicketAuxAux($$$$$$$$$)
if (!exists($managers{$ENV{'
MYURN
'}}));
}
#
# Look for hops list; optional.
#
if (GeniXML::FindNodes("n:component_hop", $linkref)) {
%hops = map { GeniXML::GetNodeId($_) => $_ }
GeniXML::FindNodes("n:component_hop",
$linkref)->get_nodelist();
}
#
# Ick. Before we create the virt_lan_lans entry, we have to check
# inside to see if one of the interfaces is connected to a lan
...
...
@@ -1150,34 +1161,85 @@ sub GetTicketAuxAux($$$$$$$$$)
if (!exists($namemap{$node_nickname}) &&
exists($external_nodemap{$node_nickname})) {
#
# I have completely punted on how we represent external
# links in the DB so that we know what to put into the
# virtual topology. For now, just hardwire the one test
# case.
# Find the hop that says how we get to the edge. For
# now I assume a single hop is all we ever have.
#
my $noderef = $external_nodemap{$node_nickname};
my $other_cm = GeniXML::GetManagerId($noderef);
if (! ($other_cm eq "urn:publicid:IDN+" .
"myelab.testbed.emulab.net+authority+cm" ||
$other_cm eq "urn:publicid:IDN+" .
"emulab.net+authority+cm")) {
my $hopref;
my $hop_urn;
foreach my $urn (keys(%hops)) {
my ($auth,undef,undef) = GeniHRN::Parse($urn);
if (defined($auth) and $auth eq $OURDOMAIN) {
$hopref = $hops{$urn};
$hop_urn= $urn;
last;
}
}
if (!defined($hopref)) {
$response =
GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"$lanname:
links to external node
");
"$lanname:
no local component_hop
");
goto bad;
}
my (undef,undef,$hop_id) = GeniHRN::Parse($hop_urn);
print "$hop_id\n";
#
# An artifact of the way we convert physical links
# into urns is that we now have to undo that to
# figure out what node and interface. At the moment
# it looks like "link-ion:eth2-procurve-pgeni-wash:(null)"
#
my ($link,$iface,undef) = split(":", $hop_id);
if (! (defined($link) && defined($iface))) {
$response =
GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"$lanname: bad component_hop");
goto bad;
}
# and strip off the link- stuff.
my ($network_id) = ($link =~ /^link-(.*)$/);
# ditto for the iface cruft.
my ($iface_id) = ($iface =~ /^(eth\d*)/);
if (! ($network_id =~ /^[-\w]*$/ &&
$iface_id =~ /^[-\w]*$/)) {
$response =
GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"$lanname: bad network or iface");
goto bad;
}
#
# Look in the external networks table to get the hop
# details.
#
my $network = ExternalNetwork->Lookup($network_id);
if (!defined($network)) {
$response =
GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"$lanname: unknown component_hop");
goto bad;
}
my $network_node = Node->Lookup($network->node_id());
if (!defined($network_node)) {
$response =
GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"$lanname: unknown component_hop node");
goto bad;
}
#
# Stick in a reference to the fake bbg node that
# corresponds to where the link comes in.
# Stick in a reference to the fake node.
#
my $virtnode =
$virtexperiment->NewTableRow("virt_nodes",
{"vname" => $node_nickname,
"type" =>
"bbgenivm"
,
"osname" => '
BBGENIVM
-
FAKE
',
"ips" => '', # deprecated
"cmd_line"=> '', # bogus
"fixed" =>
"bbg1"
});
{"vname" => $node_nickname,
"type" =>
$network->node_type()
,
"osname" => '',
"ips" => '', # deprecated
"cmd_line"=> '', # bogus
"fixed" =>
$network_node->node_id()
});
if (!defined($virtnode)) {
print STDERR "Error creating bbg node\n";
$response = GeniResponse->Create(GENIRESPONSE_ERROR);
...
...
@@ -1188,9 +1250,8 @@ sub GetTicketAuxAux($$$$$$$$$)
{"vname" => $node_nickname,
"desire" => "pcshared",
"weight" => 0.95});
$virtexperiment->multiplex_factor(1);
$virtexperiment->encap_style("vlan");
$iface_name =
""
;
$iface_name =
$iface_id
;
$iface_vport = 0;
goto stitch;
}
...
...
protogeni/lib/GeniCMV2.pm.in
View file @
1ad5c4a6
...
...
@@ -314,6 +314,8 @@ sub CreateSliver($)
my
$
impotent
=
$
argref
->{
'impotent'
}
||
0
;
require
Node
;
require
Experiment
;
require
libtestbed
;
require
libaudit
;
#
For
now
,
I
am
not
worrying
about
the
slice_urn
argument
.
if
(
! (defined($credentials) &&
...
...
@@ -426,26 +428,31 @@ sub CreateSliver($)
return
GeniResponse
->
Create
(
GENIRESPONSE_ERROR
,
undef
,
"Internal Error"
);
}
#
#
At
this
point
we
want
to
return
and
let
the
startsliver
proceed
#
in
the
background
#
my
$
mypid
=
fork
();
if
($
mypid
)
{
#
Let
the
child
get
going
.
sleep
(
1
);
return
GeniResponse
->
Create
(
GENIRESPONSE_SUCCESS
,
[$
sliver_credential
,
$
sliver_manifest
]);
}
#
This
switches
the
file
that
we
are
writing
to
.
libaudit
::
AuditFork
();
#
Make
sure
that
the
next
phase
sees
all
changes
.
Experiment
->
FlushAll
();
Node
->
FlushAll
();
if
($
aggregate
->
Start
($
API_VERSION
,
0
)
!= 0) {
$
slice
->
UnLock
();
return
GeniResponse
->
Create
(
GENIRESPONSE_ERROR
,
undef
,
"Could not start sliver"
)
;
print
STDERR
"Could not start sliver
\n
"
;
return
-
1
;
}
#
GeniCM
::
UpdateManifest
($
slice
);
$
sliver_manifest
=
$
aggregate
->
GetManifest
(
1
);
if
(
!defined($sliver_manifest)) {
print
STDERR
"CreateSliver: Could not get manifest for $aggregate
\n
"
;
return
GeniResponse
->
Create
(
GENIRESPONSE_ERROR
,
undef
,
"Internal Error"
);
}
$
slice
->
UnLock
();
return
GeniResponse
->
Create
(
GENIRESPONSE_SUCCESS
,
[$
sliver_credential
,
$
sliver_manifest
]);
return
0
;
}
#
...
...
@@ -1547,6 +1554,7 @@ sub ReserveVlanTags($)
my
$
taglist
=
$
argref
->{
'taglist'
};
my
$
response
;
my
$
actualtag
;
my
($
myauth
,
undef
,
undef
)
=
GeniHRN
::
Parse
($
ENV
{
'MYURN'
});
#
List
of
vlans
to
delete
after
getting
the
tags
.
my
@
delete
=
();
my
%
linkmap
=
();
...
...
@@ -1682,6 +1690,52 @@ sub ReserveVlanTags($)
"Could not find link in rspec"
);
goto
done
;
}
#
#
Go
through
the
component
hops
and
find
the
one
that
refers
to
us
.
#
This
is
our
external
network
point
.
#
my
$
hopref
;
foreach
my
$
ref
(
GeniXML
::
FindNodes
(
"n:component_hop"
,
$
linkref
)->
get_nodelist
())
{
my
$
component_urn
=
GeniXML
::
GetNodeId
($
ref
);
my
($
auth
,
undef
,
undef
)
=
GeniHRN
::
Parse
($
component_urn
);
if
(
defined
($
auth
)
and
$
auth
eq
$
myauth
)
{
$
hopref
=
$
ref
;
last
;
}
}
if
(
!defined($hopref)) {
$
response
=
GeniResponse
->
Create
(
GENIRESPONSE_BADARGS
,
undef
,
"Could not find hop in link"
);
goto
done
;
}
my
$
hop_urn
=
GeniXML
::
GetNodeId
($
hopref
);
my
(
undef
,
undef
,$
hop_id
)
=
GeniHRN
::
Parse
($
hop_urn
);
#
#
An
artifact
of
the
way
we
convert
physical
links
#
into
urns
is
that
we
now
have
to
undo
that
to
#
figure
out
what
node
and
interface
.
At
the
moment
#
it
looks
like
"link-ion:eth2-procurve-pgeni-wash:(null)"
#
my
($
link
,$
iface
,
undef
)
=
split
(
":"
,
$
hop_id
);
if
(
! (defined($link) && defined($iface))) {
$
response
=
GeniResponse
->
Create
(
GENIRESPONSE_ERROR
,
undef
,
"badly specified component_hop"
);
goto
done
;
}
#
and
strip
off
the
link
-
stuff
.
my
($
network_id
)
=
($
link
=~
/^
link
-(.*)$/);
my
$
network
=
ExternalNetwork
->
Lookup
($
network_id
);
if
(
!defined($network)) {
$
response
=
GeniResponse
->
Create
(
GENIRESPONSE_BADARGS
,
undef
,
"$hop_urn is not an external network"
);
goto
done
;
}
my
$
vlan
=
VLan
->
Lookup
($
slice_experiment
,
$
linkname
);
if
(
!defined($vlan)) {
#
...
...
@@ -1714,6 +1768,29 @@ sub ReserveVlanTags($)
$
actualtag
=
$
tag
;
}
else
{
#
#
Check
to
see
what
tags
are
valid
for
us
.
#
my
@
tags
=
();
foreach
my
$
tag
(@{
$
taglist
})
{
push
(@
tags
,
$
tag
)
if
($
network
->
VlanTagOkay
($
tag
));
}
if
(
!@tags) {
#
#
Return
a
list
of
okay
tags
.
#
my
@
okaytags
=
();
for
(
my
$
i
=
$
network
->
min_vlan
();
$
i
<
$
network
->
max_vlan
();
$
i
++)
{
push
(@
okaytags
,
$
i
);
}
$
response
=
GeniResponse
->
Create
(
GENIRESPONSE_SEARCHFAILED
,
\@
okaytags
,
"Could not find a suitable tag"
);
goto
done
;
}
#
#
This
is
a
debugging
hack
;
Inside
an
elabinelab
,
it
might
be
#
an
other
local
elabinelab
or
the
outer
boss
.
In
this
case
,
...
...
@@ -1722,7 +1799,6 @@ sub ReserveVlanTags($)
#
In
this
case
,
we
can
just
stipulate
that
one
of
the
tags
is
good
.
#
if
($
ELABINELAB
)
{
my
($
myauth
,
undef
,
undef
)
=
GeniHRN
::
Parse
($
ENV
{
'MYURN'
});
my
($
hisauth
,
undef
,
undef
)
=
GeniHRN
::
Parse
($
slice_urn
);
my
@
tmp
=
split
(
'\.'
,
$
OURDOMAIN
);
...
...
@@ -1732,7 +1808,7 @@ sub ReserveVlanTags($)
if
($
myauth
=~
/$
dom
$/
&&
$
hisauth
=~
/$
dom
$/)
{
print
STDERR
"ElabInElab clause is true: $myauth $hisauth
\n
"
;
my
$
tag
=
pop
(@
{
$
taglist
}
);
my
$
tag
=
pop
(@
tags
);
if
($
vlan
->
ReserveVlanTag
($
tag
))
{
$
actualtag
=
$
tag
;
goto
gottag
;
...
...
@@ -1751,7 +1827,7 @@ sub ReserveVlanTags($)
#
my
$
vlanid
=
$
vlan
->
lanid
();
my
$
tag
=
undef
;
my
@
tmp
=
@
{
$
taglist
}
;
my
@
tmp
=
@
tags
;
while
(@
tmp
)
{
$
tag
=
pop
(@
tmp
);
if
($
vlan
->
ReserveVlanTag
($
tag
,
1
))
{
...
...
protogeni/scripts/expire_daemon.in
View file @
1ad5c4a6
#!/usr/bin/perl -w
#
# GENIPUBLIC-COPYRIGHT
# Copyright (c) 2008-201
0
University of Utah and the Flux Group.
# Copyright (c) 2008-201
1
University of Utah and the Flux Group.
# All rights reserved.
#
use
strict
;
...
...
@@ -180,10 +180,13 @@ sub ExpireTickets()
sub
ExpireSlices
()
{
my
$query_result
=
GeniDB::
DBQueryWarn
("
select idx from geni_slices
"
.
"
where UNIX_TIMESTAMP(now()) >
"
.
"
UNIX_TIMESTAMP(expires) and
"
.
"
shutdown is null
");
GeniDB::
DBQueryWarn
("
select idx from geni_slices where
"
.
"
(UNIX_TIMESTAMP(now()) >
"
.
"
UNIX_TIMESTAMP(expires) or
"
.
"
(isplaceholder=1 and
"
.
"
(UNIX_TIMESTAMP(now()) -
"
.
"
UNIX_TIMESTAMP(created)) > 3600))
"
.
"
and shutdown is null
");
while
(
my
(
$idx
)
=
$query_result
->
fetchrow_array
())
{
my
$slice
=
GeniSlice
->
Lookup
(
$idx
);
...
...
@@ -211,7 +214,7 @@ sub ExpireSlices()
# See if we have any local nodes. No point in using the idle
# check if there are no nodes.
#
if
(
$idlecheck
)
{
if
(
$idlecheck
&&
!
$slice
->
isplaceholder
()
)
{
my
@localnodes
=
();
$experiment
->
LocalNodeListNames
(
\
@localnodes
);
if
(
@localnodes
)
{
...
...
@@ -233,7 +236,12 @@ sub ExpireSlices()
print
STDERR
"
$slice
(
$experiment
) is idle; releasing.
\n
";
}
else
{
print
STDERR
"
$slice
(
$experiment
) has expired; releasing.
\n
";
if
(
$slice
->
isplaceholder
())
{
print
STDERR
"
Releasing placeholder
$slice
$experiment
.
\n
";
}
else
{
print
STDERR
"
Expiring
$slice
$experiment
.
\n
";
}
}
cleanup:
if
(
GeniCM::
CleanupDeadSlice
(
$slice
)
!=
0
)
{
...
...
protogeni/scripts/reservevlans.in
View file @
1ad5c4a6
...
...
@@ -19,7 +19,8 @@ sub usage()
exit
(
1
);
}
my
$optlist
=
"";
my
$basetag
=
750
;
my
$mintag
=
750
;
my
$maxtag
=
1000
;
my
$other_manager
;
#
# Configure variables
...
...
@@ -38,8 +39,8 @@ delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
# Protos
sub
fatal
($);
sub
ReserveLocalTags
();
sub
ReserveRemoteTags
($@);
sub
ReserveLocalTags
(
@
);
sub
ReserveRemoteTags
($
$
@);
#
# Turn off line buffering on output
...
...
@@ -162,9 +163,10 @@ if (!defined($vlan)) {
fatal
("
Could not create vlan for
$linkname
");
}
my
$lanid
=
$vlan
->
lanid
();
my
@othertags
=
();
while
(
my
@tags
=
ReserveLocalTags
())
{
my
$tag
=
ReserveRemoteTags
(
$other_manager
,
@tags
);
while
(
my
@tags
=
ReserveLocalTags
(
@othertags
))
{
my
$tag
=
ReserveRemoteTags
(
$other_manager
,
\
@othertags
,
@tags
);
if
(
$tag
)
{
print
STDERR
"
Agreeded on tag
$tag
. Releasing the rest.
\n
";
foreach
my
$t
(
@tags
)
{
...
...
@@ -175,14 +177,13 @@ while (my @tags = ReserveLocalTags()) {
}
# Clear all the tags so we can try again.
$vlan
->
ClearReservedVlanTag
();
last
;
}
$vlan
->
Destroy
();
exit
(
0
);
sub
ReserveRemoteTags
($@)
sub
ReserveRemoteTags
($
$
@)
{
my
(
$authority
,
@tags
)
=
@_
;
my
(
$authority
,
$othertags
,
@tags
)
=
@_
;
my
$method_args
=
{};
$method_args
->
{'
credentials
'}
=
[
$credential
->
asString
()];
...
...
@@ -191,6 +192,7 @@ sub ReserveRemoteTags($@)
$method_args
->
{'
rspec
'}
=
$rspecstr
;
$method_args
->
{'
linkname
'}
=
$linkname
;
$method_args
->
{'
taglist
'}
=
\
@tags
;
@$othertags
=
();
my
$response
=
Genixmlrpc::
CallMethod
(
$authority
->
url
(),
...
...
@@ -207,6 +209,16 @@ sub ReserveRemoteTags($@)
}
if
(
$response
->
code
==
GENIRESPONSE_SEARCHFAILED
)
{
print
STDERR
"
*** Target CM did not like any of the tags we sent
\n
";
if
(
defined
(
$response
->
value
())
&&
ref
(
$response
->
value
())
eq
"
ARRAY
"){
my
@otags
=
@
{
$response
->
value
()
};
print
STDERR
"
*** But they said they like these tags:
@otags
\n
";
foreach
my
$t
(
@otags
)
{
if
(
!
(
$t
=~
/^\d*$/
))
{
fatal
("
Bad tag return from target CM:
$t
");
}
}
@$othertags
=
@otags
;
}
return
undef
;
}
my
$tempstr
=
$response
->
value
();
...
...
@@ -228,38 +240,62 @@ sub ReserveRemoteTags($@)
#
# Reserve a set of local tags and return a list.
#
sub
ReserveLocalTags
()
sub
ReserveLocalTags
(
@
)
{
my
@tags
=
();
my
@try
=
();
for
(
my
$i
=
0
;
$i
<
100
;
$i
++
)
{
# Try for 10.
last
if
(
scalar
(
@tags
)
>=
10
);
my
$tag
=
$basetag
++
;
my
@otags
=
@_
;
my
@tags
=
();
my
@try
=
();
my
$gototags
=
scalar
(
@otags
);
if
(
VLan
->
VlanTagAvailable
(
$tag
))
{
push
(
@try
,
$tag
);
if
(
scalar
(
@try
)
>
5
)
{
# Need more then one tag to activate "block" mode.
print
STDERR
"
Trying to allocate vlan tags:
@try
\n
";
system
("
$SNMPIT
-A
$pid
$eid
$lanid
,
"
.
join
("
,
",
@try
));
if
(
$?
)
{
fatal
("
Could not reserve vlan tags
\n
");
}
# See what tags we actually got.
foreach
my
$t
(
@try
)
{
push
(
@tags
,
$t
)
if
(
$vlan
->
HasVlanTagReserved
(
$t
));
}
@try
=
();
#
# The goal is reserve 10 tags before we call the target CM, but
# we will take what we can get.
#
while
(
scalar
(
@tags
)
<
10
)
{
if
(
$gototags
)
{
while
(
@otags
)
{
my
$t
=
pop
(
@otags
);
push
(
@try
,
$t
)
if
(
VLan
->
VlanTagAvailable
(
$t
));
# But do not let the other CM make us reserve too many at once
last
if
(
scalar
(
@try
)
>
10
);
}
}
else
{
while
(
$mintag
<
$maxtag
)
{
my
$t
=
$mintag
++
;
push
(
@try
,
$t
)
if
(
VLan
->
VlanTagAvailable
(
$t
));
# But not more then 10 at a time.
last
if
(
scalar
(
@try
)
>
10
);
}
}
# Nothing is available to reserve.
last
if
(
!
@try
);
#
# Do this in "blockmode" so that snmpit does not throw an error
# if one of the tags is not available.
#
print
STDERR
"
Trying to allocate vlan tags:
@try
\n
";
system
("
$SNMPIT
--blockmode -A
$pid
$eid
$lanid
,
"
.
join
("
,
",
@try
));
if
(
$?
)
{
fatal
("
Could not reserve vlan tags
\n
");
}
# See what tags we actually got.
foreach
my
$t
(
@try
)
{
push
(
@tags
,
$t
)
if
(
$vlan
->
HasVlanTagReserved
(
$t
));