Commit 28a7aaa0 authored by Leigh B. Stoller's avatar Leigh B. Stoller
Browse files

Add a "statewait" method to the experiment module, which allows a

client to wait for an experiment to reach a desired state (active,
swapped, etc). Use the optional timeout parameter to terminate the
wait early. See the documentation for a full descriptione of the
interface.

This is implemented using the just added "poll" interface to the
python event interface library
parent 2a4d8ae7
......@@ -19,6 +19,8 @@ import grp
import errno
import exceptions
import xmlrpclib
import signal
import syslog
sys.path.append("@prefix@/lib")
from libdb import *
from libtestbed import SENDMAIL, TBOPS
......@@ -26,6 +28,7 @@ from emulabclient import *
# Configure variables
TBDIR = "@prefix@"
BOSSNODE = "@BOSSNODE@"
# Version
VERSION = 0.1
......@@ -102,6 +105,13 @@ class NoLoginsError(EmulabError):
class UnknownUserError(EmulabError):
pass
# Exception thrown when a timer expires.
class TimedOutError(EmulabError):
pass
def TimeoutHandler(signum, frame):
raise TimedOutError, 'Timer Expired'
# User info from the passwd file
UID = pwd.getpwuid(os.getuid())[0]
ISADMIN = 0
......@@ -1001,6 +1011,123 @@ class experiment:
state = res[0][0]
return EmulabResponse(RESPONSE_SUCCESS, value=state, output=state)
#
# Wait for an experiment to reach a state; this is especially useful
# with batch experiments. There are probably race conditions inherent
# in this stuff, but typical usage should not encounter them, I hope.
#
def statewait(self, version, argdict):
if version != self.VERSION:
return EmulabResponse(RESPONSE_BADVERSION,
output="Client version mismatch!")
try:
checknologins()
pass
except NoLoginsError, e:
return EmulabResponse(RESPONSE_REFUSED, output=str(e))
argerror = CheckRequiredArgs(argdict, ("proj", "exp", "state"))
if (argerror):
return argerror
if not (re.match("^[-\w]*$", argdict["proj"]) and
re.match("^[-\w]*$", argdict["exp"])):
return EmulabResponse(RESPONSE_BADARGS,
output="Improperly formed proj/exp!")
if (argdict.has_key("timeout") and
not re.match("^[\d]*$", argdict["timeout"])):
return EmulabResponse(RESPONSE_BADARGS,
output="Improperly formed timeout!")
res = DBQueryFatal("select state,gid from experiments "
"where pid=%s and eid=%s",
(argdict["proj"], argdict["exp"]))
if len(res) == 0:
return EmulabResponse(RESPONSE_ERROR,
output="No such experiment: " +
argdict["proj"] + "/" + argdict["exp"])
#
# Check permission. This needs to be a lib routine!
#
state = res[0][0]
gid = res[0][1]
res = DBQueryFatal("SELECT trust FROM group_membership "
"WHERE uid=%s and pid=%s and gid=%s",
(UID, argdict["proj"], gid))
if len(res) == 0:
return EmulabResponse(
RESPONSE_ERROR,
output=("You do not have permission to access experiment: "
+ argdict["proj"] + "/" + argdict["exp"]))
#
# See if experiment already in the desired state.
#
if (state == argdict["state"]):
return EmulabResponse(RESPONSE_SUCCESS, value=state, output=state)
try:
import tbevent
pass
except ImportError, e:
return EmulabResponse(RESPONSE_ERROR, output="System Error")
at = tbevent.address_tuple()
at.objtype = "TBEXPTSTATE"
at.objname = argdict["proj"] + "/" + argdict["exp"]
at.expt = argdict["proj"] + "/" + argdict["exp"]
at.host = BOSSNODE
try:
mc = tbevent.EventClient(server="localhost")
mc.subscribe(at)
except:
return EmulabResponse(RESPONSE_ERROR,
output="Could not connect to Event System")
if (argdict.has_key("timeout")):
signal.signal(signal.SIGALRM, TimeoutHandler)
signal.alarm(int(argdict["timeout"]))
pass
try:
while True:
ev = mc.poll()
if ev == None:
time.sleep(1)
continue
syslog.syslog(syslog.LOG_INFO,
"statewait: " + ev.getObjType() + ", " +
ev.getEventType());
if ev.getEventType() == argdict["state"]:
break
pass
except TimedOutError, e:
return EmulabResponse(RESPONSE_TIMEDOUT,
output="Operation Timed Out")
except:
if (argdict.has_key("timeout")):
signal.alarm(0)
pass
return EmulabResponse(RESPONSE_ERROR, output="System Failure")
if (argdict.has_key("timeout")):
signal.alarm(0)
pass
del(mc);
return EmulabResponse(RESPONSE_SUCCESS, value=argdict["state"],
output=argdict["state"])
#
# Return the node/link mappings for an experiment.
#
......
......@@ -700,6 +700,52 @@ experiments.
</tr>
</table>
<br>
<li><tt><b>statewait</b></tt>: Wait for an experiment to reach a particular
state. State is one of swapped, active, swapping, activating, etc. If the
experiment is already in desired state, returns immediately, otherwise
blocks indefinitely until the experiment reaches the state. Use the timeout
option below to terminate the wait early. The required arguments are:<br><br>
<table cellpadding=2>
<tr>
<th>Name</th><th>Type</th><th>Description</th>
</tr>
<tr></tr>
<tr>
<td><tt>proj</tt></td>
<td>string</td>
<td>The Emulab project ID in which the experiment was created</td>
</tr>
<tr>
<td><tt>exp</tt></td>
<td>string</td>
<td>The Emulab ID of the experiment</td>
</tr>
<tr>
<td><tt>state</tt></td>
<td>string</td>
<td>The experiment state to wait for</td>
</tr>
</table>
<br>
The optional arguments are:<br><br>
<table cellpadding=2>
<tr>
<th>Name</th><th>Type</th><th>Default</th><th>Description</th>
</tr>
<tr></tr>
<tr>
<td><tt>timeout</tt></td>
<td>integer</td>
<td>1-999999</td>
<td>Timeout after this many <b>seconds</b>. The return code is
is <tt>RESPONSE_SUCCESS</tt> if the state is reached or
<tt>RESPONSE_TIMEDOUT</tt> if the timer expires.
</td>
</tr>
</table>
<br>
<li><tt><b>info</b></tt>: Get information about an experiment. The
return value is a hash table (Dictionary) of hash tables. For example,
......
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