All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

Commit 0c587f68 authored by Leigh B. Stoller's avatar Leigh B. 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
......@@ -29,12 +29,12 @@ class SSHTransport:
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.
# 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.
#
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
# 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