X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Fclient%2Fsfi.py;h=8cc89033d9fd4dcf07fa43247fc54aa9beec270c;hb=4ba188d2e00ad2ecb5d642c6c442fef500539752;hp=7fcb25e11af8188b13b717bff086b0284cb2adb9;hpb=3ce13b8659dec0e0f13eafbf10ce7c4d081e0d1a;p=sfa.git diff --git a/sfa/client/sfi.py b/sfa/client/sfi.py index 7fcb25e1..8cc89033 100644 --- a/sfa/client/sfi.py +++ b/sfa/client/sfi.py @@ -13,10 +13,12 @@ import datetime import codecs import pickle import json +import shutil from lxml import etree from StringIO import StringIO from optparse import OptionParser from pprint import PrettyPrinter +from tempfile import mkstemp from sfa.trust.certificate import Keypair, Certificate from sfa.trust.gid import GID @@ -40,6 +42,7 @@ 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 CM_PORT=12346 @@ -112,6 +115,21 @@ 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_raw_to_file(var, filename, format="text", banner=None): if filename == "-": @@ -179,6 +197,54 @@ def save_record_to_file(filename, record_dict): f.close() return +# used in sfi list +def terminal_render (records,options): + # sort records by type + grouped_by_type={} + for record in records: + type=record['type'] + if type not in grouped_by_type: grouped_by_type[type]=[] + grouped_by_type[type].append(record) + group_types=grouped_by_type.keys() + group_types.sort() + for type in group_types: + group=grouped_by_type[type] +# print 20 * '-', type + try: renderer=eval('terminal_render_'+type) + except: renderer=terminal_render_default + for record in group: renderer(record,options) + +def render_plural (how_many, name,names=None): + if not names: names="%ss"%name + if how_many<=0: return "No %s"%name + elif how_many==1: return "1 %s"%name + else: return "%d %s"%(how_many,names) + +def terminal_render_default (record,options): + print "%s (%s)" % (record['hrn'], record['type']) +def terminal_render_user (record, options): + print "%s (User)"%record['hrn'], + if record.get('reg-pi-authorities',None): print " [PI at %s]"%(" and ".join(record['reg-pi-authorities'])), + if record.get('reg-slices',None): print " [IN slices %s]"%(" and ".join(record['reg-slices'])), + user_keys=record.get('reg-keys',[]) + if not options.verbose: + print " [has %s]"%(render_plural(len(user_keys),"key")) + else: + print "" + for key in user_keys: print 8*' ',key.strip("\n") + +def terminal_render_slice (record, options): + print "%s (Slice)"%record['hrn'], + if record.get('reg-researchers',None): print " [USERS %s]"%(" and ".join(record['reg-researchers'])), +# print record.keys() + print "" +def terminal_render_authority (record, options): + print "%s (Authority)"%record['hrn'], + if record.get('reg-pis',None): print " [PIS %s]"%(" and ".join(record['reg-pis'])), + print "" +def terminal_render_node (record, options): + print "%s (Node)"%record['hrn'] + # minimally check a key argument def check_ssh_key (key): good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$' @@ -278,8 +344,8 @@ class Sfi: ("get_ticket", "slice_hrn rspec"), ("redeem_ticket", "ticket"), ("delegate", "name"), - ("create_gid", "[name]"), - ("get_trusted_certs", "cred"), + ("gid", "[name]"), + ("trusted", "cred"), ("config", ""), ] @@ -350,12 +416,19 @@ class Sfi: 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") + 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", @@ -378,7 +451,7 @@ class Sfi: # '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) @@ -393,6 +466,8 @@ class Sfi: if command == 'list': parser.add_option("-r", "--recursive", dest="recursive", action='store_true', help="list all child records", default=False) + parser.add_option("-v", "--verbose", dest="verbose", action='store_true', + help="gives details, like user keys", default=False) if command in ("delegate"): parser.add_option("-u", "--user", action="store_true", dest="delegate_user", default=False, @@ -463,7 +538,11 @@ class Sfi: # Main: parse arguments and dispatch to command # def dispatch(self, command, command_options, command_args): - return getattr(self, command)(command_options, command_args) + method=getattr(self, command,None) + if not method: + print "Unknown command %s"%command + return + return method(command_options, command_args) def main(self): self.sfi_parser = self.create_parser() @@ -480,37 +559,59 @@ class Sfi: self.print_command_help(options) return -1 - command = args[0] + # 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.logger.info("Command=%s" % command) + self.logger.debug("Command=%s" % command) try: self.dispatch(command, command_options, command_args) - except KeyError: - self.logger.critical ("Unknown command %s"%command) - raise + except: + self.logger.log_exc ("sfi command %s failed"%command) sys.exit(1) - + return #################### def read_config(self): config_file = os.path.join(self.options.sfi_dir,"sfi_config") + shell_config_file = os.path.join(self.options.sfi_dir,"sfi_config.sh") try: - config = Config (config_file) + if Config.is_ini(config_file): + config = Config (config_file) + else: + # try upgrading from shell config format + fp, fn = mkstemp(suffix='sfi_config', text=True) + config = Config(fn) + # we need to preload the sections we want parsed + # from the shell config + config.add_section('sfi') + config.add_section('sface') + config.load(config_file) + # back up old config + shutil.move(config_file, shell_config_file) + # write new config + config.save(config_file) + except: - self.logger.critical("Failed to read configuration file %s"%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) - sys.exit(1) + self.logger.critical("Failed to read configuration file %s"%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) + sys.exit(1) errors = 0 # Set SliceMgr URL @@ -528,7 +629,7 @@ class Sfi: elif hasattr(config, "SFI_REGISTRY"): self.reg_url = config.SFI_REGISTRY else: - self.logger.errors("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 %s" % config_file) errors += 1 # Set user HRN @@ -537,7 +638,7 @@ class Sfi: elif hasattr(config, "SFI_USER"): self.user = config.SFI_USER else: - self.logger.errors("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 %s" % config_file) errors += 1 # Set authority HRN @@ -579,7 +680,8 @@ class Sfi: # init self-signed cert, user credentials and gid def bootstrap (self): - client_bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir) + 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: client_bootstrap.init_private_key_if_missing (self.options.user_private_key) @@ -589,7 +691,7 @@ class Sfi: 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)) + 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) client_bootstrap.init_private_key_if_missing (legacy_private_key) self.logger.info("Copied private key from legacy location %s"%legacy_private_key) @@ -800,16 +902,17 @@ or version information about sfi itself 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, 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... + # This really should be in the self.filter_records funct def comment... list = filter_records(options.type, list) - for record in list: - print "%s (%s)" % (record['hrn'], record['type']) + terminal_render (list, options) if options.file: save_records_to_file(options.file, list, options.fileformat) return @@ -822,10 +925,21 @@ or version information about sfi itself self.print_help() sys.exit(1) hrn = args[0] - record_dicts = self.registry().Resolve(hrn, self.my_credential_string) + # explicitly require Resolve to run in details mode + record_dicts = self.registry().Resolve(hrn, self.my_credential_string, {'details':True}) record_dicts = filter_records(options.type, record_dicts) if not record_dicts: self.logger.error("No record of type %s"% options.type) + 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 (options.format == "text"): record.dump(sort=True) @@ -837,6 +951,8 @@ or version information about sfi itself def add(self, options, args): "add record into registry from xml file (Register)" auth_cred = self.my_authority_credential_string() + if options.show_credential: + show_credentials(auth_cred) record_dict = {} if len(args) > 0: record_filepath = args[0] @@ -894,6 +1010,8 @@ or version information about sfi itself cred = self.my_authority_credential_string() else: raise "unknown record type" + record_dict['type'] + if options.show_credential: + show_credentials(cred) return self.registry().Update(record_dict, cred) def remove(self, options, args): @@ -906,6 +1024,8 @@ or version information about sfi itself type = options.type if type in ['all']: type = '*' + if options.show_credential: + show_credentials(auth_cred) return self.registry().Remove(hrn, auth_cred, type) # ================================================================== @@ -923,6 +1043,8 @@ or version information about sfi itself # options and call_id when supported 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) if self.options.raw: @@ -947,6 +1069,8 @@ or with an slice hrn, shows currently provisioned resources 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 @@ -1001,6 +1125,7 @@ or with an slice hrn, shows currently provisioned resources # 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': @@ -1012,6 +1137,9 @@ or with an slice hrn, shows currently provisioned resources #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() @@ -1023,10 +1151,14 @@ or with an slice hrn, shows currently provisioned resources # keys: [, ] # }] users = [] + # xxx Thierry 2012 sept. 21 + # contrary to what I was first thinking, calling Resolve with details=False does not yet work properly here + # I am turning details=True on again on a - hopefully - temporary basis, just to get this whole thing to work again slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string]) - if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]: + # slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string], {'details':True}) + if slice_records and 'reg-researchers' in slice_records[0] and slice_records[0]['reg-researchers']: slice_record = slice_records[0] - user_hrns = slice_record['researcher'] + user_hrns = slice_record['reg-researchers'] user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns] user_records = self.registry().Resolve(user_urns, [self.my_credential_string]) @@ -1077,6 +1209,8 @@ or with an slice hrn, shows currently provisioned resources # options and call_id when supported api_options = {} api_options ['call_id'] = unique_call_id() + 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: @@ -1105,6 +1239,8 @@ or with an slice hrn, shows currently provisioned resources # options and call_id when supported 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) if self.options.raw: @@ -1203,6 +1339,8 @@ or with an slice hrn, shows currently provisioned resources # options and call_id when supported 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: @@ -1304,7 +1442,7 @@ or with an slice hrn, shows currently provisioned resources self.logger.log_exc(e.message) return - def create_gid(self, options, args): + def gid(self, options, args): """ Create a GID (CreateGid) """ @@ -1312,7 +1450,8 @@ or with an slice hrn, shows currently provisioned resources self.print_help() sys.exit(1) target_hrn = args[0] - gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.client_bootstrap.my_gid_string()) + my_gid_string = open(self.client_bootstrap.my_gid()).read() + gid = self.registry().CreateGid(self.my_credential_string, target_hrn, my_gid_string) if options.file: filename = options.file else: @@ -1347,7 +1486,7 @@ or with an slice hrn, shows currently provisioned resources self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn)) - def get_trusted_certs(self, options, args): + def trusted(self, options, args): """ return uhe trusted certs at this interface (get_trusted_certs) """ @@ -1356,7 +1495,7 @@ or with an slice hrn, shows currently provisioned resources 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):