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
RevLine 
[d743d60]1#!/usr/local/bin/python
2
[6e63513]3import sys, os
[b10375f]4import re
[6e63513]5import subprocess
[d743d60]6
[6e63513]7from string import join
8
9from federation.fedid import fedid, generate_fedid
[d743d60]10from federation.remote_service import service_caller
11from federation.client_lib import client_opts, exit_with_fault, RPCException, \
[6e63513]12        wrangle_standard_options, do_rpc, get_experiment_names, save_certfile,\
[353db8c]13        get_abac_certs
14from federation.util import abac_split_cert
[d743d60]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")
[fd07c48]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")
[353db8c]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
[6e63513]48        self.set_defaults(delegate=True)
[d743d60]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'] = [ ]
[0de1b94]72        for t in terms[3].split(";"):
[d743d60]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
[353db8c]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:
[6e63513]111        keyfile, certfile = abac_split_cert(cert)
112        print "%s %s" % (keyfile, certfile)
[353db8c]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 ]
[6e63513]122        if not debug:
123            if subprocess.call(cmd) != 0:
124                return None
[353db8c]125        else:
[6e63513]126            print join(cmd)
127            return None
128
129        f = open(fn, 'r')
130        rv = f.read()
131        f.close()
132        return rv
133
[353db8c]134    finally:
135        if keyfile: os.unlink(keyfile)
136        if certfile: os.unlink(certfile)
137
[6e63513]138
[d743d60]139# Main line
[b10375f]140service_re = re.compile('^\\s*#\\s*SERVICE:\\s*([\\S]+)')
[d743d60]141parser = fedd_create_opts()
142(opts, args) = parser.parse_args()
143
[353db8c]144# Option processing
[5d854e1]145cert, fid, url = wrangle_standard_options(opts)
[d743d60]146
147if opts.file:
148    try:
[b10375f]149        lines = [ line for line in open(opts.file, 'r')]
150        exp_desc = "".join(lines)
[d743d60]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
[353db8c]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
[d743d60]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])
[353db8c]175
[b10375f]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
[fd07c48]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
[d743d60]188if not svcs:
189    print >>sys.stderr, "Warning:Neither master/project nor services requested"
190
[353db8c]191# Construct the New experiment request
[d743d60]192msg = { }
193
[353db8c]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
[d743d60]202if opts.exp_name:
203    msg['experimentID'] = { 'localname': opts.exp_name }
204
[353db8c]205if acerts:
206    msg['credential'] = acerts
207
[d743d60]208if opts.debug > 1: print >>sys.stderr, msg
209
[353db8c]210# The New call
[d743d60]211try:
212    resp_dict = do_rpc(msg, 
[5d854e1]213            url, opts.transport, cert, opts.trusted, 
[d743d60]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
[353db8c]224# Save the experiment ID certificate if we need it
[d743d60]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
[353db8c]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:
[6e63513]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));
[353db8c]243
244# Construct the Create message
[d743d60]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
[353db8c]257if acerts:
258    msg['credential'] = acerts
259
[fd07c48]260if tbmap:
261    msg['testbedmap'] = [ { 'testbed': t, 'uri': u } for t, u in tbmap.items() ]
262
[d743d60]263if opts.debug > 1: print >>sys.stderr, msg
264
[353db8c]265# make the call
[d743d60]266try:
267    resp_dict = do_rpc(msg, 
[5d854e1]268            url, opts.transport, cert, opts.trusted, 
[d743d60]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
[353db8c]279# output
[d743d60]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.