X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Fclient%2Fsfi.py;h=a9725f4f69270cd17f3ecb0381be0c7883e2db82;hb=b6dcfc034473c011988fba0d02bbeb6406925465;hp=a46aa4c905d2547bfebcb7cbe84a95ad3ec6dbec;hpb=1d2d5966fc9dac88f9c171188af83f1219e9979a;p=sfa.git diff --git a/sfa/client/sfi.py b/sfa/client/sfi.py index a46aa4c9..a9725f4f 100644 --- a/sfa/client/sfi.py +++ b/sfa/client/sfi.py @@ -1,30 +1,36 @@ - -# xxx NOTE this will soon be reviewed to take advantage of sfaclientlib +# +# sfi.py - basic SFA command-line client +# this module is also used in sfascan +# import sys sys.path.append('.') import os, os.path import socket +import re import datetime import codecs import pickle +import json from lxml import etree from StringIO import StringIO from optparse import OptionParser +from pprint import PrettyPrinter from sfa.trust.certificate import Keypair, Certificate from sfa.trust.gid import GID from sfa.trust.credential import Credential from sfa.trust.sfaticket import SfaTicket +from sfa.util.faults import SfaInvalidArgument from sfa.util.sfalogging import sfi_logger -from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn +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.storage.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord +from sfa.storage.record import Record from sfa.rspecs.rspec import RSpec from sfa.rspecs.rspec_converter import RSpecConverter @@ -34,11 +40,35 @@ from sfa.client.sfaclientlib import SfaClientBootstrap 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 -AGGREGATE_PORT=12346 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) + # display methods def display_rspec(rspec, format='rspec'): if format in ['dns']: @@ -68,7 +98,7 @@ def display_records(recordList, dump=False): def display_record(record, dump=False): if dump: - record.dump() + record.dump(sort=True) else: info = record.getdict() print "%s (%s)" % (info['hrn'], info['type']) @@ -83,17 +113,44 @@ def filter_records(type, records): return filtered_records +def credential_printable (credential_string): + credential=Credential(string=credential_string) + result="" + result += credential.get_summary_tostring() + result += "\n" + rights = credential.get_privileges() + result += "rights=%s"%rights + result += "\n" + 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) + # save methods -def save_variable_to_file(var, filename, format="text"): - f = open(filename, "w") +def save_raw_to_file(var, filename, format="text", banner=None): + if filename == "-": + # if filename is "-", send it to stdout + f = sys.stdout + else: + f = open(filename, "w") + if banner: + f.write(banner+"\n") if format == "text": f.write(str(var)) 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 else: # this should never happen print "unknown output format", format - + if banner: + f.write('\n'+banner+"\n") def save_rspec_to_file(rspec, filename): if not filename.endswith(".rspec"): @@ -103,58 +160,84 @@ def save_rspec_to_file(rspec, filename): f.close() return -def save_records_to_file(filename, recordList, format="xml"): +def save_records_to_file(filename, record_dicts, format="xml"): if format == "xml": index = 0 - for record in recordList: + for record_dict in record_dicts: if index > 0: - save_record_to_file(filename + "." + str(index), record) + save_record_to_file(filename + "." + str(index), record_dict) else: - save_record_to_file(filename, record) + save_record_to_file(filename, record_dict) index = index + 1 elif format == "xmllist": f = open(filename, "w") f.write("\n") - for record in recordList: - record = SfaRecord(dict=record) - f.write('\n') + for record_dict in record_dicts: + record_obj=Record(dict=record_dict) + f.write('\n') f.write("\n") f.close() elif format == "hrnlist": f = open(filename, "w") - for record in recordList: - record = SfaRecord(dict=record) - f.write(record.get_name() + "\n") + for record_dict in record_dicts: + record_obj=Record(dict=record_dict) + f.write(record_obj.hrn + "\n") f.close() else: # this should never happen print "unknown output format", format -def save_record_to_file(filename, record): - if record['type'] in ['user']: - record = UserRecord(dict=record) - elif record['type'] in ['slice']: - record = SliceRecord(dict=record) - elif record['type'] in ['node']: - record = NodeRecord(dict=record) - elif record['type'] in ['authority', 'ma', 'sa']: - record = AuthorityRecord(dict=record) - else: - record = SfaRecord(dict=record) - str = record.save_to_string() +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(str) + f.write(xml) f.close() return +# minimally check a key argument +def check_ssh_key (key): + good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$' + return re.match(good_ssh_key, key, re.IGNORECASE) # load methods +def load_record_from_opts(options): + record_dict = {} + if hasattr(options, 'xrn') and options.xrn: + if hasattr(options, 'type') and options.type: + xrn = Xrn(options.xrn, options.type) + else: + xrn = Xrn(options.xrn) + record_dict['urn'] = xrn.get_urn() + record_dict['hrn'] = xrn.get_hrn() + record_dict['type'] = xrn.get_type() + if hasattr(options, 'key') and options.key: + try: + pubkey = open(options.key, 'r').read() + except IOError: + 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] + if hasattr(options, 'slices') and options.slices: + record_dict['slices'] = options.slices + if hasattr(options, 'researchers') and options.researchers: + record_dict['researcher'] = options.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 + + # handle extra settings + record_dict.update(options.extras) + + return Record(dict=record_dict) + def load_record_from_file(filename): f=codecs.open(filename, encoding="utf-8", mode="r") - str = f.read() + xml_string = f.read() f.close() - record = SfaRecord(string=str) - return record + return Record(xml=xml_string) import uuid @@ -162,7 +245,8 @@ def unique_call_id(): return uuid.uuid4().urn class Sfi: - required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user'] + # dirty hack to make this class usable from the outside + required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user', 'user_private_key'] @staticmethod def default_sfi_dir (): @@ -182,8 +266,6 @@ class Sfi: if not hasattr(options,opt): setattr(options,opt,None) if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir() self.options = options - self.slicemgr = None - self.registry = None self.user = None self.authority = None self.logger = sfi_logger @@ -212,8 +294,9 @@ class Sfi: ("get_ticket", "slice_hrn rspec"), ("redeem_ticket", "ticket"), ("delegate", "name"), - ("create_gid", "[name]"), - ("get_trusted_certs", "cred"), + ("gid", "[name]"), + ("trusted", "cred"), + ("config", ""), ] def print_command_help (self, options): @@ -238,9 +321,9 @@ class Sfi: print line print format3%(command,args,doc) if verbose: - self.create_cmd_parser(command).print_help() + self.create_command_parser(command).print_help() - def create_cmd_parser(self, command): + def create_command_parser(self, command): if command not in self.available_dict: msg="Invalid command\n" msg+="Commands: " @@ -251,40 +334,74 @@ class Sfi: parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \ % (command, self.available_dict[command])) + 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', + 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', + callback=optparse_listvalue_callback) + parser.add_option('-p', '--pis', dest='pis', metavar='', help='Principal Investigators/Project Managers', + default='', type="str", action='callback', callback=optparse_listvalue_callback) +# use --extra instead +# parser.add_option('-f', '--firstname', dest='firstname', metavar='', help='user first name') +# parser.add_option('-l', '--lastname', dest='lastname', metavar='', help='user last name') + parser.add_option ('-X','--extra',dest='extras',default={},type='str',metavar="", + action="callback", callback=optparse_dictvalue_callback, nargs=1, + help="set extra/testbed-dependent flags, e.g. --extra enabled=true") + # user specifies remote aggregate/sm/component if command in ("resources", "slices", "create", "delete", "start", "stop", "restart", "shutdown", "get_ticket", "renew", "status"): - parser.add_option("-a", "--aggregate", dest="aggregate", - default=None, help="aggregate host") - parser.add_option("-p", "--port", dest="port", - default=AGGREGATE_PORT, help="aggregate port") - parser.add_option("-c", "--component", dest="component", default=None, - help="component hrn") parser.add_option("-d", "--delegate", dest="delegate", default=None, action="store_true", help="Include a credential delegated to the user's root"+\ "authority in set of credentials for this call") + # show_credential option + if command in ("list","resources","create","add","update","remove","slices","delete","status","renew"): + parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False, + help="show credential(s) used in human-readable form") # 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") - # display formats + 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"): + # rspec version parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1", help="schema type and version of resulting RSpec") + # disable/enable cached rspecs + parser.add_option("-c", "--current", dest="current", default=False, + action="store_true", + help="Request the current rspec bypassing the cache. Cached rspecs are returned by default") + # display formats parser.add_option("-f", "--format", dest="format", type="choice", help="display format ([xml]|dns|ip)", default="xml", choices=("xml", "dns", "ip")) #panos: a new option to define the type of information about resources a user is interested in - parser.add_option("-i", "--info", dest="info", + parser.add_option("-i", "--info", dest="info", help="optional component information", default=None) + # a new option to retreive 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 )", + choices=("all", "resources", "leases"), default="resources") # 'create' does return the new rspec, makes sense to save that too - if command in ("resources", "show", "list", "create_gid", 'create'): + if command in ("resources", "show", "list", "gid", 'create'): parser.add_option("-o", "--output", dest="file", help="output XML to file", metavar="FILE", default=None) @@ -296,14 +413,9 @@ 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 in ("status", "version"): - parser.add_option("-o", "--output", dest="file", - help="output dictionary to file", metavar="FILE", default=None) - parser.add_option("-F", "--fileformat", dest="fileformat", type="choice", - help="output file format ([text]|pickled)", default="text", - choices=("text","pickled")) - + if command == 'list': + parser.add_option("-r", "--recursive", dest="recursive", action='store_true', + help="list all child records", default=False) if command in ("delegate"): parser.add_option("-u", "--user", action="store_true", dest="delegate_user", default=False, @@ -312,13 +424,9 @@ class Sfi: help="delegate slice credential", metavar="HRN", default=None) if command in ("version"): - parser.add_option("-a", "--aggregate", dest="aggregate", - default=None, help="aggregate host") - parser.add_option("-p", "--port", dest="port", - default=AGGREGATE_PORT, help="aggregate port") parser.add_option("-R","--registry-version", action="store_true", dest="version_registry", default=False, - help="probe registry version instead of slicemgr") + help="probe registry version instead of sliceapi") parser.add_option("-l","--local", action="store_true", dest="version_local", default=False, help="display version of the local client") @@ -333,8 +441,15 @@ class Sfi: description="Commands: %s"%(" ".join(self.available_names))) parser.add_option("-r", "--registry", dest="registry", help="root registry", metavar="URL", default=None) - parser.add_option("-s", "--slicemgr", dest="sm", - help="slice manager", metavar="URL", default=None) + parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL", + help="slice API - in general a SM URL, but can be used to talk to an aggregate") + parser.add_option("-R", "--raw", dest="raw", default=None, + help="Save raw, unparsed server response to a file") + parser.add_option("", "--rawformat", dest="rawformat", type="choice", + help="raw file format ([text]|pickled|json)", default="text", + choices=("text","pickled","json")) + parser.add_option("", "--rawbanner", dest="rawbanner", default=None, + help="text string to write before and after raw output") parser.add_option("-d", "--dir", dest="sfi_dir", help="config & working directory - default is %default", metavar="PATH", default=Sfi.default_sfi_dir()) @@ -347,8 +462,7 @@ class Sfi: parser.add_option("-D", "--debug", action="store_true", dest="debug", default=False, help="Debug (xml-rpc) protocol messages") - parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc", - help="RPC protocol (xmlrpc or soap)") + # would it make sense to use ~/.ssh/id_rsa as a default here ? parser.add_option("-k", "--private-key", action="store", dest="user_private_key", default=None, help="point to the private key file to use if not yet installed in sfi_dir") @@ -363,14 +477,16 @@ class Sfi: def print_help (self): + print "==================== Generic sfi usage" self.sfi_parser.print_help() - self.cmd_parser.print_help() + print "==================== Specific command usage" + self.command_parser.print_help() # # Main: parse arguments and dispatch to command # - def dispatch(self, command, cmd_opts, cmd_args): - return getattr(self, command)(cmd_opts, cmd_args) + def dispatch(self, command, command_options, command_args): + return getattr(self, command)(command_options, command_args) def main(self): self.sfi_parser = self.create_parser() @@ -387,22 +503,28 @@ class Sfi: self.print_command_help(options) return -1 - command = args[0] - self.cmd_parser = self.create_cmd_parser(command) - (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:]) + # complete / find unique match with command set + command_candidates = Candidates (self.available_names) + input = args[0] + command = command_candidates.only_match(input) + if not command: + self.print_command_help(options) + sys.exit(1) + # second pass options parsing + self.command_parser = self.create_command_parser(command) + (command_options, command_args) = self.command_parser.parse_args(args[1:]) + self.command_options = command_options self.read_config () self.bootstrap () - self.set_servers() - self.logger.info("Command=%s" % command) + self.logger.debug("Command=%s" % command) try: - self.dispatch(command, cmd_opts, cmd_args) + self.dispatch(command, command_options, command_args) except KeyError: self.logger.critical ("Unknown command %s"%command) - raise sys.exit(1) - + return #################### @@ -428,7 +550,7 @@ class Sfi: else: self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file) errors += 1 - + # Set Registry URL if (self.options.registry is not None): self.reg_url = self.options.registry @@ -437,7 +559,6 @@ class Sfi: else: self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file) errors += 1 - # Set user HRN if (self.options.user is not None): @@ -447,7 +568,7 @@ class Sfi: else: self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file) errors += 1 - + # Set authority HRN if (self.options.auth is not None): self.authority = self.options.auth @@ -456,50 +577,21 @@ class Sfi: else: self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file) errors += 1 - + + self.config_file=config_file if errors: sys.exit(1) - - # - # Establish Connection to SliceMgr and Registry Servers - # - def set_servers(self): - - # Get key and certificate - self.logger.info("Contacting Registry at: %s"%self.reg_url) - self.registry = SfaServerProxy(self.reg_url, self.private_key, self.my_gid, - timeout=self.options.timeout, verbose=self.options.debug) - self.logger.info("Contacting Slice Manager at: %s"%self.sm_url) - self.slicemgr = SfaServerProxy(self.sm_url, self.private_key, self.my_gid, - timeout=self.options.timeout, verbose=self.options.debug) - return - - def get_cached_server_version(self, server): - # check local cache first - cache = None - version = None - cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat') - cache_key = server.url + "-version" - try: - cache = Cache(cache_file) - except IOError: - cache = Cache() - self.logger.info("Local cache not found at: %s" % cache_file) - - if cache: - version = cache.get(cache_key) - - if not version: - result = server.GetVersion() - version= ReturnValue.get_value(result) - # cache version for 24 hours - cache.add(cache_key, version, ttl= 60*60*24) - self.logger.info("Updating cache file %s" % cache_file) - cache.save_to_file(cache_file) - - return version - + def show_config (self): + print "From configuration file %s"%self.config_file + flags=[ + ('SFI_USER','user'), + ('SFI_AUTH','authority'), + ('SFI_SM','sm_url'), + ('SFI_REGISTRY','reg_url'), + ] + for (external_name, internal_name) in flags: + print "%s='%s'"%(external_name,getattr(self,internal_name)) # # Get various credential and spec files @@ -516,55 +608,44 @@ class Sfi: # init self-signed cert, user credentials and gid def bootstrap (self): - bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir) - # xxx todo : add a -k option to specify an external private key to install in workdir + 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 if self.options.user_private_key: - bootstrap.init_private_key_if_missing (self.options.user_private_key) + client_bootstrap.init_private_key_if_missing (self.options.user_private_key) else: - # trigger legacy compat code if needed - if not os.path.isfile(bootstrap.private_key_filename()): + # trigger legacy compat code if needed + # the name has changed from just .pkey to .pkey + 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"%get_leaf(self.user)) self.logger.debug("legacy_private_key=%s"%legacy_private_key) - bootstrap.init_private_key_if_missing (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) except: self.logger.log_exc("Can't find private key ") sys.exit(1) # make it bootstrap - bootstrap.bootstrap_my_gid() + client_bootstrap.bootstrap_my_gid() # extract what's needed - self.private_key = bootstrap.private_key() - self.my_gid = bootstrap.my_gid () - self.my_credential_string = bootstrap.my_credential_string () - self.bootstrap = bootstrap + self.private_key = client_bootstrap.private_key() + self.my_credential_string = client_bootstrap.my_credential_string () + self.my_gid = client_bootstrap.my_gid () + self.client_bootstrap = client_bootstrap - # xxx this too should be handled in bootstrap - def get_cached_credential(self, file): - """ - Return a cached credential only if it hasn't expired. - """ - if (os.path.isfile(file)): - credential = Credential(filename=file) - # make sure it isnt expired - if not credential.get_expiration or \ - datetime.datetime.today() < credential.get_expiration(): - return credential - return None - - def get_auth_cred(self): + def my_authority_credential_string(self): if not self.authority: self.logger.critical("no authority specified. Use -a or set SF_AUTH") sys.exit(-1) - return self.bootstrap.authority_credential_string (self.authority) + return self.client_bootstrap.authority_credential_string (self.authority) - def get_slice_cred(self, name): - return self.bootstrap.slice_credential_string (name) + def slice_credential_string(self, name): + return self.client_bootstrap.slice_credential_string (name) - # should be supported by sfaclientbootstrap + # xxx should be supported by sfaclientbootstrap as well def delegate_cred(self, object_cred, hrn, type='authority'): # the gid and hrn of the object we are delegating if isinstance(object_cred, str): @@ -580,11 +661,113 @@ class Sfi: caller_gidfile = self.my_gid() # the gid of the user who will be delegated to - delegee_gid = self.bootstrap.gid(hrn,type) + delegee_gid = self.client_bootstrap.gid(hrn,type) delegee_hrn = delegee_gid.get_hrn() dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile) return dcred.save_to_string(save_parents=True) + # + # Management of the servers + # + + 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) + return self.registry_proxy + + def sliceapi (self): + # cache the result + if not hasattr (self, 'sliceapi_proxy'): + # if the command exposes the --component option, figure it's hostname and connect at CM_PORT + if hasattr(self.command_options,'component') and self.command_options.component: + # resolve the hrn at the registry + node_hrn = self.command_options.component + 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) + record = records[0] + cm_url = "http://%s:%d/"%(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) + return self.sliceapi_proxy + + def get_cached_server_version(self, server): + # check local cache first + cache = None + version = None + cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat') + cache_key = server.url + "-version" + try: + cache = Cache(cache_file) + except IOError: + cache = Cache() + self.logger.info("Local cache not found at: %s" % cache_file) + + if cache: + version = cache.get(cache_key) + + if not version: + result = server.GetVersion() + 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) + cache.save_to_file(cache_file) + + return version + + ### resurrect this temporarily so we can support V1 aggregates for a while + def server_supports_options_arg(self, server): + """ + Returns true if server support the optional call_id arg, false otherwise. + """ + server_version = self.get_cached_server_version(server) + result = False + # xxx need to rewrite this + if int(server_version.get('geni_api')) >= 2: + result = True + return result + + def server_supports_call_id_arg(self, server): + server_version = self.get_cached_server_version(server) + result = False + if 'sfa' in server_version and 'code_tag' in server_version: + code_tag = server_version['code_tag'] + code_tag_parts = code_tag.split("-") + version_parts = code_tag_parts[0].split(".") + major, minor = version_parts[0], version_parts[1] + rev = code_tag_parts[1] + if int(major) == 1 and minor == 0 and build >= 22: + result = True + return result + + ### ois = options if supported + # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options)) + def ois (self, server, option_dict): + if self.server_supports_options_arg (server): + return [option_dict] + elif self.server_supports_call_id_arg (server): + return [ unique_call_id () ] + else: + return [] + + ### cis = call_id if supported - like ois + def cis (self, server): + if self.server_supports_call_id_arg (server): + return [ unique_call_id ] + else: + return [] + ######################################## miscell utilities def get_rspec_file(self, rspec): if (os.path.isabs(rspec)): @@ -607,71 +790,35 @@ class Sfi: else: self.logger.critical("No such registry record file %s"%record) sys.exit(1) - - # xxx opts undefined - def get_component_proxy_from_hrn(self, hrn): - # direct connection to the nodes component manager interface - records = self.registry.Resolve(hrn, self.my_credential_string) - records = filter_records('node', records) - if not records: - self.logger.warning("No such component:%r"% opts.component) - record = records[0] - - return self.server_proxy(record['hostname'], CM_PORT, self.private_key, self.my_gid) - def server_proxy(self, host, port, keyfile, certfile): - """ - Return an instance of an xmlrpc server connection - """ - # port is appended onto the domain, before the path. Should look like: - # http://domain:port/path - host_parts = host.split('/') - host_parts[0] = host_parts[0] + ":" + str(port) - url = "http://%s" % "/".join(host_parts) - return SfaServerProxy(url, keyfile, certfile, timeout=self.options.timeout, - verbose=self.options.debug) - - # xxx opts could be retrieved in self.options - def server_proxy_from_opts(self, opts): - """ - Return instance of an xmlrpc connection to a slice manager, aggregate - or component server depending on the specified opts - """ - server = self.slicemgr - # direct connection to an aggregate - if hasattr(opts, 'aggregate') and opts.aggregate: - server = self.server_proxy(opts.aggregate, opts.port, self.private_key, self.my_gid) - # direct connection to the nodes component manager interface - if hasattr(opts, 'component') and opts.component: - server = self.get_component_proxy_from_hrn(opts.component) - - return server + #========================================================================== # Following functions implement the commands # # Registry-related commands #========================================================================== - - def version(self, opts, args): + + def version(self, options, args): """ - display an SFA server version (GetVersion) + display an SFA server version (GetVersion) or version information about sfi itself """ - if opts.version_local: + if options.version_local: version=version_core() else: - if opts.version_registry: - server=self.registry + if options.version_registry: + server=self.registry() else: - server = self.server_proxy_from_opts(opts) + server = self.sliceapi() result = server.GetVersion() version = ReturnValue.get_value(result) - for (k,v) in version.iteritems(): - print "%-20s: %s"%(k,v) - if opts.file: - save_variable_to_file(version, opts.file, opts.fileformat) + if self.options.raw: + save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) + else: + pprinter = PrettyPrinter(indent=4) + pprinter.pprint(version) - def list(self, opts, args): + def list(self, options, args): """ list entries in named authority registry (List) """ @@ -679,21 +826,27 @@ or version information about sfi itself self.print_help() sys.exit(1) hrn = args[0] + opts = {} + if options.recursive: + opts['recursive'] = options.recursive + + if options.show_credential: + show_credentials(self.my_credential_string) try: - list = self.registry.List(hrn, self.my_credential_string) + list = self.registry().List(hrn, self.my_credential_string, options) except IndexError: 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... - list = filter_records(opts.type, list) + list = filter_records(options.type, list) for record in list: print "%s (%s)" % (record['hrn'], record['type']) - if opts.file: - save_records_to_file(opts.file, list, opts.fileformat) + if options.file: + save_records_to_file(options.file, list, options.fileformat) return - def show(self, opts, args): + def show(self, options, args): """ show details about named registry record (Resolve) """ @@ -701,157 +854,208 @@ or version information about sfi itself self.print_help() sys.exit(1) hrn = args[0] - records = self.registry.Resolve(hrn, self.my_credential_string) - records = filter_records(opts.type, records) - if not records: - self.logger.error("No record of type %s"% opts.type) + record_dicts = self.registry().Resolve(hrn, self.my_credential_string) + record_dicts = filter_records(options.type, record_dicts) + if not record_dicts: + self.logger.error("No record of type %s"% options.type) + return + # user has required to focus on some keys + if options.keys: + def project (record): + projected={} + for key in options.keys: + try: projected[key]=record[key] + except: pass + return projected + record_dicts = [ project (record) for record in record_dicts ] + records = [ Record(dict=record_dict) for record_dict in record_dicts ] for record in records: - if record['type'] in ['user']: - record = UserRecord(dict=record) - elif record['type'] in ['slice']: - record = SliceRecord(dict=record) - elif record['type'] in ['node']: - record = NodeRecord(dict=record) - elif record['type'].startswith('authority'): - record = AuthorityRecord(dict=record) - else: - record = SfaRecord(dict=record) - if (opts.format == "text"): - record.dump() - else: - print record.save_to_string() - if opts.file: - save_records_to_file(opts.file, records, opts.fileformat) + if (options.format == "text"): record.dump(sort=True) + else: print record.save_as_xml() + if options.file: + save_records_to_file(options.file, record_dicts, options.fileformat) return - def add(self, opts, args): + def add(self, options, args): "add record into registry from xml file (Register)" - auth_cred = self.get_auth_cred() - if len(args)!=1: + 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 options: + record_dict.update(load_record_from_opts(options).todict()) + # we should have a type by now + if 'type' not in record_dict : self.print_help() sys.exit(1) - record_filepath = args[0] - rec_file = self.get_record_file(record_filepath) - record = load_record_from_file(rec_file).as_dict() - return self.registry.Register(record, auth_cred) + # this is still planetlab dependent.. as plc will whine without that + # also, it's only for adding + if record_dict['type'] == 'user': + if not 'first_name' in record_dict: + 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) - def update(self, opts, args): + def update(self, options, args): "update record into registry from xml file (Update)" - if len(args)!=1: + 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 options: + record_dict.update(load_record_from_opts(options).todict()) + # at the very least we need 'type' here + if 'type' not in record_dict: self.print_help() sys.exit(1) - rec_file = self.get_record_file(args[0]) - record = load_record_from_file(rec_file) - if record['type'] == "user": - if record.get_name() == self.user: + + # 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['hrn'] == self.user: cred = self.my_credential_string else: - cred = self.get_auth_cred() - elif record['type'] in ["slice"]: + cred = self.my_authority_credential_string() + elif record_dict['type'] in ["slice"]: try: - cred = self.get_slice_cred(record.get_name()) + cred = self.slice_credential_string(record_dict['hrn']) except ServerException, e: # XXX smbaker -- once we have better error return codes, update this # to do something better than a string compare if "Permission error" in e.args[0]: - cred = self.get_auth_cred() + cred = self.my_authority_credential_string() else: raise - elif record.get_type() in ["authority"]: - cred = self.get_auth_cred() - elif record.get_type() == 'node': - cred = self.get_auth_cred() + elif record_dict['type'] in ["authority"]: + cred = self.my_authority_credential_string() + elif record_dict['type'] == 'node': + cred = self.my_authority_credential_string() else: - raise "unknown record type" + record.get_type() - record = record.as_dict() - return self.registry.Update(record, cred) + raise "unknown record type" + record_dict['type'] + if options.show_credential: + show_credentials(cred) + return self.registry().Update(record_dict, cred) - def remove(self, opts, args): + def remove(self, options, args): "remove registry record by name (Remove)" - auth_cred = self.get_auth_cred() + auth_cred = self.my_authority_credential_string() if len(args)!=1: self.print_help() sys.exit(1) hrn = args[0] - type = opts.type + type = options.type if type in ['all']: type = '*' - return self.registry.Remove(hrn, auth_cred, type) + if options.show_credential: + show_credentials(auth_cred) + return self.registry().Remove(hrn, auth_cred, type) # ================================================================== # Slice-related commands # ================================================================== - def slices(self, opts, args): + def slices(self, options, args): "list instantiated slices (ListSlices) - returns urn's" + server = self.sliceapi() + # creds creds = [self.my_credential_string] - if opts.delegate: + if options.delegate: delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority)) creds.append(delegated_cred) - server = self.server_proxy_from_opts(opts) + # options and call_id when supported api_options = {} - api_options ['call_id'] = unique_call_id() - result = server.ListSlices(creds, api_options) + api_options['call_id']=unique_call_id() + if options.show_credential: + show_credentials(creds) + result = server.ListSlices(creds, *self.ois(server,api_options)) value = ReturnValue.get_value(result) - display_list(value) + if self.options.raw: + save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) + else: + display_list(value) return - + # show rspec for named slice - def resources(self, opts, args): + def resources(self, options, args): """ - with no arg, discover available resources, -or currently provisioned resources (ListResources) + with no arg, discover available resources, (ListResources) +or with an slice hrn, shows currently provisioned resources """ - server = self.server_proxy_from_opts(opts) - + server = self.sliceapi() + + # set creds + creds = [] + if args: + creds.append(self.slice_credential_string(args[0])) + else: + creds.append(self.my_credential_string) + if options.delegate: + creds.append(self.delegate_cred(cred, get_authority(self.authority))) + if options.show_credential: + show_credentials(creds) + + # no need to check if server accepts the options argument since the options has + # been a required argument since v1 API api_options = {} + # always send call_id to v2 servers api_options ['call_id'] = unique_call_id() - #panos add info api_options - if opts.info: - api_options['info'] = opts.info - + # ask for cached value if available + api_options ['cached'] = True if args: - cred = self.get_slice_cred(args[0]) hrn = args[0] api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice') - else: - cred = self.my_credential_string - - creds = [cred] - if opts.delegate: - delegated_cred = self.delegate_cred(cred, get_authority(self.authority)) - creds.append(delegated_cred) - if opts.rspec_version: + if options.info: + api_options['info'] = options.info + if options.list_leases: + api_options['list_leases'] = options.list_leases + if options.current: + if options.current == True: + api_options['cached'] = False + else: + api_options['cached'] = True + if options.rspec_version: version_manager = VersionManager() server_version = self.get_cached_server_version(server) if 'sfa' in server_version: - # just request the version the client wants - api_options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict() + # just request the version the client wants + api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict() else: - # this must be a protogeni aggregate. We should request a v2 ad rspec - # regardless of what the client user requested - api_options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict() + api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'} else: api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'} - - result = server.ListResources(creds, api_options) + result = server.ListResources (creds, api_options) value = ReturnValue.get_value(result) - if opts.file is None: - display_rspec(value, opts.format) - else: - save_rspec_to_file(value, opts.file) + 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): + display_rspec(value, options.format) + return - def create(self, opts, args): + def create(self, options, args): """ create or update named slice with given rspec """ - server = self.server_proxy_from_opts(opts) - server_version = self.get_cached_server_version(server) + 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') - slice_cred = self.get_slice_cred(slice_hrn) + + # 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 @@ -860,182 +1064,269 @@ or currently provisioned resources (ListResources) # 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]) + 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]) + 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') - creds = [slice_cred] else: + print >>sys.stderr, "\r\n \r\n \r\n WOOOOOO" users = sfa_users_arg(user_records, slice_record) - creds = [slice_cred] - if delegated_cred: - creds.append(delegated_cred) - # do not append users, keys, or slice tags. Anything - # not contained in this request will be removed from the slice + + # do not append users, keys, or slice tags. Anything + # not contained in this request will be removed from the slice + + # CreateSliver has supported the options argument for a while now so it should + # be safe to assume this server support it api_options = {} api_options ['append'] = False api_options ['call_id'] = unique_call_id() - result = server.CreateSliver(slice_urn, creds, rspec, users, api_options) + result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server, api_options)) value = ReturnValue.get_value(result) - if opts.file is None: + 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 - else: - save_rspec_to_file (value, opts.file) + return value - def delete(self, opts, args): + def delete(self, options, args): """ delete named slice (DeleteSliver) """ + server = self.sliceapi() + + # slice urn slice_hrn = args[0] slice_urn = hrn_to_urn(slice_hrn, 'slice') - slice_cred = self.get_slice_cred(slice_hrn) + + # creds + slice_cred = self.slice_credential_string(slice_hrn) creds = [slice_cred] - if opts.delegate: + if options.delegate: delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) creds.append(delegated_cred) - server = self.server_proxy_from_opts(opts) + + # options and call_id when supported api_options = {} api_options ['call_id'] = unique_call_id() - return server.DeleteSliver(slice_urn, creds, api_options) + if options.show_credential: + show_credentials(creds) + result = server.DeleteSliver(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 + return value - def status(self, opts, args): + def status(self, options, args): """ retrieve slice status (SliverStatus) """ + server = self.sliceapi() + + # slice urn slice_hrn = args[0] slice_urn = hrn_to_urn(slice_hrn, 'slice') - slice_cred = self.get_slice_cred(slice_hrn) + + # creds + slice_cred = self.slice_credential_string(slice_hrn) creds = [slice_cred] - if opts.delegate: + if options.delegate: delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) creds.append(delegated_cred) - server = self.server_proxy_from_opts(opts) + + # options and call_id when supported api_options = {} - api_options ['call_id'] = unique_call_id() - result = server.SliverStatus(slice_urn, creds, api_options) + api_options['call_id']=unique_call_id() + if options.show_credential: + show_credentials(creds) + result = server.SliverStatus(slice_urn, creds, *self.ois(server,api_options)) value = ReturnValue.get_value(result) - print value - if opts.file: - save_variable_to_file(value, opts.file, opts.fileformat) + if self.options.raw: + save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner) + else: + print value - def start(self, opts, args): + 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') - slice_cred = self.get_slice_cred(args[0]) + + # cred + slice_cred = self.slice_credential_string(args[0]) creds = [slice_cred] - if opts.delegate: + if options.delegate: delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) creds.append(delegated_cred) - server = self.server_proxy_from_opts(opts) - return server.Start(slice_urn, creds) + # 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, opts, args): + 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') - slice_cred = self.get_slice_cred(args[0]) + # cred + slice_cred = self.slice_credential_string(args[0]) creds = [slice_cred] - if opts.delegate: + if options.delegate: delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) creds.append(delegated_cred) - server = self.server_proxy_from_opts(opts) - return server.Stop(slice_urn, creds) + result = server.Stop(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 # reset named slice - def reset(self, opts, args): + def reset(self, options, args): """ reset named slice (reset_slice) """ + server = self.sliceapi() + # slice urn slice_hrn = args[0] slice_urn = hrn_to_urn(slice_hrn, 'slice') - server = self.server_proxy_from_opts(opts) - slice_cred = self.get_slice_cred(args[0]) + # cred + slice_cred = self.slice_credential_string(args[0]) creds = [slice_cred] - if opts.delegate: + if options.delegate: delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) creds.append(delegated_cred) - return server.reset_slice(creds, slice_urn) + result = server.reset_slice(creds, slice_urn) + 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 renew(self, opts, args): + def renew(self, options, args): """ renew slice (RenewSliver) """ - slice_hrn = args[0] + server = self.sliceapi() + if len(args) != 2: + self.print_help() + sys.exit(1) + [ slice_hrn, input_time ] = args + # slice urn slice_urn = hrn_to_urn(slice_hrn, 'slice') - server = self.server_proxy_from_opts(opts) - slice_cred = self.get_slice_cred(args[0]) + # time: don't try to be smart on the time format, server-side will + # creds + slice_cred = self.slice_credential_string(args[0]) creds = [slice_cred] - if opts.delegate: + if options.delegate: delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) creds.append(delegated_cred) - time = args[1] + # options and call_id when supported api_options = {} - api_options ['call_id'] = unique_call_id() - result = server.RenewSliver(slice_urn, creds, time, api_options) + api_options['call_id']=unique_call_id() + if options.show_credential: + show_credentials(creds) + result = server.RenewSliver(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) + else: + print value return value - def shutdown(self, opts, args): + def shutdown(self, options, args): """ shutdown named slice (Shutdown) """ + server = self.sliceapi() + # slice urn slice_hrn = args[0] slice_urn = hrn_to_urn(slice_hrn, 'slice') - slice_cred = self.get_slice_cred(slice_hrn) + # creds + slice_cred = self.slice_credential_string(slice_hrn) creds = [slice_cred] - if opts.delegate: + if options.delegate: delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) creds.append(delegated_cred) - server = self.server_proxy_from_opts(opts) - return server.Shutdown(slice_urn, creds) + result = server.Shutdown(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 get_ticket(self, opts, args): + 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') - slice_cred = self.get_slice_cred(slice_hrn) + # creds + slice_cred = self.slice_credential_string(slice_hrn) creds = [slice_cred] - if opts.delegate: + 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() - server = self.server_proxy_from_opts(opts) - ticket_string = server.GetTicket(slice_urn, creds, rspec, []) + # 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, opts, args): + def redeem_ticket(self, options, args): """ Connects to nodes in a slice and redeems a ticket (slice hrn is retrieved from the ticket) @@ -1046,10 +1337,12 @@ or currently provisioned resources (ListResources) # 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.get_slice_cred(slice_hrn) + slice_cred = self.slice_credential_string(slice_hrn) # get a list of node hostnames from the RSpec tree = etree.parse(StringIO(ticket.rspec)) @@ -1062,17 +1355,19 @@ or currently provisioned resources (ListResources) for hostname in hostnames: try: self.logger.info("Calling redeem_ticket at %(hostname)s " % locals()) - server = self.server_proxy(hostname, CM_PORT, self.private_key, \ - self.my_gid, verbose=self.options.debug) - server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred) + 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: Component Manager not accepting requests") + self.logger.error("redeem_ticket failed on %s: Component Manager not accepting requests"%hostname) except Exception, e: self.logger.log_exc(e.message) return - def create_gid(self, opts, args): + def gid(self, options, args): """ Create a GID (CreateGid) """ @@ -1080,34 +1375,34 @@ or currently provisioned resources (ListResources) self.print_help() sys.exit(1) target_hrn = args[0] - gid = self.registry.CreateGid(self.my_credential_string, target_hrn, self.bootstrap.my_gid_string()) - if opts.file: - filename = opts.file + gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.client_bootstrap.my_gid_string()) + 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)) GID(string=gid).save_to_file(filename) - def delegate(self, opts, args): + def delegate(self, options, args): """ (locally) create delegate credential for use by given hrn """ delegee_hrn = args[0] - if opts.delegate_user: + if options.delegate_user: cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user') - elif opts.delegate_slice: - slice_cred = self.get_slice_cred(opts.delegate_slice) + 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 opts.delegate_user: + if options.delegate_user: dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_" + get_leaf(object_hrn) + ".cred") - elif opts.delegate_slice: + elif options.delegate_slice: dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_" + get_leaf(object_hrn) + ".cred") @@ -1115,15 +1410,18 @@ or currently provisioned resources (ListResources) self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn)) - def get_trusted_certs(self, opts, args): + def trusted(self, options, args): """ return uhe trusted certs at this interface (get_trusted_certs) """ - trusted_certs = self.registry.get_trusted_certs() + trusted_certs = self.registry().get_trusted_certs() for trusted_cert in trusted_certs: gid = GID(string=trusted_cert) gid.dump() cert = Certificate(string=trusted_cert) - self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject()) + self.logger.debug('Sfi.trusted -> %r'%cert.get_subject()) return + def config (self, options, args): + "Display contents of current config" + self.show_config()