source: fedd/fedd_client.py @ 2106ed1

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

check password failures. Exception doesn_t have a message attribute under python 2.4

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