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_global_parser().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_command_parser(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_global_parser(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_command_parser(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 ("add", "update"):
390 parser.add_option('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
391 parser.add_option('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
392 parser.add_option('-e', '--email', dest='email', default="", help="email (mandatory for users)")
393 parser.add_option('-k', '--key', dest='key', metavar='<key>', help='public key string or file',
395 parser.add_option('-s', '--slices', dest='slices', metavar='<slices>', help='Set/replace slice xrns',
396 default='', type="str", action='callback', callback=optparse_listvalue_callback)
397 parser.add_option('-r', '--researchers', dest='researchers', metavar='<researchers>',
398 help='Set/replace slice researchers', default='', type="str", action='callback',
399 callback=optparse_listvalue_callback)
400 parser.add_option('-p', '--pis', dest='pis', metavar='<PIs>', help='Set/replace Principal Investigators/Project Managers',
401 default='', type="str", action='callback', callback=optparse_listvalue_callback)
402 parser.add_option ('-X','--extra',dest='extras',default={},type='str',metavar="<EXTRA_ASSIGNS>",
403 action="callback", callback=optparse_dictvalue_callback, nargs=1,
404 help="set extra/testbed-dependent flags, e.g. --extra enabled=true")
406 # user specifies remote aggregate/sm/component
407 if command in ("resources", "describe", "allocate", "provision", "delete", "allocate", "provision",
408 "action", "shutdown", "renew", "status"):
409 parser.add_option("-d", "--delegate", dest="delegate", default=None,
411 help="Include a credential delegated to the user's root"+\
412 "authority in set of credentials for this call")
414 # show_credential option
415 if command in ("list","resources", "describe", "provision", "allocate", "add","update","remove","slices","delete","status","renew"):
416 parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False,
417 help="show credential(s) used in human-readable form")
418 # registy filter option
419 if command in ("list", "show", "remove"):
420 parser.add_option("-t", "--type", dest="type", type="choice",
421 help="type filter ([all]|user|slice|authority|node|aggregate)",
422 choices=("all", "user", "slice", "authority", "node", "aggregate"),
424 if command in ("show"):
425 parser.add_option("-k","--key",dest="keys",action="append",default=[],
426 help="specify specific keys to be displayed from record")
427 if command in ("resources", "describe"):
429 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
430 help="schema type and version of resulting RSpec")
431 # disable/enable cached rspecs
432 parser.add_option("-c", "--current", dest="current", default=False,
434 help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
436 parser.add_option("-f", "--format", dest="format", type="choice",
437 help="display format ([xml]|dns|ip)", default="xml",
438 choices=("xml", "dns", "ip"))
439 #panos: a new option to define the type of information about resources a user is interested in
440 parser.add_option("-i", "--info", dest="info",
441 help="optional component information", default=None)
442 # a new option to retreive or not reservation-oriented RSpecs (leases)
443 parser.add_option("-l", "--list_leases", dest="list_leases", type="choice",
444 help="Retreive or not reservation-oriented RSpecs ([resources]|leases|all )",
445 choices=("all", "resources", "leases"), default="resources")
448 if command in ("resources", "describe", "allocate", "provision", "show", "list", "gid"):
449 parser.add_option("-o", "--output", dest="file",
450 help="output XML to file", metavar="FILE", default=None)
452 if command in ("show", "list"):
453 parser.add_option("-f", "--format", dest="format", type="choice",
454 help="display format ([text]|xml)", default="text",
455 choices=("text", "xml"))
457 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
458 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
459 choices=("xml", "xmllist", "hrnlist"))
460 if command == 'list':
461 parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
462 help="list all child records", default=False)
463 parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
464 help="gives details, like user keys", default=False)
465 if command in ("delegate"):
466 parser.add_option("-u", "--user",
467 action="store_true", dest="delegate_user", default=False,
468 help="delegate your own credentials; default if no other option is provided")
469 parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
470 metavar="slice_hrn", help="delegate cred. for slice HRN")
471 parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
472 metavar='auth_hrn', help="delegate cred for auth HRN")
473 # this primarily is a shorthand for -a my_hrn
474 parser.add_option("-p", "--pi", dest='delegate_pi', default=None, action='store_true',
475 help="delegate your PI credentials, so s.t. like -a your_hrn^")
476 parser.add_option("-A","--to-authority",dest='delegate_to_authority',action='store_true',default=False,
477 help="""by default the mandatory argument is expected to be a user,
478 use this if you mean an authority instead""")
480 if command in ("version"):
481 parser.add_option("-R","--registry-version",
482 action="store_true", dest="version_registry", default=False,
483 help="probe registry version instead of sliceapi")
484 parser.add_option("-l","--local",
485 action="store_true", dest="version_local", default=False,
486 help="display version of the local client")
492 # Main: parse arguments and dispatch to command
494 def dispatch(self, command, command_options, command_args):
495 method=getattr(self, command, None)
497 print "Unknown command %s"%command
499 return method(command_options, command_args)
502 self.sfi_parser = self.create_global_parser()
503 (options, args) = self.sfi_parser.parse_args()
505 self.print_commands_help(options)
507 self.options = options
509 self.logger.setLevelFromOptVerbose(self.options.verbose)
512 self.logger.critical("No command given. Use -h for help.")
513 self.print_commands_help(options)
516 # complete / find unique match with command set
517 command_candidates = Candidates (commands_list)
519 command = command_candidates.only_match(input)
521 self.print_commands_help(options)
523 # second pass options parsing
525 self.command_parser = self.create_command_parser(command)
526 (command_options, command_args) = self.command_parser.parse_args(args[1:])
527 if command_options.help:
530 self.command_options = command_options
534 self.logger.debug("Command=%s" % self.command)
537 self.dispatch(command, command_options, command_args)
541 self.logger.log_exc ("sfi command %s failed"%command)
547 def read_config(self):
548 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
549 shell_config_file = os.path.join(self.options.sfi_dir,"sfi_config.sh")
551 if Config.is_ini(config_file):
552 config = Config (config_file)
554 # try upgrading from shell config format
555 fp, fn = mkstemp(suffix='sfi_config', text=True)
557 # we need to preload the sections we want parsed
558 # from the shell config
559 config.add_section('sfi')
560 # sface users should be able to use this same file to configure their stuff
561 config.add_section('sface')
562 # manifold users should be able to specify the details
563 # of their backend server here for 'sfi myslice'
564 config.add_section('myslice')
565 config.load(config_file)
567 shutil.move(config_file, shell_config_file)
569 config.save(config_file)
572 self.logger.critical("Failed to read configuration file %s"%config_file)
573 self.logger.info("Make sure to remove the export clauses and to add quotes")
574 if self.options.verbose==0:
575 self.logger.info("Re-run with -v for more details")
577 self.logger.log_exc("Could not read config file %s"%config_file)
580 self.config_instance=config
583 if (self.options.sm is not None):
584 self.sm_url = self.options.sm
585 elif hasattr(config, "SFI_SM"):
586 self.sm_url = config.SFI_SM
588 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
592 if (self.options.registry is not None):
593 self.reg_url = self.options.registry
594 elif hasattr(config, "SFI_REGISTRY"):
595 self.reg_url = config.SFI_REGISTRY
597 self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
601 if (self.options.user is not None):
602 self.user = self.options.user
603 elif hasattr(config, "SFI_USER"):
604 self.user = config.SFI_USER
606 self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
610 if (self.options.auth is not None):
611 self.authority = self.options.auth
612 elif hasattr(config, "SFI_AUTH"):
613 self.authority = config.SFI_AUTH
615 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
618 self.config_file=config_file
623 # Get various credential and spec files
625 # Establishes limiting conventions
626 # - conflates MAs and SAs
627 # - assumes last token in slice name is unique
629 # Bootstraps credentials
630 # - bootstrap user credential from self-signed certificate
631 # - bootstrap authority credential from user credential
632 # - bootstrap slice credential from user credential
635 # init self-signed cert, user credentials and gid
636 def bootstrap (self):
637 client_bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir,
639 # if -k is provided, use this to initialize private key
640 if self.options.user_private_key:
641 client_bootstrap.init_private_key_if_missing (self.options.user_private_key)
643 # trigger legacy compat code if needed
644 # the name has changed from just <leaf>.pkey to <hrn>.pkey
645 if not os.path.isfile(client_bootstrap.private_key_filename()):
646 self.logger.info ("private key not found, trying legacy name")
648 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%Xrn.unescape(get_leaf(self.user)))
649 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
650 client_bootstrap.init_private_key_if_missing (legacy_private_key)
651 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
653 self.logger.log_exc("Can't find private key ")
657 client_bootstrap.bootstrap_my_gid()
658 # extract what's needed
659 self.private_key = client_bootstrap.private_key()
660 self.my_credential_string = client_bootstrap.my_credential_string ()
661 self.my_credential = {'geni_type': 'geni_sfa',
662 'geni_version': '3.0',
663 'geni_value': self.my_credential_string}
664 self.my_gid = client_bootstrap.my_gid ()
665 self.client_bootstrap = client_bootstrap
668 def my_authority_credential_string(self):
669 if not self.authority:
670 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
672 return self.client_bootstrap.authority_credential_string (self.authority)
674 def authority_credential_string(self, auth_hrn):
675 return self.client_bootstrap.authority_credential_string (auth_hrn)
677 def slice_credential_string(self, name):
678 return self.client_bootstrap.slice_credential_string (name)
680 def slice_credential(self, name):
681 return {'geni_type': 'geni_sfa',
682 'geni_version': '3.0',
683 'geni_value': self.slice_credential_string(name)}
685 # xxx should be supported by sfaclientbootstrap as well
686 def delegate_cred(self, object_cred, hrn, type='authority'):
687 # the gid and hrn of the object we are delegating
688 if isinstance(object_cred, str):
689 object_cred = Credential(string=object_cred)
690 object_gid = object_cred.get_gid_object()
691 object_hrn = object_gid.get_hrn()
693 if not object_cred.get_privileges().get_all_delegate():
694 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
697 # the delegating user's gid
698 caller_gidfile = self.my_gid()
700 # the gid of the user who will be delegated to
701 delegee_gid = self.client_bootstrap.gid(hrn,type)
702 delegee_hrn = delegee_gid.get_hrn()
703 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
704 return dcred.save_to_string(save_parents=True)
707 # Management of the servers
712 if not hasattr (self, 'registry_proxy'):
713 self.logger.info("Contacting Registry at: %s"%self.reg_url)
714 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
715 timeout=self.options.timeout, verbose=self.options.debug)
716 return self.registry_proxy
720 if not hasattr (self, 'sliceapi_proxy'):
721 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
722 if hasattr(self.command_options,'component') and self.command_options.component:
723 # resolve the hrn at the registry
724 node_hrn = self.command_options.component
725 records = self.registry().Resolve(node_hrn, self.my_credential_string)
726 records = filter_records('node', records)
728 self.logger.warning("No such component:%r"% opts.component)
730 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
731 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
733 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
734 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
735 self.sm_url = 'http://' + self.sm_url
736 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
737 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
738 timeout=self.options.timeout, verbose=self.options.debug)
739 return self.sliceapi_proxy
741 def get_cached_server_version(self, server):
742 # check local cache first
745 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
746 cache_key = server.url + "-version"
748 cache = Cache(cache_file)
751 self.logger.info("Local cache not found at: %s" % cache_file)
754 version = cache.get(cache_key)
757 result = server.GetVersion()
758 version= ReturnValue.get_value(result)
759 # cache version for 20 minutes
760 cache.add(cache_key, version, ttl= 60*20)
761 self.logger.info("Updating cache file %s" % cache_file)
762 cache.save_to_file(cache_file)
766 ### resurrect this temporarily so we can support V1 aggregates for a while
767 def server_supports_options_arg(self, server):
769 Returns true if server support the optional call_id arg, false otherwise.
771 server_version = self.get_cached_server_version(server)
773 # xxx need to rewrite this
774 if int(server_version.get('geni_api')) >= 2:
778 def server_supports_call_id_arg(self, server):
779 server_version = self.get_cached_server_version(server)
781 if 'sfa' in server_version and 'code_tag' in server_version:
782 code_tag = server_version['code_tag']
783 code_tag_parts = code_tag.split("-")
784 version_parts = code_tag_parts[0].split(".")
785 major, minor = version_parts[0], version_parts[1]
786 rev = code_tag_parts[1]
787 if int(major) == 1 and minor == 0 and build >= 22:
791 ### ois = options if supported
792 # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
793 def ois (self, server, option_dict):
794 if self.server_supports_options_arg (server):
796 elif self.server_supports_call_id_arg (server):
797 return [ unique_call_id () ]
801 ### cis = call_id if supported - like ois
802 def cis (self, server):
803 if self.server_supports_call_id_arg (server):
804 return [ unique_call_id ]
808 ######################################## miscell utilities
809 def get_rspec_file(self, rspec):
810 if (os.path.isabs(rspec)):
813 file = os.path.join(self.options.sfi_dir, rspec)
814 if (os.path.isfile(file)):
817 self.logger.critical("No such rspec file %s"%rspec)
820 def get_record_file(self, record):
821 if (os.path.isabs(record)):
824 file = os.path.join(self.options.sfi_dir, record)
825 if (os.path.isfile(file)):
828 self.logger.critical("No such registry record file %s"%record)
832 #==========================================================================
833 # Following functions implement the commands
835 # Registry-related commands
836 #==========================================================================
838 @register_command("","")
839 def config (self, options, args):
840 "Display contents of current config"
841 print "# From configuration file %s"%self.config_file
842 flags=[ ('sfi', [ ('registry','reg_url'),
843 ('auth','authority'),
849 flags.append ( ('myslice', ['backend', 'delegate', 'platform', 'username'] ) )
851 for (section, tuples) in flags:
854 for (external_name, internal_name) in tuples:
855 print "%-20s = %s"%(external_name,getattr(self,internal_name))
858 varname="%s_%s"%(section.upper(),name.upper())
859 value=getattr(self.config_instance,varname)
860 print "%-20s = %s"%(name,value)
862 @register_command("","")
863 def version(self, options, args):
865 display an SFA server version (GetVersion)
866 or version information about sfi itself
868 if options.version_local:
869 version=version_core()
871 if options.version_registry:
872 server=self.registry()
874 server = self.sliceapi()
875 result = server.GetVersion()
876 version = ReturnValue.get_value(result)
878 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
880 pprinter = PrettyPrinter(indent=4)
881 pprinter.pprint(version)
883 @register_command("authority","")
884 def list(self, options, args):
886 list entries in named authority registry (List)
893 if options.recursive:
894 opts['recursive'] = options.recursive
896 if options.show_credential:
897 show_credentials(self.my_credential_string)
899 list = self.registry().List(hrn, self.my_credential_string, options)
901 raise Exception, "Not enough parameters for the 'list' command"
903 # filter on person, slice, site, node, etc.
904 # This really should be in the self.filter_records funct def comment...
905 list = filter_records(options.type, list)
906 terminal_render (list, options)
908 save_records_to_file(options.file, list, options.fileformat)
911 @register_command("name","")
912 def show(self, options, args):
914 show details about named registry record (Resolve)
920 # explicitly require Resolve to run in details mode
921 record_dicts = self.registry().Resolve(hrn, self.my_credential_string, {'details':True})
922 record_dicts = filter_records(options.type, record_dicts)
924 self.logger.error("No record of type %s"% options.type)
926 # user has required to focus on some keys
928 def project (record):
930 for key in options.keys:
931 try: projected[key]=record[key]
934 record_dicts = [ project (record) for record in record_dicts ]
935 records = [ Record(dict=record_dict) for record_dict in record_dicts ]
936 for record in records:
937 if (options.format == "text"): record.dump(sort=True)
938 else: print record.save_as_xml()
940 save_records_to_file(options.file, record_dicts, options.fileformat)
943 @register_command("[xml-filename]","")
944 def add(self, options, args):
945 """add record into registry (Register)
946 from command line options (recommended)
947 old-school method involving an xml file still supported"""
949 auth_cred = self.my_authority_credential_string()
950 if options.show_credential:
951 show_credentials(auth_cred)
958 record_filepath = args[0]
959 rec_file = self.get_record_file(record_filepath)
960 record_dict.update(load_record_from_file(rec_file).todict())
962 print "Cannot load record file %s"%record_filepath
965 record_dict.update(load_record_from_opts(options).todict())
966 # we should have a type by now
967 if 'type' not in record_dict :
970 # this is still planetlab dependent.. as plc will whine without that
971 # also, it's only for adding
972 if record_dict['type'] == 'user':
973 if not 'first_name' in record_dict:
974 record_dict['first_name'] = record_dict['hrn']
975 if 'last_name' not in record_dict:
976 record_dict['last_name'] = record_dict['hrn']
977 return self.registry().Register(record_dict, auth_cred)
979 @register_command("[xml-filename]","")
980 def update(self, options, args):
981 """update record into registry (Update)
982 from command line options (recommended)
983 old-school method involving an xml file still supported"""
986 record_filepath = args[0]
987 rec_file = self.get_record_file(record_filepath)
988 record_dict.update(load_record_from_file(rec_file).todict())
990 record_dict.update(load_record_from_opts(options).todict())
991 # at the very least we need 'type' here
992 if 'type' not in record_dict:
996 # don't translate into an object, as this would possibly distort
997 # user-provided data; e.g. add an 'email' field to Users
998 if record_dict['type'] == "user":
999 if record_dict['hrn'] == self.user:
1000 cred = self.my_credential_string
1002 cred = self.my_authority_credential_string()
1003 elif record_dict['type'] in ["slice"]:
1005 cred = self.slice_credential_string(record_dict['hrn'])
1006 except ServerException, e:
1007 # XXX smbaker -- once we have better error return codes, update this
1008 # to do something better than a string compare
1009 if "Permission error" in e.args[0]:
1010 cred = self.my_authority_credential_string()
1013 elif record_dict['type'] in ["authority"]:
1014 cred = self.my_authority_credential_string()
1015 elif record_dict['type'] == 'node':
1016 cred = self.my_authority_credential_string()
1018 raise "unknown record type" + record_dict['type']
1019 if options.show_credential:
1020 show_credentials(cred)
1021 return self.registry().Update(record_dict, cred)
1023 @register_command("name","")
1024 def remove(self, options, args):
1025 "remove registry record by name (Remove)"
1026 auth_cred = self.my_authority_credential_string()
1034 if options.show_credential:
1035 show_credentials(auth_cred)
1036 return self.registry().Remove(hrn, auth_cred, type)
1038 # ==================================================================
1039 # Slice-related commands
1040 # ==================================================================
1042 @register_command("","")
1043 def slices(self, options, args):
1044 "list instantiated slices (ListSlices) - returns urn's"
1045 server = self.sliceapi()
1047 creds = [self.my_credential_string]
1048 # options and call_id when supported
1050 api_options['call_id']=unique_call_id()
1051 if options.show_credential:
1052 show_credentials(creds)
1053 result = server.ListSlices(creds, *self.ois(server,api_options))
1054 value = ReturnValue.get_value(result)
1055 if self.options.raw:
1056 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1061 # show rspec for named slice
1062 @register_command("","")
1063 def resources(self, options, args):
1065 discover available resources (ListResources)
1067 server = self.sliceapi()
1070 creds = [self.my_credential]
1071 if options.delegate:
1072 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
1073 if options.show_credential:
1074 show_credentials(creds)
1076 # no need to check if server accepts the options argument since the options has
1077 # been a required argument since v1 API
1079 # always send call_id to v2 servers
1080 api_options ['call_id'] = unique_call_id()
1081 # ask for cached value if available
1082 api_options ['cached'] = True
1084 api_options['info'] = options.info
1085 if options.list_leases:
1086 api_options['list_leases'] = options.list_leases
1088 if options.current == True:
1089 api_options['cached'] = False
1091 api_options['cached'] = True
1092 if options.rspec_version:
1093 version_manager = VersionManager()
1094 server_version = self.get_cached_server_version(server)
1095 if 'sfa' in server_version:
1096 # just request the version the client wants
1097 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1099 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1101 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1102 result = server.ListResources (creds, api_options)
1103 value = ReturnValue.get_value(result)
1104 if self.options.raw:
1105 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1106 if options.file is not None:
1107 save_rspec_to_file(value, options.file)
1108 if (self.options.raw is None) and (options.file is None):
1109 display_rspec(value, options.format)
1113 @register_command("slice_hrn","")
1114 def describe(self, options, args):
1116 shows currently allocated/provisioned resources
1117 of the named slice or set of slivers (Describe)
1119 server = self.sliceapi()
1122 creds = [self.slice_credential(args[0])]
1123 if options.delegate:
1124 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
1125 if options.show_credential:
1126 show_credentials(creds)
1128 api_options = {'call_id': unique_call_id(),
1130 'info': options.info,
1131 'list_leases': options.list_leases,
1132 'geni_rspec_version': {'type': 'geni', 'version': '3.0'},
1134 if options.rspec_version:
1135 version_manager = VersionManager()
1136 server_version = self.get_cached_server_version(server)
1137 if 'sfa' in server_version:
1138 # just request the version the client wants
1139 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1141 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1142 urn = Xrn(args[0], type='slice').get_urn()
1143 result = server.Describe([urn], creds, api_options)
1144 value = ReturnValue.get_value(result)
1145 if self.options.raw:
1146 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1147 if options.file is not None:
1148 save_rspec_to_file(value, options.file)
1149 if (self.options.raw is None) and (options.file is None):
1150 display_rspec(value, options.format)
1154 @register_command("slice_hrn","")
1155 def delete(self, options, args):
1157 de-allocate and de-provision all or named slivers of the slice (Delete)
1159 server = self.sliceapi()
1163 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1166 slice_cred = self.slice_credential(slice_hrn)
1167 creds = [slice_cred]
1169 # options and call_id when supported
1171 api_options ['call_id'] = unique_call_id()
1172 if options.show_credential:
1173 show_credentials(creds)
1174 result = server.Delete([slice_urn], creds, *self.ois(server, api_options ) )
1175 value = ReturnValue.get_value(result)
1176 if self.options.raw:
1177 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1182 @register_command("slice_hrn rspec","")
1183 def allocate(self, options, args):
1185 allocate resources to the named slice (Allocate)
1187 server = self.sliceapi()
1188 server_version = self.get_cached_server_version(server)
1190 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1193 creds = [self.slice_credential(slice_hrn)]
1195 delegated_cred = None
1196 if server_version.get('interface') == 'slicemgr':
1197 # delegate our cred to the slice manager
1198 # do not delegate cred to slicemgr...not working at the moment
1200 #if server_version.get('hrn'):
1201 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1202 #elif server_version.get('urn'):
1203 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1205 if options.show_credential:
1206 show_credentials(creds)
1209 rspec_file = self.get_rspec_file(args[1])
1210 rspec = open(rspec_file).read()
1212 api_options ['call_id'] = unique_call_id()
1213 result = server.Allocate(slice_urn, creds, rspec, api_options)
1214 value = ReturnValue.get_value(result)
1215 if self.options.raw:
1216 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1217 if options.file is not None:
1218 save_rspec_to_file (value, options.file)
1219 if (self.options.raw is None) and (options.file is None):
1224 @register_command("slice_hrn","")
1225 def provision(self, options, args):
1227 provision already allocated resources of named slice (Provision)
1229 server = self.sliceapi()
1230 server_version = self.get_cached_server_version(server)
1232 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1235 creds = [self.slice_credential(slice_hrn)]
1236 delegated_cred = None
1237 if server_version.get('interface') == 'slicemgr':
1238 # delegate our cred to the slice manager
1239 # do not delegate cred to slicemgr...not working at the moment
1241 #if server_version.get('hrn'):
1242 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1243 #elif server_version.get('urn'):
1244 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1246 if options.show_credential:
1247 show_credentials(creds)
1250 api_options ['call_id'] = unique_call_id()
1252 # set the requtested rspec version
1253 version_manager = VersionManager()
1254 rspec_version = version_manager._get_version('geni', '3.0').to_dict()
1255 api_options['geni_rspec_version'] = rspec_version
1258 # need to pass along user keys to the aggregate.
1260 # { urn: urn:publicid:IDN+emulab.net+user+alice
1261 # keys: [<ssh key A>, <ssh key B>]
1264 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1265 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1266 slice_record = slice_records[0]
1267 user_hrns = slice_record['researcher']
1268 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1269 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1270 users = pg_users_arg(user_records)
1272 api_options['geni_users'] = users
1273 result = server.Provision([slice_urn], creds, api_options)
1274 value = ReturnValue.get_value(result)
1275 if self.options.raw:
1276 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1277 if options.file is not None:
1278 save_rspec_to_file (value, options.file)
1279 if (self.options.raw is None) and (options.file is None):
1283 @register_command("slice_hrn","")
1284 def status(self, options, args):
1286 retrieve the status of the slivers belonging to tne named slice (Status)
1288 server = self.sliceapi()
1292 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1295 slice_cred = self.slice_credential(slice_hrn)
1296 creds = [slice_cred]
1298 # options and call_id when supported
1300 api_options['call_id']=unique_call_id()
1301 if options.show_credential:
1302 show_credentials(creds)
1303 result = server.Status([slice_urn], creds, *self.ois(server,api_options))
1304 value = ReturnValue.get_value(result)
1305 if self.options.raw:
1306 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1309 # Thierry: seemed to be missing
1312 @register_command("slice_hrn action","")
1313 def action(self, options, args):
1315 Perform the named operational action on these slivers
1317 server = self.sliceapi()
1322 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1324 slice_cred = self.slice_credential(args[0])
1325 creds = [slice_cred]
1326 if options.delegate:
1327 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1328 creds.append(delegated_cred)
1330 result = server.PerformOperationalAction([slice_urn], creds, action , api_options)
1331 value = ReturnValue.get_value(result)
1332 if self.options.raw:
1333 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1338 @register_command("slice_hrn time","")
1339 def renew(self, options, args):
1341 renew slice (RenewSliver)
1343 server = self.sliceapi()
1347 [ slice_hrn, input_time ] = args
1349 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1350 # time: don't try to be smart on the time format, server-side will
1352 slice_cred = self.slice_credential(args[0])
1353 creds = [slice_cred]
1354 # options and call_id when supported
1356 api_options['call_id']=unique_call_id()
1357 if options.show_credential:
1358 show_credentials(creds)
1359 result = server.Renew([slice_urn], creds, input_time, *self.ois(server,api_options))
1360 value = ReturnValue.get_value(result)
1361 if self.options.raw:
1362 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1368 @register_command("slice_hrn","")
1369 def shutdown(self, options, args):
1371 shutdown named slice (Shutdown)
1373 server = self.sliceapi()
1376 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1378 slice_cred = self.slice_credential(slice_hrn)
1379 creds = [slice_cred]
1380 result = server.Shutdown(slice_urn, creds)
1381 value = ReturnValue.get_value(result)
1382 if self.options.raw:
1383 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1389 @register_command("[name]","")
1390 def gid(self, options, args):
1392 Create a GID (CreateGid)
1397 target_hrn = args[0]
1398 my_gid_string = open(self.client_bootstrap.my_gid()).read()
1399 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, my_gid_string)
1401 filename = options.file
1403 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1404 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1405 GID(string=gid).save_to_file(filename)
1407 ####################
1408 @register_command("to_hrn","""$ sfi delegate -u -p -s ple.inria.heartbeat -s ple.inria.omftest ple.upmc.slicebrowser
1410 will locally create a set of delegated credentials for the benefit of ple.upmc.slicebrowser
1411 the set of credentials in the scope for this call would be
1412 (*) ple.inria.thierry_parmentelat.user_for_ple.upmc.slicebrowser.user.cred
1414 (*) ple.inria.pi_for_ple.upmc.slicebrowser.user.cred
1416 (*) ple.inria.heartbeat.slice_for_ple.upmc.slicebrowser.user.cred
1417 (*) ple.inria.omftest.slice_for_ple.upmc.slicebrowser.user.cred
1418 because of the two -s options
1421 def delegate (self, options, args):
1423 (locally) create delegate credential for use by given hrn
1424 make sure to check for 'sfi myslice' instead if you plan
1431 # support for several delegations in the same call
1432 # so first we gather the things to do
1434 for slice_hrn in options.delegate_slices:
1435 message="%s.slice"%slice_hrn
1436 original = self.slice_credential_string(slice_hrn)
1437 tuples.append ( (message, original,) )
1438 if options.delegate_pi:
1439 my_authority=self.authority
1440 message="%s.pi"%my_authority
1441 original = self.my_authority_credential_string()
1442 tuples.append ( (message, original,) )
1443 for auth_hrn in options.delegate_auths:
1444 message="%s.auth"%auth_hrn
1445 original=self.authority_credential_string(auth_hrn)
1446 tuples.append ( (message, original, ) )
1447 # if nothing was specified at all at this point, let's assume -u
1448 if not tuples: options.delegate_user=True
1450 if options.delegate_user:
1451 message="%s.user"%self.user
1452 original = self.my_credential_string
1453 tuples.append ( (message, original, ) )
1455 # default type for beneficial is user unless -A
1456 if options.delegate_to_authority: to_type='authority'
1457 else: to_type='user'
1459 # let's now handle all this
1460 # it's all in the filenaming scheme
1461 for (message,original) in tuples:
1462 delegated_string = self.client_bootstrap.delegate_credential_string(original, to_hrn, to_type)
1463 delegated_credential = Credential (string=delegated_string)
1464 filename = os.path.join ( self.options.sfi_dir,
1465 "%s_for_%s.%s.cred"%(message,to_hrn,to_type))
1466 delegated_credential.save_to_file(filename, save_parents=True)
1467 self.logger.info("delegated credential for %s to %s and wrote to %s"%(message,to_hrn,filename))
1469 ####################
1470 @register_command("","""$ less +/myslice sfi_config
1472 backend = http://manifold.pl.sophia.inria.fr:7080
1473 # the HRN that myslice uses, so that we are delegating to
1474 delegate = ple.upmc.slicebrowser
1475 # platform - this is a myslice concept
1477 # username - as of this writing (May 2013) a simple login name
1481 will first collect the slices that you are part of, then make sure
1482 all your credentials are up-to-date (read: refresh expired ones)
1483 then compute delegated credentials for user 'ple.upmc.slicebrowser'
1484 and upload them all on myslice backend, using 'platform' and 'user'.
1485 A password will be prompted for the upload part.
1487 $ sfi -v myslice -- or sfi -vv myslice
1488 same but with more and more verbosity
1491 is synonym to sfi myslice as no other command starts with an 'm'
1493 ) # register_command
1494 def myslice (self, options, args):
1496 """ This helper is for refreshing your credentials at myslice; it will
1497 * compute all the slices that you currently have credentials on
1498 * refresh all your credentials (you as a user and pi, your slices)
1499 * upload them to the manifold backend server
1500 for last phase, sfi_config is read to look for the [myslice] section,
1501 and namely the 'backend', 'delegate' and 'user' settings"""
1508 ### the rough sketch goes like this
1509 # (a) rain check for sufficient config in sfi_config
1510 # we don't allow to override these settings for now
1512 myslice_keys=['backend', 'delegate', 'platform', 'username']
1513 for key in myslice_keys:
1514 full_key="MYSLICE_" + key.upper()
1515 value=getattr(self.config_instance,full_key,None)
1516 if value: myslice_dict[key]=value
1517 else: print "Unsufficient config, missing key %s in [myslice] section of sfi_config"%key
1518 if len(myslice_dict) != len(myslice_keys):
1521 # (b) figure whether we are PI for the authority where we belong
1522 sfi_logger.info("Resolving our own id")
1523 my_records=self.registry().Resolve(self.user,self.my_credential_string)
1524 if len(my_records)!=1: print "Cannot Resolve %s -- exiting"%self.user; sys.exit(1)
1525 my_record=my_records[0]
1526 sfi_logger.info("Checking for authorities that we are PI for")
1527 my_auths = my_record['reg-pi-authorities']
1528 sfi_logger.debug("Found %d authorities: %s"%(len(my_auths),my_auths))
1530 # (c) get the set of slices that we are in
1531 sfi_logger.info("Checking for slices that we are member of")
1532 my_slices=my_record['reg-slices']
1533 sfi_logger.debug("Found %d slices: %s"%(len(my_slices),my_slices))
1535 # (d) make sure we have *valid* credentials for all these
1537 hrn_credentials.append ( (self.user, 'user', self.my_credential_string,) )
1538 for auth_hrn in my_auths:
1539 hrn_credentials.append ( (auth_hrn, 'auth', self.authority_credential_string(auth_hrn),) )
1540 for slice_hrn in my_slices:
1541 hrn_credentials.append ( (slice_hrn, 'slice', self.slice_credential_string (slice_hrn),) )
1543 # (e) check for the delegated version of these
1544 # xxx todo add an option -a/-A? like for 'sfi delegate' for when we ever
1545 # switch to myslice using an authority instead of a user
1546 delegatee_type='user'
1547 delegatee_hrn=myslice_dict['delegate']
1548 hrn_delegated_credentials = [
1549 (hrn, htype, self.client_bootstrap.delegate_credential_string (credential, delegatee_hrn, delegatee_type),)
1550 for (hrn, htype, credential) in hrn_credentials ]
1552 # (f) and finally upload them to manifold server
1553 # xxx todo add an option so the password can be set on the command line
1554 # (but *NOT* in the config file) so other apps can leverage this
1555 uploader = ManifoldUploader (logger=sfi_logger,
1556 url=myslice_dict['backend'],
1557 platform=myslice_dict['platform'],
1558 username=myslice_dict['username'])
1559 for (hrn,htype,delegated_credential) in hrn_delegated_credentials:
1560 sfi_logger.info("Uploading delegated credential for %s (%s)"%(hrn,htype))
1561 uploader.upload(delegated_credential,message=hrn)
1562 # at first I thought we would want to save these,
1563 # like 'sfi delegate does' but on second thought
1564 # it is probably not helpful as people would not
1565 # need to run 'sfi delegate' at all anymore
1568 # Thierry: I'm turning this off as a command, no idea what it's used for
1569 # @register_command("cred","")
1570 def trusted(self, options, args):
1572 return the trusted certs at this interface (get_trusted_certs)
1574 trusted_certs = self.registry().get_trusted_certs()
1575 for trusted_cert in trusted_certs:
1576 gid = GID(string=trusted_cert)
1578 cert = Certificate(string=trusted_cert)
1579 self.logger.debug('Sfi.trusted -> %r'%cert.get_subject())