2 # sfi.py - basic SFA command-line client
3 # this module is also used in sfascan
17 from lxml import etree
18 from StringIO import StringIO
19 from optparse import OptionParser
20 from pprint import PrettyPrinter
21 from tempfile import mkstemp
23 from sfa.trust.certificate import Keypair, Certificate
24 from sfa.trust.gid import GID
25 from sfa.trust.credential import Credential
26 from sfa.trust.sfaticket import SfaTicket
28 from sfa.util.faults import SfaInvalidArgument
29 from sfa.util.sfalogging import sfi_logger
30 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn, Xrn
31 from sfa.util.config import Config
32 from sfa.util.version import version_core
33 from sfa.util.cache import Cache
35 from sfa.storage.record import Record
37 from sfa.rspecs.rspec import RSpec
38 from sfa.rspecs.rspec_converter import RSpecConverter
39 from sfa.rspecs.version_manager import VersionManager
41 from sfa.client.sfaclientlib import SfaClientBootstrap
42 from sfa.client.sfaserverproxy import SfaServerProxy, ServerException
43 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
44 from sfa.client.return_value import ReturnValue
45 from sfa.client.candidates import Candidates
46 from sfa.client.manifolduploader import ManifoldUploader
50 from sfa.client.common import optparse_listvalue_callback, optparse_dictvalue_callback, \
51 terminal_render, filter_records
54 def display_rspec(rspec, format='rspec'):
56 tree = etree.parse(StringIO(rspec))
58 result = root.xpath("./network/site/node/hostname/text()")
59 elif format in ['ip']:
60 # The IP address is not yet part of the new RSpec
61 # so this doesn't do anything yet.
62 tree = etree.parse(StringIO(rspec))
64 result = root.xpath("./network/site/node/ipv4/text()")
71 def display_list(results):
72 for result in results:
75 def display_records(recordList, dump=False):
76 ''' Print all fields in the record'''
77 for record in recordList:
78 display_record(record, dump)
80 def display_record(record, dump=False):
82 record.dump(sort=True)
84 info = record.getdict()
85 print "%s (%s)" % (info['hrn'], info['type'])
89 def filter_records(type, records):
91 for record in records:
92 if (record['type'] == type) or (type == "all"):
93 filtered_records.append(record)
94 return filtered_records
97 def credential_printable (cred):
98 credential=Credential(cred=cred)
100 result += credential.get_summary_tostring()
102 rights = credential.get_privileges()
103 result += "type=%s\n" % credential.type
104 result += "version=%s\n" % credential.version
105 result += "rights=%s\n"%rights
108 def show_credentials (cred_s):
109 if not isinstance (cred_s,list): cred_s = [cred_s]
111 print "Using Credential %s"%credential_printable(cred)
114 def save_raw_to_file(var, filename, format="text", banner=None):
116 # if filename is "-", send it to stdout
119 f = open(filename, "w")
124 elif format == "pickled":
125 f.write(pickle.dumps(var))
126 elif format == "json":
127 if hasattr(json, "dumps"):
128 f.write(json.dumps(var)) # python 2.6
130 f.write(json.write(var)) # python 2.5
132 # this should never happen
133 print "unknown output format", format
135 f.write('\n'+banner+"\n")
137 def save_rspec_to_file(rspec, filename):
138 if not filename.endswith(".rspec"):
139 filename = filename + ".rspec"
140 f = open(filename, 'w')
145 def save_records_to_file(filename, record_dicts, format="xml"):
148 for record_dict in record_dicts:
150 save_record_to_file(filename + "." + str(index), record_dict)
152 save_record_to_file(filename, record_dict)
154 elif format == "xmllist":
155 f = open(filename, "w")
156 f.write("<recordlist>\n")
157 for record_dict in record_dicts:
158 record_obj=Record(dict=record_dict)
159 f.write('<record hrn="' + record_obj.hrn + '" type="' + record_obj.type + '" />\n')
160 f.write("</recordlist>\n")
162 elif format == "hrnlist":
163 f = open(filename, "w")
164 for record_dict in record_dicts:
165 record_obj=Record(dict=record_dict)
166 f.write(record_obj.hrn + "\n")
169 # this should never happen
170 print "unknown output format", format
172 def save_record_to_file(filename, record_dict):
173 record = Record(dict=record_dict)
174 xml = record.save_as_xml()
175 f=codecs.open(filename, encoding='utf-8',mode="w")
180 # minimally check a key argument
181 def check_ssh_key (key):
182 good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$'
183 return re.match(good_ssh_key, key, re.IGNORECASE)
186 def load_record_from_opts(options):
188 if hasattr(options, 'xrn') and options.xrn:
189 if hasattr(options, 'type') and options.type:
190 xrn = Xrn(options.xrn, options.type)
192 xrn = Xrn(options.xrn)
193 record_dict['urn'] = xrn.get_urn()
194 record_dict['hrn'] = xrn.get_hrn()
195 record_dict['type'] = xrn.get_type()
196 if hasattr(options, 'key') and options.key:
198 pubkey = open(options.key, 'r').read()
201 if not check_ssh_key (pubkey):
202 raise SfaInvalidArgument(name='key',msg="Could not find file, or wrong key format")
203 record_dict['keys'] = [pubkey]
204 if hasattr(options, 'slices') and options.slices:
205 record_dict['slices'] = options.slices
206 if hasattr(options, 'researchers') and options.researchers:
207 record_dict['researcher'] = options.researchers
208 if hasattr(options, 'email') and options.email:
209 record_dict['email'] = options.email
210 if hasattr(options, 'pis') and options.pis:
211 record_dict['pi'] = options.pis
213 # handle extra settings
214 record_dict.update(options.extras)
216 return Record(dict=record_dict)
218 def load_record_from_file(filename):
219 f=codecs.open(filename, encoding="utf-8", mode="r")
220 xml_string = f.read()
222 return Record(xml=xml_string)
226 def unique_call_id(): return uuid.uuid4().urn
228 ########## a simple model for maintaing 3 doc attributes per command (instead of just one)
229 # essentially for the methods that implement a subcommand like sfi list
230 # we need to keep track of
231 # (*) doc a few lines that tell what it does, still located in __doc__
232 # (*) args_string a simple one-liner that describes mandatory arguments
233 # (*) example well, one or several releant examples
235 # since __doc__ only accounts for one, we use this simple mechanism below
236 # however we keep doc in place for easier migration
238 from functools import wraps
240 # we use a list as well as a dict so we can keep track of the order
244 def register_command (args_string, example):
246 name=getattr(m,'__name__')
247 doc=getattr(m,'__doc__',"-- missing doc --")
248 doc=doc.strip(" \t\n")
249 commands_list.append(name)
250 commands_dict[name]=(doc, args_string, example)
252 def new_method (*args, **kwds): return m(*args, **kwds)
260 # dirty hack to make this class usable from the outside
261 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user', 'user_private_key']
264 def default_sfi_dir ():
265 if os.path.isfile("./sfi_config"):
268 return os.path.expanduser("~/.sfi/")
270 # dummy to meet Sfi's expectations for its 'options' field
271 # i.e. s/t we can do setattr on
275 def __init__ (self,options=None):
276 if options is None: options=Sfi.DummyOptions()
277 for opt in Sfi.required_options:
278 if not hasattr(options,opt): setattr(options,opt,None)
279 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
280 self.options = options
282 self.authority = None
283 self.logger = sfi_logger
284 self.logger.enable_console()
285 ### various auxiliary material that we keep at hand
287 # need to call this other than just 'config' as we have a command/method with that name
288 self.config_instance=None
289 self.config_file=None
290 self.client_bootstrap=None
292 ### suitable if no reasonable command has been provided
293 def print_commands_help (self, options):
294 verbose=getattr(options,'verbose')
295 format3="%18s %-15s %s"
298 print format3%("command","cmd_args","description")
302 self.create_parser_global().print_help()
303 # preserve order from the code
304 for command in commands_list:
305 (doc, args_string, example) = commands_dict[command]
308 doc=doc.replace("\n","\n"+35*' ')
309 print format3%(command,args_string,doc)
311 self.create_parser_command(command).print_help()
313 ### now if a known command was found we can be more verbose on that one
314 def print_help (self):
315 print "==================== Generic sfi usage"
316 self.sfi_parser.print_help()
317 (doc,_,example)=commands_dict[self.command]
318 print "\n==================== Purpose of %s"%self.command
320 print "\n==================== Specific usage for %s"%self.command
321 self.command_parser.print_help()
323 print "\n==================== %s example(s)"%self.command
326 def create_parser_global(self):
327 # Generate command line parser
328 parser = OptionParser(add_help_option=False,
329 usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
330 description="Commands: %s"%(" ".join(commands_list)))
331 parser.add_option("-r", "--registry", dest="registry",
332 help="root registry", metavar="URL", default=None)
333 parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
334 help="slice API - in general a SM URL, but can be used to talk to an aggregate")
335 parser.add_option("-R", "--raw", dest="raw", default=None,
336 help="Save raw, unparsed server response to a file")
337 parser.add_option("", "--rawformat", dest="rawformat", type="choice",
338 help="raw file format ([text]|pickled|json)", default="text",
339 choices=("text","pickled","json"))
340 parser.add_option("", "--rawbanner", dest="rawbanner", default=None,
341 help="text string to write before and after raw output")
342 parser.add_option("-d", "--dir", dest="sfi_dir",
343 help="config & working directory - default is %default",
344 metavar="PATH", default=Sfi.default_sfi_dir())
345 parser.add_option("-u", "--user", dest="user",
346 help="user name", metavar="HRN", default=None)
347 parser.add_option("-a", "--auth", dest="auth",
348 help="authority name", metavar="HRN", default=None)
349 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
350 help="verbose mode - cumulative")
351 parser.add_option("-D", "--debug",
352 action="store_true", dest="debug", default=False,
353 help="Debug (xml-rpc) protocol messages")
354 # would it make sense to use ~/.ssh/id_rsa as a default here ?
355 parser.add_option("-k", "--private-key",
356 action="store", dest="user_private_key", default=None,
357 help="point to the private key file to use if not yet installed in sfi_dir")
358 parser.add_option("-t", "--timeout", dest="timeout", default=None,
359 help="Amout of time to wait before timing out the request")
360 parser.add_option("-h", "--help",
361 action="store_true", dest="help", default=False,
362 help="one page summary on commands & exit")
363 parser.disable_interspersed_args()
368 def create_parser_command(self, command):
369 if command not in commands_dict:
370 msg="Invalid command\n"
372 msg += ','.join(commands_list)
373 self.logger.critical(msg)
376 # retrieve args_string
377 (_, args_string, __) = commands_dict[command]
379 parser = OptionParser(add_help_option=False,
380 usage="sfi [sfi_options] %s [cmd_options] %s"
381 % (command, args_string))
382 parser.add_option ("-h","--help",dest='help',action='store_true',default=False,
383 help="Summary of one command usage")
385 if command in ("config"):
386 parser.add_option('-m', '--myslice', dest='myslice', action='store_true', default=False,
387 help='how myslice config variables as well')
389 if command in ("version"):
390 parser.add_option("-R","--registry-version",
391 action="store_true", dest="version_registry", default=False,
392 help="probe registry version instead of sliceapi")
393 parser.add_option("-l","--local",
394 action="store_true", dest="version_local", default=False,
395 help="display version of the local client")
397 if command in ("add", "update"):
398 parser.add_option('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
399 parser.add_option('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
400 parser.add_option('-e', '--email', dest='email', default="", help="email (mandatory for users)")
401 parser.add_option('-k', '--key', dest='key', metavar='<key>', help='public key string or file',
403 parser.add_option('-s', '--slices', dest='slices', metavar='<slices>', help='Set/replace slice xrns',
404 default='', type="str", action='callback', callback=optparse_listvalue_callback)
405 parser.add_option('-r', '--researchers', dest='researchers', metavar='<researchers>',
406 help='Set/replace slice researchers', default='', type="str", action='callback',
407 callback=optparse_listvalue_callback)
408 parser.add_option('-p', '--pis', dest='pis', metavar='<PIs>', help='Set/replace Principal Investigators/Project Managers',
409 default='', type="str", action='callback', callback=optparse_listvalue_callback)
410 parser.add_option ('-X','--extra',dest='extras',default={},type='str',metavar="<EXTRA_ASSIGNS>",
411 action="callback", callback=optparse_dictvalue_callback, nargs=1,
412 help="set extra/testbed-dependent flags, e.g. --extra enabled=true")
414 # user specifies remote aggregate/sm/component
415 if command in ("resources", "describe", "allocate", "provision", "delete", "allocate", "provision",
416 "action", "shutdown", "renew", "status"):
417 parser.add_option("-d", "--delegate", dest="delegate", default=None,
419 help="Include a credential delegated to the user's root"+\
420 "authority in set of credentials for this call")
422 # show_credential option
423 if command in ("list","resources", "describe", "provision", "allocate", "add","update","remove","slices","delete","status","renew"):
424 parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False,
425 help="show credential(s) used in human-readable form")
426 # registy filter option
427 if command in ("list", "show", "remove"):
428 parser.add_option("-t", "--type", dest="type", type="choice",
429 help="type filter ([all]|user|slice|authority|node|aggregate)",
430 choices=("all", "user", "slice", "authority", "node", "aggregate"),
432 if command in ("show"):
433 parser.add_option("-k","--key",dest="keys",action="append",default=[],
434 help="specify specific keys to be displayed from record")
435 if command in ("resources", "describe"):
437 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
438 help="schema type and version of resulting RSpec")
439 # disable/enable cached rspecs
440 parser.add_option("-c", "--current", dest="current", default=False,
442 help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
444 parser.add_option("-f", "--format", dest="format", type="choice",
445 help="display format ([xml]|dns|ip)", default="xml",
446 choices=("xml", "dns", "ip"))
447 #panos: a new option to define the type of information about resources a user is interested in
448 parser.add_option("-i", "--info", dest="info",
449 help="optional component information", default=None)
450 # a new option to retreive or not reservation-oriented RSpecs (leases)
451 parser.add_option("-l", "--list_leases", dest="list_leases", type="choice",
452 help="Retreive or not reservation-oriented RSpecs ([resources]|leases|all )",
453 choices=("all", "resources", "leases"), default="resources")
456 if command in ("resources", "describe", "allocate", "provision", "show", "list", "gid"):
457 parser.add_option("-o", "--output", dest="file",
458 help="output XML to file", metavar="FILE", default=None)
460 if command in ("show", "list"):
461 parser.add_option("-f", "--format", dest="format", type="choice",
462 help="display format ([text]|xml)", default="text",
463 choices=("text", "xml"))
465 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
466 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
467 choices=("xml", "xmllist", "hrnlist"))
468 if command == 'list':
469 parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
470 help="list all child records", default=False)
471 parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
472 help="gives details, like user keys", default=False)
473 if command in ("delegate"):
474 parser.add_option("-u", "--user",
475 action="store_true", dest="delegate_user", default=False,
476 help="delegate your own credentials; default if no other option is provided")
477 parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
478 metavar="slice_hrn", help="delegate cred. for slice HRN")
479 parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
480 metavar='auth_hrn', help="delegate cred for auth HRN")
481 # this primarily is a shorthand for -a my_hrn
482 parser.add_option("-p", "--pi", dest='delegate_pi', default=None, action='store_true',
483 help="delegate your PI credentials, so s.t. like -a your_hrn^")
484 parser.add_option("-A","--to-authority",dest='delegate_to_authority',action='store_true',default=False,
485 help="""by default the mandatory argument is expected to be a user,
486 use this if you mean an authority instead""")
488 if command in ("myslice"):
489 parser.add_option("-p","--password",dest='password',action='store',default=None,
490 help="specify mainfold password on the command line")
491 parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
492 metavar="slice_hrn", help="delegate cred. for slice HRN")
493 parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
494 metavar='auth_hrn', help="delegate PI cred for auth HRN")
500 # Main: parse arguments and dispatch to command
502 def dispatch(self, command, command_options, command_args):
503 method=getattr(self, command, None)
505 print "Unknown command %s"%command
507 return method(command_options, command_args)
510 self.sfi_parser = self.create_parser_global()
511 (options, args) = self.sfi_parser.parse_args()
513 self.print_commands_help(options)
515 self.options = options
517 self.logger.setLevelFromOptVerbose(self.options.verbose)
520 self.logger.critical("No command given. Use -h for help.")
521 self.print_commands_help(options)
524 # complete / find unique match with command set
525 command_candidates = Candidates (commands_list)
527 command = command_candidates.only_match(input)
529 self.print_commands_help(options)
531 # second pass options parsing
533 self.command_parser = self.create_parser_command(command)
534 (command_options, command_args) = self.command_parser.parse_args(args[1:])
535 if command_options.help:
538 self.command_options = command_options
542 self.logger.debug("Command=%s" % self.command)
545 self.dispatch(command, command_options, command_args)
549 self.logger.log_exc ("sfi command %s failed"%command)
555 def read_config(self):
556 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
557 shell_config_file = os.path.join(self.options.sfi_dir,"sfi_config.sh")
559 if Config.is_ini(config_file):
560 config = Config (config_file)
562 # try upgrading from shell config format
563 fp, fn = mkstemp(suffix='sfi_config', text=True)
565 # we need to preload the sections we want parsed
566 # from the shell config
567 config.add_section('sfi')
568 # sface users should be able to use this same file to configure their stuff
569 config.add_section('sface')
570 # manifold users should be able to specify the details
571 # of their backend server here for 'sfi myslice'
572 config.add_section('myslice')
573 config.load(config_file)
575 shutil.move(config_file, shell_config_file)
577 config.save(config_file)
580 self.logger.critical("Failed to read configuration file %s"%config_file)
581 self.logger.info("Make sure to remove the export clauses and to add quotes")
582 if self.options.verbose==0:
583 self.logger.info("Re-run with -v for more details")
585 self.logger.log_exc("Could not read config file %s"%config_file)
588 self.config_instance=config
591 if (self.options.sm is not None):
592 self.sm_url = self.options.sm
593 elif hasattr(config, "SFI_SM"):
594 self.sm_url = config.SFI_SM
596 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
600 if (self.options.registry is not None):
601 self.reg_url = self.options.registry
602 elif hasattr(config, "SFI_REGISTRY"):
603 self.reg_url = config.SFI_REGISTRY
605 self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
609 if (self.options.user is not None):
610 self.user = self.options.user
611 elif hasattr(config, "SFI_USER"):
612 self.user = config.SFI_USER
614 self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
618 if (self.options.auth is not None):
619 self.authority = self.options.auth
620 elif hasattr(config, "SFI_AUTH"):
621 self.authority = config.SFI_AUTH
623 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
626 self.config_file=config_file
631 # Get various credential and spec files
633 # Establishes limiting conventions
634 # - conflates MAs and SAs
635 # - assumes last token in slice name is unique
637 # Bootstraps credentials
638 # - bootstrap user credential from self-signed certificate
639 # - bootstrap authority credential from user credential
640 # - bootstrap slice credential from user credential
643 # init self-signed cert, user credentials and gid
644 def bootstrap (self):
645 client_bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir,
647 # if -k is provided, use this to initialize private key
648 if self.options.user_private_key:
649 client_bootstrap.init_private_key_if_missing (self.options.user_private_key)
651 # trigger legacy compat code if needed
652 # the name has changed from just <leaf>.pkey to <hrn>.pkey
653 if not os.path.isfile(client_bootstrap.private_key_filename()):
654 self.logger.info ("private key not found, trying legacy name")
656 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%Xrn.unescape(get_leaf(self.user)))
657 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
658 client_bootstrap.init_private_key_if_missing (legacy_private_key)
659 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
661 self.logger.log_exc("Can't find private key ")
665 client_bootstrap.bootstrap_my_gid()
666 # extract what's needed
667 self.private_key = client_bootstrap.private_key()
668 self.my_credential_string = client_bootstrap.my_credential_string ()
669 self.my_credential = {'geni_type': 'geni_sfa',
670 'geni_version': '3.0',
671 'geni_value': self.my_credential_string}
672 self.my_gid = client_bootstrap.my_gid ()
673 self.client_bootstrap = client_bootstrap
676 def my_authority_credential_string(self):
677 if not self.authority:
678 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
680 return self.client_bootstrap.authority_credential_string (self.authority)
682 def authority_credential_string(self, auth_hrn):
683 return self.client_bootstrap.authority_credential_string (auth_hrn)
685 def slice_credential_string(self, name):
686 return self.client_bootstrap.slice_credential_string (name)
688 def slice_credential(self, name):
689 return {'geni_type': 'geni_sfa',
690 'geni_version': '3.0',
691 'geni_value': self.slice_credential_string(name)}
693 # xxx should be supported by sfaclientbootstrap as well
694 def delegate_cred(self, object_cred, hrn, type='authority'):
695 # the gid and hrn of the object we are delegating
696 if isinstance(object_cred, str):
697 object_cred = Credential(string=object_cred)
698 object_gid = object_cred.get_gid_object()
699 object_hrn = object_gid.get_hrn()
701 if not object_cred.get_privileges().get_all_delegate():
702 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
705 # the delegating user's gid
706 caller_gidfile = self.my_gid()
708 # the gid of the user who will be delegated to
709 delegee_gid = self.client_bootstrap.gid(hrn,type)
710 delegee_hrn = delegee_gid.get_hrn()
711 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
712 return dcred.save_to_string(save_parents=True)
715 # Management of the servers
720 if not hasattr (self, 'registry_proxy'):
721 self.logger.info("Contacting Registry at: %s"%self.reg_url)
722 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
723 timeout=self.options.timeout, verbose=self.options.debug)
724 return self.registry_proxy
728 if not hasattr (self, 'sliceapi_proxy'):
729 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
730 if hasattr(self.command_options,'component') and self.command_options.component:
731 # resolve the hrn at the registry
732 node_hrn = self.command_options.component
733 records = self.registry().Resolve(node_hrn, self.my_credential_string)
734 records = filter_records('node', records)
736 self.logger.warning("No such component:%r"% opts.component)
738 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
739 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
741 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
742 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
743 self.sm_url = 'http://' + self.sm_url
744 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
745 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
746 timeout=self.options.timeout, verbose=self.options.debug)
747 return self.sliceapi_proxy
749 def get_cached_server_version(self, server):
750 # check local cache first
753 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
754 cache_key = server.url + "-version"
756 cache = Cache(cache_file)
759 self.logger.info("Local cache not found at: %s" % cache_file)
762 version = cache.get(cache_key)
765 result = server.GetVersion()
766 version= ReturnValue.get_value(result)
767 # cache version for 20 minutes
768 cache.add(cache_key, version, ttl= 60*20)
769 self.logger.info("Updating cache file %s" % cache_file)
770 cache.save_to_file(cache_file)
774 ### resurrect this temporarily so we can support V1 aggregates for a while
775 def server_supports_options_arg(self, server):
777 Returns true if server support the optional call_id arg, false otherwise.
779 server_version = self.get_cached_server_version(server)
781 # xxx need to rewrite this
782 if int(server_version.get('geni_api')) >= 2:
786 def server_supports_call_id_arg(self, server):
787 server_version = self.get_cached_server_version(server)
789 if 'sfa' in server_version and 'code_tag' in server_version:
790 code_tag = server_version['code_tag']
791 code_tag_parts = code_tag.split("-")
792 version_parts = code_tag_parts[0].split(".")
793 major, minor = version_parts[0], version_parts[1]
794 rev = code_tag_parts[1]
795 if int(major) == 1 and minor == 0 and build >= 22:
799 ### ois = options if supported
800 # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
801 def ois (self, server, option_dict):
802 if self.server_supports_options_arg (server):
804 elif self.server_supports_call_id_arg (server):
805 return [ unique_call_id () ]
809 ### cis = call_id if supported - like ois
810 def cis (self, server):
811 if self.server_supports_call_id_arg (server):
812 return [ unique_call_id ]
816 ######################################## miscell utilities
817 def get_rspec_file(self, rspec):
818 if (os.path.isabs(rspec)):
821 file = os.path.join(self.options.sfi_dir, rspec)
822 if (os.path.isfile(file)):
825 self.logger.critical("No such rspec file %s"%rspec)
828 def get_record_file(self, record):
829 if (os.path.isabs(record)):
832 file = os.path.join(self.options.sfi_dir, record)
833 if (os.path.isfile(file)):
836 self.logger.critical("No such registry record file %s"%record)
840 #==========================================================================
841 # Following functions implement the commands
843 # Registry-related commands
844 #==========================================================================
846 @register_command("","")
847 def config (self, options, args):
848 "Display contents of current config"
849 print "# From configuration file %s"%self.config_file
850 flags=[ ('sfi', [ ('registry','reg_url'),
851 ('auth','authority'),
857 flags.append ( ('myslice', ['backend', 'delegate', 'platform', 'username'] ) )
859 for (section, tuples) in flags:
862 for (external_name, internal_name) in tuples:
863 print "%-20s = %s"%(external_name,getattr(self,internal_name))
866 varname="%s_%s"%(section.upper(),name.upper())
867 value=getattr(self.config_instance,varname)
868 print "%-20s = %s"%(name,value)
870 @register_command("","")
871 def version(self, options, args):
873 display an SFA server version (GetVersion)
874 or version information about sfi itself
876 if options.version_local:
877 version=version_core()
879 if options.version_registry:
880 server=self.registry()
882 server = self.sliceapi()
883 result = server.GetVersion()
884 version = ReturnValue.get_value(result)
886 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
888 pprinter = PrettyPrinter(indent=4)
889 pprinter.pprint(version)
891 @register_command("authority","")
892 def list(self, options, args):
894 list entries in named authority registry (List)
901 if options.recursive:
902 opts['recursive'] = options.recursive
904 if options.show_credential:
905 show_credentials(self.my_credential_string)
907 list = self.registry().List(hrn, self.my_credential_string, options)
909 raise Exception, "Not enough parameters for the 'list' command"
911 # filter on person, slice, site, node, etc.
912 # This really should be in the self.filter_records funct def comment...
913 list = filter_records(options.type, list)
914 terminal_render (list, options)
916 save_records_to_file(options.file, list, options.fileformat)
919 @register_command("name","")
920 def show(self, options, args):
922 show details about named registry record (Resolve)
928 # explicitly require Resolve to run in details mode
929 record_dicts = self.registry().Resolve(hrn, self.my_credential_string, {'details':True})
930 record_dicts = filter_records(options.type, record_dicts)
932 self.logger.error("No record of type %s"% options.type)
934 # user has required to focus on some keys
936 def project (record):
938 for key in options.keys:
939 try: projected[key]=record[key]
942 record_dicts = [ project (record) for record in record_dicts ]
943 records = [ Record(dict=record_dict) for record_dict in record_dicts ]
944 for record in records:
945 if (options.format == "text"): record.dump(sort=True)
946 else: print record.save_as_xml()
948 save_records_to_file(options.file, record_dicts, options.fileformat)
951 @register_command("[xml-filename]","")
952 def add(self, options, args):
953 """add record into registry (Register)
954 from command line options (recommended)
955 old-school method involving an xml file still supported"""
957 auth_cred = self.my_authority_credential_string()
958 if options.show_credential:
959 show_credentials(auth_cred)
966 record_filepath = args[0]
967 rec_file = self.get_record_file(record_filepath)
968 record_dict.update(load_record_from_file(rec_file).todict())
970 print "Cannot load record file %s"%record_filepath
973 record_dict.update(load_record_from_opts(options).todict())
974 # we should have a type by now
975 if 'type' not in record_dict :
978 # this is still planetlab dependent.. as plc will whine without that
979 # also, it's only for adding
980 if record_dict['type'] == 'user':
981 if not 'first_name' in record_dict:
982 record_dict['first_name'] = record_dict['hrn']
983 if 'last_name' not in record_dict:
984 record_dict['last_name'] = record_dict['hrn']
985 return self.registry().Register(record_dict, auth_cred)
987 @register_command("[xml-filename]","")
988 def update(self, options, args):
989 """update record into registry (Update)
990 from command line options (recommended)
991 old-school method involving an xml file still supported"""
994 record_filepath = args[0]
995 rec_file = self.get_record_file(record_filepath)
996 record_dict.update(load_record_from_file(rec_file).todict())
998 record_dict.update(load_record_from_opts(options).todict())
999 # at the very least we need 'type' here
1000 if 'type' not in record_dict:
1004 # don't translate into an object, as this would possibly distort
1005 # user-provided data; e.g. add an 'email' field to Users
1006 if record_dict['type'] == "user":
1007 if record_dict['hrn'] == self.user:
1008 cred = self.my_credential_string
1010 cred = self.my_authority_credential_string()
1011 elif record_dict['type'] in ["slice"]:
1013 cred = self.slice_credential_string(record_dict['hrn'])
1014 except ServerException, e:
1015 # XXX smbaker -- once we have better error return codes, update this
1016 # to do something better than a string compare
1017 if "Permission error" in e.args[0]:
1018 cred = self.my_authority_credential_string()
1021 elif record_dict['type'] in ["authority"]:
1022 cred = self.my_authority_credential_string()
1023 elif record_dict['type'] == 'node':
1024 cred = self.my_authority_credential_string()
1026 raise "unknown record type" + record_dict['type']
1027 if options.show_credential:
1028 show_credentials(cred)
1029 return self.registry().Update(record_dict, cred)
1031 @register_command("hrn","")
1032 def remove(self, options, args):
1033 "remove registry record by name (Remove)"
1034 auth_cred = self.my_authority_credential_string()
1042 if options.show_credential:
1043 show_credentials(auth_cred)
1044 return self.registry().Remove(hrn, auth_cred, type)
1046 # ==================================================================
1047 # Slice-related commands
1048 # ==================================================================
1050 @register_command("","")
1051 def slices(self, options, args):
1052 "list instantiated slices (ListSlices) - returns urn's"
1053 server = self.sliceapi()
1055 creds = [self.my_credential_string]
1056 # options and call_id when supported
1058 api_options['call_id']=unique_call_id()
1059 if options.show_credential:
1060 show_credentials(creds)
1061 result = server.ListSlices(creds, *self.ois(server,api_options))
1062 value = ReturnValue.get_value(result)
1063 if self.options.raw:
1064 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1069 # show rspec for named slice
1070 @register_command("","")
1071 def resources(self, options, args):
1073 discover available resources (ListResources)
1075 server = self.sliceapi()
1078 creds = [self.my_credential]
1079 if options.delegate:
1080 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
1081 if options.show_credential:
1082 show_credentials(creds)
1084 # no need to check if server accepts the options argument since the options has
1085 # been a required argument since v1 API
1087 # always send call_id to v2 servers
1088 api_options ['call_id'] = unique_call_id()
1089 # ask for cached value if available
1090 api_options ['cached'] = True
1092 api_options['info'] = options.info
1093 if options.list_leases:
1094 api_options['list_leases'] = options.list_leases
1096 if options.current == True:
1097 api_options['cached'] = False
1099 api_options['cached'] = True
1100 if options.rspec_version:
1101 version_manager = VersionManager()
1102 server_version = self.get_cached_server_version(server)
1103 if 'sfa' in server_version:
1104 # just request the version the client wants
1105 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1107 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1109 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1110 result = server.ListResources (creds, api_options)
1111 value = ReturnValue.get_value(result)
1112 if self.options.raw:
1113 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1114 if options.file is not None:
1115 save_rspec_to_file(value, options.file)
1116 if (self.options.raw is None) and (options.file is None):
1117 display_rspec(value, options.format)
1121 @register_command("slice_hrn","")
1122 def describe(self, options, args):
1124 shows currently allocated/provisioned resources
1125 of the named slice or set of slivers (Describe)
1127 server = self.sliceapi()
1130 creds = [self.slice_credential(args[0])]
1131 if options.delegate:
1132 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
1133 if options.show_credential:
1134 show_credentials(creds)
1136 api_options = {'call_id': unique_call_id(),
1138 'info': options.info,
1139 'list_leases': options.list_leases,
1140 'geni_rspec_version': {'type': 'geni', 'version': '3.0'},
1142 if options.rspec_version:
1143 version_manager = VersionManager()
1144 server_version = self.get_cached_server_version(server)
1145 if 'sfa' in server_version:
1146 # just request the version the client wants
1147 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1149 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
1150 urn = Xrn(args[0], type='slice').get_urn()
1151 result = server.Describe([urn], creds, api_options)
1152 value = ReturnValue.get_value(result)
1153 if self.options.raw:
1154 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1155 if options.file is not None:
1156 save_rspec_to_file(value, options.file)
1157 if (self.options.raw is None) and (options.file is None):
1158 display_rspec(value, options.format)
1162 @register_command("slice_hrn","")
1163 def delete(self, options, args):
1165 de-allocate and de-provision all or named slivers of the slice (Delete)
1167 server = self.sliceapi()
1171 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1174 slice_cred = self.slice_credential(slice_hrn)
1175 creds = [slice_cred]
1177 # options and call_id when supported
1179 api_options ['call_id'] = unique_call_id()
1180 if options.show_credential:
1181 show_credentials(creds)
1182 result = server.Delete([slice_urn], creds, *self.ois(server, api_options ) )
1183 value = ReturnValue.get_value(result)
1184 if self.options.raw:
1185 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1190 @register_command("slice_hrn rspec","")
1191 def allocate(self, options, args):
1193 allocate resources to the named slice (Allocate)
1195 server = self.sliceapi()
1196 server_version = self.get_cached_server_version(server)
1198 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1201 creds = [self.slice_credential(slice_hrn)]
1203 delegated_cred = None
1204 if server_version.get('interface') == 'slicemgr':
1205 # delegate our cred to the slice manager
1206 # do not delegate cred to slicemgr...not working at the moment
1208 #if server_version.get('hrn'):
1209 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1210 #elif server_version.get('urn'):
1211 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1213 if options.show_credential:
1214 show_credentials(creds)
1217 rspec_file = self.get_rspec_file(args[1])
1218 rspec = open(rspec_file).read()
1220 api_options ['call_id'] = unique_call_id()
1224 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1225 if slice_records and 'reg-researchers' in slice_records[0] and slice_records[0]['reg-researchers']!=[]:
1226 slice_record = slice_records[0]
1227 user_hrns = slice_record['reg-researchers']
1228 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1229 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1230 sfa_users = sfa_users_arg(user_records, slice_record)
1231 geni_users = pg_users_arg(user_records)
1233 api_options['sfa_users'] = sfa_users
1234 api_options['geni_users'] = geni_users
1236 result = server.Allocate(slice_urn, creds, rspec, api_options)
1237 value = ReturnValue.get_value(result)
1238 if self.options.raw:
1239 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1240 if options.file is not None:
1241 save_rspec_to_file (value, options.file)
1242 if (self.options.raw is None) and (options.file is None):
1247 @register_command("slice_hrn","")
1248 def provision(self, options, args):
1250 provision already allocated resources of named slice (Provision)
1252 server = self.sliceapi()
1253 server_version = self.get_cached_server_version(server)
1255 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1258 creds = [self.slice_credential(slice_hrn)]
1259 delegated_cred = None
1260 if server_version.get('interface') == 'slicemgr':
1261 # delegate our cred to the slice manager
1262 # do not delegate cred to slicemgr...not working at the moment
1264 #if server_version.get('hrn'):
1265 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1266 #elif server_version.get('urn'):
1267 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1269 if options.show_credential:
1270 show_credentials(creds)
1273 api_options ['call_id'] = unique_call_id()
1275 # set the requtested rspec version
1276 version_manager = VersionManager()
1277 rspec_version = version_manager._get_version('geni', '3.0').to_dict()
1278 api_options['geni_rspec_version'] = rspec_version
1281 # need to pass along user keys to the aggregate.
1283 # { urn: urn:publicid:IDN+emulab.net+user+alice
1284 # keys: [<ssh key A>, <ssh key B>]
1287 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1288 if slice_records and 'reg-researchers' in slice_records[0] and slice_records[0]['reg-researchers']!=[]:
1289 slice_record = slice_records[0]
1290 user_hrns = slice_record['reg-researchers']
1291 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1292 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1293 users = pg_users_arg(user_records)
1295 api_options['geni_users'] = users
1296 result = server.Provision([slice_urn], creds, api_options)
1297 value = ReturnValue.get_value(result)
1298 if self.options.raw:
1299 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1300 if options.file is not None:
1301 save_rspec_to_file (value, options.file)
1302 if (self.options.raw is None) and (options.file is None):
1306 @register_command("slice_hrn","")
1307 def status(self, options, args):
1309 retrieve the status of the slivers belonging to tne named slice (Status)
1311 server = self.sliceapi()
1315 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1318 slice_cred = self.slice_credential(slice_hrn)
1319 creds = [slice_cred]
1321 # options and call_id when supported
1323 api_options['call_id']=unique_call_id()
1324 if options.show_credential:
1325 show_credentials(creds)
1326 result = server.Status([slice_urn], creds, *self.ois(server,api_options))
1327 value = ReturnValue.get_value(result)
1328 if self.options.raw:
1329 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1332 # Thierry: seemed to be missing
1335 @register_command("slice_hrn action","")
1336 def action(self, options, args):
1338 Perform the named operational action on these slivers
1340 server = self.sliceapi()
1345 slice_urn = Xrn(slice_hrn, type='slice').get_urn()
1347 slice_cred = self.slice_credential(args[0])
1348 creds = [slice_cred]
1349 if options.delegate:
1350 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1351 creds.append(delegated_cred)
1353 result = server.PerformOperationalAction([slice_urn], creds, action , api_options)
1354 value = ReturnValue.get_value(result)
1355 if self.options.raw:
1356 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1361 @register_command("slice_hrn time","")
1362 def renew(self, options, args):
1364 renew slice (RenewSliver)
1366 server = self.sliceapi()
1370 [ slice_hrn, input_time ] = args
1372 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1373 # time: don't try to be smart on the time format, server-side will
1375 slice_cred = self.slice_credential(args[0])
1376 creds = [slice_cred]
1377 # options and call_id when supported
1379 api_options['call_id']=unique_call_id()
1380 if options.show_credential:
1381 show_credentials(creds)
1382 result = server.Renew([slice_urn], creds, input_time, *self.ois(server,api_options))
1383 value = ReturnValue.get_value(result)
1384 if self.options.raw:
1385 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1391 @register_command("slice_hrn","")
1392 def shutdown(self, options, args):
1394 shutdown named slice (Shutdown)
1396 server = self.sliceapi()
1399 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1401 slice_cred = self.slice_credential(slice_hrn)
1402 creds = [slice_cred]
1403 result = server.Shutdown(slice_urn, creds)
1404 value = ReturnValue.get_value(result)
1405 if self.options.raw:
1406 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1412 @register_command("[name]","")
1413 def gid(self, options, args):
1415 Create a GID (CreateGid)
1420 target_hrn = args[0]
1421 my_gid_string = open(self.client_bootstrap.my_gid()).read()
1422 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, my_gid_string)
1424 filename = options.file
1426 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1427 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1428 GID(string=gid).save_to_file(filename)
1430 ####################
1431 @register_command("to_hrn","""$ sfi delegate -u -p -s ple.inria.heartbeat -s ple.inria.omftest ple.upmc.slicebrowser
1433 will locally create a set of delegated credentials for the benefit of ple.upmc.slicebrowser
1434 the set of credentials in the scope for this call would be
1435 (*) ple.inria.thierry_parmentelat.user_for_ple.upmc.slicebrowser.user.cred
1437 (*) ple.inria.pi_for_ple.upmc.slicebrowser.user.cred
1439 (*) ple.inria.heartbeat.slice_for_ple.upmc.slicebrowser.user.cred
1440 (*) ple.inria.omftest.slice_for_ple.upmc.slicebrowser.user.cred
1441 because of the two -s options
1444 def delegate (self, options, args):
1446 (locally) create delegate credential for use by given hrn
1447 make sure to check for 'sfi myslice' instead if you plan
1454 # support for several delegations in the same call
1455 # so first we gather the things to do
1457 for slice_hrn in options.delegate_slices:
1458 message="%s.slice"%slice_hrn
1459 original = self.slice_credential_string(slice_hrn)
1460 tuples.append ( (message, original,) )
1461 if options.delegate_pi:
1462 my_authority=self.authority
1463 message="%s.pi"%my_authority
1464 original = self.my_authority_credential_string()
1465 tuples.append ( (message, original,) )
1466 for auth_hrn in options.delegate_auths:
1467 message="%s.auth"%auth_hrn
1468 original=self.authority_credential_string(auth_hrn)
1469 tuples.append ( (message, original, ) )
1470 # if nothing was specified at all at this point, let's assume -u
1471 if not tuples: options.delegate_user=True
1473 if options.delegate_user:
1474 message="%s.user"%self.user
1475 original = self.my_credential_string
1476 tuples.append ( (message, original, ) )
1478 # default type for beneficial is user unless -A
1479 if options.delegate_to_authority: to_type='authority'
1480 else: to_type='user'
1482 # let's now handle all this
1483 # it's all in the filenaming scheme
1484 for (message,original) in tuples:
1485 delegated_string = self.client_bootstrap.delegate_credential_string(original, to_hrn, to_type)
1486 delegated_credential = Credential (string=delegated_string)
1487 filename = os.path.join ( self.options.sfi_dir,
1488 "%s_for_%s.%s.cred"%(message,to_hrn,to_type))
1489 delegated_credential.save_to_file(filename, save_parents=True)
1490 self.logger.info("delegated credential for %s to %s and wrote to %s"%(message,to_hrn,filename))
1492 ####################
1493 @register_command("","""$ less +/myslice sfi_config
1495 backend = http://manifold.pl.sophia.inria.fr:7080
1496 # the HRN that myslice uses, so that we are delegating to
1497 delegate = ple.upmc.slicebrowser
1498 # platform - this is a myslice concept
1500 # username - as of this writing (May 2013) a simple login name
1504 will first collect the slices that you are part of, then make sure
1505 all your credentials are up-to-date (read: refresh expired ones)
1506 then compute delegated credentials for user 'ple.upmc.slicebrowser'
1507 and upload them all on myslice backend, using 'platform' and 'user'.
1508 A password will be prompted for the upload part.
1510 $ sfi -v myslice -- or sfi -vv myslice
1511 same but with more and more verbosity
1514 is synonym to sfi myslice as no other command starts with an 'm'
1516 ) # register_command
1517 def myslice (self, options, args):
1519 """ This helper is for refreshing your credentials at myslice; it will
1520 * compute all the slices that you currently have credentials on
1521 * refresh all your credentials (you as a user and pi, your slices)
1522 * upload them to the manifold backend server
1523 for last phase, sfi_config is read to look for the [myslice] section,
1524 and namely the 'backend', 'delegate' and 'user' settings"""
1530 ### the rough sketch goes like this
1531 # (a) rain check for sufficient config in sfi_config
1532 # we don't allow to override these settings for now
1534 myslice_keys=['backend', 'delegate', 'platform', 'username']
1535 for key in myslice_keys:
1536 full_key="MYSLICE_" + key.upper()
1537 value=getattr(self.config_instance,full_key,None)
1538 if value: myslice_dict[key]=value
1539 else: print "Unsufficient config, missing key %s in [myslice] section of sfi_config"%key
1540 if len(myslice_dict) != len(myslice_keys):
1543 # (b) figure whether we are PI for the authority where we belong
1544 self.logger.info("Resolving our own id")
1545 my_records=self.registry().Resolve(self.user,self.my_credential_string)
1546 if len(my_records)!=1: print "Cannot Resolve %s -- exiting"%self.user; sys.exit(1)
1547 my_record=my_records[0]
1548 my_auths_all = my_record['reg-pi-authorities']
1549 self.logger.info("Found %d authorities that we are PI for"%len(my_auths_all))
1550 self.logger.debug("They are %s"%(my_auths_all))
1552 my_auths = my_auths_all
1553 if options.delegate_auths:
1554 my_auths = list(set(my_auths_all).intersection(set(options.delegate_auths)))
1556 self.logger.info("Delegate PI creds for authorities: %s"%my_auths )
1557 # (c) get the set of slices that we are in
1558 my_slices_all=my_record['reg-slices']
1559 self.logger.info("Found %d slices that we are member of"%len(my_slices_all))
1560 self.logger.debug("They are: %s"%(my_slices_all))
1562 my_slices = my_slices_all
1563 if options.delegate_slices:
1564 my_slices = list(set(my_slices_all).intersection(set(options.delegate_slices)))
1566 self.logger.info("Delegate slice creds for slices: %s"%my_slices)
1568 # (d) make sure we have *valid* credentials for all these
1570 hrn_credentials.append ( (self.user, 'user', self.my_credential_string,) )
1571 for auth_hrn in my_auths:
1572 hrn_credentials.append ( (auth_hrn, 'auth', self.authority_credential_string(auth_hrn),) )
1573 for slice_hrn in my_slices:
1574 hrn_credentials.append ( (slice_hrn, 'slice', self.slice_credential_string (slice_hrn),) )
1576 # (e) check for the delegated version of these
1577 # xxx todo add an option -a/-A? like for 'sfi delegate' for when we ever
1578 # switch to myslice using an authority instead of a user
1579 delegatee_type='user'
1580 delegatee_hrn=myslice_dict['delegate']
1581 hrn_delegated_credentials = []
1582 for (hrn, htype, credential) in hrn_credentials:
1583 delegated_credential = self.client_bootstrap.delegate_credential_string (credential, delegatee_hrn, delegatee_type)
1584 hrn_delegated_credentials.append ((hrn, htype, delegated_credential, ))
1586 # (f) and finally upload them to manifold server
1587 # xxx todo add an option so the password can be set on the command line
1588 # (but *NOT* in the config file) so other apps can leverage this
1589 uploader = ManifoldUploader (logger=self.logger,
1590 url=myslice_dict['backend'],
1591 platform=myslice_dict['platform'],
1592 username=myslice_dict['username'],
1593 password=options.password)
1594 uploader.prompt_all()
1595 (count_all,count_success)=(0,0)
1596 for (hrn,htype,delegated_credential) in hrn_delegated_credentials:
1598 inspect=Credential(string=delegated_credential)
1599 expire_datetime=inspect.get_expiration()
1600 message="%s (%s) [exp:%s]"%(hrn,htype,expire_datetime)
1601 if uploader.upload(delegated_credential,message=message):
1605 self.logger.info("Successfully uploaded %d/%d credentials"%(count_success,count_all))
1606 # at first I thought we would want to save these,
1607 # like 'sfi delegate does' but on second thought
1608 # it is probably not helpful as people would not
1609 # need to run 'sfi delegate' at all anymore
1610 if count_success != count_all: sys.exit(1)
1613 # Thierry: I'm turning this off as a command, no idea what it's used for
1614 # @register_command("cred","")
1615 def trusted(self, options, args):
1617 return the trusted certs at this interface (get_trusted_certs)
1619 trusted_certs = self.registry().get_trusted_certs()
1620 for trusted_cert in trusted_certs:
1621 gid = GID(string=trusted_cert)
1623 cert = Certificate(string=trusted_cert)
1624 self.logger.debug('Sfi.trusted -> %r'%cert.get_subject())