source: fedd/fedd_create.py @ 725c55d

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

Checkpoint - successful swap in and out

  • Property mode set to 100755
File size: 8.9 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))
[725c55d]177else:
178    acerts = None
[353db8c]179
[d743d60]180# Fill in services
181svcs = []
182if opts.master and opts.project:
183    svcs.append(project_export_service(opts.master, opts.project))
184svcs.extend([ parse_service(s) for s in opts.service])
[353db8c]185
[b10375f]186# Parse all the strings that we can pull out of the file using the service_re.
187svcs.extend([parse_service(service_re.match(l).group(1)) \
188        for l in lines if service_re.match(l)])
189
[fd07c48]190# Create a testbed map if one is specified
191tbmap = { }
192for m in opts.map:
193    i = m.find(":")
194    if i != -1: tbmap[m[0:i]] = m[i+1:]
195    else: sys.exit("Bad mapping argument: %s" %m )
196
197
[d743d60]198if not svcs:
199    print >>sys.stderr, "Warning:Neither master/project nor services requested"
200
[353db8c]201# Construct the New experiment request
[d743d60]202msg = { }
203
[353db8c]204
205# Generate a certificate if requested and put it into the message
206if opts.gen_cert:
207    expid, expcert = generate_fedid(opts.exp_name or 'dummy')
208    msg['experimentAccess'] = { 'X509': expcert }
209else:
210    expid = expcert = None
211
[d743d60]212if opts.exp_name:
213    msg['experimentID'] = { 'localname': opts.exp_name }
214
[353db8c]215if acerts:
216    msg['credential'] = acerts
217
[d743d60]218if opts.debug > 1: print >>sys.stderr, msg
219
[353db8c]220# The New call
[d743d60]221try:
222    resp_dict = do_rpc(msg, 
[5d854e1]223            url, opts.transport, cert, opts.trusted, 
[d743d60]224            serialize_only=opts.serialize_only,
225            tracefile=opts.tracefile,
226            caller=service_caller('New'), responseBody="NewResponseBody")
227except RPCException, e:
228    exit_with_fault(e)
229except RuntimeError, e:
230    sys.exit("Error processing RPC: %s" % e)
231
232if opts.debug > 1: print >>sys.stderr, resp_dict
233
[353db8c]234# Save the experiment ID certificate if we need it
[d743d60]235try:
236    save_certfile(opts.out_certfile, resp_dict.get('experimentAccess', None))
237except EnvironmentError, e:
238    sys.exit('Could not write to %s: %s' %  (opts.out_certfile, e))
239
240e_fedid, e_local = get_experiment_names(resp_dict.get('experimentID', None))
241if not e_fedid and not e_local and opts.serialize_only:
242    e_local = "serialize"
243
[353db8c]244# If delegation is requested and we have a target, make the delegation, and add
245# the credential to acerts.
246if e_fedid and opts.delegate:
[6e63513]247    try:
[c573278]248        creds = delegate(e_fedid, cert, opts.abac_dir, name=opts.exp_name)
249        if creds:
250            acerts.extend(creds)
[6e63513]251    except EnvironmentError, e:
252        sys.exit("Cannot delegate rights %s: %s" % (e.filename, e.strerror));
[353db8c]253
254# Construct the Create message
[d743d60]255msg = { 'experimentdescription': { 'ns2description': exp_desc }, }
256
257if svcs:
258    msg['service'] = svcs
259
260if e_fedid:
261    msg['experimentID'] = { 'fedid': e_fedid }
262elif e_local:
263    msg['experimentID'] = { 'localname': e_local }
264else:
265    sys.exit("New did not return an experiment ID??")
266
[353db8c]267if acerts:
268    msg['credential'] = acerts
269
[fd07c48]270if tbmap:
271    msg['testbedmap'] = [ { 'testbed': t, 'uri': u } for t, u in tbmap.items() ]
272
[d743d60]273if opts.debug > 1: print >>sys.stderr, msg
274
[353db8c]275# make the call
[d743d60]276try:
277    resp_dict = do_rpc(msg, 
[5d854e1]278            url, opts.transport, cert, opts.trusted, 
[d743d60]279            serialize_only=opts.serialize_only,
280            tracefile=opts.tracefile,
281            caller=service_caller('Create'), responseBody="CreateResponseBody")
282except RPCException, e:
283    exit_with_fault(e)
284except RuntimeError, e:
285    sys.exit("Error processing RPC: %s" % e)
286
287if opts.debug > 1: print >>sys.stderr, resp_dict
288
[353db8c]289# output
[d743d60]290e_fedid, e_local = get_experiment_names(resp_dict.get('experimentID', None))
291st = resp_dict.get('experimentStatus', None)
292
293if e_local: print "localname: %s" % e_local
294if e_fedid: print "fedid: %s" % e_fedid
295if st: print "status: %s" % st
296
Note: See TracBrowser for help on using the repository browser.