X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Fclient%2Fsfi.py;h=be94f8205374b7a721fb10574f2b036d9664d284;hb=4ef76bd85ea24906c4ebb580c896fe1084d6ba88;hp=2ce796b01cd10ee435730bc125be0a4da2db95cd;hpb=dcec4892ea998e7df5297ba08a7a93da0e8d7053;p=sfa.git diff --git a/sfa/client/sfi.py b/sfa/client/sfi.py index 2ce796b0..be94f820 100644 --- a/sfa/client/sfi.py +++ b/sfa/client/sfi.py @@ -3,6 +3,8 @@ # this module is also used in sfascan # +from __future__ import print_function + import sys sys.path.append('.') @@ -31,6 +33,7 @@ from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn, Xrn from sfa.util.config import Config from sfa.util.version import version_core from sfa.util.cache import Cache +from sfa.util.printable import printable from sfa.storage.record import Record @@ -45,7 +48,8 @@ from sfa.client.return_value import ReturnValue from sfa.client.candidates import Candidates from sfa.client.manifolduploader import ManifoldUploader -CM_PORT=12346 +CM_PORT = 12346 +DEFAULT_RSPEC_VERSION = "GENI 3" from sfa.client.common import optparse_listvalue_callback, optparse_dictvalue_callback, \ terminal_render, filter_records @@ -65,12 +69,12 @@ def display_rspec(rspec, format='rspec'): else: result = rspec - print result + print(result) return def display_list(results): for result in results: - print result + print(result) def display_records(recordList, dump=False): ''' Print all fields in the record''' @@ -82,7 +86,7 @@ def display_record(record, dump=False): record.dump(sort=True) else: info = record.getdict() - print "%s (%s)" % (info['hrn'], info['type']) + print("{} ({})".format(info['hrn'], info['type'])) return @@ -95,87 +99,83 @@ def filter_records(type, records): def credential_printable (cred): - credential=Credential(cred=cred) + credential = Credential(cred=cred) result="" - result += credential.get_summary_tostring() + result += credential.pretty_cred() result += "\n" rights = credential.get_privileges() - result += "type=%s\n" % credential.type - result += "version=%s\n" % credential.version - result += "rights=%s\n"%rights + result += "type={}\n".format(credential.type) + result += "version={}\n".format(credential.version) + result += "rights={}\n".format(rights) return result def show_credentials (cred_s): if not isinstance (cred_s,list): cred_s = [cred_s] for cred in cred_s: - print "Using Credential %s"%credential_printable(cred) + print("Using Credential {}".format(credential_printable(cred))) + +########## save methods -# save methods -def save_raw_to_file(var, filename, format="text", banner=None): - if filename == "-": - # if filename is "-", send it to stdout - f = sys.stdout +### raw +def save_raw_to_file(var, filename, format='text', banner=None): + if filename == '-': + _save_raw_to_file(var, sys.stdout, format, banner) else: - f = open(filename, "w") - if banner: - f.write(banner+"\n") + with open(filename, w) as fileobj: + _save_raw_to_file(var, fileobj, format, banner) + print("(Over)wrote {}".format(filename)) + +def _save_raw_to_file(var, f, format, banner): if format == "text": - f.write(str(var)) + if banner: f.write(banner+"\n") + f.write("{}".format(var)) + if banner: f.write('\n'+banner+"\n") elif format == "pickled": f.write(pickle.dumps(var)) elif format == "json": - if hasattr(json, "dumps"): - f.write(json.dumps(var)) # python 2.6 - else: - f.write(json.write(var)) # python 2.5 + f.write(json.dumps(var)) # python 2.6 else: # this should never happen - print "unknown output format", format - if banner: - f.write('\n'+banner+"\n") + print("unknown output format", format) +### def save_rspec_to_file(rspec, filename): if not filename.endswith(".rspec"): filename = filename + ".rspec" - f = open(filename, 'w') - f.write("%s"%rspec) - f.close() - return + with open(filename, 'w') as f: + f.write("{}".format(rspec)) + print("(Over)wrote {}".format(filename)) + +def save_record_to_file(filename, record_dict): + record = Record(dict=record_dict) + xml = record.save_as_xml() + with codecs.open(filename, encoding='utf-8',mode="w") as f: + f.write(xml) + print("(Over)wrote {}".format(filename)) def save_records_to_file(filename, record_dicts, format="xml"): if format == "xml": - index = 0 - for record_dict in record_dicts: - if index > 0: - save_record_to_file(filename + "." + str(index), record_dict) - else: - save_record_to_file(filename, record_dict) - index = index + 1 + for index, record_dict in enumerate(record_dicts): + save_record_to_file(filename + "." + str(index), record_dict) elif format == "xmllist": - f = open(filename, "w") - f.write("\n") - for record_dict in record_dicts: - record_obj=Record(dict=record_dict) - f.write('\n') - f.write("\n") - f.close() + with open(filename, "w") as f: + f.write("\n") + for record_dict in record_dicts: + record_obj = Record(dict=record_dict) + f.write('\n') + f.write("\n") + print("(Over)wrote {}".format(filename)) + elif format == "hrnlist": - f = open(filename, "w") - for record_dict in record_dicts: - record_obj=Record(dict=record_dict) - f.write(record_obj.hrn + "\n") - f.close() + with open(filename, "w") as f: + for record_dict in record_dicts: + record_obj = Record(dict=record_dict) + f.write(record_obj.hrn + "\n") + print("(Over)wrote {}".format(filename)) + else: # this should never happen - print "unknown output format", format - -def save_record_to_file(filename, record_dict): - record = Record(dict=record_dict) - xml = record.save_as_xml() - f=codecs.open(filename, encoding='utf-8',mode="w") - f.write(xml) - f.close() - return + print("unknown output format", format) # minimally check a key argument def check_ssh_key (key): @@ -183,6 +183,23 @@ def check_ssh_key (key): return re.match(good_ssh_key, key, re.IGNORECASE) # load methods +def normalize_type (type): + if type.startswith('au'): + return 'authority' + elif type.startswith('us'): + return 'user' + elif type.startswith('sl'): + return 'slice' + elif type.startswith('no'): + return 'node' + elif type.startswith('ag'): + return 'aggregate' + elif type.startswith('al'): + return 'all' + else: + print('unknown type {} - should start with one of au|us|sl|no|ag|al'.format(type)) + return None + def load_record_from_opts(options): record_dict = {} if hasattr(options, 'xrn') and options.xrn: @@ -200,15 +217,18 @@ def load_record_from_opts(options): pubkey = options.key if not check_ssh_key (pubkey): raise SfaInvalidArgument(name='key',msg="Could not find file, or wrong key format") - record_dict['keys'] = [pubkey] + record_dict['reg-keys'] = [pubkey] if hasattr(options, 'slices') and options.slices: record_dict['slices'] = options.slices - if hasattr(options, 'researchers') and options.researchers is not None: - record_dict['researcher'] = options.researchers + if hasattr(options, 'reg_researchers') and options.reg_researchers is not None: + record_dict['reg-researchers'] = options.reg_researchers if hasattr(options, 'email') and options.email: record_dict['email'] = options.email - if hasattr(options, 'pis') and options.pis: - record_dict['pi'] = options.pis + # authorities can have a name for standalone deployment + if hasattr(options, 'name') and options.name: + record_dict['name'] = options.name + if hasattr(options, 'reg_pis') and options.reg_pis: + record_dict['reg-pis'] = options.reg_pis # handle extra settings record_dict.update(options.extras) @@ -216,11 +236,9 @@ def load_record_from_opts(options): return Record(dict=record_dict) def load_record_from_file(filename): - f=codecs.open(filename, encoding="utf-8", mode="r") - xml_string = f.read() - f.close() - return Record(xml=xml_string) - + with codecs.open(filename, encoding="utf-8", mode="r") as f: + xml_str = f.read() + return Record(xml=xml_str) import uuid def unique_call_id(): return uuid.uuid4().urn @@ -259,6 +277,11 @@ def declare_command (args_string, example,aliases=None): return new_method return wrap + +def remove_none_fields (record): + none_fields=[ k for (k,v) in record.items() if v is None ] + for k in none_fields: del record[k] + ########## class Sfi: @@ -298,53 +321,54 @@ class Sfi: ### suitable if no reasonable command has been provided def print_commands_help (self, options): verbose=getattr(options,'verbose') - format3="%10s %-30s %s" - format3offset=42 + format3="%10s %-35s %s" + format3offset=47 line=80*'-' if not verbose: - print format3%("command","cmd_args","description") - print line + print(format3%("command", "cmd_args", "description")) + print(line) else: - print line + print(line) self.create_parser_global().print_help() # preserve order from the code for command in commands_list: try: (doc, args_string, example, canonical) = commands_dict[command] except: - print "Cannot find info on command %s - skipped"%command + print("Cannot find info on command %s - skipped"%command) continue if verbose: - print line + print(line) if command==canonical: - doc=doc.replace("\n","\n"+format3offset*' ') - print format3%(command,args_string,doc) + doc = doc.replace("\n", "\n" + format3offset * ' ') + print(format3 % (command,args_string,doc)) if verbose: self.create_parser_command(command).print_help() else: - print format3%(command,"<>"%canonical,"") + print(format3 % (command,"<>"%canonical,"")) ### now if a known command was found we can be more verbose on that one def print_help (self): - print "==================== Generic sfi usage" + print("==================== Generic sfi usage") self.sfi_parser.print_help() - (doc,_,example,canonical)=commands_dict[self.command] + (doc, _, example, canonical) = commands_dict[self.command] if canonical != self.command: - print "\n==================== NOTE: %s is an alias for genuine %s"%(self.command,canonical) - self.command=canonical - print "\n==================== Purpose of %s"%self.command - print doc - print "\n==================== Specific usage for %s"%self.command + print("\n==================== NOTE: {} is an alias for genuine {}" + .format(self.command, canonical)) + self.command = canonical + print("\n==================== Purpose of {}".format(self.command)) + print(doc) + print("\n==================== Specific usage for {}".format(self.command)) self.command_parser.print_help() if example: - print "\n==================== %s example(s)"%self.command - print example + print("\n==================== {} example(s)".format(self.command)) + print(example) def create_parser_global(self): # Generate command line parser parser = OptionParser(add_help_option=False, usage="sfi [sfi_options] command [cmd_options] [cmd_args]", - description="Commands: %s"%(" ".join(commands_list))) + description="Commands: {}".format(" ".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", @@ -391,47 +415,48 @@ class Sfi: sys.exit(2) # retrieve args_string - (_, args_string, __,___) = commands_dict[command] + (_, args_string, __,canonical) = commands_dict[command] parser = OptionParser(add_help_option=False, - usage="sfi [sfi_options] %s [cmd_options] %s" - % (command, args_string)) + usage="sfi [sfi_options] {} [cmd_options] {}"\ + .format(command, args_string)) parser.add_option ("-h","--help",dest='help',action='store_true',default=False, help="Summary of one command usage") - if command in ("config"): + if canonical in ("config"): parser.add_option('-m', '--myslice', dest='myslice', action='store_true', default=False, help='how myslice config variables as well') - if command in ("version"): + if canonical in ("version"): parser.add_option("-l","--local", action="store_true", dest="version_local", default=False, help="display version of the local client") - if command in ("version", "trusted"): + if canonical in ("version", "trusted"): parser.add_option("-R","--registry_interface", action="store_true", dest="registry_interface", default=False, help="target the registry interface instead of slice interface") - if command in ("register", "update"): + if canonical in ("register", "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('-t', '--type', dest='type', metavar='', help='object type (2 first chars is enough)', default=None) parser.add_option('-e', '--email', dest='email', default="", help="email (mandatory for users)") + parser.add_option('-n', '--name', dest='name', default="", help="name (optional for authorities)") 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='Set/replace slice xrns', default='', type="str", action='callback', callback=optparse_listvalue_callback) - parser.add_option('-r', '--researchers', dest='researchers', metavar='', - help='Set/replace slice researchers - use -r none to reset', default='', type="str", action='callback', + parser.add_option('-r', '--researchers', dest='reg_researchers', metavar='', + help='Set/replace slice researchers - use -r none to reset', default=None, type="str", action='callback', callback=optparse_listvalue_callback) - parser.add_option('-p', '--pis', dest='pis', metavar='', help='Set/replace Principal Investigators/Project Managers', + parser.add_option('-p', '--pis', dest='reg_pis', metavar='', help='Set/replace Principal Investigators/Project Managers', default='', type="str", action='callback', callback=optparse_listvalue_callback) 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", "allocate", "provision", "delete", "allocate", "provision", + if canonical in ("resources", "describe", "allocate", "provision", "delete", "allocate", "provision", "action", "shutdown", "renew", "status"): parser.add_option("-d", "--delegate", dest="delegate", default=None, action="store_true", @@ -439,24 +464,26 @@ class Sfi: "authority in set of credentials for this call") # show_credential option - if command in ("list","resources", "describe", "provision", "allocate", "register","update","remove","delete","status","renew"): + if canonical in ("list","resources", "describe", "provision", "allocate", "register","update","remove","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") + if canonical in ("renew"): + parser.add_option("-l","--as-long-as-possible",dest='alap',action='store_true',default=False, + help="renew as long as possible") # registy filter option - if command in ("list", "show", "remove"): - parser.add_option("-t", "--type", dest="type", type="choice", - help="type filter ([all]|user|slice|authority|node|aggregate)", - choices=("all", "user", "slice", "authority", "node", "aggregate"), - default="all") - if command in ("show"): + if canonical in ("list", "show", "remove"): + parser.add_option("-t", "--type", dest="type", metavar="", + default="all", + help="type filter - 2 first chars is enough ([all]|user|slice|authority|node|aggregate)") + if canonical in ("show"): parser.add_option("-k","--key",dest="keys",action="append",default=[], help="specify specific keys to be displayed from record") parser.add_option("-n","--no-details",dest="no_details",action="store_true",default=False, help="call Resolve without the 'details' option") - if command in ("resources", "describe"): + if canonical in ("resources", "describe"): # rspec version - parser.add_option("-r", "--rspec-version", dest="rspec_version", default="GENI 3", - help="schema type and version of resulting RSpec") + parser.add_option("-r", "--rspec-version", dest="rspec_version", default=DEFAULT_RSPEC_VERSION, + help="schema type and version of resulting RSpec (default:{})".format(DEFAULT_RSPEC_VERSION)) # disable/enable cached rspecs parser.add_option("-c", "--current", dest="current", default=False, action="store_true", @@ -468,17 +495,17 @@ class Sfi: #panos: a new option to define the type of information about resources a user is interested in parser.add_option("-i", "--info", dest="info", help="optional component information", default=None) - # a new option to retreive or not reservation-oriented RSpecs (leases) + # a new option to retrieve or not reservation-oriented RSpecs (leases) parser.add_option("-l", "--list_leases", dest="list_leases", type="choice", - help="Retreive or not reservation-oriented RSpecs ([resources]|leases|all )", + help="Retrieve or not reservation-oriented RSpecs ([resources]|leases|all)", choices=("all", "resources", "leases"), default="resources") - if command in ("resources", "describe", "allocate", "provision", "show", "list", "gid"): + if canonical in ("resources", "describe", "allocate", "provision", "show", "list", "gid"): parser.add_option("-o", "--output", dest="file", help="output XML to file", metavar="FILE", default=None) - if command in ("show", "list"): + if canonical in ("show", "list"): parser.add_option("-f", "--format", dest="format", type="choice", help="display format ([text]|xml)", default="text", choices=("text", "xml")) @@ -486,12 +513,12 @@ class Sfi: parser.add_option("-F", "--fileformat", dest="fileformat", type="choice", help="output file format ([xml]|xmllist|hrnlist)", default="xml", choices=("xml", "xmllist", "hrnlist")) - if command == 'list': + if canonical == '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"): + if canonical in ("delegate"): parser.add_option("-u", "--user", action="store_true", dest="delegate_user", default=False, help="delegate your own credentials; default if no other option is provided") @@ -499,14 +526,14 @@ class Sfi: 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 + # 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^") + 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 ("myslice"): + if canonical in ("myslice"): parser.add_option("-p","--password",dest='password',action='store',default=None, help="specify mainfold password on the command line") parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[], @@ -523,10 +550,15 @@ use this if you mean an authority instead""") # Main: parse arguments and dispatch to command # def dispatch(self, command, command_options, command_args): - method=getattr(self, command, None) + (doc, args_string, example, canonical) = commands_dict[command] + method=getattr(self, canonical, None) if not method: - print "Unknown command %s"%command - return + print("sfi: unknown command {}".format(command)) + raise SystemExit("Unknown command {}".format(command)) + for arg in command_args: + if 'help' in arg or arg == '-h': + self.print_help() + sys.exit(1) return method(command_options, command_args) def main(self): @@ -560,19 +592,24 @@ use this if you mean an authority instead""") sys.exit(1) self.command_options = command_options + # allow incoming types on 2 characters only + if hasattr(command_options, 'type'): + command_options.type = normalize_type(command_options.type) + if not command_options.type: + sys.exit(1) + self.read_config () self.bootstrap () - self.logger.debug("Command=%s" % self.command) + self.logger.debug("Command={}".format(self.command)) try: - self.dispatch(command, command_options, command_args) + retcod = self.dispatch(command, command_options, command_args) except SystemExit: return 1 except: - self.logger.log_exc ("sfi command %s failed"%command) + self.logger.log_exc ("sfi command {} failed".format(command)) return 1 - - return 0 + return retcod #################### def read_config(self): @@ -600,12 +637,12 @@ use this if you mean an authority instead""") config.save(config_file) except: - self.logger.critical("Failed to read configuration file %s"%config_file) + self.logger.critical("Failed to read configuration file {}".format(config_file)) self.logger.info("Make sure to remove the export clauses and to add quotes") if self.options.verbose==0: self.logger.info("Re-run with -v for more details") else: - self.logger.log_exc("Could not read config file %s"%config_file) + self.logger.log_exc("Could not read config file {}".format(config_file)) sys.exit(1) self.config_instance=config @@ -616,7 +653,7 @@ use this if you mean an authority instead""") elif hasattr(config, "SFI_SM"): self.sm_url = config.SFI_SM else: - self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file) + self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in {}".format(config_file)) errors += 1 # Set Registry URL @@ -625,7 +662,7 @@ use this if you mean an authority instead""") elif hasattr(config, "SFI_REGISTRY"): self.reg_url = config.SFI_REGISTRY else: - self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file) + self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in {}".format(config_file)) errors += 1 # Set user HRN @@ -634,7 +671,7 @@ use this if you mean an authority instead""") elif hasattr(config, "SFI_USER"): self.user = config.SFI_USER else: - self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file) + self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in {}".format(config_file)) errors += 1 # Set authority HRN @@ -643,7 +680,7 @@ use this if you mean an authority instead""") elif hasattr(config, "SFI_AUTH"): self.authority = config.SFI_AUTH else: - self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file) + self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in {}".format(config_file)) errors += 1 self.config_file=config_file @@ -665,6 +702,8 @@ use this if you mean an authority instead""") # init self-signed cert, user credentials and gid def bootstrap (self): + if self.options.verbose: + self.logger.info("Initializing SfaClientBootstrap with {}".format(self.reg_url)) client_bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir, logger=self.logger) # if -k is provided, use this to initialize private key @@ -676,10 +715,13 @@ use this if you mean an authority instead""") if not os.path.isfile(client_bootstrap.private_key_filename()): self.logger.info ("private key not found, trying legacy name") try: - legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%Xrn.unescape(get_leaf(self.user))) - self.logger.debug("legacy_private_key=%s"%legacy_private_key) + legacy_private_key = os.path.join (self.options.sfi_dir, "{}.pkey" + .format(Xrn.unescape(get_leaf(self.user)))) + self.logger.debug("legacy_private_key={}" + .format(legacy_private_key)) client_bootstrap.init_private_key_if_missing (legacy_private_key) - self.logger.info("Copied private key from legacy location %s"%legacy_private_key) + self.logger.info("Copied private key from legacy location {}" + .format(legacy_private_key)) except: self.logger.log_exc("Can't find private key ") sys.exit(1) @@ -722,7 +764,8 @@ use this if you mean an authority instead""") 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) + self.logger.error("Object credential {} does not have delegate bit set" + .format(object_hrn)) return # the delegating user's gid @@ -741,9 +784,10 @@ use this if you mean an authority instead""") def registry (self): # cache the result if not hasattr (self, 'registry_proxy'): - self.logger.info("Contacting Registry at: %s"%self.reg_url) - self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid, - timeout=self.options.timeout, verbose=self.options.debug) + self.logger.info("Contacting Registry at: {}".format(self.reg_url)) + self.registry_proxy \ + = SfaServerProxy(self.reg_url, self.private_key, self.my_gid, + timeout=self.options.timeout, verbose=self.options.debug) return self.registry_proxy def sliceapi (self): @@ -756,17 +800,18 @@ use this if you mean an authority instead""") records = self.registry().Resolve(node_hrn, self.my_credential_string) records = filter_records('node', records) if not records: - self.logger.warning("No such component:%r"% opts.component) + self.logger.warning("No such component:{}".format(opts.component)) record = records[0] - cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT) + cm_url = "http://{}:{}/".format(record['hostname'], CM_PORT) self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid) else: # otherwise use what was provided as --sliceapi, or SFI_SM in the config if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'): self.sm_url = 'http://' + self.sm_url - self.logger.info("Contacting Slice Manager at: %s"%self.sm_url) - self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid, - timeout=self.options.timeout, verbose=self.options.debug) + self.logger.info("Contacting Slice Manager at: {}".format(self.sm_url)) + self.sliceapi_proxy \ + = SfaServerProxy(self.sm_url, self.private_key, self.my_gid, + timeout=self.options.timeout, verbose=self.options.debug) return self.sliceapi_proxy def get_cached_server_version(self, server): @@ -779,7 +824,7 @@ use this if you mean an authority instead""") cache = Cache(cache_file) except IOError: cache = Cache() - self.logger.info("Local cache not found at: %s" % cache_file) + self.logger.info("Local cache not found at: {}".format(cache_file)) if cache: version = cache.get(cache_key) @@ -789,7 +834,7 @@ use this if you mean an authority instead""") version= ReturnValue.get_value(result) # cache version for 20 minutes cache.add(cache_key, version, ttl= 60*20) - self.logger.info("Updating cache file %s" % cache_file) + self.logger.info("Updating cache file {}".format(cache_file)) cache.save_to_file(cache_file) return version @@ -845,7 +890,7 @@ use this if you mean an authority instead""") if (os.path.isfile(file)): return file else: - self.logger.critical("No such rspec file %s"%rspec) + self.logger.critical("No such rspec file {}".format(rspec)) sys.exit(1) def get_record_file(self, record): @@ -856,10 +901,22 @@ use this if you mean an authority instead""") if (os.path.isfile(file)): return file else: - self.logger.critical("No such registry record file %s"%record) + self.logger.critical("No such registry record file {}".format(record)) sys.exit(1) + # helper function to analyze raw output + # for main : return 0 if everything is fine, something else otherwise (mostly 1 for now) + def success (self, raw): + return_value=ReturnValue (raw) + output=ReturnValue.get_output(return_value) + # means everything is fine + if not output: + return 0 + # something went wrong + print('ERROR:', output) + return 1 + #========================================================================== # Following functions implement the commands # @@ -869,7 +926,7 @@ use this if you mean an authority instead""") @declare_command("","") def config (self, options, args): "Display contents of current config" - print "# From configuration file %s"%self.config_file + print("# From configuration file {}".format(self.config_file)) flags=[ ('sfi', [ ('registry','reg_url'), ('auth','authority'), ('user','user'), @@ -880,15 +937,17 @@ use this if you mean an authority instead""") flags.append ( ('myslice', ['backend', 'delegate', 'platform', 'username'] ) ) for (section, tuples) in flags: - print "[%s]"%section + print("[{}]".format(section)) try: - for (external_name, internal_name) in tuples: - print "%-20s = %s"%(external_name,getattr(self,internal_name)) + for external_name, internal_name in tuples: + print("{:<20} = {}".format(external_name, getattr(self, internal_name))) except: - for name in tuples: - varname="%s_%s"%(section.upper(),name.upper()) - value=getattr(self.config_instance,varname) - print "%-20s = %s"%(name,value) + for external_name, internal_name in tuples: + varname = "{}_{}".format(section.upper(), external_name.upper()) + value = getattr(self.config_instance,varname) + print("{:<20} = {}".format(external_name, value)) + # xxx should analyze result + return 0 @declare_command("","") def version(self, options, args): @@ -910,6 +969,8 @@ use this if you mean an authority instead""") else: pprinter = PrettyPrinter(indent=4) pprinter.pprint(version) + # xxx should analyze result + return 0 @declare_command("authority","") def list(self, options, args): @@ -937,7 +998,8 @@ use this if you mean an authority instead""") terminal_render (list, options) if options.file: save_records_to_file(options.file, list, options.fileformat) - return + # xxx should analyze result + return 0 @declare_command("name","") def show(self, options, args): @@ -954,7 +1016,7 @@ use this if you mean an authority instead""") record_dicts = self.registry().Resolve(hrn, self.my_credential_string, resolve_options) record_dicts = filter_records(options.type, record_dicts) if not record_dicts: - self.logger.error("No record of type %s"% options.type) + self.logger.error("No record of type {}".format(options.type)) return # user has required to focus on some keys if options.keys: @@ -968,10 +1030,11 @@ use this if you mean an authority instead""") records = [ Record(dict=record_dict) for record_dict in record_dicts ] for record in records: if (options.format == "text"): record.dump(sort=True) - else: print record.save_as_xml() + else: print(record.save_as_xml()) if options.file: save_records_to_file(options.file, record_dicts, options.fileformat) - return + # xxx should analyze result + return 0 # this historically was named 'add', it is now 'register' with an alias for legacy @declare_command("[xml-filename]","",['add']) @@ -991,12 +1054,12 @@ use this if you mean an authority instead""") try: record_filepath = args[0] rec_file = self.get_record_file(record_filepath) - record_dict.update(load_record_from_file(rec_file).todict()) + record_dict.update(load_record_from_file(rec_file).record_to_dict()) except: - print "Cannot load record file %s"%record_filepath + print("Cannot load record file {}".format(record_filepath)) sys.exit(1) if options: - record_dict.update(load_record_from_opts(options).todict()) + record_dict.update(load_record_from_opts(options).record_to_dict()) # we should have a type by now if 'type' not in record_dict : self.print_help() @@ -1008,7 +1071,11 @@ use this if you mean an authority instead""") record_dict['first_name'] = record_dict['hrn'] if 'last_name' not in record_dict: record_dict['last_name'] = record_dict['hrn'] - return self.registry().Register(record_dict, auth_cred) + register = self.registry().Register(record_dict, auth_cred) + # xxx looks like the result here is not ReturnValue-compatible + #return self.success (register) + # xxx should analyze result + return 0 @declare_command("[xml-filename]","") def update(self, options, args): @@ -1019,22 +1086,22 @@ use this if you mean an authority instead""") 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()) + record_dict.update(load_record_from_file(rec_file).record_to_dict()) if options: - record_dict.update(load_record_from_opts(options).todict()) + record_dict.update(load_record_from_opts(options).record_to_dict()) # at the very least we need 'type' here - if 'type' not in record_dict: + if 'type' not in record_dict or record_dict['type'] is None: self.print_help() sys.exit(1) # don't translate into an object, as this would possibly distort # user-provided data; e.g. add an 'email' field to Users - if record_dict['type'] == "user": + if record_dict['type'] in ['user']: if record_dict['hrn'] == self.user: cred = self.my_credential_string else: cred = self.my_authority_credential_string() - elif record_dict['type'] in ["slice"]: + elif record_dict['type'] in ['slice']: try: cred = self.slice_credential_string(record_dict['hrn']) except ServerException, e: @@ -1044,15 +1111,19 @@ use this if you mean an authority instead""") cred = self.my_authority_credential_string() else: raise - elif record_dict['type'] in ["authority"]: + elif record_dict['type'] in ['authority']: cred = self.my_authority_credential_string() - elif record_dict['type'] == 'node': + elif record_dict['type'] in ['node']: cred = self.my_authority_credential_string() else: - raise "unknown record type" + record_dict['type'] + raise Exception("unknown record type {}".format(record_dict['type'])) if options.show_credential: show_credentials(cred) - return self.registry().Update(record_dict, cred) + update = self.registry().Update(record_dict, cred) + # xxx looks like the result here is not ReturnValue-compatible + #return self.success(update) + # xxx should analyze result + return 0 @declare_command("hrn","") def remove(self, options, args): @@ -1067,14 +1138,18 @@ use this if you mean an authority instead""") type = '*' if options.show_credential: show_credentials(auth_cred) - return self.registry().Remove(hrn, auth_cred, type) + remove = self.registry().Remove(hrn, auth_cred, type) + # xxx looks like the result here is not ReturnValue-compatible + #return self.success (remove) + # xxx should analyze result + return 0 # ================================================================== # Slice-related commands # ================================================================== # show rspec for named slice - @declare_command("","") + @declare_command("","",['discover']) def resources(self, options, args): """ discover available resources (ListResources) @@ -1111,19 +1186,19 @@ use this if you mean an authority instead""") # just request the version the client wants api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict() else: - api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'} + api_options['geni_rspec_version'] = {'type': options.rspec_version} else: api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'} - result = server.ListResources (creds, api_options) - value = ReturnValue.get_value(result) + + list_resources = server.ListResources (creds, api_options) + value = ReturnValue.get_value(list_resources) if self.options.raw: - save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) + save_raw_to_file(list_resources, 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): display_rspec(value, options.format) - - return + return self.success(list_resources) @declare_command("slice_hrn","") def describe(self, options, args): @@ -1142,7 +1217,7 @@ use this if you mean an authority instead""") api_options = {'call_id': unique_call_id(), 'cached': True, - #'info': options.info, + 'info': options.info, 'list_leases': options.list_leases, 'geni_rspec_version': {'type': 'geni', 'version': '3'}, } @@ -1157,17 +1232,17 @@ use this if you mean an authority instead""") api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict() else: api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'} - urn = Xrn(args[0], type='slice').get_urn() - result = server.Describe([urn], creds, api_options) - value = ReturnValue.get_value(result) + urn = Xrn(args[0], type='slice').get_urn() + remove_none_fields(api_options) + describe = server.Describe([urn], creds, api_options) + value = ReturnValue.get_value(describe) if self.options.raw: - save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) + save_raw_to_file(describe, self.options.raw, self.options.rawformat, self.options.rawbanner) if options.file is not None: save_rspec_to_file(value['geni_rspec'], options.file) if (self.options.raw is None) and (options.file is None): - display_rspec(value, options.format) - - return + display_rspec(value['geni_rspec'], options.format) + return self.success (describe) @declare_command("slice_hrn [...]","") def delete(self, options, args): @@ -1196,13 +1271,13 @@ use this if you mean an authority instead""") api_options ['call_id'] = unique_call_id() if options.show_credential: show_credentials(creds) - result = server.Delete(sliver_urns, creds, *self.ois(server, api_options ) ) - value = ReturnValue.get_value(result) + delete = server.Delete(sliver_urns, creds, *self.ois(server, api_options ) ) + value = ReturnValue.get_value(delete) if self.options.raw: - save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) + save_raw_to_file(delete, self.options.raw, self.options.rawformat, self.options.rawbanner) else: - print value - return value + print(value) + return self.success (delete) @declare_command("slice_hrn rspec","") def allocate(self, options, args): @@ -1211,7 +1286,12 @@ use this if you mean an authority instead""") """ server = self.sliceapi() server_version = self.get_cached_server_version(server) + if len(args) != 2: + self.print_help() + sys.exit(1) slice_hrn = args[0] + rspec_file = self.get_rspec_file(args[1]) + slice_urn = Xrn(slice_hrn, type='slice').get_urn() # credentials @@ -1231,14 +1311,13 @@ use this if you mean an authority instead""") show_credentials(creds) # rspec - rspec_file = self.get_rspec_file(args[1]) - rspec = open(rspec_file).read() api_options = {} api_options ['call_id'] = unique_call_id() # users sfa_users = [] geni_users = [] slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string]) + remove_none_fields(slice_records[0]) 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'] @@ -1250,16 +1329,17 @@ use this if you mean an authority instead""") api_options['sfa_users'] = sfa_users api_options['geni_users'] = geni_users - result = server.Allocate(slice_urn, creds, rspec, api_options) - value = ReturnValue.get_value(result) + with open(rspec_file) as rspec: + rspec_xml = rspec.read() + allocate = server.Allocate(slice_urn, creds, rspec_xml, api_options) + value = ReturnValue.get_value(allocate) if self.options.raw: - save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) + save_raw_to_file(allocate, self.options.raw, self.options.rawformat, self.options.rawbanner) if options.file is not None: save_rspec_to_file (value['geni_rspec'], options.file) if (self.options.raw is None) and (options.file is None): - print value - return value - + print(value) + return self.success(allocate) @declare_command("slice_hrn [...]","") def provision(self, options, args): @@ -1316,15 +1396,15 @@ use this if you mean an authority instead""") users = pg_users_arg(user_records) api_options['geni_users'] = users - result = server.Provision(sliver_urns, creds, api_options) - value = ReturnValue.get_value(result) + provision = server.Provision(sliver_urns, creds, api_options) + value = ReturnValue.get_value(provision) if self.options.raw: - save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) + save_raw_to_file(provision, self.options.raw, self.options.rawformat, self.options.rawbanner) if options.file is not None: save_rspec_to_file (value['geni_rspec'], options.file) if (self.options.raw is None) and (options.file is None): - print value - return value + print(value) + return self.success(provision) @declare_command("slice_hrn","") def status(self, options, args): @@ -1346,14 +1426,13 @@ use this if you mean an authority instead""") 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) + status = server.Status([slice_urn], creds, *self.ois(server,api_options)) + value = ReturnValue.get_value(status) if self.options.raw: - save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) + save_raw_to_file(status, self.options.raw, self.options.rawformat, self.options.rawbanner) else: - print value - # Thierry: seemed to be missing - return value + print(value) + return self.success (status) @declare_command("slice_hrn [...] action","") def action(self, options, args): @@ -1379,15 +1458,20 @@ use this if you mean an authority instead""") delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) creds.append(delegated_cred) - result = server.PerformOperationalAction(sliver_urns, creds, action , api_options) - value = ReturnValue.get_value(result) + perform_action = server.PerformOperationalAction(sliver_urns, creds, action , api_options) + value = ReturnValue.get_value(perform_action) if self.options.raw: - save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) + save_raw_to_file(perform_action, self.options.raw, self.options.rawformat, self.options.rawbanner) else: - print value - return value - - @declare_command("slice_hrn [...] time","") + print(value) + return self.success (perform_action) + + @declare_command("slice_hrn [...] time", + "\n".join(["sfi renew onelab.ple.heartbeat 2015-04-31", + "sfi renew onelab.ple.heartbeat 2015-04-31T14:00:00Z", + "sfi renew onelab.ple.heartbeat +5d", + "sfi renew onelab.ple.heartbeat +3w", + "sfi renew onelab.ple.heartbeat +2m",])) def renew(self, options, args): """ renew slice (Renew) @@ -1414,16 +1498,17 @@ use this if you mean an authority instead""") # options and call_id when supported api_options = {} api_options['call_id']=unique_call_id() + if options.alap: + api_options['geni_extend_alap']=True if options.show_credential: show_credentials(creds) - result = server.Renew(sliver_urns, creds, input_time, *self.ois(server,api_options)) - value = ReturnValue.get_value(result) + renew = server.Renew(sliver_urns, creds, input_time, *self.ois(server,api_options)) + value = ReturnValue.get_value(renew) if self.options.raw: - save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) + save_raw_to_file(renew, self.options.raw, self.options.rawformat, self.options.rawbanner) else: - print value - return value - + print(value) + return self.success(renew) @declare_command("slice_hrn","") def shutdown(self, options, args): @@ -1437,14 +1522,13 @@ use this if you mean an authority instead""") # creds slice_cred = self.slice_credential(slice_hrn) creds = [slice_cred] - result = server.Shutdown(slice_urn, creds) - value = ReturnValue.get_value(result) + shutdown = server.Shutdown(slice_urn, creds) + value = ReturnValue.get_value(shutdown) if self.options.raw: - save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) + save_raw_to_file(shutdown, self.options.raw, self.options.rawformat, self.options.rawbanner) else: - print value - return value - + print(value) + return self.success (shutdown) @declare_command("[name]","") def gid(self, options, args): @@ -1460,9 +1544,11 @@ use this if you mean an authority instead""") if options.file: filename = options.file else: - filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn]) - self.logger.info("writing %s gid to %s" % (target_hrn, filename)) + filename = os.sep.join([self.options.sfi_dir, '{}.gid'.format(target_hrn)]) + self.logger.info("writing {} gid to {}".format(target_hrn, filename)) GID(string=gid).save_to_file(filename) + # xxx should analyze result + return 0 #################### @declare_command("to_hrn","""$ sfi delegate -u -p -s ple.inria.heartbeat -s ple.inria.omftest ple.upmc.slicebrowser @@ -1490,25 +1576,25 @@ use this if you mean an authority instead""") to_hrn = args[0] # support for several delegations in the same call # so first we gather the things to do - tuples=[] + tuples = [] for slice_hrn in options.delegate_slices: - message="%s.slice"%slice_hrn + message = "{}.slice".format(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 + message = "{}.pi".format(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) + message = "{}.auth".format(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: - message="%s.user"%self.user + message = "{}.user".format(self.user) original = self.my_credential_string tuples.append ( (message, original, ) ) @@ -1521,10 +1607,11 @@ use this if you mean an authority instead""") 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)) + filename = os.path.join(self.options.sfi_dir, + "{}_for_{}.{}.cred".format(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)) + self.logger.info("delegated credential for {} to {} and wrote to {}" + .format(message, to_hrn, filename)) #################### @declare_command("","""$ less +/myslice sfi_config @@ -1583,35 +1670,40 @@ $ sfi m -b http://mymanifold.foo.com:7080/ else: full_key="MYSLICE_" + key.upper() value=getattr(self.config_instance,full_key,None) - if value: myslice_dict[key]=value - else: print "Unsufficient config, missing key %s in [myslice] section of sfi_config"%key + if value: + myslice_dict[key]=value + else: + print("Unsufficient config, missing key {} in [myslice] section of sfi_config" + .format(key)) if len(myslice_dict) != len(myslice_keys): sys.exit(1) # (b) figure whether we are PI for the authority where we belong - self.logger.info("Resolving our own id %s"%self.user) + self.logger.info("Resolving our own id {}".format(self.user)) my_records=self.registry().Resolve(self.user,self.my_credential_string) - if len(my_records)!=1: print "Cannot Resolve %s -- exiting"%self.user; sys.exit(1) - my_record=my_records[0] + if len(my_records) != 1: + print("Cannot Resolve {} -- exiting".format(self.user)) + sys.exit(1) + my_record = my_records[0] my_auths_all = my_record['reg-pi-authorities'] - self.logger.info("Found %d authorities that we are PI for"%len(my_auths_all)) - self.logger.debug("They are %s"%(my_auths_all)) + self.logger.info("Found {} authorities that we are PI for".format(len(my_auths_all))) + self.logger.debug("They are {}".format(my_auths_all)) my_auths = my_auths_all if options.delegate_auths: my_auths = list(set(my_auths_all).intersection(set(options.delegate_auths))) - self.logger.debug("Restricted to user-provided auths"%(my_auths)) + self.logger.debug("Restricted to user-provided auths {}".format(my_auths)) # (c) get the set of slices that we are in my_slices_all=my_record['reg-slices'] - self.logger.info("Found %d slices that we are member of"%len(my_slices_all)) - self.logger.debug("They are: %s"%(my_slices_all)) + self.logger.info("Found {} slices that we are member of".format(len(my_slices_all))) + self.logger.debug("They are: {}".format(my_slices_all)) my_slices = my_slices_all # if user provided slices, deal only with these - if they are found if options.delegate_slices: my_slices = list(set(my_slices_all).intersection(set(options.delegate_slices))) - self.logger.debug("Restricted to user-provided slices: %s"%(my_slices)) + self.logger.debug("Restricted to user-provided slices: {}".format(my_slices)) # (d) make sure we have *valid* credentials for all these hrn_credentials=[] @@ -1631,16 +1723,17 @@ $ sfi m -b http://mymanifold.foo.com:7080/ delegated_credential = self.client_bootstrap.delegate_credential_string (credential, delegatee_hrn, delegatee_type) # save these so user can monitor what she's uploaded filename = os.path.join ( self.options.sfi_dir, - "%s.%s_for_%s.%s.cred"%(hrn,htype,delegatee_hrn,delegatee_type)) + "{}.{}_for_{}.{}.cred"\ + .format(hrn, htype, delegatee_hrn, delegatee_type)) with file(filename,'w') as f: f.write(delegated_credential) - self.logger.debug("(Over)wrote %s"%filename) + self.logger.debug("(Over)wrote {}".format(filename)) hrn_delegated_credentials.append ((hrn, htype, delegated_credential, filename, )) # (f) and finally upload them to manifold server # xxx todo add an option so the password can be set on the command line # (but *NOT* in the config file) so other apps can leverage this - self.logger.info("Uploading on backend at %s"%myslice_dict['backend']) + self.logger.info("Uploading on backend at {}".format(myslice_dict['backend'])) uploader = ManifoldUploader (logger=self.logger, url=myslice_dict['backend'], platform=myslice_dict['platform'], @@ -1652,18 +1745,20 @@ $ sfi m -b http://mymanifold.foo.com:7080/ # inspect inspect=Credential(string=delegated_credential) expire_datetime=inspect.get_expiration() - message="%s (%s) [exp:%s]"%(hrn,htype,expire_datetime) + message="{} ({}) [exp:{}]".format(hrn, htype, expire_datetime) if uploader.upload(delegated_credential,message=message): count_success+=1 count_all+=1 - self.logger.info("Successfully uploaded %d/%d credentials"%(count_success,count_all)) + self.logger.info("Successfully uploaded {}/{} credentials" + .format(count_success, count_all)) # at first I thought we would want to save these, # like 'sfi delegate does' but on second thought # it is probably not helpful as people would not # need to run 'sfi delegate' at all anymore if count_success != count_all: sys.exit(1) - return + # xxx should analyze result + return 0 @declare_command("cred","") def trusted(self, options, args): @@ -1680,11 +1775,11 @@ $ sfi m -b http://mymanifold.foo.com:7080/ trusted_certs = ReturnValue.get_value(trusted_certs) for trusted_cert in trusted_certs: - print "\n===========================================================\n" + print("\n===========================================================\n") gid = GID(string=trusted_cert) gid.dump() cert = Certificate(string=trusted_cert) - self.logger.debug('Sfi.trusted -> %r'%cert.get_subject()) - print "Certificate:\n%s\n\n"%trusted_cert - return - + self.logger.debug('Sfi.trusted -> {}'.format(cert.get_subject())) + print("Certificate:\n{}\n\n".format(trusted_cert)) + # xxx should analyze result + return 0