X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=sfa%2Fclient%2Fsfi.py;h=0e8b8e16d655211c38e3d05e88c49fd883d858b3;hb=984e5fce16029f5346c073fe2a43f3231674de76;hp=27210cbc54687fcab338b3e762e6b3a211a91816;hpb=6053f21b7bb4b1bb3efd8a72478dc9c618b32e6a;p=sfa.git diff --git a/sfa/client/sfi.py b/sfa/client/sfi.py index 27210cbc..0e8b8e16 100644 --- a/sfa/client/sfi.py +++ b/sfa/client/sfi.py @@ -43,33 +43,12 @@ from sfa.client.sfaserverproxy import SfaServerProxy, ServerException from sfa.client.client_helper import pg_users_arg, sfa_users_arg from sfa.client.return_value import ReturnValue from sfa.client.candidates import Candidates +from sfa.client.manifolduploader import ManifoldUploader 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'): @@ -115,14 +94,15 @@ def filter_records(type, records): return filtered_records -def credential_printable (credential_string): - credential=Credential(string=credential_string) +def credential_printable (cred): + credential=Credential(cred=cred) result="" result += credential.get_summary_tostring() result += "\n" rights = credential.get_privileges() - result += "rights=%s"%rights - result += "\n" + result += "type=%s\n" % credential.type + result += "version=%s\n" % credential.version + result += "rights=%s\n"%rights return result def show_credentials (cred_s): @@ -245,6 +225,36 @@ def load_record_from_file(filename): import uuid def unique_call_id(): return uuid.uuid4().urn +########## a simple model for maintaing 3 doc attributes per command (instead of just one) +# essentially for the methods that implement a subcommand like sfi list +# we need to keep track of +# (*) doc a few lines that tell what it does, still located in __doc__ +# (*) args_string a simple one-liner that describes mandatory arguments +# (*) example well, one or several releant examples +# +# since __doc__ only accounts for one, we use this simple mechanism below +# however we keep doc in place for easier migration + +from functools import wraps + +# we use a list as well as a dict so we can keep track of the order +commands_list=[] +commands_dict={} + +def register_command (args_string, example): + def wrap(m): + name=getattr(m,'__name__') + doc=getattr(m,'__doc__',"-- missing doc --") + doc=doc.strip(" \t\n") + commands_list.append(name) + commands_dict[name]=(doc, args_string, example) + @wraps(m) + def new_method (*args, **kwds): return m(*args, **kwds) + return new_method + return wrap + +########## + class Sfi: # dirty hack to make this class usable from the outside @@ -272,36 +282,12 @@ class Sfi: self.authority = None self.logger = sfi_logger self.logger.enable_console() - self.available_names = [ tuple[0] for tuple in Sfi.available ] - self.available_dict = dict (Sfi.available) - - # tuples command-name expected-args in the order in which they should appear in the help - available = [ - ("version", ""), - ("list", "authority"), - ("show", "name"), - ("add", "record"), - ("update", "record"), - ("remove", "name"), - ("resources", ""), - ("describe", "slice_hrn"), - ("create", "slice_hrn rspec"), - ("allocate", "slice_hrn rspec"), - ("provison", "slice_hrn"), - ("action", "slice_hrn action"), - ("delete", "slice_hrn"), - ("status", "slice_hrn"), - ("renew", "slice_hrn time"), - ("shutdown", "slice_hrn"), - ("get_ticket", "slice_hrn rspec"), - ("redeem_ticket", "ticket"), - ("delegate", "name"), - ("gid", "[name]"), - ("trusted", "cred"), - ("config", ""), - ] - - def print_command_help (self, options): + self.command=None + self.config=None + self.config_file=None + + ### suitable if no reasonable command has been provided + def print_commands_help (self, options): verbose=getattr(options,'verbose') format3="%18s %-15s %s" line=80*'-' @@ -311,65 +297,73 @@ class Sfi: else: print line self.create_parser().print_help() - for command in self.available_names: - args=self.available_dict[command] - method=getattr(self,command,None) - doc="" - if method: doc=getattr(method,'__doc__',"") - if not doc: doc="*** no doc found ***" - doc=doc.strip(" \t\n") - doc=doc.replace("\n","\n"+35*' ') + # preserve order from the code + for command in commands_list: + (doc, args_string, example) = commands_dict[command] if verbose: print line - print format3%(command,args,doc) + doc=doc.replace("\n","\n"+35*' ') + print format3%(command,args_string,doc) if verbose: self.create_command_parser(command).print_help() + + ### now if a known command was found we can be more verbose on that one + def print_help (self): + print "==================== Generic sfi usage" + self.sfi_parser.print_help() + (doc,_,example)=commands_dict[self.command] + print "\n==================== Purpose of %s"%self.command + print doc + print "\n==================== Specific usage for %s"%self.command + self.command_parser.print_help() + if example: + print "\n==================== %s example(s)"%self.command + print example def create_command_parser(self, command): - if command not in self.available_dict: + if command not in commands_dict: msg="Invalid command\n" msg+="Commands: " - msg += ','.join(self.available_names) + msg += ','.join(commands_list) self.logger.critical(msg) sys.exit(2) - parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \ - % (command, self.available_dict[command])) + # retrieve args_string + (_, args_string, __) = commands_dict[command] + + parser = OptionParser(add_help_option=False, + usage="sfi [sfi_options] %s [cmd_options] %s" + % (command, args_string)) + parser.add_option ("-h","--help",dest='help',action='store_true',default=False, + help="Summary of one command usage") if command in ("add", "update"): 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", "describe", "create", "delete", "allocate", "provision", - "action", "shutdown", "get_ticket", "renew", "status"): + if command in ("resources", "describe", "allocate", "provision", "delete", "allocate", "provision", + "action", "shutdown", "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"): + if command in ("list","resources", "describe", "provision", "allocate", "add","update","remove","slices","delete","status","renew"): parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False, help="show credential(s) used in human-readable form") # registy filter option @@ -381,7 +375,7 @@ class Sfi: if command in ("show"): parser.add_option("-k","--key",dest="keys",action="append",default=[], help="specify specific keys to be displayed from record") - if command in ("resources"): + if command in ("resources", "describe"): # rspec version parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1", help="schema type and version of resulting RSpec") @@ -402,8 +396,7 @@ class Sfi: choices=("all", "resources", "leases"), default="resources") - # 'create' does return the new rspec, makes sense to save that too - if command in ("resources", "show", "list", "gid", 'create'): + if command in ("resources", "describe", "allocate", "provision", "show", "list", "gid"): parser.add_option("-o", "--output", dest="file", help="output XML to file", metavar="FILE", default=None) @@ -418,12 +411,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", @@ -439,8 +442,9 @@ class Sfi: def create_parser(self): # Generate command line parser - parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]", - description="Commands: %s"%(" ".join(self.available_names))) + parser = OptionParser(add_help_option=False, + usage="sfi [sfi_options] command [cmd_options] [cmd_args]", + description="Commands: %s"%(" ".join(commands_list))) parser.add_option("-r", "--registry", dest="registry", help="root registry", metavar="URL", default=None) parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL", @@ -470,31 +474,29 @@ class Sfi: help="point to the private key file to use if not yet installed in sfi_dir") parser.add_option("-t", "--timeout", dest="timeout", default=None, help="Amout of time to wait before timing out the request") - parser.add_option("-?", "--commands", - action="store_true", dest="command_help", default=False, + parser.add_option("-h", "--help", + action="store_true", dest="help", default=False, help="one page summary on commands & exit") parser.disable_interspersed_args() return parser - def print_help (self): - print "==================== Generic sfi usage" - self.sfi_parser.print_help() - print "==================== Specific command usage" - self.command_parser.print_help() - # # 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() (options, args) = self.sfi_parser.parse_args() - if options.command_help: - self.print_command_help(options) + if options.help: + self.print_commands_help(options) sys.exit(1) self.options = options @@ -502,32 +504,38 @@ class Sfi: if len(args) <= 0: self.logger.critical("No command given. Use -h for help.") - self.print_command_help(options) + self.print_commands_help(options) return -1 # complete / find unique match with command set - command_candidates = Candidates (self.available_names) + command_candidates = Candidates (commands_list) input = args[0] command = command_candidates.only_match(input) if not command: - self.print_command_help(options) + self.print_commands_help(options) sys.exit(1) # second pass options parsing + self.command=command self.command_parser = self.create_command_parser(command) (command_options, command_args) = self.command_parser.parse_args(args[1:]) + if command_options.help: + self.print_help() + sys.exit(1) self.command_options = command_options self.read_config () self.bootstrap () - self.logger.debug("Command=%s" % command) + self.logger.debug("Command=%s" % self.command) try: self.dispatch(command, command_options, command_args) - except KeyError: - self.logger.critical ("Unknown command %s"%command) - sys.exit(1) + except SystemExit: + return 1 + except: + self.logger.log_exc ("sfi command %s failed"%command) + return 1 - return + return 0 #################### def read_config(self): @@ -543,7 +551,11 @@ class Sfi: # we need to preload the sections we want parsed # from the shell config config.add_section('sfi') + # sface users should be able to use this same file to configure their stuff config.add_section('sface') + # manifold users should be able to specify the details + # of their backend server here for 'sfi myslice' + config.add_section('myslice') config.load(config_file) # back up old config shutil.move(config_file, shell_config_file) @@ -559,6 +571,7 @@ class Sfi: self.logger.log_exc("Could not read config file %s"%config_file) sys.exit(1) + self.config=config errors = 0 # Set SliceMgr URL if (self.options.sm is not None): @@ -650,6 +663,9 @@ class Sfi: # extract what's needed self.private_key = client_bootstrap.private_key() self.my_credential_string = client_bootstrap.my_credential_string () + self.my_credential = {'geni_type': 'geni_sfa', + 'geni_version': '3.0', + 'geni_value': self.my_credential_string} self.my_gid = client_bootstrap.my_gid () self.client_bootstrap = client_bootstrap @@ -660,9 +676,17 @@ 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) + def slice_credential(self, name): + return {'geni_type': 'geni_sfa', + 'geni_version': '3.0', + 'geni_value': self.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 @@ -816,10 +840,16 @@ class Sfi: # Registry-related commands #========================================================================== + @register_command("","") + def config (self, options, args): + "Display contents of current config" + self.show_config() + + @register_command("","") def version(self, options, args): """ display an SFA server version (GetVersion) -or version information about sfi itself + or version information about sfi itself """ if options.version_local: version=version_core() @@ -836,6 +866,7 @@ or version information about sfi itself pprinter = PrettyPrinter(indent=4) pprinter.pprint(version) + @register_command("authority","") def list(self, options, args): """ list entries in named authority registry (List) @@ -856,14 +887,14 @@ 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 + @register_command("name","") def show(self, options, args): """ show details about named registry record (Resolve) @@ -872,7 +903,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) @@ -894,16 +926,27 @@ or version information about sfi itself save_records_to_file(options.file, record_dicts, options.fileformat) return + @register_command("[xml-filename]","") def add(self, options, args): - "add record into registry from xml file (Register)" + """add record into registry (Register) + from command line options (recommended) + old-school method involving an xml file still supported""" + 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 @@ -919,8 +962,11 @@ or version information about sfi itself record_dict['last_name'] = record_dict['hrn'] return self.registry().Register(record_dict, auth_cred) + @register_command("[xml-filename]","") def update(self, options, args): - "update record into registry from xml file (Update)" + """update record into registry (Update) + from command line options (recommended) + old-school method involving an xml file still supported""" record_dict = {} if len(args) > 0: record_filepath = args[0] @@ -960,6 +1006,7 @@ or version information about sfi itself show_credentials(cred) return self.registry().Update(record_dict, cred) + @register_command("name","") def remove(self, options, args): "remove registry record by name (Remove)" auth_cred = self.my_authority_credential_string() @@ -978,17 +1025,15 @@ or version information about sfi itself # Slice-related commands # ================================================================== + @register_command("","") def slices(self, options, args): "list instantiated slices (ListSlices) - returns urn's" 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() + api_options['call_id']=unique_call_id() if options.show_credential: show_credentials(creds) result = server.ListSlices(creds, *self.ois(server,api_options)) @@ -1000,15 +1045,15 @@ or version information about sfi itself return # show rspec for named slice + @register_command("","") def resources(self, options, args): """ - discover available resources -or with an slice hrn, shows currently provisioned resources + discover available resources (ListResources) """ server = self.sliceapi() # set creds - creds = [self.my_credential_string] + creds = [self.my_credential] if options.delegate: creds.append(self.delegate_cred(cred, get_authority(self.authority))) if options.show_credential: @@ -1051,14 +1096,16 @@ or with an slice hrn, shows currently provisioned resources return + @register_command("slice_hrn","") def describe(self, options, args): """ - Shows currently provisioned resources. + shows currently allocated/provisioned resources + of the named slice or set of slivers (Describe) """ server = self.sliceapi() # set creds - creds = [self.slice_credential_string(args[0])] + creds = [self.slice_credential(args[0])] if options.delegate: creds.append(self.delegate_cred(cred, get_authority(self.authority))) if options.show_credential: @@ -1067,7 +1114,7 @@ or with an slice hrn, shows currently provisioned resources api_options = {'call_id': unique_call_id(), 'cached': True, 'info': options.info, - 'list_leases': options.list_lease, + 'list_leases': options.list_leases, 'geni_rspec_version': {'type': 'geni', 'version': '3.0'}, } if options.rspec_version: @@ -1090,80 +1137,10 @@ or with an slice hrn, shows currently provisioned resources return - def create(self, options, args): - """ - create or update named slice with given rspec - """ - server = self.sliceapi() - - # xxx do we need to check usage (len(args)) ? - # slice urn - slice_hrn = args[0] - slice_urn = hrn_to_urn(slice_hrn, 'slice') - - # credentials - creds = [self.slice_credential_string(slice_hrn)] - - delegated_cred = None - server_version = self.get_cached_server_version(server) - if server_version.get('interface') == 'slicemgr': - # delegate our cred to the slice manager - # do not delegate cred to slicemgr...not working at the moment - pass - #if server_version.get('hrn'): - # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn']) - #elif server_version.get('urn'): - # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn'])) - - if options.show_credential: - show_credentials(creds) - - # rspec - rspec_file = self.get_rspec_file(args[1]) - rspec = open(rspec_file).read() - - # users - # need to pass along user keys to the aggregate. - # users = [ - # { urn: urn:publicid:IDN+emulab.net+user+alice - # keys: [, ] - # }] - users = [] - 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_record = slice_records[0] - user_hrns = slice_record['researcher'] - user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns] - user_records = self.registry().Resolve(user_urns, [self.my_credential_string]) - - if 'sfa' not in server_version: - users = pg_users_arg(user_records) - rspec = RSpec(rspec) - rspec.filter({'component_manager_id': server_version['urn']}) - rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request') - else: - users = sfa_users_arg(user_records, slice_record) - - # do not append users, keys, or slice tags. Anything - # not contained in this request will be removed from the slice - - api_options = {} - api_options ['append'] = False - api_options ['call_id'] = unique_call_id() - result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server, api_options)) - value = ReturnValue.get_value(result) - if self.options.raw: - save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) - if options.file is not None: - save_rspec_to_file (value, options.file) - if (self.options.raw is None) and (options.file is None): - print value - - return value - + @register_command("slice_hrn","") def delete(self, options, args): """ - delete named slice (DeleteSliver) + de-allocate and de-provision all or named slivers of the slice (Delete) """ server = self.sliceapi() @@ -1172,18 +1149,15 @@ or with an slice hrn, shows currently provisioned resources slice_urn = hrn_to_urn(slice_hrn, 'slice') # creds - slice_cred = self.slice_credential_string(slice_hrn) + 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 = {} api_options ['call_id'] = unique_call_id() if options.show_credential: show_credentials(creds) - result = server.Delete(slice_urn, creds, *self.ois(server, api_options ) ) + result = server.Delete([slice_urn], creds, *self.ois(server, api_options ) ) value = ReturnValue.get_value(result) if self.options.raw: save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) @@ -1191,15 +1165,20 @@ or with an slice hrn, shows currently provisioned resources print value return value + @register_command("slice_hrn rspec","") def allocate(self, options, args): + """ + allocate resources to the named slice (Allocate) + """ + server = self.sliceapi() + server_version = self.get_cached_server_version(server) slice_hrn = args[0] slice_urn = Xrn(slice_hrn, type='slice').get_urn() # credentials - creds = [self.slice_credential_string(slice_hrn)] + creds = [self.slice_credential(slice_hrn)] delegated_cred = None - server_version = self.get_cached_server_version(server) if server_version.get('interface') == 'slicemgr': # delegate our cred to the slice manager # do not delegate cred to slicemgr...not working at the moment @@ -1215,32 +1194,8 @@ or with an slice hrn, shows currently provisioned resources # rspec rspec_file = self.get_rspec_file(args[1]) rspec = open(rspec_file).read() - - # users - # need to pass along user keys to the aggregate. - # users = [ - # { urn: urn:publicid:IDN+emulab.net+user+alice - # keys: [, ] - # }] - users = [] - 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_record = slice_records[0] - user_hrns = slice_record['researcher'] - user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns] - user_records = self.registry().Resolve(user_urns, [self.my_credential_string]) - - if 'sfa' not in server_version: - users = pg_users_arg(user_records) - rspec = RSpec(rspec) - rspec.filter({'component_manager_id': server_version['urn']}) - rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request') - else: - users = sfa_users_arg(user_records, slice_record) - api_options = {} api_options ['call_id'] = unique_call_id() - api_options['geni_users'] = users result = server.Allocate(slice_urn, creds, rspec, api_options) value = ReturnValue.get_value(result) if self.options.raw: @@ -1249,18 +1204,22 @@ or with an slice hrn, shows currently provisioned resources save_rspec_to_file (value, options.file) if (self.options.raw is None) and (options.file is None): print value - return value + @register_command("slice_hrn","") def provision(self, options, args): + """ + provision already allocated resources of named slice (Provision) + """ + server = self.sliceapi() + server_version = self.get_cached_server_version(server) slice_hrn = args[0] slice_urn = Xrn(slice_hrn, type='slice').get_urn() # credentials - creds = [self.slice_credential_string(slice_hrn)] + creds = [self.slice_credential(slice_hrn)] delegated_cred = None - server_version = self.get_cached_server_version(server) if server_version.get('interface') == 'slicemgr': # delegate our cred to the slice manager # do not delegate cred to slicemgr...not working at the moment @@ -1275,7 +1234,29 @@ or with an slice hrn, shows currently provisioned resources api_options = {} api_options ['call_id'] = unique_call_id() - result = server.CreateSliver(slice_urn, creds, api_options) + + # set the requtested rspec version + version_manager = VersionManager() + rspec_version = version_manager._get_version('geni', '3.0').to_dict() + api_options['geni_rspec_version'] = rspec_version + + # users + # need to pass along user keys to the aggregate. + # users = [ + # { urn: urn:publicid:IDN+emulab.net+user+alice + # keys: [, ] + # }] + users = [] + 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_record = slice_records[0] + user_hrns = slice_record['researcher'] + user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns] + user_records = self.registry().Resolve(user_urns, [self.my_credential_string]) + users = pg_users_arg(user_records) + + api_options['geni_users'] = users + result = server.Provision([slice_urn], creds, api_options) value = ReturnValue.get_value(result) if self.options.raw: save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) @@ -1285,9 +1266,10 @@ or with an slice hrn, shows currently provisioned resources print value return value + @register_command("slice_hrn","") def status(self, options, args): """ - retrieve slice status (SliverStatus) + retrieve the status of the slivers belonging to tne named slice (Status) """ server = self.sliceapi() @@ -1296,89 +1278,42 @@ or with an slice hrn, shows currently provisioned resources slice_urn = hrn_to_urn(slice_hrn, 'slice') # creds - slice_cred = self.slice_credential_string(slice_hrn) + 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 = {} api_options['call_id']=unique_call_id() if options.show_credential: show_credentials(creds) - result = server.Status(slice_urn, creds, *self.ois(server,api_options)) - value = ReturnValue.get_value(result) - if self.options.raw: - save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) - else: - print value - - def start(self, options, args): - """ - start named slice (Start) - """ - server = self.sliceapi() - - # the slice urn - slice_hrn = args[0] - slice_urn = hrn_to_urn(slice_hrn, 'slice') - - # 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) - if self.options.raw: - save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) - else: - print value - return value - - def stop(self, options, args): - """ - stop named slice (Stop) - """ - server = self.sliceapi() - # slice urn - slice_hrn = args[0] - slice_urn = hrn_to_urn(slice_hrn, 'slice') - # 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) + result = server.Status([slice_urn], creds, *self.ois(server,api_options)) value = ReturnValue.get_value(result) if self.options.raw: save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) else: print value + # Thierry: seemed to be missing return value - - # reset named slice + + @register_command("slice_hrn action","") def action(self, options, args): """ - Perform the named operational action on the named slivers + Perform the named operational action on these slivers """ server = self.sliceapi() + api_options = {} # slice urn slice_hrn = args[0] action = args[1] slice_urn = Xrn(slice_hrn, type='slice').get_urn() # cred - slice_cred = self.slice_credential_string(args[0]) + 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) - result = server.PerformOperationalAction(slice_urn, creds, action ) + result = server.PerformOperationalAction([slice_urn], creds, action , api_options) value = ReturnValue.get_value(result) if self.options.raw: save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) @@ -1386,6 +1321,7 @@ or with an slice hrn, shows currently provisioned resources print value return value + @register_command("slice_hrn time","") def renew(self, options, args): """ renew slice (RenewSliver) @@ -1399,17 +1335,14 @@ or with an slice hrn, shows currently provisioned resources slice_urn = hrn_to_urn(slice_hrn, 'slice') # time: don't try to be smart on the time format, server-side will # creds - slice_cred = self.slice_credential_string(args[0]) + 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() if options.show_credential: show_credentials(creds) - result = server.Renew(slice_urn, creds, input_time, *self.ois(server,api_options)) + result = server.Renew([slice_urn], creds, input_time, *self.ois(server,api_options)) value = ReturnValue.get_value(result) if self.options.raw: save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) @@ -1418,6 +1351,7 @@ or with an slice hrn, shows currently provisioned resources return value + @register_command("slice_hrn","") def shutdown(self, options, args): """ shutdown named slice (Shutdown) @@ -1427,11 +1361,8 @@ or with an slice hrn, shows currently provisioned resources slice_hrn = args[0] slice_urn = hrn_to_urn(slice_hrn, 'slice') # creds - slice_cred = self.slice_credential_string(slice_hrn) + 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: @@ -1441,75 +1372,7 @@ or with an slice hrn, shows currently provisioned resources return value - def get_ticket(self, options, args): - """ - get a ticket for the specified slice - """ - server = self.sliceapi() - # slice urn - slice_hrn, rspec_path = args[0], args[1] - slice_urn = hrn_to_urn(slice_hrn, 'slice') - # 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() - # options and call_id when supported - api_options = {} - api_options['call_id']=unique_call_id() - # get ticket at the server - ticket_string = server.GetTicket(slice_urn, creds, rspec, *self.ois(server,api_options)) - # save - file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket") - self.logger.info("writing ticket to %s"%file) - ticket = SfaTicket(string=ticket_string) - ticket.save_to_file(filename=file, save_parents=True) - - def redeem_ticket(self, options, args): - """ - Connects to nodes in a slice and redeems a ticket -(slice hrn is retrieved from the ticket) - """ - ticket_file = args[0] - - # get slice hrn from the ticket - # use this to get the right slice credential - ticket = SfaTicket(filename=ticket_file) - ticket.decode() - ticket_string = ticket.save_to_string(save_parents=True) - - slice_hrn = ticket.gidObject.get_hrn() - slice_urn = hrn_to_urn(slice_hrn, 'slice') - #slice_hrn = ticket.attributes['slivers'][0]['hrn'] - slice_cred = self.slice_credential_string(slice_hrn) - - # get a list of node hostnames from the RSpec - tree = etree.parse(StringIO(ticket.rspec)) - root = tree.getroot() - hostnames = root.xpath("./network/site/node/hostname/text()") - - # create an xmlrpc connection to the component manager at each of these - # components and gall redeem_ticket - connections = {} - for hostname in hostnames: - try: - self.logger.info("Calling redeem_ticket at %(hostname)s " % locals()) - cm_url="http://%s:%s/"%(hostname,CM_PORT) - server = SfaServerProxy(cm_url, self.private_key, self.my_gid) - server = self.server_proxy(hostname, CM_PORT, self.private_key, - timeout=self.options.timeout, verbose=self.options.debug) - server.RedeemTicket(ticket_string, slice_cred) - self.logger.info("Success") - except socket.gaierror: - self.logger.error("redeem_ticket failed on %s: Component Manager not accepting requests"%hostname) - except Exception, e: - self.logger.log_exc(e.message) - return - + @register_command("[name]","") def gid(self, options, args): """ Create a GID (CreateGid) @@ -1518,7 +1381,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: @@ -1527,35 +1391,108 @@ or with an slice hrn, shows currently provisioned resources GID(string=gid).save_to_file(filename) - def delegate(self, options, args): + @register_command("to_hrn","""$ sfi delegate -u -p -s ple.inria.heartbeat -s ple.inria.omftest ple.upmc.slicebrowser + + will locally create a set of delegated credentials for the benefit of ple.upmc.slicebrowser + the set of credentials in the scope for this call would be + (*) ple.inria.thierry_parmentelat.user_for_ple.upmc.slicebrowser.user.cred + as per -u/--user + (*) ple.inria.pi_for_ple.upmc.slicebrowser.user.cred + as per -p/--pi + (*) ple.inria.heartbeat.slice_for_ple.upmc.slicebrowser.user.cred + (*) ple.inria.omftest.slice_for_ple.upmc.slicebrowser.user.cred + because of the two -s options + +""") + def delegate (self, options, args): """ (locally) create delegate credential for use by given hrn + make sure to check for 'sfi myslice' instead if you plan + on using MySlice """ - 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)) + #################### + @register_command("","""$ less +/myslice sfi_config +[myslice] +backend = 'http://manifold.pl.sophia.inria.fr:7080' +# the HRN that myslice uses, so that we are delegating to +delegate = 'ple.upmc.slicebrowser' +# platform - this is a myslice concept +platform = 'ple' +# username - as of this writing (May 2013) a simple login name +user = 'thierry' + +$ sfi myslice + + will first collect the slices that you are part of, then make sure + all your credentials are up-to-date (read: refresh expired ones) + then compute delegated credentials for user 'ple.upmc.slicebrowser' + and upload them all on myslice backend, using 'platform' and 'user'. + A password will be prompted for the upload part. +""" +) # register_command + def myslice (self, options, args): + + """ This helper is for refreshing your credentials at myslice; it will + * compute all the slices that you currently have credentials on + * refresh all your credentials (you as a user and pi, your slices) + * upload them to the manifold backend server + for last phase, sfi_config is read to look for the [myslice] section, + and namely the 'backend', 'delegate' and 'user' settings""" + + ########## + if len(args)>0: + self.print_help() + sys.exit(1) + # ManifoldUploader + pass + +# Thierry: I'm turning this off, no idea what it's used for +# @register_command("cred","") def trusted(self, options, args): """ - return uhe trusted certs at this interface (get_trusted_certs) + return the trusted certs at this interface (get_trusted_certs) """ trusted_certs = self.registry().get_trusted_certs() for trusted_cert in trusted_certs: @@ -1565,6 +1502,3 @@ or with an slice hrn, shows currently provisioned resources self.logger.debug('Sfi.trusted -> %r'%cert.get_subject()) return - def config (self, options, args): - "Display contents of current config" - self.show_config()