Commit d85afe43 authored by Leigh B Stoller's avatar Leigh B Stoller
Browse files

Merge branch 'master' of git-public.flux.utah.edu:/flux/git/emulab-devel

parents 6a7a5a0b ba73e37b
...@@ -36,9 +36,21 @@ inline double loss_distance(double a,double b) { ...@@ -36,9 +36,21 @@ inline double loss_distance(double a,double b) {
class tb_delay_info { class tb_delay_info {
public: public:
// TODO: Make a real constructor!
tb_delay_info() : bandwidth(0), delay(0), loss(0.0),
adjust_to_native_bandwidth(false) { ; } ;
// Note the adjust_to_native_bandwidth flag below
int bandwidth; int bandwidth;
int delay; int delay;
double loss; double loss;
// If true, we just use the native bandwidth of the interface that gets
// picked; the bandwidth member gets updated on every assignment. This only
// makes sense on virtual links.
bool adjust_to_native_bandwidth;
// TODO: We don't actually use any of these; we should get 'em out of here.
int bw_under,bw_over; int bw_under,bw_over;
int delay_under,delay_over; int delay_under,delay_over;
double loss_under,loss_over; double loss_under,loss_over;
...@@ -62,12 +74,14 @@ public: ...@@ -62,12 +74,14 @@ public:
friend ostream &operator<<(ostream &o, const tb_delay_info& delay) friend ostream &operator<<(ostream &o, const tb_delay_info& delay)
{ {
o << "tb_delay_info: bw=" << delay.bandwidth << "+" << // Only print out the stuff that matters, not all of the pieces we don't
delay.bw_over << "-" << delay.bw_under << "/" << delay.bw_weight; // use
o << " delay=" << delay.delay << "+" << delay.delay_over << o << "tb_delay_info: bw=" << delay.bandwidth;
"-" << delay.delay_under << "/" << delay.delay_weight; if (delay.adjust_to_native_bandwidth) {
o << " loss=" << delay.loss << "+" << delay.loss_over << "-" << o << "(adjusted)";
delay.loss_under << "/" << delay.loss_weight; }
o << " delay=" << delay.delay;
o << " loss=" << delay.loss;
o << endl; o << endl;
return o; return o;
} }
......
...@@ -98,8 +98,8 @@ top file: <line>* ...@@ -98,8 +98,8 @@ top file: <line>*
<desire> ::= <desire_type><desire_name>:<float> <desire> ::= <desire_type><desire_name>:<float>
<desire_type> ::= | "?+" | "&*" | "*!" <desire_type> ::= | "?+" | "&*" | "*!"
<link_line1> ::= link <link_name> <interface> <interface> <int> <float> <float> <link_line1> ::= link <link_name> <interface> <interface> <int|'*'> <float>
<link_flag>* <float> <link_flag>*
<link_line2> ::= link <link_name> <interface> <interface> <int> <float> <float> <link_line2> ::= link <link_name> <interface> <interface> <int> <float> <float>
<link_type> <link_flag*> <link_type> <link_flag*>
<link_flag> ::= nodelay | emulated | trivial_ok | fixsrciface:<string> | <link_flag> ::= nodelay | emulated | trivial_ok | fixsrciface:<string> |
...@@ -124,7 +124,9 @@ Interpretation: ...@@ -124,7 +124,9 @@ Interpretation:
<desire> - see <feature> in the ptop file <desire> - see <feature> in the ptop file
<desire_type> - see <feature_type> in the ptop file <desire_type> - see <feature_type> in the ptop file
<link_line1> and <link_line2> - see notes below for the difference between the <link_line1> and <link_line2> - see notes below for the difference between the
two line types. See <link_line> in the ptop file. two line types. See <link_line> in the ptop file. The integer gives the
bandwidth for the link, but if the special value '*' is given, assign
simply uses the native bandwidth of the interfaces selected.
<link_type> - when present, a valid mapping must put this virtual link on a <link_type> - when present, a valid mapping must put this virtual link on a
physical link of the same type. For multi-hop link mappings (ie. those physical link of the same type. For multi-hop link mappings (ie. those
that traverse multiple switches), *all* links used must be of this type. I that traverse multiple switches), *all* links used must be of this type. I
......
...@@ -18,6 +18,7 @@ static const char rcsid[] = "$Id: parse_request_rspec.cc,v 1.16 2009-10-21 20:49 ...@@ -18,6 +18,7 @@ static const char rcsid[] = "$Id: parse_request_rspec.cc,v 1.16 2009-10-21 20:49
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <sys/time.h>
#include "anneal.h" #include "anneal.h"
#include "vclass.h" #include "vclass.h"
......
...@@ -242,8 +242,18 @@ int parse_top(tb_vgraph &vg, istream& input) ...@@ -242,8 +242,18 @@ int parse_top(tb_vgraph &vg, istream& input)
l->type = link_type; l->type = link_type;
put(vedge_pmap,e,l); put(vedge_pmap,e,l);
if ((sscanf(bw.c_str(),"%d",&(l->delay_info.bandwidth)) != 1) || // Special flag: treat a bandwidth of '*' specially
(sscanf(bwunder.c_str(),"%d",&(l->delay_info.bw_under)) != 1) || if (!strcmp(bw.c_str(),"*")) {
l->delay_info.bandwidth = -2; // Special flag
l->delay_info.adjust_to_native_bandwidth = true;
} else {
if (sscanf(bw.c_str(),"%d",&(l->delay_info.bandwidth)) != 1) {
top_error("Bad line line, bad bandwidth characteristics.");
}
}
// Scan in the rest of the delay_info structure
if ((sscanf(bwunder.c_str(),"%d",&(l->delay_info.bw_under)) != 1) ||
(sscanf(bwover.c_str(),"%d",&(l->delay_info.bw_over)) != 1) || (sscanf(bwover.c_str(),"%d",&(l->delay_info.bw_over)) != 1) ||
(sscanf(bwweight.c_str(),"%lg",&(l->delay_info.bw_weight)) != 1) || (sscanf(bwweight.c_str(),"%lg",&(l->delay_info.bw_weight)) != 1) ||
(sscanf(delay.c_str(),"%d",&(l->delay_info.delay)) != 1) || (sscanf(delay.c_str(),"%d",&(l->delay_info.delay)) != 1) ||
...@@ -288,10 +298,10 @@ int parse_top(tb_vgraph &vg, istream& input) ...@@ -288,10 +298,10 @@ int parse_top(tb_vgraph &vg, istream& input)
parsed_line[i] << "."); parsed_line[i] << ".");
} }
} }
#ifdef PER_VNODE_TT
tb_vnode *vnode1 = get(vvertex_pmap,node1); tb_vnode *vnode1 = get(vvertex_pmap,node1);
tb_vnode *vnode2 = get(vvertex_pmap,node2); tb_vnode *vnode2 = get(vvertex_pmap,node2);
#ifdef PER_VNODE_TT
if (l->emulated) { if (l->emulated) {
if (!l->allow_trivial) { if (!l->allow_trivial) {
vnode1->total_bandwidth += l->delay_info.bandwidth; vnode1->total_bandwidth += l->delay_info.bandwidth;
...@@ -304,6 +314,14 @@ int parse_top(tb_vgraph &vg, istream& input) ...@@ -304,6 +314,14 @@ int parse_top(tb_vgraph &vg, istream& input)
vnode2->link_counts[link_type]++; vnode2->link_counts[link_type]++;
} }
#endif #endif
// Some sanity checks: this combination is illegal for now
if (l->delay_info.adjust_to_native_bandwidth && (l->allow_trivial ||
l->emulated)) {
top_error("Auto-assigning bandwidth on trivial or emulated links"
" not allowed!");
}
} }
} else if (command == string("make-vclass")) { } else if (command == string("make-vclass")) {
if (parsed_line.size() < 4) { if (parsed_line.size() < 4) {
......
...@@ -336,47 +336,95 @@ float find_link_resolutions(resolution_vector &resolutions, pvertex pv, ...@@ -336,47 +336,95 @@ float find_link_resolutions(resolution_vector &resolutions, pvertex pv,
++dest_switch_it) { ++dest_switch_it) {
if (*source_switch_it == *dest_switch_it) continue; if (*source_switch_it == *dest_switch_it) continue;
tb_link_info info(tb_link_info::LINK_INTERSWITCH); tb_link_info info(tb_link_info::LINK_INTERSWITCH);
if (find_interswitch_path(*source_switch_it,*dest_switch_it,vlink->delay_info.bandwidth,
info.plinks, info.switches) != 0) {
bool first_link, second_link;
/*
* Check to see if either, or both, pnodes are actually the
* switches we are looking for
*/
if ((pv == *source_switch_it) || (pv ==
*dest_switch_it)) {
first_link = false;
} else {
first_link = true;
}
if ((dest_pv == *source_switch_it) ||
(dest_pv == *dest_switch_it)) {
second_link = false;
} else {
second_link = true;
}
if (first_link) { /*
// Check only whether the source interface is fixed - this is the * Check to see if either, or both, pnodes are actually the
// first link in a multi-hop path * switches we are looking for
if (!find_best_link(pv,*source_switch_it,vlink,first,true,false)) { */
// No link to this switch bool first_link, second_link;
SDEBUG(cerr << " interswitch failed - no first link" if ((pv == *source_switch_it) || (pv ==
<< endl;) *dest_switch_it)) {
continue; first_link = false;
} } else {
} first_link = true;
}
if ((dest_pv == *source_switch_it) ||
(dest_pv == *dest_switch_it)) {
second_link = false;
} else {
second_link = true;
}
if (second_link) { // Get link objects
// Check only whether the dest interface is fixed - this is the if (first_link) {
// last link in a multi-hop path // Check only whether the source interface is fixed - this is the
if (!find_best_link(dest_pv,*dest_switch_it,vlink,second,false,true)) { // first link in a multi-hop path
// No link to tshis switch if (!find_best_link(pv,*source_switch_it,vlink,first,true,false)) {
SDEBUG(cerr << " interswitch failed - no second link" << endl;) // No link to this switch
SDEBUG(cerr << " interswitch failed - no first link"
<< endl;)
continue; continue;
} }
}
if (second_link) {
// Check only whether the dest interface is fixed - this is the
// last link in a multi-hop path
if (!find_best_link(dest_pv,*dest_switch_it,vlink,second,false,true)) {
// No link to tshis switch
SDEBUG(cerr << " interswitch failed - no second link" << endl;)
continue;
} }
}
// For regular links, we just grab the vlink's bandwidth; for links
// where we're matching the link speed to the 'native' one for the
// interface, we have to look up interface speeds on both ends.
int bandwidth;
if (vlink->delay_info.adjust_to_native_bandwidth) {
// Grab the actual plink objects for both pedges - it's possible for
// one or both to be missing if we're linking directly to a switch
// (as with a LAN)
tb_plink *first_plink = NULL;
tb_plink *second_plink = NULL;
if (first_link) {
first_plink = get(pedge_pmap,first);
}
if (second_link) {
second_plink = get(pedge_pmap,second);
}
if (first_plink != NULL && second_plink != NULL) {
// If both endpoints are not switches, we use the minimum
// bandwidth
bandwidth = min(first_plink->delay_info.bandwidth,
second_plink->delay_info.bandwidth);
} else if (first_plink == NULL) {
// If one end is a switch, use the bandwidth from the other
// end
bandwidth = second_plink->delay_info.bandwidth;
} else if (second_plink == NULL) {
bandwidth = first_plink->delay_info.bandwidth;
} else {
// Both endpoints are switches! (eg. this might be a link between
// two LANs): It is not at all clear what the right semantics
// for this would be, and unfortunately, we can't catch this
// earlier. So, exiting with an error is crappy, but it's
// unlikely to happen in our regular use, and it's the best we
// can do.
cerr << "*** Using bandwidth adjustment on virutal links " <<
"between switches not allowed " << endl;
exit(EXIT_FATAL);
}
} else {
// If not auto-adjusting, just use the specified bandwidth
bandwidth = vlink->delay_info.bandwidth;
}
// Find a path on the switch fabric between these two switches
if (find_interswitch_path(*source_switch_it,*dest_switch_it,bandwidth,
info.plinks, info.switches) != 0) {
// Okay, we found a real resolution!
if (flipped) { // Order these need to go in depends on flipped bit if (flipped) { // Order these need to go in depends on flipped bit
if (second_link) { if (second_link) {
info.plinks.push_front(second); info.plinks.push_front(second);
...@@ -491,6 +539,8 @@ void resolve_link(vvertex vv, pvertex pv, tb_vnode *vnode, tb_pnode *pnode, ...@@ -491,6 +539,8 @@ void resolve_link(vvertex vv, pvertex pv, tb_vnode *vnode, tb_pnode *pnode,
* doesn't usually get called for trivial links, and * doesn't usually get called for trivial links, and
* letting them fall through into the 'normal' link code * letting them fall through into the 'normal' link code
* below is disatrous! * below is disatrous!
* Note: We can't get here when doing adjust_to_native_bandwidth,
* since it's illegal to allow trivial links when it's in use.
*/ */
score_link_info(edge,pnode,dest_pnode,vnode,dest_vnode); score_link_info(edge,pnode,dest_pnode,vnode,dest_vnode);
} else { } else {
...@@ -784,6 +834,12 @@ void unscore_link_info(vedge ve,tb_pnode *src_pnode,tb_pnode *dst_pnode, tb_vnod ...@@ -784,6 +834,12 @@ void unscore_link_info(vedge ve,tb_pnode *src_pnode,tb_pnode *dst_pnode, tb_vnod
} }
#endif #endif
// If auto-adjusting the vlink bandwidth, we set it to a sentinel value
// so that we can detect any problems next time we do a score_link_info()
if (vlink->delay_info.adjust_to_native_bandwidth) {
vlink->delay_info.bandwidth = -2;
}
} }
/* /*
...@@ -1019,6 +1075,22 @@ void score_link_info(vedge ve, tb_pnode *src_pnode, tb_pnode *dst_pnode, tb_vnod ...@@ -1019,6 +1075,22 @@ void score_link_info(vedge ve, tb_pnode *src_pnode, tb_pnode *dst_pnode, tb_vnod
{ {
tb_vlink *vlink = get(vedge_pmap,ve); tb_vlink *vlink = get(vedge_pmap,ve);
tb_pnode *the_switch; tb_pnode *the_switch;
// If this link is to be adjusted to the native speed of the interface, go
// ahead and do that now - we use the minimum of the two endpoint interfaces
// Note! Not currently supported on trivial links! (it's illegal for
// adjust_to_native_bandwidth and trivial_ok to both be true)
if (vlink->delay_info.adjust_to_native_bandwidth &&
vlink->link_info.type_used != tb_link_info::LINK_TRIVIAL) {
// Check for special sentinel value to make sure we remembered to re-set
// the value before
assert(vlink->delay_info.bandwidth == -2);
tb_plink *front_plink = get(pedge_pmap, vlink->link_info.plinks.front());
tb_plink *back_plink = get(pedge_pmap, vlink->link_info.plinks.back());
vlink->delay_info.bandwidth =
min(front_plink->delay_info.bandwidth, back_plink->delay_info.bandwidth);
}
switch (vlink->link_info.type_used) { switch (vlink->link_info.type_used) {
case tb_link_info::LINK_DIRECT: case tb_link_info::LINK_DIRECT:
SADD(SCORE_DIRECT_LINK); SADD(SCORE_DIRECT_LINK);
...@@ -1479,9 +1551,13 @@ bool find_best_link(pvertex pv,pvertex switch_pv,tb_vlink *vlink, ...@@ -1479,9 +1551,13 @@ bool find_best_link(pvertex pv,pvertex switch_pv,tb_vlink *vlink,
} }
} else { } else {
// For non-emulated links, we're just looking for links with few (0, // For non-emulated links, we're just looking for links with few (0,
// actually) users, and enough bandwidth // actually) users, and enough bandwidth (if we're adjusting the bw
// on the vlink to match what's on the interfaces selected, we don't
// even need to check bandwidth)
if ((users < best_users) && if ((users < best_users) &&
(plink->delay_info.bandwidth >= vlink->delay_info.bandwidth)) { (vlink->delay_info.adjust_to_native_bandwidth ||
(plink->delay_info.bandwidth >= vlink->delay_info.bandwidth)
)) {
best_pedge = *pedge_it; best_pedge = *pedge_it;
best_distance = distance; best_distance = distance;
found_best = true; found_best = true;
......
...@@ -60,11 +60,7 @@ sub runTest ...@@ -60,11 +60,7 @@ sub runTest
my $num = $_[2]; my $num = $_[2];
startProgram($dest, "sh /local/bw-bottleneck-wavelet/multiplex-server.sh $serverPort /local/bw-bottleneck-wavelet", 0); startProgram($dest, "sh /local/bw-bottleneck-wavelet/multiplex-server.sh $serverPort /local/bw-bottleneck-wavelet", 0);
sleep(2); sleep(2);
<<<<<<< scan-multiplex.pl
startProgram($source, "sh /bw-bottleneck/multiplex-client.sh $serverPort /bw-bottleneck end-$dest $duration $source-to-$dest-run-$run-$num", 0);
=======
startProgram($source, "sh /local/bw-bottleneck-wavelet/multiplex-client.sh $serverPort /local/bw-bottleneck-wavelet node-$dest.$exp.$proj.emulab.net $duration $source-to-$dest-run-$run-$num", 0); startProgram($source, "sh /local/bw-bottleneck-wavelet/multiplex-client.sh $serverPort /local/bw-bottleneck-wavelet node-$dest.$exp.$proj.emulab.net $duration $source-to-$dest-run-$run-$num", 0);
>>>>>>> 1.2
sleep($duration * 11); sleep($duration * 11);
stopProgram($dest); stopProgram($dest);
...@@ -77,22 +73,11 @@ $count = 0; ...@@ -77,22 +73,11 @@ $count = 0;
srand($run); srand($run);
<<<<<<< scan-multiplex.pl
#while (1)
#{
# $i = int(rand($nodeCount));
# $j = int(rand($nodeCount));
for ($i = 1; $i <= $nodeCount; ++$i)
=======
my %PairHash = (); my %PairHash = ();
my $iterLimit = $nodeCount*($nodeCount-1); my $iterLimit = $nodeCount*($nodeCount-1);
while (1) while (1)
>>>>>>> 1.2
{ {
<<<<<<< scan-multiplex.pl
for ($j = 1; $j <= $nodeCount; ++$j)
=======
$i = int(rand($nodeCount)) + 1; $i = int(rand($nodeCount)) + 1;
$j = int(rand($nodeCount)) + 1; $j = int(rand($nodeCount)) + 1;
...@@ -102,16 +87,7 @@ while (1) ...@@ -102,16 +87,7 @@ while (1)
} }
if ($i != $j) if ($i != $j)
>>>>>>> 1.2
{ {
<<<<<<< scan-multiplex.pl
if ($i != $j)
{
runTest($i, $j, $count);
++$count;
# runTest($nodes[$i], $nodes[$j]);
}
=======
my $nodeString = $i . ":" . $j; my $nodeString = $i . ":" . $j;
if( not ( $PairHash{$nodeString} ) ) if( not ( $PairHash{$nodeString} ) )
{ {
...@@ -125,7 +101,6 @@ while (1) ...@@ -125,7 +101,6 @@ while (1)
{ {
next; next;
} }
>>>>>>> 1.2
} }
} }
# sleep(600); # sleep(600);
......
...@@ -17,7 +17,7 @@ LIB_SCRIPTS = GeniDB.pm GeniUser.pm \ ...@@ -17,7 +17,7 @@ LIB_SCRIPTS = GeniDB.pm GeniUser.pm \
GeniComponent.pm GeniCH.pm GeniEmulab.pm \ GeniComponent.pm GeniCH.pm GeniEmulab.pm \
GeniAuthority.pm GeniCertificate.pm GeniAggregate.pm \ GeniAuthority.pm GeniCertificate.pm GeniAggregate.pm \
GeniUtil.pm GeniRegistry.pm GeniUsage.pm GeniHRN.pm \ GeniUtil.pm GeniRegistry.pm GeniUsage.pm GeniHRN.pm \
GeniSES.pm GeniResource.pm GeniSES.pm GeniResource.pm GeniXML.pm
SBIN_SCRIPTS = plabnodewrapper plabslicewrapper SBIN_SCRIPTS = plabnodewrapper plabslicewrapper
SCRIPTS = genischemacheck.pl SCRIPTS = genischemacheck.pl
......
...@@ -27,6 +27,7 @@ use GeniUtil; ...@@ -27,6 +27,7 @@ use GeniUtil;
use GeniUser; use GeniUser;
use GeniComponent; use GeniComponent;
use GeniHRN; use GeniHRN;
use GeniXML;
use emutil; use emutil;
use Lan; use Lan;
use Data::Dumper; use Data::Dumper;
...@@ -647,29 +648,22 @@ sub GetManifest($$) ...@@ -647,29 +648,22 @@ sub GetManifest($$)
} }
my ($xml) = $query_result->fetchrow_array(); my ($xml) = $query_result->fetchrow_array();
my $manifest = my $manifest = GeniXML::Parse($xml);
eval { XMLin($xml, KeyAttr => [], if (!defined($manifest)) {
ForceArray => ["node", "link", "interface",
"interface_ref", "linkendpoints"]) };
if ($@) {
print STDERR "XMLin error reading manifest: $@\n";
return undef; return undef;
} }
# #
# Update the manifest ticket to reflect the current expiration time. # Update the manifest ticket to reflect the current expiration time.
# #
$manifest->{'valid_until'} = my $valid_date = POSIX::strftime("20%y-%m-%dT%H:%M:%S",
POSIX::strftime("20%y-%m-%dT%H:%M:%S", gmtime(str2time($slice->expires())));
gmtime(str2time($slice->expires()))); GeniXML::SetText("valid_until", $manifest, $valid_date);
return $manifest return $manifest
if (!$asxml); if (!$asxml);
$xml = eval { XMLout($manifest, "NoAttr" => 1, RootName => "manifest") }; $xml = $manifest->toString();
if ($@) {
print STDERR "XMLout error on manifest: $@\n";
return undef;
}
return $xml; return $xml;
} }
...@@ -1236,10 +1230,11 @@ sub Create($$$$$$) ...@@ -1236,10 +1230,11 @@ sub Create($$$$$$)
$linkrspec, $node1rspec, $node2rspec) = @_; $linkrspec, $node1rspec, $node2rspec) = @_;
my $clearinghouse; my $clearinghouse;
my $linkname = $linkrspec->{"nickname"} || $linkrspec->{"virtual_id"}; my $linkname = GeniXML::GetVirtualId($linkrspec);
return undef return undef
if (!defined($linkname)); if (!defined($linkname));
my @interfaces = @{ $linkrspec->{'interface_ref'} }; my @interfaces = GeniXML::FindNodes("n:interface_ref",
$linkrspec)->get_nodelist();
my $experiment = Experiment->Lookup($slice->uuid()); my $experiment = Experiment->Lookup($slice->uuid());
if (!defined($experiment)) { if (!defined($experiment)) {
...@@ -1270,8 +1265,8 @@ sub Create($$$$$$) ...@@ -1270,8 +1265,8 @@ sub Create($$$$$$)
my $iface2ref = $interfaces[1]; my $iface2ref = $interfaces[1];
# These are the ips of the tunnel. # These are the ips of the tunnel.
my $ip1 = $iface1ref->{'tunnel_ip'}; my $ip1 = GeniXML::GetText("tunnel_ip", $iface1ref);
my $ip2 = $iface2ref->{'tunnel_ip'}; my $ip2 = GeniXML::GetText("tunnel_ip", $iface2ref);
my $ctrlip1; my $ctrlip1;
my $ctrlip2; my $ctrlip2;
my $iface1; my $iface1;
...@@ -1296,7 +1291,7 @@ sub Create($$$$$$) ...@@ -1296,7 +1291,7 @@ sub Create($$$$$$)
} }
} }
else { else {
my $node_uuid = $node1rspec->{'component_uuid'}; my $node_uuid = GeniXML::GetNodeId($node1rspec);
my $component = GeniComponent->CreateFromRegistry($node_uuid); my $component = GeniComponent->CreateFromRegistry($node_uuid);
if (!defined($component)) { if (!defined($component)) {
...@@ -1331,7 +1326,7 @@ sub Create($$$$$$) ...@@ -1331,7 +1326,7 @@ sub Create($$$$$$)
} }
} }
else { else {
my $node_uuid = $node2rspec->{'component_uuid'}; my $node_uuid = GeniXML::GetNodeId($node2rspec);
my $component = GeniComponent->CreateFromRegistry($node_uuid); my $component = GeniComponent->CreateFromRegistry($node_uuid);
if (!defined($component)) { if (!defined($component)) {
......
This diff is collapsed.
...@@ -19,6 +19,7 @@ use vars qw(@ISA @EXPORT); ...@@ -19,6 +19,7 @@ use vars qw(@ISA @EXPORT);
use GeniDB; use GeniDB;
use GeniCertificate; use GeniCertificate;
use GeniUtil; use GeniUtil;