source: fedd/fedd_create.py @ 4d48e01

axis_examplecompt_changesinfo-opsversion-1.30version-2.00version-3.01version-3.02
Last change on this file since 4d48e01 was 4d48e01, checked in by Ted Faber <faber@…>, 16 years ago

command line experiment creation

  • Property mode set to 100755
File size: 9.0 KB
Line 
1#!/usr/local/bin/python
2
3import sys
4import os
5import pwd
6
7from fedd_services import *
8
9from M2Crypto import SSL, X509
10from M2Crypto.m2xmlrpclib import SSL_Transport
11import M2Crypto.httpslib
12
13from xmlrpclib import ServerProxy, Error, dumps, loads
14from ZSI import SoapWriter
15from ZSI.TC import QName, String, URI, AnyElement, UNBOUNDED, Any
16from ZSI.wstools.Namespaces import SOAP
17from ZSI.fault import FaultType, Detail
18
19import xmlrpclib
20
21from fedd_util import fedid, fedd_ssl_context, pack_soap, unpack_soap, \
22        pack_id, unpack_id
23
24from optparse import OptionParser, OptionValueError
25
26import parse_detail
27
28# Turn off the matching of hostname to certificate ID
29SSL.Connection.clientPostConnectionCheck = None
30
31class IDFormatException(RuntimeError): pass
32
33class access_method:
34    """Encapsulates an access method generically."""
35    (type_ssh, type_x509, type_pgp) = ('sshPubkey', 'X509', 'pgpPubkey')
36    default_type = type_ssh
37    def __init__(self, buf=None, type=None, file=None):
38        self.buf = buf
39
40        if type != None: self.type = type
41        else: self.type = access_method.default_type
42
43        if file != None:
44            self.readfile(file)
45   
46    def readfile(self, file, type=None):
47        f = open(file, "r")
48        self.buf = f.read();
49        f.close()
50        if type == None:
51            if self.type == None:
52                self.type = access_method.default_type
53        else:
54            self.type = type;
55   
56class node_desc:
57    def __init__(self, image, hardware, count=1):
58        if getattr(image, "__iter__", None) == None:
59            if image == None: self.image = [ ]
60            else: self.image = [ image ]
61        else:
62            self.image = image
63
64        if getattr(hardware, "__iter__", None) == None: 
65            if hardware == None: self.hardware = [ ]
66            else: self.hardware = [ hardware ]
67        else:
68            self.hardware = hardware
69        if count != None: self.count = int(count)
70        else: self.count = 1
71
72class fedd_client_opts(OptionParser):
73    """Encapsulate option processing in this class, rather than in main"""
74    def __init__(self):
75        OptionParser.__init__(self, usage="%prog [opts] (--help for details)",
76                version="0.1")
77
78        self.set_defaults(url="https://localhost:23235", anonymous=False,
79                serialize_only=False, transport="soap", use_fedid=False,
80                debug=0, file=None, master=None)
81
82        self.add_option("-c","--cert", action="store", dest="cert",
83                type="string", help="my certificate file")
84        self.add_option("-d", "--debug", action="count", dest="debug", 
85                help="Set debug.  Repeat for more information")
86        self.add_option("-f", "--file", dest="file", 
87                help="experiment description file")
88        self.add_option("-F","--useFedid", action="store_true",
89                dest="use_fedid",
90                help="Use a fedid derived from my certificate as user identity")
91        self.add_option("-k", "--sshKey", action="callback", type="string", 
92                callback=add_ssh_key, callback_args=(access_keys,),
93                help="ssh key for access (can be supplied more than once")
94        self.add_option("-K", "--x509Key", action="callback", type="string", 
95                callback=add_x509_cert, callback_args=(access_keys,),
96                help="X509 certificate for access " + \
97                        "(can be supplied more than once")
98        self.add_option("-m", "--master", dest="master",
99                help="Master testbed in the federation")
100        self.add_option("-s", "--serializeOnly", action="store_true", 
101                dest="serialize_only",
102                help="Print the SOAP request that would be sent and exit")
103        self.add_option("-T","--trusted", action="store", dest="trusted",
104                type="string", help="Trusted certificates (required)")
105        self.add_option("-u", "--url", action="store", dest="url",
106                type="string",
107                help="URL to connect to (default %default)")
108        self.add_option("-U", "--username", action="store", dest="user",
109                type="string", help="Use this username instead of the uid")
110        self.add_option("-x","--transport", action="store", type="choice",
111                choices=("xmlrpc", "soap"),
112                help="Transport for request (xmlrpc|soap) (Default: %default)")
113        self.add_option("--trace", action="store_const", dest="tracefile", 
114                const=sys.stderr, help="Print SOAP exchange to stderr")
115
116def exit_with_fault(dict, out=sys.stderr):
117    """ Print an error message and exit.
118
119    The dictionary contains the FeddFaultBody elements."""
120    codestr = ""
121
122    if dict.has_key('errstr'):
123        codestr = "Error: %s" % dict['errstr']
124
125    if dict.has_key('code'):
126        if len(codestr) > 0 : 
127            codestr += " (%d)" % dict['code']
128        else:
129            codestr = "Error Code: %d" % dict['code']
130
131    print>>out, codestr
132    print>>out, "Description: %s" % dict['desc']
133    sys.exit(dict.get('code', 20))
134
135def add_ssh_key(option, opt_str, value, parser, access_keys):
136    try:
137        access_keys.append(access_method(file=value,
138            type=access_method.type_ssh))
139    except IOError, (errno, strerror):
140        raise OptionValueError("Cannot generate sshPubkey from %s: %s (%d)" %
141                (value,strerror,errno))
142
143def add_x509_cert(option, opt_str, value, parser, access_keys):
144    try:
145        access_keys.append(access_method(file=value,
146            type=access_method.type_x509))
147    except IOError, (errno, strerror):
148        raise OptionValueError("Cannot read x509 cert from %s: %s (%d)" %
149                (value,strerror,errno))
150
151def get_user_info(access_keys):
152    pw = pwd.getpwuid(os.getuid());
153    try_cert=None
154    user = None
155
156    if pw != None:
157        user = pw[0]
158        try_cert = "%s/.ssl/emulab.pem" % pw[5];
159        if not os.access(try_cert, os.R_OK):
160            try_cert = None
161        if len(access_keys) == 0:
162            for k in ["%s/.ssh/id_rsa.pub", "%s/.ssh/id_dsa.pub", 
163                    "%s/.ssh/identity.pub"]:
164                try_key = k % pw[5];
165                if os.access(try_key, os.R_OK):
166                    access_keys.append(access_method(file=try_key,
167                        type=access_method.type_ssh))
168                    break
169    return (user, try_cert)
170
171access_keys = []
172
173# Process the options using the customized option parser defined above
174parser = fedd_client_opts()
175
176(opts, args) = parser.parse_args()
177
178if opts.trusted != None:
179    if ( not os.access(opts.trusted, os.R_OK) ) :
180        sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
181else:
182    parser.error("--trusted is required")
183
184if opts.debug > 0: opts.tracefile=sys.stderr
185
186(user, cert) = get_user_info(access_keys)
187
188if opts.user: user = opts.user
189
190if opts.cert != None: cert = opts.cert
191
192if cert == None:
193    sys.exit("No certificate given (--cert) or found")
194
195if os.access(cert, os.R_OK):
196    fid = fedid(file=cert)
197    if opts.use_fedid == True:
198        user = fid
199else:
200    sys.exit("Cannot read certificate (%s)" % cert)
201
202if opts.file:
203    exp_desc = ""
204    try:
205        f = open(opts.file, 'r')
206        for line in f:
207            exp_desc += line
208        f.close()
209    except IOError:
210        sys.exit("Cannot read description file (%s)" %opts.file)
211else:
212    sys.exit("Must specify an experiment description (--file)")
213
214if not opts.master:
215    sys.exit("Must specify a master testbed (--master)")
216
217
218context = None
219while context == None:
220    try:
221        context = fedd_ssl_context(cert, opts.trusted)
222    except SSL.SSLError, e:
223        # Yes, doing this on message type is not ideal.  The string comes from
224        # OpenSSL, so check there is this stops working.
225        if str(e) == "bad decrypt": 
226            print >>sys.stderr, "Bad Passphrase given."
227        else: raise
228
229msg = {
230        'experimentdescription': exp_desc,
231        'master': opts.master,
232        'user' : [ {\
233                'userID': pack_id(user), \
234                'access': [ { a.type: a.buf } for a in access_keys]\
235                } ]
236        }
237
238if opts.debug > 1: print >>sys.stderr, msg
239
240if opts.transport == "soap":
241    loc = feddServiceLocator();
242    port = loc.getfeddPortType(opts.url,
243            transport=M2Crypto.httpslib.HTTPSConnection, 
244            transdict={ 'ssl_context' : context },
245            tracefile=opts.tracefile)
246
247    req = CreateRequestMessage()
248
249    req.set_element_CreateRequestBody(
250            pack_soap(req, "CreateRequestBody", msg))
251
252    if opts.serialize_only:
253        sw = SoapWriter()
254        sw.serialize(req)
255        print str(sw)
256        sys.exit(0)
257
258    try:
259        resp = port.Create(req)
260    except ZSI.ParseException, e:
261        sys.exit("Malformed response (XMLPRC?): %s" % e)
262    except ZSI.FaultException, e:
263        resp = e.fault.detail[0]
264
265    if resp:
266        if 'get_element_CreateResponseBody' in dir(resp): 
267            resp_body = resp.get_element_CreateResponseBody()
268            if ( resp_body != None): 
269                try:
270                    resp_dict = unpack_soap(resp_body)
271                    if opts.debug > 1: print >>sys.stderr, resp_dict
272                except RuntimeError, e:
273                    sys.exit("Bad response. %s" % e.message)
274        elif 'get_element_FeddFaultBody' in dir(resp): 
275            resp_body = resp.get_element_FeddFaultBody()
276            if resp_body != None: 
277                exit_with_fault(unpack_soap(resp_body))
278        else: sys.exit("No body in response!?")
279    else: sys.exit("No response?!?")
280elif opts.transport == "xmlrpc":
281    if opts.serialize_only:
282        ser = dumps((msg,))
283        print ser
284        sys.exit(0)
285
286    transport = SSL_Transport(context)
287    port = ServerProxy(opts.url, transport=transport)
288
289    try:
290        resp = port.RequestAccess({ 'CreateRequestBody': msg})
291        resp, method = loads(resp)
292        resp = resp[0]
293    except Error, e:
294        resp = { 'FeddFaultBody': \
295                { 'errstr' : e.faultCode, 'desc' : e.faultString } }
296    if resp:
297        if resp.has_key('CreateResponseBody'): 
298            try:
299                resp_dict = resp['CreateResponseBody']
300                if opts.debug > 1: print >>sys.stderr, resp_dict
301            except RuntimeError, e:
302                sys.exit("Bad response. %s" % e.messgae)
303        elif resp.has_key('FeddFaultBody'):
304            exit_with_fault(resp['FeddFaultBody'])
305        else: sys.exit("No body in response!?")
306
307    else: sys.exit("No response?!?")
Note: See TracBrowser for help on using the repository browser.