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 api_options ['call_id'] = unique_call_id()
1230 result = server.Allocate(slice_urn, creds, rspec, api_options)
1231 value = ReturnValue.get_value(result)
1232 if self.options.raw:
1233 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1234 if options.file is not None:
1235 save_rspec_to_file (value, options.file)
1236 if (self.options.raw is None) and (options.file is None):
1242 def provision(self, options, args):
1243 server = self.sliceapi()
1244 server_version = self.get_cached_server_version(server)
1246 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1249 creds = [self.slice_credential(slice_hrn)]
1250 delegated_cred = None
1251 if server_version.get('interface') == 'slicemgr':
1252 # delegate our cred to the slice manager
1253 # do not delegate cred to slicemgr...not working at the moment
1255 #if server_version.get('hrn'):
1256 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1257 #elif server_version.get('urn'):
1258 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1260 if options.show_credential:
1261 show_credentials(creds)
1264 api_options ['call_id'] = unique_call_id()
1266 # set the requtested rspec version
1267 version_manager = VersionManager()
1268 rspec_version = version_manager._get_version('geni', '3.0').to_dict()
1269 api_options['geni_rspec_version'] = rspec_version
1272 # need to pass along user keys to the aggregate.
1274 # { urn: urn:publicid:IDN+emulab.net+user+alice
1275 # keys: [<ssh key A>, <ssh key B>]
1278 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1279 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1280 slice_record = slice_records[0]
1281 user_hrns = slice_record['researcher']
1282 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1283 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1284 users = pg_users_arg(user_records)
1286 api_options['geni_users'] = users
1287 result = server.Provision([slice_urn], creds, api_options)
1288 value = ReturnValue.get_value(result)
1289 if self.options.raw:
1290 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1291 if options.file is not None:
1292 save_rspec_to_file (value, options.file)
1293 if (self.options.raw is None) and (options.file is None):
1297 def status(self, options, args):
1299 retrieve slice status (SliverStatus)
1301 server = self.sliceapi()
1305 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1308 slice_cred = self.slice_credential(slice_hrn)
1309 creds = [slice_cred]
1310 if options.delegate:
1311 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1312 creds.append(delegated_cred)
1314 # options and call_id when supported
1316 api_options['call_id']=unique_call_id()
1317 if options.show_credential:
1318 show_credentials(creds)
1319 result = server.Status([slice_urn], creds, *self.ois(server,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)
1326 def start(self, options, args):
1328 start named slice (Start)
1330 server = self.sliceapi()
1334 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1337 slice_cred = self.slice_credential_string(args[0])
1338 creds = [slice_cred]
1339 if options.delegate:
1340 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1341 creds.append(delegated_cred)
1342 # xxx Thierry - does this not need an api_options as well ?
1343 result = server.Start(slice_urn, creds)
1344 value = ReturnValue.get_value(result)
1345 if self.options.raw:
1346 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1351 def stop(self, options, args):
1353 stop named slice (Stop)
1355 server = self.sliceapi()
1358 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1360 slice_cred = self.slice_credential_string(args[0])
1361 creds = [slice_cred]
1362 if options.delegate:
1363 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1364 creds.append(delegated_cred)
1365 result = server.Stop(slice_urn, creds)
1366 value = ReturnValue.get_value(result)
1367 if self.options.raw:
1368 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1374 def action(self, options, args):
1376 Perform the named operational action on the named slivers
1378 server = self.sliceapi()
1383 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1385 slice_cred = self.slice_credential(args[0])
1386 creds = [slice_cred]
1387 if options.delegate:
1388 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1389 creds.append(delegated_cred)
1391 result = server.PerformOperationalAction([slice_urn], creds, action , api_options)
1392 value = ReturnValue.get_value(result)
1393 if self.options.raw:
1394 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1399 def renew(self, options, args):
1401 renew slice (RenewSliver)
1403 server = self.sliceapi()
1407 [ slice_hrn, input_time ] = args
1409 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1410 # time: don't try to be smart on the time format, server-side will
1412 slice_cred = self.slice_credential(args[0])
1413 creds = [slice_cred]
1414 if options.delegate:
1415 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1416 creds.append(delegated_cred)
1417 # options and call_id when supported
1419 api_options['call_id']=unique_call_id()
1420 if options.show_credential:
1421 show_credentials(creds)
1422 result = server.Renew([slice_urn], creds, input_time, *self.ois(server,api_options))
1423 value = ReturnValue.get_value(result)
1424 if self.options.raw:
1425 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1431 def shutdown(self, options, args):
1433 shutdown named slice (Shutdown)
1435 server = self.sliceapi()
1438 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1440 slice_cred = self.slice_credential(slice_hrn)
1441 creds = [slice_cred]
1442 if options.delegate:
1443 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1444 creds.append(delegated_cred)
1445 result = server.Shutdown(slice_urn, creds)
1446 value = ReturnValue.get_value(result)
1447 if self.options.raw:
1448 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1454 def get_ticket(self, options, args):
1456 get a ticket for the specified slice
1458 server = self.sliceapi()
1460 slice_hrn, rspec_path = args[0], args[1]
1461 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1463 slice_cred = self.slice_credential_string(slice_hrn)
1464 creds = [slice_cred]
1465 if options.delegate:
1466 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1467 creds.append(delegated_cred)
1469 rspec_file = self.get_rspec_file(rspec_path)
1470 rspec = open(rspec_file).read()
1471 # options and call_id when supported
1473 api_options['call_id']=unique_call_id()
1474 # get ticket at the server
1475 ticket_string = server.GetTicket(slice_urn, creds, rspec, *self.ois(server,api_options))
1477 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1478 self.logger.info("writing ticket to %s"%file)
1479 ticket = SfaTicket(string=ticket_string)
1480 ticket.save_to_file(filename=file, save_parents=True)
1482 def redeem_ticket(self, options, args):
1484 Connects to nodes in a slice and redeems a ticket
1485 (slice hrn is retrieved from the ticket)
1487 ticket_file = args[0]
1489 # get slice hrn from the ticket
1490 # use this to get the right slice credential
1491 ticket = SfaTicket(filename=ticket_file)
1493 ticket_string = ticket.save_to_string(save_parents=True)
1495 slice_hrn = ticket.gidObject.get_hrn()
1496 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1497 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1498 slice_cred = self.slice_credential_string(slice_hrn)
1500 # get a list of node hostnames from the RSpec
1501 tree = etree.parse(StringIO(ticket.rspec))
1502 root = tree.getroot()
1503 hostnames = root.xpath("./network/site/node/hostname/text()")
1505 # create an xmlrpc connection to the component manager at each of these
1506 # components and gall redeem_ticket
1508 for hostname in hostnames:
1510 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1511 cm_url="http://%s:%s/"%(hostname,CM_PORT)
1512 server = SfaServerProxy(cm_url, self.private_key, self.my_gid)
1513 server = self.server_proxy(hostname, CM_PORT, self.private_key,
1514 timeout=self.options.timeout, verbose=self.options.debug)
1515 server.RedeemTicket(ticket_string, slice_cred)
1516 self.logger.info("Success")
1517 except socket.gaierror:
1518 self.logger.error("redeem_ticket failed on %s: Component Manager not accepting requests"%hostname)
1519 except Exception, e:
1520 self.logger.log_exc(e.message)
1523 def gid(self, options, args):
1525 Create a GID (CreateGid)
1530 target_hrn = args[0]
1531 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.client_bootstrap.my_gid_string())
1533 filename = options.file
1535 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1536 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1537 GID(string=gid).save_to_file(filename)
1540 def delegate(self, options, args):
1542 (locally) create delegate credential for use by given hrn
1544 delegee_hrn = args[0]
1545 if options.delegate_user:
1546 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1547 elif options.delegate_slice:
1548 slice_cred = self.slice_credential_string(options.delegate_slice)
1549 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1551 self.logger.warning("Must specify either --user or --slice <hrn>")
1553 delegated_cred = Credential(string=cred)
1554 object_hrn = delegated_cred.get_gid_object().get_hrn()
1555 if options.delegate_user:
1556 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1557 + get_leaf(object_hrn) + ".cred")
1558 elif options.delegate_slice:
1559 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1560 + get_leaf(object_hrn) + ".cred")
1562 delegated_cred.save_to_file(dest_fn, save_parents=True)
1564 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1566 def trusted(self, options, args):
1568 return uhe trusted certs at this interface (get_trusted_certs)
1570 trusted_certs = self.registry().get_trusted_certs()
1571 for trusted_cert in trusted_certs:
1572 gid = GID(string=trusted_cert)
1574 cert = Certificate(string=trusted_cert)
1575 self.logger.debug('Sfi.trusted -> %r'%cert.get_subject())
1578 def config (self, options, args):
1579 "Display contents of current config"