sshxmlrpc_client.py.in 6.84 KB
Newer Older
1 2 3
#! /usr/bin/env python
#
# Copyright (c) 2004 University of Utah and the Flux Group.
4
# 
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
# 
24

25
import sys
26
sys.path.append("@prefix@/lib")
27
import getopt
28
import os
29 30

from sshxmlrpc import *
31
from emulabclient import *
32 33 34 35

##
# The package version number
#
36
PACKAGE_VERSION = 0.1
37

38 39 40 41 42 43 44 45 46
# 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"]

47 48 49
# The default RPC module to invoke.
module          = "experiment"

50 51 52
# Debugging output.
debug           = 0

53 54 55 56 57 58 59 60 61
#
# For admin people, and for using their devel trees. These options are
# meaningless unless you are an Emulab developer; they will be rejected
# at the server most ungraciously.
#
SERVER_PATH     = "/usr/testbed"
SERVER_DIR      = "sbin"
DEVEL_DIR       = "devel"
develuser       = None
62
path            = None
63 64
admin           = 0
devel           = 0
65

66 67 68 69
##
# Print the usage statement to stdout.
#
def usage():
70 71
    print "Make a request to the Emulab XML-RPC (SSH-based) server."
    print ("Usage: " + sys.argv[0] 
72 73
                     + " [-hV] [-l login] [-s server] [-m module] "
                     + "<method> [param=value ...]")
74 75 76
    print
    print "Options:"
    print "  -h, --help\t\t  Display this help message"
77 78 79
    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)"
80
    print "  -m, --module\t\t  Set the RPC module (defaults to experiment)"
81 82 83
    print
    print "Required arguments:"
    print "  method\t\t  The method to execute on the server"
84
    print "  params\t\t\t  The method arguments in param=value format"
85 86 87 88
    print
    print "Example:"
    print ("  "
           + sys.argv[0]
89
           + " -s boss.emulab.net echo \"Hello World!\"")
90 91
    return

92 93 94 95 96
#
# Process a single command line
#
def do_method(server, method_and_args):
    # Get a pointer to the function we want to invoke.
97 98 99 100 101 102
    methodname = method_and_args[0]
    if methodname.count(".") == 0:
        methodname = module + "." + methodname
        pass
    
    meth = getattr(server, methodname)
103 104 105 106 107 108 109 110 111 112

    # Pop off the method, and then convert the rest of the arguments.
    # Be sure to add the version.
    method_and_args.pop(0)

    #
    # Convert all params (name=value) into a Dictionary. 
    # 
    params = {}
    for param in method_and_args:
113
        plist = string.split(param, "=", 1)
114
        if len(plist) != 2:
115 116 117
            print ("error: Parameter, '"
                   + param
                   + "', is not of the form: param=value!")
118
            return -1
119 120 121 122 123 124
        value = plist[1]

        #
        # If the first character of the argument looks like a dictionary,
        # try to evaluate it.
        #
125 126 127
        if value.startswith("{"):
            value = eval(value);
            pass
128
    
129
        params[plist[0]] = value
130 131 132 133 134 135
        pass
    meth_args = [ PACKAGE_VERSION, params ]

    #
    # Make the call. 
    #
136 137 138
    try:
        response = apply(meth, meth_args)
        pass
139 140 141 142 143
    except BadResponse, e:
        print ("error: bad reponse from host, " + e.args[0]
               + ", and handler: " + e.args[1])
        print "error: " + e.args[2]
        return -1
144 145 146
    except xmlrpclib.Fault, e:
        print e.faultString
        return -1
147 148

    #
149
    # Parse the Response, which is a Dictionary. See EmulabResponse in the
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
    # emulabclient.py module. The XML standard converts classes to a plain
    # Dictionary, hence the code below. 
    # 
    if len(response["output"]):
        print response["output"]
        pass

    rval = response["code"]

    #
    # If the code indicates failure, look for a "value". Use that as the
    # return value instead of the code. 
    # 
    if rval != RESPONSE_SUCCESS:
        if response["value"]:
            rval = response["value"]
            pass
        pass
168 169 170 171 172

    if debug and response["value"]:
        print str(response["value"])
        pass
        
173 174 175 176 177
    return rval

#
# Process program arguments.
# 
178 179
try:
    # Parse the options,
180
    opts, req_args =  getopt.getopt(sys.argv[1:],
181
                      "dhVs:l:am:zx:",
182
                      [ "help", "version", "server=", "login=", "module="])
183 184 185 186 187 188 189 190 191 192
    # ... act on them appropriately, and
    for opt, val in opts:
        if opt in ("-h", "--help"):
            usage()
            sys.exit()
            pass
        elif opt in ("-V", "--version"):
            print PACKAGE_VERSION
            sys.exit()
            pass
193 194 195 196 197 198
        elif opt in ("-s", "--server"):
	    xmlrpc_server = val
            pass
        elif opt in ("-l", "--login"):
	    login_id = val
            pass
199 200 201
        elif opt in ("-m", "--module"):
	    module = val
            pass
202
        elif opt in ("-d", "--debug"):
203 204
	    debug = 1
            pass
205
        elif opt in ("-a", "--admin"):
206 207 208 209 210 211 212
	    admin = 1
            pass
        elif opt in ("-z", "--devel"):
	    devel = 1
            pass
        elif opt in ("-x", "--develuser"):
	    develuser = val
213
            pass
214 215 216 217 218 219 220 221
        pass
    pass
except getopt.error, e:
    print e.args[0]
    usage()
    sys.exit(2)
    pass

222 223 224 225
# 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.
226 227 228 229 230 231 232 233 234 235 236 237
if admin:
    path = SERVER_PATH
    if devel:
        path += "/" + DEVEL_DIR
        if develuser:
            path += "/" + develuser
            pass
        else:
            path += "/" + login_id
            pass
        pass
    path += "/" + SERVER_DIR
238
    URI +=  "/server"
239 240
    pass

241
# Get a handle on the server,
242
server = SSHServerProxy(URI, path=path,
243
                        user_agent="sshxmlrpc_client-v0.1")
244

245 246 247 248 249 250 251 252
if len(req_args):
    # Method and args are on the command line.
    sys.exit(do_method(server, req_args))
else:
    # Prompt the user for input.
    try:
        while True:
            line = raw_input("$ ")
253
            tokens = line.split(" ")
254
            if len(tokens) >= 1 and len(tokens[0]) > 0:
255
                print str(do_method(server, tokens))
256 257 258 259 260 261
                pass
            pass
        pass
    except EOFError:
        pass
    print
262 263
    pass