X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Fclient%2Fsfi.py;h=15c83cd6c8f82af172c49ac74796933d9cdf8d20;hb=f357d5c677573e29f260f82318c9450119474dce;hp=352701911f0e132db3a6956e66e0d3ae13df8f4d;hpb=e0e3b09aa39ec0f078cf007927bfd4e9573a1211;p=sfa.git diff --git a/sfa/client/sfi.py b/sfa/client/sfi.py index 35270191..15c83cd6 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'): @@ -107,14 +85,6 @@ def display_record(record, dump=False): return -def filter_records(type, records): - filtered_records = [] - for record in records: - if (record['type'] == type) or (type == "all"): - filtered_records.append(record) - return filtered_records - - def credential_printable (credential_string): credential=Credential(string=credential_string) result="" @@ -197,39 +167,6 @@ def save_record_to_file(filename, record_dict): f.close() return -# used in sfi list -def terminal_render (records,options): - # sort records by type - grouped_by_type={} - for record in records: - type=record['type'] - if type not in grouped_by_type: grouped_by_type[type]=[] - grouped_by_type[type].append(record) - for (type, list) in grouped_by_type.items(): -# print 20 * '-', type - try: renderer=eval('terminal_render_'+type) - except: renderer=terminal_render_default - for record in list: renderer(record,options) - -def terminal_render_default (record,options): - print "%s (%s)" % (record['hrn'], record['type']) -def terminal_render_user (record, options): - print "%s (User)"%record['hrn'], - if record.get('reg-pi-authorities',None): print " [PI at %s]"%(" and ".join(record['reg-pi-authorities'])), - if record.get('reg-slices',None): print " [IN slices %s]"%(" and ".join(record['reg-slices'])), - print "" -def terminal_render_slice (record, options): - print "%s (Slice)"%record['hrn'], - if record.get('reg-researchers',None): print " [USERS %s]"%(" and ".join(record['reg-researchers'])), - print record.keys() - print "" -def terminal_render_authority (record, options): - print "%s (Authority)"%record['hrn'], - if record.get('reg-pis',None): print " [PIS %s]"%(" and ".join(record['reg-pis'])), - print "" -def terminal_render_node (record, options): - print "%s (Node)"%record['hrn'] - # minimally check a key argument def check_ssh_key (key): good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$' @@ -313,8 +250,8 @@ class Sfi: ("version", ""), ("list", "authority"), ("show", "name"), - ("add", "record"), - ("update", "record"), + ("add", "[record]"), + ("update", "[record]"), ("remove", "name"), ("slices", ""), ("resources", "[slice_hrn]"), @@ -328,7 +265,7 @@ class Sfi: ("shutdown", "slice_hrn"), ("get_ticket", "slice_hrn rspec"), ("redeem_ticket", "ticket"), - ("delegate", "name"), + ("delegate", "to_hrn"), ("gid", "[name]"), ("trusted", "cred"), ("config", ""), @@ -373,34 +310,19 @@ 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") - # user specifies remote aggregate/sm/component - if command in ("resources", "slices", "create", "delete", "start", "stop", - "restart", "shutdown", "get_ticket", "renew", "status"): - parser.add_option("-d", "--delegate", dest="delegate", default=None, - action="store_true", - help="Include a credential delegated to the user's root"+\ - "authority in set of credentials for this call") - # show_credential option if command in ("list","resources","create","add","update","remove","slices","delete","status","renew"): parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False, @@ -451,12 +373,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", @@ -521,7 +453,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() @@ -556,8 +492,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 @@ -693,30 +629,12 @@ 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) - # xxx should be supported by sfaclientbootstrap as well - def delegate_cred(self, object_cred, hrn, type='authority'): - # the gid and hrn of the object we are delegating - if isinstance(object_cred, str): - object_cred = Credential(string=object_cred) - object_gid = object_cred.get_gid_object() - object_hrn = object_gid.get_hrn() - - if not object_cred.get_privileges().get_all_delegate(): - self.logger.error("Object credential %s does not have delegate bit set"%object_hrn) - return - - # the delegating user's gid - caller_gidfile = self.my_gid() - - # the gid of the user who will be delegated to - delegee_gid = self.client_bootstrap.gid(hrn,type) - delegee_hrn = delegee_gid.get_hrn() - dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile) - return dcred.save_to_string(save_parents=True) - # # Management of the servers # @@ -904,9 +822,8 @@ or version information about sfi itself self.print_help() sys.exit(1) hrn = args[0] - # xxx should set details=True here but that's not in the xmlrpc interface ... - # record_dicts = self.registry().Resolve(hrn, self.my_credential_string, details=True) - 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) @@ -929,15 +846,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 @@ -954,7 +878,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] @@ -1017,9 +941,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() @@ -1044,11 +965,11 @@ or with an slice hrn, shows currently provisioned resources # set creds creds = [] if args: - creds.append(self.slice_credential_string(args[0])) + the_credential=self.slice_credential_string(args[0]) + creds.append(the_credential) else: - creds.append(self.my_credential_string) - if options.delegate: - creds.append(self.delegate_cred(cred, get_authority(self.authority))) + the_credential=self.my_credential_string + creds.append(the_credential) if options.show_credential: show_credentials(creds) @@ -1131,7 +1052,11 @@ 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]) + # 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['reg-researchers'] @@ -1178,9 +1103,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) # options and call_id when supported api_options = {} @@ -1208,9 +1130,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) # options and call_id when supported api_options = {} @@ -1237,9 +1156,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) @@ -1260,9 +1176,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: @@ -1283,9 +1196,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.reset_slice(creds, slice_urn) value = ReturnValue.get_value(result) if self.options.raw: @@ -1309,9 +1219,6 @@ or with an slice hrn, shows currently provisioned resources # creds 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) # options and call_id when supported api_options = {} api_options['call_id']=unique_call_id() @@ -1337,9 +1244,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) result = server.Shutdown(slice_urn, creds) value = ReturnValue.get_value(result) if self.options.raw: @@ -1360,9 +1264,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() @@ -1426,7 +1327,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: @@ -1435,31 +1337,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): """