Commit 41c54939 authored by Kirk Webb's avatar Kirk Webb

The revived Plab interface is here!

Lots of updates to the plab backend, including improved plab <-> elab node
id translation and update handling.  Includes support for the current PLC
API, and the new pl_conf node manager interface API.  Several more db library
routines were ported from the perl library to the python one to support the
new code (mostly the node_id tracking stuff).  Fixes to the client side and
also a rootball creation cleanup (binaries removed from the CVS repo).

There are also enhancements to the experiment view page for experiments
including plab nodes: site and widearea hostname are now displayed along
with the other node information.

Note that the way setup timeout for vnodes is calculated has been changed a
bit.  Instead of using a hardwired base timeout, the base timeout is now
based on the reload_waittime database field, which comes from the 'OS'
(e.g., FBSD-JAIL, RHL-PLAB) the vnode runs.

The default max duration for a plab slice created through the plab_ez interface
is set to 1 year, and linktest is currently disabled and hidden through
the ez interface.

There is still work to do, but this checkin brings with it a functional
plab portal!
parent 2d003fcc
......@@ -2285,6 +2285,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
tbsetup/fetchtar.proxy tbsetup/webfrisbeekiller \
tbsetup/plab/GNUmakefile tbsetup/plab/libplab.py \
tbsetup/plab/mod_dslice.py tbsetup/plab/mod_PLC.py \
tbsetup/plab/mod_PLCNM.py \
tbsetup/plab/plabslice tbsetup/plab/plabnode tbsetup/plab/plabrenewd \
tbsetup/plab/plabmetrics tbsetup/plab/plabstats \
tbsetup/plab/plabmonitord tbsetup/plab/plablinkdata \
......
......@@ -723,6 +723,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
tbsetup/fetchtar.proxy tbsetup/webfrisbeekiller \
tbsetup/plab/GNUmakefile tbsetup/plab/libplab.py \
tbsetup/plab/mod_dslice.py tbsetup/plab/mod_PLC.py \
tbsetup/plab/mod_PLCNM.py \
tbsetup/plab/plabslice tbsetup/plab/plabnode tbsetup/plab/plabrenewd \
tbsetup/plab/plabmetrics tbsetup/plab/plabstats \
tbsetup/plab/plabmonitord tbsetup/plab/plablinkdata \
......
......@@ -25,8 +25,24 @@ from libtestbed import *
#
# Debug vars.
#
verbose = 0;
debug = 0;
verbose = 0
debug = 0
# Constants
TBOPSPID = "emulab-ops"
NODEDEAD_PID = TBOPSPID
NODEDEAD_EID = "hwdown"
TB_NODEHISTORY_OP_MOVE = "move"
# Node Log Types
TB_NODELOGTYPE_MISC = "misc"
TB_NODELOGTYPES = (TB_NODELOGTYPE_MISC, )
TB_DEFAULT_NODELOGTYPE = TB_NODELOGTYPE_MISC
# Node History Stuff.
TB_NODEHISTORY_OP_FREE = "free"
TB_NODEHISTORY_OP_ALLOC = "alloc"
TB_NODEHISTORY_OP_MOVE = "move"
#
# DB variables.
......@@ -90,7 +106,7 @@ def DBQuery(queryPat, querySub = (), asDict = False):
if ret == None:
return ()
return ret
except MySQLdb.MySQLError:
except MySQLdb.MySQLError, e:
tries -= 1
if tries == 0:
break
......@@ -100,6 +116,7 @@ def DBQuery(queryPat, querySub = (), asDict = False):
__dbConnection.ping()
except MySQLdb.MySQLError:
pass
tbmsg = queryPat % cursor.connection.literal(querySub)
tbmsg += "\n\n"
tbmsg += "".join(traceback.format_exception(*sys.exc_info()))
......@@ -112,6 +129,157 @@ def DBQueryFatal(*args):
raise RuntimeError, "DBQueryFatal failed"
return ret
def DBQueryWarn(*args):
return DBQuery(*args)
def DBQuoteSpecial(str):
TBDBConnect()
return __dbConnection.escape_string(str)
#
# Map UID to DB UID (login). Does a DB check to make sure user is known to
# the DB (user obviously has a regular account), and that account will
# always match what the DB says. Redundant, I know. But consider it a
# sanity (or consistency) check.
#
# usage: UNIX2DBUID(int uid)
# returns username if the UID is okay.
# raises a UserError exception if the UID is bogus.
#
class UserError(StandardError): pass # XXX: need better suite of exceptions
def UNIX2DBUID (unix_uid):
qres = \
DBQueryFatal("select uid from users where unix_uid=%s",
(unix_uid))
if not len(qres):
raise UserError, "*** %s not a valid Emulab user!" % uid
pwname = pwd.getpwuid(unix_uid)[0]
dbuser = qres[0][0]
if dbuser != pwname:
raise UserError, "*** %s (passwd file) does not match %s (db)" % \
(pwname, dbuser)
return dbuser
#
# Helper. Test if numeric. Convert to dbuid if numeric.
#
def MapNumericUID(uid):
name = ""
try:
uid = int(uid)
name = UNIX2DBUID(uid)
except ValueError:
name = uid
pass
return name
#
# Return the IDX for a current experiment.
#
# usage: TBExptIDX(char $pid, char *gid, int \$idx)
# returns 1 if okay.
# returns 0 if error.
#
class UnknownExptID(StandardError): pass # XXX: need better suite of exceptions
def TBExptIDX(pid,eid):
qres = \
DBQueryWarn("select idx from experiments "
"where pid=%s and eid=%s",
(pid, eid))
if not len(qres):
raise UnknownExptID, "Experiment %s/%s unknown!" % (pid,eid)
idx = qres[0][0]
return int(idx)
#
# Insert a Log entry for a node.
#
# usage: TBSetNodeLogEntry(char *node, char *uid, char *type, char *message)
# Returns 1 if okay.
# Returns 0 if failed.
#
def TBSetNodeLogEntry(node, dbuid, type, message):
if not TBValidNodeName(node) or not TBValidNodeLogType(type):
return 0
return DBQueryWarn("insert into nodelog "
"values "
"(%s, NULL, %s, %s, %s, now())",
(node, type, dbuid, message))
#
# Validate a node name.
#
# usage: TBValidNodeName(char *name)
# Returns 1 if the node is valid.
# Returns 0 if not.
#
def TBValidNodeName(node):
qres = \
DBQueryWarn("select node_id from nodes where node_id=%s",
(node))
if len(qres) == 0:
return 0
return 1
#
# Validate a node log type.
#
# usage: TBValidNodeLogType(char *type)
# Returns 1 if the type string is valid.
# Returns 0 if not.
#
def TBValidNodeLogType(type):
if type in TB_NODELOGTYPES:
return 1
return 0
#
# Mark a Phys node as down. Cannot use next reserve since the pnode is not
# going to go through the free path.
#
# usage: MarkPhysNodeDown(char *nodeid)
#
def MarkPhysNodeDown(pnode):
pid = NODEDEAD_PID;
eid = NODEDEAD_EID;
DBQueryFatal("lock tables reserved write")
DBQueryFatal("update reserved set "
" pid=%s,eid=%s,rsrv_time=now() "
"where node_id=%s",
(pid, eid, pnode))
DBQueryFatal("unlock tables")
TBSetNodeHistory(pnode, TB_NODEHISTORY_OP_MOVE, os.getuid(), pid, eid)
return
def TBSetNodeHistory(nodeid, op, uid, pid, eid):
exptidx = 0
try:
exptidx = TBExptIDX(pid, eid)
except:
print "*** WARNING: No such experiment %s/%s!" % (pid,eid)
return 0
try:
uid = int(uid)
# val = <expr> ? TrueRet : FalseRet
uid = uid == 0 and "root" or UNIX2DBUID(uid)
pass
except ValueError:
pass
return DBQueryWarn("insert into node_history set "
" history_id=0, node_id=%s, op=%s, "
" uid=%s, stamp=UNIX_TIMESTAMP(now()), "
" exptidx=%s",
(nodeid,op,uid,exptidx))
def TBSiteVarExists(name):
name = DBQuoteSpecial(name)
......@@ -142,9 +310,3 @@ def TBGetSiteVar(name):
raise RuntimeException, \
"*** attempted to fetch unknown site variable name!"
def DBQuoteSpecial(str):
TBDBConnect()
return __dbConnection.escape_string(str)
......@@ -929,10 +929,18 @@ elsif (@vnodelist) {
my $pnode = $vnode2pnode{$node};
my $islocal= exists($nodes{$pnode});
my $wstart = $waitstart{$node};
my $maxwait = 90 + (40 * $pnodevcount{$pnode});
my $curallocstate;
my $actual_state;
#
# Base the maxwait for vnodes on the reboot_waittime field for
# their respective OSIDs, with some slop time that scales up
# as a function of the number of vnodes on the parent pnode.
#
my $osid = $osids{$node};
my $reboot_time = $reboot_waittime{$osid};
my $maxwait = $reboot_time + (40 * $pnodevcount{$pnode});
TBGetNodeAllocState($node, \$curallocstate);
#
......
......@@ -17,7 +17,7 @@ SUBDIRS = libdslice etc
SBIN_STUFF = plabslice plabnode plabrenewd plabmetrics plabstats \
plabmonitord plablinkdata plabdist plabhttpd plabdiscover
LIB_STUFF = libplab.py mod_dslice.py mod_PLC.py
LIB_STUFF = libplab.py mod_dslice.py mod_PLC.py mod_PLCNM.py
LIBEXEC_STUFF = webplabstats
......
This diff is collapsed.
This diff is collapsed.
......@@ -97,7 +97,7 @@ my $logfile = "$TB/log/plabmonitord";
my @oldnodes = ();
my $LOOPSLEEP = 1800; # 1/2 hour between successive loops.
my $PAUSETIME = 120; # 2 minute pause after running vnode_setup
my $SETUPWAIT = 960; # how long to wait for vnode to setup.
my $SETUPWAIT = 300; # how long to wait for vnode to setup.
my $BATCHNUM = 40; # degree of parallelization
#
......
......@@ -6,7 +6,7 @@
# All rights reserved.
#
import sys
import sys, os
sys.path.append("@prefix@/lib")
import getopt
from libtestbed import *
......
......@@ -326,6 +326,10 @@ my ($pid, $eid, $vname);
TBDebugTimeStamp("vnodesetup calling into libsetup");
if ($doplab) {
# Need to be real root for vnodeplabsetup to work
# since it runs subcommands via system() that need to be
# root.
$UID = 0;
($pid, $eid, $vname) = vnodeplabsetup($vnodeid);
}
else {
......
......@@ -23,11 +23,10 @@ ALLSCRIPTS = $(ULEESCRIPTS) $(OSSCRIPTS) $(EESCRIPTS)
THISDIR = $(PWD)
#
# Linux binaries the tarball needs
# Root of tree containing stock package stuff that is needed
# in the plab vserver environment to support Emulab (e.g. sshd)
#
BINARIES = $(SRCDIR)/tmcc $(SRCDIR)/suidperl $(SRCDIR)/tcsh \
$(SRCDIR)/gzip
ALLBINARIES = $(BINARIES)
PKGSRCTREE = /share/plab/pkgroot
#
# Configuration, or other files necessary for tarball
......@@ -58,7 +57,7 @@ $(ROOTBALLNAME): copyfiles
chown -R 0:0 plabroot
tar cf - --numeric-owner -C plabroot . | bzip2 -c -9 > $(ROOTBALLNAME)
copyfiles: $(ALLSCRIPTS) $(ALLBINARIES) $(ALLCONFS) $(ALLCERTS) mkdirtree
copyfiles: $(ALLSCRIPTS) $(ALLCONFS) $(ALLCERTS) $(PKGSRCTREE) mkdirtree
(cd ../common/config; $(MAKE) script-install DESTDIR=$(THISDIR)/plabroot)
$(INSTALL_PROGRAM) $(ULEESCRIPTS) plabroot/usr/local/etc/emulab
chmod u+s plabroot/usr/local/etc/emulab/vnodesetup
......@@ -69,17 +68,11 @@ copyfiles: $(ALLSCRIPTS) $(ALLBINARIES) $(ALLCONFS) $(ALLCERTS) mkdirtree
$(INSTALL_PROGRAM) $(EESCRIPTS) plabroot/etc
$(INSTALL) -m 440 $(TESTBED_SRCDIR)/tmcd/plab/sudoers \
plabroot/etc/sudoers
ln -fs suidperl plabroot/usr/bin/sperl5.8.0
$(INSTALL_PROGRAM) -s $(SRCDIR)/tmcc \
plabroot/usr/local/etc/emulab/tmcc.bin
$(INSTALL_PROGRAM) -s -m 4511 $(SRCDIR)/suidperl plabroot/usr/bin
$(INSTALL_PROGRAM) -s $(SRCDIR)/tcsh plabroot/bin
$(INSTALL_PROGRAM) -s $(SRCDIR)/gzip plabroot/bin
ln -fs gzip plabroot/bin/gunzip
$(INSTALL_DATA) /usr/testbed/etc/pcplab.pem \
plabroot/usr/local/etc/emulab/client.pem
$(INSTALL_DATA) /usr/testbed/etc/emulab.pem \
plabroot/usr/local/etc/emulab
cp -pR $(PKGSRCTREE)/ plabroot
echo "@BOSSNODE@" > plabroot/usr/local/etc/emulab/bossnode
cp /dev/null plabroot/usr/local/etc/emulab/isrem
......
......@@ -132,6 +132,21 @@ sub doboot()
if (tmcc(TMCCCMD_STATE, "ISUP") < 0) {
fatal("Error sneding ISUP to Emulab Control!");
}
#
# After everything is setup, run any startup command.
#
# Note that this mechanism is legacy, and will be replaced
# by the program agent once the event system becomes available
# on plab vnodes.
#
if (-x "$RCDIR/rc.startcmd") {
TBDebugTimeStamp("running $RCDIR/rc.startcmd");
system("$RCDIR/rc.startcmd boot");
if ($?) {
fatal("Error running $RCDIR/rc.startcmd");
}
}
}
#
......@@ -215,6 +230,8 @@ sub doplabconfig()
print RC "/sbin/syslogd\n";
# Start sshd
print RC "/usr/sbin/useradd -u 22 -g 22 -d /var/empty ".
"-c \"sshd separation account\" -s /nonexistent sshd\n";
print RC "/etc/init.d/sshd restart\n";
}
else {
......
......@@ -508,6 +508,12 @@ function SPITFORM($formfields, $errors)
# Run linktest, and level.
#
if (STUDLY() || $EXPOSELINKTEST) {
if (isset($view['hide_linktest'])) {
if ($formfields[exp_linktest]) {
echo "<input type='hidden' name='formfields[exp_linktest]'
value='$formfields[exp_linktest]'\n";
}
} else {
echo "<tr>
<td><a href='$TBDOCBASE/doc/docwrapper.php3?".
"docname=linktest.html'>Linktest</a> Option:</td>
......@@ -529,6 +535,7 @@ function SPITFORM($formfields, $errors)
echo " </td>
</tr>\n";
}
}
#
# Batch Experiment?
......
......@@ -34,8 +34,8 @@ LOGGEDINORDIE($uid);
unset($view);
if (isset($view_style) && $view_style == "plab") {
$view['hide_proj'] = $view['hide_group'] = $view['hide_swap'] =
$view['hide_preload'] = $view['hide_batch'] = $view['quiet'] =
$view['plab_ns_message'] = 1;
$view['hide_preload'] = $view['hide_batch'] = $view['hide_linktest'] =
$view['quiet'] = $view['plab_ns_message'] = 1;
}
include("beginexp_form.php3");
......
......@@ -46,6 +46,12 @@ function SPITFORM($advanced,$formfields, $errors = array()) {
global $TBBASE;
global $plab_types, $plab_type_descr;
#
# Default autoswap time - very long...
#
$aswapunits = 168; # 1 week (in hours)
$aswaptime = 52; # 52 weeks == 1 year
#
# Header/footer view options
#
......@@ -183,6 +189,8 @@ function SPITFORM($advanced,$formfields, $errors = array()) {
echo "<input type='hidden' name='formfields[canfail]' value='Yep'>\n";
echo "<input type='hidden' name='formfields[type]' value='pcplab'>\n";
echo "<input type='hidden' name='formfields[resusage]' value='3'>\n";
echo "<input type='hidden' name='formfields[units]' value='$aswapunits'>\n";
echo "<input type='hidden' name='formfields[when]' value='$aswaptime'>\n";
}
#
......@@ -477,6 +485,9 @@ function MAKENS($formfields) {
"&formfields[exp_noidleswap_reason]=" .
urlencode("PlanetLab experiment");
# XXX: until we want to use linktest on plab
$url .= "&formfields[exp_linktest]=0";
#
# Batched?
#
......@@ -487,11 +498,15 @@ function MAKENS($formfields) {
#
# Determine the auto-swap time
#
if ($formfields['when'] && ($formfields['when'] != 'never')) {
if ($formfields['when']) {
if (strcmp($formfields['when'], 'never') == 0) {
$url .= "&formfields[exp_autoswap]=0";
} else {
$swaptime = $formfields['when'] * $formfields['units'];
$url .= "&formfields[exp_autoswap]=1";
$url .= "&formfields[exp_autoswap_timeout]=$swaptime";
}
}
#
# Run nsgen - make up a random nsref for use with it
......@@ -545,7 +560,7 @@ function CHECKFORM($formfields) {
if (!preg_match("/^\d+$/",$formfields['count'],$matches)) {
$errors['count'] = "Number of nodes must be a positive integer";
}
if ($formfields['when'] && ($formfields['when'] != "never") &&
if ($formfields['when'] && (strcmp($formfields['when'],"never") != 0) &&
(!preg_match("/^\d*(\.\d+)?$/",$formfields['when'],$matches))) {
$errors['when'] = "Auto-terminate time must be a positive decimal " .
"or 'never'";
......
......@@ -1221,6 +1221,21 @@ function SHOWNODES($pid, $eid, $sortby, $showclass) {
}
}
#
# Discover whether to show or hide certain columns
#
$colcheck_query_result =
DBQueryFatal("SELECT sum(oi.OS = 'Windows') as winoscount, ".
" sum(nt.isplabdslice) as plabcount ".
"from reserved as r ".
"left join nodes as n on n.node_id=r.node_id ".
"left join os_info as oi on n.def_boot_osid=oi.osid ".
"left join node_types as nt on n.type = nt.type ".
"WHERE r.eid='$eid' and r.pid='$pid'");
$colcheckrow = mysql_fetch_array($colcheck_query_result);
$anywindows = $colcheckrow[winoscount];
$anyplab = $colcheckrow[plabcount];
if ($showlastlog) {
#
# We need to extract, for each node, just the latest nodelog message.
......@@ -1242,12 +1257,14 @@ function SHOWNODES($pid, $eid, $sortby, $showclass) {
# every reserved node.
#
$query_result =
DBQueryFatal("SELECT r.*,n.*,nt.isvirtnode,oi.OS,tip.tipname, ".
DBQueryFatal("SELECT r.*,n.*,nt.isvirtnode,nt.isplabdslice, ".
" oi.OS,tip.tipname,wa.site,wa.hostname, ".
" ns.status as nodestatus, ".
" date_format(rsrv_time,\"%Y-%m-%d&nbsp;%T\") as rsrvtime, ".
"nl.reported,nl.entry ".
"from reserved as r ".
"left join nodes as n on n.node_id=r.node_id ".
"left join widearea_nodeinfo as wa on wa.node_id=n.phys_nodeid ".
"left join node_types as nt on nt.type=n.type ".
"left join node_status as ns on ns.node_id=r.node_id ".
"left join os_info as oi on n.def_boot_osid=oi.osid ".
......@@ -1262,11 +1279,13 @@ function SHOWNODES($pid, $eid, $sortby, $showclass) {
}
else {
$query_result =
DBQueryFatal("SELECT r.*,n.*,nt.isvirtnode,oi.OS,tip.tipname, ".
DBQueryFatal("SELECT r.*,n.*,nt.isvirtnode,nt.isplabdslice, ".
" oi.OS,tip.tipname,wa.site,wa.hostname, ".
" ns.status as nodestatus, ".
" date_format(rsrv_time,\"%Y-%m-%d&nbsp;%T\") as rsrvtime ".
"from reserved as r ".
"left join nodes as n on n.node_id=r.node_id ".
"left join widearea_nodeinfo as wa on wa.node_id=n.phys_nodeid ".
"left join node_types as nt on nt.type=n.type ".
"left join node_status as ns on ns.node_id=r.node_id ".
"left join os_info as oi on n.def_boot_osid=oi.osid ".
......@@ -1288,6 +1307,13 @@ function SHOWNODES($pid, $eid, $sortby, $showclass) {
<th><a href=\"$SCRIPT_NAME?pid=$pid&eid=$eid".
"&sortby=vname&showclass=$showclass\">
Name</a></th>\n";
# Only show 'Site' column if there are plab nodes.
if ($anyplab) {
echo " <th>Site</th>
<th>Widearea<br>Hostname</th>\n";
}
if ($pid == $TBOPSPID) {
echo "<th>Reserved<br>
<a href=\"$SCRIPT_NAME?pid=$pid&eid=$eid".
......@@ -1305,17 +1331,12 @@ function SHOWNODES($pid, $eid, $sortby, $showclass) {
echo " <th>Last Log<br>Time</th>
<th>Last Log Message</th>\n";
}
echo " <th><a href=\"docwrapper.php3?docname=ssh-mime.html\">SSH</a></th>
<th><a href=\"faq.php3#tiptunnel\">Console</a></th> .
<th>Log</th>";
# Only put out a RDP column header if there are any Windows nodes.
$windows_query_result = DBQueryFatal("SELECT r.pid,r.eid,n.node_id,oi.OS ".
"from reserved as r ".
"left join nodes as n on n.node_id=r.node_id ".
"left join os_info as oi on n.def_boot_osid=oi.osid ".
"WHERE r.eid='$eid' and r.pid='$pid' and oi.OS='Windows'");
$anywindows = mysql_num_rows($windows_query_result);
if ($anywindows) {
echo " <th>
<a href=\"docwrapper.php3?docname=rdp-mime.html\">RDP</a>
......@@ -1331,11 +1352,14 @@ function SHOWNODES($pid, $eid, $sortby, $showclass) {
$vname = $row[vname];
$rsrvtime= $row[rsrvtime];
$type = $row[type];
$wasite = $row[site];
$wahost = $row[hostname];
$def_boot_osid = $row[def_boot_osid];
$startstatus = $row[startstatus];
$status = $row[nodestatus];
$bootstate = $row[eventstate];
$isvirtnode = $row[isvirtnode];
$isplabdslice = $row[isplabdslice];
$tipname = $row[tipname];
$iswindowsnode = $row[OS]=='Windows';
$idlehours = TBGetNodeIdleTime($node_id);
......@@ -1358,6 +1382,16 @@ function SHOWNODES($pid, $eid, $sortby, $showclass) {
echo "<tr>
<td><A href='shownode.php3?node_id=$node_id'>$node_id</a></td>
<td>$vname</td>\n";
if ($isplabdslice) {
echo " <td>$wasite</td>
<td>$wahost</td>\n";
}
elseif ($anyplab) {
echo " <td>&nbsp</td>
<td>&nbsp</td>\n";
}
if ($pid == $TBOPSPID)
echo "<td>$rsrvtime</td>\n";
echo " <td>$type</td>\n";
......
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