2 # sfi.py - basic SFA command-line client
3 # this module is also used in sfascan
18 # import bonfire.py for listing bonfire resources
21 from lxml import etree
22 from StringIO import StringIO
23 from optparse import OptionParser
24 from pprint import PrettyPrinter
25 from tempfile import mkstemp
27 from sfa.trust.certificate import Keypair, Certificate
28 from sfa.trust.gid import GID
29 from sfa.trust.credential import Credential
30 from sfa.trust.sfaticket import SfaTicket
32 from sfa.util.faults import SfaInvalidArgument
33 from sfa.util.sfalogging import sfi_logger
34 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn, Xrn
35 from sfa.util.config import Config
36 from sfa.util.version import version_core
37 from sfa.util.cache import Cache
39 from sfa.storage.record import Record
41 from sfa.rspecs.rspec import RSpec
42 from sfa.rspecs.rspec_converter import RSpecConverter
43 from sfa.rspecs.version_manager import VersionManager
45 from sfa.client.sfaclientlib import SfaClientBootstrap
46 from sfa.client.sfaserverproxy import SfaServerProxy, ServerException
47 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
48 from sfa.client.return_value import ReturnValue
49 from sfa.client.candidates import Candidates
50 from sfa.client.manifolduploader import ManifoldUploader
54 from sfa.client.common import optparse_listvalue_callback, optparse_dictvalue_callback, \
55 terminal_render, filter_records
58 def display_rspec(rspec, format='rspec'):
60 tree = etree.parse(StringIO(rspec))
62 result = root.xpath("./network/site/node/hostname/text()")
63 elif format in ['ip']:
64 # The IP address is not yet part of the new RSpec
65 # so this doesn't do anything yet.
66 tree = etree.parse(StringIO(rspec))
68 result = root.xpath("./network/site/node/ipv4/text()")
75 def display_list(results):
76 for result in results:
79 def display_records(recordList, dump=False):
80 ''' Print all fields in the record'''
81 for record in recordList:
82 display_record(record, dump)
84 def display_record(record, dump=False):
86 record.dump(sort=True)
88 info = record.getdict()
89 print "%s (%s)" % (info['hrn'], info['type'])
93 def filter_records(type, records):
95 for record in records:
96 if (record['type'] == type) or (type == "all"):
97 filtered_records.append(record)
98 return filtered_records
101 def credential_printable (cred):
102 credential=Credential(cred=cred)
104 result += credential.get_summary_tostring()
106 rights = credential.get_privileges()
107 result += "type=%s\n" % credential.type
108 result += "version=%s\n" % credential.version
109 result += "rights=%s\n"%rights
112 def show_credentials (cred_s):
113 if not isinstance (cred_s,list): cred_s = [cred_s]
115 print "Using Credential %s"%credential_printable(cred)
118 def save_raw_to_file(var, filename, format="text", banner=None):
120 # if filename is "-", send it to stdout
123 f = open(filename, "w")
128 elif format == "pickled":
129 f.write(pickle.dumps(var))
130 elif format == "json":
131 if hasattr(json, "dumps"):
132 f.write(json.dumps(var)) # python 2.6
134 f.write(json.write(var)) # python 2.5
136 # this should never happen
137 print "unknown output format", format
139 f.write('\n'+banner+"\n")
141 def save_rspec_to_file(rspec, filename):
142 if not filename.endswith(".rspec"):
143 filename = filename + ".rspec"
144 f = open(filename, 'w')
149 def save_records_to_file(filename, record_dicts, format="xml"):
152 for record_dict in record_dicts:
154 save_record_to_file(filename + "." + str(index), record_dict)
156 save_record_to_file(filename, record_dict)
158 elif format == "xmllist":
159 f = open(filename, "w")
160 f.write("<recordlist>\n")
161 for record_dict in record_dicts:
162 record_obj=Record(dict=record_dict)
163 f.write('<record hrn="' + record_obj.hrn + '" type="' + record_obj.type + '" />\n')
164 f.write("</recordlist>\n")
166 elif format == "hrnlist":
167 f = open(filename, "w")
168 for record_dict in record_dicts:
169 record_obj=Record(dict=record_dict)
170 f.write(record_obj.hrn + "\n")
173 # this should never happen
174 print "unknown output format", format
176 def save_record_to_file(filename, record_dict):
177 record = Record(dict=record_dict)
178 xml = record.save_as_xml()
179 f=codecs.open(filename, encoding='utf-8',mode="w")
184 # minimally check a key argument
185 def check_ssh_key (key):
186 good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$'
187 return re.match(good_ssh_key, key, re.IGNORECASE)
190 def load_record_from_opts(options):
192 if hasattr(options, 'xrn') and options.xrn:
193 if hasattr(options, 'type') and options.type:
194 xrn = Xrn(options.xrn, options.type)
196 xrn = Xrn(options.xrn)
197 record_dict['urn'] = xrn.get_urn()
198 record_dict['hrn'] = xrn.get_hrn()
199 record_dict['type'] = xrn.get_type()
200 if hasattr(options, 'key') and options.key:
202 pubkey = open(options.key, 'r').read()
205 if not check_ssh_key (pubkey):
206 raise SfaInvalidArgument(name='key',msg="Could not find file, or wrong key format")
207 record_dict['keys'] = [pubkey]
208 if hasattr(options, 'slices') and options.slices:
209 record_dict['slices'] = options.slices
210 if hasattr(options, 'researchers') and options.researchers:
211 record_dict['researcher'] = options.researchers
212 if hasattr(options, 'email') and options.email:
213 record_dict['email'] = options.email
214 if hasattr(options, 'pis') and options.pis:
215 record_dict['pi'] = options.pis
217 # handle extra settings
218 record_dict.update(options.extras)
220 return Record(dict=record_dict)
222 def load_record_from_file(filename):
223 f=codecs.open(filename, encoding="utf-8", mode="r")
224 xml_string = f.read()
226 return Record(xml=xml_string)
230 def unique_call_id(): return uuid.uuid4().urn
232 ########## a simple model for maintaing 3 doc attributes per command (instead of just one)
233 # essentially for the methods that implement a subcommand like sfi list
234 # we need to keep track of
235 # (*) doc a few lines that tell what it does, still located in __doc__
236 # (*) args_string a simple one-liner that describes mandatory arguments
237 # (*) example well, one or several releant examples
239 # since __doc__ only accounts for one, we use this simple mechanism below
240 # however we keep doc in place for easier migration
242 from functools import wraps
244 # we use a list as well as a dict so we can keep track of the order
248 def register_command (args_string, example):
250 name=getattr(m,'__name__')
251 doc=getattr(m,'__doc__',"-- missing doc --")
252 doc=doc.strip(" \t\n")
253 commands_list.append(name)
254 commands_dict[name]=(doc, args_string, example)
256 def new_method (*args, **kwds): return m(*args, **kwds)
264 # dirty hack to make this class usable from the outside
265 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user', 'user_private_key']
268 def default_sfi_dir ():
269 if os.path.isfile("./sfi_config"):
272 return os.path.expanduser("~/.sfi/")
274 # dummy to meet Sfi's expectations for its 'options' field
275 # i.e. s/t we can do setattr on
279 def __init__ (self,options=None):
280 if options is None: options=Sfi.DummyOptions()
281 for opt in Sfi.required_options:
282 if not hasattr(options,opt): setattr(options,opt,None)
283 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
284 self.options = options
286 self.authority = None
287 self.logger = sfi_logger
288 self.logger.enable_console()
289 ### various auxiliary material that we keep at hand
291 # need to call this other than just 'config' as we have a command/method with that name
292 self.config_instance=None
293 self.config_file=None
294 self.client_bootstrap=None
296 ### suitable if no reasonable command has been provided
297 def print_commands_help (self, options):
298 verbose=getattr(options,'verbose')
299 format3="%18s %-15s %s"
302 print format3%("command","cmd_args","description")
306 self.create_parser_global().print_help()
307 # preserve order from the code
308 for command in commands_list:
309 (doc, args_string, example) = commands_dict[command]
312 doc=doc.replace("\n","\n"+35*' ')
313 print format3%(command,args_string,doc)
315 self.create_parser_command(command).print_help()
317 ### now if a known command was found we can be more verbose on that one
318 def print_help (self):
319 print "==================== Generic sfi usage"
320 self.sfi_parser.print_help()
321 (doc,_,example)=commands_dict[self.command]
322 print "\n==================== Purpose of %s"%self.command
324 print "\n==================== Specific usage for %s"%self.command
325 self.command_parser.print_help()
327 print "\n==================== %s example(s)"%self.command
330 def create_parser_global(self):
331 # Generate command line parser
332 parser = OptionParser(add_help_option=False,
333 usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
334 description="Commands: %s"%(" ".join(commands_list)))
335 parser.add_option("-r", "--registry", dest="registry",
336 help="root registry", metavar="URL", default=None)
337 parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
338 help="slice API - in general a SM URL, but can be used to talk to an aggregate")
339 parser.add_option("-R", "--raw", dest="raw", default=None,
340 help="Save raw, unparsed server response to a file")
341 parser.add_option("", "--rawformat", dest="rawformat", type="choice",
342 help="raw file format ([text]|pickled|json)", default="text",
343 choices=("text","pickled","json"))
344 parser.add_option("", "--rawbanner", dest="rawbanner", default=None,
345 help="text string to write before and after raw output")
346 parser.add_option("-d", "--dir", dest="sfi_dir",
347 help="config & working directory - default is %default",
348 metavar="PATH", default=Sfi.default_sfi_dir())
349 parser.add_option("-u", "--user", dest="user",
350 help="user name", metavar="HRN", default=None)
351 parser.add_option("-a", "--auth", dest="auth",
352 help="authority name", metavar="HRN", default=None)
353 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
354 help="verbose mode - cumulative")
355 parser.add_option("-D", "--debug",
356 action="store_true", dest="debug", default=False,
357 help="Debug (xml-rpc) protocol messages")
358 # would it make sense to use ~/.ssh/id_rsa as a default here ?
359 parser.add_option("-k", "--private-key",
360 action="store", dest="user_private_key", default=None,
361 help="point to the private key file to use if not yet installed in sfi_dir")
362 parser.add_option("-t", "--timeout", dest="timeout", default=None,
363 help="Amout of time to wait before timing out the request")
364 parser.add_option("-h", "--help",
365 action="store_true", dest="help", default=False,
366 help="one page summary on commands & exit")
367 parser.disable_interspersed_args()
372 def create_parser_command(self, command):
373 if command not in commands_dict:
374 msg="Invalid command\n"
376 msg += ','.join(commands_list)
377 self.logger.critical(msg)
380 # retrieve args_string
381 (_, args_string, __) = commands_dict[command]
383 parser = OptionParser(add_help_option=False,
384 usage="sfi [sfi_options] %s [cmd_options] %s"
385 % (command, args_string))
386 parser.add_option ("-h","--help",dest='help',action='store_true',default=False,
387 help="Summary of one command usage")
389 if command in ("config"):
390 parser.add_option('-m', '--myslice', dest='myslice', action='store_true', default=False,
391 help='how myslice config variables as well')
393 if command in ("version"):
394 parser.add_option("-R","--registry-version",
395 action="store_true", dest="version_registry", default=False,
396 help="probe registry version instead of sliceapi")
397 parser.add_option("-l","--local",
398 action="store_true", dest="version_local", default=False,
399 help="display version of the local client")
401 if command in ("add", "update"):
402 parser.add_option('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
403 parser.add_option('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
404 parser.add_option('-e', '--email', dest='email', default="", help="email (mandatory for users)")
405 parser.add_option('-k', '--key', dest='key', metavar='<key>', help='public key string or file',
407 parser.add_option('-s', '--slices', dest='slices', metavar='<slices>', help='Set/replace slice xrns',
408 default='', type="str", action='callback', callback=optparse_listvalue_callback)
409 parser.add_option('-r', '--researchers', dest='researchers', metavar='<researchers>',
410 help='Set/replace slice researchers', default='', type="str", action='callback',
411 callback=optparse_listvalue_callback)
412 parser.add_option('-p', '--pis', dest='pis', metavar='<PIs>', help='Set/replace Principal Investigators/Project Managers',
413 default='', type="str", action='callback', callback=optparse_listvalue_callback)
414 parser.add_option ('-X','--extra',dest='extras',default={},type='str',metavar="<EXTRA_ASSIGNS>",
415 action="callback", callback=optparse_dictvalue_callback, nargs=1,
416 help="set extra/testbed-dependent flags, e.g. --extra enabled=true")
418 # user specifies remote aggregate/sm/component
419 if command in ("resources", "describe", "allocate", "provision", "delete", "allocate", "provision",
420 "action", "shutdown", "renew", "status"):
421 parser.add_option("-d", "--delegate", dest="delegate", default=None,
423 help="Include a credential delegated to the user's root"+\
424 "authority in set of credentials for this call")
426 # show_credential option
427 if command in ("list","resources", "describe", "provision", "allocate", "add","update","remove","delete","status","renew"):
428 parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False,
429 help="show credential(s) used in human-readable form")
430 # registy filter option
431 if command in ("list", "show", "remove"):
432 parser.add_option("-t", "--type", dest="type", type="choice",
433 help="type filter ([all]|user|slice|authority|node|aggregate)",
434 choices=("all", "user", "slice", "authority", "node", "aggregate"),
436 if command in ("show"):
437 parser.add_option("-k","--key",dest="keys",action="append",default=[],
438 help="specify specific keys to be displayed from record")
439 if command in ("resources", "describe"):
441 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
442 help="schema type and version of resulting RSpec")
443 # disable/enable cached rspecs
444 parser.add_option("-c", "--current", dest="current", default=False,
446 help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
448 parser.add_option("-f", "--format", dest="format", type="choice",
449 help="display format ([xml]|dns|ip)", default="xml",
450 choices=("xml", "dns", "ip"))
451 #panos: a new option to define the type of information about resources a user is interested in
452 parser.add_option("-i", "--info", dest="info",
453 help="optional component information", default=None)
454 # a new option to retreive or not reservation-oriented RSpecs (leases)
455 parser.add_option("-l", "--list_leases", dest="list_leases", type="choice",
456 help="Retreive or not reservation-oriented RSpecs ([resources]|leases|all )",
457 choices=("all", "resources", "leases"), default="resources")
460 if command in ("resources", "describe", "allocate", "provision", "show", "list", "gid"):
461 parser.add_option("-o", "--output", dest="file",
462 help="output XML to file", metavar="FILE", default=None)
464 if command in ("show", "list"):
465 parser.add_option("-f", "--format", dest="format", type="choice",
466 help="display format ([text]|xml)", default="text",
467 choices=("text", "xml"))
469 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
470 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
471 choices=("xml", "xmllist", "hrnlist"))
472 if command == 'list':
473 parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
474 help="list all child records", default=False)
475 parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
476 help="gives details, like user keys", default=False)
477 if command in ("delegate"):
478 parser.add_option("-u", "--user",
479 action="store_true", dest="delegate_user", default=False,
480 help="delegate your own credentials; default if no other option is provided")
481 parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
482 metavar="slice_hrn", help="delegate cred. for slice HRN")
483 parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
484 metavar='auth_hrn', help="delegate cred for auth HRN")
485 # this primarily is a shorthand for -a my_hrn
486 parser.add_option("-p", "--pi", dest='delegate_pi', default=None, action='store_true',
487 help="delegate your PI credentials, so s.t. like -a your_hrn^")
488 parser.add_option("-A","--to-authority",dest='delegate_to_authority',action='store_true',default=False,
489 help="""by default the mandatory argument is expected to be a user,
490 use this if you mean an authority instead""")
492 if command in ("myslice"):
493 parser.add_option("-p","--password",dest='password',action='store',default=None,
494 help="specify mainfold password on the command line")
495 parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
496 metavar="slice_hrn", help="delegate cred. for slice HRN")
497 parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
498 metavar='auth_hrn', help="delegate PI cred for auth HRN")
499 parser.add_option('-d', '--delegate', dest='delegate', help="Override 'delegate' from the config file")
500 parser.add_option('-b', '--backend', dest='backend', help="Override 'backend' from the config file")
506 # Main: parse arguments and dispatch to command
508 def dispatch(self, command, command_options, command_args):
509 method=getattr(self, command, None)
511 print "Unknown command %s"%command
513 return method(command_options, command_args)
516 self.sfi_parser = self.create_parser_global()
517 (options, args) = self.sfi_parser.parse_args()
519 self.print_commands_help(options)
521 self.options = options
523 self.logger.setLevelFromOptVerbose(self.options.verbose)
526 self.logger.critical("No command given. Use -h for help.")
527 self.print_commands_help(options)
530 # complete / find unique match with command set
531 command_candidates = Candidates (commands_list)
533 command = command_candidates.only_match(input)
535 self.print_commands_help(options)
537 # second pass options parsing
539 self.command_parser = self.create_parser_command(command)
540 (command_options, command_args) = self.command_parser.parse_args(args[1:])
541 if command_options.help:
544 self.command_options = command_options
548 self.logger.debug("Command=%s" % self.command)
551 self.dispatch(command, command_options, command_args)
555 self.logger.log_exc ("sfi command %s failed"%command)
561 def read_config(self):
562 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
563 shell_config_file = os.path.join(self.options.sfi_dir,"sfi_config.sh")
565 if Config.is_ini(config_file):
566 config = Config (config_file)
568 # try upgrading from shell config format
569 fp, fn = mkstemp(suffix='sfi_config', text=True)
571 # we need to preload the sections we want parsed
572 # from the shell config
573 config.add_section('sfi')
574 # sface users should be able to use this same file to configure their stuff
575 config.add_section('sface')
576 # manifold users should be able to specify the details
577 # of their backend server here for 'sfi myslice'
578 config.add_section('myslice')
579 config.load(config_file)
581 shutil.move(config_file, shell_config_file)
583 config.save(config_file)
586 self.logger.critical("Failed to read configuration file %s"%config_file)
587 self.logger.info("Make sure to remove the export clauses and to add quotes")
588 if self.options.verbose==0:
589 self.logger.info("Re-run with -v for more details")
591 self.logger.log_exc("Could not read config file %s"%config_file)
594 self.config_instance=config
597 if (self.options.sm is not None):
598 self.sm_url = self.options.sm
599 elif hasattr(config, "SFI_SM"):
600 self.sm_url = config.SFI_SM
602 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
606 if (self.options.registry is not None):
607 self.reg_url = self.options.registry
608 elif hasattr(config, "SFI_REGISTRY"):
609 self.reg_url = config.SFI_REGISTRY
611 self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
615 if (self.options.user is not None):
616 self.user = self.options.user
617 elif hasattr(config, "SFI_USER"):
618 self.user = config.SFI_USER
620 self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
624 if (self.options.auth is not None):
625 self.authority = self.options.auth
626 elif hasattr(config, "SFI_AUTH"):
627 self.authority = config.SFI_AUTH
629 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
632 self.config_file=config_file
637 # Get various credential and spec files
639 # Establishes limiting conventions
640 # - conflates MAs and SAs
641 # - assumes last token in slice name is unique
643 # Bootstraps credentials
644 # - bootstrap user credential from self-signed certificate
645 # - bootstrap authority credential from user credential
646 # - bootstrap slice credential from user credential
649 # init self-signed cert, user credentials and gid
650 def bootstrap (self):
651 client_bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir,
653 # if -k is provided, use this to initialize private key
654 if self.options.user_private_key:
655 client_bootstrap.init_private_key_if_missing (self.options.user_private_key)
657 # trigger legacy compat code if needed
658 # the name has changed from just <leaf>.pkey to <hrn>.pkey
659 if not os.path.isfile(client_bootstrap.private_key_filename()):
660 self.logger.info ("private key not found, trying legacy name")
662 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%Xrn.unescape(get_leaf(self.user)))
663 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
664 client_bootstrap.init_private_key_if_missing (legacy_private_key)
665 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
667 self.logger.log_exc("Can't find private key ")
671 client_bootstrap.bootstrap_my_gid()
672 # extract what's needed
673 self.private_key = client_bootstrap.private_key()
674 self.my_credential_string = client_bootstrap.my_credential_string ()
675 self.my_credential = {'geni_type': 'geni_sfa',
677 'geni_value': self.my_credential_string}
678 self.my_gid = client_bootstrap.my_gid ()
679 self.client_bootstrap = client_bootstrap
682 def my_authority_credential_string(self):
683 if not self.authority:
684 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
686 return self.client_bootstrap.authority_credential_string (self.authority)
688 def authority_credential_string(self, auth_hrn):
689 return self.client_bootstrap.authority_credential_string (auth_hrn)
691 def slice_credential_string(self, name):
692 return self.client_bootstrap.slice_credential_string (name)
694 def slice_credential(self, name):
695 return {'geni_type': 'geni_sfa',
697 'geni_value': self.slice_credential_string(name)}
699 # xxx should be supported by sfaclientbootstrap as well
700 def delegate_cred(self, object_cred, hrn, type='authority'):
701 # the gid and hrn of the object we are delegating
702 if isinstance(object_cred, str):
703 object_cred = Credential(string=object_cred)
704 object_gid = object_cred.get_gid_object()
705 object_hrn = object_gid.get_hrn()
707 if not object_cred.get_privileges().get_all_delegate():
708 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
711 # the delegating user's gid
712 caller_gidfile = self.my_gid()
714 # the gid of the user who will be delegated to
715 delegee_gid = self.client_bootstrap.gid(hrn,type)
716 delegee_hrn = delegee_gid.get_hrn()
717 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
718 return dcred.save_to_string(save_parents=True)
721 # Management of the servers
726 if not hasattr (self, 'registry_proxy'):
727 self.logger.info("Contacting Registry at: %s"%self.reg_url)
728 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
729 timeout=self.options.timeout, verbose=self.options.debug)
730 return self.registry_proxy
734 if not hasattr (self, 'sliceapi_proxy'):
735 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
736 if hasattr(self.command_options,'component') and self.command_options.component:
737 # resolve the hrn at the registry
738 node_hrn = self.command_options.component
739 records = self.registry().Resolve(node_hrn, self.my_credential_string)
740 records = filter_records('node', records)
742 self.logger.warning("No such component:%r"% opts.component)
744 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
745 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
747 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
748 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
749 self.sm_url = 'http://' + self.sm_url
750 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
751 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
752 timeout=self.options.timeout, verbose=self.options.debug)
753 return self.sliceapi_proxy
755 def get_cached_server_version(self, server):
756 # check local cache first
759 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
760 cache_key = server.url + "-version"
762 cache = Cache(cache_file)
765 self.logger.info("Local cache not found at: %s" % cache_file)
768 version = cache.get(cache_key)
771 result = server.GetVersion()
772 version= ReturnValue.get_value(result)
773 # cache version for 20 minutes
774 cache.add(cache_key, version, ttl= 60*20)
775 self.logger.info("Updating cache file %s" % cache_file)
776 cache.save_to_file(cache_file)
780 ### resurrect this temporarily so we can support V1 aggregates for a while
781 def server_supports_options_arg(self, server):
783 Returns true if server support the optional call_id arg, false otherwise.
785 server_version = self.get_cached_server_version(server)
787 # xxx need to rewrite this
788 if int(server_version.get('geni_api')) >= 2:
792 def server_supports_call_id_arg(self, server):
793 server_version = self.get_cached_server_version(server)
795 if 'sfa' in server_version and 'code_tag' in server_version:
796 code_tag = server_version['code_tag']
797 code_tag_parts = code_tag.split("-")
798 version_parts = code_tag_parts[0].split(".")
799 major, minor = version_parts[0], version_parts[1]
800 rev = code_tag_parts[1]
801 if int(major) == 1 and minor == 0 and build >= 22:
805 ### ois = options if supported
806 # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
807 def ois (self, server, option_dict):
808 if self.server_supports_options_arg (server):
810 elif self.server_supports_call_id_arg (server):
811 return [ unique_call_id () ]
815 ### cis = call_id if supported - like ois
816 def cis (self, server):
817 if self.server_supports_call_id_arg (server):
818 return [ unique_call_id ]
822 ######################################## miscell utilities
823 def get_rspec_file(self, rspec):
824 if (os.path.isabs(rspec)):
827 file = os.path.join(self.options.sfi_dir, rspec)
828 if (os.path.isfile(file)):
831 self.logger.critical("No such rspec file %s"%rspec)
834 def get_record_file(self, record):
835 if (os.path.isabs(record)):
838 file = os.path.join(self.options.sfi_dir, record)
839 if (os.path.isfile(file)):
842 self.logger.critical("No such registry record file %s"%record)
846 #==========================================================================
847 # Following functions implement the commands
849 # Registry-related commands
850 #==========================================================================
852 @register_command("","")
853 def config (self, options, args):
854 "Display contents of current config"
855 print "# From configuration file %s"%self.config_file
856 flags=[ ('sfi', [ ('registry','reg_url'),
857 ('auth','authority'),
863 flags.append ( ('myslice', ['backend', 'delegate', 'platform', 'username'] ) )
865 for (section, tuples) in flags:
868 for (external_name, internal_name) in tuples:
869 print "%-20s = %s"%(external_name,getattr(self,internal_name))
872 varname="%s_%s"%(section.upper(),name.upper())
873 value=getattr(self.config_instance,varname)
874 print "%-20s = %s"%(name,value)
876 @register_command("","")
877 def version(self, options, args):
879 display an SFA server version (GetVersion)
880 or version information about sfi itself
882 if options.version_local:
883 version=version_core()
885 if options.version_registry:
886 server=self.registry()
888 server = self.sliceapi()
889 result = server.GetVersion()
890 version = ReturnValue.get_value(result)
892 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
894 pprinter = PrettyPrinter(indent=4)
895 pprinter.pprint(version)
897 @register_command("authority","")
898 def list(self, options, args):
900 list entries in named authority registry (List)
907 if options.recursive:
908 opts['recursive'] = options.recursive
910 if options.show_credential:
911 show_credentials(self.my_credential_string)
913 list = self.registry().List(hrn, self.my_credential_string, options)
915 raise Exception, "Not enough parameters for the 'list' command"
917 # filter on person, slice, site, node, etc.
918 # This really should be in the self.filter_records funct def comment...
919 list = filter_records(options.type, list)
920 terminal_render (list, options)
922 save_records_to_file(options.file, list, options.fileformat)
925 @register_command("name","")
926 def show(self, options, args):
928 show details about named registry record (Resolve)
934 # explicitly require Resolve to run in details mode
935 record_dicts = self.registry().Resolve(hrn, self.my_credential_string, {'details':True})
936 record_dicts = filter_records(options.type, record_dicts)
938 self.logger.error("No record of type %s"% options.type)
940 # user has required to focus on some keys
942 def project (record):
944 for key in options.keys:
945 try: projected[key]=record[key]
948 record_dicts = [ project (record) for record in record_dicts ]
949 records = [ Record(dict=record_dict) for record_dict in record_dicts ]
950 for record in records:
951 if (options.format == "text"): record.dump(sort=True)
952 else: print record.save_as_xml()
954 save_records_to_file(options.file, record_dicts, options.fileformat)
957 @register_command("[xml-filename]","")
958 def add(self, options, args):
959 """add record into registry (Register)
960 from command line options (recommended)
961 old-school method involving an xml file still supported"""
963 auth_cred = self.my_authority_credential_string()
964 if options.show_credential:
965 show_credentials(auth_cred)
972 record_filepath = args[0]
973 rec_file = self.get_record_file(record_filepath)
974 record_dict.update(load_record_from_file(rec_file).todict())
976 print "Cannot load record file %s"%record_filepath
979 record_dict.update(load_record_from_opts(options).todict())
980 # we should have a type by now
981 if 'type' not in record_dict :
984 # this is still planetlab dependent.. as plc will whine without that
985 # also, it's only for adding
986 if record_dict['type'] == 'user':
987 if not 'first_name' in record_dict:
988 record_dict['first_name'] = record_dict['hrn']
989 if 'last_name' not in record_dict:
990 record_dict['last_name'] = record_dict['hrn']
991 return self.registry().Register(record_dict, auth_cred)
993 @register_command("[xml-filename]","")
994 def update(self, options, args):
995 """update record into registry (Update)
996 from command line options (recommended)
997 old-school method involving an xml file still supported"""
1000 record_filepath = args[0]
1001 rec_file = self.get_record_file(record_filepath)
1002 record_dict.update(load_record_from_file(rec_file).todict())
1004 record_dict.update(load_record_from_opts(options).todict())
1005 # at the very least we need 'type' here
1006 if 'type' not in record_dict:
1010 # don't translate into an object, as this would possibly distort
1011 # user-provided data; e.g. add an 'email' field to Users
1012 if record_dict['type'] == "user":
1013 if record_dict['hrn'] == self.user:
1014 cred = self.my_credential_string
1016 cred = self.my_authority_credential_string()
1017 elif record_dict['type'] in ["slice"]:
1019 cred = self.slice_credential_string(record_dict['hrn'])
1020 except ServerException, e:
1021 # XXX smbaker -- once we have better error return codes, update this
1022 # to do something better than a string compare
1023 if "Permission error" in e.args[0]:
1024 cred = self.my_authority_credential_string()
1027 elif record_dict['type'] in ["authority"]:
1028 cred = self.my_authority_credential_string()
1029 elif record_dict['type'] == 'node':
1030 cred = self.my_authority_credential_string()
1032 raise "unknown record type" + record_dict['type']
1033 if options.show_credential:
1034 show_credentials(cred)
1035 return self.registry().Update(record_dict, cred)
1037 @register_command("hrn","")
1038 def remove(self, options, args):
1039 "remove registry record by name (Remove)"
1040 auth_cred = self.my_authority_credential_string()
1048 if options.show_credential:
1049 show_credentials(auth_cred)
1050 return self.registry().Remove(hrn, auth_cred, type)
1052 # ==================================================================
1053 # Slice-related commands
1054 # ==================================================================
1056 # show rspec for named slice
1057 @register_command("","")
1058 def resources(self, options, args):
1060 discover available resources (ListResources)
1062 server = self.sliceapi()
1065 creds = [self.my_credential]
1066 if options.delegate:
1067 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
1068 if options.show_credential:
1069 show_credentials(creds)
1071 # no need to check if server accepts the options argument since the options has
1072 # been a required argument since v1 API
1074 # always send call_id to v2 servers
1075 api_options ['call_id'] = unique_call_id()
1076 # ask for cached value if available
1077 api_options ['cached'] = True
1079 api_options['info'] = options.info
1080 if options.list_leases:
1081 api_options['list_leases'] = options.list_leases
1083 if options.current == True:
1084 api_options['cached'] = False
1086 api_options['cached'] = True
1087 if options.rspec_version:
1088 version_manager = VersionManager()
1089 server_version = self.get_cached_server_version(server)
1090 if 'sfa' in server_version:
1091 # just request the version the client wants
1092 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1094 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'}
1096 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'}
1097 result = server.ListResources (creds, api_options)
1098 value = ReturnValue.get_value(result)
1099 if self.options.raw:
1100 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1101 if options.file is not None:
1102 save_rspec_to_file(value, options.file)
1103 if (self.options.raw is None) and (options.file is None):
1104 display_rspec(value, options.format)
1108 @register_command("slice_hrn","")
1109 def describe(self, options, args):
1111 shows currently allocated/provisioned resources
1112 of the named slice or set of slivers (Describe)
1114 server = self.sliceapi()
1117 creds = [self.slice_credential(args[0])]
1118 if options.delegate:
1119 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
1120 if options.show_credential:
1121 show_credentials(creds)
1123 api_options = {'call_id': unique_call_id(),
1125 'info': options.info,
1126 'list_leases': options.list_leases,
1127 'geni_rspec_version': {'type': 'geni', 'version': '3'},
1129 if options.rspec_version:
1130 version_manager = VersionManager()
1131 server_version = self.get_cached_server_version(server)
1132 if 'sfa' in server_version:
1133 # just request the version the client wants
1134 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1136 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'}
1137 urn = Xrn(args[0], type='slice').get_urn()
1138 result = server.Describe([urn], creds, api_options)
1139 value = ReturnValue.get_value(result)
1140 if self.options.raw:
1141 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1142 if options.file is not None:
1143 save_rspec_to_file(value, options.file)
1144 if (self.options.raw is None) and (options.file is None):
1145 display_rspec(value, options.format)
1146 #adding resources from bonfire
1147 bonfire_resources = bonfire.bonsources()
1148 display_rspec(bonfire_resources, options.format)
1152 @register_command("slice_hrn","")
1153 def delete(self, options, args):
1155 de-allocate and de-provision all or named slivers of the slice (Delete)
1157 server = self.sliceapi()
1161 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1164 slice_cred = self.slice_credential(slice_hrn)
1165 creds = [slice_cred]
1167 # options and call_id when supported
1169 api_options ['call_id'] = unique_call_id()
1170 if options.show_credential:
1171 show_credentials(creds)
1172 result = server.Delete([slice_urn], creds, *self.ois(server, api_options ) )
1173 value = ReturnValue.get_value(result)
1174 if self.options.raw:
1175 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1180 @register_command("slice_hrn rspec","")
1181 def allocate(self, options, args):
1183 allocate resources to the named slice (Allocate)
1185 server = self.sliceapi()
1186 server_version = self.get_cached_server_version(server)
1188 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1191 creds = [self.slice_credential(slice_hrn)]
1193 delegated_cred = None
1194 if server_version.get('interface') == 'slicemgr':
1195 # delegate our cred to the slice manager
1196 # do not delegate cred to slicemgr...not working at the moment
1198 #if server_version.get('hrn'):
1199 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1200 #elif server_version.get('urn'):
1201 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1203 if options.show_credential:
1204 show_credentials(creds)
1207 rspec_file = self.get_rspec_file(args[1])
1208 rspec = open(rspec_file).read()
1210 api_options ['call_id'] = unique_call_id()
1214 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1215 if slice_records and 'reg-researchers' in slice_records[0] and slice_records[0]['reg-researchers']!=[]:
1216 slice_record = slice_records[0]
1217 user_hrns = slice_record['reg-researchers']
1218 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1219 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1220 sfa_users = sfa_users_arg(user_records, slice_record)
1221 geni_users = pg_users_arg(user_records)
1223 api_options['sfa_users'] = sfa_users
1224 api_options['geni_users'] = geni_users
1226 result = server.Allocate(slice_urn, creds, rspec, api_options)
1227 value = ReturnValue.get_value(result)
1228 if self.options.raw:
1229 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1230 if options.file is not None:
1231 save_rspec_to_file (value, options.file)
1232 if (self.options.raw is None) and (options.file is None):
1237 @register_command("slice_hrn","")
1238 def provision(self, options, args):
1240 provision already allocated resources of named slice (Provision)
1242 server = self.sliceapi()
1243 server_version = self.get_cached_server_version(server)
1245 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1248 creds = [self.slice_credential(slice_hrn)]
1249 delegated_cred = None
1250 if server_version.get('interface') == 'slicemgr':
1251 # delegate our cred to the slice manager
1252 # do not delegate cred to slicemgr...not working at the moment
1254 #if server_version.get('hrn'):
1255 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1256 #elif server_version.get('urn'):
1257 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1259 if options.show_credential:
1260 show_credentials(creds)
1263 api_options ['call_id'] = unique_call_id()
1265 # set the requtested rspec version
1266 version_manager = VersionManager()
1267 rspec_version = version_manager._get_version('geni', '3').to_dict()
1268 api_options['geni_rspec_version'] = rspec_version
1271 # need to pass along user keys to the aggregate.
1273 # { urn: urn:publicid:IDN+emulab.net+user+alice
1274 # keys: [<ssh key A>, <ssh key B>]
1277 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1278 if slice_records and 'reg-researchers' in slice_records[0] and slice_records[0]['reg-researchers']!=[]:
1279 slice_record = slice_records[0]
1280 user_hrns = slice_record['reg-researchers']
1281 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1282 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1283 users = pg_users_arg(user_records)
1285 api_options['geni_users'] = users
1286 result = server.Provision([slice_urn], creds, api_options)
1287 value = ReturnValue.get_value(result)
1288 if self.options.raw:
1289 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1290 if options.file is not None:
1291 save_rspec_to_file (value, options.file)
1292 if (self.options.raw is None) and (options.file is None):
1296 @register_command("slice_hrn","")
1297 def status(self, options, args):
1299 retrieve the status of the slivers belonging to tne named slice (Status)
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]
1311 # options and call_id when supported
1313 api_options['call_id']=unique_call_id()
1314 if options.show_credential:
1315 show_credentials(creds)
1316 result = server.Status([slice_urn], creds, *self.ois(server,api_options))
1317 value = ReturnValue.get_value(result)
1318 if self.options.raw:
1319 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1322 # Thierry: seemed to be missing
1325 @register_command("slice_hrn action","")
1326 def action(self, options, args):
1328 Perform the named operational action on these slivers
1330 server = self.sliceapi()
1335 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1337 slice_cred = self.slice_credential(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)
1343 result = server.PerformOperationalAction([slice_urn], creds, action , api_options)
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 @register_command("slice_hrn time","")
1352 def renew(self, options, args):
1354 renew slice (RenewSliver)
1356 server = self.sliceapi()
1360 [ slice_hrn, input_time ] = args
1362 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1363 # time: don't try to be smart on the time format, server-side will
1365 slice_cred = self.slice_credential(args[0])
1366 creds = [slice_cred]
1367 # options and call_id when supported
1369 api_options['call_id']=unique_call_id()
1370 if options.show_credential:
1371 show_credentials(creds)
1372 result = server.Renew([slice_urn], creds, input_time, *self.ois(server,api_options))
1373 value = ReturnValue.get_value(result)
1374 if self.options.raw:
1375 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1381 @register_command("slice_hrn","")
1382 def shutdown(self, options, args):
1384 shutdown named slice (Shutdown)
1386 server = self.sliceapi()
1389 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1391 slice_cred = self.slice_credential(slice_hrn)
1392 creds = [slice_cred]
1393 result = server.Shutdown(slice_urn, creds)
1394 value = ReturnValue.get_value(result)
1395 if self.options.raw:
1396 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1402 @register_command("[name]","")
1403 def gid(self, options, args):
1405 Create a GID (CreateGid)
1410 target_hrn = args[0]
1411 my_gid_string = open(self.client_bootstrap.my_gid()).read()
1412 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, my_gid_string)
1414 filename = options.file
1416 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1417 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1418 GID(string=gid).save_to_file(filename)
1420 ####################
1421 @register_command("to_hrn","""$ sfi delegate -u -p -s ple.inria.heartbeat -s ple.inria.omftest ple.upmc.slicebrowser
1423 will locally create a set of delegated credentials for the benefit of ple.upmc.slicebrowser
1424 the set of credentials in the scope for this call would be
1425 (*) ple.inria.thierry_parmentelat.user_for_ple.upmc.slicebrowser.user.cred
1427 (*) ple.inria.pi_for_ple.upmc.slicebrowser.user.cred
1429 (*) ple.inria.heartbeat.slice_for_ple.upmc.slicebrowser.user.cred
1430 (*) ple.inria.omftest.slice_for_ple.upmc.slicebrowser.user.cred
1431 because of the two -s options
1434 def delegate (self, options, args):
1436 (locally) create delegate credential for use by given hrn
1437 make sure to check for 'sfi myslice' instead if you plan
1444 # support for several delegations in the same call
1445 # so first we gather the things to do
1447 for slice_hrn in options.delegate_slices:
1448 message="%s.slice"%slice_hrn
1449 original = self.slice_credential_string(slice_hrn)
1450 tuples.append ( (message, original,) )
1451 if options.delegate_pi:
1452 my_authority=self.authority
1453 message="%s.pi"%my_authority
1454 original = self.my_authority_credential_string()
1455 tuples.append ( (message, original,) )
1456 for auth_hrn in options.delegate_auths:
1457 message="%s.auth"%auth_hrn
1458 original=self.authority_credential_string(auth_hrn)
1459 tuples.append ( (message, original, ) )
1460 # if nothing was specified at all at this point, let's assume -u
1461 if not tuples: options.delegate_user=True
1463 if options.delegate_user:
1464 message="%s.user"%self.user
1465 original = self.my_credential_string
1466 tuples.append ( (message, original, ) )
1468 # default type for beneficial is user unless -A
1469 if options.delegate_to_authority: to_type='authority'
1470 else: to_type='user'
1472 # let's now handle all this
1473 # it's all in the filenaming scheme
1474 for (message,original) in tuples:
1475 delegated_string = self.client_bootstrap.delegate_credential_string(original, to_hrn, to_type)
1476 delegated_credential = Credential (string=delegated_string)
1477 filename = os.path.join ( self.options.sfi_dir,
1478 "%s_for_%s.%s.cred"%(message,to_hrn,to_type))
1479 delegated_credential.save_to_file(filename, save_parents=True)
1480 self.logger.info("delegated credential for %s to %s and wrote to %s"%(message,to_hrn,filename))
1482 ####################
1483 @register_command("","""$ less +/myslice sfi_config
1485 backend = http://manifold.pl.sophia.inria.fr:7080
1486 # the HRN that myslice uses, so that we are delegating to
1487 delegate = ple.upmc.slicebrowser
1488 # platform - this is a myslice concept
1490 # username - as of this writing (May 2013) a simple login name
1494 will first collect the slices that you are part of, then make sure
1495 all your credentials are up-to-date (read: refresh expired ones)
1496 then compute delegated credentials for user 'ple.upmc.slicebrowser'
1497 and upload them all on myslice backend, using 'platform' and 'user'.
1498 A password will be prompted for the upload part.
1500 $ sfi -v myslice -- or sfi -vv myslice
1501 same but with more and more verbosity
1503 $ sfi m -b http://mymanifold.foo.com:7080/
1504 is synonym to sfi myslice as no other command starts with an 'm'
1505 and uses a custom backend for this one call
1507 ) # register_command
1508 def myslice (self, options, args):
1510 """ This helper is for refreshing your credentials at myslice; it will
1511 * compute all the slices that you currently have credentials on
1512 * refresh all your credentials (you as a user and pi, your slices)
1513 * upload them to the manifold backend server
1514 for last phase, sfi_config is read to look for the [myslice] section,
1515 and namely the 'backend', 'delegate' and 'user' settings"""
1521 # enable info by default
1522 self.logger.setLevelFromOptVerbose(self.options.verbose+1)
1523 ### the rough sketch goes like this
1524 # (0) produce a p12 file
1525 self.client_bootstrap.my_pkcs12()
1527 # (a) rain check for sufficient config in sfi_config
1529 myslice_keys=[ 'backend', 'delegate', 'platform', 'username']
1530 for key in myslice_keys:
1532 # oct 2013 - I'm finding myself juggling with config files
1533 # so a couple of command-line options can now override config
1534 if hasattr(options,key) and getattr(options,key) is not None:
1535 value=getattr(options,key)
1537 full_key="MYSLICE_" + key.upper()
1538 value=getattr(self.config_instance,full_key,None)
1539 if value: myslice_dict[key]=value
1540 else: print "Unsufficient config, missing key %s in [myslice] section of sfi_config"%key
1541 if len(myslice_dict) != len(myslice_keys):
1544 # (b) figure whether we are PI for the authority where we belong
1545 self.logger.info("Resolving our own id %s"%self.user)
1546 my_records=self.registry().Resolve(self.user,self.my_credential_string)
1547 if len(my_records)!=1: print "Cannot Resolve %s -- exiting"%self.user; sys.exit(1)
1548 my_record=my_records[0]
1549 my_auths_all = my_record['reg-pi-authorities']
1550 self.logger.info("Found %d authorities that we are PI for"%len(my_auths_all))
1551 self.logger.debug("They are %s"%(my_auths_all))
1553 my_auths = my_auths_all
1554 if options.delegate_auths:
1555 my_auths = list(set(my_auths_all).intersection(set(options.delegate_auths)))
1556 self.logger.debug("Restricted to user-provided auths"%(my_auths))
1558 # (c) get the set of slices that we are in
1559 my_slices_all=my_record['reg-slices']
1560 self.logger.info("Found %d slices that we are member of"%len(my_slices_all))
1561 self.logger.debug("They are: %s"%(my_slices_all))
1563 my_slices = my_slices_all
1564 # if user provided slices, deal only with these - if they are found
1565 if options.delegate_slices:
1566 my_slices = list(set(my_slices_all).intersection(set(options.delegate_slices)))
1567 self.logger.debug("Restricted to user-provided slices: %s"%(my_slices))
1569 # (d) make sure we have *valid* credentials for all these
1571 hrn_credentials.append ( (self.user, 'user', self.my_credential_string,) )
1572 for auth_hrn in my_auths:
1573 hrn_credentials.append ( (auth_hrn, 'auth', self.authority_credential_string(auth_hrn),) )
1574 for slice_hrn in my_slices:
1575 hrn_credentials.append ( (slice_hrn, 'slice', self.slice_credential_string (slice_hrn),) )
1577 # (e) check for the delegated version of these
1578 # xxx todo add an option -a/-A? like for 'sfi delegate' for when we ever
1579 # switch to myslice using an authority instead of a user
1580 delegatee_type='user'
1581 delegatee_hrn=myslice_dict['delegate']
1582 hrn_delegated_credentials = []
1583 for (hrn, htype, credential) in hrn_credentials:
1584 delegated_credential = self.client_bootstrap.delegate_credential_string (credential, delegatee_hrn, delegatee_type)
1585 # save these so user can monitor what she's uploaded
1586 filename = os.path.join ( self.options.sfi_dir,
1587 "%s.%s_for_%s.%s.cred"%(hrn,htype,delegatee_hrn,delegatee_type))
1588 with file(filename,'w') as f:
1589 f.write(delegated_credential)
1590 self.logger.debug("(Over)wrote %s"%filename)
1591 hrn_delegated_credentials.append ((hrn, htype, delegated_credential, filename, ))
1593 # (f) and finally upload them to manifold server
1594 # xxx todo add an option so the password can be set on the command line
1595 # (but *NOT* in the config file) so other apps can leverage this
1596 self.logger.info("Uploading on backend at %s"%myslice_dict['backend'])
1597 uploader = ManifoldUploader (logger=self.logger,
1598 url=myslice_dict['backend'],
1599 platform=myslice_dict['platform'],
1600 username=myslice_dict['username'],
1601 password=options.password)
1602 uploader.prompt_all()
1603 (count_all,count_success)=(0,0)
1604 for (hrn,htype,delegated_credential,filename) in hrn_delegated_credentials:
1606 inspect=Credential(string=delegated_credential)
1607 expire_datetime=inspect.get_expiration()
1608 message="%s (%s) [exp:%s]"%(hrn,htype,expire_datetime)
1609 if uploader.upload(delegated_credential,message=message):
1612 self.logger.info("Successfully uploaded %d/%d credentials"%(count_success,count_all))
1614 # at first I thought we would want to save these,
1615 # like 'sfi delegate does' but on second thought
1616 # it is probably not helpful as people would not
1617 # need to run 'sfi delegate' at all anymore
1618 if count_success != count_all: sys.exit(1)
1621 @register_command("cred","")
1622 def trusted(self, options, args):
1624 return the trusted certs at this interface (get_trusted_certs)
1626 trusted_certs = self.registry().get_trusted_certs()
1627 for trusted_cert in trusted_certs:
1628 print "\n===========================================================\n"
1629 gid = GID(string=trusted_cert)
1631 cert = Certificate(string=trusted_cert)
1632 self.logger.debug('Sfi.trusted -> %r'%cert.get_subject())
1633 print "Certificate:\n%s\n\n"%trusted_cert