source: fedd/fedd_client.py @ 8f91e66

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

serialize only option

  • Property mode set to 100755
File size: 10.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
10import M2Crypto.httpslib
11from ZSI import SoapWriter
12
13from fedd_util import fedid, fedd_ssl_context, pack_soap, pack_id
14
15from optparse import OptionParser, OptionValueError
16
17# Turn off the matching of hostname to certificate ID
18SSL.Connection.clientPostConnectionCheck = None
19
20class IDFormatException(RuntimeError): pass
21
22class access_method:
23    """Encapsulates an access method generically."""
24    (type_ssh, type_x509, type_pgp) = ('sshPubkey', 'X509', 'pgpPubkey')
25    default_type = type_ssh
26    def __init__(self, buf=None, type=None, file=None):
27        self.buf = buf
28
29        if type != None: self.type = type
30        else: self.type = access_method.default_type
31
32        if file != None:
33            self.readfile(file)
34   
35    def readfile(self, file, type=None):
36        f = open(file, "r")
37        self.buf = f.read();
38        f.close()
39        if type == None:
40            if self.type == None:
41                self.type = access_method.default_type
42        else:
43            self.type = type;
44   
45class node_desc:
46    def __init__(self, image, hardware, count=1):
47        if getattr(image, "__iter__", None) == None:
48            if image == None: self.image = [ ]
49            else: self.image = [ image ]
50        else:
51            self.image = image
52
53        if getattr(hardware, "__iter__", None) == None: 
54            if hardware == None: self.hardware = [ ]
55            else: self.hardware = [ hardware ]
56        else:
57            self.hardware = hardware
58        if count != None: self.count = int(count)
59        else: self.count = 1
60
61class fedd_client_opts(OptionParser):
62    """Encapsulate option processing in this class, rather than in main"""
63    def __init__(self):
64        OptionParser.__init__(self, usage="%prog [opts] (--help for details)",
65                version="0.1")
66
67        self.set_defaults(url="https://localhost:23235", anonymous=False,
68                serialize_only=False, use_fedid=False, debug=0)
69
70        self.add_option("-a","--anonymous", action="store_true",
71                dest="anonymous", help="Do not include a user in the request")
72        self.add_option("-c","--cert", action="store", dest="cert",
73                type="string", help="my certificate file")
74        self.add_option("-d", "--debug", action="count", dest="debug", 
75                help="Set debug.  Repeat for more information")
76        self.add_option("-f","--useFedid", action="store_true",
77                dest="use_fedid",
78                help="Use a fedid derived from my certificate as user identity")
79        self.add_option("-k", "--sshKey", action="callback", type="string", 
80                callback=add_ssh_key, callback_args=(access_keys,),
81                help="ssh key for access (can be supplied more than once")
82        self.add_option("-K", "--x509Key", action="callback", type="string", 
83                callback=add_x509_cert, callback_args=(access_keys,),
84                help="X509 certificate for access " + \
85                        "(can be supplied more than once")
86        self.add_option("-l","--label", action="store", dest="label",
87                type="string", help="Label for output")
88        self.add_option("-n", "--node", action="callback", type="string", 
89                callback=add_node_desc, callback_args=(node_descs,),
90                help="Node description: image:hardware[:count]")
91        self.add_option("-p", "--project", action="store", dest="project", 
92                type="string",
93                help="Use a project request with this project name")
94        self.add_option("-s", "--serializeOnly", action="store_true", 
95                dest="serialize_only",
96                help="Print the SOAP request that would be sent and exit")
97        self.add_option("-t", "--testbed", action="store", dest="testbed",
98                type="string",
99                help="Testbed identifier (URI) to contact (required)")
100        self.add_option("-T","--trusted", action="store", dest="trusted",
101                type="string", help="Trusted certificates (required)")
102        self.add_option("-u", "--url", action="store", dest="url",
103                type="string",
104                help="URL to connect to (default %default)")
105        self.add_option("-U", "--username", action="store", dest="user",
106                type="string", help="Use this username instead of the uid")
107        self.add_option("--trace", action="store_const", dest="tracefile", 
108                const=sys.stderr, help="Print SOAP exchange to stderr")
109
110
111
112def print_ID(id, out=sys.stdout, prefix=None, no_nl=False):
113    """Print an ID element (which may have many types) to as a string
114   
115    If out is given, that is the output destination. If prefix is given that
116    string is prepended.  If no_nl is given, the newline is not printed.
117    """
118    # This comprehension selects all accessors from id of the form
119    # get_element_*, calls those in turn and returns a list of non-None ones.
120    # For valid ID elements that will contain exactly one element, which we
121    # print.  Anything else is an error.
122    names = [ getattr(id, method)() for method in dir(id) \
123                    if method.startswith("get_element_") \
124                        and callable(getattr(id, method)) 
125                        and getattr(id,method)() != None ]
126    if len(names) == 1:
127        if prefix == None: print >>out, names[0],
128        else: print >>out, "%s%s" % (prefix,names[0]),
129        if not no_nl: print >>out
130    else:
131        raise IDFormatException()
132
133
134def print_users(ul, out=sys.stdout):
135    """
136    Print a list of users, with "User: " prepended, one to a line on out.
137   
138    @param ul sequence of user elements
139    @param out output object.  Defaults to sys.stdout
140    """
141    if ul != None:
142        for u in ul:
143            print_ID(u.get_element_userID(), out, "User: ")
144
145
146def print_response_as_testbed(resp, label, out=sys.stdout):
147    """Print the response as input to the splitter script"""
148    emulab_data = { 
149            "Boss: ": "get_element_boss",
150            "OpsNode: ": "get_element_ops",
151            "Domain: ": "get_element_domain",
152            "FileServer: ": "get_element_fileServer",
153            "EventServer: ": "get_element_eventServer",
154            }
155    if (label != None): print >> out, "[%s]" % label
156    e = resp.get_element_emulab()
157    if ( e != None):
158        p = e.get_element_project()
159        if ( p != None ):
160            print_ID(p.get_element_name(), out, "Project: ")
161            print_users(p.get_element_user(), out)
162        for k, m in emulab_data.iteritems():
163            method = getattr(e, m);
164            if method != None and callable(method):
165                print >> out, "%s%s" % (k, method())
166        for a in e.get_element_fedAttr():
167            print >>out, "%s: %s" % \
168                    (a.get_element_attribute(), a.get_element_value())
169def add_ssh_key(option, opt_str, value, parser, access_keys):
170    try:
171        access_keys.append(access_method(file=value,
172            type=access_method.type_ssh))
173    except IOError, (errno, strerror):
174        raise OptionValueError("Cannot generate sshPubkey from %s: %s (%d)" %
175                (value,strerror,errno))
176
177def add_x509_cert(option, opt_str, value, parser, access_keys):
178    try:
179        access_keys.append(access_method(file=value,
180            type=access_method.type_x509))
181    except IOError, (errno, strerror):
182        raise OptionValueError("Cannot read x509 cert from %s: %s (%d)" %
183                (value,strerror,errno))
184
185def add_node_desc(option, opt_str, value, parser, node_descs):
186    def none_if_zero(x):
187        if len(x) > 0: return x
188        else: return None
189
190    params = map(none_if_zero, value.split(":"));
191   
192    if len(params) < 4 and len(params) > 1:
193        node_descs.append(node_desc(*params))
194    else:
195        raise OptionValueError("Bad node description: %s" % value)
196
197def get_user_info(access_keys):
198    pw = pwd.getpwuid(os.getuid());
199    try_cert=None
200    user = None
201
202    if pw != None:
203        user = pw[0]
204        try_cert = "%s/.ssl/emulab.pem" % pw[5];
205        if not os.access(try_cert, os.R_OK):
206            try_cert = None
207        if len(access_keys) == 0:
208            for k in ["%s/.ssh/id_rsa.pub", "%s/.ssh/id_dsa.pub", 
209                    "%s/.ssh/identity.pub"]:
210                try_key = k % pw[5];
211                if os.access(try_key, os.R_OK):
212                    access_keys.append(access_method(file=try_key,
213                        type=access_method.type_ssh))
214                    break
215    return (user, try_cert)
216
217access_keys = []
218node_descs = []
219proj = None
220
221# Process the options using the customized option parser defined above
222(opts, args) = fedd_client_opts().parse_args()
223
224if opts.testbed == None:
225    parser.error("--testbed is required")
226
227if opts.trusted != None:
228    if ( not os.access(opts.trusted, os.R_OK) ) :
229        sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
230else:
231    parser.error("--trusted is required")
232
233if opts.debug > 0: opts.tracefile=sys.stderr
234
235(user, cert) = get_user_info(access_keys)
236
237if opts.user: user = opts.user
238
239if opts.cert != None: cert = opts.cert
240
241if cert == None:
242    sys.exit("No certificate given (--cert) or found")
243
244if os.access(cert, os.R_OK):
245    fid = fedid(file=cert)
246    if opts.use_fedid == True:
247        user = fid
248else:
249    sys.exit("Cannot read certificate (%s)" % cert)
250
251context = None
252while context == None:
253    try:
254        context = fedd_ssl_context(cert, opts.trusted)
255    except SSL.SSLError, e:
256        # Yes, doing this on message type is not ideal.  The string comes from
257        # OpenSSL, so check there is this stops working.
258        if e.message == "bad decrypt": 
259            print >>sys.stderr, "Bad Passphrase given."
260        else: raise
261
262loc = feddServiceLocator();
263port = loc.getfeddPortType(opts.url,
264        transport=M2Crypto.httpslib.HTTPSConnection, 
265        transdict={ 'ssl_context' : context },
266        tracefile=opts.tracefile)
267
268req = RequestAccessRequestMessage()
269
270msg = {
271        'allocID': pack_id('test alloc'),
272        'destinationTestbed': pack_id(opts.testbed),
273        'access' : [ { a.type: a.buf } for a in access_keys ],
274        }
275
276if len(node_descs) > 0:
277    msg['resources'] = { 
278            'node': [ 
279                { 
280                    'image':  n.image ,
281                    'hardware':  n.hardware,
282                    'count': n.count,
283                } for n in node_descs],
284            }
285
286if opts.project != None:
287    if not opts.anonymous and user != None:
288        msg['project'] = {
289                'name': pack_id(opts.project),
290                'user': [ { 'userID': pack_id(user) } ],
291                }
292    else:
293        msg['project'] = { 'name': pack_id(opts.project) }
294else:
295    if not opts.anonymous and user != None:
296        msg['user'] = [ { 'userID': pack_id(user) } ]
297    else:
298        msg['user'] = [];
299
300req.set_element_RequestAccessRequestBody(
301        pack_soap(req, "RequestAccessRequestBody", msg))
302
303if opts.debug > 1:
304    print msg
305
306if opts.serialize_only:
307    sw = SoapWriter()
308    sw.serialize(req)
309    print str(sw)
310    sys.exit(0)
311
312try:
313    resp = port.RequestAccess(req)
314except ZSI.FaultException, e:
315    sys.exit("Fault: %s" % e)
316
317if (resp != None):
318    resp_body = resp.get_element_RequestAccessResponseBody()
319    if ( resp_body != None): 
320        try:
321            print_response_as_testbed(resp_body, opts.label)
322        except RuntimeError, e:
323            sys.exit("Bad response. %s" % e.messgae)
324    else: sys.exit("No body in resonpse!?")
325else: sys.exit("No response?!?")
Note: See TracBrowser for help on using the repository browser.