Commit 0c587f68 authored by Leigh Stoller's avatar Leigh Stoller

Checkpoint latest working code in case anyone wants to take a looksie.

parent 838b119b
......@@ -14,7 +14,8 @@ include $(OBJDIR)/Makeconf
BIN_SCRIPTS = sshxmlrpc_client.py
SBIN_SCRIPTS = sshxmlrpc_server.py
LIB_STUFF = sshxmlrpc.py emulabserver.py
LIB_STUFF = sshxmlrpc.py emulabserver.py emulabclient.py
LIBEXEC_STUFF = webxmlrpc
#
# These are the ones installed on plastic (users, control, etc).
......@@ -25,12 +26,13 @@ USERBINS = sshxmlrpc_client.py
# Force dependencies on the scripts so that they will be rerun through
# configure if the .in file is changed.
#
all: $(BIN_SCRIPTS) $(SBIN_SCRIPTS) $(LIB_STUFF)
all: $(BIN_SCRIPTS) $(SBIN_SCRIPTS) $(LIB_STUFF) $(LIBEXEC_STUFF)
include $(TESTBED_SRCDIR)/GNUmakerules
install: $(addprefix $(INSTALL_BINDIR)/, $(BIN_SCRIPTS)) \
$(addprefix $(INSTALL_SBINDIR)/, $(SBIN_SCRIPTS)) \
$(addprefix $(INSTALL_LIBEXECDIR)/, $(LIBEXEC_STUFF)) \
$(addprefix $(INSTALL_LIBDIR)/, $(LIB_STUFF))
#
......
This diff is collapsed.
#! /usr/bin/env python
#
# EMULAB-COPYRIGHT
# Copyright (c) 2004 University of Utah and the Flux Group.
# All rights reserved.
#
import sys
import socket
import os
import popen2
import tempfile
import time
sys.path.append("@prefix@/lib")
import libdb
from libtestbed import SENDMAIL, TBOPS
from emulabclient import ResponseBlock
# Configure variables
TBDIR = "@prefix@"
#
# This class implements the server side of the XMLRPC interface to Emulab.
#
# Arguments passed as a Dictionary. This converts to a XML "struct"
# which in Perl/PHP/Ruby would be a hash. So, a client written in
# pretty much any language should be able to talk to this class.
#
class emulabserver:
##
# Initialize the object. Currently only sets the objects 'VERSION' value.
#
def __init__(self):
self.VERSION = 0.1
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, version, argdict):
return ResponseBlock(0, socket.gethostname() + ": " + str(version)
+ " " + str(argdict["list"]))
#
# Start an experiment using batchexp. We get the NS file inline, which
# we have to write to a temp file first.
#
def batchexp(self, version, argdict):
if version != self.VERSION:
return ResponseBlock(-1, "Client version mismatch!");
pass
nsfilename = None
argstr = ""
for opt, val in argdict.items():
if opt in ("batchmode"):
if val == 0:
argstr += " -i"
pass
pass
elif opt in ("description"):
argstr += " -E "
argstr += val
pass
elif opt in ("gid"):
argstr += " -g "
argstr += val
pass
elif opt in ("eid"):
argstr += " -e "
argstr += val
pass
elif opt in ("pid"):
argstr += " -p "
argstr += val
pass
elif opt in ("idleswap"):
if opt == 0:
argstr += " -S "
argstr += argdict["noswap_reason"]
pass
pass
elif opt in ("noswap_reason"):
pass
elif opt in ("idleswap"):
if opt == 0:
argstr += " -L "
argstr += argdict["noidleswap_reason"]
pass
else:
argstr += " -l "
argstr += val
pass
pass
elif opt in ("noidleswap_reason"):
pass
elif opt in ("autoswap"):
argstr += " -a "
argstr += val
pass
elif opt in ("frontend"):
argstr += " -f "
pass
elif opt in ("waitmode"):
argstr += " -w "
pass
elif opt in ("nsfilepath"):
# Backend script will verify this local path.
nsfilename = val
pass
elif opt in ("nsfilestr"):
nsfilestr = val
if len(nsfilestr) > (1024 * 512):
return ResponseBlock(-1, "NS File way too big!");
(nsfp, nsfilename) = writensfile(nsfilestr)
if not nsfilename:
return ResponseBlock(-1, "Server Error")
pass
pass
if nsfilename:
argstr += " "
argstr += nsfilename
pass
(exitval, output) = runcommand(TBDIR + "/bin/batchexp " + argstr)
return ResponseBlock(exitval >> 8, output)
#
# swap an experiment using swapexp. We get the NS file inline, which
# we have to write to a temp file first.
#
def swapexp(self, version, argdict):
if version != self.VERSION:
return ResponseBlock(-1, "Client version mismatch!");
pass
nsfilename = None
argstr = ""
for opt, val in argdict.items():
if opt in ("waitmode"):
argstr += " -w "
pass
elif opt in ("reboot_nodes"):
argstr += " -r "
pass
elif opt in ("restart_eventsys"):
argstr += " -e "
pass
elif opt in ("swapop"):
argstr += " -s "
argstr += val
pass
elif opt in ("nsfilepath"):
# Backend script will verify this local path.
nsfilename = val
pass
elif opt in ("nsfilestr"):
nsfilestr = val
if len(nsfilestr) > (1024 * 512):
return ResponseBlock(-1, "NS File way too big!");
(nsfp, nsfilename) = writensfile(nsfilestr)
if not nsfilename:
return ResponseBlock(-1, "Server Error")
pass
pass
if argdict.has_key("pid"):
argstr += " "
argstr += argdict["pid"]
pass
if argdict.has_key("eid"):
argstr += " "
argstr += argdict["eid"]
pass
if nsfilename:
argstr += " "
argstr += nsfilename
pass
(exitval, output) = runcommand(TBDIR + "/bin/swapexp " + argstr)
return ResponseBlock(exitval >> 8, output)
#
# end an experiment using endexp.
#
def endexp(self, version, argdict):
if version != self.VERSION:
return ResponseBlock(-1, "Client version mismatch!");
pass
argstr = ""
for opt, val in argdict.items():
if opt in ("waitmode"):
argstr += " -w "
pass
pass
if argdict.has_key("pid"):
argstr += " "
argstr += argdict["pid"]
pass
if argdict.has_key("eid"):
argstr += " "
argstr += argdict["eid"]
pass
(exitval, output) = runcommand(TBDIR + "/bin/endexp " + argstr)
return ResponseBlock(exitval >> 8, output)
#
# nscheck an NS file.
#
def nscheck(self, version, argdict):
if version != self.VERSION:
return ResponseBlock(-1, "Client version mismatch!");
pass
argstr = ""
if argdict.has_key("nsfilestr"):
nsfilestr = argdict["nsfilestr"]
if len(nsfilestr) > (1024 * 512):
return ResponseBlock(-1, "NS File way too big!");
(nsfp, nsfilename) = writensfile(nsfilestr)
if not nsfilename:
return ResponseBlock(-1, "Server Error")
argstr += nsfilename
pass
elif argdict.has_key("nsfilepath"):
# Backend script will verify this local path.
argstr += escapeshellarg(argdict["nsfilepath"])
pass
(exitval, output) = runcommand(TBDIR + "/bin/nscheck " + argstr)
return ResponseBlock(exitval >> 8, output)
#
# create_image.
#
def create_image(self, version, argdict):
if version != self.VERSION:
return ResponseBlock(-1, "Client version mismatch!");
pass
argstr = ""
for opt, val in argdict.items():
if opt in ("pid"):
argstr += " -p "
argstr += escapeshellarg(val)
pass
pass
if argdict.has_key("imageid"):
argstr += " "
argstr += argdict["imageid"]
pass
if argdict.has_key("nodeid"):
argstr += " "
argstr += argdict["nodeid"]
pass
(exitval, output) = runcommand(TBDIR + "/bin/create_image " + argstr)
return ResponseBlock(exitval >> 8, output)
pass
#
# Utility functions
#
#
# escapeshellarg() adds single quotes around a string and quotes/escapes any
# existing single quotes allowing string to be passed directly to a shell
# function and having it be treated as a single safe argument.
#
def escapeshellarg(s):
s2 = ""
for c in s:
if c == '\'':
s2 = s2 + '\'\\\''
s2 = s2 + c
return '\'' + s2 + '\''
#
# Run a command. args is a list of strings to pass as arguments to cmd.
# Return the exitcode and the output as a tuple.
#
def runcommand(cmd):
child = popen2.Popen4(cmd)
output = child.fromchild.read(8192)
return (child.wait(), output)
def writensfile(str):
tempfile.tempdir = "/var/tmp"
try:
fp = tempfile.NamedTemporaryFile(prefix="php")
fp.write(str)
fp.flush()
except:
SENDMAIL(TBOPS, "writensfile failed",
"Could not write temporary NS file:\n" +
"%s:%s" % (sys.exc_type, sys.exc_value))
return None
pass
# Yuck. Need to maintain a ref so that the file is not deleted!
return (fp, fp.name)
......@@ -18,7 +18,7 @@ class SSHTransport:
# Send a request to the destination.
#
# @param host The host name on which to execute the request
# @param handler The python file that will handle the request.
# @param handler The python module that will handle the request.
# @param request_body The XML-RPC encoded request.
# @param verbose unused.
# @return The value returned
......@@ -28,13 +28,13 @@ class SSHTransport:
if handler.startswith('/'):
handler = handler[1:]
pass
self.user, self.realhost = urllib.splituser(host)
# SSH to the host and call python on the handler.
self.myChild = popen2.Popen3("ssh -x "
+ host
+ " 'python "
+ handler
+ "'")
self.myChild = popen2.Popen3("ssh -x -l " + self.user + " "
+ self.realhost + " "
+ handler)
# Send the request over SSH's stdin,
self.myChild.tochild.write(request_body)
......@@ -205,3 +205,4 @@ class SSHServerProxy:
def __getattr__(self, name):
# magic method dispatcher
return xmlrpclib._Method(self.__request, name)
......@@ -5,44 +5,59 @@
# All rights reserved.
#
import sys
sys.path.append("@prefix@/lib")
import getopt
import os
from sshxmlrpc import *
from emulabclient import emulabclient
##
# The package version number
#
PACKAGE_VERSION = "0.1"
# Default server
XMLRPC_SERVER = "@BOSSNODE@"
# User supplied server name.
xmlrpc_server = XMLRPC_SERVER
# User supplied login ID to use (overrides env variable USER if exists).
login_id = os.environ["USER"]
# Debugging output.
debug = 0
##
# Print the usage statement to stdout.
#
def usage():
print "Send an XML-RPC request to a SSH-based server."
print "Usage: " + sys.argv[0] + " [-hV] <URI> <method> [<args>]"
print "Make a request to the Emulab XML-RPC (SSH-based) server."
print ("Usage: " + sys.argv[0]
+ " [-hV] [-l login] [-s server] <method> [<args>]")
print
print "Options:"
print " -h, --help\t\t Display this help message"
print " -V, --version\t\t Show the vesion number"
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
print "Required arguments:"
print " URI\t\t\t The URI of the server (e.g. ssh://localhost/echo.py)"
print " method\t\t The method to execute on the server"
print " args\t\t\t The method arguments, given as a python list"
print " \t\t\t (Note: This value gets eval'd)"
print " args\t\t\t The method arguments"
print
print "Example:"
print (" "
+ sys.argv[0]
+ " ssh://ops.emulab.net/~stack/sshxmlrpc/echo.py"
+ " echo '\"Hello, World!\"'")
+ " -s boss.emulab.net echo \"Hello World!\"")
return
try:
# Parse the options,
opts, req_args = getopt.getopt(sys.argv[1:],
"hV",
[ "help", "version", ])
"dhVs:l:",
[ "help", "version", "server=", "login=", ])
# ... act on them appropriately, and
for opt, val in opts:
if opt in ("-h", "--help"):
......@@ -53,9 +68,18 @@ try:
print PACKAGE_VERSION
sys.exit()
pass
elif opt in ("-s", "--server"):
xmlrpc_server = val
pass
elif opt in ("-l", "--login"):
login_id = val
pass
elif opt in ("-d"):
debug = 1
pass
pass
# ... make sure the required arguments are there.
if len(req_args) < 2:
if len(req_args) < 1:
raise getopt.error("Required arguments not given", "")
pass
except getopt.error, e:
......@@ -65,18 +89,19 @@ except getopt.error, e:
pass
# Get a handle on the server,
server = SSHServerProxy(req_args[0])
server = SSHServerProxy("ssh://" + login_id + "@" + xmlrpc_server + "/sshxmlrpc")
# ... get the requested method,
meth = getattr(server, req_args[1])
# Get a handle on the emulab client object, giving it the transport to use.
client = emulabclient(server, debug);
# ... cons up the arguments, and
if len(req_args) > 2:
meth_args = eval("[" + req_args[2] + "]")
pass
else:
meth_args = []
pass
# Get a pointer to the client side of the function we want to invoke.
meth = getattr(client, req_args[0])
# ... make the call.
print str(apply(meth, meth_args))
# The rest of the list is passed as is.
req_args.pop(0)
#
# Make the call. The clientside class will do whatever it needs before
# invoking the equiv method on the server side.
#
sys.exit(apply(meth, req_args))
#! /usr/bin/env python
#
# EMULAB-COPYRIGHT
# Copyright (c) 2004 University of Utah and the Flux Group.
# All rights reserved.
#
import sys
import time
sys.path.append("@prefix@/lib")
import socket
import sshxmlrpc
from emulabserver import emulabserver
#
# This class implements the server side of the XMLRPC interface to Emulab.
#
class MyEchoObject:
##
# Initialize the object. Currently only sets the objects 'VERSION' value.
#
def __init__(self):
self.VERSION = "MyEchoObject v0.1"
return
##
# Echo a message, basically, prepend the host name to the parameter.
#
# @param msg The message to echo.
# @return The 'msg' value with this machine's name prepended.
#
def echo(self, msg):
return socket.gethostname() + ": " + msg
##
# Similar to echo, except it also prepends the current time.
#
# @param msg The message to echo.
# @return The 'msg' value with this machine's name and current time
# prepended.
#
# @sa echo
#
def echoWithTime(self, msg):
return (socket.gethostname()
+ " - "
+ time.asctime(time.localtime())
+ ": "
+ msg)
pass
# This is invoked inside an SSH, typically from the paperbag shell.
# We use stdin/stdout to read/write the request/response. We handle
# just a single request this way, and then exit.
#
# Construct and wrap our object.
wrapper = sshxmlrpc.SSHServerWrapper(MyEchoObject())
wrapper = sshxmlrpc.SSHServerWrapper(emulabserver())
# Handle the request on stdin and send the response to stdout.
wrapper.handle_request((sys.stdin, sys.stdout))
sys.exit(0)
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2004 University of Utah and the Flux Group.
# All rights reserved.
#
use English;
#
# This gets invoked from the Web interface. Simply a wrapper ...
#
# usage: webxmlrpc arguments ...
#
#
# Configure variables
#
my $TB = "@prefix@";
#
# Run the real thing, and never return.
#
exec "$TB/sbin/sshxmlrpc_server.py", @ARGV;
die("webxmlrpc: Could not exec sshxmlrpc_server.py: $!");
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