source: fedd/fedd_create.py @ c573278

axis_examplecompt_changesinfo-ops
Last change on this file since c573278 was c573278, checked in by Ted Faber <faber@…>, 13 years ago

Checkpoint. Still lots to do

  • Property mode set to 100755
File size: 8.8 KB
RevLine 
[d743d60]1#!/usr/local/bin/python
2
[6e63513]3import sys, os
[b10375f]4import re
[6e63513]5import subprocess
[d743d60]6
[c573278]7import ABAC
8
[6e63513]9from string import join
10
11from federation.fedid import fedid, generate_fedid
[d743d60]12from federation.remote_service import service_caller
13from federation.client_lib import client_opts, exit_with_fault, RPCException, \
[6e63513]14        wrangle_standard_options, do_rpc, get_experiment_names, save_certfile,\
[353db8c]15        get_abac_certs
[c573278]16from federation.util import abac_split_cert, abac_context_to_creds
[d743d60]17
18class fedd_create_opts(client_opts):
19    """
20    Extra options that create needs.  Help entries should explain them.
21    """
22    def __init__(self):
23        client_opts.__init__(self)
24        self.add_option("--experiment_cert", dest="out_certfile",
25                type="string", help="output certificate file")
26        self.add_option("--experiment_name", dest="exp_name",
27                type="string", help="Suggested experiment name")
28        self.add_option("--file", dest="file", 
29                help="experiment description file")
30        self.add_option("--project", action="store", dest="project", 
31                type="string",
32                help="Project to export from master")
33        self.add_option("--master", dest="master",
34                help="Master testbed in the federation (pseudo project export)")
35        self.add_option("--service", dest="service", action="append",
36                type="string", default=[],
37                help="Service description name:exporters:importers:attrs")
[fd07c48]38        self.add_option("--map", dest="map", action="append",
39                type="string", default=[],
40                help="Explicit map from testbed label to URI - " + \
41                        "deter:https://users.isi.deterlab/net:13232")
[353db8c]42        self.add_option('--gen_cert', action='store_true', dest='gen_cert',
43                default=False,
44                help='generate a cert to which to delegate rights')
45        self.add_option('--delegate', action='store_true', dest='generate',
46                help='Delegate rights to a generated cert (default)')
47        self.add_option('--no-delegate', action='store_true', dest='generate',
48                help='Do not delegate rights to a generated cert')
49
[6e63513]50        self.set_defaults(delegate=True)
[d743d60]51
52def parse_service(svc):
53    """
54    Pasre a service entry into a hash representing a service entry in a
55    message.  The format is:
56        svc_name:exporter(s):importer(s):attr=val,attr=val
57    The svc_name is teh service name, exporter is the exporting testbeds
58    (comma-separated) importer is the importing testbeds (if any) and the rest
59    are attr=val pairs that will become attributes of the service.  These
60    include parameters and other such stuff.
61    """
62
63    terms = svc.split(':')
64    svcd = { }
65    if len(terms) < 2 or len(terms[0]) == 0 or len(terms[1]) == 0:
66        sys.exit("Bad service description '%s': Not enough terms" % svc)
67   
68    svcd['name'] = terms[0]
69    svcd['export'] = terms[1].split(",")
70    if len(terms) > 2 and len(terms[2]) > 0:
71        svcd['import'] = terms[2].split(",")
72    if len(terms) > 3 and len(terms[3]) > 0:
73        svcd['fedAttr'] = [ ]
[0de1b94]74        for t in terms[3].split(";"):
[d743d60]75            i = t.find("=")
76            if i != -1 :
77                svcd['fedAttr'].append(
78                        {'attribute': t[0:i], 'value': t[i+1:]})
79            else:
80                sys.exit("Bad service attribute '%s': no equals sign" % t)
81    return svcd
82
83def project_export_service(master, project):
84    """
85    Output the dict representation of a project_export service for this project
86    and master.
87    """
88    return {
89            'name': 'project_export', 
90            'export': [master], 
91            'importall': True,
92            'fedAttr': [ 
93                { 'attribute': 'project', 'value': project },
94                ],
95            }
96
[353db8c]97def delegate(fedid, cert, dir, name=None, debug=False, 
98        creddy='/usr/local/bin/creddy'):
99    '''
100    Make the creddy call to create an attribute delegating rights to the new
101    experiment.  The cert parameter points to a conventional cert & key combo,
102    which we split out into tempfiles, which we delete on return.  The return
103    value if the filename in which the certificate was stored.
104    '''
105    certfile = keyfile = None
106    expid = "%s" % fedid
107
108    # Trim the "fedid:"
109    if expid.startswith("fedid:"): expid = expid[6:]
110
111    try:
[6e63513]112        keyfile, certfile = abac_split_cert(cert)
[353db8c]113
114        rv = 0
[c573278]115        if name: 
116            fn ='%s/%s_attr.der' % (dir, name) 
117            id_fn = '%s/%s_id.pem' % (dir, name)
118        else: 
119            fn = '%s/%s_attr.der' % (dir, expid)
120            id_fn = '%s/%s_id.pem' % (dir, expid)
[353db8c]121
122        cmd = [creddy, '--attribute', '--issuer=%s' % certfile, 
123                '--key=%s' % keyfile,
124                '--role=acting_for', '--subject-id=%s' % expid, 
125                '--out=%s' % fn ]
[6e63513]126        if not debug:
127            if subprocess.call(cmd) != 0:
[c573278]128                return []
[353db8c]129        else:
[6e63513]130            print join(cmd)
[c573278]131            return []
132
133        context = ABAC.Context()
134        if context.load_id_file(certfile) != ABAC.ABAC_CERT_SUCCESS or \
135                context.load_attribute_file(fn) != ABAC.ABAC_CERT_SUCCESS:
136            return []
137        ids, attrs = abac_context_to_creds(context)
138
139        return ids + attrs
[6e63513]140
141
[353db8c]142    finally:
143        if keyfile: os.unlink(keyfile)
144        if certfile: os.unlink(certfile)
145
[6e63513]146
[d743d60]147# Main line
[b10375f]148service_re = re.compile('^\\s*#\\s*SERVICE:\\s*([\\S]+)')
[d743d60]149parser = fedd_create_opts()
150(opts, args) = parser.parse_args()
151
[353db8c]152# Option processing
[5d854e1]153cert, fid, url = wrangle_standard_options(opts)
[d743d60]154
155if opts.file:
156    try:
[b10375f]157        lines = [ line for line in open(opts.file, 'r')]
158        exp_desc = "".join(lines)
[d743d60]159    except EnvironmentError:
160        sys.exit("Cannot read description file (%s)" %opts.file)
161else:
162    sys.exit("Must specify an experiment description (--file)")
163
164out_certfile = opts.out_certfile
165
[353db8c]166# If there is no abac directory in which to store a delegated credential, don't
167# delegate.
168if not opts.abac_dir and opts.delegate:
169    opts.delegate = False
170
171# Load ABAC certs
172if opts.abac_dir:
173    try:
174        acerts = get_abac_certs(opts.abac_dir)
175    except EnvironmentError, e:
176        sys.exit('%s: %s' % (e.filename, e.strerror))
177
[d743d60]178# Fill in services
179svcs = []
180if opts.master and opts.project:
181    svcs.append(project_export_service(opts.master, opts.project))
182svcs.extend([ parse_service(s) for s in opts.service])
[353db8c]183
[b10375f]184# Parse all the strings that we can pull out of the file using the service_re.
185svcs.extend([parse_service(service_re.match(l).group(1)) \
186        for l in lines if service_re.match(l)])
187
[fd07c48]188# Create a testbed map if one is specified
189tbmap = { }
190for m in opts.map:
191    i = m.find(":")
192    if i != -1: tbmap[m[0:i]] = m[i+1:]
193    else: sys.exit("Bad mapping argument: %s" %m )
194
195
[d743d60]196if not svcs:
197    print >>sys.stderr, "Warning:Neither master/project nor services requested"
198
[353db8c]199# Construct the New experiment request
[d743d60]200msg = { }
201
[353db8c]202
203# Generate a certificate if requested and put it into the message
204if opts.gen_cert:
205    expid, expcert = generate_fedid(opts.exp_name or 'dummy')
206    msg['experimentAccess'] = { 'X509': expcert }
207else:
208    expid = expcert = None
209
[d743d60]210if opts.exp_name:
211    msg['experimentID'] = { 'localname': opts.exp_name }
212
[353db8c]213if acerts:
214    msg['credential'] = acerts
215
[d743d60]216if opts.debug > 1: print >>sys.stderr, msg
217
[353db8c]218# The New call
[d743d60]219try:
220    resp_dict = do_rpc(msg, 
[5d854e1]221            url, opts.transport, cert, opts.trusted, 
[d743d60]222            serialize_only=opts.serialize_only,
223            tracefile=opts.tracefile,
224            caller=service_caller('New'), responseBody="NewResponseBody")
225except RPCException, e:
226    exit_with_fault(e)
227except RuntimeError, e:
228    sys.exit("Error processing RPC: %s" % e)
229
230if opts.debug > 1: print >>sys.stderr, resp_dict
231
[353db8c]232# Save the experiment ID certificate if we need it
[d743d60]233try:
234    save_certfile(opts.out_certfile, resp_dict.get('experimentAccess', None))
235except EnvironmentError, e:
236    sys.exit('Could not write to %s: %s' %  (opts.out_certfile, e))
237
238e_fedid, e_local = get_experiment_names(resp_dict.get('experimentID', None))
239if not e_fedid and not e_local and opts.serialize_only:
240    e_local = "serialize"
241
[353db8c]242# If delegation is requested and we have a target, make the delegation, and add
243# the credential to acerts.
244if e_fedid and opts.delegate:
[6e63513]245    try:
[c573278]246        creds = delegate(e_fedid, cert, opts.abac_dir, name=opts.exp_name)
247        if creds:
248            acerts.extend(creds)
[6e63513]249    except EnvironmentError, e:
250        sys.exit("Cannot delegate rights %s: %s" % (e.filename, e.strerror));
[353db8c]251
252# Construct the Create message
[d743d60]253msg = { 'experimentdescription': { 'ns2description': exp_desc }, }
254
255if svcs:
256    msg['service'] = svcs
257
258if e_fedid:
259    msg['experimentID'] = { 'fedid': e_fedid }
260elif e_local:
261    msg['experimentID'] = { 'localname': e_local }
262else:
263    sys.exit("New did not return an experiment ID??")
264
[353db8c]265if acerts:
266    msg['credential'] = acerts
267
[fd07c48]268if tbmap:
269    msg['testbedmap'] = [ { 'testbed': t, 'uri': u } for t, u in tbmap.items() ]
270
[d743d60]271if opts.debug > 1: print >>sys.stderr, msg
272
[353db8c]273# make the call
[d743d60]274try:
275    resp_dict = do_rpc(msg, 
[5d854e1]276            url, opts.transport, cert, opts.trusted, 
[d743d60]277            serialize_only=opts.serialize_only,
278            tracefile=opts.tracefile,
279            caller=service_caller('Create'), responseBody="CreateResponseBody")
280except RPCException, e:
281    exit_with_fault(e)
282except RuntimeError, e:
283    sys.exit("Error processing RPC: %s" % e)
284
285if opts.debug > 1: print >>sys.stderr, resp_dict
286
[353db8c]287# output
[d743d60]288e_fedid, e_local = get_experiment_names(resp_dict.get('experimentID', None))
289st = resp_dict.get('experimentStatus', None)
290
291if e_local: print "localname: %s" % e_local
292if e_fedid: print "fedid: %s" % e_fedid
293if st: print "status: %s" % st
294
Note: See TracBrowser for help on using the repository browser.