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("-l","--local",
383 action="store_true", dest="version_local", default=False,
384 help="display version of the local client")
386 if command in ("version", "trusted"):
387 parser.add_option("-R","--registry_interface",
388 action="store_true", dest="registry_interface", default=False,
389 help="target the registry interface instead of slice interface")
391 if command in ("add", "update"):
392 parser.add_option('-x', '--xrn', dest='xrn', metavar='<xrn>', help='object hrn/urn (mandatory)')
393 parser.add_option('-t', '--type', dest='type', metavar='<type>', help='object type', default=None)
394 parser.add_option('-e', '--email', dest='email', default="", help="email (mandatory for users)")
395 parser.add_option('-k', '--key', dest='key', metavar='<key>', help='public key string or file',
397 parser.add_option('-s', '--slices', dest='slices', metavar='<slices>', help='Set/replace slice xrns',
398 default='', type="str", action='callback', callback=optparse_listvalue_callback)
399 parser.add_option('-r', '--researchers', dest='researchers', metavar='<researchers>',
400 help='Set/replace slice researchers', default='', type="str", action='callback',
401 callback=optparse_listvalue_callback)
402 parser.add_option('-p', '--pis', dest='pis', metavar='<PIs>', help='Set/replace Principal Investigators/Project Managers',
403 default='', type="str", action='callback', callback=optparse_listvalue_callback)
404 parser.add_option ('-X','--extra',dest='extras',default={},type='str',metavar="<EXTRA_ASSIGNS>",
405 action="callback", callback=optparse_dictvalue_callback, nargs=1,
406 help="set extra/testbed-dependent flags, e.g. --extra enabled=true")
408 # show_credential option
409 if command in ("list","resources","create","add","update","remove","slices","delete","status","renew"):
410 parser.add_option("-C","--credential",dest='show_credential',action='store_true',default=False,
411 help="show credential(s) used in human-readable form")
412 # registy filter option
413 if command in ("list", "show", "remove"):
414 parser.add_option("-t", "--type", dest="type", type="choice",
415 help="type filter ([all]|user|slice|authority|node|aggregate)",
416 choices=("all", "user", "slice", "authority", "node", "aggregate"),
418 if command in ("show"):
419 parser.add_option("-k","--key",dest="keys",action="append",default=[],
420 help="specify specific keys to be displayed from record")
421 if command in ("resources"):
423 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
424 help="schema type and version of resulting RSpec")
425 # disable/enable cached rspecs
426 parser.add_option("-c", "--current", dest="current", default=False,
428 help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
430 parser.add_option("-f", "--format", dest="format", type="choice",
431 help="display format ([xml]|dns|ip)", default="xml",
432 choices=("xml", "dns", "ip"))
433 #panos: a new option to define the type of information about resources a user is interested in
434 parser.add_option("-i", "--info", dest="info",
435 help="optional component information", default=None)
436 # a new option to retreive or not reservation-oriented RSpecs (leases)
437 parser.add_option("-l", "--list_leases", dest="list_leases", type="choice",
438 help="Retreive or not reservation-oriented RSpecs ([resources]|leases|all )",
439 choices=("all", "resources", "leases"), default="resources")
442 # 'create' does return the new rspec, makes sense to save that too
443 if command in ("resources", "show", "list", "gid", 'create'):
444 parser.add_option("-o", "--output", dest="file",
445 help="output XML to file", metavar="FILE", default=None)
447 if command in ("show", "list"):
448 parser.add_option("-f", "--format", dest="format", type="choice",
449 help="display format ([text]|xml)", default="text",
450 choices=("text", "xml"))
452 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
453 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
454 choices=("xml", "xmllist", "hrnlist"))
455 if command == 'list':
456 parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
457 help="list all child records", default=False)
458 parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
459 help="gives details, like user keys", default=False)
460 if command in ("delegate"):
461 parser.add_option("-u", "--user",
462 action="store_true", dest="delegate_user", default=False,
463 help="delegate your own credentials; default if no other option is provided")
464 parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
465 metavar="slice_hrn", help="delegate cred. for slice HRN")
466 parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
467 metavar='auth_hrn', help="delegate cred for auth HRN")
468 # this primarily is a shorthand for -a my_hrn
469 parser.add_option("-p", "--pi", dest='delegate_pi', default=None, action='store_true',
470 help="delegate your PI credentials, so s.t. like -a your_hrn^")
471 parser.add_option("-A","--to-authority",dest='delegate_to_authority',action='store_true',default=False,
472 help="""by default the mandatory argument is expected to be a user,
473 use this if you mean an authority instead""")
475 if command in ("myslice"):
476 parser.add_option("-p","--password",dest='password',action='store',default=None,
477 help="specify mainfold password on the command line")
478 parser.add_option("-s", "--slice", dest="delegate_slices",action='append',default=[],
479 metavar="slice_hrn", help="delegate cred. for slice HRN")
480 parser.add_option("-a", "--auths", dest='delegate_auths',action='append',default=[],
481 metavar='auth_hrn', help="delegate PI cred for auth HRN")
482 parser.add_option('-d', '--delegate', dest='delegate', help="Override 'delegate' from the config file")
483 parser.add_option('-b', '--backend', dest='backend', help="Override 'backend' from the config file")
489 # Main: parse arguments and dispatch to command
491 def dispatch(self, command, command_options, command_args):
492 method=getattr(self, command, None)
494 print "Unknown command %s"%command
496 return method(command_options, command_args)
499 self.sfi_parser = self.create_parser_global()
500 (options, args) = self.sfi_parser.parse_args()
502 self.print_commands_help(options)
504 self.options = options
506 self.logger.setLevelFromOptVerbose(self.options.verbose)
509 self.logger.critical("No command given. Use -h for help.")
510 self.print_commands_help(options)
513 # complete / find unique match with command set
514 command_candidates = Candidates (commands_list)
516 command = command_candidates.only_match(input)
518 self.print_commands_help(options)
520 # second pass options parsing
522 self.command_parser = self.create_parser_command(command)
523 (command_options, command_args) = self.command_parser.parse_args(args[1:])
524 if command_options.help:
527 self.command_options = command_options
531 self.logger.debug("Command=%s" % self.command)
534 self.dispatch(command, command_options, command_args)
538 self.logger.log_exc ("sfi command %s failed"%command)
544 def read_config(self):
545 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
546 shell_config_file = os.path.join(self.options.sfi_dir,"sfi_config.sh")
548 if Config.is_ini(config_file):
549 config = Config (config_file)
551 # try upgrading from shell config format
552 fp, fn = mkstemp(suffix='sfi_config', text=True)
554 # we need to preload the sections we want parsed
555 # from the shell config
556 config.add_section('sfi')
557 # sface users should be able to use this same file to configure their stuff
558 config.add_section('sface')
559 # manifold users should be able to specify the details
560 # of their backend server here for 'sfi myslice'
561 config.add_section('myslice')
562 config.load(config_file)
564 shutil.move(config_file, shell_config_file)
566 config.save(config_file)
569 self.logger.critical("Failed to read configuration file %s"%config_file)
570 self.logger.info("Make sure to remove the export clauses and to add quotes")
571 if self.options.verbose==0:
572 self.logger.info("Re-run with -v for more details")
574 self.logger.log_exc("Could not read config file %s"%config_file)
577 self.config_instance=config
580 if (self.options.sm is not None):
581 self.sm_url = self.options.sm
582 elif hasattr(config, "SFI_SM"):
583 self.sm_url = config.SFI_SM
585 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
589 if (self.options.registry is not None):
590 self.reg_url = self.options.registry
591 elif hasattr(config, "SFI_REGISTRY"):
592 self.reg_url = config.SFI_REGISTRY
594 self.logger.error("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
598 if (self.options.user is not None):
599 self.user = self.options.user
600 elif hasattr(config, "SFI_USER"):
601 self.user = config.SFI_USER
603 self.logger.error("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
607 if (self.options.auth is not None):
608 self.authority = self.options.auth
609 elif hasattr(config, "SFI_AUTH"):
610 self.authority = config.SFI_AUTH
612 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
615 self.config_file=config_file
620 # Get various credential and spec files
622 # Establishes limiting conventions
623 # - conflates MAs and SAs
624 # - assumes last token in slice name is unique
626 # Bootstraps credentials
627 # - bootstrap user credential from self-signed certificate
628 # - bootstrap authority credential from user credential
629 # - bootstrap slice credential from user credential
632 # init self-signed cert, user credentials and gid
633 def bootstrap (self):
634 client_bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir,
636 # if -k is provided, use this to initialize private key
637 if self.options.user_private_key:
638 client_bootstrap.init_private_key_if_missing (self.options.user_private_key)
640 # trigger legacy compat code if needed
641 # the name has changed from just <leaf>.pkey to <hrn>.pkey
642 if not os.path.isfile(client_bootstrap.private_key_filename()):
643 self.logger.info ("private key not found, trying legacy name")
645 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%Xrn.unescape(get_leaf(self.user)))
646 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
647 client_bootstrap.init_private_key_if_missing (legacy_private_key)
648 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
650 self.logger.log_exc("Can't find private key ")
654 client_bootstrap.bootstrap_my_gid()
655 # extract what's needed
656 self.private_key = client_bootstrap.private_key()
657 self.my_credential_string = client_bootstrap.my_credential_string ()
658 self.my_gid = client_bootstrap.my_gid ()
659 self.client_bootstrap = client_bootstrap
662 def my_authority_credential_string(self):
663 if not self.authority:
664 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
666 return self.client_bootstrap.authority_credential_string (self.authority)
668 def authority_credential_string(self, auth_hrn):
669 return self.client_bootstrap.authority_credential_string (auth_hrn)
671 def slice_credential_string(self, name):
672 return self.client_bootstrap.slice_credential_string (name)
675 # Management of the servers
680 if not hasattr (self, 'registry_proxy'):
681 self.logger.info("Contacting Registry at: %s"%self.reg_url)
682 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
683 timeout=self.options.timeout, verbose=self.options.debug)
684 return self.registry_proxy
688 if not hasattr (self, 'sliceapi_proxy'):
689 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
690 if hasattr(self.command_options,'component') and self.command_options.component:
691 # resolve the hrn at the registry
692 node_hrn = self.command_options.component
693 records = self.registry().Resolve(node_hrn, self.my_credential_string)
694 records = filter_records('node', records)
696 self.logger.warning("No such component:%r"% opts.component)
698 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
699 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
701 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
702 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
703 self.sm_url = 'http://' + self.sm_url
704 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
705 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
706 timeout=self.options.timeout, verbose=self.options.debug)
707 return self.sliceapi_proxy
709 def get_cached_server_version(self, server):
710 # check local cache first
713 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
714 cache_key = server.url + "-version"
716 cache = Cache(cache_file)
719 self.logger.info("Local cache not found at: %s" % cache_file)
722 version = cache.get(cache_key)
725 result = server.GetVersion()
726 version= ReturnValue.get_value(result)
727 # cache version for 20 minutes
728 cache.add(cache_key, version, ttl= 60*20)
729 self.logger.info("Updating cache file %s" % cache_file)
730 cache.save_to_file(cache_file)
734 ### resurrect this temporarily so we can support V1 aggregates for a while
735 def server_supports_options_arg(self, server):
737 Returns true if server support the optional call_id arg, false otherwise.
739 server_version = self.get_cached_server_version(server)
741 # xxx need to rewrite this
742 if int(server_version.get('geni_api')) >= 2:
746 def server_supports_call_id_arg(self, server):
747 server_version = self.get_cached_server_version(server)
749 if 'sfa' in server_version and 'code_tag' in server_version:
750 code_tag = server_version['code_tag']
751 code_tag_parts = code_tag.split("-")
752 version_parts = code_tag_parts[0].split(".")
753 major, minor = version_parts[0], version_parts[1]
754 rev = code_tag_parts[1]
755 if int(major) == 1 and minor == 0 and build >= 22:
759 ### ois = options if supported
760 # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
761 def ois (self, server, option_dict):
762 if self.server_supports_options_arg (server):
764 elif self.server_supports_call_id_arg (server):
765 return [ unique_call_id () ]
769 ### cis = call_id if supported - like ois
770 def cis (self, server):
771 if self.server_supports_call_id_arg (server):
772 return [ unique_call_id ]
776 ######################################## miscell utilities
777 def get_rspec_file(self, rspec):
778 if (os.path.isabs(rspec)):
781 file = os.path.join(self.options.sfi_dir, rspec)
782 if (os.path.isfile(file)):
785 self.logger.critical("No such rspec file %s"%rspec)
788 def get_record_file(self, record):
789 if (os.path.isabs(record)):
792 file = os.path.join(self.options.sfi_dir, record)
793 if (os.path.isfile(file)):
796 self.logger.critical("No such registry record file %s"%record)
800 #==========================================================================
801 # Following functions implement the commands
803 # Registry-related commands
804 #==========================================================================
806 @register_command("","")
807 def config (self, options, args):
808 "Display contents of current config"
809 print "# From configuration file %s"%self.config_file
810 flags=[ ('sfi', [ ('registry','reg_url'),
811 ('auth','authority'),
817 flags.append ( ('myslice', ['backend', 'delegate', 'platform', 'username'] ) )
819 for (section, tuples) in flags:
822 for (external_name, internal_name) in tuples:
823 print "%-20s = %s"%(external_name,getattr(self,internal_name))
826 varname="%s_%s"%(section.upper(),name.upper())
827 value=getattr(self.config_instance,varname)
828 print "%-20s = %s"%(name,value)
830 @register_command("","")
831 def version(self, options, args):
833 display an SFA server version (GetVersion)
834 or version information about sfi itself
836 if options.version_local:
837 version=version_core()
839 if options.registry_interface:
840 server=self.registry()
842 server = self.sliceapi()
843 result = server.GetVersion()
844 version = ReturnValue.get_value(result)
846 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
848 pprinter = PrettyPrinter(indent=4)
849 pprinter.pprint(version)
851 @register_command("authority","")
852 def list(self, options, args):
854 list entries in named authority registry (List)
861 if options.recursive:
862 opts['recursive'] = options.recursive
864 if options.show_credential:
865 show_credentials(self.my_credential_string)
867 list = self.registry().List(hrn, self.my_credential_string, options)
869 raise Exception, "Not enough parameters for the 'list' command"
871 # filter on person, slice, site, node, etc.
872 # This really should be in the self.filter_records funct def comment...
873 list = filter_records(options.type, list)
874 terminal_render (list, options)
876 save_records_to_file(options.file, list, options.fileformat)
879 @register_command("name","")
880 def show(self, options, args):
882 show details about named registry record (Resolve)
888 # explicitly require Resolve to run in details mode
889 record_dicts = self.registry().Resolve(hrn, self.my_credential_string, {'details':True})
890 record_dicts = filter_records(options.type, record_dicts)
892 self.logger.error("No record of type %s"% options.type)
894 # user has required to focus on some keys
896 def project (record):
898 for key in options.keys:
899 try: projected[key]=record[key]
902 record_dicts = [ project (record) for record in record_dicts ]
903 records = [ Record(dict=record_dict) for record_dict in record_dicts ]
904 for record in records:
905 if (options.format == "text"): record.dump(sort=True)
906 else: print record.save_as_xml()
908 save_records_to_file(options.file, record_dicts, options.fileformat)
911 @register_command("[xml-filename]","")
912 def add(self, options, args):
913 """add record into registry (Register)
914 from command line options (recommended)
915 old-school method involving an xml file still supported"""
917 auth_cred = self.my_authority_credential_string()
918 if options.show_credential:
919 show_credentials(auth_cred)
926 record_filepath = args[0]
927 rec_file = self.get_record_file(record_filepath)
928 record_dict.update(load_record_from_file(rec_file).todict())
930 print "Cannot load record file %s"%record_filepath
933 record_dict.update(load_record_from_opts(options).todict())
934 # we should have a type by now
935 if 'type' not in record_dict :
938 # this is still planetlab dependent.. as plc will whine without that
939 # also, it's only for adding
940 if record_dict['type'] == 'user':
941 if not 'first_name' in record_dict:
942 record_dict['first_name'] = record_dict['hrn']
943 if 'last_name' not in record_dict:
944 record_dict['last_name'] = record_dict['hrn']
945 return self.registry().Register(record_dict, auth_cred)
947 @register_command("[xml-filename]","")
948 def update(self, options, args):
949 """update record into registry (Update)
950 from command line options (recommended)
951 old-school method involving an xml file still supported"""
954 record_filepath = args[0]
955 rec_file = self.get_record_file(record_filepath)
956 record_dict.update(load_record_from_file(rec_file).todict())
958 record_dict.update(load_record_from_opts(options).todict())
959 # at the very least we need 'type' here
960 if 'type' not in record_dict:
964 # don't translate into an object, as this would possibly distort
965 # user-provided data; e.g. add an 'email' field to Users
966 if record_dict['type'] == "user":
967 if record_dict['hrn'] == self.user:
968 cred = self.my_credential_string
970 cred = self.my_authority_credential_string()
971 elif record_dict['type'] in ["slice"]:
973 cred = self.slice_credential_string(record_dict['hrn'])
974 except ServerException, e:
975 # XXX smbaker -- once we have better error return codes, update this
976 # to do something better than a string compare
977 if "Permission error" in e.args[0]:
978 cred = self.my_authority_credential_string()
981 elif record_dict['type'] in ["authority"]:
982 cred = self.my_authority_credential_string()
983 elif record_dict['type'] == 'node':
984 cred = self.my_authority_credential_string()
986 raise "unknown record type" + record_dict['type']
987 if options.show_credential:
988 show_credentials(cred)
989 return self.registry().Update(record_dict, cred)
991 @register_command("hrn","")
992 def remove(self, options, args):
993 "remove registry record by name (Remove)"
994 auth_cred = self.my_authority_credential_string()
1002 if options.show_credential:
1003 show_credentials(auth_cred)
1004 return self.registry().Remove(hrn, auth_cred, type)
1006 # ==================================================================
1007 # Slice-related commands
1008 # ==================================================================
1010 @register_command("","")
1011 def slices(self, options, args):
1012 "list instantiated slices (ListSlices) - returns urn's"
1013 server = self.sliceapi()
1015 creds = [self.my_credential_string]
1016 # options and call_id when supported
1018 api_options['call_id']=unique_call_id()
1019 if options.show_credential:
1020 show_credentials(creds)
1021 result = server.ListSlices(creds, *self.ois(server,api_options))
1022 value = ReturnValue.get_value(result)
1023 if self.options.raw:
1024 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1029 # show rspec for named slice
1030 @register_command("[slice_hrn]","")
1031 def resources(self, options, args):
1033 with no arg, discover available resources, (ListResources)
1034 or with an slice hrn, shows currently provisioned resources
1036 server = self.sliceapi()
1041 the_credential=self.slice_credential_string(args[0])
1042 creds.append(the_credential)
1044 the_credential=self.my_credential_string
1045 creds.append(the_credential)
1046 if options.show_credential:
1047 show_credentials(creds)
1049 # no need to check if server accepts the options argument since the options has
1050 # been a required argument since v1 API
1052 # always send call_id to v2 servers
1053 api_options ['call_id'] = unique_call_id()
1054 # ask for cached value if available
1055 api_options ['cached'] = True
1058 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
1060 api_options['info'] = options.info
1061 if options.list_leases:
1062 api_options['list_leases'] = options.list_leases
1064 if options.current == True:
1065 api_options['cached'] = False
1067 api_options['cached'] = True
1068 if options.rspec_version:
1069 version_manager = VersionManager()
1070 server_version = self.get_cached_server_version(server)
1071 if 'sfa' in server_version:
1072 # just request the version the client wants
1073 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
1075 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'}
1077 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3'}
1078 result = server.ListResources (creds, api_options)
1079 value = ReturnValue.get_value(result)
1080 if self.options.raw:
1081 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1082 if options.file is not None:
1083 save_rspec_to_file(value, options.file)
1084 if (self.options.raw is None) and (options.file is None):
1085 display_rspec(value, options.format)
1089 @register_command("slice_hrn rspec","")
1090 def create(self, options, args):
1092 create or update named slice with given rspec (CreateSliver)
1094 server = self.sliceapi()
1096 # xxx do we need to check usage (len(args)) ?
1099 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1102 creds = [self.slice_credential_string(slice_hrn)]
1104 delegated_cred = None
1105 server_version = self.get_cached_server_version(server)
1106 if server_version.get('interface') == 'slicemgr':
1107 # delegate our cred to the slice manager
1108 # do not delegate cred to slicemgr...not working at the moment
1110 #if server_version.get('hrn'):
1111 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1112 #elif server_version.get('urn'):
1113 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1115 if options.show_credential:
1116 show_credentials(creds)
1119 rspec_file = self.get_rspec_file(args[1])
1120 rspec = open(rspec_file).read()
1123 # need to pass along user keys to the aggregate.
1125 # { urn: urn:publicid:IDN+emulab.net+user+alice
1126 # keys: [<ssh key A>, <ssh key B>]
1129 # xxx Thierry 2012 sept. 21
1130 # contrary to what I was first thinking, calling Resolve with details=False does not yet work properly here
1131 # I am turning details=True on again on a - hopefully - temporary basis, just to get this whole thing to work again
1132 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
1133 # slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string], {'details':True})
1134 if slice_records and 'reg-researchers' in slice_records[0] and slice_records[0]['reg-researchers']:
1135 slice_record = slice_records[0]
1136 user_hrns = slice_record['reg-researchers']
1137 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1138 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
1140 if 'sfa' not in server_version:
1141 users = pg_users_arg(user_records)
1142 #rspec = RSpec(rspec)
1143 #rspec.filter({'component_manager_id': server_version['urn']})
1144 #rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1146 users = sfa_users_arg(user_records, slice_record)
1148 # do not append users, keys, or slice tags. Anything
1149 # not contained in this request will be removed from the slice
1151 # CreateSliver has supported the options argument for a while now so it should
1152 # be safe to assume this server support it
1154 api_options ['append'] = False
1155 api_options ['call_id'] = unique_call_id()
1156 result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server, api_options))
1157 value = ReturnValue.get_value(result)
1158 if self.options.raw:
1159 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1160 if options.file is not None:
1161 save_rspec_to_file (value, options.file)
1162 if (self.options.raw is None) and (options.file is None):
1167 @register_command("slice_hrn","")
1168 def delete(self, options, args):
1170 delete named slice (DeleteSliver)
1172 server = self.sliceapi()
1176 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1179 slice_cred = self.slice_credential_string(slice_hrn)
1180 creds = [slice_cred]
1182 # options and call_id when supported
1184 api_options ['call_id'] = unique_call_id()
1185 if options.show_credential:
1186 show_credentials(creds)
1187 result = server.DeleteSliver(slice_urn, creds, *self.ois(server, api_options ) )
1188 value = ReturnValue.get_value(result)
1189 if self.options.raw:
1190 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1195 @register_command("slice_hrn","")
1196 def status(self, options, args):
1198 retrieve slice status (SliverStatus)
1200 server = self.sliceapi()
1204 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1207 slice_cred = self.slice_credential_string(slice_hrn)
1208 creds = [slice_cred]
1210 # options and call_id when supported
1212 api_options['call_id']=unique_call_id()
1213 if options.show_credential:
1214 show_credentials(creds)
1215 result = server.SliverStatus(slice_urn, creds, *self.ois(server,api_options))
1216 value = ReturnValue.get_value(result)
1217 if self.options.raw:
1218 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1222 @register_command("slice_hrn","")
1223 def start(self, options, args):
1225 start named slice (Start)
1227 server = self.sliceapi()
1231 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1234 slice_cred = self.slice_credential_string(args[0])
1235 creds = [slice_cred]
1236 # xxx Thierry - does this not need an api_options as well ?
1237 result = server.Start(slice_urn, creds)
1238 value = ReturnValue.get_value(result)
1239 if self.options.raw:
1240 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1245 @register_command("slice_hrn","")
1246 def stop(self, options, args):
1248 stop named slice (Stop)
1250 server = self.sliceapi()
1253 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1255 slice_cred = self.slice_credential_string(args[0])
1256 creds = [slice_cred]
1257 result = server.Stop(slice_urn, creds)
1258 value = ReturnValue.get_value(result)
1259 if self.options.raw:
1260 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1266 @register_command("slice_hrn","")
1267 def reset(self, options, args):
1269 reset named slice (reset_slice)
1271 server = self.sliceapi()
1274 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1276 slice_cred = self.slice_credential_string(args[0])
1277 creds = [slice_cred]
1278 result = server.reset_slice(creds, slice_urn)
1279 value = ReturnValue.get_value(result)
1280 if self.options.raw:
1281 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1286 @register_command("slice_hrn time","")
1287 def renew(self, options, args):
1289 renew slice (RenewSliver)
1291 server = self.sliceapi()
1295 [ slice_hrn, input_time ] = args
1297 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1298 # time: don't try to be smart on the time format, server-side will
1300 slice_cred = self.slice_credential_string(args[0])
1301 creds = [slice_cred]
1302 # options and call_id when supported
1304 api_options['call_id']=unique_call_id()
1305 if options.show_credential:
1306 show_credentials(creds)
1307 result = server.RenewSliver(slice_urn, creds, input_time, *self.ois(server,api_options))
1308 value = ReturnValue.get_value(result)
1309 if self.options.raw:
1310 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1316 @register_command("slice_hrn","")
1317 def shutdown(self, options, args):
1319 shutdown named slice (Shutdown)
1321 server = self.sliceapi()
1324 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1326 slice_cred = self.slice_credential_string(slice_hrn)
1327 creds = [slice_cred]
1328 result = server.Shutdown(slice_urn, creds)
1329 value = ReturnValue.get_value(result)
1330 if self.options.raw:
1331 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1337 @register_command("slice_hrn rspec","")
1338 def get_ticket(self, options, args):
1340 get a ticket for the specified slice
1342 server = self.sliceapi()
1344 slice_hrn, rspec_path = args[0], args[1]
1345 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1347 slice_cred = self.slice_credential_string(slice_hrn)
1348 creds = [slice_cred]
1350 rspec_file = self.get_rspec_file(rspec_path)
1351 rspec = open(rspec_file).read()
1352 # options and call_id when supported
1354 api_options['call_id']=unique_call_id()
1355 # get ticket at the server
1356 ticket_string = server.GetTicket(slice_urn, creds, rspec, *self.ois(server,api_options))
1358 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1359 self.logger.info("writing ticket to %s"%file)
1360 ticket = SfaTicket(string=ticket_string)
1361 ticket.save_to_file(filename=file, save_parents=True)
1363 @register_command("ticket","")
1364 def redeem_ticket(self, options, args):
1366 Connects to nodes in a slice and redeems a ticket
1367 (slice hrn is retrieved from the ticket)
1369 ticket_file = args[0]
1371 # get slice hrn from the ticket
1372 # use this to get the right slice credential
1373 ticket = SfaTicket(filename=ticket_file)
1375 ticket_string = ticket.save_to_string(save_parents=True)
1377 slice_hrn = ticket.gidObject.get_hrn()
1378 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1379 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1380 slice_cred = self.slice_credential_string(slice_hrn)
1382 # get a list of node hostnames from the RSpec
1383 tree = etree.parse(StringIO(ticket.rspec))
1384 root = tree.getroot()
1385 hostnames = root.xpath("./network/site/node/hostname/text()")
1387 # create an xmlrpc connection to the component manager at each of these
1388 # components and gall redeem_ticket
1390 for hostname in hostnames:
1392 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1393 cm_url="http://%s:%s/"%(hostname,CM_PORT)
1394 server = SfaServerProxy(cm_url, self.private_key, self.my_gid)
1395 server = self.server_proxy(hostname, CM_PORT, self.private_key,
1396 timeout=self.options.timeout, verbose=self.options.debug)
1397 server.RedeemTicket(ticket_string, slice_cred)
1398 self.logger.info("Success")
1399 except socket.gaierror:
1400 self.logger.error("redeem_ticket failed on %s: Component Manager not accepting requests"%hostname)
1401 except Exception, e:
1402 self.logger.log_exc(e.message)
1405 @register_command("[name]","")
1406 def gid(self, options, args):
1408 Create a GID (CreateGid)
1413 target_hrn = args[0]
1414 my_gid_string = open(self.client_bootstrap.my_gid()).read()
1415 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, my_gid_string)
1417 filename = options.file
1419 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1420 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1421 GID(string=gid).save_to_file(filename)
1423 ####################
1424 @register_command("to_hrn","""$ sfi delegate -u -p -s ple.inria.heartbeat -s ple.inria.omftest ple.upmc.slicebrowser
1426 will locally create a set of delegated credentials for the benefit of ple.upmc.slicebrowser
1427 the set of credentials in the scope for this call would be
1428 (*) ple.inria.thierry_parmentelat.user_for_ple.upmc.slicebrowser.user.cred
1430 (*) ple.inria.pi_for_ple.upmc.slicebrowser.user.cred
1432 (*) ple.inria.heartbeat.slice_for_ple.upmc.slicebrowser.user.cred
1433 (*) ple.inria.omftest.slice_for_ple.upmc.slicebrowser.user.cred
1434 because of the two -s options
1437 def delegate (self, options, args):
1439 (locally) create delegate credential for use by given hrn
1440 make sure to check for 'sfi myslice' instead if you plan
1447 # support for several delegations in the same call
1448 # so first we gather the things to do
1450 for slice_hrn in options.delegate_slices:
1451 message="%s.slice"%slice_hrn
1452 original = self.slice_credential_string(slice_hrn)
1453 tuples.append ( (message, original,) )
1454 if options.delegate_pi:
1455 my_authority=self.authority
1456 message="%s.pi"%my_authority
1457 original = self.my_authority_credential_string()
1458 tuples.append ( (message, original,) )
1459 for auth_hrn in options.delegate_auths:
1460 message="%s.auth"%auth_hrn
1461 original=self.authority_credential_string(auth_hrn)
1462 tuples.append ( (message, original, ) )
1463 # if nothing was specified at all at this point, let's assume -u
1464 if not tuples: options.delegate_user=True
1466 if options.delegate_user:
1467 message="%s.user"%self.user
1468 original = self.my_credential_string
1469 tuples.append ( (message, original, ) )
1471 # default type for beneficial is user unless -A
1472 if options.delegate_to_authority: to_type='authority'
1473 else: to_type='user'
1475 # let's now handle all this
1476 # it's all in the filenaming scheme
1477 for (message,original) in tuples:
1478 delegated_string = self.client_bootstrap.delegate_credential_string(original, to_hrn, to_type)
1479 delegated_credential = Credential (string=delegated_string)
1480 filename = os.path.join ( self.options.sfi_dir,
1481 "%s_for_%s.%s.cred"%(message,to_hrn,to_type))
1482 delegated_credential.save_to_file(filename, save_parents=True)
1483 self.logger.info("delegated credential for %s to %s and wrote to %s"%(message,to_hrn,filename))
1485 ####################
1486 @register_command("","""$ less +/myslice sfi_config
1488 backend = http://manifold.pl.sophia.inria.fr:7080
1489 # the HRN that myslice uses, so that we are delegating to
1490 delegate = ple.upmc.slicebrowser
1491 # platform - this is a myslice concept
1493 # username - as of this writing (May 2013) a simple login name
1497 will first collect the slices that you are part of, then make sure
1498 all your credentials are up-to-date (read: refresh expired ones)
1499 then compute delegated credentials for user 'ple.upmc.slicebrowser'
1500 and upload them all on myslice backend, using 'platform' and 'user'.
1501 A password will be prompted for the upload part.
1503 $ sfi -v myslice -- or sfi -vv myslice
1504 same but with more and more verbosity
1506 $ sfi m -b http://mymanifold.foo.com:7080/
1507 is synonym to sfi myslice as no other command starts with an 'm'
1508 and uses a custom backend for this one call
1510 ) # register_command
1511 def myslice (self, options, args):
1513 """ This helper is for refreshing your credentials at myslice; it will
1514 * compute all the slices that you currently have credentials on
1515 * refresh all your credentials (you as a user and pi, your slices)
1516 * upload them to the manifold backend server
1517 for last phase, sfi_config is read to look for the [myslice] section,
1518 and namely the 'backend', 'delegate' and 'user' settings"""
1524 # enable info by default
1525 self.logger.setLevelFromOptVerbose(self.options.verbose+1)
1526 ### the rough sketch goes like this
1527 # (0) produce a p12 file
1528 self.client_bootstrap.my_pkcs12()
1530 # (a) rain check for sufficient config in sfi_config
1532 myslice_keys=[ 'backend', 'delegate', 'platform', 'username']
1533 for key in myslice_keys:
1535 # oct 2013 - I'm finding myself juggling with config files
1536 # so a couple of command-line options can now override config
1537 if hasattr(options,key) and getattr(options,key) is not None:
1538 value=getattr(options,key)
1540 full_key="MYSLICE_" + key.upper()
1541 value=getattr(self.config_instance,full_key,None)
1542 if value: myslice_dict[key]=value
1543 else: print "Unsufficient config, missing key %s in [myslice] section of sfi_config"%key
1544 if len(myslice_dict) != len(myslice_keys):
1547 # (b) figure whether we are PI for the authority where we belong
1548 self.logger.info("Resolving our own id %s"%self.user)
1549 my_records=self.registry().Resolve(self.user,self.my_credential_string)
1550 if len(my_records)!=1: print "Cannot Resolve %s -- exiting"%self.user; sys.exit(1)
1551 my_record=my_records[0]
1552 my_auths_all = my_record['reg-pi-authorities']
1553 self.logger.info("Found %d authorities that we are PI for"%len(my_auths_all))
1554 self.logger.debug("They are %s"%(my_auths_all))
1556 my_auths = my_auths_all
1557 if options.delegate_auths:
1558 my_auths = list(set(my_auths_all).intersection(set(options.delegate_auths)))
1559 self.logger.debug("Restricted to user-provided auths"%(my_auths))
1561 # (c) get the set of slices that we are in
1562 my_slices_all=my_record['reg-slices']
1563 self.logger.info("Found %d slices that we are member of"%len(my_slices_all))
1564 self.logger.debug("They are: %s"%(my_slices_all))
1566 my_slices = my_slices_all
1567 # if user provided slices, deal only with these - if they are found
1568 if options.delegate_slices:
1569 my_slices = list(set(my_slices_all).intersection(set(options.delegate_slices)))
1570 self.logger.debug("Restricted to user-provided slices: %s"%(my_slices))
1572 # (d) make sure we have *valid* credentials for all these
1574 hrn_credentials.append ( (self.user, 'user', self.my_credential_string,) )
1575 for auth_hrn in my_auths:
1576 hrn_credentials.append ( (auth_hrn, 'auth', self.authority_credential_string(auth_hrn),) )
1577 for slice_hrn in my_slices:
1578 hrn_credentials.append ( (slice_hrn, 'slice', self.slice_credential_string (slice_hrn),) )
1580 # (e) check for the delegated version of these
1581 # xxx todo add an option -a/-A? like for 'sfi delegate' for when we ever
1582 # switch to myslice using an authority instead of a user
1583 delegatee_type='user'
1584 delegatee_hrn=myslice_dict['delegate']
1585 hrn_delegated_credentials = []
1586 for (hrn, htype, credential) in hrn_credentials:
1587 delegated_credential = self.client_bootstrap.delegate_credential_string (credential, delegatee_hrn, delegatee_type)
1588 # save these so user can monitor what she's uploaded
1589 filename = os.path.join ( self.options.sfi_dir,
1590 "%s.%s_for_%s.%s.cred"%(hrn,htype,delegatee_hrn,delegatee_type))
1591 with file(filename,'w') as f:
1592 f.write(delegated_credential)
1593 self.logger.debug("(Over)wrote %s"%filename)
1594 hrn_delegated_credentials.append ((hrn, htype, delegated_credential, filename, ))
1596 # (f) and finally upload them to manifold server
1597 # xxx todo add an option so the password can be set on the command line
1598 # (but *NOT* in the config file) so other apps can leverage this
1599 self.logger.info("Uploading on backend at %s"%myslice_dict['backend'])
1600 uploader = ManifoldUploader (logger=self.logger,
1601 url=myslice_dict['backend'],
1602 platform=myslice_dict['platform'],
1603 username=myslice_dict['username'],
1604 password=options.password)
1605 uploader.prompt_all()
1606 (count_all,count_success)=(0,0)
1607 for (hrn,htype,delegated_credential,filename) in hrn_delegated_credentials:
1609 inspect=Credential(string=delegated_credential)
1610 expire_datetime=inspect.get_expiration()
1611 message="%s (%s) [exp:%s]"%(hrn,htype,expire_datetime)
1612 if uploader.upload(delegated_credential,message=message):
1615 self.logger.info("Successfully uploaded %d/%d credentials"%(count_success,count_all))
1617 # at first I thought we would want to save these,
1618 # like 'sfi delegate does' but on second thought
1619 # it is probably not helpful as people would not
1620 # need to run 'sfi delegate' at all anymore
1621 if count_success != count_all: sys.exit(1)
1624 @register_command("cred","")
1625 def trusted(self, options, args):
1627 return the trusted certs at this interface (get_trusted_certs)
1629 if options.registry_interface:
1630 server=self.registry()
1632 server = self.sliceapi()
1633 cred = self.my_authority_credential_string()
1634 trusted_certs = server.get_trusted_certs(cred)
1635 if not options.registry_interface:
1636 trusted_certs = ReturnValue.get_value(trusted_certs)
1638 for trusted_cert in trusted_certs:
1639 print "\n===========================================================\n"
1640 gid = GID(string=trusted_cert)
1642 cert = Certificate(string=trusted_cert)
1643 self.logger.debug('Sfi.trusted -> %r'%cert.get_subject())
1644 print "Certificate:\n%s\n\n"%trusted_cert