source: fedd/fedd_create.py @ cd60510

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

Use before init error.

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