X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Fclient%2Fsfi.py;h=512bf9a3e6b70dddbead6e1100ae4256b6cf86d9;hb=bc77b3e1ce73d7f748d9cc978472dba246034d74;hp=9cf655191b3b728787636ff174e33ce59cd7aeb4;hpb=611096d4c778281585f73b4edef458adad81ea2c;p=sfa.git diff --git a/sfa/client/sfi.py b/sfa/client/sfi.py index 9cf65519..512bf9a3 100644 --- a/sfa/client/sfi.py +++ b/sfa/client/sfi.py @@ -1,7 +1,6 @@ # # sfi.py - basic SFA command-line client -# the actual binary in sfa/clientbin essentially runs main() -# this module is used in sfascan +# this module is also used in sfascan # import sys @@ -9,6 +8,7 @@ sys.path.append('.') import os, os.path import socket +import re import datetime import codecs import pickle @@ -23,6 +23,7 @@ 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, Xrn from sfa.util.config import Config @@ -43,9 +44,30 @@ from sfa.client.return_value import ReturnValue CM_PORT=12346 # utility methods here -def optparse_listvalue_callback(option, opt, value, parser): +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']: @@ -75,7 +97,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']) @@ -157,6 +179,10 @@ def save_record_to_file(filename, record_dict): 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): @@ -169,15 +195,13 @@ def load_record_from_opts(options): record_dict['urn'] = xrn.get_urn() record_dict['hrn'] = xrn.get_hrn() record_dict['type'] = xrn.get_type() - if hasattr(options, 'url') and options.url: - record_dict['url'] = options.url - if hasattr(options, 'description') and options.description: - record_dict['description'] = options.description 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 @@ -188,13 +212,9 @@ def load_record_from_opts(options): if hasattr(options, 'pis') and options.pis: record_dict['pi'] = options.pis - # fill in the blanks - 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'] - + # handle extra settings + record_dict.update(options.extras) + return Record(dict=record_dict) def load_record_from_file(filename): @@ -302,9 +322,10 @@ class Sfi: parser.add_option('-x', '--xrn', dest='xrn', metavar='', help='object hrn/urn (mandatory)') parser.add_option('-t', '--type', dest='type', metavar='', help='object type', default=None) parser.add_option('-e', '--email', dest='email', default="", help="email (mandatory for users)") - 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) +# 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', @@ -314,8 +335,12 @@ class Sfi: 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) - parser.add_option('-f', '--firstname', dest='firstname', metavar='', help='user first name') - parser.add_option('-l', '--lastname', dest='lastname', metavar='', help='user last name') +# 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", @@ -331,6 +356,9 @@ class Sfi: 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", @@ -346,6 +374,10 @@ 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) + 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 @@ -425,7 +457,9 @@ class Sfi: def print_help (self): + print "==================== Generic sfi usage" self.sfi_parser.print_help() + print "==================== Specific command usage" self.command_parser.print_help() # @@ -462,9 +496,8 @@ class Sfi: self.dispatch(command, command_options, command_args) except KeyError: self.logger.critical ("Unknown command %s"%command) - raise sys.exit(1) - + return #################### @@ -795,9 +828,19 @@ or version information about sfi itself 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() + 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) @@ -806,17 +849,25 @@ 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() - record = {} + record_dict = {} if len(args) > 0: record_filepath = args[0] rec_file = self.get_record_file(record_filepath) - record.update(load_record_from_file(rec_file).todict()) + record_dict.update(load_record_from_file(rec_file).todict()) if options: - record.update(load_record_from_opts(options).todict()) - if not record: + 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) - 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, options, args): "update record into registry from xml file (Update)" @@ -827,19 +878,21 @@ or version information about sfi itself record_dict.update(load_record_from_file(rec_file).todict()) if options: record_dict.update(load_record_from_opts(options).todict()) - if not record_dict: + # at the very least we need 'type' here + if 'type' not in record_dict: self.print_help() sys.exit(1) - record = Record(dict=record_dict) - if record.type == "user": - if record.hrn == 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.my_authority_credential_string() - elif record.type in ["slice"]: + elif record_dict['type'] in ["slice"]: try: - cred = self.slice_credential_string(record.hrn) + 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 @@ -847,13 +900,12 @@ or version information about sfi itself cred = self.my_authority_credential_string() else: raise - elif record.type in ["authority"]: + elif record_dict['type'] in ["authority"]: cred = self.my_authority_credential_string() - elif record.type == 'node': + elif record_dict['type'] == 'node': cred = self.my_authority_credential_string() else: - raise "unknown record type" + record.type - record_dict = record.todict() + raise "unknown record type" + record_dict['type'] return self.registry().Update(record_dict, cred) def remove(self, options, args): @@ -920,6 +972,8 @@ or with an slice hrn, shows currently provisioned resources api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice') 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 @@ -994,6 +1048,7 @@ or with an slice hrn, shows currently provisioned resources rspec.filter({'component_manager_id': server_version['urn']}) rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request') else: + print >>sys.stderr, "\r\n \r\n \r\n WOOOOOO" users = sfa_users_arg(user_records, slice_record) # do not append users, keys, or slice tags. Anything @@ -1145,21 +1200,23 @@ or with an slice hrn, shows currently provisioned resources renew slice (RenewSliver) """ server = self.sliceapi() + if len(args) != 2: + self.print_help() + sys.exit(1) + [ slice_hrn, input_time ] = args # slice urn - slice_hrn = args[0] slice_urn = hrn_to_urn(slice_hrn, 'slice') + # time: don't try to be smart on the time format, server-side will # creds slice_cred = self.slice_credential_string(args[0]) creds = [slice_cred] if options.delegate: delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) creds.append(delegated_cred) - # time - 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, *self.ois(server,api_options)) + 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)