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
49 # utility methods here
50 def optparse_listvalue_callback(option, option_string, value, parser):
51 setattr(parser.values, option.dest, value.split(','))
53 # a code fragment that could be helpful for argparse which unfortunately is
54 # available with 2.7 only, so this feels like too strong a requirement for the client side
55 #class ExtraArgAction (argparse.Action):
56 # def __call__ (self, parser, namespace, values, option_string=None):
57 # would need a try/except of course
58 # (k,v)=values.split('=')
59 # d=getattr(namespace,self.dest)
62 #parser.add_argument ("-X","--extra",dest='extras', default={}, action=ExtraArgAction,
63 # help="set extra flags, testbed dependent, e.g. --extra enabled=true")
65 def optparse_dictvalue_callback (option, option_string, value, parser):
67 (k,v)=value.split('=',1)
68 d=getattr(parser.values, option.dest)
75 def display_rspec(rspec, format='rspec'):
77 tree = etree.parse(StringIO(rspec))
79 result = root.xpath("./network/site/node/hostname/text()")
80 elif format in ['ip']:
81 # The IP address is not yet part of the new RSpec
82 # so this doesn't do anything yet.
83 tree = etree.parse(StringIO(rspec))
85 result = root.xpath("./network/site/node/ipv4/text()")
92 def display_list(results):
93 for result in results:
96 def display_records(recordList, dump=False):
97 ''' Print all fields in the record'''
98 for record in recordList:
99 display_record(record, dump)
101 def display_record(record, dump=False):
103 record.dump(sort=True)
105 info = record.getdict()
106 print "%s (%s)" % (info['hrn'], info['type'])
110 def filter_records(type, records):
111 filtered_records = []
112 for record in records:
113 if (record['type'] == type) or (type == "all"):
114 filtered_records.append(record)
115 return filtered_records
118 def credential_printable (cred):
119 credential=Credential(cred=cred)
121 result += credential.get_summary_tostring()
123 rights = credential.get_privileges()
124 result += "type=%s\n" % credential.type
125 result += "version=%s\n" % credential.version
126 result += "rights=%s\n"%rights
129 def show_credentials (cred_s):
130 if not isinstance (cred_s,list): cred_s = [cred_s]
132 print "Using Credential %s"%credential_printable(cred)
135 def save_raw_to_file(var, filename, format="text", banner=None):
137 # if filename is "-", send it to stdout
140 f = open(filename, "w")
145 elif format == "pickled":
146 f.write(pickle.dumps(var))
147 elif format == "json":
148 if hasattr(json, "dumps"):
149 f.write(json.dumps(var)) # python 2.6
151 f.write(json.write(var)) # python 2.5
153 # this should never happen
154 print "unknown output format", format
156 f.write('\n'+banner+"\n")
158 def save_rspec_to_file(rspec, filename):
159 if not filename.endswith(".rspec"):
160 filename = filename + ".rspec"
161 f = open(filename, 'w')
166 def save_records_to_file(filename, record_dicts, format="xml"):
169 for record_dict in record_dicts:
171 save_record_to_file(filename + "." + str(index), record_dict)
173 save_record_to_file(filename, record_dict)
175 elif format == "xmllist":
176 f = open(filename, "w")
177 f.write("<recordlist>\n")
178 for record_dict in record_dicts:
179 record_obj=Record(dict=record_dict)
180 f.write('<record hrn="' + record_obj.hrn + '" type="' + record_obj.type + '" />\n')
181 f.write("</recordlist>\n")
183 elif format == "hrnlist":
184 f = open(filename, "w")
185 for record_dict in record_dicts:
186 record_obj=Record(dict=record_dict)
187 f.write(record_obj.hrn + "\n")
190 # this should never happen
191 print "unknown output format", format
193 def save_record_to_file(filename, record_dict):
194 record = Record(dict=record_dict)
195 xml = record.save_as_xml()
196 f=codecs.open(filename, encoding='utf-8',mode="w")
201 # minimally check a key argument
202 def check_ssh_key (key):
203 good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$'
204 return re.match(good_ssh_key, key, re.IGNORECASE)
207 def load_record_from_opts(options):
209 if hasattr(options, 'xrn') and options.xrn:
210 if hasattr(options, 'type') and options.type:
211 xrn = Xrn(options.xrn, options.type)
213 xrn = Xrn(options.xrn)
214 record_dict['urn'] = xrn.get_urn()
215 record_dict['hrn'] = xrn.get_hrn()
216 record_dict['type'] = xrn.get_type()
217 if hasattr(options, 'key') and options.key:
219 pubkey = open(options.key, 'r').read()
222 if not check_ssh_key (pubkey):
223 raise SfaInvalidArgument(name='key',msg="Could not find file, or wrong key format")
224 record_dict['keys'] = [pubkey]
225 if hasattr(options, 'slices') and options.slices:
226 record_dict['slices'] = options.slices
227 if hasattr(options, 'researchers') and options.researchers:
228 record_dict['researcher'] = options.researchers
229 if hasattr(options, 'email') and options.email:
230 record_dict['email'] = options.email
231 if hasattr(options, 'pis') and options.pis:
232 record_dict['pi'] = options.pis
234 # handle extra settings
235 record_dict.update(options.extras)
237 return Record(dict=record_dict)
239 def load_record_from_file(filename):
240 f=codecs.open(filename, encoding="utf-8", mode="r")
241 xml_string = f.read()
243 return Record(xml=xml_string)
247 def unique_call_id(): return uuid.uuid4().urn
251 # dirty hack to make this class usable from the outside
252 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user', 'user_private_key']
255 def default_sfi_dir ():
256 if os.path.isfile("./sfi_config"):
259 return os.path.expanduser("~/.sfi/")
261 # dummy to meet Sfi's expectations for its 'options' field
262 # i.e. s/t we can do setattr on
266 def __init__ (self,options=None):
267 if options is None: options=Sfi.DummyOptions()
268 for opt in Sfi.required_options:
269 if not hasattr(options,opt): setattr(options,opt,None)
270 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
271 self.options = options
273 self.authority = None
274 self.logger = sfi_logger
275 self.logger.enable_console()
276 self.available_names = [ tuple[0] for tuple in Sfi.available ]
277 self.available_dict = dict (Sfi.available)
279 # tuples command-name expected-args in the order in which they should appear in the help
282 ("list", "authority"),
285 ("update", "record"),
288 ("describe", "slice_hrn"),
289 ("create", "slice_hrn rspec"),
290 ("allocate", "slice_hrn rspec"),
291 ("provision", "slice_hrn"),
292 ("action", "slice_hrn action"),
293 ("delete", "slice_hrn"),
294 ("status", "slice_hrn"),
295 ("renew", "slice_hrn time"),
296 ("shutdown", "slice_hrn"),
297 ("get_ticket", "slice_hrn rspec"),
298 ("redeem_ticket", "ticket"),
299 ("delegate", "name"),
305 def print_command_help (self, options):
306 verbose=getattr(options,'verbose')
307 format3="%18s %-15s %s"
310 print format3%("command","cmd_args","description")
314 self.create_parser().print_help()
315 for command in self.available_names:
316 args=self.available_dict[command]
317 method=getattr(self,command,None)
319 if method: doc=getattr(method,'__doc__',"")
320 if not doc: doc="*** no doc found ***"
321 doc=doc.strip(" \t\n")
322 doc=doc.replace("\n","\n"+35*' ')
325 print format3%(command,args,doc)
327 self.create_command_parser(command).print_help()
329 def create_command_parser(self, command):
330 if command not in self.available_dict:
331 msg="Invalid command\n"
333 msg += ','.join(self.available_names)
334 self.logger.critical(msg)
337 parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \
338 % (command, self.available_dict[command]))
340 if command in ("add", "update"):
341 parser.add_option('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
342 parser.add_option('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
343 parser.add_option('-e', '--email', dest='email', default="", help="email (mandatory for users)")
344 # use --extra instead
345 # parser.add_option('-u', '--url', dest='url', metavar='<url>', default=None, help="URL, useful for slices")
346 # parser.add_option('-d', '--description', dest='description', metavar='<description>',
347 # help='Description, useful for slices', default=None)
348 parser.add_option('-k', '--key', dest='key', metavar='<key>', help='public key string or file',
350 parser.add_option('-s', '--slices', dest='slices', metavar='<slices>', help='slice xrns',
351 default='', type="str", action='callback', callback=optparse_listvalue_callback)
352 parser.add_option('-r', '--researchers', dest='researchers', metavar='<researchers>',
353 help='slice researchers', default='', type="str", action='callback',
354 callback=optparse_listvalue_callback)
355 parser.add_option('-p', '--pis', dest='pis', metavar='<PIs>', help='Principal Investigators/Project Managers',
356 default='', type="str", action='callback', callback=optparse_listvalue_callback)
357 # use --extra instead
358 # parser.add_option('-f', '--firstname', dest='firstname', metavar='<firstname>', help='user first name')
359 # parser.add_option('-l', '--lastname', dest='lastname', metavar='<lastname>', help='user last name')
360 parser.add_option ('-X','--extra',dest='extras',default={},type='str',metavar="<EXTRA_ASSIGNS>",
361 action="callback", callback=optparse_dictvalue_callback, nargs=1,
362 help="set extra/testbed-dependent flags, e.g. --extra enabled=true")
364 # user specifies remote aggregate/sm/component
365 if command in ("resources", "describe", "allocate", "provision", "create", "delete", "allocate", "provision",
366 "action", "shutdown", "get_ticket", "renew", "status"):
367 parser.add_option("-d", "--delegate", dest="delegate", default=None,
369 help="Include a credential delegated to the user's root"+\
370 "authority in set of credentials for this call")
372 # show_credential option
373 if command in ("list","resources", "describe", "provision", "allocate", "create","add","update","remove","slices","delete","status","renew"):
374 parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False,
375 help="show credential(s) used in human-readable form")
376 # registy filter option
377 if command in ("list", "show", "remove"):
378 parser.add_option("-t", "--type", dest="type", type="choice",
379 help="type filter ([all]|user|slice|authority|node|aggregate)",
380 choices=("all", "user", "slice", "authority", "node", "aggregate"),
382 if command in ("show"):
383 parser.add_option("-k","--key",dest="keys",action="append",default=[],
384 help="specify specific keys to be displayed from record")
385 if command in ("resources", "describe"):
387 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
388 help="schema type and version of resulting RSpec")
389 # disable/enable cached rspecs
390 parser.add_option("-c", "--current", dest="current", default=False,
392 help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
394 parser.add_option("-f", "--format", dest="format", type="choice",
395 help="display format ([xml]|dns|ip)", default="xml",
396 choices=("xml", "dns", "ip"))
397 #panos: a new option to define the type of information about resources a user is interested in
398 parser.add_option("-i", "--info", dest="info",
399 help="optional component information", default=None)
400 # a new option to retreive or not reservation-oriented RSpecs (leases)
401 parser.add_option("-l", "--list_leases", dest="list_leases", type="choice",
402 help="Retreive or not reservation-oriented RSpecs ([resources]|leases|all )",
403 choices=("all", "resources", "leases"), default="resources")
406 # 'create' does return the new rspec, makes sense to save that too
407 if command in ("resources", "describe", "allocate", "provision", "show", "list", "gid", 'create'):
408 parser.add_option("-o", "--output", dest="file",
409 help="output XML to file", metavar="FILE", default=None)
411 if command in ("show", "list"):
412 parser.add_option("-f", "--format", dest="format", type="choice",
413 help="display format ([text]|xml)", default="text",
414 choices=("text", "xml"))
416 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
417 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
418 choices=("xml", "xmllist", "hrnlist"))
419 if command == 'list':
420 parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
421 help="list all child records", default=False)
422 if command in ("delegate"):
423 parser.add_option("-u", "--user",
424 action="store_true", dest="delegate_user", default=False,
425 help="delegate user credential")
426 parser.add_option("-s", "--slice", dest="delegate_slice",
427 help="delegate slice credential", metavar="HRN", default=None)
429 if command in ("version"):
430 parser.add_option("-R","--registry-version",
431 action="store_true", dest="version_registry", default=False,
432 help="probe registry version instead of sliceapi")
433 parser.add_option("-l","--local",
434 action="store_true", dest="version_local", default=False,
435 help="display version of the local client")
440 def create_parser(self):
442 # Generate command line parser
443 parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
444 description="Commands: %s"%(" ".join(self.available_names)))
445 parser.add_option("-r", "--registry", dest="registry",
446 help="root registry", metavar="URL", default=None)
447 parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
448 help="slice API - in general a SM URL, but can be used to talk to an aggregate")
449 parser.add_option("-R", "--raw", dest="raw", default=None,
450 help="Save raw, unparsed server response to a file")
451 parser.add_option("", "--rawformat", dest="rawformat", type="choice",
452 help="raw file format ([text]|pickled|json)", default="text",
453 choices=("text","pickled","json"))
454 parser.add_option("", "--rawbanner", dest="rawbanner", default=None,
455 help="text string to write before and after raw output")
456 parser.add_option("-d", "--dir", dest="sfi_dir",
457 help="config & working directory - default is %default",
458 metavar="PATH", default=Sfi.default_sfi_dir())
459 parser.add_option("-u", "--user", dest="user",
460 help="user name", metavar="HRN", default=None)
461 parser.add_option("-a", "--auth", dest="auth",
462 help="authority name", metavar="HRN", default=None)
463 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
464 help="verbose mode - cumulative")
465 parser.add_option("-D", "--debug",
466 action="store_true", dest="debug", default=False,
467 help="Debug (xml-rpc) protocol messages")
468 # would it make sense to use ~/.ssh/id_rsa as a default here ?
469 parser.add_option("-k", "--private-key",
470 action="store", dest="user_private_key", default=None,
471 help="point to the private key file to use if not yet installed in sfi_dir")
472 parser.add_option("-t", "--timeout", dest="timeout", default=None,
473 help="Amout of time to wait before timing out the request")
474 parser.add_option("-?", "--commands",
475 action="store_true", dest="command_help", default=False,
476 help="one page summary on commands & exit")
477 parser.disable_interspersed_args()
482 def print_help (self):
483 print "==================== Generic sfi usage"
484 self.sfi_parser.print_help()
485 print "==================== Specific command usage"
486 self.command_parser.print_help()
489 # Main: parse arguments and dispatch to command
491 def dispatch(self, command, command_options, command_args):
492 return getattr(self, command)(command_options, command_args)
495 self.sfi_parser = self.create_parser()
496 (options, args) = self.sfi_parser.parse_args()
497 if options.command_help:
498 self.print_command_help(options)
500 self.options = options
502 self.logger.setLevelFromOptVerbose(self.options.verbose)
505 self.logger.critical("No command given. Use -h for help.")
506 self.print_command_help(options)
509 # complete / find unique match with command set
510 command_candidates = Candidates (self.available_names)
512 command = command_candidates.only_match(input)
514 self.print_command_help(options)
516 # second pass options parsing
517 self.command_parser = self.create_command_parser(command)
518 (command_options, command_args) = self.command_parser.parse_args(args[1:])
519 self.command_options = command_options
523 self.logger.debug("Command=%s" % command)
526 self.dispatch(command, command_options, command_args)
528 self.logger.critical ("Unknown command %s"%command)
534 def read_config(self):
535 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
536 shell_config_file = os.path.join(self.options.sfi_dir,"sfi_config.sh")
538 if Config.is_ini(config_file):
539 config = Config (config_file)
541 # try upgrading from shell config format
542 fp, fn = mkstemp(suffix='sfi_config', text=True)
544 # we need to preload the sections we want parsed
545 # from the shell config
546 config.add_section('sfi')
547 config.add_section('sface')
548 config.load(config_file)
550 shutil.move(config_file, shell_config_file)
552 config.save(config_file)
555 self.logger.critical("Failed to read configuration file %s"%config_file)
556 self.logger.info("Make sure to remove the export clauses and to add quotes")
557 if self.options.verbose==0:
558 self.logger.info("Re-run with -v for more details")
560 self.logger.log_exc("Could not read config file %s"%config_file)
565 if (self.options.sm is not None):
566 self.sm_url = self.options.sm
567 elif hasattr(config, "SFI_SM"):
568 self.sm_url = config.SFI_SM
570 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
574 if (self.options.registry is not None):
575 self.reg_url = self.options.registry
576 elif hasattr(config, "SFI_REGISTRY"):
577 self.reg_url = config.SFI_REGISTRY
579 self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
583 if (self.options.user is not None):
584 self.user = self.options.user
585 elif hasattr(config, "SFI_USER"):
586 self.user = config.SFI_USER
588 self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
592 if (self.options.auth is not None):
593 self.authority = self.options.auth
594 elif hasattr(config, "SFI_AUTH"):
595 self.authority = config.SFI_AUTH
597 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
600 self.config_file=config_file
604 def show_config (self):
605 print "From configuration file %s"%self.config_file
608 ('SFI_AUTH','authority'),
610 ('SFI_REGISTRY','reg_url'),
612 for (external_name, internal_name) in flags:
613 print "%s='%s'"%(external_name,getattr(self,internal_name))
616 # Get various credential and spec files
618 # Establishes limiting conventions
619 # - conflates MAs and SAs
620 # - assumes last token in slice name is unique
622 # Bootstraps credentials
623 # - bootstrap user credential from self-signed certificate
624 # - bootstrap authority credential from user credential
625 # - bootstrap slice credential from user credential
628 # init self-signed cert, user credentials and gid
629 def bootstrap (self):
630 client_bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir,
632 # if -k is provided, use this to initialize private key
633 if self.options.user_private_key:
634 client_bootstrap.init_private_key_if_missing (self.options.user_private_key)
636 # trigger legacy compat code if needed
637 # the name has changed from just <leaf>.pkey to <hrn>.pkey
638 if not os.path.isfile(client_bootstrap.private_key_filename()):
639 self.logger.info ("private key not found, trying legacy name")
641 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%Xrn.unescape(get_leaf(self.user)))
642 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
643 client_bootstrap.init_private_key_if_missing (legacy_private_key)
644 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
646 self.logger.log_exc("Can't find private key ")
650 client_bootstrap.bootstrap_my_gid()
651 # extract what's needed
652 self.private_key = client_bootstrap.private_key()
653 self.my_credential_string = client_bootstrap.my_credential_string ()
654 self.my_credential = {'geni_type': 'geni_sfa',
655 'geni_version': '3.0',
656 'geni_value': self.my_credential_string}
657 self.my_gid = client_bootstrap.my_gid ()
658 self.client_bootstrap = client_bootstrap
661 def my_authority_credential_string(self):
662 if not self.authority:
663 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
665 return self.client_bootstrap.authority_credential_string (self.authority)
667 def slice_credential_string(self, name):
668 return self.client_bootstrap.slice_credential_string (name)
670 def slice_credential(self, name):
671 return {'geni_type': 'geni_sfa',
672 'geni_version': '3.0',
673 'geni_value': self.slice_credential_string(name)}
675 # xxx should be supported by sfaclientbootstrap as well
676 def delegate_cred(self, object_cred, hrn, type='authority'):
677 # the gid and hrn of the object we are delegating
678 if isinstance(object_cred, str):
679 object_cred = Credential(string=object_cred)
680 object_gid = object_cred.get_gid_object()
681 object_hrn = object_gid.get_hrn()
683 if not object_cred.get_privileges().get_all_delegate():
684 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
687 # the delegating user's gid
688 caller_gidfile = self.my_gid()
690 # the gid of the user who will be delegated to
691 delegee_gid = self.client_bootstrap.gid(hrn,type)
692 delegee_hrn = delegee_gid.get_hrn()
693 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
694 return dcred.save_to_string(save_parents=True)
697 # Management of the servers
702 if not hasattr (self, 'registry_proxy'):
703 self.logger.info("Contacting Registry at: %s"%self.reg_url)
704 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
705 timeout=self.options.timeout, verbose=self.options.debug)
706 return self.registry_proxy
710 if not hasattr (self, 'sliceapi_proxy'):
711 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
712 if hasattr(self.command_options,'component') and self.command_options.component:
713 # resolve the hrn at the registry
714 node_hrn = self.command_options.component
715 records = self.registry().Resolve(node_hrn, self.my_credential_string)
716 records = filter_records('node', records)
718 self.logger.warning("No such component:%r"% opts.component)
720 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
721 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
723 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
724 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
725 self.sm_url = 'http://' + self.sm_url
726 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
727 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
728 timeout=self.options.timeout, verbose=self.options.debug)
729 return self.sliceapi_proxy
731 def get_cached_server_version(self, server):
732 # check local cache first
735 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
736 cache_key = server.url + "-version"
738 cache = Cache(cache_file)
741 self.logger.info("Local cache not found at: %s" % cache_file)
744 version = cache.get(cache_key)
747 result = server.GetVersion()
748 version= ReturnValue.get_value(result)
749 # cache version for 20 minutes
750 cache.add(cache_key, version, ttl= 60*20)
751 self.logger.info("Updating cache file %s" % cache_file)
752 cache.save_to_file(cache_file)
756 ### resurrect this temporarily so we can support V1 aggregates for a while
757 def server_supports_options_arg(self, server):
759 Returns true if server support the optional call_id arg, false otherwise.
761 server_version = self.get_cached_server_version(server)
763 # xxx need to rewrite this
764 if int(server_version.get('geni_api')) >= 2:
768 def server_supports_call_id_arg(self, server):
769 server_version = self.get_cached_server_version(server)
771 if 'sfa' in server_version and 'code_tag' in server_version:
772 code_tag = server_version['code_tag']
773 code_tag_parts = code_tag.split("-")
774 version_parts = code_tag_parts[0].split(".")
775 major, minor = version_parts[0], version_parts[1]
776 rev = code_tag_parts[1]
777 if int(major) == 1 and minor == 0 and build >= 22:
781 ### ois = options if supported
782 # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
783 def ois (self, server, option_dict):
784 if self.server_supports_options_arg (server):
786 elif self.server_supports_call_id_arg (server):
787 return [ unique_call_id () ]
791 ### cis = call_id if supported - like ois
792 def cis (self, server):
793 if self.server_supports_call_id_arg (server):
794 return [ unique_call_id ]
798 ######################################## miscell utilities
799 def get_rspec_file(self, rspec):
800 if (os.path.isabs(rspec)):
803 file = os.path.join(self.options.sfi_dir, rspec)
804 if (os.path.isfile(file)):
807 self.logger.critical("No such rspec file %s"%rspec)
810 def get_record_file(self, record):
811 if (os.path.isabs(record)):
814 file = os.path.join(self.options.sfi_dir, record)
815 if (os.path.isfile(file)):
818 self.logger.critical("No such registry record file %s"%record)
822 #==========================================================================
823 # Following functions implement the commands
825 # Registry-related commands
826 #==========================================================================
828 def version(self, options, args):
830 display an SFA server version (GetVersion)
831 or version information about sfi itself
833 if options.version_local:
834 version=version_core()
836 if options.version_registry:
837 server=self.registry()
839 server = self.sliceapi()
840 result = server.GetVersion()
841 version = ReturnValue.get_value(result)
843 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
845 pprinter = PrettyPrinter(indent=4)
846 pprinter.pprint(version)
848 def list(self, options, args):
850 list entries in named authority registry (List)
857 if options.recursive:
858 opts['recursive'] = options.recursive
860 if options.show_credential:
861 show_credentials(self.my_credential_string)
863 list = self.registry().List(hrn, self.my_credential_string, options)
865 raise Exception, "Not enough parameters for the 'list' command"
867 # filter on person, slice, site, node, etc.
868 # THis really should be in the self.filter_records funct def comment...
869 list = filter_records(options.type, list)
871 print "%s (%s)" % (record['hrn'], record['type'])
873 save_records_to_file(options.file, list, options.fileformat)
876 def show(self, options, args):
878 show details about named registry record (Resolve)
884 record_dicts = self.registry().Resolve(hrn, self.my_credential_string)
885 record_dicts = filter_records(options.type, record_dicts)
887 self.logger.error("No record of type %s"% options.type)
889 # user has required to focus on some keys
891 def project (record):
893 for key in options.keys:
894 try: projected[key]=record[key]
897 record_dicts = [ project (record) for record in record_dicts ]
898 records = [ Record(dict=record_dict) for record_dict in record_dicts ]
899 for record in records:
900 if (options.format == "text"): record.dump(sort=True)
901 else: print record.save_as_xml()
903 save_records_to_file(options.file, record_dicts, options.fileformat)
906 def add(self, options, args):
907 "add record into registry from xml file (Register)"
908 auth_cred = self.my_authority_credential_string()
909 if options.show_credential:
910 show_credentials(auth_cred)
913 record_filepath = args[0]
914 rec_file = self.get_record_file(record_filepath)
915 record_dict.update(load_record_from_file(rec_file).todict())
917 record_dict.update(load_record_from_opts(options).todict())
918 # we should have a type by now
919 if 'type' not in record_dict :
922 # this is still planetlab dependent.. as plc will whine without that
923 # also, it's only for adding
924 if record_dict['type'] == 'user':
925 if not 'first_name' in record_dict:
926 record_dict['first_name'] = record_dict['hrn']
927 if 'last_name' not in record_dict:
928 record_dict['last_name'] = record_dict['hrn']
929 return self.registry().Register(record_dict, auth_cred)
931 def update(self, options, args):
932 "update record into registry from xml file (Update)"
935 record_filepath = args[0]
936 rec_file = self.get_record_file(record_filepath)
937 record_dict.update(load_record_from_file(rec_file).todict())
939 record_dict.update(load_record_from_opts(options).todict())
940 # at the very least we need 'type' here
941 if 'type' not in record_dict:
945 # don't translate into an object, as this would possibly distort
946 # user-provided data; e.g. add an 'email' field to Users
947 if record_dict['type'] == "user":
948 if record_dict['hrn'] == self.user:
949 cred = self.my_credential_string
951 cred = self.my_authority_credential_string()
952 elif record_dict['type'] in ["slice"]:
954 cred = self.slice_credential_string(record_dict['hrn'])
955 except ServerException, e:
956 # XXX smbaker -- once we have better error return codes, update this
957 # to do something better than a string compare
958 if "Permission error" in e.args[0]:
959 cred = self.my_authority_credential_string()
962 elif record_dict['type'] in ["authority"]:
963 cred = self.my_authority_credential_string()
964 elif record_dict['type'] == 'node':
965 cred = self.my_authority_credential_string()
967 raise "unknown record type" + record_dict['type']
968 if options.show_credential:
969 show_credentials(cred)
970 return self.registry().Update(record_dict, cred)
972 def remove(self, options, args):
973 "remove registry record by name (Remove)"
974 auth_cred = self.my_authority_credential_string()
982 if options.show_credential:
983 show_credentials(auth_cred)
984 return self.registry().Remove(hrn, auth_cred, type)
986 # ==================================================================
987 # Slice-related commands
988 # ==================================================================
990 def slices(self, options, args):
991 "list instantiated slices (ListSlices) - returns urn's"
992 server = self.sliceapi()
994 creds = [self.my_credential_string]
996 delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority))
997 creds.append(delegated_cred)
998 # options and call_id when supported
1000 api_options['call_id']=unique_call_id()
1001 if options.show_credential:
1002 show_credentials(creds)
1003 result = server.ListSlices(creds, *self.ois(server,api_options))
1004 value = ReturnValue.get_value(result)
1005 if self.options.raw:
1006 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1011 # show rspec for named slice
1012 def resources(self, options, args):
1014 discover available resources
1015 or with an slice hrn, shows currently provisioned resources
1017 server = self.sliceapi()
1020 creds = [self.my_credential]
1021 if options.delegate:
1022 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
1023 if options.show_credential:
1024 show_credentials(creds)
1026 # no need to check if server accepts the options argument since the options has
1027 # been a required argument since v1 API
1029 # always send call_id to v2 servers
1030 api_options ['call_id'] = unique_call_id()
1031 # ask for cached value if available
1032 api_options ['cached'] = True
1034 api_options['info'] = options.info
1035 if options.list_leases:
1036 api_options['list_leases'] = options.list_leases
1038 if options.current == True:
1039 api_options['cached'] = False
1041 api_options['cached'] = True
1042 if options.rspec_version:
1043 version_manager = VersionManager()
1044 server_version = self.get_cached_server_version(server)
1045 if 'sfa' in server_version:
1046 # just request the version the client wants
1047 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1049 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1051 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1052 result = server.ListResources (creds, api_options)
1053 value = ReturnValue.get_value(result)
1054 if self.options.raw:
1055 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1056 if options.file is not None:
1057 save_rspec_to_file(value, options.file)
1058 if (self.options.raw is None) and (options.file is None):
1059 display_rspec(value, options.format)
1063 def describe(self, options, args):
1065 Shows currently provisioned resources.
1067 server = self.sliceapi()
1070 creds = [self.slice_credential(args[0])]
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 api_options = {'call_id': unique_call_id(),
1078 'info': options.info,
1079 'list_leases': options.list_leases,
1080 'geni_rspec_version': {'type': 'geni', 'version': '3.0'},
1082 if options.rspec_version:
1083 version_manager = VersionManager()
1084 server_version = self.get_cached_server_version(server)
1085 if 'sfa' in server_version:
1086 # just request the version the client wants
1087 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1089 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1090 urn = Xrn(args[0], type='slice').get_urn()
1091 result = server.Describe([urn], 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 def create(self, options, args):
1104 create or update named slice with given rspec
1106 server = self.sliceapi()
1108 # xxx do we need to check usage (len(args)) ?
1111 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1114 creds = [self.slice_credential_string(slice_hrn)]
1116 delegated_cred = None
1117 server_version = self.get_cached_server_version(server)
1118 if server_version.get('interface') == 'slicemgr':
1119 # delegate our cred to the slice manager
1120 # do not delegate cred to slicemgr...not working at the moment
1122 #if server_version.get('hrn'):
1123 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1124 #elif server_version.get('urn'):
1125 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1127 if options.show_credential:
1128 show_credentials(creds)
1131 rspec_file = self.get_rspec_file(args[1])
1132 rspec = open(rspec_file).read()
1135 # need to pass along user keys to the aggregate.
1137 # { urn: urn:publicid:IDN+emulab.net+user+alice
1138 # keys: [<ssh key A>, <ssh key B>]
1141 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1142 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1143 slice_record = slice_records[0]
1144 user_hrns = slice_record['researcher']
1145 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1146 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1148 if 'sfa' not in server_version:
1149 users = pg_users_arg(user_records)
1150 rspec = RSpec(rspec)
1151 rspec.filter({'component_manager_id': server_version['urn']})
1152 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1154 users = sfa_users_arg(user_records, slice_record)
1156 # do not append users, keys, or slice tags. Anything
1157 # not contained in this request will be removed from the slice
1160 api_options ['append'] = False
1161 api_options ['call_id'] = unique_call_id()
1162 result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server, api_options))
1163 value = ReturnValue.get_value(result)
1164 if self.options.raw:
1165 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1166 if options.file is not None:
1167 save_rspec_to_file (value, options.file)
1168 if (self.options.raw is None) and (options.file is None):
1173 def delete(self, options, args):
1175 delete named slice (DeleteSliver)
1177 server = self.sliceapi()
1181 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1184 slice_cred = self.slice_credential(slice_hrn)
1185 creds = [slice_cred]
1186 if options.delegate:
1187 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1188 creds.append(delegated_cred)
1190 # options and call_id when supported
1192 api_options ['call_id'] = unique_call_id()
1193 if options.show_credential:
1194 show_credentials(creds)
1195 result = server.Delete([slice_urn], creds, *self.ois(server, api_options ) )
1196 value = ReturnValue.get_value(result)
1197 if self.options.raw:
1198 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1203 def allocate(self, options, args):
1204 server = self.sliceapi()
1205 server_version = self.get_cached_server_version(server)
1207 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1210 creds = [self.slice_credential(slice_hrn)]
1212 delegated_cred = None
1213 if server_version.get('interface') == 'slicemgr':
1214 # delegate our cred to the slice manager
1215 # do not delegate cred to slicemgr...not working at the moment
1217 #if server_version.get('hrn'):
1218 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1219 #elif server_version.get('urn'):
1220 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1222 if options.show_credential:
1223 show_credentials(creds)
1226 rspec_file = self.get_rspec_file(args[1])
1227 rspec = open(rspec_file).read()
1229 # need to pass along user keys to the aggregate.
1231 # { urn: urn:publicid:IDN+emulab.net+user+alice
1232 # keys: [<ssh key A>, <ssh key B>]
1235 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1236 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1237 slice_record = slice_records[0]
1238 user_hrns = slice_record['researcher']
1239 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1240 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1243 api_options ['call_id'] = unique_call_id()
1244 api_options['geni_users'] = users
1245 result = server.Allocate(slice_urn, creds, rspec, api_options)
1246 value = ReturnValue.get_value(result)
1247 if self.options.raw:
1248 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1249 if options.file is not None:
1250 save_rspec_to_file (value, options.file)
1251 if (self.options.raw is None) and (options.file is None):
1257 def provision(self, options, args):
1258 server = self.sliceapi()
1259 server_version = self.get_cached_server_version(server)
1261 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1264 creds = [self.slice_credential(slice_hrn)]
1265 delegated_cred = None
1266 if server_version.get('interface') == 'slicemgr':
1267 # delegate our cred to the slice manager
1268 # do not delegate cred to slicemgr...not working at the moment
1270 #if server_version.get('hrn'):
1271 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1272 #elif server_version.get('urn'):
1273 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1275 if options.show_credential:
1276 show_credentials(creds)
1279 api_options ['call_id'] = unique_call_id()
1281 # set the requtested rspec version
1282 version_manager = VersionManager()
1283 rspec_version = version_manager._get_version('geni', '3.0').to_dict()
1284 api_options['geni_rspec_version'] = rspec_version
1285 result = server.Provision([slice_urn], creds, api_options)
1286 value = ReturnValue.get_value(result)
1287 if self.options.raw:
1288 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1289 if options.file is not None:
1290 save_rspec_to_file (value, options.file)
1291 if (self.options.raw is None) and (options.file is None):
1295 def status(self, options, args):
1297 retrieve slice status (SliverStatus)
1299 server = self.sliceapi()
1303 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1306 slice_cred = self.slice_credential(slice_hrn)
1307 creds = [slice_cred]
1308 if options.delegate:
1309 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1310 creds.append(delegated_cred)
1312 # options and call_id when supported
1314 api_options['call_id']=unique_call_id()
1315 if options.show_credential:
1316 show_credentials(creds)
1317 result = server.Status([slice_urn], creds, *self.ois(server,api_options))
1318 value = ReturnValue.get_value(result)
1319 if self.options.raw:
1320 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1324 def start(self, options, args):
1326 start named slice (Start)
1328 server = self.sliceapi()
1332 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1335 slice_cred = self.slice_credential_string(args[0])
1336 creds = [slice_cred]
1337 if options.delegate:
1338 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1339 creds.append(delegated_cred)
1340 # xxx Thierry - does this not need an api_options as well ?
1341 result = server.Start(slice_urn, creds)
1342 value = ReturnValue.get_value(result)
1343 if self.options.raw:
1344 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1349 def stop(self, options, args):
1351 stop named slice (Stop)
1353 server = self.sliceapi()
1356 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1358 slice_cred = self.slice_credential_string(args[0])
1359 creds = [slice_cred]
1360 if options.delegate:
1361 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1362 creds.append(delegated_cred)
1363 result = server.Stop(slice_urn, creds)
1364 value = ReturnValue.get_value(result)
1365 if self.options.raw:
1366 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1372 def action(self, options, args):
1374 Perform the named operational action on the named slivers
1376 server = self.sliceapi()
1381 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1383 slice_cred = self.slice_credential(args[0])
1384 creds = [slice_cred]
1385 if options.delegate:
1386 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1387 creds.append(delegated_cred)
1389 result = server.PerformOperationalAction([slice_urn], creds, action , api_options)
1390 value = ReturnValue.get_value(result)
1391 if self.options.raw:
1392 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1397 def renew(self, options, args):
1399 renew slice (RenewSliver)
1401 server = self.sliceapi()
1405 [ slice_hrn, input_time ] = args
1407 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1408 # time: don't try to be smart on the time format, server-side will
1410 slice_cred = self.slice_credential(args[0])
1411 creds = [slice_cred]
1412 if options.delegate:
1413 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1414 creds.append(delegated_cred)
1415 # options and call_id when supported
1417 api_options['call_id']=unique_call_id()
1418 if options.show_credential:
1419 show_credentials(creds)
1420 result = server.Renew([slice_urn], creds, input_time, *self.ois(server,api_options))
1421 value = ReturnValue.get_value(result)
1422 if self.options.raw:
1423 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1429 def shutdown(self, options, args):
1431 shutdown named slice (Shutdown)
1433 server = self.sliceapi()
1436 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1438 slice_cred = self.slice_credential(slice_hrn)
1439 creds = [slice_cred]
1440 if options.delegate:
1441 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1442 creds.append(delegated_cred)
1443 result = server.Shutdown(slice_urn, creds)
1444 value = ReturnValue.get_value(result)
1445 if self.options.raw:
1446 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1452 def get_ticket(self, options, args):
1454 get a ticket for the specified slice
1456 server = self.sliceapi()
1458 slice_hrn, rspec_path = args[0], args[1]
1459 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1461 slice_cred = self.slice_credential_string(slice_hrn)
1462 creds = [slice_cred]
1463 if options.delegate:
1464 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1465 creds.append(delegated_cred)
1467 rspec_file = self.get_rspec_file(rspec_path)
1468 rspec = open(rspec_file).read()
1469 # options and call_id when supported
1471 api_options['call_id']=unique_call_id()
1472 # get ticket at the server
1473 ticket_string = server.GetTicket(slice_urn, creds, rspec, *self.ois(server,api_options))
1475 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1476 self.logger.info("writing ticket to %s"%file)
1477 ticket = SfaTicket(string=ticket_string)
1478 ticket.save_to_file(filename=file, save_parents=True)
1480 def redeem_ticket(self, options, args):
1482 Connects to nodes in a slice and redeems a ticket
1483 (slice hrn is retrieved from the ticket)
1485 ticket_file = args[0]
1487 # get slice hrn from the ticket
1488 # use this to get the right slice credential
1489 ticket = SfaTicket(filename=ticket_file)
1491 ticket_string = ticket.save_to_string(save_parents=True)
1493 slice_hrn = ticket.gidObject.get_hrn()
1494 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1495 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1496 slice_cred = self.slice_credential_string(slice_hrn)
1498 # get a list of node hostnames from the RSpec
1499 tree = etree.parse(StringIO(ticket.rspec))
1500 root = tree.getroot()
1501 hostnames = root.xpath("./network/site/node/hostname/text()")
1503 # create an xmlrpc connection to the component manager at each of these
1504 # components and gall redeem_ticket
1506 for hostname in hostnames:
1508 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1509 cm_url="http://%s:%s/"%(hostname,CM_PORT)
1510 server = SfaServerProxy(cm_url, self.private_key, self.my_gid)
1511 server = self.server_proxy(hostname, CM_PORT, self.private_key,
1512 timeout=self.options.timeout, verbose=self.options.debug)
1513 server.RedeemTicket(ticket_string, slice_cred)
1514 self.logger.info("Success")
1515 except socket.gaierror:
1516 self.logger.error("redeem_ticket failed on %s: Component Manager not accepting requests"%hostname)
1517 except Exception, e:
1518 self.logger.log_exc(e.message)
1521 def gid(self, options, args):
1523 Create a GID (CreateGid)
1528 target_hrn = args[0]
1529 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.client_bootstrap.my_gid_string())
1531 filename = options.file
1533 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1534 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1535 GID(string=gid).save_to_file(filename)
1538 def delegate(self, options, args):
1540 (locally) create delegate credential for use by given hrn
1542 delegee_hrn = args[0]
1543 if options.delegate_user:
1544 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1545 elif options.delegate_slice:
1546 slice_cred = self.slice_credential_string(options.delegate_slice)
1547 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1549 self.logger.warning("Must specify either --user or --slice <hrn>")
1551 delegated_cred = Credential(string=cred)
1552 object_hrn = delegated_cred.get_gid_object().get_hrn()
1553 if options.delegate_user:
1554 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1555 + get_leaf(object_hrn) + ".cred")
1556 elif options.delegate_slice:
1557 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1558 + get_leaf(object_hrn) + ".cred")
1560 delegated_cred.save_to_file(dest_fn, save_parents=True)
1562 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1564 def trusted(self, options, args):
1566 return uhe trusted certs at this interface (get_trusted_certs)
1568 trusted_certs = self.registry().get_trusted_certs()
1569 for trusted_cert in trusted_certs:
1570 gid = GID(string=trusted_cert)
1572 cert = Certificate(string=trusted_cert)
1573 self.logger.debug('Sfi.trusted -> %r'%cert.get_subject())
1576 def config (self, options, args):
1577 "Display contents of current config"