source: fedd/fedd_create.py @ 61a634d

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

Checkpoint - successful swap in and out

  • Property mode set to 100755
File size: 8.9 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))
177else:
178    acerts = None
179
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])
185
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
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
198if not svcs:
199    print >>sys.stderr, "Warning:Neither master/project nor services requested"
200
201# Construct the New experiment request
202msg = { }
203
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
212if opts.exp_name:
213    msg['experimentID'] = { 'localname': opts.exp_name }
214
215if acerts:
216    msg['credential'] = acerts
217
218if opts.debug > 1: print >>sys.stderr, msg
219
220# The New call
221try:
222    resp_dict = do_rpc(msg, 
223            url, opts.transport, cert, opts.trusted, 
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
234# Save the experiment ID certificate if we need it
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
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:
247    try:
248        creds = delegate(e_fedid, cert, opts.abac_dir, name=opts.exp_name)
249        if creds:
250            acerts.extend(creds)
251    except EnvironmentError, e:
252        sys.exit("Cannot delegate rights %s: %s" % (e.filename, e.strerror));
253
254# Construct the Create message
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
267if acerts:
268    msg['credential'] = acerts
269
270if tbmap:
271    msg['testbedmap'] = [ { 'testbed': t, 'uri': u } for t, u in tbmap.items() ]
272
273if opts.debug > 1: print >>sys.stderr, msg
274
275# make the call
276try:
277    resp_dict = do_rpc(msg, 
278            url, opts.transport, cert, opts.trusted, 
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
289# output
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.