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().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_command_parser(self, command):
327 if command not in commands_dict:
328 msg="Invalid command\n"
330 msg += ','.join(commands_list)
331 self.logger.critical(msg)
334 # retrieve args_string
335 (_, args_string, __) = commands_dict[command]
337 parser = OptionParser(add_help_option=False,
338 usage="sfi [sfi_options] %s [cmd_options] %s"
339 % (command, args_string))
340 parser.add_option ("-h","--help",dest='help',action='store_true',default=False,
341 help="Summary of one command usage")
343 if command in ("add", "update"):
344 parser.add_option('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
345 parser.add_option('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
346 parser.add_option('-e', '--email', dest='email', default="", help="email (mandatory for users)")
347 parser.add_option('-k', '--key', dest='key', metavar='<key>', help='public key string or file',
349 parser.add_option('-s', '--slices', dest='slices', metavar='<slices>', help='Set/replace slice xrns',
350 default='', type="str", action='callback', callback=optparse_listvalue_callback)
351 parser.add_option('-r', '--researchers', dest='researchers', metavar='<researchers>',
352 help='Set/replace slice researchers', default='', type="str", action='callback',
353 callback=optparse_listvalue_callback)
354 parser.add_option('-p', '--pis', dest='pis', metavar='<PIs>', help='Set/replace Principal Investigators/Project Managers',
355 default='', type="str", action='callback', callback=optparse_listvalue_callback)
356 parser.add_option ('-X','--extra',dest='extras',default={},type='str',metavar="<EXTRA_ASSIGNS>",
357 action="callback", callback=optparse_dictvalue_callback, nargs=1,
358 help="set extra/testbed-dependent flags, e.g. --extra enabled=true")
360 # user specifies remote aggregate/sm/component
361 if command in ("resources", "describe", "allocate", "provision", "delete", "allocate", "provision",
362 "action", "shutdown", "renew", "status"):
363 parser.add_option("-d", "--delegate", dest="delegate", default=None,
365 help="Include a credential delegated to the user's root"+\
366 "authority in set of credentials for this call")
368 # show_credential option
369 if command in ("list","resources", "describe", "provision", "allocate", "add","update","remove","slices","delete","status","renew"):
370 parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False,
371 help="show credential(s) used in human-readable form")
372 # registy filter option
373 if command in ("list", "show", "remove"):
374 parser.add_option("-t", "--type", dest="type", type="choice",
375 help="type filter ([all]|user|slice|authority|node|aggregate)",
376 choices=("all", "user", "slice", "authority", "node", "aggregate"),
378 if command in ("show"):
379 parser.add_option("-k","--key",dest="keys",action="append",default=[],
380 help="specify specific keys to be displayed from record")
381 if command in ("resources", "describe"):
383 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
384 help="schema type and version of resulting RSpec")
385 # disable/enable cached rspecs
386 parser.add_option("-c", "--current", dest="current", default=False,
388 help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
390 parser.add_option("-f", "--format", dest="format", type="choice",
391 help="display format ([xml]|dns|ip)", default="xml",
392 choices=("xml", "dns", "ip"))
393 #panos: a new option to define the type of information about resources a user is interested in
394 parser.add_option("-i", "--info", dest="info",
395 help="optional component information", default=None)
396 # a new option to retreive or not reservation-oriented RSpecs (leases)
397 parser.add_option("-l", "--list_leases", dest="list_leases", type="choice",
398 help="Retreive or not reservation-oriented RSpecs ([resources]|leases|all )",
399 choices=("all", "resources", "leases"), default="resources")
402 if command in ("resources", "describe", "allocate", "provision", "show", "list", "gid"):
403 parser.add_option("-o", "--output", dest="file",
404 help="output XML to file", metavar="FILE", default=None)
406 if command in ("show", "list"):
407 parser.add_option("-f", "--format", dest="format", type="choice",
408 help="display format ([text]|xml)", default="text",
409 choices=("text", "xml"))
411 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
412 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
413 choices=("xml", "xmllist", "hrnlist"))
414 if command == 'list':
415 parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
416 help="list all child records", default=False)
417 parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
418 help="gives details, like user keys", default=False)
419 if command in ("delegate"):
420 parser.add_option("-u", "--user",
421 action="store_true", dest="delegate_user", default=False,
422 help="delegate your own credentials; default if no other option is provided")
423 parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
424 metavar="slice_hrn", help="delegate cred. for slice HRN")
425 parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
426 metavar='auth_hrn', help="delegate cred for auth HRN")
427 # this primarily is a shorthand for -a my_hrn
428 parser.add_option("-p", "--pi", dest='delegate_pi', default=None, action='store_true',
429 help="delegate your PI credentials, so s.t. like -a your_hrn^")
430 parser.add_option("-A","--to-authority",dest='delegate_to_authority',action='store_true',default=False,
431 help="""by default the mandatory argument is expected to be a user,
432 use this if you mean an authority instead""")
434 if command in ("version"):
435 parser.add_option("-R","--registry-version",
436 action="store_true", dest="version_registry", default=False,
437 help="probe registry version instead of sliceapi")
438 parser.add_option("-l","--local",
439 action="store_true", dest="version_local", default=False,
440 help="display version of the local client")
445 def create_parser(self):
447 # Generate command line parser
448 parser = OptionParser(add_help_option=False,
449 usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
450 description="Commands: %s"%(" ".join(commands_list)))
451 parser.add_option("-r", "--registry", dest="registry",
452 help="root registry", metavar="URL", default=None)
453 parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
454 help="slice API - in general a SM URL, but can be used to talk to an aggregate")
455 parser.add_option("-R", "--raw", dest="raw", default=None,
456 help="Save raw, unparsed server response to a file")
457 parser.add_option("", "--rawformat", dest="rawformat", type="choice",
458 help="raw file format ([text]|pickled|json)", default="text",
459 choices=("text","pickled","json"))
460 parser.add_option("", "--rawbanner", dest="rawbanner", default=None,
461 help="text string to write before and after raw output")
462 parser.add_option("-d", "--dir", dest="sfi_dir",
463 help="config & working directory - default is %default",
464 metavar="PATH", default=Sfi.default_sfi_dir())
465 parser.add_option("-u", "--user", dest="user",
466 help="user name", metavar="HRN", default=None)
467 parser.add_option("-a", "--auth", dest="auth",
468 help="authority name", metavar="HRN", default=None)
469 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
470 help="verbose mode - cumulative")
471 parser.add_option("-D", "--debug",
472 action="store_true", dest="debug", default=False,
473 help="Debug (xml-rpc) protocol messages")
474 # would it make sense to use ~/.ssh/id_rsa as a default here ?
475 parser.add_option("-k", "--private-key",
476 action="store", dest="user_private_key", default=None,
477 help="point to the private key file to use if not yet installed in sfi_dir")
478 parser.add_option("-t", "--timeout", dest="timeout", default=None,
479 help="Amout of time to wait before timing out the request")
480 parser.add_option("-h", "--help",
481 action="store_true", dest="help", default=False,
482 help="one page summary on commands & exit")
483 parser.disable_interspersed_args()
489 # Main: parse arguments and dispatch to command
491 def dispatch(self, command, command_options, command_args):
492 method=getattr(self, command, None)
494 print "Unknown command %s"%command
496 return method(command_options, command_args)
499 self.sfi_parser = self.create_parser()
500 (options, args) = self.sfi_parser.parse_args()
502 self.print_commands_help(options)
504 self.options = options
506 self.logger.setLevelFromOptVerbose(self.options.verbose)
509 self.logger.critical("No command given. Use -h for help.")
510 self.print_commands_help(options)
513 # complete / find unique match with command set
514 command_candidates = Candidates (commands_list)
516 command = command_candidates.only_match(input)
518 self.print_commands_help(options)
520 # second pass options parsing
522 self.command_parser = self.create_command_parser(command)
523 (command_options, command_args) = self.command_parser.parse_args(args[1:])
524 if command_options.help:
527 self.command_options = command_options
531 self.logger.debug("Command=%s" % self.command)
534 self.dispatch(command, command_options, command_args)
538 self.logger.log_exc ("sfi command %s failed"%command)
544 def read_config(self):
545 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
546 shell_config_file = os.path.join(self.options.sfi_dir,"sfi_config.sh")
548 if Config.is_ini(config_file):
549 config = Config (config_file)
551 # try upgrading from shell config format
552 fp, fn = mkstemp(suffix='sfi_config', text=True)
554 # we need to preload the sections we want parsed
555 # from the shell config
556 config.add_section('sfi')
557 # sface users should be able to use this same file to configure their stuff
558 config.add_section('sface')
559 # manifold users should be able to specify the details
560 # of their backend server here for 'sfi myslice'
561 config.add_section('myslice')
562 config.load(config_file)
564 shutil.move(config_file, shell_config_file)
566 config.save(config_file)
569 self.logger.critical("Failed to read configuration file %s"%config_file)
570 self.logger.info("Make sure to remove the export clauses and to add quotes")
571 if self.options.verbose==0:
572 self.logger.info("Re-run with -v for more details")
574 self.logger.log_exc("Could not read config file %s"%config_file)
577 self.config_instance=config
580 if (self.options.sm is not None):
581 self.sm_url = self.options.sm
582 elif hasattr(config, "SFI_SM"):
583 self.sm_url = config.SFI_SM
585 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
589 if (self.options.registry is not None):
590 self.reg_url = self.options.registry
591 elif hasattr(config, "SFI_REGISTRY"):
592 self.reg_url = config.SFI_REGISTRY
594 self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
598 if (self.options.user is not None):
599 self.user = self.options.user
600 elif hasattr(config, "SFI_USER"):
601 self.user = config.SFI_USER
603 self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
607 if (self.options.auth is not None):
608 self.authority = self.options.auth
609 elif hasattr(config, "SFI_AUTH"):
610 self.authority = config.SFI_AUTH
612 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
615 self.config_file=config_file
619 def show_config (self):
620 print "From configuration file %s"%self.config_file
623 ('SFI_AUTH','authority'),
625 ('SFI_REGISTRY','reg_url'),
627 for (external_name, internal_name) in flags:
628 print "%s='%s'"%(external_name,getattr(self,internal_name))
631 # Get various credential and spec files
633 # Establishes limiting conventions
634 # - conflates MAs and SAs
635 # - assumes last token in slice name is unique
637 # Bootstraps credentials
638 # - bootstrap user credential from self-signed certificate
639 # - bootstrap authority credential from user credential
640 # - bootstrap slice credential from user credential
643 # init self-signed cert, user credentials and gid
644 def bootstrap (self):
645 client_bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir,
647 # if -k is provided, use this to initialize private key
648 if self.options.user_private_key:
649 client_bootstrap.init_private_key_if_missing (self.options.user_private_key)
651 # trigger legacy compat code if needed
652 # the name has changed from just <leaf>.pkey to <hrn>.pkey
653 if not os.path.isfile(client_bootstrap.private_key_filename()):
654 self.logger.info ("private key not found, trying legacy name")
656 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%Xrn.unescape(get_leaf(self.user)))
657 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
658 client_bootstrap.init_private_key_if_missing (legacy_private_key)
659 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
661 self.logger.log_exc("Can't find private key ")
665 client_bootstrap.bootstrap_my_gid()
666 # extract what's needed
667 self.private_key = client_bootstrap.private_key()
668 self.my_credential_string = client_bootstrap.my_credential_string ()
669 self.my_credential = {'geni_type': 'geni_sfa',
670 'geni_version': '3.0',
671 'geni_value': self.my_credential_string}
672 self.my_gid = client_bootstrap.my_gid ()
673 self.client_bootstrap = client_bootstrap
676 def my_authority_credential_string(self):
677 if not self.authority:
678 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
680 return self.client_bootstrap.authority_credential_string (self.authority)
682 def authority_credential_string(self, auth_hrn):
683 return self.client_bootstrap.authority_credential_string (auth_hrn)
685 def slice_credential_string(self, name):
686 return self.client_bootstrap.slice_credential_string (name)
688 def slice_credential(self, name):
689 return {'geni_type': 'geni_sfa',
690 'geni_version': '3.0',
691 'geni_value': self.slice_credential_string(name)}
693 # xxx should be supported by sfaclientbootstrap as well
694 def delegate_cred(self, object_cred, hrn, type='authority'):
695 # the gid and hrn of the object we are delegating
696 if isinstance(object_cred, str):
697 object_cred = Credential(string=object_cred)
698 object_gid = object_cred.get_gid_object()
699 object_hrn = object_gid.get_hrn()
701 if not object_cred.get_privileges().get_all_delegate():
702 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
705 # the delegating user's gid
706 caller_gidfile = self.my_gid()
708 # the gid of the user who will be delegated to
709 delegee_gid = self.client_bootstrap.gid(hrn,type)
710 delegee_hrn = delegee_gid.get_hrn()
711 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
712 return dcred.save_to_string(save_parents=True)
715 # Management of the servers
720 if not hasattr (self, 'registry_proxy'):
721 self.logger.info("Contacting Registry at: %s"%self.reg_url)
722 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
723 timeout=self.options.timeout, verbose=self.options.debug)
724 return self.registry_proxy
728 if not hasattr (self, 'sliceapi_proxy'):
729 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
730 if hasattr(self.command_options,'component') and self.command_options.component:
731 # resolve the hrn at the registry
732 node_hrn = self.command_options.component
733 records = self.registry().Resolve(node_hrn, self.my_credential_string)
734 records = filter_records('node', records)
736 self.logger.warning("No such component:%r"% opts.component)
738 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
739 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
741 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
742 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
743 self.sm_url = 'http://' + self.sm_url
744 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
745 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
746 timeout=self.options.timeout, verbose=self.options.debug)
747 return self.sliceapi_proxy
749 def get_cached_server_version(self, server):
750 # check local cache first
753 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
754 cache_key = server.url + "-version"
756 cache = Cache(cache_file)
759 self.logger.info("Local cache not found at: %s" % cache_file)
762 version = cache.get(cache_key)
765 result = server.GetVersion()
766 version= ReturnValue.get_value(result)
767 # cache version for 20 minutes
768 cache.add(cache_key, version, ttl= 60*20)
769 self.logger.info("Updating cache file %s" % cache_file)
770 cache.save_to_file(cache_file)
774 ### resurrect this temporarily so we can support V1 aggregates for a while
775 def server_supports_options_arg(self, server):
777 Returns true if server support the optional call_id arg, false otherwise.
779 server_version = self.get_cached_server_version(server)
781 # xxx need to rewrite this
782 if int(server_version.get('geni_api')) >= 2:
786 def server_supports_call_id_arg(self, server):
787 server_version = self.get_cached_server_version(server)
789 if 'sfa' in server_version and 'code_tag' in server_version:
790 code_tag = server_version['code_tag']
791 code_tag_parts = code_tag.split("-")
792 version_parts = code_tag_parts[0].split(".")
793 major, minor = version_parts[0], version_parts[1]
794 rev = code_tag_parts[1]
795 if int(major) == 1 and minor == 0 and build >= 22:
799 ### ois = options if supported
800 # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
801 def ois (self, server, option_dict):
802 if self.server_supports_options_arg (server):
804 elif self.server_supports_call_id_arg (server):
805 return [ unique_call_id () ]
809 ### cis = call_id if supported - like ois
810 def cis (self, server):
811 if self.server_supports_call_id_arg (server):
812 return [ unique_call_id ]
816 ######################################## miscell utilities
817 def get_rspec_file(self, rspec):
818 if (os.path.isabs(rspec)):
821 file = os.path.join(self.options.sfi_dir, rspec)
822 if (os.path.isfile(file)):
825 self.logger.critical("No such rspec file %s"%rspec)
828 def get_record_file(self, record):
829 if (os.path.isabs(record)):
832 file = os.path.join(self.options.sfi_dir, record)
833 if (os.path.isfile(file)):
836 self.logger.critical("No such registry record file %s"%record)
840 #==========================================================================
841 # Following functions implement the commands
843 # Registry-related commands
844 #==========================================================================
846 @register_command("","")
847 def config (self, options, args):
848 "Display contents of current config"
851 @register_command("","")
852 def version(self, options, args):
854 display an SFA server version (GetVersion)
855 or version information about sfi itself
857 if options.version_local:
858 version=version_core()
860 if options.version_registry:
861 server=self.registry()
863 server = self.sliceapi()
864 result = server.GetVersion()
865 version = ReturnValue.get_value(result)
867 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
869 pprinter = PrettyPrinter(indent=4)
870 pprinter.pprint(version)
872 @register_command("authority","")
873 def list(self, options, args):
875 list entries in named authority registry (List)
882 if options.recursive:
883 opts['recursive'] = options.recursive
885 if options.show_credential:
886 show_credentials(self.my_credential_string)
888 list = self.registry().List(hrn, self.my_credential_string, options)
890 raise Exception, "Not enough parameters for the 'list' command"
892 # filter on person, slice, site, node, etc.
893 # This really should be in the self.filter_records funct def comment...
894 list = filter_records(options.type, list)
895 terminal_render (list, options)
897 save_records_to_file(options.file, list, options.fileformat)
900 @register_command("name","")
901 def show(self, options, args):
903 show details about named registry record (Resolve)
909 # explicitly require Resolve to run in details mode
910 record_dicts = self.registry().Resolve(hrn, self.my_credential_string, {'details':True})
911 record_dicts = filter_records(options.type, record_dicts)
913 self.logger.error("No record of type %s"% options.type)
915 # user has required to focus on some keys
917 def project (record):
919 for key in options.keys:
920 try: projected[key]=record[key]
923 record_dicts = [ project (record) for record in record_dicts ]
924 records = [ Record(dict=record_dict) for record_dict in record_dicts ]
925 for record in records:
926 if (options.format == "text"): record.dump(sort=True)
927 else: print record.save_as_xml()
929 save_records_to_file(options.file, record_dicts, options.fileformat)
932 @register_command("[xml-filename]","")
933 def add(self, options, args):
934 """add record into registry (Register)
935 from command line options (recommended)
936 old-school method involving an xml file still supported"""
938 auth_cred = self.my_authority_credential_string()
939 if options.show_credential:
940 show_credentials(auth_cred)
947 record_filepath = args[0]
948 rec_file = self.get_record_file(record_filepath)
949 record_dict.update(load_record_from_file(rec_file).todict())
951 print "Cannot load record file %s"%record_filepath
954 record_dict.update(load_record_from_opts(options).todict())
955 # we should have a type by now
956 if 'type' not in record_dict :
959 # this is still planetlab dependent.. as plc will whine without that
960 # also, it's only for adding
961 if record_dict['type'] == 'user':
962 if not 'first_name' in record_dict:
963 record_dict['first_name'] = record_dict['hrn']
964 if 'last_name' not in record_dict:
965 record_dict['last_name'] = record_dict['hrn']
966 return self.registry().Register(record_dict, auth_cred)
968 @register_command("[xml-filename]","")
969 def update(self, options, args):
970 """update record into registry (Update)
971 from command line options (recommended)
972 old-school method involving an xml file still supported"""
975 record_filepath = args[0]
976 rec_file = self.get_record_file(record_filepath)
977 record_dict.update(load_record_from_file(rec_file).todict())
979 record_dict.update(load_record_from_opts(options).todict())
980 # at the very least we need 'type' here
981 if 'type' not in record_dict:
985 # don't translate into an object, as this would possibly distort
986 # user-provided data; e.g. add an 'email' field to Users
987 if record_dict['type'] == "user":
988 if record_dict['hrn'] == self.user:
989 cred = self.my_credential_string
991 cred = self.my_authority_credential_string()
992 elif record_dict['type'] in ["slice"]:
994 cred = self.slice_credential_string(record_dict['hrn'])
995 except ServerException, e:
996 # XXX smbaker -- once we have better error return codes, update this
997 # to do something better than a string compare
998 if "Permission error" in e.args[0]:
999 cred = self.my_authority_credential_string()
1002 elif record_dict['type'] in ["authority"]:
1003 cred = self.my_authority_credential_string()
1004 elif record_dict['type'] == 'node':
1005 cred = self.my_authority_credential_string()
1007 raise "unknown record type" + record_dict['type']
1008 if options.show_credential:
1009 show_credentials(cred)
1010 return self.registry().Update(record_dict, cred)
1012 @register_command("name","")
1013 def remove(self, options, args):
1014 "remove registry record by name (Remove)"
1015 auth_cred = self.my_authority_credential_string()
1023 if options.show_credential:
1024 show_credentials(auth_cred)
1025 return self.registry().Remove(hrn, auth_cred, type)
1027 # ==================================================================
1028 # Slice-related commands
1029 # ==================================================================
1031 @register_command("","")
1032 def slices(self, options, args):
1033 "list instantiated slices (ListSlices) - returns urn's"
1034 server = self.sliceapi()
1036 creds = [self.my_credential_string]
1037 # options and call_id when supported
1039 api_options['call_id']=unique_call_id()
1040 if options.show_credential:
1041 show_credentials(creds)
1042 result = server.ListSlices(creds, *self.ois(server,api_options))
1043 value = ReturnValue.get_value(result)
1044 if self.options.raw:
1045 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1050 # show rspec for named slice
1051 @register_command("","")
1052 def resources(self, options, args):
1054 discover available resources (ListResources)
1056 server = self.sliceapi()
1059 creds = [self.my_credential]
1060 if options.delegate:
1061 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
1062 if options.show_credential:
1063 show_credentials(creds)
1065 # no need to check if server accepts the options argument since the options has
1066 # been a required argument since v1 API
1068 # always send call_id to v2 servers
1069 api_options ['call_id'] = unique_call_id()
1070 # ask for cached value if available
1071 api_options ['cached'] = True
1073 api_options['info'] = options.info
1074 if options.list_leases:
1075 api_options['list_leases'] = options.list_leases
1077 if options.current == True:
1078 api_options['cached'] = False
1080 api_options['cached'] = True
1081 if options.rspec_version:
1082 version_manager = VersionManager()
1083 server_version = self.get_cached_server_version(server)
1084 if 'sfa' in server_version:
1085 # just request the version the client wants
1086 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1088 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1090 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1091 result = server.ListResources (creds, api_options)
1092 value = ReturnValue.get_value(result)
1093 if self.options.raw:
1094 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1095 if options.file is not None:
1096 save_rspec_to_file(value, options.file)
1097 if (self.options.raw is None) and (options.file is None):
1098 display_rspec(value, options.format)
1102 @register_command("slice_hrn","")
1103 def describe(self, options, args):
1105 shows currently allocated/provisioned resources
1106 of the named slice or set of slivers (Describe)
1108 server = self.sliceapi()
1111 creds = [self.slice_credential(args[0])]
1112 if options.delegate:
1113 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
1114 if options.show_credential:
1115 show_credentials(creds)
1117 api_options = {'call_id': unique_call_id(),
1119 'info': options.info,
1120 'list_leases': options.list_leases,
1121 'geni_rspec_version': {'type': 'geni', 'version': '3.0'},
1123 if options.rspec_version:
1124 version_manager = VersionManager()
1125 server_version = self.get_cached_server_version(server)
1126 if 'sfa' in server_version:
1127 # just request the version the client wants
1128 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1130 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1131 urn = Xrn(args[0], type='slice').get_urn()
1132 result = server.Describe([urn], creds, api_options)
1133 value = ReturnValue.get_value(result)
1134 if self.options.raw:
1135 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1136 if options.file is not None:
1137 save_rspec_to_file(value, options.file)
1138 if (self.options.raw is None) and (options.file is None):
1139 display_rspec(value, options.format)
1143 @register_command("slice_hrn","")
1144 def delete(self, options, args):
1146 de-allocate and de-provision all or named slivers of the slice (Delete)
1148 server = self.sliceapi()
1152 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1155 slice_cred = self.slice_credential(slice_hrn)
1156 creds = [slice_cred]
1158 # options and call_id when supported
1160 api_options ['call_id'] = unique_call_id()
1161 if options.show_credential:
1162 show_credentials(creds)
1163 result = server.Delete([slice_urn], creds, *self.ois(server, api_options ) )
1164 value = ReturnValue.get_value(result)
1165 if self.options.raw:
1166 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1171 @register_command("slice_hrn rspec","")
1172 def allocate(self, options, args):
1174 allocate resources to the named slice (Allocate)
1176 server = self.sliceapi()
1177 server_version = self.get_cached_server_version(server)
1179 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1182 creds = [self.slice_credential(slice_hrn)]
1184 delegated_cred = None
1185 if server_version.get('interface') == 'slicemgr':
1186 # delegate our cred to the slice manager
1187 # do not delegate cred to slicemgr...not working at the moment
1189 #if server_version.get('hrn'):
1190 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1191 #elif server_version.get('urn'):
1192 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1194 if options.show_credential:
1195 show_credentials(creds)
1198 rspec_file = self.get_rspec_file(args[1])
1199 rspec = open(rspec_file).read()
1201 api_options ['call_id'] = unique_call_id()
1202 result = server.Allocate(slice_urn, creds, rspec, api_options)
1203 value = ReturnValue.get_value(result)
1204 if self.options.raw:
1205 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1206 if options.file is not None:
1207 save_rspec_to_file (value, options.file)
1208 if (self.options.raw is None) and (options.file is None):
1213 @register_command("slice_hrn","")
1214 def provision(self, options, args):
1216 provision already allocated resources of named slice (Provision)
1218 server = self.sliceapi()
1219 server_version = self.get_cached_server_version(server)
1221 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1224 creds = [self.slice_credential(slice_hrn)]
1225 delegated_cred = None
1226 if server_version.get('interface') == 'slicemgr':
1227 # delegate our cred to the slice manager
1228 # do not delegate cred to slicemgr...not working at the moment
1230 #if server_version.get('hrn'):
1231 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1232 #elif server_version.get('urn'):
1233 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1235 if options.show_credential:
1236 show_credentials(creds)
1239 api_options ['call_id'] = unique_call_id()
1241 # set the requtested rspec version
1242 version_manager = VersionManager()
1243 rspec_version = version_manager._get_version('geni', '3.0').to_dict()
1244 api_options['geni_rspec_version'] = rspec_version
1247 # need to pass along user keys to the aggregate.
1249 # { urn: urn:publicid:IDN+emulab.net+user+alice
1250 # keys: [<ssh key A>, <ssh key B>]
1253 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1254 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1255 slice_record = slice_records[0]
1256 user_hrns = slice_record['researcher']
1257 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1258 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1259 users = pg_users_arg(user_records)
1261 api_options['geni_users'] = users
1262 result = server.Provision([slice_urn], creds, api_options)
1263 value = ReturnValue.get_value(result)
1264 if self.options.raw:
1265 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1266 if options.file is not None:
1267 save_rspec_to_file (value, options.file)
1268 if (self.options.raw is None) and (options.file is None):
1272 @register_command("slice_hrn","")
1273 def status(self, options, args):
1275 retrieve the status of the slivers belonging to tne named slice (Status)
1277 server = self.sliceapi()
1281 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1284 slice_cred = self.slice_credential(slice_hrn)
1285 creds = [slice_cred]
1287 # options and call_id when supported
1289 api_options['call_id']=unique_call_id()
1290 if options.show_credential:
1291 show_credentials(creds)
1292 result = server.Status([slice_urn], creds, *self.ois(server,api_options))
1293 value = ReturnValue.get_value(result)
1294 if self.options.raw:
1295 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1298 # Thierry: seemed to be missing
1301 @register_command("slice_hrn action","")
1302 def action(self, options, args):
1304 Perform the named operational action on these slivers
1306 server = self.sliceapi()
1311 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1313 slice_cred = self.slice_credential(args[0])
1314 creds = [slice_cred]
1315 if options.delegate:
1316 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1317 creds.append(delegated_cred)
1319 result = server.PerformOperationalAction([slice_urn], creds, action , api_options)
1320 value = ReturnValue.get_value(result)
1321 if self.options.raw:
1322 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1327 @register_command("slice_hrn time","")
1328 def renew(self, options, args):
1330 renew slice (RenewSliver)
1332 server = self.sliceapi()
1336 [ slice_hrn, input_time ] = args
1338 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1339 # time: don't try to be smart on the time format, server-side will
1341 slice_cred = self.slice_credential(args[0])
1342 creds = [slice_cred]
1343 # options and call_id when supported
1345 api_options['call_id']=unique_call_id()
1346 if options.show_credential:
1347 show_credentials(creds)
1348 result = server.Renew([slice_urn], creds, input_time, *self.ois(server,api_options))
1349 value = ReturnValue.get_value(result)
1350 if self.options.raw:
1351 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1357 @register_command("slice_hrn","")
1358 def shutdown(self, options, args):
1360 shutdown named slice (Shutdown)
1362 server = self.sliceapi()
1365 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1367 slice_cred = self.slice_credential(slice_hrn)
1368 creds = [slice_cred]
1369 result = server.Shutdown(slice_urn, creds)
1370 value = ReturnValue.get_value(result)
1371 if self.options.raw:
1372 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1378 @register_command("[name]","")
1379 def gid(self, options, args):
1381 Create a GID (CreateGid)
1386 target_hrn = args[0]
1387 my_gid_string = open(self.client_bootstrap.my_gid()).read()
1388 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, my_gid_string)
1390 filename = options.file
1392 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1393 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1394 GID(string=gid).save_to_file(filename)
1396 ####################
1397 @register_command("to_hrn","""$ sfi delegate -u -p -s ple.inria.heartbeat -s ple.inria.omftest ple.upmc.slicebrowser
1399 will locally create a set of delegated credentials for the benefit of ple.upmc.slicebrowser
1400 the set of credentials in the scope for this call would be
1401 (*) ple.inria.thierry_parmentelat.user_for_ple.upmc.slicebrowser.user.cred
1403 (*) ple.inria.pi_for_ple.upmc.slicebrowser.user.cred
1405 (*) ple.inria.heartbeat.slice_for_ple.upmc.slicebrowser.user.cred
1406 (*) ple.inria.omftest.slice_for_ple.upmc.slicebrowser.user.cred
1407 because of the two -s options
1410 def delegate (self, options, args):
1412 (locally) create delegate credential for use by given hrn
1413 make sure to check for 'sfi myslice' instead if you plan
1420 # support for several delegations in the same call
1421 # so first we gather the things to do
1423 for slice_hrn in options.delegate_slices:
1424 message="%s.slice"%slice_hrn
1425 original = self.slice_credential_string(slice_hrn)
1426 tuples.append ( (message, original,) )
1427 if options.delegate_pi:
1428 my_authority=self.authority
1429 message="%s.pi"%my_authority
1430 original = self.my_authority_credential_string()
1431 tuples.append ( (message, original,) )
1432 for auth_hrn in options.delegate_auths:
1433 message="%s.auth"%auth_hrn
1434 original=self.authority_credential_string(auth_hrn)
1435 tuples.append ( (message, original, ) )
1436 # if nothing was specified at all at this point, let's assume -u
1437 if not tuples: options.delegate_user=True
1439 if options.delegate_user:
1440 message="%s.user"%self.user
1441 original = self.my_credential_string
1442 tuples.append ( (message, original, ) )
1444 # default type for beneficial is user unless -A
1445 if options.delegate_to_authority: to_type='authority'
1446 else: to_type='user'
1448 # let's now handle all this
1449 # it's all in the filenaming scheme
1450 for (message,original) in tuples:
1451 delegated_string = self.client_bootstrap.delegate_credential_string(original, to_hrn, to_type)
1452 delegated_credential = Credential (string=delegated_string)
1453 filename = os.path.join ( self.options.sfi_dir,
1454 "%s_for_%s.%s.cred"%(message,to_hrn,to_type))
1455 delegated_credential.save_to_file(filename, save_parents=True)
1456 self.logger.info("delegated credential for %s to %s and wrote to %s"%(message,to_hrn,filename))
1458 ####################
1459 @register_command("","""$ less +/myslice sfi_config
1461 backend = http://manifold.pl.sophia.inria.fr:7080
1462 # the HRN that myslice uses, so that we are delegating to
1463 delegate = ple.upmc.slicebrowser
1464 # platform - this is a myslice concept
1466 # username - as of this writing (May 2013) a simple login name
1470 will first collect the slices that you are part of, then make sure
1471 all your credentials are up-to-date (read: refresh expired ones)
1472 then compute delegated credentials for user 'ple.upmc.slicebrowser'
1473 and upload them all on myslice backend, using 'platform' and 'user'.
1474 A password will be prompted for the upload part.
1476 $ sfi -v myslice -- or sfi -vv myslice
1477 same but with more and more verbosity
1480 is synonym to sfi myslice as no other command starts with an 'm'
1482 ) # register_command
1483 def myslice (self, options, args):
1485 """ This helper is for refreshing your credentials at myslice; it will
1486 * compute all the slices that you currently have credentials on
1487 * refresh all your credentials (you as a user and pi, your slices)
1488 * upload them to the manifold backend server
1489 for last phase, sfi_config is read to look for the [myslice] section,
1490 and namely the 'backend', 'delegate' and 'user' settings"""
1497 ### the rough sketch goes like this
1498 # (a) rain check for sufficient config in sfi_config
1499 # we don't allow to override these settings for now
1501 myslice_keys=['backend', 'delegate', 'platform', 'username']
1502 for key in myslice_keys:
1503 full_key="MYSLICE_" + key.upper()
1504 value=getattr(self.config_instance,full_key,None)
1505 if value: myslice_dict[key]=value
1506 else: print "Unsufficient config, missing key %s in [myslice] section of sfi_config"%key
1507 if len(myslice_dict) != len(myslice_keys):
1510 # (b) figure whether we are PI for the authority where we belong
1511 sfi_logger.info("Resolving our own id")
1512 my_records=self.registry().Resolve(self.user,self.my_credential_string)
1513 if len(my_records)!=1: print "Cannot Resolve %s -- exiting"%self.user; sys.exit(1)
1514 my_record=my_records[0]
1515 sfi_logger.info("Checking for authorities that we are PI for")
1516 my_auths = my_record['reg-pi-authorities']
1517 sfi_logger.debug("Found %d authorities: %s"%(len(my_auths),my_auths))
1519 # (c) get the set of slices that we are in
1520 sfi_logger.info("Checking for slices that we are member of")
1521 my_slices=my_record['reg-slices']
1522 sfi_logger.debug("Found %d slices: %s"%(len(my_slices),my_slices))
1524 # (d) make sure we have *valid* credentials for all these
1526 hrn_credentials.append ( (self.user, 'user', self.my_credential_string,) )
1527 for auth_hrn in my_auths:
1528 hrn_credentials.append ( (auth_hrn, 'auth', self.authority_credential_string(auth_hrn),) )
1529 for slice_hrn in my_slices:
1530 hrn_credentials.append ( (slice_hrn, 'slice', self.slice_credential_string (slice_hrn),) )
1532 # (e) check for the delegated version of these
1533 # xxx todo add an option -a/-A? like for 'sfi delegate' for when we ever
1534 # switch to myslice using an authority instead of a user
1535 delegatee_type='user'
1536 delegatee_hrn=myslice_dict['delegate']
1537 hrn_delegated_credentials = [
1538 (hrn, htype, self.client_bootstrap.delegate_credential_string (credential, delegatee_hrn, delegatee_type),)
1539 for (hrn, htype, credential) in hrn_credentials ]
1541 # (f) and finally upload them to manifold server
1542 # xxx todo add an option so the password can be set on the command line
1543 # (but *NOT* in the config file) so other apps can leverage this
1544 uploader = ManifoldUploader (logger=sfi_logger,
1545 url=myslice_dict['backend'],
1546 platform=myslice_dict['platform'],
1547 username=myslice_dict['username'])
1548 for (hrn,htype,delegated_credential) in hrn_delegated_credentials:
1549 sfi_logger.info("Uploading delegated credential for %s (%s)"%(hrn,htype))
1550 uploader.upload(delegated_credential,message=hrn)
1551 # at first I thought we would want to save these,
1552 # like 'sfi delegate does' but on second thought
1553 # it is probably not helpful as people would not
1554 # need to run 'sfi delegate' at all anymore
1557 # Thierry: I'm turning this off as a command, no idea what it's used for
1558 # @register_command("cred","")
1559 def trusted(self, options, args):
1561 return the trusted certs at this interface (get_trusted_certs)
1563 trusted_certs = self.registry().get_trusted_certs()
1564 for trusted_cert in trusted_certs:
1565 gid = GID(string=trusted_cert)
1567 cert = Certificate(string=trusted_cert)
1568 self.logger.debug('Sfi.trusted -> %r'%cert.get_subject())