Commit 8fddf3ce authored by Leigh Stoller's avatar Leigh Stoller

A couple more minor changes before I turn the new stuff loose.

* Added a wrapper class so that you can invoke methods as
  experiment.swapexp or node.reboot. So instead of invoking as
  /XMLRPC/experiment can calling swapexp, you can call the server as
  /XMLRPC and call experiment.swapexp. This allows you to use a single
  connection to talk to different parts of the API. Note this is standard
  (or is it defacto) syntax in XMLRPC.

* Changed the demonstration client to talk the server this way.

* Changed paperbag to allow this as well; the xmlrpc server is invoked with
  no args, which tells it to export the wrapper interface instead of a
  specific module interface.

* A few more cleanups in the server, more permission checks, etc.
parent 1c3d4186
......@@ -43,6 +43,7 @@ my $USERNODE = "@USERNODE@";
"swapexp" => "$TB/bin/swapexp",
"endexp" => "$TB/bin/endexp",
"tbreport" => "$TB/bin/tbreport",
"xmlrpc" => "$TB/sbin/sshxmlrpc_server.py",
"xmlrpc/experiment" => "$TB/sbin/sshxmlrpc_server.py",
"xmlrpc/node" => "$TB/sbin/sshxmlrpc_server.py",
"xmlrpc/imageid" => "$TB/sbin/sshxmlrpc_server.py",
......@@ -148,9 +149,12 @@ do {{
# xmlrpc is special for now, while we use paperbag for it.
# Pass the second token as the first arg to the server.
if ($command =~ /^xmlrpc\/(\w*)$/) {
unshift(@args, $1);
@args = ($1);
$command = "xmlrpc/$1";
}
elsif ($command eq "xmlrpc") {
@args = ();
}
else {
# Strip off all path information from the command
$command =~ /([^\/]+)$/; $command = $1;
......
......@@ -167,6 +167,11 @@ def CheckRequiredArgs(argdict, arglist):
# Check user permission to access an experiment.
#
def CheckExptPermission(pid, eid):
if not (re.match("^[-\w]*$", pid) and
re.match("^[-\w]*$", eid)):
return EmulabResponse(RESPONSE_BADARGS,
output="Illegal characters in project and/or experiment IDs!")
res = DBQueryFatal("SELECT gid FROM experiments "
"WHERE pid=%s and eid=%s",
(pid, eid))
......@@ -188,6 +193,32 @@ def CheckExptPermission(pid, eid):
"access experiment: " + pid + "/" + eid))
return None
#
# This is a wrapper class so that you can invoke methods in dotted form.
# For example experiment.swapexp(...).
#
class EmulabServer:
def __init__(self):
self.instances = {};
self.instances["emulab"] = emulab();
self.instances["user"] = user();
self.instances["fs"] = fs();
self.instances["imageid"] = imageid();
self.instances["osid"] = osid();
self.instances["experiment"] = experiment();
self.instances["node"] = node();
return
def __getattr__(self, name):
dotted = name.split(".");
if len(dotted) != 2:
raise AttributeError("Bad name '%s'" % name)
if not self.instances.has_key(dotted[0]):
raise AttributeError("unknown subclass '%s'" % name)
return getattr(self.instances[dotted[0]], dotted[1]);
pass
#
# This class implements the server side of the XMLRPC interface to emulab as a
# whole.
......@@ -921,6 +952,18 @@ class experiment:
if (argerror):
return argerror
#
# Check permission. This will check proj/exp for illegal chars.
#
permerror = CheckExptPermission(argdict["proj"], argdict["exp"])
if (permerror):
return permerror
if not (argdict["direction"] == "in" or
argdict["direction"] == "out"):
return EmulabResponse(RESPONSE_BADARGS,
output="direction must be 'in' or 'out'");
argstr = "-q"
for opt, val in argdict.items():
if opt == "wait":
......@@ -959,6 +1002,13 @@ class experiment:
if (argerror):
return argerror
#
# Check permission. This will check proj/exp for illegal chars.
#
permerror = CheckExptPermission(argdict["proj"], argdict["exp"])
if (permerror):
return permerror
nsfilename = None
argstr = "-q"
......@@ -1028,6 +1078,13 @@ class experiment:
if (argerror):
return argerror
#
# Check permission. This will check proj/exp for illegal chars.
#
permerror = CheckExptPermission(argdict["proj"], argdict["exp"])
if (permerror):
return permerror
argstr = "-q"
for opt, val in argdict.items():
if opt == "wait":
......@@ -1046,6 +1103,57 @@ class experiment:
return EmulabResponse(RESPONSE_SUCCESS, output=output)
#
# Get textual info from tbreport and send back as string
#
def expinfo(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", "show"))
if (argerror):
return argerror
#
# Check permission. This will check proj/exp for illegal chars.
#
permerror = CheckExptPermission(argdict["proj"], argdict["exp"])
if (permerror):
return permerror
argstr = ""
tokens = argdict["show"].split(",")
for show in tokens:
if show == "nodeinfo":
argstr += " -n"
pass
elif show == "mapping":
argstr += " -m"
pass
elif show == "linkinfo":
argstr += " -l"
pass
elif show == "shaping":
argstr += " -d"
pass
pass
argstr += " " + escapeshellarg(argdict["proj"])
argstr += " " + escapeshellarg(argdict["exp"])
(exitval, output) = runcommand(TBDIR + "/bin/tbreport " + argstr)
if exitval:
return EmulabResponse(RESPONSE_ERROR, exitval >> 8, output=output)
return EmulabResponse(RESPONSE_SUCCESS, output=output)
#
# Return the state of an experiment.
#
......
#!/bin/sh
DIR=@prefix@/sbin/
exec $DIR/sshxmlrpc_server.py
......@@ -86,7 +86,12 @@ def usage():
#
def do_method(server, method_and_args):
# Get a pointer to the function we want to invoke.
meth = getattr(server, method_and_args[0])
methodname = method_and_args[0]
if methodname.count(".") == 0:
methodname = module + "." + methodname
pass
meth = getattr(server, methodname)
# Pop off the method, and then convert the rest of the arguments.
# Be sure to add the version.
......@@ -109,9 +114,9 @@ def do_method(server, method_and_args):
# If the first character of the argument looks like a dictionary,
# try to evaluate it.
#
if value[0] == "{":
value = eval(value);
pass
# if value[0] == '{':
# value = eval(value);
# pass
params[plist[0]] = value
pass
......@@ -201,6 +206,10 @@ except getopt.error, e:
sys.exit(2)
pass
# This is parsed by the Proxy object.
URI = "ssh://" + login_id + "@" + xmlrpc_server + "/xmlrpc";
# All of this admin cruft is totally ignored if you are not a testbed admin.
if admin:
path = SERVER_PATH
if devel:
......@@ -213,12 +222,11 @@ if admin:
pass
pass
path += "/" + SERVER_DIR
URI += "/server"
pass
# Get a handle on the server,
server = SSHServerProxy("ssh://" + login_id + "@" + xmlrpc_server +
"/xmlrpc/" + module,
path=path,
server = SSHServerProxy(URI, path=path,
user_agent="sshxmlrpc_client-v0.1")
if len(req_args):
......
......@@ -11,12 +11,17 @@ import socket
import sshxmlrpc
from emulabserver import *
DEFAULT_MODULE = "experiment"
#
# By default, run a wrapper class that includes all off the modules.
# The client can invoke methods of the form experiment.swapexp when
# the server is invoked in this manner.
#
DEFAULT_MODULE = "EmulabServer"
module = DEFAULT_MODULE
#
# Optional argument indicates what module the server wants to use. This
# will probably change a lot in the future!
# Optional argument indicates the specific module the server wants to use.
# This form should be deprecated, I think.
#
if len(sys.argv) > 1:
module = sys.argv[1]
......
......@@ -43,6 +43,7 @@ my $USERNODE = "@USERNODE@";
"swapexp" => "$TB/bin/swapexp",
"endexp" => "$TB/bin/endexp",
"tbreport" => "$TB/bin/tbreport",
"xmlrpc" => "$TB/sbin/sshxmlrpc_server.py",
"xmlrpc/experiment" => "$TB/sbin/sshxmlrpc_server.py",
"xmlrpc/node" => "$TB/sbin/sshxmlrpc_server.py",
"xmlrpc/imageid" => "$TB/sbin/sshxmlrpc_server.py",
......@@ -148,9 +149,12 @@ do {{
# xmlrpc is special for now, while we use paperbag for it.
# Pass the second token as the first arg to the server.
if ($command =~ /^xmlrpc\/(\w*)$/) {
unshift(@args, $1);
@args = ($1);
$command = "xmlrpc/$1";
}
elsif ($command eq "xmlrpc") {
@args = ();
}
else {
# Strip off all path information from the command
$command =~ /([^\/]+)$/; $command = $1;
......
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