2 # sfi.py - basic SFA command-line client
3 # this module is also used in sfascan
17 from lxml import etree
18 from StringIO import StringIO
19 from optparse import OptionParser
20 from pprint import PrettyPrinter
21 from tempfile import mkstemp
23 from sfa.trust.certificate import Keypair, Certificate
24 from sfa.trust.gid import GID
25 from sfa.trust.credential import Credential
26 from sfa.trust.sfaticket import SfaTicket
28 from sfa.util.faults import SfaInvalidArgument
29 from sfa.util.sfalogging import sfi_logger
30 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn, Xrn
31 from sfa.util.config import Config
32 from sfa.util.version import version_core
33 from sfa.util.cache import Cache
35 from sfa.storage.record import Record
37 from sfa.rspecs.rspec import RSpec
38 from sfa.rspecs.rspec_converter import RSpecConverter
39 from sfa.rspecs.version_manager import VersionManager
41 from sfa.client.sfaclientlib import SfaClientBootstrap
42 from sfa.client.sfaserverproxy import SfaServerProxy, ServerException
43 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
44 from sfa.client.return_value import ReturnValue
45 from sfa.client.candidates import Candidates
46 from sfa.client.manifolduploader import ManifoldUploader
50 from sfa.client.common import optparse_listvalue_callback, optparse_dictvalue_callback, \
51 terminal_render, filter_records
54 def display_rspec(rspec, format='rspec'):
56 tree = etree.parse(StringIO(rspec))
58 result = root.xpath("./network/site/node/hostname/text()")
59 elif format in ['ip']:
60 # The IP address is not yet part of the new RSpec
61 # so this doesn't do anything yet.
62 tree = etree.parse(StringIO(rspec))
64 result = root.xpath("./network/site/node/ipv4/text()")
71 def display_list(results):
72 for result in results:
75 def display_records(recordList, dump=False):
76 ''' Print all fields in the record'''
77 for record in recordList:
78 display_record(record, dump)
80 def display_record(record, dump=False):
82 record.dump(sort=True)
84 info = record.getdict()
85 print "%s (%s)" % (info['hrn'], info['type'])
89 def credential_printable (credential_string):
90 credential=Credential(string=credential_string)
92 result += credential.get_summary_tostring()
94 rights = credential.get_privileges()
95 result += "rights=%s"%rights
99 def show_credentials (cred_s):
100 if not isinstance (cred_s,list): cred_s = [cred_s]
102 print "Using Credential %s"%credential_printable(cred)
105 def save_raw_to_file(var, filename, format="text", banner=None):
107 # if filename is "-", send it to stdout
110 f = open(filename, "w")
115 elif format == "pickled":
116 f.write(pickle.dumps(var))
117 elif format == "json":
118 if hasattr(json, "dumps"):
119 f.write(json.dumps(var)) # python 2.6
121 f.write(json.write(var)) # python 2.5
123 # this should never happen
124 print "unknown output format", format
126 f.write('\n'+banner+"\n")
128 def save_rspec_to_file(rspec, filename):
129 if not filename.endswith(".rspec"):
130 filename = filename + ".rspec"
131 f = open(filename, 'w')
136 def save_records_to_file(filename, record_dicts, format="xml"):
139 for record_dict in record_dicts:
141 save_record_to_file(filename + "." + str(index), record_dict)
143 save_record_to_file(filename, record_dict)
145 elif format == "xmllist":
146 f = open(filename, "w")
147 f.write("<recordlist>\n")
148 for record_dict in record_dicts:
149 record_obj=Record(dict=record_dict)
150 f.write('<record hrn="' + record_obj.hrn + '" type="' + record_obj.type + '" />\n')
151 f.write("</recordlist>\n")
153 elif format == "hrnlist":
154 f = open(filename, "w")
155 for record_dict in record_dicts:
156 record_obj=Record(dict=record_dict)
157 f.write(record_obj.hrn + "\n")
160 # this should never happen
161 print "unknown output format", format
163 def save_record_to_file(filename, record_dict):
164 record = Record(dict=record_dict)
165 xml = record.save_as_xml()
166 f=codecs.open(filename, encoding='utf-8',mode="w")
171 # minimally check a key argument
172 def check_ssh_key (key):
173 good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$'
174 return re.match(good_ssh_key, key, re.IGNORECASE)
177 def load_record_from_opts(options):
179 if hasattr(options, 'xrn') and options.xrn:
180 if hasattr(options, 'type') and options.type:
181 xrn = Xrn(options.xrn, options.type)
183 xrn = Xrn(options.xrn)
184 record_dict['urn'] = xrn.get_urn()
185 record_dict['hrn'] = xrn.get_hrn()
186 record_dict['type'] = xrn.get_type()
187 if hasattr(options, 'key') and options.key:
189 pubkey = open(options.key, 'r').read()
192 if not check_ssh_key (pubkey):
193 raise SfaInvalidArgument(name='key',msg="Could not find file, or wrong key format")
194 record_dict['keys'] = [pubkey]
195 if hasattr(options, 'slices') and options.slices:
196 record_dict['slices'] = options.slices
197 if hasattr(options, 'researchers') and options.researchers:
198 record_dict['researcher'] = options.researchers
199 if hasattr(options, 'email') and options.email:
200 record_dict['email'] = options.email
201 if hasattr(options, 'pis') and options.pis:
202 record_dict['pi'] = options.pis
204 # handle extra settings
205 record_dict.update(options.extras)
207 return Record(dict=record_dict)
209 def load_record_from_file(filename):
210 f=codecs.open(filename, encoding="utf-8", mode="r")
211 xml_string = f.read()
213 return Record(xml=xml_string)
217 def unique_call_id(): return uuid.uuid4().urn
219 ########## a simple model for maintaing 3 doc attributes per command (instead of just one)
220 # essentially for the methods that implement a subcommand like sfi list
221 # we need to keep track of
222 # (*) doc a few lines that tell what it does, still located in __doc__
223 # (*) args_string a simple one-liner that describes mandatory arguments
224 # (*) example well, one or several releant examples
226 # since __doc__ only accounts for one, we use this simple mechanism below
227 # however we keep doc in place for easier migration
229 from functools import wraps
231 # we use a list as well as a dict so we can keep track of the order
235 def register_command (args_string, example):
237 name=getattr(m,'__name__')
238 doc=getattr(m,'__doc__',"-- missing doc --")
239 doc=doc.strip(" \t\n")
240 commands_list.append(name)
241 commands_dict[name]=(doc, args_string, example)
243 def new_method (*args, **kwds): return m(*args, **kwds)
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 ### various auxiliary material that we keep at hand
278 # need to call this other than just 'config' as we have a command/method with that name
279 self.config_instance=None
280 self.config_file=None
281 self.client_bootstrap=None
283 ### suitable if no reasonable command has been provided
284 def print_commands_help (self, options):
285 verbose=getattr(options,'verbose')
286 format3="%18s %-15s %s"
289 print format3%("command","cmd_args","description")
293 self.create_parser_global().print_help()
294 # preserve order from the code
295 for command in commands_list:
296 (doc, args_string, example) = commands_dict[command]
299 doc=doc.replace("\n","\n"+35*' ')
300 print format3%(command,args_string,doc)
302 self.create_parser_command(command).print_help()
304 ### now if a known command was found we can be more verbose on that one
305 def print_help (self):
306 print "==================== Generic sfi usage"
307 self.sfi_parser.print_help()
308 (doc,_,example)=commands_dict[self.command]
309 print "\n==================== Purpose of %s"%self.command
311 print "\n==================== Specific usage for %s"%self.command
312 self.command_parser.print_help()
314 print "\n==================== %s example(s)"%self.command
317 def create_parser_global(self):
319 # Generate command line parser
320 parser = OptionParser(add_help_option=False,
321 usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
322 description="Commands: %s"%(" ".join(commands_list)))
323 parser.add_option("-r", "--registry", dest="registry",
324 help="root registry", metavar="URL", default=None)
325 parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
326 help="slice API - in general a SM URL, but can be used to talk to an aggregate")
327 parser.add_option("-R", "--raw", dest="raw", default=None,
328 help="Save raw, unparsed server response to a file")
329 parser.add_option("", "--rawformat", dest="rawformat", type="choice",
330 help="raw file format ([text]|pickled|json)", default="text",
331 choices=("text","pickled","json"))
332 parser.add_option("", "--rawbanner", dest="rawbanner", default=None,
333 help="text string to write before and after raw output")
334 parser.add_option("-d", "--dir", dest="sfi_dir",
335 help="config & working directory - default is %default",
336 metavar="PATH", default=Sfi.default_sfi_dir())
337 parser.add_option("-u", "--user", dest="user",
338 help="user name", metavar="HRN", default=None)
339 parser.add_option("-a", "--auth", dest="auth",
340 help="authority name", metavar="HRN", default=None)
341 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
342 help="verbose mode - cumulative")
343 parser.add_option("-D", "--debug",
344 action="store_true", dest="debug", default=False,
345 help="Debug (xml-rpc) protocol messages")
346 # would it make sense to use ~/.ssh/id_rsa as a default here ?
347 parser.add_option("-k", "--private-key",
348 action="store", dest="user_private_key", default=None,
349 help="point to the private key file to use if not yet installed in sfi_dir")
350 parser.add_option("-t", "--timeout", dest="timeout", default=None,
351 help="Amout of time to wait before timing out the request")
352 parser.add_option("-h", "--help",
353 action="store_true", dest="help", default=False,
354 help="one page summary on commands & exit")
355 parser.disable_interspersed_args()
360 def create_parser_command(self, command):
361 if command not in commands_dict:
362 msg="Invalid command\n"
364 msg += ','.join(commands_list)
365 self.logger.critical(msg)
368 # retrieve args_string
369 (_, args_string, __) = commands_dict[command]
371 parser = OptionParser(add_help_option=False,
372 usage="sfi [sfi_options] %s [cmd_options] %s"
373 % (command, args_string))
374 parser.add_option ("-h","--help",dest='help',action='store_true',default=False,
375 help="Summary of one command usage")
377 if command in ("config"):
378 parser.add_option('-m', '--myslice', dest='myslice', action='store_true', default=False,
379 help='how myslice config variables as well')
381 if command in ("version"):
382 parser.add_option("-R","--registry-version",
383 action="store_true", dest="version_registry", default=False,
384 help="probe registry version instead of sliceapi")
385 parser.add_option("-l","--local",
386 action="store_true", dest="version_local", default=False,
387 help="display version of the local client")
389 if command in ("add", "update"):
390 parser.add_option('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
391 parser.add_option('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
392 parser.add_option('-e', '--email', dest='email', default="", help="email (mandatory for users)")
393 parser.add_option('-k', '--key', dest='key', metavar='<key>', help='public key string or file',
395 parser.add_option('-s', '--slices', dest='slices', metavar='<slices>', help='Set/replace slice xrns',
396 default='', type="str", action='callback', callback=optparse_listvalue_callback)
397 parser.add_option('-r', '--researchers', dest='researchers', metavar='<researchers>',
398 help='Set/replace slice researchers', default='', type="str", action='callback',
399 callback=optparse_listvalue_callback)
400 parser.add_option('-p', '--pis', dest='pis', metavar='<PIs>', help='Set/replace Principal Investigators/Project Managers',
401 default='', type="str", action='callback', callback=optparse_listvalue_callback)
402 parser.add_option ('-X','--extra',dest='extras',default={},type='str',metavar="<EXTRA_ASSIGNS>",
403 action="callback", callback=optparse_dictvalue_callback, nargs=1,
404 help="set extra/testbed-dependent flags, e.g. --extra enabled=true")
406 # show_credential option
407 if command in ("list","resources","create","add","update","remove","slices","delete","status","renew"):
408 parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False,
409 help="show credential(s) used in human-readable form")
410 # registy filter option
411 if command in ("list", "show", "remove"):
412 parser.add_option("-t", "--type", dest="type", type="choice",
413 help="type filter ([all]|user|slice|authority|node|aggregate)",
414 choices=("all", "user", "slice", "authority", "node", "aggregate"),
416 if command in ("show"):
417 parser.add_option("-k","--key",dest="keys",action="append",default=[],
418 help="specify specific keys to be displayed from record")
419 if command in ("resources"):
421 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
422 help="schema type and version of resulting RSpec")
423 # disable/enable cached rspecs
424 parser.add_option("-c", "--current", dest="current", default=False,
426 help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
428 parser.add_option("-f", "--format", dest="format", type="choice",
429 help="display format ([xml]|dns|ip)", default="xml",
430 choices=("xml", "dns", "ip"))
431 #panos: a new option to define the type of information about resources a user is interested in
432 parser.add_option("-i", "--info", dest="info",
433 help="optional component information", default=None)
434 # a new option to retreive or not reservation-oriented RSpecs (leases)
435 parser.add_option("-l", "--list_leases", dest="list_leases", type="choice",
436 help="Retreive or not reservation-oriented RSpecs ([resources]|leases|all )",
437 choices=("all", "resources", "leases"), default="resources")
440 # 'create' does return the new rspec, makes sense to save that too
441 if command in ("resources", "show", "list", "gid", 'create'):
442 parser.add_option("-o", "--output", dest="file",
443 help="output XML to file", metavar="FILE", default=None)
445 if command in ("show", "list"):
446 parser.add_option("-f", "--format", dest="format", type="choice",
447 help="display format ([text]|xml)", default="text",
448 choices=("text", "xml"))
450 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
451 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
452 choices=("xml", "xmllist", "hrnlist"))
453 if command == 'list':
454 parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
455 help="list all child records", default=False)
456 parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
457 help="gives details, like user keys", default=False)
458 if command in ("delegate"):
459 parser.add_option("-u", "--user",
460 action="store_true", dest="delegate_user", default=False,
461 help="delegate your own credentials; default if no other option is provided")
462 parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
463 metavar="slice_hrn", help="delegate cred. for slice HRN")
464 parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
465 metavar='auth_hrn', help="delegate cred for auth HRN")
466 # this primarily is a shorthand for -a my_hrn
467 parser.add_option("-p", "--pi", dest='delegate_pi', default=None, action='store_true',
468 help="delegate your PI credentials, so s.t. like -a your_hrn^")
469 parser.add_option("-A","--to-authority",dest='delegate_to_authority',action='store_true',default=False,
470 help="""by default the mandatory argument is expected to be a user,
471 use this if you mean an authority instead""")
473 if command in ("myslice"):
474 parser.add_option("-p","--password",dest='password',action='store',default=None,
475 help="specify mainfold password on the command line")
476 parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
477 metavar="slice_hrn", help="delegate cred. for slice HRN")
478 parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
479 metavar='auth_hrn', help="delegate PI cred for auth HRN")
485 # Main: parse arguments and dispatch to command
487 def dispatch(self, command, command_options, command_args):
488 method=getattr(self, command, None)
490 print "Unknown command %s"%command
492 return method(command_options, command_args)
495 self.sfi_parser = self.create_parser_global()
496 (options, args) = self.sfi_parser.parse_args()
498 self.print_commands_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_commands_help(options)
509 # complete / find unique match with command set
510 command_candidates = Candidates (commands_list)
512 command = command_candidates.only_match(input)
514 self.print_commands_help(options)
516 # second pass options parsing
518 self.command_parser = self.create_parser_command(command)
519 (command_options, command_args) = self.command_parser.parse_args(args[1:])
520 if command_options.help:
523 self.command_options = command_options
527 self.logger.debug("Command=%s" % self.command)
530 self.dispatch(command, command_options, command_args)
534 self.logger.log_exc ("sfi command %s failed"%command)
540 def read_config(self):
541 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
542 shell_config_file = os.path.join(self.options.sfi_dir,"sfi_config.sh")
544 if Config.is_ini(config_file):
545 config = Config (config_file)
547 # try upgrading from shell config format
548 fp, fn = mkstemp(suffix='sfi_config', text=True)
550 # we need to preload the sections we want parsed
551 # from the shell config
552 config.add_section('sfi')
553 # sface users should be able to use this same file to configure their stuff
554 config.add_section('sface')
555 # manifold users should be able to specify the details
556 # of their backend server here for 'sfi myslice'
557 config.add_section('myslice')
558 config.load(config_file)
560 shutil.move(config_file, shell_config_file)
562 config.save(config_file)
565 self.logger.critical("Failed to read configuration file %s"%config_file)
566 self.logger.info("Make sure to remove the export clauses and to add quotes")
567 if self.options.verbose==0:
568 self.logger.info("Re-run with -v for more details")
570 self.logger.log_exc("Could not read config file %s"%config_file)
573 self.config_instance=config
576 if (self.options.sm is not None):
577 self.sm_url = self.options.sm
578 elif hasattr(config, "SFI_SM"):
579 self.sm_url = config.SFI_SM
581 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
585 if (self.options.registry is not None):
586 self.reg_url = self.options.registry
587 elif hasattr(config, "SFI_REGISTRY"):
588 self.reg_url = config.SFI_REGISTRY
590 self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
594 if (self.options.user is not None):
595 self.user = self.options.user
596 elif hasattr(config, "SFI_USER"):
597 self.user = config.SFI_USER
599 self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
603 if (self.options.auth is not None):
604 self.authority = self.options.auth
605 elif hasattr(config, "SFI_AUTH"):
606 self.authority = config.SFI_AUTH
608 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
611 self.config_file=config_file
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_gid = client_bootstrap.my_gid ()
655 self.client_bootstrap = client_bootstrap
658 def my_authority_credential_string(self):
659 if not self.authority:
660 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
662 return self.client_bootstrap.authority_credential_string (self.authority)
664 def authority_credential_string(self, auth_hrn):
665 return self.client_bootstrap.authority_credential_string (auth_hrn)
667 def slice_credential_string(self, name):
668 return self.client_bootstrap.slice_credential_string (name)
671 # Management of the servers
676 if not hasattr (self, 'registry_proxy'):
677 self.logger.info("Contacting Registry at: %s"%self.reg_url)
678 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
679 timeout=self.options.timeout, verbose=self.options.debug)
680 return self.registry_proxy
684 if not hasattr (self, 'sliceapi_proxy'):
685 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
686 if hasattr(self.command_options,'component') and self.command_options.component:
687 # resolve the hrn at the registry
688 node_hrn = self.command_options.component
689 records = self.registry().Resolve(node_hrn, self.my_credential_string)
690 records = filter_records('node', records)
692 self.logger.warning("No such component:%r"% opts.component)
694 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
695 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
697 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
698 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
699 self.sm_url = 'http://' + self.sm_url
700 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
701 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
702 timeout=self.options.timeout, verbose=self.options.debug)
703 return self.sliceapi_proxy
705 def get_cached_server_version(self, server):
706 # check local cache first
709 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
710 cache_key = server.url + "-version"
712 cache = Cache(cache_file)
715 self.logger.info("Local cache not found at: %s" % cache_file)
718 version = cache.get(cache_key)
721 result = server.GetVersion()
722 version= ReturnValue.get_value(result)
723 # cache version for 20 minutes
724 cache.add(cache_key, version, ttl= 60*20)
725 self.logger.info("Updating cache file %s" % cache_file)
726 cache.save_to_file(cache_file)
730 ### resurrect this temporarily so we can support V1 aggregates for a while
731 def server_supports_options_arg(self, server):
733 Returns true if server support the optional call_id arg, false otherwise.
735 server_version = self.get_cached_server_version(server)
737 # xxx need to rewrite this
738 if int(server_version.get('geni_api')) >= 2:
742 def server_supports_call_id_arg(self, server):
743 server_version = self.get_cached_server_version(server)
745 if 'sfa' in server_version and 'code_tag' in server_version:
746 code_tag = server_version['code_tag']
747 code_tag_parts = code_tag.split("-")
748 version_parts = code_tag_parts[0].split(".")
749 major, minor = version_parts[0], version_parts[1]
750 rev = code_tag_parts[1]
751 if int(major) == 1 and minor == 0 and build >= 22:
755 ### ois = options if supported
756 # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
757 def ois (self, server, option_dict):
758 if self.server_supports_options_arg (server):
760 elif self.server_supports_call_id_arg (server):
761 return [ unique_call_id () ]
765 ### cis = call_id if supported - like ois
766 def cis (self, server):
767 if self.server_supports_call_id_arg (server):
768 return [ unique_call_id ]
772 ######################################## miscell utilities
773 def get_rspec_file(self, rspec):
774 if (os.path.isabs(rspec)):
777 file = os.path.join(self.options.sfi_dir, rspec)
778 if (os.path.isfile(file)):
781 self.logger.critical("No such rspec file %s"%rspec)
784 def get_record_file(self, record):
785 if (os.path.isabs(record)):
788 file = os.path.join(self.options.sfi_dir, record)
789 if (os.path.isfile(file)):
792 self.logger.critical("No such registry record file %s"%record)
796 #==========================================================================
797 # Following functions implement the commands
799 # Registry-related commands
800 #==========================================================================
802 @register_command("","")
803 def config (self, options, args):
804 "Display contents of current config"
805 print "# From configuration file %s"%self.config_file
806 flags=[ ('sfi', [ ('registry','reg_url'),
807 ('auth','authority'),
813 flags.append ( ('myslice', ['backend', 'delegate', 'platform', 'username'] ) )
815 for (section, tuples) in flags:
818 for (external_name, internal_name) in tuples:
819 print "%-20s = %s"%(external_name,getattr(self,internal_name))
822 varname="%s_%s"%(section.upper(),name.upper())
823 value=getattr(self.config_instance,varname)
824 print "%-20s = %s"%(name,value)
826 @register_command("","")
827 def version(self, options, args):
829 display an SFA server version (GetVersion)
830 or version information about sfi itself
832 if options.version_local:
833 version=version_core()
835 if options.version_registry:
836 server=self.registry()
838 server = self.sliceapi()
839 result = server.GetVersion()
840 version = ReturnValue.get_value(result)
842 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
844 pprinter = PrettyPrinter(indent=4)
845 pprinter.pprint(version)
847 @register_command("authority","")
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)
870 terminal_render (list, options)
872 save_records_to_file(options.file, list, options.fileformat)
875 @register_command("name","")
876 def show(self, options, args):
878 show details about named registry record (Resolve)
884 # explicitly require Resolve to run in details mode
885 record_dicts = self.registry().Resolve(hrn, self.my_credential_string, {'details':True})
886 record_dicts = filter_records(options.type, record_dicts)
888 self.logger.error("No record of type %s"% options.type)
890 # user has required to focus on some keys
892 def project (record):
894 for key in options.keys:
895 try: projected[key]=record[key]
898 record_dicts = [ project (record) for record in record_dicts ]
899 records = [ Record(dict=record_dict) for record_dict in record_dicts ]
900 for record in records:
901 if (options.format == "text"): record.dump(sort=True)
902 else: print record.save_as_xml()
904 save_records_to_file(options.file, record_dicts, options.fileformat)
907 @register_command("[xml-filename]","")
908 def add(self, options, args):
909 """add record into registry (Register)
910 from command line options (recommended)
911 old-school method involving an xml file still supported"""
913 auth_cred = self.my_authority_credential_string()
914 if options.show_credential:
915 show_credentials(auth_cred)
922 record_filepath = args[0]
923 rec_file = self.get_record_file(record_filepath)
924 record_dict.update(load_record_from_file(rec_file).todict())
926 print "Cannot load record file %s"%record_filepath
929 record_dict.update(load_record_from_opts(options).todict())
930 # we should have a type by now
931 if 'type' not in record_dict :
934 # this is still planetlab dependent.. as plc will whine without that
935 # also, it's only for adding
936 if record_dict['type'] == 'user':
937 if not 'first_name' in record_dict:
938 record_dict['first_name'] = record_dict['hrn']
939 if 'last_name' not in record_dict:
940 record_dict['last_name'] = record_dict['hrn']
941 return self.registry().Register(record_dict, auth_cred)
943 @register_command("[xml-filename]","")
944 def update(self, options, args):
945 """update record into registry (Update)
946 from command line options (recommended)
947 old-school method involving an xml file still supported"""
950 record_filepath = args[0]
951 rec_file = self.get_record_file(record_filepath)
952 record_dict.update(load_record_from_file(rec_file).todict())
954 record_dict.update(load_record_from_opts(options).todict())
955 # at the very least we need 'type' here
956 if 'type' not in record_dict:
960 # don't translate into an object, as this would possibly distort
961 # user-provided data; e.g. add an 'email' field to Users
962 if record_dict['type'] == "user":
963 if record_dict['hrn'] == self.user:
964 cred = self.my_credential_string
966 cred = self.my_authority_credential_string()
967 elif record_dict['type'] in ["slice"]:
969 cred = self.slice_credential_string(record_dict['hrn'])
970 except ServerException, e:
971 # XXX smbaker -- once we have better error return codes, update this
972 # to do something better than a string compare
973 if "Permission error" in e.args[0]:
974 cred = self.my_authority_credential_string()
977 elif record_dict['type'] in ["authority"]:
978 cred = self.my_authority_credential_string()
979 elif record_dict['type'] == 'node':
980 cred = self.my_authority_credential_string()
982 raise "unknown record type" + record_dict['type']
983 if options.show_credential:
984 show_credentials(cred)
985 return self.registry().Update(record_dict, cred)
987 @register_command("hrn","")
988 def remove(self, options, args):
989 "remove registry record by name (Remove)"
990 auth_cred = self.my_authority_credential_string()
998 if options.show_credential:
999 show_credentials(auth_cred)
1000 return self.registry().Remove(hrn, auth_cred, type)
1002 # ==================================================================
1003 # Slice-related commands
1004 # ==================================================================
1006 @register_command("","")
1007 def slices(self, options, args):
1008 "list instantiated slices (ListSlices) - returns urn's"
1009 server = self.sliceapi()
1011 creds = [self.my_credential_string]
1012 # options and call_id when supported
1014 api_options['call_id']=unique_call_id()
1015 if options.show_credential:
1016 show_credentials(creds)
1017 result = server.ListSlices(creds, *self.ois(server,api_options))
1018 value = ReturnValue.get_value(result)
1019 if self.options.raw:
1020 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1025 # show rspec for named slice
1026 @register_command("[slice_hrn]","")
1027 def resources(self, options, args):
1029 with no arg, discover available resources, (ListResources)
1030 or with an slice hrn, shows currently provisioned resources
1032 server = self.sliceapi()
1037 the_credential=self.slice_credential_string(args[0])
1038 creds.append(the_credential)
1040 the_credential=self.my_credential_string
1041 creds.append(the_credential)
1042 if options.show_credential:
1043 show_credentials(creds)
1045 # no need to check if server accepts the options argument since the options has
1046 # been a required argument since v1 API
1048 # always send call_id to v2 servers
1049 api_options ['call_id'] = unique_call_id()
1050 # ask for cached value if available
1051 api_options ['cached'] = True
1054 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
1056 api_options['info'] = options.info
1057 if options.list_leases:
1058 api_options['list_leases'] = options.list_leases
1060 if options.current == True:
1061 api_options['cached'] = False
1063 api_options['cached'] = True
1064 if options.rspec_version:
1065 version_manager = VersionManager()
1066 server_version = self.get_cached_server_version(server)
1067 if 'sfa' in server_version:
1068 # just request the version the client wants
1069 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1071 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'}
1073 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'}
1074 result = server.ListResources (creds, api_options)
1075 value = ReturnValue.get_value(result)
1076 if self.options.raw:
1077 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1078 if options.file is not None:
1079 save_rspec_to_file(value, options.file)
1080 if (self.options.raw is None) and (options.file is None):
1081 display_rspec(value, options.format)
1085 @register_command("slice_hrn rspec","")
1086 def create(self, options, args):
1088 create or update named slice with given rspec (CreateSliver)
1090 server = self.sliceapi()
1092 # xxx do we need to check usage (len(args)) ?
1095 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1098 creds = [self.slice_credential_string(slice_hrn)]
1100 delegated_cred = None
1101 server_version = self.get_cached_server_version(server)
1102 if server_version.get('interface') == 'slicemgr':
1103 # delegate our cred to the slice manager
1104 # do not delegate cred to slicemgr...not working at the moment
1106 #if server_version.get('hrn'):
1107 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1108 #elif server_version.get('urn'):
1109 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1111 if options.show_credential:
1112 show_credentials(creds)
1115 rspec_file = self.get_rspec_file(args[1])
1116 rspec = open(rspec_file).read()
1119 # need to pass along user keys to the aggregate.
1121 # { urn: urn:publicid:IDN+emulab.net+user+alice
1122 # keys: [<ssh key A>, <ssh key B>]
1125 # xxx Thierry 2012 sept. 21
1126 # contrary to what I was first thinking, calling Resolve with details=False does not yet work properly here
1127 # I am turning details=True on again on a - hopefully - temporary basis, just to get this whole thing to work again
1128 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1129 # slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string], {'details':True})
1130 if slice_records and 'reg-researchers' in slice_records[0] and slice_records[0]['reg-researchers']:
1131 slice_record = slice_records[0]
1132 user_hrns = slice_record['reg-researchers']
1133 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1134 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1136 if 'sfa' not in server_version:
1137 users = pg_users_arg(user_records)
1138 rspec = RSpec(rspec)
1139 rspec.filter({'component_manager_id': server_version['urn']})
1140 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1142 users = sfa_users_arg(user_records, slice_record)
1144 # do not append users, keys, or slice tags. Anything
1145 # not contained in this request will be removed from the slice
1147 # CreateSliver has supported the options argument for a while now so it should
1148 # be safe to assume this server support it
1150 api_options ['append'] = False
1151 api_options ['call_id'] = unique_call_id()
1152 result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server, api_options))
1153 value = ReturnValue.get_value(result)
1154 if self.options.raw:
1155 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1156 if options.file is not None:
1157 save_rspec_to_file (value, options.file)
1158 if (self.options.raw is None) and (options.file is None):
1163 @register_command("slice_hrn","")
1164 def delete(self, options, args):
1166 delete named slice (DeleteSliver)
1168 server = self.sliceapi()
1172 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1175 slice_cred = self.slice_credential_string(slice_hrn)
1176 creds = [slice_cred]
1178 # options and call_id when supported
1180 api_options ['call_id'] = unique_call_id()
1181 if options.show_credential:
1182 show_credentials(creds)
1183 result = server.DeleteSliver(slice_urn, creds, *self.ois(server, api_options ) )
1184 value = ReturnValue.get_value(result)
1185 if self.options.raw:
1186 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1191 @register_command("slice_hrn","")
1192 def status(self, options, args):
1194 retrieve slice status (SliverStatus)
1196 server = self.sliceapi()
1200 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1203 slice_cred = self.slice_credential_string(slice_hrn)
1204 creds = [slice_cred]
1206 # options and call_id when supported
1208 api_options['call_id']=unique_call_id()
1209 if options.show_credential:
1210 show_credentials(creds)
1211 result = server.SliverStatus(slice_urn, creds, *self.ois(server,api_options))
1212 value = ReturnValue.get_value(result)
1213 if self.options.raw:
1214 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1218 @register_command("slice_hrn","")
1219 def start(self, options, args):
1221 start named slice (Start)
1223 server = self.sliceapi()
1227 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1230 slice_cred = self.slice_credential_string(args[0])
1231 creds = [slice_cred]
1232 # xxx Thierry - does this not need an api_options as well ?
1233 result = server.Start(slice_urn, creds)
1234 value = ReturnValue.get_value(result)
1235 if self.options.raw:
1236 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1241 @register_command("slice_hrn","")
1242 def stop(self, options, args):
1244 stop named slice (Stop)
1246 server = self.sliceapi()
1249 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1251 slice_cred = self.slice_credential_string(args[0])
1252 creds = [slice_cred]
1253 result = server.Stop(slice_urn, creds)
1254 value = ReturnValue.get_value(result)
1255 if self.options.raw:
1256 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1262 @register_command("slice_hrn","")
1263 def reset(self, options, args):
1265 reset named slice (reset_slice)
1267 server = self.sliceapi()
1270 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1272 slice_cred = self.slice_credential_string(args[0])
1273 creds = [slice_cred]
1274 result = server.reset_slice(creds, slice_urn)
1275 value = ReturnValue.get_value(result)
1276 if self.options.raw:
1277 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1282 @register_command("slice_hrn time","")
1283 def renew(self, options, args):
1285 renew slice (RenewSliver)
1287 server = self.sliceapi()
1291 [ slice_hrn, input_time ] = args
1293 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1294 # time: don't try to be smart on the time format, server-side will
1296 slice_cred = self.slice_credential_string(args[0])
1297 creds = [slice_cred]
1298 # options and call_id when supported
1300 api_options['call_id']=unique_call_id()
1301 if options.show_credential:
1302 show_credentials(creds)
1303 result = server.RenewSliver(slice_urn, creds, input_time, *self.ois(server,api_options))
1304 value = ReturnValue.get_value(result)
1305 if self.options.raw:
1306 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1312 @register_command("slice_hrn","")
1313 def shutdown(self, options, args):
1315 shutdown named slice (Shutdown)
1317 server = self.sliceapi()
1320 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1322 slice_cred = self.slice_credential_string(slice_hrn)
1323 creds = [slice_cred]
1324 result = server.Shutdown(slice_urn, creds)
1325 value = ReturnValue.get_value(result)
1326 if self.options.raw:
1327 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1333 @register_command("slice_hrn rspec","")
1334 def get_ticket(self, options, args):
1336 get a ticket for the specified slice
1338 server = self.sliceapi()
1340 slice_hrn, rspec_path = args[0], args[1]
1341 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1343 slice_cred = self.slice_credential_string(slice_hrn)
1344 creds = [slice_cred]
1346 rspec_file = self.get_rspec_file(rspec_path)
1347 rspec = open(rspec_file).read()
1348 # options and call_id when supported
1350 api_options['call_id']=unique_call_id()
1351 # get ticket at the server
1352 ticket_string = server.GetTicket(slice_urn, creds, rspec, *self.ois(server,api_options))
1354 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1355 self.logger.info("writing ticket to %s"%file)
1356 ticket = SfaTicket(string=ticket_string)
1357 ticket.save_to_file(filename=file, save_parents=True)
1359 @register_command("ticket","")
1360 def redeem_ticket(self, options, args):
1362 Connects to nodes in a slice and redeems a ticket
1363 (slice hrn is retrieved from the ticket)
1365 ticket_file = args[0]
1367 # get slice hrn from the ticket
1368 # use this to get the right slice credential
1369 ticket = SfaTicket(filename=ticket_file)
1371 ticket_string = ticket.save_to_string(save_parents=True)
1373 slice_hrn = ticket.gidObject.get_hrn()
1374 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1375 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1376 slice_cred = self.slice_credential_string(slice_hrn)
1378 # get a list of node hostnames from the RSpec
1379 tree = etree.parse(StringIO(ticket.rspec))
1380 root = tree.getroot()
1381 hostnames = root.xpath("./network/site/node/hostname/text()")
1383 # create an xmlrpc connection to the component manager at each of these
1384 # components and gall redeem_ticket
1386 for hostname in hostnames:
1388 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1389 cm_url="http://%s:%s/"%(hostname,CM_PORT)
1390 server = SfaServerProxy(cm_url, self.private_key, self.my_gid)
1391 server = self.server_proxy(hostname, CM_PORT, self.private_key,
1392 timeout=self.options.timeout, verbose=self.options.debug)
1393 server.RedeemTicket(ticket_string, slice_cred)
1394 self.logger.info("Success")
1395 except socket.gaierror:
1396 self.logger.error("redeem_ticket failed on %s: Component Manager not accepting requests"%hostname)
1397 except Exception, e:
1398 self.logger.log_exc(e.message)
1401 @register_command("[name]","")
1402 def gid(self, options, args):
1404 Create a GID (CreateGid)
1409 target_hrn = args[0]
1410 my_gid_string = open(self.client_bootstrap.my_gid()).read()
1411 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, my_gid_string)
1413 filename = options.file
1415 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1416 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1417 GID(string=gid).save_to_file(filename)
1419 ####################
1420 @register_command("to_hrn","""$ sfi delegate -u -p -s ple.inria.heartbeat -s ple.inria.omftest ple.upmc.slicebrowser
1422 will locally create a set of delegated credentials for the benefit of ple.upmc.slicebrowser
1423 the set of credentials in the scope for this call would be
1424 (*) ple.inria.thierry_parmentelat.user_for_ple.upmc.slicebrowser.user.cred
1426 (*) ple.inria.pi_for_ple.upmc.slicebrowser.user.cred
1428 (*) ple.inria.heartbeat.slice_for_ple.upmc.slicebrowser.user.cred
1429 (*) ple.inria.omftest.slice_for_ple.upmc.slicebrowser.user.cred
1430 because of the two -s options
1433 def delegate (self, options, args):
1435 (locally) create delegate credential for use by given hrn
1436 make sure to check for 'sfi myslice' instead if you plan
1443 # support for several delegations in the same call
1444 # so first we gather the things to do
1446 for slice_hrn in options.delegate_slices:
1447 message="%s.slice"%slice_hrn
1448 original = self.slice_credential_string(slice_hrn)
1449 tuples.append ( (message, original,) )
1450 if options.delegate_pi:
1451 my_authority=self.authority
1452 message="%s.pi"%my_authority
1453 original = self.my_authority_credential_string()
1454 tuples.append ( (message, original,) )
1455 for auth_hrn in options.delegate_auths:
1456 message="%s.auth"%auth_hrn
1457 original=self.authority_credential_string(auth_hrn)
1458 tuples.append ( (message, original, ) )
1459 # if nothing was specified at all at this point, let's assume -u
1460 if not tuples: options.delegate_user=True
1462 if options.delegate_user:
1463 message="%s.user"%self.user
1464 original = self.my_credential_string
1465 tuples.append ( (message, original, ) )
1467 # default type for beneficial is user unless -A
1468 if options.delegate_to_authority: to_type='authority'
1469 else: to_type='user'
1471 # let's now handle all this
1472 # it's all in the filenaming scheme
1473 for (message,original) in tuples:
1474 delegated_string = self.client_bootstrap.delegate_credential_string(original, to_hrn, to_type)
1475 delegated_credential = Credential (string=delegated_string)
1476 filename = os.path.join ( self.options.sfi_dir,
1477 "%s_for_%s.%s.cred"%(message,to_hrn,to_type))
1478 delegated_credential.save_to_file(filename, save_parents=True)
1479 self.logger.info("delegated credential for %s to %s and wrote to %s"%(message,to_hrn,filename))
1481 ####################
1482 @register_command("","""$ less +/myslice sfi_config
1484 backend = http://manifold.pl.sophia.inria.fr:7080
1485 # the HRN that myslice uses, so that we are delegating to
1486 delegate = ple.upmc.slicebrowser
1487 # platform - this is a myslice concept
1489 # username - as of this writing (May 2013) a simple login name
1493 will first collect the slices that you are part of, then make sure
1494 all your credentials are up-to-date (read: refresh expired ones)
1495 then compute delegated credentials for user 'ple.upmc.slicebrowser'
1496 and upload them all on myslice backend, using 'platform' and 'user'.
1497 A password will be prompted for the upload part.
1499 $ sfi -v myslice -- or sfi -vv myslice
1500 same but with more and more verbosity
1503 is synonym to sfi myslice as no other command starts with an 'm'
1505 ) # register_command
1506 def myslice (self, options, args):
1508 """ This helper is for refreshing your credentials at myslice; it will
1509 * compute all the slices that you currently have credentials on
1510 * refresh all your credentials (you as a user and pi, your slices)
1511 * upload them to the manifold backend server
1512 for last phase, sfi_config is read to look for the [myslice] section,
1513 and namely the 'backend', 'delegate' and 'user' settings"""
1519 ### the rough sketch goes like this
1520 # (a) rain check for sufficient config in sfi_config
1521 # we don't allow to override these settings for now
1523 myslice_keys=['backend', 'delegate', 'platform', 'username']
1524 for key in myslice_keys:
1525 full_key="MYSLICE_" + key.upper()
1526 value=getattr(self.config_instance,full_key,None)
1527 if value: myslice_dict[key]=value
1528 else: print "Unsufficient config, missing key %s in [myslice] section of sfi_config"%key
1529 if len(myslice_dict) != len(myslice_keys):
1532 # (b) figure whether we are PI for the authority where we belong
1533 self.logger.info("Resolving our own id")
1534 my_records=self.registry().Resolve(self.user,self.my_credential_string)
1535 if len(my_records)!=1: print "Cannot Resolve %s -- exiting"%self.user; sys.exit(1)
1536 my_record=my_records[0]
1537 my_auths_all = my_record['reg-pi-authorities']
1538 self.logger.info("Found %d authorities that we are PI for"%len(my_auths_all))
1539 self.logger.debug("They are %s"%(my_auths_all))
1541 my_auths = my_auths_all
1542 if options.delegate_auths:
1543 my_auths = list(set(my_auths_all).intersection(set(options.delegate_auths)))
1545 self.logger.info("Delegate PI creds for authorities: %s"%my_auths )
1546 # (c) get the set of slices that we are in
1547 my_slices_all=my_record['reg-slices']
1548 self.logger.info("Found %d slices that we are member of"%len(my_slices_all))
1549 self.logger.debug("They are: %s"%(my_slices_all))
1551 my_slices = my_slices_all
1552 if options.delegate_slices:
1553 my_slices = list(set(my_slices_all).intersection(set(options.delegate_slices)))
1555 self.logger.info("Delegate slice creds for slices: %s"%my_slices)
1557 # (d) make sure we have *valid* credentials for all these
1559 hrn_credentials.append ( (self.user, 'user', self.my_credential_string,) )
1560 for auth_hrn in my_auths:
1561 hrn_credentials.append ( (auth_hrn, 'auth', self.authority_credential_string(auth_hrn),) )
1562 for slice_hrn in my_slices:
1563 hrn_credentials.append ( (slice_hrn, 'slice', self.slice_credential_string (slice_hrn),) )
1565 # (e) check for the delegated version of these
1566 # xxx todo add an option -a/-A? like for 'sfi delegate' for when we ever
1567 # switch to myslice using an authority instead of a user
1568 delegatee_type='user'
1569 delegatee_hrn=myslice_dict['delegate']
1570 hrn_delegated_credentials = []
1571 for (hrn, htype, credential) in hrn_credentials:
1572 delegated_credential = self.client_bootstrap.delegate_credential_string (credential, delegatee_hrn, delegatee_type)
1573 hrn_delegated_credentials.append ((hrn, htype, delegated_credential, ))
1575 # (f) and finally upload them to manifold server
1576 # xxx todo add an option so the password can be set on the command line
1577 # (but *NOT* in the config file) so other apps can leverage this
1578 uploader = ManifoldUploader (logger=self.logger,
1579 url=myslice_dict['backend'],
1580 platform=myslice_dict['platform'],
1581 username=myslice_dict['username'],
1582 password=options.password)
1583 uploader.prompt_all()
1584 (count_all,count_success)=(0,0)
1585 for (hrn,htype,delegated_credential) in hrn_delegated_credentials:
1587 inspect=Credential(string=delegated_credential)
1588 expire_datetime=inspect.get_expiration()
1589 message="%s (%s) [exp:%s]"%(hrn,htype,expire_datetime)
1590 if uploader.upload(delegated_credential,message=message):
1594 self.logger.info("Successfully uploaded %d/%d credentials"%(count_success,count_all))
1595 # at first I thought we would want to save these,
1596 # like 'sfi delegate does' but on second thought
1597 # it is probably not helpful as people would not
1598 # need to run 'sfi delegate' at all anymore
1599 if count_success != count_all: sys.exit(1)
1602 @register_command("cred","")
1603 def trusted(self, options, args):
1605 return the trusted certs at this interface (get_trusted_certs)
1607 trusted_certs = self.registry().get_trusted_certs()
1608 for trusted_cert in trusted_certs:
1609 print "\n===========================================================\n"
1610 gid = GID(string=trusted_cert)
1612 cert = Certificate(string=trusted_cert)
1613 self.logger.debug('Sfi.trusted -> %r'%cert.get_subject())
1614 print "Certificate:\n%s\n\n"%trusted_cert