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

Another checkpoint before bringing in Tim's latest code.

parent 63792e12
......@@ -12,11 +12,11 @@ UNIFIED = @UNIFIED_BOSS_AND_OPS@
include $(OBJDIR)/Makeconf
BIN_SCRIPTS = sshxmlrpc_client.py client.py
BIN_SCRIPTS = sshxmlrpc_client.py
SBIN_SCRIPTS = sshxmlrpc_server.py
LIB_STUFF = sshxmlrpc.py emulabserver.py emulabclient.py
LIBEXEC_STUFF = webxmlrpc
WWW_STUFF = xmlapi.php3
WWW_STUFF = xmlrpcapi.php3
#
# These are the ones installed on plastic (users, control, etc).
......
......@@ -36,404 +36,6 @@ class ResponseBlock:
self.output = output
return
#
# This class implements the client side of the XMLRPC interface to Emulab.
# We do not try to do any serious argument processing here, but just pass
# it all through to the server.
#
# This is intended to serve as a demonstration of how to use the RPC
# interface, and to provide a test harness (via sshxmlrpc_client.py)
# for the server.
#
class emulabclient:
#
# Initialize the object. The server argument is intended to
# make this class independent of how we talk to the server. It just needs
# to export the same interface as the xmlrpclib ServerProxy class.
#
def __init__(self, server, debug):
self.VERSION = 0.1
self.server = server
self.debug = debug
return
##
# Echo a message, basically, prepend the host name to the parameter list.
#
# @param args The argument list to echo back.
# @return The 'msg' value with this machine's name prepended.
#
def echo(self, *args):
argdict = {};
argdict["list"] = args
response = self.server.echo(self.VERSION, argdict)
print response["output"]
return 0
#
# Start an experiment using batchexp. The wrinkle is that we have to
# package up the NS file and pass it inline.
#
def batchexp(self, *args):
# The args are read-only, but I want a mutable version.
arglist = [a for a in args];
if len(arglist) == 0:
print "batchexp: Must provide some arguments"
return -1
#
# Parse args. We do not try to do any checking, but rather just
# convert them to functional arguments and place them into the
# Dict that is passed along.
#
argdict = {};
opt_args, req_args = getopt.getopt(arglist, "iE:g:S:L:a:l:fw")
for opt, val in opt_args:
if opt in ("-i"):
argdict["batchmode"] = 0
pass
elif opt in ("-E"):
argdict["description"] = val
pass
elif opt in ("-g"):
argdict["gid"] = val
pass
elif opt in ("-S"):
argdict["swapable"] = 0
argdict["noswap_reason"] = val
pass
elif opt in ("-L"):
argdict["idleswap"] = 0
argdict["noidleswap_reason"] = val
pass
elif opt in ("-a"):
argdict["autoswap"] = val
pass
elif opt in ("-l"):
argdict["idleswap"] = val
pass
elif opt in ("-f"):
argdict["frontend"] = 1
pass
elif opt in ("-w"):
argdict["waitmode"] = 1
pass
pass
#
# Next two args must be pid and eid. This differs from the batchexp
# command line interface, but is consistent with swapexp and endexp.
#
if len(req_args) < 2 or len(req_args) > 3:
print "batchexp: Must provide pid, eid, and optional NS file!"
return -1
argdict["pid"] = req_args[0]
argdict["eid"] = req_args[1]
# Optional NS file.
if len(req_args) == 3:
nsfilename = req_args[2]
nsfilestr = readnsfile(nsfilename, self.debug)
# Watch for error reading NS file.
if not nsfilestr:
return -1
if len(nsfilestr) > MAXNSFILESIZE:
print "batchexp: NS file too big; server will reject it."
return -1
argdict["nsfilestr"] = nsfilestr
pass
response = self.server.batchexp(self.VERSION, argdict)
if len(response["output"]):
print response["output"]
pass
if response["code"] != RESPONSE_SUCCESS:
if response["value"]:
return response["value"]
else:
return response["code"]
pass
return RESPONSE_SUCCESS
#
# startexp is an alias for batchexp.
#
def startexp(self, *args):
return self.batchexp(*args)
#
# swapexp. Also does swapmod. The wrinkle is that like above, we need to
# pakcage up the NS file and pass it inline. The difference is that the
# NS file is optional, dependent on the previous options; swapmod only.
#
def swapexp(self, *args):
# The args are read-only, but I want a mutable version.
arglist = [a for a in args];
if len(arglist) == 0:
print "swapexp: Must provide some arguments"
return -1
#
# Parse args. We do not try to do any checking, but rather just
# convert them to functional arguments and place them into the
# Dict that is passed along.
#
argdict = {};
opt_args, req_args = getopt.getopt(arglist, "wres:")
for opt, val in opt_args:
if opt in ("-w"):
argdict["waitmode"] = 1
pass
elif opt in ("-r"):
argdict["reboot_nodes"] = 1
pass
elif opt in ("-e"):
argdict["restart_eventsys"] = 1
pass
elif opt in ("-s"):
argdict["swapop"] = val
pass
pass
if len(req_args) < 2 or len(req_args) > 3:
print "swapexp: Must provide pid and eid and an optiona NS file!"
return -1
argdict["pid"] = req_args[0]
argdict["eid"] = req_args[1]
if len(req_args) == 3:
nsfilename = req_args[2]
nsfilestr = readnsfile(nsfilename, self.debug)
# Watch for error reading NS file.
if not nsfilestr:
return -1
if len(nsfilestr) > MAXNSFILESIZE:
print "batchexp: NS file too big; server will reject it."
return -1
argdict["nsfilestr"] = nsfilestr
pass
response = self.server.swapexp(self.VERSION, argdict)
if len(response["output"]):
print response["output"]
pass
if response["code"] != RESPONSE_SUCCESS:
if response["value"]:
return response["value"]
else:
return response["code"]
pass
return RESPONSE_SUCCESS
#
# endexp.
#
def endexp(self, *args):
# The args are read-only, but I want a mutable version.
arglist = [a for a in args];
if len(arglist) == 0:
print "endexp: Must provide some arguments"
return -1
#
# Parse args. We do not try to do any checking, but rather just
# convert them to functional arguments and place them into the
# Dict that is passed along.
#
argdict = {};
opt_args, req_args = getopt.getopt(arglist, "w")
for opt, val in opt_args:
if opt in ("-w"):
argdict["waitmode"] = 1
pass
pass
if len(req_args) != 2:
print "swapexp: Must provide pid and eid!"
return -1
argdict["pid"] = req_args[0]
argdict["eid"] = req_args[1]
response = self.server.endexp(self.VERSION, argdict)
if len(response["output"]):
print response["output"]
pass
if response["code"] != RESPONSE_SUCCESS:
if response["value"]:
return response["value"]
else:
return response["code"]
pass
return RESPONSE_SUCCESS
#
# nscheck. Syntax check an NS file. The wrinkle is that like above, we
# need to pakcage up the NS file and pass it inline.
##
def nscheck(self, *args):
# The args are read-only, but I want a mutable version.
arglist = [a for a in args];
if len(arglist) != 1:
print "nscheck: Must provide an NS file!"
return -1
argdict = {};
nsfilename = arglist[0]
nsfilestr = readnsfile(nsfilename, self.debug)
# Watch for error reading NS file.
if not nsfilestr:
return -1
if len(nsfilestr) > MAXNSFILESIZE:
print "nscheck: NS file too big; server will reject it."
return -1
argdict["nsfilestr"] = nsfilestr
response = self.server.nscheck(self.VERSION, argdict)
if len(response["output"]):
print response["output"]
pass
if response["code"] != RESPONSE_SUCCESS:
if response["value"]:
return response["value"]
else:
return response["code"]
pass
return RESPONSE_SUCCESS
#
# create_image. Create a disk image from a node.
#
def create_image(self, *args):
# The args are read-only, but I want a mutable version.
arglist = [a for a in args];
if len(arglist) == 0:
print "create_image: Must provide some arguments!"
return -1
#
# Parse args. We do not try to do any checking, but rather just
# convert them to functional arguments and place them into the
# Dict that is passed along.
#
argdict = {};
opt_args, req_args = getopt.getopt(arglist, "p")
for opt, val in opt_args:
if opt in ("-p"):
argdict["pid"] = val
pass
pass
if len(req_args) != 2:
print "create_image: Must provide imageid and nodeid!"
return -1
argdict["imageid"] = req_args[0]
argdict["nodeid"] = req_args[1]
response = self.server.create_image(self.VERSION, argdict)
if len(response["output"]):
print response["output"]
pass
if response["code"] != RESPONSE_SUCCESS:
if response["value"]:
return response["value"]
else:
return response["code"]
pass
return RESPONSE_SUCCESS
#
# delay_config. Alter link characteristics.
#
def delay_config(self, *args):
# The args are read-only, but I want a mutable version.
arglist = [a for a in args];
if len(arglist) == 0:
print "create_image: Must provide some arguments!"
return -1
#
# Parse args. We do not try to do any checking, but rather just
# convert them to functional arguments and place them into the
# Dict that is passed along.
#
argdict = {};
opt_args, req_args = getopt.getopt(arglist, "ms:")
for opt, val in opt_args:
if opt in ("-m"):
argdict["modify_exp"] = 1
pass
if opt in ("-s"):
argdict["srcvnode"] = val
pass
pass
if len(req_args) < 4:
print "delay_config: Must provide pid, eid, link, param=value ...!"
return -1
argdict["pid"] = req_args.pop(0)
argdict["eid"] = req_args.pop(0)
argdict["link"] = req_args.pop(0)
#
# The params are supplied as a separate dictionary. It is up to the
# server to reject ones it does not like.
#
params = {}
for param in req_args:
plist = string.split(param, "=")
if len(plist) != 2:
print "delay_config: Parameters are of the form: param=value!"
return -1
params[plist[0]] = plist[1]
pass
argdict["params"] = params
response = self.server.delay_config(self.VERSION, argdict)
if len(response["output"]):
print response["output"]
pass
if response["code"] != RESPONSE_SUCCESS:
if response["value"]:
return response["value"]
else:
return response["code"]
pass
return RESPONSE_SUCCESS
pass
#
# Utility functions
#
#
# Read an nsfile and return a single string.
#
......
......@@ -15,7 +15,7 @@ from emulabclient import *
##
# The package version number
#
PACKAGE_VERSION = "0.1"
PACKAGE_VERSION = 0.1
# Default server
XMLRPC_SERVER = "@BOSSNODE@"
......@@ -26,6 +26,9 @@ xmlrpc_server = XMLRPC_SERVER
# User supplied login ID to use (overrides env variable USER if exists).
login_id = os.environ["USER"]
# The default RPC module to invoke.
module = "experiment"
# Debugging output.
debug = 0
......@@ -35,17 +38,19 @@ debug = 0
def usage():
print "Make a request to the Emulab XML-RPC (SSH-based) server."
print ("Usage: " + sys.argv[0]
+ " [-hV] [-l login] [-s server] <method> [<args>]")
+ " [-hV] [-l login] [-s server] [-m module] "
+ "<method> [param=value ...]")
print
print "Options:"
print " -h, --help\t\t Display this help message"
print " -V, --version\t\t Show the version number"
print " -s, --server\t\t Set the server hostname"
print " -l, --login\t\t Set the login id (defaults to $USER)"
print " -m, --module\t\t Set the RPC module (defaults to experiment)"
print
print "Required arguments:"
print " method\t\t The method to execute on the server"
print " args\t\t\t The method arguments"
print " params\t\t\t The method arguments in param=value format"
print
print "Example:"
print (" "
......@@ -74,6 +79,9 @@ try:
elif opt in ("-l", "--login"):
login_id = val
pass
elif opt in ("-m", "--module"):
module = val
pass
elif opt in ("-d"):
debug = 1
pass
......@@ -89,19 +97,46 @@ except getopt.error, e:
pass
# Get a handle on the server,
server = SSHServerProxy("ssh://" + login_id + "@" + xmlrpc_server + "/sshxmlrpc")
# Get a handle on the emulab client object, giving it the transport to use.
client = emulabclient(server, debug);
server = SSHServerProxy("ssh://" + login_id + "@" + xmlrpc_server +
"/xmlrpc/" + module)
# Get a pointer to the client side of the function we want to invoke.
meth = getattr(client, req_args[0])
# Get a pointer to the function we want to invoke.
meth = getattr(server, req_args[0])
# The rest of the list is passed as is.
# Pop off the method, and then convert the rest of the arguments. Be sure to
# add the version.
req_args.pop(0)
params = {}
for param in req_args:
plist = string.split(param, "=")
if len(plist) != 2:
print "Parameters are of the form: param=value!"
sys.exit(-1)
params[plist[0]] = plist[1]
pass
meth_args = [ PACKAGE_VERSION, params ]
#
# Make the call. The clientside class will do whatever it needs before
# invoking the equiv method on the server side.
# Make the call.
#
sys.exit(apply(meth, req_args))
response = apply(meth, meth_args)
#
# Parse the Response, which is a Dictionary. See ResponseBlock in the
# emulabclient.py module. The XML standard converts classes to a plain
# Dictionary, hence the code below.
#
if len(response["output"]):
print response["output"]
pass
if response["code"] != RESPONSE_SUCCESS:
if response["value"]:
sys.exit(response["value"])
else:
sys.exit(response["code"])
pass
sys.exit(RESPONSE_SUCCESS)
......@@ -57,32 +57,35 @@ Python is also available that you can use on your desktop to invoke
commands from the shell. For example:
<code><pre>
$ sshxmlrpc_client.py startexp -i -w mypid myeid ~/nsfile.ns</code></pre>
$ sshxmlrpc_client.py startexp batch=false wait=true pid="mypid" eid="myeid" nsfilestr="`cat ~/nsfile.ns`"</code></pre>
which says to create an experiment called "myeid" in the "mypid"
project, swap it in immediately, wait for the exit status (instead of
running asynchronously), using the NS file nsfile.ns in your home dir.
By default, the client will contact the RPC server at
<tt><?php echo $BOSSNODE ?></tt>, but you can override that by using
the <tt>-s hostname</tt> option to sshxmlrpc_client. If your login ID
on the local machine is different then your login ID at Emulab, you
can use the <tt>-l login</tt> option. For example:
which says to create an experiment called "myeid" in the "mypid" project,
swap it in immediately, wait for the exit status (instead of running
asynchronously), passing inline the contents of nsfile.ns in your home dir.
By default, the client will contact the RPC server at <tt><?php echo
$BOSSNODE ?></tt>, but you can override that by using the <tt>-s
hostname</tt> option to sshxmlrpc_client. If your login ID on the local
machine is different then your login ID at Emulab, you can use the <tt>-l
login</tt> option. For example:
<code><pre>
$ sshxmlrpc_client.py -s boss.emulab.net -l rellots startexp ...</code></pre>
which would invoke the RPC server on <tt>boss.emulab.net</tt>, using the login
ID <tt>rellots</tt> (for the purposes of SSH authentication). You will
be prompted for your SSH passphrase, unless you are running an SSH
agent and the key you have uploaded to Emulab has been added to your
local agent.
which would invoke the RPC server on <tt>boss.emulab.net</tt>, using the
login ID <tt>rellots</tt> (for the purposes of SSH authentication). You
will be prompted for your SSH passphrase, unless you are running an SSH
agent and the key you have uploaded to Emulab has been added to your local
agent.
</p>
<p>
The <tt>sshxmlrpc_client</tt> python program is a simple demonstration of
how to use Emulab's RPC server. It converts command lines into RPCs to the
server, and prints out the results that the server sends back, exiting with
whatever status code the server returned. You can use this client program
how to use Emulab's RPC server. If you do not give provide a method
and arguments on the command line, it will enter a command loop where
you can type in commands (method and arguments) and wait for responses
from the server. It converts your command lines into RPCs to the
server, and prints out the results that the server sends back (exiting with
whatever status code the server returned). You can use this client program
as is, or you can write your own client program in whatever language you
like, as long as you speak to the server over an SSH connection. The API
for the server is broken into several different modules that export a
......
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