X-Git-Url: http://git.onelab.eu/?p=sfa.git;a=blobdiff_plain;f=sfa%2Fclient%2Fsfi.py;h=53d655b80adca14d36803968c5f086cb715b754f;hp=57cb37ee9aeedbb5e4d120e478be14689e4e935e;hb=1cc8e9613cab8b5b22478de369f259e591c54e6d;hpb=ad6e6a07af006740c39cfbf091f7514dc7f8e507 diff --git a/sfa/client/sfi.py b/sfa/client/sfi.py index 57cb37ee..53d655b8 100644 --- a/sfa/client/sfi.py +++ b/sfa/client/sfi.py @@ -46,30 +46,8 @@ from sfa.client.candidates import Candidates CM_PORT=12346 -# utility methods here -def optparse_listvalue_callback(option, option_string, value, parser): - setattr(parser.values, option.dest, value.split(',')) - -# a code fragment that could be helpful for argparse which unfortunately is -# available with 2.7 only, so this feels like too strong a requirement for the client side -#class ExtraArgAction (argparse.Action): -# def __call__ (self, parser, namespace, values, option_string=None): -# would need a try/except of course -# (k,v)=values.split('=') -# d=getattr(namespace,self.dest) -# d[k]=v -##### -#parser.add_argument ("-X","--extra",dest='extras', default={}, action=ExtraArgAction, -# help="set extra flags, testbed dependent, e.g. --extra enabled=true") - -def optparse_dictvalue_callback (option, option_string, value, parser): - try: - (k,v)=value.split('=',1) - d=getattr(parser.values, option.dest) - d[k]=v - except: - parser.print_help() - sys.exit(1) +from sfa.client.common import optparse_listvalue_callback, optparse_dictvalue_callback, \ + terminal_render, filter_records # display methods def display_rspec(rspec, format='rspec'): @@ -281,8 +259,8 @@ class Sfi: ("version", ""), ("list", "authority"), ("show", "name"), - ("add", "record"), - ("update", "record"), + ("add", "[record]"), + ("update", "[record]"), ("remove", "name"), ("resources", ""), ("describe", "slice_hrn"), @@ -296,7 +274,7 @@ class Sfi: ("shutdown", "slice_hrn"), ("get_ticket", "slice_hrn rspec"), ("redeem_ticket", "ticket"), - ("delegate", "name"), + ("delegate", "to_hrn"), ("gid", "[name]"), ("trusted", "cred"), ("config", ""), @@ -341,22 +319,15 @@ class Sfi: parser.add_option('-x', '--xrn', dest='xrn', metavar='', help='object hrn/urn (mandatory)') parser.add_option('-t', '--type', dest='type', metavar='', help='object type', default=None) parser.add_option('-e', '--email', dest='email', default="", help="email (mandatory for users)") -# use --extra instead -# parser.add_option('-u', '--url', dest='url', metavar='', default=None, help="URL, useful for slices") -# parser.add_option('-d', '--description', dest='description', metavar='', -# help='Description, useful for slices', default=None) parser.add_option('-k', '--key', dest='key', metavar='', help='public key string or file', default=None) - parser.add_option('-s', '--slices', dest='slices', metavar='', help='slice xrns', + parser.add_option('-s', '--slices', dest='slices', metavar='', help='Set/replace slice xrns', default='', type="str", action='callback', callback=optparse_listvalue_callback) parser.add_option('-r', '--researchers', dest='researchers', metavar='', - help='slice researchers', default='', type="str", action='callback', + help='Set/replace slice researchers', default='', type="str", action='callback', callback=optparse_listvalue_callback) - parser.add_option('-p', '--pis', dest='pis', metavar='', help='Principal Investigators/Project Managers', + parser.add_option('-p', '--pis', dest='pis', metavar='', help='Set/replace Principal Investigators/Project Managers', default='', type="str", action='callback', callback=optparse_listvalue_callback) -# use --extra instead -# parser.add_option('-f', '--firstname', dest='firstname', metavar='', help='user first name') -# parser.add_option('-l', '--lastname', dest='lastname', metavar='', help='user last name') parser.add_option ('-X','--extra',dest='extras',default={},type='str',metavar="", action="callback", callback=optparse_dictvalue_callback, nargs=1, help="set extra/testbed-dependent flags, e.g. --extra enabled=true") @@ -419,12 +390,22 @@ class Sfi: if command == 'list': parser.add_option("-r", "--recursive", dest="recursive", action='store_true', help="list all child records", default=False) + parser.add_option("-v", "--verbose", dest="verbose", action='store_true', + help="gives details, like user keys", default=False) if command in ("delegate"): parser.add_option("-u", "--user", - action="store_true", dest="delegate_user", default=False, - help="delegate user credential") - parser.add_option("-s", "--slice", dest="delegate_slice", - help="delegate slice credential", metavar="HRN", default=None) + action="store_true", dest="delegate_user", default=False, + help="delegate your own credentials; default if no other option is provided") + parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[], + metavar="slice_hrn", help="delegate cred. for slice HRN") + parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[], + metavar='auth_hrn', help="delegate cred for auth HRN") + # this primarily is a shorthand for -a my_hrn^ + parser.add_option("-p", "--pi", dest='delegate_pi', default=None, action='store_true', + help="delegate your PI credentials, so s.t. like -a your_hrn^") + parser.add_option("-A","--to-authority",dest='delegate_to_authority',action='store_true',default=False, + help="""by default the mandatory argument is expected to be a user, +use this if you mean an authority instead""") if command in ("version"): parser.add_option("-R","--registry-version", @@ -489,7 +470,11 @@ class Sfi: # Main: parse arguments and dispatch to command # def dispatch(self, command, command_options, command_args): - return getattr(self, command)(command_options, command_args) + method=getattr(self, command,None) + if not method: + print "Unknown command %s"%command + return + return method(command_options, command_args) def main(self): self.sfi_parser = self.create_parser() @@ -524,8 +509,8 @@ class Sfi: try: self.dispatch(command, command_options, command_args) - except KeyError: - self.logger.critical ("Unknown command %s"%command) + except: + self.logger.log_exc ("sfi command %s failed"%command) sys.exit(1) return @@ -664,6 +649,9 @@ class Sfi: sys.exit(-1) return self.client_bootstrap.authority_credential_string (self.authority) + def authority_credential_string(self, auth_hrn): + return self.client_bootstrap.authority_credential_string (auth_hrn) + def slice_credential_string(self, name): return self.client_bootstrap.slice_credential_string (name) @@ -865,10 +853,9 @@ or version information about sfi itself raise Exception, "Not enough parameters for the 'list' command" # filter on person, slice, site, node, etc. - # THis really should be in the self.filter_records funct def comment... + # This really should be in the self.filter_records funct def comment... list = filter_records(options.type, list) - for record in list: - print "%s (%s)" % (record['hrn'], record['type']) + terminal_render (list, options) if options.file: save_records_to_file(options.file, list, options.fileformat) return @@ -881,7 +868,8 @@ or version information about sfi itself self.print_help() sys.exit(1) hrn = args[0] - record_dicts = self.registry().Resolve(hrn, self.my_credential_string) + # explicitly require Resolve to run in details mode + record_dicts = self.registry().Resolve(hrn, self.my_credential_string, {'details':True}) record_dicts = filter_records(options.type, record_dicts) if not record_dicts: self.logger.error("No record of type %s"% options.type) @@ -904,15 +892,22 @@ or version information about sfi itself return def add(self, options, args): - "add record into registry from xml file (Register)" + "add record into registry by using the command options (Recommended) or from xml file (Register)" auth_cred = self.my_authority_credential_string() if options.show_credential: show_credentials(auth_cred) record_dict = {} - if len(args) > 0: - record_filepath = args[0] - rec_file = self.get_record_file(record_filepath) - record_dict.update(load_record_from_file(rec_file).todict()) + if len(args) > 1: + self.print_help() + sys.exit(1) + if len(args)==1: + try: + record_filepath = args[0] + rec_file = self.get_record_file(record_filepath) + record_dict.update(load_record_from_file(rec_file).todict()) + except: + print "Cannot load record file %s"%record_filepath + sys.exit(1) if options: record_dict.update(load_record_from_opts(options).todict()) # we should have a type by now @@ -929,7 +924,7 @@ or version information about sfi itself return self.registry().Register(record_dict, auth_cred) def update(self, options, args): - "update record into registry from xml file (Update)" + "update record into registry by using the command options (Recommended) or from xml file (Update)" record_dict = {} if len(args) > 0: record_filepath = args[0] @@ -992,9 +987,6 @@ or version information about sfi itself server = self.sliceapi() # creds creds = [self.my_credential_string] - if options.delegate: - delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority)) - creds.append(delegated_cred) # options and call_id when supported api_options = {} api_options['call_id']=unique_call_id() @@ -1138,10 +1130,14 @@ or with an slice hrn, shows currently provisioned resources # keys: [, ] # }] users = [] + # xxx Thierry 2012 sept. 21 + # contrary to what I was first thinking, calling Resolve with details=False does not yet work properly here + # I am turning details=True on again on a - hopefully - temporary basis, just to get this whole thing to work again slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string]) - if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]: + # slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string], {'details':True}) + if slice_records and 'reg-researchers' in slice_records[0] and slice_records[0]['reg-researchers']: slice_record = slice_records[0] - user_hrns = slice_record['researcher'] + user_hrns = slice_record['reg-researchers'] user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns] user_records = self.registry().Resolve(user_urns, [self.my_credential_string]) @@ -1183,9 +1179,6 @@ or with an slice hrn, shows currently provisioned resources # creds slice_cred = self.slice_credential(slice_hrn) creds = [slice_cred] - if options.delegate: - delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) - creds.append(delegated_cred) # options and call_id when supported api_options = {} @@ -1307,9 +1300,6 @@ or with an slice hrn, shows currently provisioned resources # creds slice_cred = self.slice_credential(slice_hrn) creds = [slice_cred] - if options.delegate: - delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) - creds.append(delegated_cred) # options and call_id when supported api_options = {} @@ -1336,9 +1326,6 @@ or with an slice hrn, shows currently provisioned resources # cred slice_cred = self.slice_credential_string(args[0]) creds = [slice_cred] - if options.delegate: - delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) - creds.append(delegated_cred) # xxx Thierry - does this not need an api_options as well ? result = server.Start(slice_urn, creds) value = ReturnValue.get_value(result) @@ -1359,9 +1346,6 @@ or with an slice hrn, shows currently provisioned resources # cred slice_cred = self.slice_credential_string(args[0]) creds = [slice_cred] - if options.delegate: - delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) - creds.append(delegated_cred) result = server.Stop(slice_urn, creds) value = ReturnValue.get_value(result) if self.options.raw: @@ -1411,9 +1395,6 @@ or with an slice hrn, shows currently provisioned resources # creds slice_cred = self.slice_credential(args[0]) creds = [slice_cred] - if options.delegate: - delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) - creds.append(delegated_cred) # options and call_id when supported api_options = {} api_options['call_id']=unique_call_id() @@ -1439,9 +1420,6 @@ or with an slice hrn, shows currently provisioned resources # creds slice_cred = self.slice_credential(slice_hrn) creds = [slice_cred] - if options.delegate: - delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) - creds.append(delegated_cred) result = server.Shutdown(slice_urn, creds) value = ReturnValue.get_value(result) if self.options.raw: @@ -1462,9 +1440,6 @@ or with an slice hrn, shows currently provisioned resources # creds slice_cred = self.slice_credential_string(slice_hrn) creds = [slice_cred] - if options.delegate: - delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) - creds.append(delegated_cred) # rspec rspec_file = self.get_rspec_file(rspec_path) rspec = open(rspec_file).read() @@ -1528,7 +1503,8 @@ or with an slice hrn, shows currently provisioned resources self.print_help() sys.exit(1) target_hrn = args[0] - gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.client_bootstrap.my_gid_string()) + my_gid_string = open(self.client_bootstrap.my_gid()).read() + gid = self.registry().CreateGid(self.my_credential_string, target_hrn, my_gid_string) if options.file: filename = options.file else: @@ -1537,31 +1513,51 @@ or with an slice hrn, shows currently provisioned resources GID(string=gid).save_to_file(filename) - def delegate(self, options, args): + def delegate (self, options, args): """ (locally) create delegate credential for use by given hrn """ - delegee_hrn = args[0] - if options.delegate_user: - cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user') - elif options.delegate_slice: - slice_cred = self.slice_credential_string(options.delegate_slice) - cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice') - else: - self.logger.warning("Must specify either --user or --slice ") - return - delegated_cred = Credential(string=cred) - object_hrn = delegated_cred.get_gid_object().get_hrn() + if len(args) != 1: + self.print_help() + sys.exit(1) + to_hrn = args[0] + # support for several delegations in the same call + # so first we gather the things to do + tuples=[] + for slice_hrn in options.delegate_slices: + message="%s.slice"%slice_hrn + original = self.slice_credential_string(slice_hrn) + tuples.append ( (message, original,) ) + if options.delegate_pi: + my_authority=self.authority + message="%s.pi"%my_authority + original = self.my_authority_credential_string() + tuples.append ( (message, original,) ) + for auth_hrn in options.delegate_auths: + message="%s.auth"%auth_hrn + original=self.authority_credential_string(auth_hrn) + tuples.append ( (message, original, ) ) + # if nothing was specified at all at this point, let's assume -u + if not tuples: options.delegate_user=True + # this user cred if options.delegate_user: - dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_" - + get_leaf(object_hrn) + ".cred") - elif options.delegate_slice: - dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_" - + get_leaf(object_hrn) + ".cred") - - delegated_cred.save_to_file(dest_fn, save_parents=True) - - self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn)) + message="%s.user"%self.user + original = self.my_credential_string + tuples.append ( (message, original, ) ) + + # default type for beneficial is user unless -A + if options.delegate_to_authority: to_type='authority' + else: to_type='user' + + # let's now handle all this + # it's all in the filenaming scheme + for (message,original) in tuples: + delegated_string = self.client_bootstrap.delegate_credential_string(original, to_hrn, to_type) + delegated_credential = Credential (string=delegated_string) + filename = os.path.join ( self.options.sfi_dir, + "%s_for_%s.%s.cred"%(message,to_hrn,to_type)) + delegated_credential.save_to_file(filename, save_parents=True) + self.logger.info("delegated credential for %s to %s and wrote to %s"%(message,to_hrn,filename)) def trusted(self, options, args): """