2 # sfi.py - basic SFA command-line client
3 # this module is also used in sfascan
17 from lxml import etree
18 from StringIO import StringIO
19 from optparse import OptionParser
20 from pprint import PrettyPrinter
21 from tempfile import mkstemp
23 from sfa.trust.certificate import Keypair, Certificate
24 from sfa.trust.gid import GID
25 from sfa.trust.credential import Credential
26 from sfa.trust.sfaticket import SfaTicket
28 from sfa.util.faults import SfaInvalidArgument
29 from sfa.util.sfalogging import sfi_logger
30 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn, Xrn
31 from sfa.util.config import Config
32 from sfa.util.version import version_core
33 from sfa.util.cache import Cache
35 from sfa.storage.record import Record
37 from sfa.rspecs.rspec import RSpec
38 from sfa.rspecs.rspec_converter import RSpecConverter
39 from sfa.rspecs.version_manager import VersionManager
41 from sfa.client.sfaclientlib import SfaClientBootstrap
42 from sfa.client.sfaserverproxy import SfaServerProxy, ServerException
43 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
44 from sfa.client.return_value import ReturnValue
45 from sfa.client.candidates import Candidates
46 from sfa.client.manifolduploader import ManifoldUploader
50 from sfa.client.common import optparse_listvalue_callback, optparse_dictvalue_callback, \
51 terminal_render, filter_records
54 def display_rspec(rspec, format='rspec'):
56 tree = etree.parse(StringIO(rspec))
58 result = root.xpath("./network/site/node/hostname/text()")
59 elif format in ['ip']:
60 # The IP address is not yet part of the new RSpec
61 # so this doesn't do anything yet.
62 tree = etree.parse(StringIO(rspec))
64 result = root.xpath("./network/site/node/ipv4/text()")
71 def display_list(results):
72 for result in results:
75 def display_records(recordList, dump=False):
76 ''' Print all fields in the record'''
77 for record in recordList:
78 display_record(record, dump)
80 def display_record(record, dump=False):
82 record.dump(sort=True)
84 info = record.getdict()
85 print "%s (%s)" % (info['hrn'], info['type'])
89 def filter_records(type, records):
91 for record in records:
92 if (record['type'] == type) or (type == "all"):
93 filtered_records.append(record)
94 return filtered_records
97 def credential_printable (cred):
98 credential=Credential(cred=cred)
100 result += credential.get_summary_tostring()
102 rights = credential.get_privileges()
103 result += "type=%s\n" % credential.type
104 result += "version=%s\n" % credential.version
105 result += "rights=%s\n"%rights
108 def show_credentials (cred_s):
109 if not isinstance (cred_s,list): cred_s = [cred_s]
111 print "Using Credential %s"%credential_printable(cred)
114 def save_raw_to_file(var, filename, format="text", banner=None):
116 # if filename is "-", send it to stdout
119 f = open(filename, "w")
124 elif format == "pickled":
125 f.write(pickle.dumps(var))
126 elif format == "json":
127 if hasattr(json, "dumps"):
128 f.write(json.dumps(var)) # python 2.6
130 f.write(json.write(var)) # python 2.5
132 # this should never happen
133 print "unknown output format", format
135 f.write('\n'+banner+"\n")
137 def save_rspec_to_file(rspec, filename):
138 if not filename.endswith(".rspec"):
139 filename = filename + ".rspec"
140 f = open(filename, 'w')
145 def save_records_to_file(filename, record_dicts, format="xml"):
148 for record_dict in record_dicts:
150 save_record_to_file(filename + "." + str(index), record_dict)
152 save_record_to_file(filename, record_dict)
154 elif format == "xmllist":
155 f = open(filename, "w")
156 f.write("<recordlist>\n")
157 for record_dict in record_dicts:
158 record_obj=Record(dict=record_dict)
159 f.write('<record hrn="' + record_obj.hrn + '" type="' + record_obj.type + '" />\n')
160 f.write("</recordlist>\n")
162 elif format == "hrnlist":
163 f = open(filename, "w")
164 for record_dict in record_dicts:
165 record_obj=Record(dict=record_dict)
166 f.write(record_obj.hrn + "\n")
169 # this should never happen
170 print "unknown output format", format
172 def save_record_to_file(filename, record_dict):
173 record = Record(dict=record_dict)
174 xml = record.save_as_xml()
175 f=codecs.open(filename, encoding='utf-8',mode="w")
180 # minimally check a key argument
181 def check_ssh_key (key):
182 good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$'
183 return re.match(good_ssh_key, key, re.IGNORECASE)
186 def load_record_from_opts(options):
188 if hasattr(options, 'xrn') and options.xrn:
189 if hasattr(options, 'type') and options.type:
190 xrn = Xrn(options.xrn, options.type)
192 xrn = Xrn(options.xrn)
193 record_dict['urn'] = xrn.get_urn()
194 record_dict['hrn'] = xrn.get_hrn()
195 record_dict['type'] = xrn.get_type()
196 if hasattr(options, 'key') and options.key:
198 pubkey = open(options.key, 'r').read()
201 if not check_ssh_key (pubkey):
202 raise SfaInvalidArgument(name='key',msg="Could not find file, or wrong key format")
203 record_dict['keys'] = [pubkey]
204 if hasattr(options, 'slices') and options.slices:
205 record_dict['slices'] = options.slices
206 if hasattr(options, 'researchers') and options.researchers:
207 record_dict['researcher'] = options.researchers
208 if hasattr(options, 'email') and options.email:
209 record_dict['email'] = options.email
210 if hasattr(options, 'pis') and options.pis:
211 record_dict['pi'] = options.pis
213 # handle extra settings
214 record_dict.update(options.extras)
216 return Record(dict=record_dict)
218 def load_record_from_file(filename):
219 f=codecs.open(filename, encoding="utf-8", mode="r")
220 xml_string = f.read()
222 return Record(xml=xml_string)
226 def unique_call_id(): return uuid.uuid4().urn
228 ########## a simple model for maintaing 3 doc attributes per command (instead of just one)
229 # essentially for the methods that implement a subcommand like sfi list
230 # we need to keep track of
231 # (*) doc a few lines that tell what it does, still located in __doc__
232 # (*) args_string a simple one-liner that describes mandatory arguments
233 # (*) example well, one or several releant examples
235 # since __doc__ only accounts for one, we use this simple mechanism below
236 # however we keep doc in place for easier migration
238 from functools import wraps
240 # we use a list as well as a dict so we can keep track of the order
244 def register_command (args_string, example):
246 name=getattr(m,'__name__')
247 doc=getattr(m,'__doc__',"-- missing doc --")
248 doc=doc.strip(" \t\n")
249 commands_list.append(name)
250 commands_dict[name]=(doc, args_string, example)
252 def new_method (*args, **kwds): return m(*args, **kwds)
260 # dirty hack to make this class usable from the outside
261 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user', 'user_private_key']
264 def default_sfi_dir ():
265 if os.path.isfile("./sfi_config"):
268 return os.path.expanduser("~/.sfi/")
270 # dummy to meet Sfi's expectations for its 'options' field
271 # i.e. s/t we can do setattr on
275 def __init__ (self,options=None):
276 if options is None: options=Sfi.DummyOptions()
277 for opt in Sfi.required_options:
278 if not hasattr(options,opt): setattr(options,opt,None)
279 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
280 self.options = options
282 self.authority = None
283 self.logger = sfi_logger
284 self.logger.enable_console()
285 ### various auxiliary material that we keep at hand
287 # need to call this other than just 'config' as we have a command/method with that name
288 self.config_instance=None
289 self.config_file=None
290 self.client_bootstrap=None
292 ### suitable if no reasonable command has been provided
293 def print_commands_help (self, options):
294 verbose=getattr(options,'verbose')
295 format3="%18s %-15s %s"
298 print format3%("command","cmd_args","description")
302 self.create_parser_global().print_help()
303 # preserve order from the code
304 for command in commands_list:
305 (doc, args_string, example) = commands_dict[command]
308 doc=doc.replace("\n","\n"+35*' ')
309 print format3%(command,args_string,doc)
311 self.create_parser_command(command).print_help()
313 ### now if a known command was found we can be more verbose on that one
314 def print_help (self):
315 print "==================== Generic sfi usage"
316 self.sfi_parser.print_help()
317 (doc,_,example)=commands_dict[self.command]
318 print "\n==================== Purpose of %s"%self.command
320 print "\n==================== Specific usage for %s"%self.command
321 self.command_parser.print_help()
323 print "\n==================== %s example(s)"%self.command
326 def create_parser_global(self):
327 # Generate command line parser
328 parser = OptionParser(add_help_option=False,
329 usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
330 description="Commands: %s"%(" ".join(commands_list)))
331 parser.add_option("-r", "--registry", dest="registry",
332 help="root registry", metavar="URL", default=None)
333 parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
334 help="slice API - in general a SM URL, but can be used to talk to an aggregate")
335 parser.add_option("-R", "--raw", dest="raw", default=None,
336 help="Save raw, unparsed server response to a file")
337 parser.add_option("", "--rawformat", dest="rawformat", type="choice",
338 help="raw file format ([text]|pickled|json)", default="text",
339 choices=("text","pickled","json"))
340 parser.add_option("", "--rawbanner", dest="rawbanner", default=None,
341 help="text string to write before and after raw output")
342 parser.add_option("-d", "--dir", dest="sfi_dir",
343 help="config & working directory - default is %default",
344 metavar="PATH", default=Sfi.default_sfi_dir())
345 parser.add_option("-u", "--user", dest="user",
346 help="user name", metavar="HRN", default=None)
347 parser.add_option("-a", "--auth", dest="auth",
348 help="authority name", metavar="HRN", default=None)
349 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
350 help="verbose mode - cumulative")
351 parser.add_option("-D", "--debug",
352 action="store_true", dest="debug", default=False,
353 help="Debug (xml-rpc) protocol messages")
354 # would it make sense to use ~/.ssh/id_rsa as a default here ?
355 parser.add_option("-k", "--private-key",
356 action="store", dest="user_private_key", default=None,
357 help="point to the private key file to use if not yet installed in sfi_dir")
358 parser.add_option("-t", "--timeout", dest="timeout", default=None,
359 help="Amout of time to wait before timing out the request")
360 parser.add_option("-h", "--help",
361 action="store_true", dest="help", default=False,
362 help="one page summary on commands & exit")
363 parser.disable_interspersed_args()
368 def create_parser_command(self, command):
369 if command not in commands_dict:
370 msg="Invalid command\n"
372 msg += ','.join(commands_list)
373 self.logger.critical(msg)
376 # retrieve args_string
377 (_, args_string, __) = commands_dict[command]
379 parser = OptionParser(add_help_option=False,
380 usage="sfi [sfi_options] %s [cmd_options] %s"
381 % (command, args_string))
382 parser.add_option ("-h","--help",dest='help',action='store_true',default=False,
383 help="Summary of one command usage")
385 if command in ("config"):
386 parser.add_option('-m', '--myslice', dest='myslice', action='store_true', default=False,
387 help='how myslice config variables as well')
389 if command in ("version"):
390 parser.add_option("-R","--registry-version",
391 action="store_true", dest="version_registry", default=False,
392 help="probe registry version instead of sliceapi")
393 parser.add_option("-l","--local",
394 action="store_true", dest="version_local", default=False,
395 help="display version of the local client")
397 if command in ("add", "update"):
398 parser.add_option('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
399 parser.add_option('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
400 parser.add_option('-e', '--email', dest='email', default="", help="email (mandatory for users)")
401 parser.add_option('-k', '--key', dest='key', metavar='<key>', help='public key string or file',
403 parser.add_option('-s', '--slices', dest='slices', metavar='<slices>', help='Set/replace slice xrns',
404 default='', type="str", action='callback', callback=optparse_listvalue_callback)
405 parser.add_option('-r', '--researchers', dest='researchers', metavar='<researchers>',
406 help='Set/replace slice researchers', default='', type="str", action='callback',
407 callback=optparse_listvalue_callback)
408 parser.add_option('-p', '--pis', dest='pis', metavar='<PIs>', help='Set/replace Principal Investigators/Project Managers',
409 default='', type="str", action='callback', callback=optparse_listvalue_callback)
410 parser.add_option ('-X','--extra',dest='extras',default={},type='str',metavar="<EXTRA_ASSIGNS>",
411 action="callback", callback=optparse_dictvalue_callback, nargs=1,
412 help="set extra/testbed-dependent flags, e.g. --extra enabled=true")
414 # user specifies remote aggregate/sm/component
415 if command in ("resources", "describe", "allocate", "provision", "delete", "allocate", "provision",
416 "action", "shutdown", "renew", "status"):
417 parser.add_option("-d", "--delegate", dest="delegate", default=None,
419 help="Include a credential delegated to the user's root"+\
420 "authority in set of credentials for this call")
422 # show_credential option
423 if command in ("list","resources", "describe", "provision", "allocate", "add","update","remove","slices","delete","status","renew"):
424 parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False,
425 help="show credential(s) used in human-readable form")
426 # registy filter option
427 if command in ("list", "show", "remove"):
428 parser.add_option("-t", "--type", dest="type", type="choice",
429 help="type filter ([all]|user|slice|authority|node|aggregate)",
430 choices=("all", "user", "slice", "authority", "node", "aggregate"),
432 if command in ("show"):
433 parser.add_option("-k","--key",dest="keys",action="append",default=[],
434 help="specify specific keys to be displayed from record")
435 if command in ("resources", "describe"):
437 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
438 help="schema type and version of resulting RSpec")
439 # disable/enable cached rspecs
440 parser.add_option("-c", "--current", dest="current", default=False,
442 help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
444 parser.add_option("-f", "--format", dest="format", type="choice",
445 help="display format ([xml]|dns|ip)", default="xml",
446 choices=("xml", "dns", "ip"))
447 #panos: a new option to define the type of information about resources a user is interested in
448 parser.add_option("-i", "--info", dest="info",
449 help="optional component information", default=None)
450 # a new option to retreive or not reservation-oriented RSpecs (leases)
451 parser.add_option("-l", "--list_leases", dest="list_leases", type="choice",
452 help="Retreive or not reservation-oriented RSpecs ([resources]|leases|all )",
453 choices=("all", "resources", "leases"), default="resources")
456 if command in ("resources", "describe", "allocate", "provision", "show", "list", "gid"):
457 parser.add_option("-o", "--output", dest="file",
458 help="output XML to file", metavar="FILE", default=None)
460 if command in ("show", "list"):
461 parser.add_option("-f", "--format", dest="format", type="choice",
462 help="display format ([text]|xml)", default="text",
463 choices=("text", "xml"))
465 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
466 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
467 choices=("xml", "xmllist", "hrnlist"))
468 if command == 'list':
469 parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
470 help="list all child records", default=False)
471 parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
472 help="gives details, like user keys", default=False)
473 if command in ("delegate"):
474 parser.add_option("-u", "--user",
475 action="store_true", dest="delegate_user", default=False,
476 help="delegate your own credentials; default if no other option is provided")
477 parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
478 metavar="slice_hrn", help="delegate cred. for slice HRN")
479 parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
480 metavar='auth_hrn', help="delegate cred for auth HRN")
481 # this primarily is a shorthand for -a my_hrn
482 parser.add_option("-p", "--pi", dest='delegate_pi', default=None, action='store_true',
483 help="delegate your PI credentials, so s.t. like -a your_hrn^")
484 parser.add_option("-A","--to-authority",dest='delegate_to_authority',action='store_true',default=False,
485 help="""by default the mandatory argument is expected to be a user,
486 use this if you mean an authority instead""")
488 if command in ("myslice"):
489 parser.add_option("-p","--password",dest='password',action='store',default=None,
490 help="specify mainfold password on the command line")
496 # Main: parse arguments and dispatch to command
498 def dispatch(self, command, command_options, command_args):
499 method=getattr(self, command, None)
501 print "Unknown command %s"%command
503 return method(command_options, command_args)
506 self.sfi_parser = self.create_parser_global()
507 (options, args) = self.sfi_parser.parse_args()
509 self.print_commands_help(options)
511 self.options = options
513 self.logger.setLevelFromOptVerbose(self.options.verbose)
516 self.logger.critical("No command given. Use -h for help.")
517 self.print_commands_help(options)
520 # complete / find unique match with command set
521 command_candidates = Candidates (commands_list)
523 command = command_candidates.only_match(input)
525 self.print_commands_help(options)
527 # second pass options parsing
529 self.command_parser = self.create_parser_command(command)
530 (command_options, command_args) = self.command_parser.parse_args(args[1:])
531 if command_options.help:
534 self.command_options = command_options
538 self.logger.debug("Command=%s" % self.command)
541 self.dispatch(command, command_options, command_args)
545 self.logger.log_exc ("sfi command %s failed"%command)
551 def read_config(self):
552 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
553 shell_config_file = os.path.join(self.options.sfi_dir,"sfi_config.sh")
555 if Config.is_ini(config_file):
556 config = Config (config_file)
558 # try upgrading from shell config format
559 fp, fn = mkstemp(suffix='sfi_config', text=True)
561 # we need to preload the sections we want parsed
562 # from the shell config
563 config.add_section('sfi')
564 # sface users should be able to use this same file to configure their stuff
565 config.add_section('sface')
566 # manifold users should be able to specify the details
567 # of their backend server here for 'sfi myslice'
568 config.add_section('myslice')
569 config.load(config_file)
571 shutil.move(config_file, shell_config_file)
573 config.save(config_file)
576 self.logger.critical("Failed to read configuration file %s"%config_file)
577 self.logger.info("Make sure to remove the export clauses and to add quotes")
578 if self.options.verbose==0:
579 self.logger.info("Re-run with -v for more details")
581 self.logger.log_exc("Could not read config file %s"%config_file)
584 self.config_instance=config
587 if (self.options.sm is not None):
588 self.sm_url = self.options.sm
589 elif hasattr(config, "SFI_SM"):
590 self.sm_url = config.SFI_SM
592 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
596 if (self.options.registry is not None):
597 self.reg_url = self.options.registry
598 elif hasattr(config, "SFI_REGISTRY"):
599 self.reg_url = config.SFI_REGISTRY
601 self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
605 if (self.options.user is not None):
606 self.user = self.options.user
607 elif hasattr(config, "SFI_USER"):
608 self.user = config.SFI_USER
610 self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
614 if (self.options.auth is not None):
615 self.authority = self.options.auth
616 elif hasattr(config, "SFI_AUTH"):
617 self.authority = config.SFI_AUTH
619 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
622 self.config_file=config_file
627 # Get various credential and spec files
629 # Establishes limiting conventions
630 # - conflates MAs and SAs
631 # - assumes last token in slice name is unique
633 # Bootstraps credentials
634 # - bootstrap user credential from self-signed certificate
635 # - bootstrap authority credential from user credential
636 # - bootstrap slice credential from user credential
639 # init self-signed cert, user credentials and gid
640 def bootstrap (self):
641 client_bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir,
643 # if -k is provided, use this to initialize private key
644 if self.options.user_private_key:
645 client_bootstrap.init_private_key_if_missing (self.options.user_private_key)
647 # trigger legacy compat code if needed
648 # the name has changed from just <leaf>.pkey to <hrn>.pkey
649 if not os.path.isfile(client_bootstrap.private_key_filename()):
650 self.logger.info ("private key not found, trying legacy name")
652 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%Xrn.unescape(get_leaf(self.user)))
653 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
654 client_bootstrap.init_private_key_if_missing (legacy_private_key)
655 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
657 self.logger.log_exc("Can't find private key ")
661 client_bootstrap.bootstrap_my_gid()
662 # extract what's needed
663 self.private_key = client_bootstrap.private_key()
664 self.my_credential_string = client_bootstrap.my_credential_string ()
665 self.my_credential = {'geni_type': 'geni_sfa',
666 'geni_version': '3.0',
667 'geni_value': self.my_credential_string}
668 self.my_gid = client_bootstrap.my_gid ()
669 self.client_bootstrap = client_bootstrap
672 def my_authority_credential_string(self):
673 if not self.authority:
674 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
676 return self.client_bootstrap.authority_credential_string (self.authority)
678 def authority_credential_string(self, auth_hrn):
679 return self.client_bootstrap.authority_credential_string (auth_hrn)
681 def slice_credential_string(self, name):
682 return self.client_bootstrap.slice_credential_string (name)
684 def slice_credential(self, name):
685 return {'geni_type': 'geni_sfa',
686 'geni_version': '3.0',
687 'geni_value': self.slice_credential_string(name)}
689 # xxx should be supported by sfaclientbootstrap as well
690 def delegate_cred(self, object_cred, hrn, type='authority'):
691 # the gid and hrn of the object we are delegating
692 if isinstance(object_cred, str):
693 object_cred = Credential(string=object_cred)
694 object_gid = object_cred.get_gid_object()
695 object_hrn = object_gid.get_hrn()
697 if not object_cred.get_privileges().get_all_delegate():
698 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
701 # the delegating user's gid
702 caller_gidfile = self.my_gid()
704 # the gid of the user who will be delegated to
705 delegee_gid = self.client_bootstrap.gid(hrn,type)
706 delegee_hrn = delegee_gid.get_hrn()
707 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
708 return dcred.save_to_string(save_parents=True)
711 # Management of the servers
716 if not hasattr (self, 'registry_proxy'):
717 self.logger.info("Contacting Registry at: %s"%self.reg_url)
718 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
719 timeout=self.options.timeout, verbose=self.options.debug)
720 return self.registry_proxy
724 if not hasattr (self, 'sliceapi_proxy'):
725 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
726 if hasattr(self.command_options,'component') and self.command_options.component:
727 # resolve the hrn at the registry
728 node_hrn = self.command_options.component
729 records = self.registry().Resolve(node_hrn, self.my_credential_string)
730 records = filter_records('node', records)
732 self.logger.warning("No such component:%r"% opts.component)
734 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
735 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
737 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
738 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
739 self.sm_url = 'http://' + self.sm_url
740 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
741 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
742 timeout=self.options.timeout, verbose=self.options.debug)
743 return self.sliceapi_proxy
745 def get_cached_server_version(self, server):
746 # check local cache first
749 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
750 cache_key = server.url + "-version"
752 cache = Cache(cache_file)
755 self.logger.info("Local cache not found at: %s" % cache_file)
758 version = cache.get(cache_key)
761 result = server.GetVersion()
762 version= ReturnValue.get_value(result)
763 # cache version for 20 minutes
764 cache.add(cache_key, version, ttl= 60*20)
765 self.logger.info("Updating cache file %s" % cache_file)
766 cache.save_to_file(cache_file)
770 ### resurrect this temporarily so we can support V1 aggregates for a while
771 def server_supports_options_arg(self, server):
773 Returns true if server support the optional call_id arg, false otherwise.
775 server_version = self.get_cached_server_version(server)
777 # xxx need to rewrite this
778 if int(server_version.get('geni_api')) >= 2:
782 def server_supports_call_id_arg(self, server):
783 server_version = self.get_cached_server_version(server)
785 if 'sfa' in server_version and 'code_tag' in server_version:
786 code_tag = server_version['code_tag']
787 code_tag_parts = code_tag.split("-")
788 version_parts = code_tag_parts[0].split(".")
789 major, minor = version_parts[0], version_parts[1]
790 rev = code_tag_parts[1]
791 if int(major) == 1 and minor == 0 and build >= 22:
795 ### ois = options if supported
796 # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
797 def ois (self, server, option_dict):
798 if self.server_supports_options_arg (server):
800 elif self.server_supports_call_id_arg (server):
801 return [ unique_call_id () ]
805 ### cis = call_id if supported - like ois
806 def cis (self, server):
807 if self.server_supports_call_id_arg (server):
808 return [ unique_call_id ]
812 ######################################## miscell utilities
813 def get_rspec_file(self, rspec):
814 if (os.path.isabs(rspec)):
817 file = os.path.join(self.options.sfi_dir, rspec)
818 if (os.path.isfile(file)):
821 self.logger.critical("No such rspec file %s"%rspec)
824 def get_record_file(self, record):
825 if (os.path.isabs(record)):
828 file = os.path.join(self.options.sfi_dir, record)
829 if (os.path.isfile(file)):
832 self.logger.critical("No such registry record file %s"%record)
836 #==========================================================================
837 # Following functions implement the commands
839 # Registry-related commands
840 #==========================================================================
842 @register_command("","")
843 def config (self, options, args):
844 "Display contents of current config"
845 print "# From configuration file %s"%self.config_file
846 flags=[ ('sfi', [ ('registry','reg_url'),
847 ('auth','authority'),
853 flags.append ( ('myslice', ['backend', 'delegate', 'platform', 'username'] ) )
855 for (section, tuples) in flags:
858 for (external_name, internal_name) in tuples:
859 print "%-20s = %s"%(external_name,getattr(self,internal_name))
862 varname="%s_%s"%(section.upper(),name.upper())
863 value=getattr(self.config_instance,varname)
864 print "%-20s = %s"%(name,value)
866 @register_command("","")
867 def version(self, options, args):
869 display an SFA server version (GetVersion)
870 or version information about sfi itself
872 if options.version_local:
873 version=version_core()
875 if options.version_registry:
876 server=self.registry()
878 server = self.sliceapi()
879 result = server.GetVersion()
880 version = ReturnValue.get_value(result)
882 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
884 pprinter = PrettyPrinter(indent=4)
885 pprinter.pprint(version)
887 @register_command("authority","")
888 def list(self, options, args):
890 list entries in named authority registry (List)
897 if options.recursive:
898 opts['recursive'] = options.recursive
900 if options.show_credential:
901 show_credentials(self.my_credential_string)
903 list = self.registry().List(hrn, self.my_credential_string, options)
905 raise Exception, "Not enough parameters for the 'list' command"
907 # filter on person, slice, site, node, etc.
908 # This really should be in the self.filter_records funct def comment...
909 list = filter_records(options.type, list)
910 terminal_render (list, options)
912 save_records_to_file(options.file, list, options.fileformat)
915 @register_command("name","")
916 def show(self, options, args):
918 show details about named registry record (Resolve)
924 # explicitly require Resolve to run in details mode
925 record_dicts = self.registry().Resolve(hrn, self.my_credential_string, {'details':True})
926 record_dicts = filter_records(options.type, record_dicts)
928 self.logger.error("No record of type %s"% options.type)
930 # user has required to focus on some keys
932 def project (record):
934 for key in options.keys:
935 try: projected[key]=record[key]
938 record_dicts = [ project (record) for record in record_dicts ]
939 records = [ Record(dict=record_dict) for record_dict in record_dicts ]
940 for record in records:
941 if (options.format == "text"): record.dump(sort=True)
942 else: print record.save_as_xml()
944 save_records_to_file(options.file, record_dicts, options.fileformat)
947 @register_command("[xml-filename]","")
948 def add(self, options, args):
949 """add record into registry (Register)
950 from command line options (recommended)
951 old-school method involving an xml file still supported"""
953 auth_cred = self.my_authority_credential_string()
954 if options.show_credential:
955 show_credentials(auth_cred)
962 record_filepath = args[0]
963 rec_file = self.get_record_file(record_filepath)
964 record_dict.update(load_record_from_file(rec_file).todict())
966 print "Cannot load record file %s"%record_filepath
969 record_dict.update(load_record_from_opts(options).todict())
970 # we should have a type by now
971 if 'type' not in record_dict :
974 # this is still planetlab dependent.. as plc will whine without that
975 # also, it's only for adding
976 if record_dict['type'] == 'user':
977 if not 'first_name' in record_dict:
978 record_dict['first_name'] = record_dict['hrn']
979 if 'last_name' not in record_dict:
980 record_dict['last_name'] = record_dict['hrn']
981 return self.registry().Register(record_dict, auth_cred)
983 @register_command("[xml-filename]","")
984 def update(self, options, args):
985 """update record into registry (Update)
986 from command line options (recommended)
987 old-school method involving an xml file still supported"""
990 record_filepath = args[0]
991 rec_file = self.get_record_file(record_filepath)
992 record_dict.update(load_record_from_file(rec_file).todict())
994 record_dict.update(load_record_from_opts(options).todict())
995 # at the very least we need 'type' here
996 if 'type' not in record_dict:
1000 # don't translate into an object, as this would possibly distort
1001 # user-provided data; e.g. add an 'email' field to Users
1002 if record_dict['type'] == "user":
1003 if record_dict['hrn'] == self.user:
1004 cred = self.my_credential_string
1006 cred = self.my_authority_credential_string()
1007 elif record_dict['type'] in ["slice"]:
1009 cred = self.slice_credential_string(record_dict['hrn'])
1010 except ServerException, e:
1011 # XXX smbaker -- once we have better error return codes, update this
1012 # to do something better than a string compare
1013 if "Permission error" in e.args[0]:
1014 cred = self.my_authority_credential_string()
1017 elif record_dict['type'] in ["authority"]:
1018 cred = self.my_authority_credential_string()
1019 elif record_dict['type'] == 'node':
1020 cred = self.my_authority_credential_string()
1022 raise "unknown record type" + record_dict['type']
1023 if options.show_credential:
1024 show_credentials(cred)
1025 return self.registry().Update(record_dict, cred)
1027 @register_command("hrn","")
1028 def remove(self, options, args):
1029 "remove registry record by name (Remove)"
1030 auth_cred = self.my_authority_credential_string()
1038 if options.show_credential:
1039 show_credentials(auth_cred)
1040 return self.registry().Remove(hrn, auth_cred, type)
1042 # ==================================================================
1043 # Slice-related commands
1044 # ==================================================================
1046 @register_command("","")
1047 def slices(self, options, args):
1048 "list instantiated slices (ListSlices) - returns urn's"
1049 server = self.sliceapi()
1051 creds = [self.my_credential_string]
1052 # options and call_id when supported
1054 api_options['call_id']=unique_call_id()
1055 if options.show_credential:
1056 show_credentials(creds)
1057 result = server.ListSlices(creds, *self.ois(server,api_options))
1058 value = ReturnValue.get_value(result)
1059 if self.options.raw:
1060 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1065 # show rspec for named slice
1066 @register_command("","")
1067 def resources(self, options, args):
1069 discover available resources (ListResources)
1071 server = self.sliceapi()
1074 creds = [self.my_credential]
1075 if options.delegate:
1076 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
1077 if options.show_credential:
1078 show_credentials(creds)
1080 # no need to check if server accepts the options argument since the options has
1081 # been a required argument since v1 API
1083 # always send call_id to v2 servers
1084 api_options ['call_id'] = unique_call_id()
1085 # ask for cached value if available
1086 api_options ['cached'] = True
1088 api_options['info'] = options.info
1089 if options.list_leases:
1090 api_options['list_leases'] = options.list_leases
1092 if options.current == True:
1093 api_options['cached'] = False
1095 api_options['cached'] = True
1096 if options.rspec_version:
1097 version_manager = VersionManager()
1098 server_version = self.get_cached_server_version(server)
1099 if 'sfa' in server_version:
1100 # just request the version the client wants
1101 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1103 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1105 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1106 result = server.ListResources (creds, api_options)
1107 value = ReturnValue.get_value(result)
1108 if self.options.raw:
1109 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1110 if options.file is not None:
1111 save_rspec_to_file(value, options.file)
1112 if (self.options.raw is None) and (options.file is None):
1113 display_rspec(value, options.format)
1117 @register_command("slice_hrn","")
1118 def describe(self, options, args):
1120 shows currently allocated/provisioned resources
1121 of the named slice or set of slivers (Describe)
1123 server = self.sliceapi()
1126 creds = [self.slice_credential(args[0])]
1127 if options.delegate:
1128 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
1129 if options.show_credential:
1130 show_credentials(creds)
1132 api_options = {'call_id': unique_call_id(),
1134 'info': options.info,
1135 'list_leases': options.list_leases,
1136 'geni_rspec_version': {'type': 'geni', 'version': '3.0'},
1138 if options.rspec_version:
1139 version_manager = VersionManager()
1140 server_version = self.get_cached_server_version(server)
1141 if 'sfa' in server_version:
1142 # just request the version the client wants
1143 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1145 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1146 urn = Xrn(args[0], type='slice').get_urn()
1147 result = server.Describe([urn], creds, api_options)
1148 value = ReturnValue.get_value(result)
1149 if self.options.raw:
1150 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1151 if options.file is not None:
1152 save_rspec_to_file(value, options.file)
1153 if (self.options.raw is None) and (options.file is None):
1154 display_rspec(value, options.format)
1158 @register_command("slice_hrn","")
1159 def delete(self, options, args):
1161 de-allocate and de-provision all or named slivers of the slice (Delete)
1163 server = self.sliceapi()
1167 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1170 slice_cred = self.slice_credential(slice_hrn)
1171 creds = [slice_cred]
1173 # options and call_id when supported
1175 api_options ['call_id'] = unique_call_id()
1176 if options.show_credential:
1177 show_credentials(creds)
1178 result = server.Delete([slice_urn], creds, *self.ois(server, api_options ) )
1179 value = ReturnValue.get_value(result)
1180 if self.options.raw:
1181 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1186 @register_command("slice_hrn rspec","")
1187 def allocate(self, options, args):
1189 allocate resources to the named slice (Allocate)
1191 server = self.sliceapi()
1192 server_version = self.get_cached_server_version(server)
1194 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1197 creds = [self.slice_credential(slice_hrn)]
1199 delegated_cred = None
1200 if server_version.get('interface') == 'slicemgr':
1201 # delegate our cred to the slice manager
1202 # do not delegate cred to slicemgr...not working at the moment
1204 #if server_version.get('hrn'):
1205 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1206 #elif server_version.get('urn'):
1207 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1209 if options.show_credential:
1210 show_credentials(creds)
1213 rspec_file = self.get_rspec_file(args[1])
1214 rspec = open(rspec_file).read()
1216 api_options ['call_id'] = unique_call_id()
1217 result = server.Allocate(slice_urn, creds, rspec, api_options)
1218 value = ReturnValue.get_value(result)
1219 if self.options.raw:
1220 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1221 if options.file is not None:
1222 save_rspec_to_file (value, options.file)
1223 if (self.options.raw is None) and (options.file is None):
1228 @register_command("slice_hrn","")
1229 def provision(self, options, args):
1231 provision already allocated resources of named slice (Provision)
1233 server = self.sliceapi()
1234 server_version = self.get_cached_server_version(server)
1236 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1239 creds = [self.slice_credential(slice_hrn)]
1240 delegated_cred = None
1241 if server_version.get('interface') == 'slicemgr':
1242 # delegate our cred to the slice manager
1243 # do not delegate cred to slicemgr...not working at the moment
1245 #if server_version.get('hrn'):
1246 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1247 #elif server_version.get('urn'):
1248 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1250 if options.show_credential:
1251 show_credentials(creds)
1254 api_options ['call_id'] = unique_call_id()
1256 # set the requtested rspec version
1257 version_manager = VersionManager()
1258 rspec_version = version_manager._get_version('geni', '3.0').to_dict()
1259 api_options['geni_rspec_version'] = rspec_version
1262 # need to pass along user keys to the aggregate.
1264 # { urn: urn:publicid:IDN+emulab.net+user+alice
1265 # keys: [<ssh key A>, <ssh key B>]
1268 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1269 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1270 slice_record = slice_records[0]
1271 user_hrns = slice_record['researcher']
1272 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1273 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1274 users = pg_users_arg(user_records)
1276 api_options['geni_users'] = users
1277 result = server.Provision([slice_urn], creds, api_options)
1278 value = ReturnValue.get_value(result)
1279 if self.options.raw:
1280 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1281 if options.file is not None:
1282 save_rspec_to_file (value, options.file)
1283 if (self.options.raw is None) and (options.file is None):
1287 @register_command("slice_hrn","")
1288 def status(self, options, args):
1290 retrieve the status of the slivers belonging to tne named slice (Status)
1292 server = self.sliceapi()
1296 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1299 slice_cred = self.slice_credential(slice_hrn)
1300 creds = [slice_cred]
1302 # options and call_id when supported
1304 api_options['call_id']=unique_call_id()
1305 if options.show_credential:
1306 show_credentials(creds)
1307 result = server.Status([slice_urn], creds, *self.ois(server,api_options))
1308 value = ReturnValue.get_value(result)
1309 if self.options.raw:
1310 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1313 # Thierry: seemed to be missing
1316 @register_command("slice_hrn action","")
1317 def action(self, options, args):
1319 Perform the named operational action on these slivers
1321 server = self.sliceapi()
1326 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1328 slice_cred = self.slice_credential(args[0])
1329 creds = [slice_cred]
1330 if options.delegate:
1331 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1332 creds.append(delegated_cred)
1334 result = server.PerformOperationalAction([slice_urn], creds, action , api_options)
1335 value = ReturnValue.get_value(result)
1336 if self.options.raw:
1337 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1342 @register_command("slice_hrn time","")
1343 def renew(self, options, args):
1345 renew slice (RenewSliver)
1347 server = self.sliceapi()
1351 [ slice_hrn, input_time ] = args
1353 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1354 # time: don't try to be smart on the time format, server-side will
1356 slice_cred = self.slice_credential(args[0])
1357 creds = [slice_cred]
1358 # options and call_id when supported
1360 api_options['call_id']=unique_call_id()
1361 if options.show_credential:
1362 show_credentials(creds)
1363 result = server.Renew([slice_urn], creds, input_time, *self.ois(server,api_options))
1364 value = ReturnValue.get_value(result)
1365 if self.options.raw:
1366 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1372 @register_command("slice_hrn","")
1373 def shutdown(self, options, args):
1375 shutdown named slice (Shutdown)
1377 server = self.sliceapi()
1380 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1382 slice_cred = self.slice_credential(slice_hrn)
1383 creds = [slice_cred]
1384 result = server.Shutdown(slice_urn, creds)
1385 value = ReturnValue.get_value(result)
1386 if self.options.raw:
1387 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1393 @register_command("[name]","")
1394 def gid(self, options, args):
1396 Create a GID (CreateGid)
1401 target_hrn = args[0]
1402 my_gid_string = open(self.client_bootstrap.my_gid()).read()
1403 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, my_gid_string)
1405 filename = options.file
1407 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1408 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1409 GID(string=gid).save_to_file(filename)
1411 ####################
1412 @register_command("to_hrn","""$ sfi delegate -u -p -s ple.inria.heartbeat -s ple.inria.omftest ple.upmc.slicebrowser
1414 will locally create a set of delegated credentials for the benefit of ple.upmc.slicebrowser
1415 the set of credentials in the scope for this call would be
1416 (*) ple.inria.thierry_parmentelat.user_for_ple.upmc.slicebrowser.user.cred
1418 (*) ple.inria.pi_for_ple.upmc.slicebrowser.user.cred
1420 (*) ple.inria.heartbeat.slice_for_ple.upmc.slicebrowser.user.cred
1421 (*) ple.inria.omftest.slice_for_ple.upmc.slicebrowser.user.cred
1422 because of the two -s options
1425 def delegate (self, options, args):
1427 (locally) create delegate credential for use by given hrn
1428 make sure to check for 'sfi myslice' instead if you plan
1435 # support for several delegations in the same call
1436 # so first we gather the things to do
1438 for slice_hrn in options.delegate_slices:
1439 message="%s.slice"%slice_hrn
1440 original = self.slice_credential_string(slice_hrn)
1441 tuples.append ( (message, original,) )
1442 if options.delegate_pi:
1443 my_authority=self.authority
1444 message="%s.pi"%my_authority
1445 original = self.my_authority_credential_string()
1446 tuples.append ( (message, original,) )
1447 for auth_hrn in options.delegate_auths:
1448 message="%s.auth"%auth_hrn
1449 original=self.authority_credential_string(auth_hrn)
1450 tuples.append ( (message, original, ) )
1451 # if nothing was specified at all at this point, let's assume -u
1452 if not tuples: options.delegate_user=True
1454 if options.delegate_user:
1455 message="%s.user"%self.user
1456 original = self.my_credential_string
1457 tuples.append ( (message, original, ) )
1459 # default type for beneficial is user unless -A
1460 if options.delegate_to_authority: to_type='authority'
1461 else: to_type='user'
1463 # let's now handle all this
1464 # it's all in the filenaming scheme
1465 for (message,original) in tuples:
1466 delegated_string = self.client_bootstrap.delegate_credential_string(original, to_hrn, to_type)
1467 delegated_credential = Credential (string=delegated_string)
1468 filename = os.path.join ( self.options.sfi_dir,
1469 "%s_for_%s.%s.cred"%(message,to_hrn,to_type))
1470 delegated_credential.save_to_file(filename, save_parents=True)
1471 self.logger.info("delegated credential for %s to %s and wrote to %s"%(message,to_hrn,filename))
1473 ####################
1474 @register_command("","""$ less +/myslice sfi_config
1476 backend = http://manifold.pl.sophia.inria.fr:7080
1477 # the HRN that myslice uses, so that we are delegating to
1478 delegate = ple.upmc.slicebrowser
1479 # platform - this is a myslice concept
1481 # username - as of this writing (May 2013) a simple login name
1485 will first collect the slices that you are part of, then make sure
1486 all your credentials are up-to-date (read: refresh expired ones)
1487 then compute delegated credentials for user 'ple.upmc.slicebrowser'
1488 and upload them all on myslice backend, using 'platform' and 'user'.
1489 A password will be prompted for the upload part.
1491 $ sfi -v myslice -- or sfi -vv myslice
1492 same but with more and more verbosity
1495 is synonym to sfi myslice as no other command starts with an 'm'
1497 ) # register_command
1498 def myslice (self, options, args):
1500 """ This helper is for refreshing your credentials at myslice; it will
1501 * compute all the slices that you currently have credentials on
1502 * refresh all your credentials (you as a user and pi, your slices)
1503 * upload them to the manifold backend server
1504 for last phase, sfi_config is read to look for the [myslice] section,
1505 and namely the 'backend', 'delegate' and 'user' settings"""
1512 ### the rough sketch goes like this
1513 # (a) rain check for sufficient config in sfi_config
1514 # we don't allow to override these settings for now
1516 myslice_keys=['backend', 'delegate', 'platform', 'username']
1517 for key in myslice_keys:
1518 full_key="MYSLICE_" + key.upper()
1519 value=getattr(self.config_instance,full_key,None)
1520 if value: myslice_dict[key]=value
1521 else: print "Unsufficient config, missing key %s in [myslice] section of sfi_config"%key
1522 if len(myslice_dict) != len(myslice_keys):
1525 # (b) figure whether we are PI for the authority where we belong
1526 self.logger.info("Resolving our own id")
1527 my_records=self.registry().Resolve(self.user,self.my_credential_string)
1528 if len(my_records)!=1: print "Cannot Resolve %s -- exiting"%self.user; sys.exit(1)
1529 my_record=my_records[0]
1530 my_auths = my_record['reg-pi-authorities']
1531 self.logger.info("Found %d authorities that we are PI for"%len(my_auths))
1532 self.logger.debug("They are %s"%(my_auths))
1534 # (c) get the set of slices that we are in
1535 my_slices=my_record['reg-slices']
1536 self.logger.info("Found %d slices that we are member of"%len(my_slices))
1537 self.logger.debug("They are: %s"%(my_slices))
1539 # (d) make sure we have *valid* credentials for all these
1541 hrn_credentials.append ( (self.user, 'user', self.my_credential_string,) )
1542 for auth_hrn in my_auths:
1543 hrn_credentials.append ( (auth_hrn, 'auth', self.authority_credential_string(auth_hrn),) )
1544 for slice_hrn in my_slices:
1545 hrn_credentials.append ( (slice_hrn, 'slice', self.slice_credential_string (slice_hrn),) )
1547 # (e) check for the delegated version of these
1548 # xxx todo add an option -a/-A? like for 'sfi delegate' for when we ever
1549 # switch to myslice using an authority instead of a user
1550 delegatee_type='user'
1551 delegatee_hrn=myslice_dict['delegate']
1552 hrn_delegated_credentials = []
1553 for (hrn, htype, credential) in hrn_credentials:
1554 delegated_credential = self.client_bootstrap.delegate_credential_string (credential, delegatee_hrn, delegatee_type)
1555 hrn_delegated_credentials.append ((hrn, htype, delegated_credential, ))
1557 # (f) and finally upload them to manifold server
1558 # xxx todo add an option so the password can be set on the command line
1559 # (but *NOT* in the config file) so other apps can leverage this
1560 uploader = ManifoldUploader (logger=self.logger,
1561 url=myslice_dict['backend'],
1562 platform=myslice_dict['platform'],
1563 username=myslice_dict['username'],
1564 password=options.password)
1565 uploader.prompt_all()
1566 (count_all,count_success)=(0,0)
1567 for (hrn,htype,delegated_credential) in hrn_delegated_credentials:
1569 inspect=Credential(string=delegated_credential)
1570 expire_datetime=inspect.get_expiration()
1571 message="%s (%s) [exp:%s]"%(hrn,htype,expire_datetime)
1572 if uploader.upload(delegated_credential,message=message):
1576 self.logger.info("Successfully uploaded %d/%d credentials"%(count_success,count_all))
1577 # at first I thought we would want to save these,
1578 # like 'sfi delegate does' but on second thought
1579 # it is probably not helpful as people would not
1580 # need to run 'sfi delegate' at all anymore
1581 if count_success != count_all: sys.exit(1)
1584 # Thierry: I'm turning this off as a command, no idea what it's used for
1585 # @register_command("cred","")
1586 def trusted(self, options, args):
1588 return the trusted certs at this interface (get_trusted_certs)
1590 trusted_certs = self.registry().get_trusted_certs()
1591 for trusted_cert in trusted_certs:
1592 gid = GID(string=trusted_cert)
1594 cert = Certificate(string=trusted_cert)
1595 self.logger.debug('Sfi.trusted -> %r'%cert.get_subject())