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
Line 
1#!/usr/local/bin/python
2
3import sys, os
4import re
5import subprocess
6
7import ABAC
8
9from string import join
10
11from federation.fedid import fedid, generate_fedid
12from federation.remote_service import service_caller
13from federation.client_lib import client_opts, exit_with_fault, RPCException, \
14        wrangle_standard_options, do_rpc, get_experiment_names, save_certfile,\
15        get_abac_certs
16from federation.util import abac_split_cert, abac_context_to_creds
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")
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")
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
50        self.set_defaults(delegate=True)
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'] = [ ]
74        for t in terms[3].split(";"):
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
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:
112        keyfile, certfile = abac_split_cert(cert)
113
114        rv = 0
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)
121
122        cmd = [creddy, '--attribute', '--issuer=%s' % certfile, 
123                '--key=%s' % keyfile,
124                '--role=acting_for', '--subject-id=%s' % expid, 
125                '--out=%s' % fn ]
126        if not debug:
127            if subprocess.call(cmd) != 0:
128                return []
129        else:
130            print join(cmd)
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
140
141
142    finally:
143        if keyfile: os.unlink(keyfile)
144        if certfile: os.unlink(certfile)
145
146
147# Main line
148service_re = re.compile('^\\s*#\\s*SERVICE:\\s*([\\S]+)')
149parser = fedd_create_opts()
150(opts, args) = parser.parse_args()
151
152# Option processing
153cert, fid, url = wrangle_standard_options(opts)
154
155if opts.file:
156    try:
157        lines = [ line for line in open(opts.file, 'r')]
158        exp_desc = "".join(lines)
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
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
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])
183
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
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
196if not svcs:
197    print >>sys.stderr, "Warning:Neither master/project nor services requested"
198
199# Construct the New experiment request
200msg = { }
201
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
210if opts.exp_name:
211    msg['experimentID'] = { 'localname': opts.exp_name }
212
213if acerts:
214    msg['credential'] = acerts
215
216if opts.debug > 1: print >>sys.stderr, msg
217
218# The New call
219try:
220    resp_dict = do_rpc(msg, 
221            url, opts.transport, cert, opts.trusted, 
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
232# Save the experiment ID certificate if we need it
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
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:
245    try:
246        creds = delegate(e_fedid, cert, opts.abac_dir, name=opts.exp_name)
247        if creds:
248            acerts.extend(creds)
249    except EnvironmentError, e:
250        sys.exit("Cannot delegate rights %s: %s" % (e.filename, e.strerror));
251
252# Construct the Create message
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
265if acerts:
266    msg['credential'] = acerts
267
268if tbmap:
269    msg['testbedmap'] = [ { 'testbed': t, 'uri': u } for t, u in tbmap.items() ]
270
271if opts.debug > 1: print >>sys.stderr, msg
272
273# make the call
274try:
275    resp_dict = do_rpc(msg, 
276            url, opts.transport, cert, opts.trusted, 
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
287# output
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.