2 # sfi.py - basic SFA command-line client
3 # this module is also used in sfascan
17 from lxml import etree
18 from StringIO import StringIO
19 from optparse import OptionParser
20 from pprint import PrettyPrinter
21 from tempfile import mkstemp
23 from sfa.trust.certificate import Keypair, Certificate
24 from sfa.trust.gid import GID
25 from sfa.trust.credential import Credential
26 from sfa.trust.sfaticket import SfaTicket
28 from sfa.util.faults import SfaInvalidArgument
29 from sfa.util.sfalogging import sfi_logger
30 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn, Xrn
31 from sfa.util.config import Config
32 from sfa.util.version import version_core
33 from sfa.util.cache import Cache
35 from sfa.storage.record import Record
37 from sfa.rspecs.rspec import RSpec
38 from sfa.rspecs.rspec_converter import RSpecConverter
39 from sfa.rspecs.version_manager import VersionManager
41 from sfa.client.sfaclientlib import SfaClientBootstrap
42 from sfa.client.sfaserverproxy import SfaServerProxy, ServerException
43 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
44 from sfa.client.return_value import ReturnValue
45 from sfa.client.candidates import Candidates
46 from sfa.client.manifolduploader import ManifoldUploader
50 from sfa.client.common import optparse_listvalue_callback, optparse_dictvalue_callback, \
51 terminal_render, filter_records
54 def display_rspec(rspec, format='rspec'):
56 tree = etree.parse(StringIO(rspec))
58 result = root.xpath("./network/site/node/hostname/text()")
59 elif format in ['ip']:
60 # The IP address is not yet part of the new RSpec
61 # so this doesn't do anything yet.
62 tree = etree.parse(StringIO(rspec))
64 result = root.xpath("./network/site/node/ipv4/text()")
71 def display_list(results):
72 for result in results:
75 def display_records(recordList, dump=False):
76 ''' Print all fields in the record'''
77 for record in recordList:
78 display_record(record, dump)
80 def display_record(record, dump=False):
82 record.dump(sort=True)
84 info = record.getdict()
85 print "%s (%s)" % (info['hrn'], info['type'])
89 def credential_printable (credential_string):
90 credential=Credential(string=credential_string)
92 result += credential.get_summary_tostring()
94 rights = credential.get_privileges()
95 result += "rights=%s"%rights
99 def show_credentials (cred_s):
100 if not isinstance (cred_s,list): cred_s = [cred_s]
102 print "Using Credential %s"%credential_printable(cred)
105 def save_raw_to_file(var, filename, format="text", banner=None):
107 # if filename is "-", send it to stdout
110 f = open(filename, "w")
115 elif format == "pickled":
116 f.write(pickle.dumps(var))
117 elif format == "json":
118 if hasattr(json, "dumps"):
119 f.write(json.dumps(var)) # python 2.6
121 f.write(json.write(var)) # python 2.5
123 # this should never happen
124 print "unknown output format", format
126 f.write('\n'+banner+"\n")
128 def save_rspec_to_file(rspec, filename):
129 if not filename.endswith(".rspec"):
130 filename = filename + ".rspec"
131 f = open(filename, 'w')
136 def save_records_to_file(filename, record_dicts, format="xml"):
139 for record_dict in record_dicts:
141 save_record_to_file(filename + "." + str(index), record_dict)
143 save_record_to_file(filename, record_dict)
145 elif format == "xmllist":
146 f = open(filename, "w")
147 f.write("<recordlist>\n")
148 for record_dict in record_dicts:
149 record_obj=Record(dict=record_dict)
150 f.write('<record hrn="' + record_obj.hrn + '" type="' + record_obj.type + '" />\n')
151 f.write("</recordlist>\n")
153 elif format == "hrnlist":
154 f = open(filename, "w")
155 for record_dict in record_dicts:
156 record_obj=Record(dict=record_dict)
157 f.write(record_obj.hrn + "\n")
160 # this should never happen
161 print "unknown output format", format
163 def save_record_to_file(filename, record_dict):
164 record = Record(dict=record_dict)
165 xml = record.save_as_xml()
166 f=codecs.open(filename, encoding='utf-8',mode="w")
171 # minimally check a key argument
172 def check_ssh_key (key):
173 good_ssh_key = r'^.*(?:ssh-dss|ssh-rsa)[ ]+[A-Za-z0-9+/=]+(?: .*)?$'
174 return re.match(good_ssh_key, key, re.IGNORECASE)
177 def load_record_from_opts(options):
179 if hasattr(options, 'xrn') and options.xrn:
180 if hasattr(options, 'type') and options.type:
181 xrn = Xrn(options.xrn, options.type)
183 xrn = Xrn(options.xrn)
184 record_dict['urn'] = xrn.get_urn()
185 record_dict['hrn'] = xrn.get_hrn()
186 record_dict['type'] = xrn.get_type()
187 if hasattr(options, 'key') and options.key:
189 pubkey = open(options.key, 'r').read()
192 if not check_ssh_key (pubkey):
193 raise SfaInvalidArgument(name='key',msg="Could not find file, or wrong key format")
194 record_dict['keys'] = [pubkey]
195 if hasattr(options, 'slices') and options.slices:
196 record_dict['slices'] = options.slices
197 if hasattr(options, 'researchers') and options.researchers:
198 record_dict['researcher'] = options.researchers
199 if hasattr(options, 'email') and options.email:
200 record_dict['email'] = options.email
201 if hasattr(options, 'pis') and options.pis:
202 record_dict['pi'] = options.pis
204 # handle extra settings
205 record_dict.update(options.extras)
207 return Record(dict=record_dict)
209 def load_record_from_file(filename):
210 f=codecs.open(filename, encoding="utf-8", mode="r")
211 xml_string = f.read()
213 return Record(xml=xml_string)
217 def unique_call_id(): return uuid.uuid4().urn
219 ########## a simple model for maintaing 3 doc attributes per command (instead of just one)
220 # essentially for the methods that implement a subcommand like sfi list
221 # we need to keep track of
222 # (*) doc a few lines that tell what it does, still located in __doc__
223 # (*) args_string a simple one-liner that describes mandatory arguments
224 # (*) example well, one or several releant examples
226 # since __doc__ only accounts for one, we use this simple mechanism below
227 # however we keep doc in place for easier migration
229 from functools import wraps
231 # we use a list as well as a dict so we can keep track of the order
235 def register_command (args_string, example):
237 name=getattr(m,'__name__')
238 doc=getattr(m,'__doc__',"-- missing doc --")
239 doc=doc.strip(" \t\n")
240 commands_list.append(name)
241 commands_dict[name]=(doc, args_string, example)
243 def new_method (*args, **kwds): return m(*args, **kwds)
251 # dirty hack to make this class usable from the outside
252 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user', 'user_private_key']
255 def default_sfi_dir ():
256 if os.path.isfile("./sfi_config"):
259 return os.path.expanduser("~/.sfi/")
261 # dummy to meet Sfi's expectations for its 'options' field
262 # i.e. s/t we can do setattr on
266 def __init__ (self,options=None):
267 if options is None: options=Sfi.DummyOptions()
268 for opt in Sfi.required_options:
269 if not hasattr(options,opt): setattr(options,opt,None)
270 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
271 self.options = options
273 self.authority = None
274 self.logger = sfi_logger
275 self.logger.enable_console()
276 ### various auxiliary material that we keep at hand
278 # need to call this other than just 'config' as we have a command/method with that name
279 self.config_instance=None
280 self.config_file=None
281 self.client_bootstrap=None
283 ### suitable if no reasonable command has been provided
284 def print_commands_help (self, options):
285 verbose=getattr(options,'verbose')
286 format3="%18s %-15s %s"
289 print format3%("command","cmd_args","description")
293 self.create_parser_global().print_help()
294 # preserve order from the code
295 for command in commands_list:
296 (doc, args_string, example) = commands_dict[command]
299 doc=doc.replace("\n","\n"+35*' ')
300 print format3%(command,args_string,doc)
302 self.create_parser_command(command).print_help()
304 ### now if a known command was found we can be more verbose on that one
305 def print_help (self):
306 print "==================== Generic sfi usage"
307 self.sfi_parser.print_help()
308 (doc,_,example)=commands_dict[self.command]
309 print "\n==================== Purpose of %s"%self.command
311 print "\n==================== Specific usage for %s"%self.command
312 self.command_parser.print_help()
314 print "\n==================== %s example(s)"%self.command
317 def create_parser_global(self):
319 # Generate command line parser
320 parser = OptionParser(add_help_option=False,
321 usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
322 description="Commands: %s"%(" ".join(commands_list)))
323 parser.add_option("-r", "--registry", dest="registry",
324 help="root registry", metavar="URL", default=None)
325 parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
326 help="slice API - in general a SM URL, but can be used to talk to an aggregate")
327 parser.add_option("-R", "--raw", dest="raw", default=None,
328 help="Save raw, unparsed server response to a file")
329 parser.add_option("", "--rawformat", dest="rawformat", type="choice",
330 help="raw file format ([text]|pickled|json)", default="text",
331 choices=("text","pickled","json"))
332 parser.add_option("", "--rawbanner", dest="rawbanner", default=None,
333 help="text string to write before and after raw output")
334 parser.add_option("-d", "--dir", dest="sfi_dir",
335 help="config & working directory - default is %default",
336 metavar="PATH", default=Sfi.default_sfi_dir())
337 parser.add_option("-u", "--user", dest="user",
338 help="user name", metavar="HRN", default=None)
339 parser.add_option("-a", "--auth", dest="auth",
340 help="authority name", metavar="HRN", default=None)
341 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
342 help="verbose mode - cumulative")
343 parser.add_option("-D", "--debug",
344 action="store_true", dest="debug", default=False,
345 help="Debug (xml-rpc) protocol messages")
346 # would it make sense to use ~/.ssh/id_rsa as a default here ?
347 parser.add_option("-k", "--private-key",
348 action="store", dest="user_private_key", default=None,
349 help="point to the private key file to use if not yet installed in sfi_dir")
350 parser.add_option("-t", "--timeout", dest="timeout", default=None,
351 help="Amout of time to wait before timing out the request")
352 parser.add_option("-h", "--help",
353 action="store_true", dest="help", default=False,
354 help="one page summary on commands & exit")
355 parser.disable_interspersed_args()
360 def create_parser_command(self, command):
361 if command not in commands_dict:
362 msg="Invalid command\n"
364 msg += ','.join(commands_list)
365 self.logger.critical(msg)
368 # retrieve args_string
369 (_, args_string, __) = commands_dict[command]
371 parser = OptionParser(add_help_option=False,
372 usage="sfi [sfi_options] %s [cmd_options] %s"
373 % (command, args_string))
374 parser.add_option ("-h","--help",dest='help',action='store_true',default=False,
375 help="Summary of one command usage")
377 if command in ("config"):
378 parser.add_option('-m', '--myslice', dest='myslice', action='store_true', default=False,
379 help='how myslice config variables as well')
381 if command in ("version"):
382 parser.add_option("-R","--registry-version",
383 action="store_true", dest="version_registry", default=False,
384 help="probe registry version instead of sliceapi")
385 parser.add_option("-l","--local",
386 action="store_true", dest="version_local", default=False,
387 help="display version of the local client")
389 if command in ("add", "update"):
390 parser.add_option('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
391 parser.add_option('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
392 parser.add_option('-e', '--email', dest='email', default="", help="email (mandatory for users)")
393 parser.add_option('-k', '--key', dest='key', metavar='<key>', help='public key string or file',
395 parser.add_option('-s', '--slices', dest='slices', metavar='<slices>', help='Set/replace slice xrns',
396 default='', type="str", action='callback', callback=optparse_listvalue_callback)
397 parser.add_option('-r', '--researchers', dest='researchers', metavar='<researchers>',
398 help='Set/replace slice researchers', default='', type="str", action='callback',
399 callback=optparse_listvalue_callback)
400 parser.add_option('-p', '--pis', dest='pis', metavar='<PIs>', help='Set/replace Principal Investigators/Project Managers',
401 default='', type="str", action='callback', callback=optparse_listvalue_callback)
402 parser.add_option ('-X','--extra',dest='extras',default={},type='str',metavar="<EXTRA_ASSIGNS>",
403 action="callback", callback=optparse_dictvalue_callback, nargs=1,
404 help="set extra/testbed-dependent flags, e.g. --extra enabled=true")
406 # show_credential option
407 if command in ("list","resources","create","add","update","remove","slices","delete","status","renew"):
408 parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False,
409 help="show credential(s) used in human-readable form")
410 # registy filter option
411 if command in ("list", "show", "remove"):
412 parser.add_option("-t", "--type", dest="type", type="choice",
413 help="type filter ([all]|user|slice|authority|node|aggregate)",
414 choices=("all", "user", "slice", "authority", "node", "aggregate"),
416 if command in ("show"):
417 parser.add_option("-k","--key",dest="keys",action="append",default=[],
418 help="specify specific keys to be displayed from record")
419 if command in ("resources"):
421 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
422 help="schema type and version of resulting RSpec")
423 # disable/enable cached rspecs
424 parser.add_option("-c", "--current", dest="current", default=False,
426 help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
428 parser.add_option("-f", "--format", dest="format", type="choice",
429 help="display format ([xml]|dns|ip)", default="xml",
430 choices=("xml", "dns", "ip"))
431 #panos: a new option to define the type of information about resources a user is interested in
432 parser.add_option("-i", "--info", dest="info",
433 help="optional component information", default=None)
434 # a new option to retreive or not reservation-oriented RSpecs (leases)
435 parser.add_option("-l", "--list_leases", dest="list_leases", type="choice",
436 help="Retreive or not reservation-oriented RSpecs ([resources]|leases|all )",
437 choices=("all", "resources", "leases"), default="resources")
440 # 'create' does return the new rspec, makes sense to save that too
441 if command in ("resources", "show", "list", "gid", 'create'):
442 parser.add_option("-o", "--output", dest="file",
443 help="output XML to file", metavar="FILE", default=None)
445 if command in ("show", "list"):
446 parser.add_option("-f", "--format", dest="format", type="choice",
447 help="display format ([text]|xml)", default="text",
448 choices=("text", "xml"))
450 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
451 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
452 choices=("xml", "xmllist", "hrnlist"))
453 if command == 'list':
454 parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
455 help="list all child records", default=False)
456 parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
457 help="gives details, like user keys", default=False)
458 if command in ("delegate"):
459 parser.add_option("-u", "--user",
460 action="store_true", dest="delegate_user", default=False,
461 help="delegate your own credentials; default if no other option is provided")
462 parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
463 metavar="slice_hrn", help="delegate cred. for slice HRN")
464 parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
465 metavar='auth_hrn', help="delegate cred for auth HRN")
466 # this primarily is a shorthand for -a my_hrn
467 parser.add_option("-p", "--pi", dest='delegate_pi', default=None, action='store_true',
468 help="delegate your PI credentials, so s.t. like -a your_hrn^")
469 parser.add_option("-A","--to-authority",dest='delegate_to_authority',action='store_true',default=False,
470 help="""by default the mandatory argument is expected to be a user,
471 use this if you mean an authority instead""")
473 if command in ("myslice"):
474 parser.add_option("-p","--password",dest='password',action='store',default=None,
475 help="specify mainfold password on the command line")
476 parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
477 metavar="slice_hrn", help="delegate cred. for slice HRN")
478 parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
479 metavar='auth_hrn', help="delegate PI cred for auth HRN")
480 parser.add_option('-d', '--delegate', dest='delegate', help="Override 'delegate' from the config file")
481 parser.add_option('-b', '--backend', dest='backend', help="Override 'backend' from the config file")
487 # Main: parse arguments and dispatch to command
489 def dispatch(self, command, command_options, command_args):
490 method=getattr(self, command, None)
492 print "Unknown command %s"%command
494 return method(command_options, command_args)
497 self.sfi_parser = self.create_parser_global()
498 (options, args) = self.sfi_parser.parse_args()
500 self.print_commands_help(options)
502 self.options = options
504 self.logger.setLevelFromOptVerbose(self.options.verbose)
507 self.logger.critical("No command given. Use -h for help.")
508 self.print_commands_help(options)
511 # complete / find unique match with command set
512 command_candidates = Candidates (commands_list)
514 command = command_candidates.only_match(input)
516 self.print_commands_help(options)
518 # second pass options parsing
520 self.command_parser = self.create_parser_command(command)
521 (command_options, command_args) = self.command_parser.parse_args(args[1:])
522 if command_options.help:
525 self.command_options = command_options
529 self.logger.debug("Command=%s" % self.command)
532 self.dispatch(command, command_options, command_args)
536 self.logger.log_exc ("sfi command %s failed"%command)
542 def read_config(self):
543 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
544 shell_config_file = os.path.join(self.options.sfi_dir,"sfi_config.sh")
546 if Config.is_ini(config_file):
547 config = Config (config_file)
549 # try upgrading from shell config format
550 fp, fn = mkstemp(suffix='sfi_config', text=True)
552 # we need to preload the sections we want parsed
553 # from the shell config
554 config.add_section('sfi')
555 # sface users should be able to use this same file to configure their stuff
556 config.add_section('sface')
557 # manifold users should be able to specify the details
558 # of their backend server here for 'sfi myslice'
559 config.add_section('myslice')
560 config.load(config_file)
562 shutil.move(config_file, shell_config_file)
564 config.save(config_file)
567 self.logger.critical("Failed to read configuration file %s"%config_file)
568 self.logger.info("Make sure to remove the export clauses and to add quotes")
569 if self.options.verbose==0:
570 self.logger.info("Re-run with -v for more details")
572 self.logger.log_exc("Could not read config file %s"%config_file)
575 self.config_instance=config
578 if (self.options.sm is not None):
579 self.sm_url = self.options.sm
580 elif hasattr(config, "SFI_SM"):
581 self.sm_url = config.SFI_SM
583 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
587 if (self.options.registry is not None):
588 self.reg_url = self.options.registry
589 elif hasattr(config, "SFI_REGISTRY"):
590 self.reg_url = config.SFI_REGISTRY
592 self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
596 if (self.options.user is not None):
597 self.user = self.options.user
598 elif hasattr(config, "SFI_USER"):
599 self.user = config.SFI_USER
601 self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
605 if (self.options.auth is not None):
606 self.authority = self.options.auth
607 elif hasattr(config, "SFI_AUTH"):
608 self.authority = config.SFI_AUTH
610 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
613 self.config_file=config_file
618 # Get various credential and spec files
620 # Establishes limiting conventions
621 # - conflates MAs and SAs
622 # - assumes last token in slice name is unique
624 # Bootstraps credentials
625 # - bootstrap user credential from self-signed certificate
626 # - bootstrap authority credential from user credential
627 # - bootstrap slice credential from user credential
630 # init self-signed cert, user credentials and gid
631 def bootstrap (self):
632 client_bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir,
634 # if -k is provided, use this to initialize private key
635 if self.options.user_private_key:
636 client_bootstrap.init_private_key_if_missing (self.options.user_private_key)
638 # trigger legacy compat code if needed
639 # the name has changed from just <leaf>.pkey to <hrn>.pkey
640 if not os.path.isfile(client_bootstrap.private_key_filename()):
641 self.logger.info ("private key not found, trying legacy name")
643 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%Xrn.unescape(get_leaf(self.user)))
644 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
645 client_bootstrap.init_private_key_if_missing (legacy_private_key)
646 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
648 self.logger.log_exc("Can't find private key ")
652 client_bootstrap.bootstrap_my_gid()
653 # extract what's needed
654 self.private_key = client_bootstrap.private_key()
655 self.my_credential_string = client_bootstrap.my_credential_string ()
656 self.my_gid = client_bootstrap.my_gid ()
657 self.client_bootstrap = client_bootstrap
660 def my_authority_credential_string(self):
661 if not self.authority:
662 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
664 return self.client_bootstrap.authority_credential_string (self.authority)
666 def authority_credential_string(self, auth_hrn):
667 return self.client_bootstrap.authority_credential_string (auth_hrn)
669 def slice_credential_string(self, name):
670 return self.client_bootstrap.slice_credential_string (name)
673 # Management of the servers
678 if not hasattr (self, 'registry_proxy'):
679 self.logger.info("Contacting Registry at: %s"%self.reg_url)
680 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
681 timeout=self.options.timeout, verbose=self.options.debug)
682 return self.registry_proxy
686 if not hasattr (self, 'sliceapi_proxy'):
687 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
688 if hasattr(self.command_options,'component') and self.command_options.component:
689 # resolve the hrn at the registry
690 node_hrn = self.command_options.component
691 records = self.registry().Resolve(node_hrn, self.my_credential_string)
692 records = filter_records('node', records)
694 self.logger.warning("No such component:%r"% opts.component)
696 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
697 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
699 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
700 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
701 self.sm_url = 'http://' + self.sm_url
702 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
703 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
704 timeout=self.options.timeout, verbose=self.options.debug)
705 return self.sliceapi_proxy
707 def get_cached_server_version(self, server):
708 # check local cache first
711 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
712 cache_key = server.url + "-version"
714 cache = Cache(cache_file)
717 self.logger.info("Local cache not found at: %s" % cache_file)
720 version = cache.get(cache_key)
723 result = server.GetVersion()
724 version= ReturnValue.get_value(result)
725 # cache version for 20 minutes
726 cache.add(cache_key, version, ttl= 60*20)
727 self.logger.info("Updating cache file %s" % cache_file)
728 cache.save_to_file(cache_file)
732 ### resurrect this temporarily so we can support V1 aggregates for a while
733 def server_supports_options_arg(self, server):
735 Returns true if server support the optional call_id arg, false otherwise.
737 server_version = self.get_cached_server_version(server)
739 # xxx need to rewrite this
740 if int(server_version.get('geni_api')) >= 2:
744 def server_supports_call_id_arg(self, server):
745 server_version = self.get_cached_server_version(server)
747 if 'sfa' in server_version and 'code_tag' in server_version:
748 code_tag = server_version['code_tag']
749 code_tag_parts = code_tag.split("-")
750 version_parts = code_tag_parts[0].split(".")
751 major, minor = version_parts[0], version_parts[1]
752 rev = code_tag_parts[1]
753 if int(major) == 1 and minor == 0 and build >= 22:
757 ### ois = options if supported
758 # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
759 def ois (self, server, option_dict):
760 if self.server_supports_options_arg (server):
762 elif self.server_supports_call_id_arg (server):
763 return [ unique_call_id () ]
767 ### cis = call_id if supported - like ois
768 def cis (self, server):
769 if self.server_supports_call_id_arg (server):
770 return [ unique_call_id ]
774 ######################################## miscell utilities
775 def get_rspec_file(self, rspec):
776 if (os.path.isabs(rspec)):
779 file = os.path.join(self.options.sfi_dir, rspec)
780 if (os.path.isfile(file)):
783 self.logger.critical("No such rspec file %s"%rspec)
786 def get_record_file(self, record):
787 if (os.path.isabs(record)):
790 file = os.path.join(self.options.sfi_dir, record)
791 if (os.path.isfile(file)):
794 self.logger.critical("No such registry record file %s"%record)
798 #==========================================================================
799 # Following functions implement the commands
801 # Registry-related commands
802 #==========================================================================
804 @register_command("","")
805 def config (self, options, args):
806 "Display contents of current config"
807 print "# From configuration file %s"%self.config_file
808 flags=[ ('sfi', [ ('registry','reg_url'),
809 ('auth','authority'),
815 flags.append ( ('myslice', ['backend', 'delegate', 'platform', 'username'] ) )
817 for (section, tuples) in flags:
820 for (external_name, internal_name) in tuples:
821 print "%-20s = %s"%(external_name,getattr(self,internal_name))
824 varname="%s_%s"%(section.upper(),name.upper())
825 value=getattr(self.config_instance,varname)
826 print "%-20s = %s"%(name,value)
828 @register_command("","")
829 def version(self, options, args):
831 display an SFA server version (GetVersion)
832 or version information about sfi itself
834 if options.version_local:
835 version=version_core()
837 if options.version_registry:
838 server=self.registry()
840 server = self.sliceapi()
841 result = server.GetVersion()
842 version = ReturnValue.get_value(result)
844 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
846 pprinter = PrettyPrinter(indent=4)
847 pprinter.pprint(version)
849 @register_command("authority","")
850 def list(self, options, args):
852 list entries in named authority registry (List)
859 if options.recursive:
860 opts['recursive'] = options.recursive
862 if options.show_credential:
863 show_credentials(self.my_credential_string)
865 list = self.registry().List(hrn, self.my_credential_string, options)
867 raise Exception, "Not enough parameters for the 'list' command"
869 # filter on person, slice, site, node, etc.
870 # This really should be in the self.filter_records funct def comment...
871 list = filter_records(options.type, list)
872 terminal_render (list, options)
874 save_records_to_file(options.file, list, options.fileformat)
877 @register_command("name","")
878 def show(self, options, args):
880 show details about named registry record (Resolve)
886 # explicitly require Resolve to run in details mode
887 record_dicts = self.registry().Resolve(hrn, self.my_credential_string, {'details':True})
888 record_dicts = filter_records(options.type, record_dicts)
890 self.logger.error("No record of type %s"% options.type)
892 # user has required to focus on some keys
894 def project (record):
896 for key in options.keys:
897 try: projected[key]=record[key]
900 record_dicts = [ project (record) for record in record_dicts ]
901 records = [ Record(dict=record_dict) for record_dict in record_dicts ]
902 for record in records:
903 if (options.format == "text"): record.dump(sort=True)
904 else: print record.save_as_xml()
906 save_records_to_file(options.file, record_dicts, options.fileformat)
909 @register_command("[xml-filename]","")
910 def add(self, options, args):
911 """add record into registry (Register)
912 from command line options (recommended)
913 old-school method involving an xml file still supported"""
915 auth_cred = self.my_authority_credential_string()
916 if options.show_credential:
917 show_credentials(auth_cred)
924 record_filepath = args[0]
925 rec_file = self.get_record_file(record_filepath)
926 record_dict.update(load_record_from_file(rec_file).todict())
928 print "Cannot load record file %s"%record_filepath
931 record_dict.update(load_record_from_opts(options).todict())
932 # we should have a type by now
933 if 'type' not in record_dict :
936 # this is still planetlab dependent.. as plc will whine without that
937 # also, it's only for adding
938 if record_dict['type'] == 'user':
939 if not 'first_name' in record_dict:
940 record_dict['first_name'] = record_dict['hrn']
941 if 'last_name' not in record_dict:
942 record_dict['last_name'] = record_dict['hrn']
943 return self.registry().Register(record_dict, auth_cred)
945 @register_command("[xml-filename]","")
946 def update(self, options, args):
947 """update record into registry (Update)
948 from command line options (recommended)
949 old-school method involving an xml file still supported"""
952 record_filepath = args[0]
953 rec_file = self.get_record_file(record_filepath)
954 record_dict.update(load_record_from_file(rec_file).todict())
956 record_dict.update(load_record_from_opts(options).todict())
957 # at the very least we need 'type' here
958 if 'type' not in record_dict:
962 # don't translate into an object, as this would possibly distort
963 # user-provided data; e.g. add an 'email' field to Users
964 if record_dict['type'] == "user":
965 if record_dict['hrn'] == self.user:
966 cred = self.my_credential_string
968 cred = self.my_authority_credential_string()
969 elif record_dict['type'] in ["slice"]:
971 cred = self.slice_credential_string(record_dict['hrn'])
972 except ServerException, e:
973 # XXX smbaker -- once we have better error return codes, update this
974 # to do something better than a string compare
975 if "Permission error" in e.args[0]:
976 cred = self.my_authority_credential_string()
979 elif record_dict['type'] in ["authority"]:
980 cred = self.my_authority_credential_string()
981 elif record_dict['type'] == 'node':
982 cred = self.my_authority_credential_string()
984 raise "unknown record type" + record_dict['type']
985 if options.show_credential:
986 show_credentials(cred)
987 return self.registry().Update(record_dict, cred)
989 @register_command("hrn","")
990 def remove(self, options, args):
991 "remove registry record by name (Remove)"
992 auth_cred = self.my_authority_credential_string()
1000 if options.show_credential:
1001 show_credentials(auth_cred)
1002 return self.registry().Remove(hrn, auth_cred, type)
1004 # ==================================================================
1005 # Slice-related commands
1006 # ==================================================================
1008 @register_command("","")
1009 def slices(self, options, args):
1010 "list instantiated slices (ListSlices) - returns urn's"
1011 server = self.sliceapi()
1013 creds = [self.my_credential_string]
1014 # options and call_id when supported
1016 api_options['call_id']=unique_call_id()
1017 if options.show_credential:
1018 show_credentials(creds)
1019 result = server.ListSlices(creds, *self.ois(server,api_options))
1020 value = ReturnValue.get_value(result)
1021 if self.options.raw:
1022 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1027 # show rspec for named slice
1028 @register_command("[slice_hrn]","")
1029 def resources(self, options, args):
1031 with no arg, discover available resources, (ListResources)
1032 or with an slice hrn, shows currently provisioned resources
1034 server = self.sliceapi()
1039 the_credential=self.slice_credential_string(args[0])
1040 creds.append(the_credential)
1042 the_credential=self.my_credential_string
1043 creds.append(the_credential)
1044 if options.show_credential:
1045 show_credentials(creds)
1047 # no need to check if server accepts the options argument since the options has
1048 # been a required argument since v1 API
1050 # always send call_id to v2 servers
1051 api_options ['call_id'] = unique_call_id()
1052 # ask for cached value if available
1053 api_options ['cached'] = True
1056 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
1058 api_options['info'] = options.info
1059 if options.list_leases:
1060 api_options['list_leases'] = options.list_leases
1062 if options.current == True:
1063 api_options['cached'] = False
1065 api_options['cached'] = True
1066 if options.rspec_version:
1067 version_manager = VersionManager()
1068 server_version = self.get_cached_server_version(server)
1069 if 'sfa' in server_version:
1070 # just request the version the client wants
1071 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1073 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'}
1075 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'}
1076 result = server.ListResources (creds, api_options)
1077 value = ReturnValue.get_value(result)
1078 if self.options.raw:
1079 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1080 if options.file is not None:
1081 save_rspec_to_file(value, options.file)
1082 if (self.options.raw is None) and (options.file is None):
1083 display_rspec(value, options.format)
1087 @register_command("slice_hrn rspec","")
1088 def create(self, options, args):
1090 create or update named slice with given rspec (CreateSliver)
1092 server = self.sliceapi()
1094 # xxx do we need to check usage (len(args)) ?
1097 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1100 creds = [self.slice_credential_string(slice_hrn)]
1102 delegated_cred = None
1103 server_version = self.get_cached_server_version(server)
1104 if server_version.get('interface') == 'slicemgr':
1105 # delegate our cred to the slice manager
1106 # do not delegate cred to slicemgr...not working at the moment
1108 #if server_version.get('hrn'):
1109 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1110 #elif server_version.get('urn'):
1111 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1113 if options.show_credential:
1114 show_credentials(creds)
1117 rspec_file = self.get_rspec_file(args[1])
1118 rspec = open(rspec_file).read()
1121 # need to pass along user keys to the aggregate.
1123 # { urn: urn:publicid:IDN+emulab.net+user+alice
1124 # keys: [<ssh key A>, <ssh key B>]
1127 # xxx Thierry 2012 sept. 21
1128 # contrary to what I was first thinking, calling Resolve with details=False does not yet work properly here
1129 # I am turning details=True on again on a - hopefully - temporary basis, just to get this whole thing to work again
1130 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1131 # slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string], {'details':True})
1132 if slice_records and 'reg-researchers' in slice_records[0] and slice_records[0]['reg-researchers']:
1133 slice_record = slice_records[0]
1134 user_hrns = slice_record['reg-researchers']
1135 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1136 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1138 if 'sfa' not in server_version:
1139 users = pg_users_arg(user_records)
1140 #rspec = RSpec(rspec)
1141 #rspec.filter({'component_manager_id': server_version['urn']})
1142 #rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1144 users = sfa_users_arg(user_records, slice_record)
1146 # do not append users, keys, or slice tags. Anything
1147 # not contained in this request will be removed from the slice
1149 # CreateSliver has supported the options argument for a while now so it should
1150 # be safe to assume this server support it
1152 api_options ['append'] = False
1153 api_options ['call_id'] = unique_call_id()
1154 result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server, api_options))
1155 value = ReturnValue.get_value(result)
1156 if self.options.raw:
1157 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1158 if options.file is not None:
1159 save_rspec_to_file (value, options.file)
1160 if (self.options.raw is None) and (options.file is None):
1165 @register_command("slice_hrn","")
1166 def delete(self, options, args):
1168 delete named slice (DeleteSliver)
1170 server = self.sliceapi()
1174 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1177 slice_cred = self.slice_credential_string(slice_hrn)
1178 creds = [slice_cred]
1180 # options and call_id when supported
1182 api_options ['call_id'] = unique_call_id()
1183 if options.show_credential:
1184 show_credentials(creds)
1185 result = server.DeleteSliver(slice_urn, creds, *self.ois(server, api_options ) )
1186 value = ReturnValue.get_value(result)
1187 if self.options.raw:
1188 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1193 @register_command("slice_hrn","")
1194 def status(self, options, args):
1196 retrieve slice status (SliverStatus)
1198 server = self.sliceapi()
1202 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1205 slice_cred = self.slice_credential_string(slice_hrn)
1206 creds = [slice_cred]
1208 # options and call_id when supported
1210 api_options['call_id']=unique_call_id()
1211 if options.show_credential:
1212 show_credentials(creds)
1213 result = server.SliverStatus(slice_urn, creds, *self.ois(server,api_options))
1214 value = ReturnValue.get_value(result)
1215 if self.options.raw:
1216 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1220 @register_command("slice_hrn","")
1221 def start(self, options, args):
1223 start named slice (Start)
1225 server = self.sliceapi()
1229 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1232 slice_cred = self.slice_credential_string(args[0])
1233 creds = [slice_cred]
1234 # xxx Thierry - does this not need an api_options as well ?
1235 result = server.Start(slice_urn, creds)
1236 value = ReturnValue.get_value(result)
1237 if self.options.raw:
1238 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1243 @register_command("slice_hrn","")
1244 def stop(self, options, args):
1246 stop named slice (Stop)
1248 server = self.sliceapi()
1251 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1253 slice_cred = self.slice_credential_string(args[0])
1254 creds = [slice_cred]
1255 result = server.Stop(slice_urn, creds)
1256 value = ReturnValue.get_value(result)
1257 if self.options.raw:
1258 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1264 @register_command("slice_hrn","")
1265 def reset(self, options, args):
1267 reset named slice (reset_slice)
1269 server = self.sliceapi()
1272 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1274 slice_cred = self.slice_credential_string(args[0])
1275 creds = [slice_cred]
1276 result = server.reset_slice(creds, slice_urn)
1277 value = ReturnValue.get_value(result)
1278 if self.options.raw:
1279 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1284 @register_command("slice_hrn time","")
1285 def renew(self, options, args):
1287 renew slice (RenewSliver)
1289 server = self.sliceapi()
1293 [ slice_hrn, input_time ] = args
1295 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1296 # time: don't try to be smart on the time format, server-side will
1298 slice_cred = self.slice_credential_string(args[0])
1299 creds = [slice_cred]
1300 # options and call_id when supported
1302 api_options['call_id']=unique_call_id()
1303 if options.show_credential:
1304 show_credentials(creds)
1305 result = server.RenewSliver(slice_urn, creds, input_time, *self.ois(server,api_options))
1306 value = ReturnValue.get_value(result)
1307 if self.options.raw:
1308 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1314 @register_command("slice_hrn","")
1315 def shutdown(self, options, args):
1317 shutdown named slice (Shutdown)
1319 server = self.sliceapi()
1322 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1324 slice_cred = self.slice_credential_string(slice_hrn)
1325 creds = [slice_cred]
1326 result = server.Shutdown(slice_urn, creds)
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)
1335 @register_command("slice_hrn rspec","")
1336 def get_ticket(self, options, args):
1338 get a ticket for the specified slice
1340 server = self.sliceapi()
1342 slice_hrn, rspec_path = args[0], args[1]
1343 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1345 slice_cred = self.slice_credential_string(slice_hrn)
1346 creds = [slice_cred]
1348 rspec_file = self.get_rspec_file(rspec_path)
1349 rspec = open(rspec_file).read()
1350 # options and call_id when supported
1352 api_options['call_id']=unique_call_id()
1353 # get ticket at the server
1354 ticket_string = server.GetTicket(slice_urn, creds, rspec, *self.ois(server,api_options))
1356 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1357 self.logger.info("writing ticket to %s"%file)
1358 ticket = SfaTicket(string=ticket_string)
1359 ticket.save_to_file(filename=file, save_parents=True)
1361 @register_command("ticket","")
1362 def redeem_ticket(self, options, args):
1364 Connects to nodes in a slice and redeems a ticket
1365 (slice hrn is retrieved from the ticket)
1367 ticket_file = args[0]
1369 # get slice hrn from the ticket
1370 # use this to get the right slice credential
1371 ticket = SfaTicket(filename=ticket_file)
1373 ticket_string = ticket.save_to_string(save_parents=True)
1375 slice_hrn = ticket.gidObject.get_hrn()
1376 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1377 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1378 slice_cred = self.slice_credential_string(slice_hrn)
1380 # get a list of node hostnames from the RSpec
1381 tree = etree.parse(StringIO(ticket.rspec))
1382 root = tree.getroot()
1383 hostnames = root.xpath("./network/site/node/hostname/text()")
1385 # create an xmlrpc connection to the component manager at each of these
1386 # components and gall redeem_ticket
1388 for hostname in hostnames:
1390 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1391 cm_url="http://%s:%s/"%(hostname,CM_PORT)
1392 server = SfaServerProxy(cm_url, self.private_key, self.my_gid)
1393 server = self.server_proxy(hostname, CM_PORT, self.private_key,
1394 timeout=self.options.timeout, verbose=self.options.debug)
1395 server.RedeemTicket(ticket_string, slice_cred)
1396 self.logger.info("Success")
1397 except socket.gaierror:
1398 self.logger.error("redeem_ticket failed on %s: Component Manager not accepting requests"%hostname)
1399 except Exception, e:
1400 self.logger.log_exc(e.message)
1403 @register_command("[name]","")
1404 def gid(self, options, args):
1406 Create a GID (CreateGid)
1411 target_hrn = args[0]
1412 my_gid_string = open(self.client_bootstrap.my_gid()).read()
1413 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, my_gid_string)
1415 filename = options.file
1417 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1418 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1419 GID(string=gid).save_to_file(filename)
1421 ####################
1422 @register_command("to_hrn","""$ sfi delegate -u -p -s ple.inria.heartbeat -s ple.inria.omftest ple.upmc.slicebrowser
1424 will locally create a set of delegated credentials for the benefit of ple.upmc.slicebrowser
1425 the set of credentials in the scope for this call would be
1426 (*) ple.inria.thierry_parmentelat.user_for_ple.upmc.slicebrowser.user.cred
1428 (*) ple.inria.pi_for_ple.upmc.slicebrowser.user.cred
1430 (*) ple.inria.heartbeat.slice_for_ple.upmc.slicebrowser.user.cred
1431 (*) ple.inria.omftest.slice_for_ple.upmc.slicebrowser.user.cred
1432 because of the two -s options
1435 def delegate (self, options, args):
1437 (locally) create delegate credential for use by given hrn
1438 make sure to check for 'sfi myslice' instead if you plan
1445 # support for several delegations in the same call
1446 # so first we gather the things to do
1448 for slice_hrn in options.delegate_slices:
1449 message="%s.slice"%slice_hrn
1450 original = self.slice_credential_string(slice_hrn)
1451 tuples.append ( (message, original,) )
1452 if options.delegate_pi:
1453 my_authority=self.authority
1454 message="%s.pi"%my_authority
1455 original = self.my_authority_credential_string()
1456 tuples.append ( (message, original,) )
1457 for auth_hrn in options.delegate_auths:
1458 message="%s.auth"%auth_hrn
1459 original=self.authority_credential_string(auth_hrn)
1460 tuples.append ( (message, original, ) )
1461 # if nothing was specified at all at this point, let's assume -u
1462 if not tuples: options.delegate_user=True
1464 if options.delegate_user:
1465 message="%s.user"%self.user
1466 original = self.my_credential_string
1467 tuples.append ( (message, original, ) )
1469 # default type for beneficial is user unless -A
1470 if options.delegate_to_authority: to_type='authority'
1471 else: to_type='user'
1473 # let's now handle all this
1474 # it's all in the filenaming scheme
1475 for (message,original) in tuples:
1476 delegated_string = self.client_bootstrap.delegate_credential_string(original, to_hrn, to_type)
1477 delegated_credential = Credential (string=delegated_string)
1478 filename = os.path.join ( self.options.sfi_dir,
1479 "%s_for_%s.%s.cred"%(message,to_hrn,to_type))
1480 delegated_credential.save_to_file(filename, save_parents=True)
1481 self.logger.info("delegated credential for %s to %s and wrote to %s"%(message,to_hrn,filename))
1483 ####################
1484 @register_command("","""$ less +/myslice sfi_config
1486 backend = http://manifold.pl.sophia.inria.fr:7080
1487 # the HRN that myslice uses, so that we are delegating to
1488 delegate = ple.upmc.slicebrowser
1489 # platform - this is a myslice concept
1491 # username - as of this writing (May 2013) a simple login name
1495 will first collect the slices that you are part of, then make sure
1496 all your credentials are up-to-date (read: refresh expired ones)
1497 then compute delegated credentials for user 'ple.upmc.slicebrowser'
1498 and upload them all on myslice backend, using 'platform' and 'user'.
1499 A password will be prompted for the upload part.
1501 $ sfi -v myslice -- or sfi -vv myslice
1502 same but with more and more verbosity
1505 is synonym to sfi myslice as no other command starts with an 'm'
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(args,key):
1535 value=getattr(args,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 uploader = ManifoldUploader (logger=self.logger,
1597 url=myslice_dict['backend'],
1598 platform=myslice_dict['platform'],
1599 username=myslice_dict['username'],
1600 password=options.password)
1601 uploader.prompt_all()
1602 (count_all,count_success)=(0,0)
1603 for (hrn,htype,delegated_credential,filename) in hrn_delegated_credentials:
1605 inspect=Credential(string=delegated_credential)
1606 expire_datetime=inspect.get_expiration()
1607 message="%s (%s) [exp:%s]"%(hrn,htype,expire_datetime)
1608 if uploader.upload(delegated_credential,message=message):
1611 self.logger.info("Successfully uploaded %d/%d credentials"%(count_success,count_all))
1613 # at first I thought we would want to save these,
1614 # like 'sfi delegate does' but on second thought
1615 # it is probably not helpful as people would not
1616 # need to run 'sfi delegate' at all anymore
1617 if count_success != count_all: sys.exit(1)
1620 @register_command("cred","")
1621 def trusted(self, options, args):
1623 return the trusted certs at this interface (get_trusted_certs)
1625 trusted_certs = self.registry().get_trusted_certs()
1626 for trusted_cert in trusted_certs:
1627 print "\n===========================================================\n"
1628 gid = GID(string=trusted_cert)
1630 cert = Certificate(string=trusted_cert)
1631 self.logger.debug('Sfi.trusted -> %r'%cert.get_subject())
1632 print "Certificate:\n%s\n\n"%trusted_cert