source: fedd/fedd_create.py @ 6e63513

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

Checkpoint

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