source: fedd/fedd_create.py @ ccfc03d

Last change on this file since ccfc03d was c259a77, checked in by Ted Faber <faber@…>, 10 years ago

Service info in xml files.

  • Property mode set to 100755
File size: 9.6 KB
RevLine 
[2e46f35]1#!/usr/bin/env python
[d743d60]2
[6e63513]3import sys, os
[b10375f]4import re
[6e63513]5import subprocess
[d743d60]6
[c573278]7import ABAC
8
[990b746]9from string import join, ascii_letters
10from random import choice
[6e63513]11
[6bedbdba]12from deter import topdl
13from deter import fedid, generate_fedid
14
[e83f2f2]15from federation.proof import proof
[d743d60]16from federation.remote_service import service_caller
17from federation.client_lib import client_opts, exit_with_fault, RPCException, \
[6e63513]18        wrangle_standard_options, do_rpc, get_experiment_names, save_certfile,\
[c259a77]19        get_abac_certs, log_authentication, parse_service,\
20        service_dict_to_line, ns_service_re, extract_services_from_xml
[c573278]21from federation.util import abac_split_cert, abac_context_to_creds
[a7c0bcb]22
23from xml.parsers.expat import ExpatError
[d743d60]24
25class fedd_create_opts(client_opts):
26    """
27    Extra options that create needs.  Help entries should explain them.
28    """
29    def __init__(self):
30        client_opts.__init__(self)
31        self.add_option("--experiment_cert", dest="out_certfile",
[62f3dd9]32                action="callback", callback=self.expand_file,
[d743d60]33                type="string", help="output certificate file")
34        self.add_option("--experiment_name", dest="exp_name",
35                type="string", help="Suggested experiment name")
[62f3dd9]36        self.add_option("--file", dest="file", action="callback",
37                callback=self.expand_file, type="str",
[d743d60]38                help="experiment description file")
39        self.add_option("--project", action="store", dest="project", 
40                type="string",
41                help="Project to export from master")
42        self.add_option("--master", dest="master",
43                help="Master testbed in the federation (pseudo project export)")
44        self.add_option("--service", dest="service", action="append",
45                type="string", default=[],
46                help="Service description name:exporters:importers:attrs")
[fd07c48]47        self.add_option("--map", dest="map", action="append",
48                type="string", default=[],
49                help="Explicit map from testbed label to URI - " + \
50                        "deter:https://users.isi.deterlab/net:13232")
[353db8c]51        self.add_option('--gen_cert', action='store_true', dest='gen_cert',
52                default=False,
53                help='generate a cert to which to delegate rights')
54        self.add_option('--delegate', action='store_true', dest='generate',
55                help='Delegate rights to a generated cert (default)')
56        self.add_option('--no-delegate', action='store_true', dest='generate',
57                help='Do not delegate rights to a generated cert')
58
[6e63513]59        self.set_defaults(delegate=True)
[d743d60]60
61def project_export_service(master, project):
62    """
63    Output the dict representation of a project_export service for this project
64    and master.
65    """
66    return {
67            'name': 'project_export', 
68            'export': [master], 
69            'importall': True,
70            'fedAttr': [ 
71                { 'attribute': 'project', 'value': project },
72                ],
73            }
74
[65f6442]75def delegate(fedid, cert, dir, name=None, debug=False):
[353db8c]76    '''
77    Make the creddy call to create an attribute delegating rights to the new
78    experiment.  The cert parameter points to a conventional cert & key combo,
79    which we split out into tempfiles, which we delete on return.  The return
80    value if the filename in which the certificate was stored.
81    '''
82    certfile = keyfile = None
83    expid = "%s" % fedid
84
85    # Trim the "fedid:"
86    if expid.startswith("fedid:"): expid = expid[6:]
87
88    try:
[6e63513]89        keyfile, certfile = abac_split_cert(cert)
[353db8c]90
91        rv = 0
[c573278]92        if name: 
93            fn ='%s/%s_attr.der' % (dir, name) 
94            id_fn = '%s/%s_id.pem' % (dir, name)
95        else: 
96            fn = '%s/%s_attr.der' % (dir, expid)
97            id_fn = '%s/%s_id.pem' % (dir, expid)
[353db8c]98
[65f6442]99        try:
[67fa1cf]100            cid = ABAC.ID(certfile)
[65f6442]101            cid.load_privkey(keyfile)
[67fa1cf]102            cattr = ABAC.Attribute(cid, 'acting_for', 3600 * 24 * 365 * 10)
[65f6442]103            cattr.principal("%s" % expid)
104            cattr.bake()
[67fa1cf]105            cattr.write_file(fn)
[65f6442]106        except RuntimeError:
107            print >>sys.stderr, "Cannot create ABAC delegation. " + \
108                    "Did you run cert_to_fedid.py on your X.509 cert?"
[c573278]109            return []
110
111        context = ABAC.Context()
112        if context.load_id_file(certfile) != ABAC.ABAC_CERT_SUCCESS or \
113                context.load_attribute_file(fn) != ABAC.ABAC_CERT_SUCCESS:
[65f6442]114            print >>sys.stderr, "Cannot load delegation into ABAC context. " + \
[66879a0]115                    "Did you run cert_to_fedid.py on your X.509 cert?"
[c573278]116            return []
117        ids, attrs = abac_context_to_creds(context)
118
119        return ids + attrs
[6e63513]120
121
[353db8c]122    finally:
123        if keyfile: os.unlink(keyfile)
124        if certfile: os.unlink(certfile)
125
[6e63513]126
[d743d60]127# Main line
128parser = fedd_create_opts()
129(opts, args) = parser.parse_args()
130
[cd60510]131svcs = []
[353db8c]132# Option processing
[a0c2866]133try:
134    cert, fid, url = wrangle_standard_options(opts)
135except RuntimeError, e:
136    sys.exit("%s" %e)
[d743d60]137
138if opts.file:
[a7c0bcb]139
140    top = None
141    # Try the file as a topdl description
[d743d60]142    try:
[a7c0bcb]143        top = topdl.topology_from_xml(filename=opts.file, top='experiment')
[c259a77]144        # Pull any service descriptions from the file as well
145        svcs = extract_services_from_xml(filename=opts.file)
[d743d60]146    except EnvironmentError:
[a7c0bcb]147        # Can't read the file, fail now
[d743d60]148        sys.exit("Cannot read description file (%s)" %opts.file)
[a7c0bcb]149    except ExpatError:
150        # The file is not topdl, fall and assume it's ns2
151        pass
152
153    if top is None:
154        try:
155            lines = [ line for line in open(opts.file, 'r')]
156            exp_desc = "".join(lines)
157            # Parse all the strings that we can pull out of the file using the
158            # service_re.
[c259a77]159            svcs.extend([parse_service(ns_service_re.match(l).group(1)) \
160                    for l in lines if ns_service_re.match(l)])
[a7c0bcb]161        except EnvironmentError:
162            sys.exit("Cannot read description file (%s)" %opts.file)
[d743d60]163else:
164    sys.exit("Must specify an experiment description (--file)")
165
166out_certfile = opts.out_certfile
167
[353db8c]168# If there is no abac directory in which to store a delegated credential, don't
169# delegate.
170if not opts.abac_dir and opts.delegate:
171    opts.delegate = False
172
173# Load ABAC certs
174if opts.abac_dir:
175    try:
176        acerts = get_abac_certs(opts.abac_dir)
177    except EnvironmentError, e:
178        sys.exit('%s: %s' % (e.filename, e.strerror))
[725c55d]179else:
180    acerts = None
[353db8c]181
[d743d60]182# Fill in services
183if opts.master and opts.project:
184    svcs.append(project_export_service(opts.master, opts.project))
185svcs.extend([ parse_service(s) for s in opts.service])
[353db8c]186
[fd07c48]187# Create a testbed map if one is specified
188tbmap = { }
189for m in opts.map:
190    i = m.find(":")
191    if i != -1: tbmap[m[0:i]] = m[i+1:]
192    else: sys.exit("Bad mapping argument: %s" %m )
193
194
[d743d60]195if not svcs:
196    print >>sys.stderr, "Warning:Neither master/project nor services requested"
197
[353db8c]198# Construct the New experiment request
[d743d60]199msg = { }
200
[353db8c]201
202# Generate a certificate if requested and put it into the message
203if opts.gen_cert:
204    expid, expcert = generate_fedid(opts.exp_name or 'dummy')
205    msg['experimentAccess'] = { 'X509': expcert }
206else:
207    expid = expcert = None
208
[d743d60]209if opts.exp_name:
210    msg['experimentID'] = { 'localname': opts.exp_name }
211
[353db8c]212if acerts:
213    msg['credential'] = acerts
214
[990b746]215# ZSI will not properly construct an empty message.  If nothing has been added
216# to msg, pick a random localname to ensure the message is created
217if not msg:
218    msg['experimentID'] = { 
219            'localname': join([choice(ascii_letters) for i in range(0,8)],""),
220            }
221
222
[d743d60]223if opts.debug > 1: print >>sys.stderr, msg
224
[353db8c]225# The New call
[d743d60]226try:
227    resp_dict = do_rpc(msg, 
[5d854e1]228            url, opts.transport, cert, opts.trusted, 
[d743d60]229            serialize_only=opts.serialize_only,
230            tracefile=opts.tracefile,
231            caller=service_caller('New'), responseBody="NewResponseBody")
232except RPCException, e:
[e83f2f2]233    exit_with_fault(e, 'New (create)', opts)
[d743d60]234except RuntimeError, e:
235    sys.exit("Error processing RPC: %s" % e)
236
237if opts.debug > 1: print >>sys.stderr, resp_dict
238
[7ec0dc2]239p = proof.from_dict(resp_dict.get('proof', {}))
240if p and opts.auth_log:
241    log_authentication(opts.auth_log, 'New (create)', 'succeeded', p)
[353db8c]242# Save the experiment ID certificate if we need it
[d743d60]243try:
244    save_certfile(opts.out_certfile, resp_dict.get('experimentAccess', None))
245except EnvironmentError, e:
246    sys.exit('Could not write to %s: %s' %  (opts.out_certfile, e))
247
248e_fedid, e_local = get_experiment_names(resp_dict.get('experimentID', None))
249if not e_fedid and not e_local and opts.serialize_only:
250    e_local = "serialize"
251
[353db8c]252# If delegation is requested and we have a target, make the delegation, and add
253# the credential to acerts.
254if e_fedid and opts.delegate:
[6e63513]255    try:
[c573278]256        creds = delegate(e_fedid, cert, opts.abac_dir, name=opts.exp_name)
257        if creds:
258            acerts.extend(creds)
[6e63513]259    except EnvironmentError, e:
260        sys.exit("Cannot delegate rights %s: %s" % (e.filename, e.strerror));
[353db8c]261
262# Construct the Create message
[a7c0bcb]263if top: 
264    msg = { 'experimentdescription' : { 'topdldescription': top.to_dict() }, }
265else: 
266    msg = { 'experimentdescription': { 'ns2description': exp_desc }, }
[d743d60]267
268if svcs:
269    msg['service'] = svcs
270
271if e_fedid:
272    msg['experimentID'] = { 'fedid': e_fedid }
273elif e_local:
274    msg['experimentID'] = { 'localname': e_local }
275else:
276    sys.exit("New did not return an experiment ID??")
277
[353db8c]278if acerts:
279    msg['credential'] = acerts
280
[fd07c48]281if tbmap:
[a11eda5]282    msg['testbedmap'] = [ 
283            { 
284                'testbed': t, 
285                'uri': u,
286            } for t, u in tbmap.items()
287        ]
[fd07c48]288
[d743d60]289if opts.debug > 1: print >>sys.stderr, msg
290
[353db8c]291# make the call
[d743d60]292try:
293    resp_dict = do_rpc(msg, 
[5d854e1]294            url, opts.transport, cert, opts.trusted, 
[d743d60]295            serialize_only=opts.serialize_only,
296            tracefile=opts.tracefile,
[e83f2f2]297            caller=service_caller('Create', max_retries=1), responseBody="CreateResponseBody")
[d743d60]298except RPCException, e:
[e83f2f2]299    exit_with_fault(e, 'Create', opts)
[d743d60]300except RuntimeError, e:
301    sys.exit("Error processing RPC: %s" % e)
302
303if opts.debug > 1: print >>sys.stderr, resp_dict
304
[353db8c]305# output
[d743d60]306e_fedid, e_local = get_experiment_names(resp_dict.get('experimentID', None))
307st = resp_dict.get('experimentStatus', None)
308
309if e_local: print "localname: %s" % e_local
310if e_fedid: print "fedid: %s" % e_fedid
311if st: print "status: %s" % st
[7ec0dc2]312#proof = proof.from_dict(resp_dict.get('proof', {}))
313p_list = resp_dict.get('proof', {})
314if p_list and opts.auth_log:
315    for p in p_list:
316        log_authentication(opts.auth_log, 'Create', 'succeeded', 
317                proof.from_dict(p))
Note: See TracBrowser for help on using the repository browser.