Commit d2858e5d authored by Leigh Stoller's avatar Leigh Stoller

Convert osids and imageids from $pid-$name to globally unique identifiers

(also knows an integers).
parent 3e3f7025
......@@ -2348,7 +2348,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
capture/GNUmakefile \
db/GNUmakefile \
db/Experiment.pm db/Group.pm db/Interface.pm db/Node.pm \
db/NodeType.pm db/Project.pm db/User.pm \
db/NodeType.pm db/Project.pm db/User.pm db/Image.pm db/OSinfo.pm \
db/nalloc db/nfree db/if2port db/backup \
db/webcontrol db/node_status db/genelists db/genelists.proxy \
db/setsitevar db/newwanode db/audit db/changeuid db/changepid \
......
......@@ -730,7 +730,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
capture/GNUmakefile \
db/GNUmakefile \
db/Experiment.pm db/Group.pm db/Interface.pm db/Node.pm \
db/NodeType.pm db/Project.pm db/User.pm \
db/NodeType.pm db/Project.pm db/User.pm db/Image.pm db/OSinfo.pm \
db/nalloc db/nfree db/if2port db/backup \
db/webcontrol db/node_status db/genelists db/genelists.proxy \
db/setsitevar db/newwanode db/audit db/changeuid db/changepid \
......
......@@ -23,7 +23,8 @@ WEB_SBIN_SCRIPTS= webnodelog webnewwanode webidlemail webchangeuid
WEB_BIN_SCRIPTS = webnfree
LIBEXEC_SCRIPTS = $(WEB_BIN_SCRIPTS) $(WEB_SBIN_SCRIPTS) xmlconvert
LIB_SCRIPTS = libdb.pm Node.pm libdb.py libadminctrl.pm Experiment.pm \
NodeType.pm Interface.pm User.pm Group.pm Project.pm
NodeType.pm Interface.pm User.pm Group.pm Project.pm \
Image.pm OSinfo.pm
# Stuff installed on plastic.
USERSBINS = genelists.proxy dumperrorlog.proxy
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2007 University of Utah and the Flux Group.
# All rights reserved.
#
package Image;
use strict;
use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = "Exporter";
@EXPORT = qw ( );
# Must come after package declaration!
use lib '@prefix@/lib';
use libdb;
use libtestbed;
use User;
use English;
use Data::Dumper;
use overload ('""' => 'Stringify');
# Configure variables
my $TB = "@prefix@";
my $BOSSNODE = "@BOSSNODE@";
my $CONTROL = "@USERNODE@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBAPPROVAL = "@TBAPPROVALEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
my $TBBASE = "@TBBASE@";
my $TBWWW = "@TBWWW@";
# Cache of instances to avoid regenerating them.
my %images = ();
my $debug = 0;
# Little helper and debug function.
sub mysystem($)
{
my ($command) = @_;
print STDERR "Running '$command'\n"
if ($debug);
return system($command);
}
#
# Lookup by idx or pid,osname, depending on the args.
#
sub Lookup($$;$)
{
my ($class, $arg1, $arg2) = @_;
my $imageid;
#
# A single arg is either an index or a "pid,osname" or "pid/osname" string.
#
if (!defined($arg2)) {
if ($arg1 =~ /^(\d*)$/) {
$imageid = $1;
}
elsif ($arg1 =~ /^([-\w]*),([-\w\.\+]*)$/ ||
$arg1 =~ /^([-\w]*)\/([-\w\.\+]*)$/) {
$arg1 = $1;
$arg2 = $2;
}
else {
return undef;
}
}
elsif (! (($arg1 =~ /^[-\w\.\+]*$/) && ($arg2 =~ /^[-\w\.\+]*$/))) {
return undef;
}
#
# Two args means pid/imagename lookup instead of gid_idx.
#
if (defined($arg2)) {
my $images_result =
DBQueryWarn("select imageid from images ".
"where pid='$arg1' and imagename='$arg2'");
return undef
if (! $images_result || !$images_result->numrows);
($imageid) = $images_result->fetchrow_array();
}
# Look in cache first
return $images{"$imageid"}
if (exists($images{"$imageid"}));
my $query_result =
DBQueryWarn("select * from images where imageid='$imageid'");
return undef
if (!$query_result || !$query_result->numrows);
my $self = {};
$self->{'IMAGE'} = $query_result->fetchrow_hashref();
bless($self, $class);
# Add to cache.
$images{"$imageid"} = $self;
return $self;
}
# accessors
sub field($$) { return ((! ref($_[0])) ? -1 : $_[0]->{'IMAGE'}->{$_[1]}); }
sub imagename($) { return field($_[0], "imagename"); }
sub imageid($) { return field($_[0], "imageid"); }
sub old_imageid($) { return field($_[0], "old_imageid"); }
sub pid($) { return field($_[0], "pid"); }
sub gid($) { return field($_[0], "gid"); }
sub pid_idx($) { return field($_[0], "pid_idx"); }
sub gid_idx($) { return field($_[0], "gid_idx"); }
sub creator($) { return field($_[0], "creator"); }
sub creator_idx($) { return field($_[0], "creator_idx"); }
sub created($) { return field($_[0], "created"); }
sub description($) { return field($_[0], "description"); }
sub loadpart($) { return field($_[0], "loadpart"); }
sub loadlength($) { return field($_[0], "loadlength"); }
sub part1_osid($) { return field($_[0], "part1_osid"); }
sub part2_osid($) { return field($_[0], "part2_osid"); }
sub part3_osid($) { return field($_[0], "part3_osid"); }
sub part4_osid($) { return field($_[0], "part4_osid"); }
sub default_osid($) { return field($_[0], "default_osid"); }
sub path($) { return field($_[0], "path"); }
sub magic($) { return field($_[0], "magic"); }
sub load_address($) { return field($_[0], "load_address"); }
sub frisbee_pid($) { return field($_[0], "frisbee_pid"); }
sub load_busy($) { return field($_[0], "load_busy"); }
sub ezid($) { return field($_[0], "ezid"); }
sub shared($) { return field($_[0], "shared"); }
sub global($) { return field($_[0], "global"); }
sub updated($) { return field($_[0], "updated"); }
#
# Refresh a class instance by reloading from the DB.
#
sub Refresh($)
{
my ($self) = @_;
return -1
if (! ref($self));
my $imageid = $self->imageid();
my $query_result =
DBQueryWarn("select * from images where imageid=$imageid");
return -1
if (!$query_result || !$query_result->numrows);
$self->{'IMAGE'} = $query_result->fetchrow_hashref();
return 0;
}
#
# Stringify for output.
#
sub Stringify($)
{
my ($self) = @_;
my $pid = $self->pid();
my $imageid = $self->imageid();
my $imagename = $self->imagename();
return "[Image $imageid: $pid,$imagename]";
}
#
# Return the internal DB rowref. Used for code that has not been converted.
#
sub DBData($)
{
my ($self) = @_;
return undef
if (! ref($self));
return $self->{'IMAGE'}
}
#
# Perform some updates ...
#
sub Update($$)
{
my ($self, $argref) = @_;
# Must be a real reference.
return -1
if (! ref($self));
my $imageid = $self->imageid();
my $query = "update images set ".
join(",", map("$_='" . $argref->{$_} . "'", keys(%{$argref})));
$query .= " where imageid='$imageid'";
return -1
if (! DBQueryWarn($query));
return Refresh($self);
}
#
# Check permissions.
#
sub AccessCheck($$$)
{
my ($self, $user, $access_type) = @_;
# Must be a real reference.
return -1
if (! ref($self));
my $uid = (ref($user) ? $user->uid() : $user);
return TBImageIDAccessCheck($uid, $self->imageid(), $access_type);
}
# _Always_ make sure that this 1 is at the end of the file...
1;
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2007 University of Utah and the Flux Group.
# All rights reserved.
#
package OSinfo;
use strict;
use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = "Exporter";
@EXPORT = qw ( );
# Must come after package declaration!
use lib '@prefix@/lib';
use libdb;
use libtestbed;
use User;
use English;
use Data::Dumper;
use overload ('""' => 'Stringify');
# Configure variables
my $TB = "@prefix@";
my $BOSSNODE = "@BOSSNODE@";
my $CONTROL = "@USERNODE@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBAPPROVAL = "@TBAPPROVALEMAIL@";
my $TBAUDIT = "@TBAUDITEMAIL@";
my $TBBASE = "@TBBASE@";
my $TBWWW = "@TBWWW@";
# Cache of instances to avoid regenerating them.
my %osids = ();
my $debug = 0;
# Little helper and debug function.
sub mysystem($)
{
my ($command) = @_;
print STDERR "Running '$command'\n"
if ($debug);
return system($command);
}
#
# Lookup by idx or pid,osname, depending on the args.
#
sub Lookup($$;$)
{
my ($class, $arg1, $arg2) = @_;
my $osid;
#
# A single arg is either an index or a "pid,osname" or "pid/osname" string.
#
if (!defined($arg2)) {
if ($arg1 =~ /^(\d*)$/) {
$osid = $1;
}
elsif ($arg1 =~ /^([-\w]*),([-\w\.\+]*)$/ ||
$arg1 =~ /^([-\w]*)\/([-\w\.\+]*)$/) {
$arg1 = $1;
$arg2 = $2;
}
else {
return undef;
}
}
elsif (! (($arg1 =~ /^[-\w\.\+]*$/) && ($arg2 =~ /^[-\w\.\+]*$/))) {
return undef;
}
#
# Two args means pid/osname lookup instead of gid_idx.
#
if (defined($arg2)) {
my $osid_result =
DBQueryWarn("select osid from os_info ".
"where pid='$arg1' and osname='$arg2'");
return undef
if (! $osid_result || !$osid_result->numrows);
($osid) = $osid_result->fetchrow_array();
}
# Look in cache first
return $osids{"$osid"}
if (exists($osids{"$osid"}));
my $query_result =
DBQueryWarn("select * from os_info where osid='$osid'");
return undef
if (!$query_result || !$query_result->numrows);
my $self = {};
$self->{'OSINFO'} = $query_result->fetchrow_hashref();
bless($self, $class);
# Add to cache.
$osids{"$osid"} = $self;
return $self;
}
# accessors
sub field($$) { return ((! ref($_[0])) ? -1 : $_[0]->{'OSINFO'}->{$_[1]}); }
sub osname($) { return field($_[0], "osname"); }
sub osid($) { return field($_[0], "osid"); }
sub pid($) { return field($_[0], "pid"); }
sub gid($) { return field($_[0], "gid"); }
sub pid_idx($) { return field($_[0], "pid_idx"); }
sub gid_idx($) { return field($_[0], "gid_idx"); }
sub creator($) { return field($_[0], "creator"); }
sub creator_idx($) { return field($_[0], "creator_idx"); }
sub created($) { return field($_[0], "created"); }
sub description($) { return field($_[0], "description"); }
sub OS($) { return field($_[0], "OS"); }
sub version($) { return field($_[0], "version"); }
sub path($) { return field($_[0], "path"); }
sub magic($) { return field($_[0], "magic"); }
sub machinetype($) { return field($_[0], "machinetype"); }
sub osfeatures($) { return field($_[0], "osfeatures"); }
sub ezid($) { return field($_[0], "ezid"); }
sub shared($) { return field($_[0], "shared"); }
sub mustclean($) { return field($_[0], "mustclean"); }
sub op_mode($) { return field($_[0], "op_mode"); }
sub nextosid($) { return field($_[0], "nextosid"); }
sub max_concurrent($) { return field($_[0], "max_concurrent"); }
sub mfs($) { return field($_[0], "mfs"); }
sub reboot_waittime($) { return field($_[0], "reboot_waittime"); }
#
# Refresh a class instance by reloading from the DB.
#
sub Refresh($)
{
my ($self) = @_;
return -1
if (! ref($self));
my $osid = $self->osid();
my $query_result =
DBQueryWarn("select * from os_info where osid=$osid");
return -1
if (!$query_result || !$query_result->numrows);
$self->{'OSINFO'} = $query_result->fetchrow_hashref();
return 0;
}
#
# Stringify for output.
#
sub Stringify($)
{
my ($self) = @_;
my $pid = $self->pid();
my $osid = $self->osid();
my $osname = $self->osname();
return "[OS $osid: $pid,$osname]";
}
#
# Perform some updates ...
#
sub Update($$)
{
my ($self, $argref) = @_;
# Must be a real reference.
return -1
if (! ref($self));
my $osid = $self->osid();
my $query = "update os_info set ".
join(",", map("$_='" . $argref->{$_} . "'", keys(%{$argref})));
$query .= " where osid='$osid'";
return -1
if (! DBQueryWarn($query));
return Refresh($self);
}
#
# Check permissions.
#
sub AccessCheck($$$)
{
my ($self, $user, $access_type) = @_;
# Must be a real reference.
return -1
if (! ref($self));
my $uid = (ref($user) ? $user->uid() : $user);
return TBOSIDAccessCheck($uid, $self->osid(), $access_type);
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -2013,11 +2013,11 @@ sub TBBootWhat($;$)
# The priority would seem pretty clear.
#
return ($next_boot_osid, $next_boot_opmode)
if (defined($next_boot_osid) && $next_boot_osid ne "");
if (defined($next_boot_osid) && $next_boot_osid ne 0);
return ($temp_boot_osid, $temp_boot_opmode)
if (defined($temp_boot_osid) && $temp_boot_osid ne "");
if (defined($temp_boot_osid) && $temp_boot_osid ne 0);
return ($def_boot_osid, $def_boot_opmode)
if (defined($def_boot_osid) && $def_boot_osid ne "");
if (defined($def_boot_osid) && $def_boot_osid ne 0);
print("*** Warning: node '$node': All boot info was null!\n");
return undef;
......@@ -2341,8 +2341,11 @@ sub TBResolveNextOSID($;$$)
# images, at least short of creating a new experiment with the same
# ns file.
#
if ($next_osid && $next_osid =~ /^MAP:(\w+)/) {
my $map = $1;
# next_osid used to be MAP:osid_map, but now its an integer field
# so just look for a 0 index, which is not a "valid" osid.
#
if (defined($next_osid) && $next_osid == 0) {
my $map = "osid_map";
my $timestr;
if (defined($pid) && defined($eid)) {
......
......@@ -378,7 +378,7 @@ foreach my $n (@freed_nodes) {
DBQueryWarn("update nodes set startupcmd='',rpms='',deltas='', ".
"tarballs='',failureaction='fatal', routertype='none', ".
"def_boot_cmd_line='',next_boot_cmd_line='', ".
"temp_boot_osid='',next_boot_osid='', ".
"temp_boot_osid=NULL,next_boot_osid=NULL, ".
"update_accounts=0,ipport_next=ipport_low, ".
"sfshostid=NULL,allocstate='$allocFreeState',boot_errno=0 ".
"where node_id='$n'") || $error++;
......
......@@ -208,7 +208,7 @@ CREATE TABLE `comments` (
DROP TABLE IF EXISTS `current_reloads`;
CREATE TABLE `current_reloads` (
`node_id` varchar(32) NOT NULL default '',
`image_id` varchar(45) NOT NULL default '',
`image_id` int(8) unsigned NOT NULL default '0',
`mustwipe` tinyint(4) NOT NULL default '0',
PRIMARY KEY (`node_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
......@@ -1196,7 +1196,7 @@ CREATE TABLE `iface_counters` (
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Table structure for table `images`
-- Table structure for table `images `
--
DROP TABLE IF EXISTS `images`;
......@@ -1206,18 +1206,19 @@ CREATE TABLE `images` (
`gid_idx` mediumint(8) unsigned NOT NULL default '0',
`pid` varchar(12) NOT NULL default '',
`gid` varchar(12) NOT NULL default '',
`imageid` varchar(45) NOT NULL default '',
`imageid` int(8) unsigned NOT NULL default '0',
`old_imageid` varchar(45) NOT NULL default '',
`creator` varchar(8) default NULL,
`creator_idx` mediumint(8) unsigned NOT NULL default '0',
`created` datetime default NULL,
`description` tinytext NOT NULL,
`loadpart` tinyint(4) NOT NULL default '0',
`loadlength` tinyint(4) NOT NULL default '0',
`part1_osid` varchar(35) default NULL,
`part2_osid` varchar(35) default NULL,
`part3_osid` varchar(35) default NULL,
`part4_osid` varchar(35) default NULL,
`default_osid` varchar(35) NOT NULL default '',
`part1_osid` int(8) unsigned default NULL,
`part2_osid` int(8) unsigned default NULL,
`part3_osid` int(8) unsigned default NULL,
`part4_osid` int(8) unsigned default NULL,
`default_osid` int(8) unsigned NOT NULL default '0',
`path` tinytext,
`magic` tinytext,
`load_address` text,
......@@ -1229,7 +1230,8 @@ CREATE TABLE `images` (
`updated` datetime default NULL,
PRIMARY KEY (`imageid`),
UNIQUE KEY `pid` (`pid`,`imagename`),
KEY `gid` (`gid`)
KEY `gid` (`gid`),
KEY `old_imageid` (`old_imageid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
......@@ -1889,11 +1891,11 @@ CREATE TABLE `nodes` (
`type` varchar(30) NOT NULL default '',
`phys_nodeid` varchar(32) default NULL,
`role` enum('testnode','virtnode','ctrlnode','testswitch','ctrlswitch','powerctrl','unused') NOT NULL default 'unused',
`def_boot_osid` varchar(35) NOT NULL default '',
`def_boot_osid` int(8) unsigned default NULL,
`def_boot_path` text,
`def_boot_cmd_line` text,
`temp_boot_osid` varchar(35) NOT NULL default '',
`next_boot_osid` varchar(35) NOT NULL default '',
`temp_boot_osid` int(8) unsigned default NULL,
`next_boot_osid` int(8) unsigned default NULL,
`next_boot_path` text,
`next_boot_cmd_line` text,
`pxe_boot_path` text,
......@@ -1918,7 +1920,7 @@ CREATE TABLE `nodes` (
`update_accounts` smallint(6) default '0',
`next_op_mode` varchar(20) NOT NULL default '',
`ipodhash` varchar(64) default NULL,
`osid` varchar(35) NOT NULL default '',
`osid` int(8) unsigned default NULL,
`ntpdrift` float default NULL,
`ipport_low` int(11) NOT NULL default '11000',
`ipport_next` int(11) NOT NULL default '11000',
......@@ -2067,7 +2069,8 @@ CREATE TABLE `os_info` (
`osname` varchar(20) NOT NULL default '',
`pid` varchar(12) NOT NULL default '',
`pid_idx` mediumint(8) unsigned NOT NULL default '0',
`osid` varchar(35) NOT NULL default '',
`osid` int(8) unsigned NOT NULL default '0',
`old_osid` varchar(35) NOT NULL default '',
`creator` varchar(8) default NULL,
`creator_idx` mediumint(8) unsigned NOT NULL default '0',
`created` datetime default NULL,
......@@ -2082,14 +2085,16 @@ CREATE TABLE `os_info` (
`shared` tinyint(4) NOT NULL default '0',
`mustclean` tinyint(4) NOT NULL default '1',
`op_mode` varchar(20) NOT NULL default 'MINIMAL',
`nextosid` varchar(35) default NULL,
`nextosid` int(8) unsigned default NULL,
`old_nextosid` varchar(35) NOT NULL default '',
`max_concurrent` int(11) default NULL,
`mfs` tinyint(4) NOT NULL default '0',
`reboot_waittime` int(10) unsigned default NULL,
PRIMARY KEY (`osid`),
UNIQUE KEY `pid` (`pid`,`osname`),
KEY `OS` (`OS`),
KEY `path` (`path`(255))
KEY `path` (`path`(255)),
KEY `old_osid` (`old_osid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
......@@ -2098,10 +2103,10 @@ CREATE TABLE `os_info` (
DROP TABLE IF EXISTS `osid_map`;
CREATE TABLE `osid_map` (
`osid` varchar(35) NOT NULL default '',
`osid` int(8) unsigned NOT NULL default '0',
`btime` datetime NOT NULL default '1000-01-01 00:00:00',
`etime` datetime NOT NULL default '9999-12-31 23:59:59',
`nextosid` varchar(35) default NULL,
`imageid` int(8) unsigned NOT NULL default '0',
PRIMARY KEY (`osid`,`btime`,`etime`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
......@@ -2111,9 +2116,9 @@ CREATE TABLE `osid_map` (
DROP TABLE IF EXISTS `osidtoimageid`;
CREATE TABLE `osidtoimageid` (
`osid` varchar(35) NOT NULL default '',
`osid` int(8) unsigned NOT NULL default '0',
`type` varchar(30) NOT NULL default '',
`imageid` varchar(45) NOT NULL default '',
`imageid` int(8) unsigned NOT NULL default '0',
PRIMARY KEY (`osid`,`type`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
......@@ -2152,8 +2157,8 @@ DROP TABLE IF EXISTS `partitions`;
CREATE TABLE `partitions` (
`node_id` varchar(32) NOT NULL default '',
`partition` tinyint(4) NOT NULL default '0',
`osid` varchar(35) default NULL,
`imageid` varchar(45) default NULL,
`osid` int(8) unsigned default NULL,
`imageid` int(8) unsigned default NULL,
`imagepid` varchar(12) NOT NULL default '',
PRIMARY KEY (`node_id`,`partition`),
KEY `osid` (`osid`)
......@@ -2476,7 +2481,7 @@ CREATE TABLE `reserved` (
DROP TABLE IF EXISTS `scheduled_reloads`;
CREATE TABLE `scheduled_reloads` (
`node_id` varchar(32) NOT NULL default '',
`image_id` varchar(45) NOT NULL default '',
`image_id` int(8) unsigned NOT NULL default '0',
`reload_type` enum('netdisk','frisbee') default NULL,
PRIMARY KEY (`node_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
......
......@@ -17,3 +17,4 @@ INSERT IGNORE INTO os_boot_cmd VALUES ('FreeBSD','6.0','delay','/boot/kernel.del
INSERT IGNORE INTO os_boot_cmd VALUES ('FreeBSD','6.0','linkdelay','/boot/kernel.linkdelay/kernel');
INSERT IGNORE INTO emulab_indicies (name,idx) VALUES ('cur_log_seq', 1);
INSERT IGNORE INTO emulab_indicies (name,idx) VALUES ('next_osid', 100);
......@@ -3930,5 +3930,8 @@ last_net_act,last_cpu_act,last_ext_act);
4.111: Minor fix to schema file; skip to next entry.
4.112: OSIDs and IMAGEIDs are now globally unique.
All of the sql changes are made in the following script:
./step4_newids.pl
This diff is collapsed.
......@@ -864,7 +864,7 @@ sub cleanup()
$experiment->SetState(EXPTSTATE_SWAPPED);
}
if ($experiment->End("-force") != 0) {
if ($experiment->End("-f") != 0) {
print "tbend failed!\n";
}
}
......
......@@ -1006,7 +1006,8 @@ sub TearDownEmulab()
# why that is.
#
DBQueryFatal("update nodes set ".
" def_boot_osid='',next_boot_osid='',temp_boot_osid='' ".
" def_boot_osid=NULL,next_boot_osid=NULL,".
" temp_boot_osid=NULL ".
"where " .
join(" or ", map("node_id='$_'",
($bossnode, $opsnode,
......@@ -1216,7 +1217,8 @@ sub RemoveNodes()
# why that is.
#
DBQueryFatal("update nodes set ".
" def_boot_osid='',next_boot_osid='',temp_boot_osid='' ".
" def_boot_osid=NULL,next_boot_osid=NULL,".
" temp_boot_osid=NULL ".
"where " .
join(" or ", map("node_id='$_'", @nodes)));
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002, 2004, 2006 University of Utah and the Flux Group.
# Copyright (c) 2000-2002, 2004, 2006, 2007 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
......@@ -54,6 +54,7 @@ use libdb;