source: fedd/fedd_create.py @ a7c0bcb

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

Allow users to pass topdl to fedd_create.py. fedd_create autodetects and composes the proper message. The experiment controller now just takes the topology as given.

  • 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
155# Option processing
156cert, fid, url = wrangle_standard_options(opts)
157
158if opts.file:
159
160    top = None
161    # Try the file as a topdl description
162    try:
163        top = topdl.topology_from_xml(filename=opts.file, top='experiment')
164    except EnvironmentError:
165        # Can't read the file, fail now
166        sys.exit("Cannot read description file (%s)" %opts.file)
167    except ExpatError:
168        # The file is not topdl, fall and assume it's ns2
169        pass
170
171    if top is None:
172        try:
173            lines = [ line for line in open(opts.file, 'r')]
174            exp_desc = "".join(lines)
175            # Parse all the strings that we can pull out of the file using the
176            # service_re.
177            svcs.extend([parse_service(service_re.match(l).group(1)) \
178                    for l in lines if service_re.match(l)])
179        except EnvironmentError:
180            sys.exit("Cannot read description file (%s)" %opts.file)
181else:
182    sys.exit("Must specify an experiment description (--file)")
183
184out_certfile = opts.out_certfile
185
186# If there is no abac directory in which to store a delegated credential, don't
187# delegate.
188if not opts.abac_dir and opts.delegate:
189    opts.delegate = False
190
191# Load ABAC certs
192if opts.abac_dir:
193    try:
194        acerts = get_abac_certs(opts.abac_dir)
195    except EnvironmentError, e:
196        sys.exit('%s: %s' % (e.filename, e.strerror))
197else:
198    acerts = None
199
200# Fill in services
201svcs = []
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.