2 # sfi.py - basic SFA command-line client
3 # the actual binary in sfa/clientbin essentially runs main()
4 # this module is used in sfascan
15 from lxml import etree
16 from StringIO import StringIO
17 from optparse import OptionParser
18 from pprint import PrettyPrinter
20 from sfa.trust.certificate import Keypair, Certificate
21 from sfa.trust.gid import GID
22 from sfa.trust.credential import Credential
23 from sfa.trust.sfaticket import SfaTicket
25 from sfa.util.sfalogging import sfi_logger
26 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn
27 from sfa.util.config import Config
28 from sfa.util.version import version_core
29 from sfa.util.cache import Cache
31 from sfa.storage.persistentobjs import RegRecord, RegAuthority, RegUser, RegSlice, RegNode
32 from sfa.storage.persistentobjs import make_record
34 from sfa.rspecs.rspec import RSpec
35 from sfa.rspecs.rspec_converter import RSpecConverter
36 from sfa.rspecs.version_manager import VersionManager
38 from sfa.client.sfaclientlib import SfaClientBootstrap
39 from sfa.client.sfaserverproxy import SfaServerProxy, ServerException
40 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
41 from sfa.client.return_value import ReturnValue
45 # utility methods here
47 def display_rspec(rspec, format='rspec'):
49 tree = etree.parse(StringIO(rspec))
51 result = root.xpath("./network/site/node/hostname/text()")
52 elif format in ['ip']:
53 # The IP address is not yet part of the new RSpec
54 # so this doesn't do anything yet.
55 tree = etree.parse(StringIO(rspec))
57 result = root.xpath("./network/site/node/ipv4/text()")
64 def display_list(results):
65 for result in results:
68 def display_records(recordList, dump=False):
69 ''' Print all fields in the record'''
70 for record in recordList:
71 display_record(record, dump)
73 def display_record(record, dump=False):
77 info = record.getdict()
78 print "%s (%s)" % (info['hrn'], info['type'])
82 def filter_records(type, records):
84 for record in records:
85 if (record['type'] == type) or (type == "all"):
86 filtered_records.append(record)
87 return filtered_records
91 def save_variable_to_file(var, filename, format="text"):
92 f = open(filename, "w")
95 elif format == "pickled":
96 f.write(pickle.dumps(var))
98 # this should never happen
99 print "unknown output format", format
102 def save_rspec_to_file(rspec, filename):
103 if not filename.endswith(".rspec"):
104 filename = filename + ".rspec"
105 f = open(filename, 'w')
110 def save_records_to_file(filename, record_dicts, format="xml"):
113 for record_dict in record_dicts:
115 save_record_to_file(filename + "." + str(index), record_dict)
117 save_record_to_file(filename, record_dict)
119 elif format == "xmllist":
120 f = open(filename, "w")
121 f.write("<recordlist>\n")
122 for record_dict in record_dicts:
123 record_obj=make_record (dict=record_dict)
124 f.write('<record hrn="' + record_obj.hrn + '" type="' + record_obj.type + '" />\n')
125 f.write("</recordlist>\n")
127 elif format == "hrnlist":
128 f = open(filename, "w")
129 for record_dict in record_dicts:
130 record_obj=make_record (dict=record_dict)
131 f.write(record_obj.hrn + "\n")
134 # this should never happen
135 print "unknown output format", format
137 def save_record_to_file(filename, record_dict):
138 rec_record = make_record (dict=record_dict)
139 str = record.save_to_string()
140 f=codecs.open(filename, encoding='utf-8',mode="w")
147 def load_record_from_file(filename):
148 f=codecs.open(filename, encoding="utf-8", mode="r")
149 xml_string = f.read()
151 return make_record (xml=xml_string)
155 def unique_call_id(): return uuid.uuid4().urn
159 # dirty hack to make this class usable from the outside
160 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user', 'user_private_key']
163 def default_sfi_dir ():
164 if os.path.isfile("./sfi_config"):
167 return os.path.expanduser("~/.sfi/")
169 # dummy to meet Sfi's expectations for its 'options' field
170 # i.e. s/t we can do setattr on
174 def __init__ (self,options=None):
175 if options is None: options=Sfi.DummyOptions()
176 for opt in Sfi.required_options:
177 if not hasattr(options,opt): setattr(options,opt,None)
178 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
179 self.options = options
181 self.authority = None
182 self.logger = sfi_logger
183 self.logger.enable_console()
184 self.available_names = [ tuple[0] for tuple in Sfi.available ]
185 self.available_dict = dict (Sfi.available)
187 # tuples command-name expected-args in the order in which they should appear in the help
190 ("list", "authority"),
193 ("update", "record"),
196 ("resources", "[slice_hrn]"),
197 ("create", "slice_hrn rspec"),
198 ("delete", "slice_hrn"),
199 ("status", "slice_hrn"),
200 ("start", "slice_hrn"),
201 ("stop", "slice_hrn"),
202 ("reset", "slice_hrn"),
203 ("renew", "slice_hrn time"),
204 ("shutdown", "slice_hrn"),
205 ("get_ticket", "slice_hrn rspec"),
206 ("redeem_ticket", "ticket"),
207 ("delegate", "name"),
208 ("create_gid", "[name]"),
209 ("get_trusted_certs", "cred"),
212 def print_command_help (self, options):
213 verbose=getattr(options,'verbose')
214 format3="%18s %-15s %s"
217 print format3%("command","cmd_args","description")
221 self.create_parser().print_help()
222 for command in self.available_names:
223 args=self.available_dict[command]
224 method=getattr(self,command,None)
226 if method: doc=getattr(method,'__doc__',"")
227 if not doc: doc="*** no doc found ***"
228 doc=doc.strip(" \t\n")
229 doc=doc.replace("\n","\n"+35*' ')
232 print format3%(command,args,doc)
234 self.create_command_parser(command).print_help()
236 def create_command_parser(self, command):
237 if command not in self.available_dict:
238 msg="Invalid command\n"
240 msg += ','.join(self.available_names)
241 self.logger.critical(msg)
244 parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \
245 % (command, self.available_dict[command]))
247 # user specifies remote aggregate/sm/component
248 if command in ("resources", "slices", "create", "delete", "start", "stop",
249 "restart", "shutdown", "get_ticket", "renew", "status"):
250 parser.add_option("-d", "--delegate", dest="delegate", default=None,
252 help="Include a credential delegated to the user's root"+\
253 "authority in set of credentials for this call")
255 # registy filter option
256 if command in ("list", "show", "remove"):
257 parser.add_option("-t", "--type", dest="type", type="choice",
258 help="type filter ([all]|user|slice|authority|node|aggregate)",
259 choices=("all", "user", "slice", "authority", "node", "aggregate"),
261 if command in ("resources"):
263 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
264 help="schema type and version of resulting RSpec")
265 # disable/enable cached rspecs
266 parser.add_option("-c", "--current", dest="current", default=False,
268 help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
270 parser.add_option("-f", "--format", dest="format", type="choice",
271 help="display format ([xml]|dns|ip)", default="xml",
272 choices=("xml", "dns", "ip"))
273 #panos: a new option to define the type of information about resources a user is interested in
274 parser.add_option("-i", "--info", dest="info",
275 help="optional component information", default=None)
278 # 'create' does return the new rspec, makes sense to save that too
279 if command in ("resources", "show", "list", "create_gid", 'create'):
280 parser.add_option("-o", "--output", dest="file",
281 help="output XML to file", metavar="FILE", default=None)
283 if command in ("show", "list"):
284 parser.add_option("-f", "--format", dest="format", type="choice",
285 help="display format ([text]|xml)", default="text",
286 choices=("text", "xml"))
288 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
289 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
290 choices=("xml", "xmllist", "hrnlist"))
292 if command in ("status", "version"):
293 parser.add_option("-o", "--output", dest="file",
294 help="output dictionary to file", metavar="FILE", default=None)
295 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
296 help="output file format ([text]|pickled)", default="text",
297 choices=("text","pickled"))
299 if command in ("delegate"):
300 parser.add_option("-u", "--user",
301 action="store_true", dest="delegate_user", default=False,
302 help="delegate user credential")
303 parser.add_option("-s", "--slice", dest="delegate_slice",
304 help="delegate slice credential", metavar="HRN", default=None)
306 if command in ("version"):
307 parser.add_option("-R","--registry-version",
308 action="store_true", dest="version_registry", default=False,
309 help="probe registry version instead of sliceapi")
310 parser.add_option("-l","--local",
311 action="store_true", dest="version_local", default=False,
312 help="display version of the local client")
317 def create_parser(self):
319 # Generate command line parser
320 parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
321 description="Commands: %s"%(" ".join(self.available_names)))
322 parser.add_option("-r", "--registry", dest="registry",
323 help="root registry", metavar="URL", default=None)
324 parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
325 help="slice API - in general a SM URL, but can be used to talk to an aggregate")
326 parser.add_option("-d", "--dir", dest="sfi_dir",
327 help="config & working directory - default is %default",
328 metavar="PATH", default=Sfi.default_sfi_dir())
329 parser.add_option("-u", "--user", dest="user",
330 help="user name", metavar="HRN", default=None)
331 parser.add_option("-a", "--auth", dest="auth",
332 help="authority name", metavar="HRN", default=None)
333 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
334 help="verbose mode - cumulative")
335 parser.add_option("-D", "--debug",
336 action="store_true", dest="debug", default=False,
337 help="Debug (xml-rpc) protocol messages")
338 # would it make sense to use ~/.ssh/id_rsa as a default here ?
339 parser.add_option("-k", "--private-key",
340 action="store", dest="user_private_key", default=None,
341 help="point to the private key file to use if not yet installed in sfi_dir")
342 parser.add_option("-t", "--timeout", dest="timeout", default=None,
343 help="Amout of time to wait before timing out the request")
344 parser.add_option("-?", "--commands",
345 action="store_true", dest="command_help", default=False,
346 help="one page summary on commands & exit")
347 parser.disable_interspersed_args()
352 def print_help (self):
353 self.sfi_parser.print_help()
354 self.command_parser.print_help()
357 # Main: parse arguments and dispatch to command
359 def dispatch(self, command, command_options, command_args):
360 return getattr(self, command)(command_options, command_args)
363 self.sfi_parser = self.create_parser()
364 (options, args) = self.sfi_parser.parse_args()
365 if options.command_help:
366 self.print_command_help(options)
368 self.options = options
370 self.logger.setLevelFromOptVerbose(self.options.verbose)
373 self.logger.critical("No command given. Use -h for help.")
374 self.print_command_help(options)
378 self.command_parser = self.create_command_parser(command)
379 (command_options, command_args) = self.command_parser.parse_args(args[1:])
380 self.command_options = command_options
384 self.logger.info("Command=%s" % command)
387 self.dispatch(command, command_options, command_args)
389 self.logger.critical ("Unknown command %s"%command)
396 def read_config(self):
397 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
399 config = Config (config_file)
401 self.logger.critical("Failed to read configuration file %s"%config_file)
402 self.logger.info("Make sure to remove the export clauses and to add quotes")
403 if self.options.verbose==0:
404 self.logger.info("Re-run with -v for more details")
406 self.logger.log_exc("Could not read config file %s"%config_file)
411 if (self.options.sm is not None):
412 self.sm_url = self.options.sm
413 elif hasattr(config, "SFI_SM"):
414 self.sm_url = config.SFI_SM
416 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
420 if (self.options.registry is not None):
421 self.reg_url = self.options.registry
422 elif hasattr(config, "SFI_REGISTRY"):
423 self.reg_url = config.SFI_REGISTRY
425 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
429 if (self.options.user is not None):
430 self.user = self.options.user
431 elif hasattr(config, "SFI_USER"):
432 self.user = config.SFI_USER
434 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
438 if (self.options.auth is not None):
439 self.authority = self.options.auth
440 elif hasattr(config, "SFI_AUTH"):
441 self.authority = config.SFI_AUTH
443 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
450 # Get various credential and spec files
452 # Establishes limiting conventions
453 # - conflates MAs and SAs
454 # - assumes last token in slice name is unique
456 # Bootstraps credentials
457 # - bootstrap user credential from self-signed certificate
458 # - bootstrap authority credential from user credential
459 # - bootstrap slice credential from user credential
462 # init self-signed cert, user credentials and gid
463 def bootstrap (self):
464 bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir)
465 # if -k is provided, use this to initialize private key
466 if self.options.user_private_key:
467 bootstrap.init_private_key_if_missing (self.options.user_private_key)
469 # trigger legacy compat code if needed
470 # the name has changed from just <leaf>.pkey to <hrn>.pkey
471 if not os.path.isfile(bootstrap.private_key_filename()):
472 self.logger.info ("private key not found, trying legacy name")
474 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%get_leaf(self.user))
475 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
476 bootstrap.init_private_key_if_missing (legacy_private_key)
477 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
479 self.logger.log_exc("Can't find private key ")
483 bootstrap.bootstrap_my_gid()
484 # extract what's needed
485 self.private_key = bootstrap.private_key()
486 self.my_credential_string = bootstrap.my_credential_string ()
487 self.my_gid = bootstrap.my_gid ()
488 self.bootstrap = bootstrap
491 def my_authority_credential_string(self):
492 if not self.authority:
493 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
495 return self.bootstrap.authority_credential_string (self.authority)
497 def slice_credential_string(self, name):
498 return self.bootstrap.slice_credential_string (name)
500 # xxx should be supported by sfaclientbootstrap as well
501 def delegate_cred(self, object_cred, hrn, type='authority'):
502 # the gid and hrn of the object we are delegating
503 if isinstance(object_cred, str):
504 object_cred = Credential(string=object_cred)
505 object_gid = object_cred.get_gid_object()
506 object_hrn = object_gid.get_hrn()
508 if not object_cred.get_privileges().get_all_delegate():
509 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
512 # the delegating user's gid
513 caller_gidfile = self.my_gid()
515 # the gid of the user who will be delegated to
516 delegee_gid = self.bootstrap.gid(hrn,type)
517 delegee_hrn = delegee_gid.get_hrn()
518 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
519 return dcred.save_to_string(save_parents=True)
522 # Management of the servers
527 if not hasattr (self, 'registry_proxy'):
528 self.logger.info("Contacting Registry at: %s"%self.reg_url)
529 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
530 timeout=self.options.timeout, verbose=self.options.debug)
531 return self.registry_proxy
535 if not hasattr (self, 'sliceapi_proxy'):
536 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
537 if hasattr(self.command_options,'component') and self.command_options.component:
538 # resolve the hrn at the registry
539 node_hrn = self.command_options.component
540 records = self.registry().Resolve(node_hrn, self.my_credential_string)
541 records = filter_records('node', records)
543 self.logger.warning("No such component:%r"% opts.component)
545 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
546 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
548 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
549 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
550 self.sm_url = 'http://' + self.sm_url
551 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
552 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
553 timeout=self.options.timeout, verbose=self.options.debug)
554 return self.sliceapi_proxy
556 def get_cached_server_version(self, server):
557 # check local cache first
560 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
561 cache_key = server.url + "-version"
563 cache = Cache(cache_file)
566 self.logger.info("Local cache not found at: %s" % cache_file)
569 version = cache.get(cache_key)
572 result = server.GetVersion()
573 version= ReturnValue.get_value(result)
574 # cache version for 20 minutes
575 cache.add(cache_key, version, ttl= 60*20)
576 self.logger.info("Updating cache file %s" % cache_file)
577 cache.save_to_file(cache_file)
581 ### resurrect this temporarily so we can support V1 aggregates for a while
582 def server_supports_options_arg(self, server):
584 Returns true if server support the optional call_id arg, false otherwise.
586 server_version = self.get_cached_server_version(server)
588 # xxx need to rewrite this
589 if int(server_version.get('geni_api')) >= 2:
593 def server_supports_call_id_arg(self, server):
594 server_version = self.get_cached_server_version(server)
596 if 'sfa' in server_version and 'code_tag' in server_version:
597 code_tag = server_version['code_tag']
598 code_tag_parts = code_tag.split("-")
599 version_parts = code_tag_parts[0].split(".")
600 major, minor = version_parts[0], version_parts[1]
601 rev = code_tag_parts[1]
602 if int(major) == 1 and minor == 0 and build >= 22:
606 ### ois = options if supported
607 # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
608 def ois (self, server, option_dict):
609 if self.server_supports_options_arg (server):
611 elif self.server_supports_call_id_arg (server):
612 return [ unique_call_id () ]
616 ### cis = call_id if supported - like ois
617 def cis (self, server):
618 if self.server_supports_call_id_arg (server):
619 return [ unique_call_id ]
623 ######################################## miscell utilities
624 def get_rspec_file(self, rspec):
625 if (os.path.isabs(rspec)):
628 file = os.path.join(self.options.sfi_dir, rspec)
629 if (os.path.isfile(file)):
632 self.logger.critical("No such rspec file %s"%rspec)
635 def get_record_file(self, record):
636 if (os.path.isabs(record)):
639 file = os.path.join(self.options.sfi_dir, record)
640 if (os.path.isfile(file)):
643 self.logger.critical("No such registry record file %s"%record)
647 #==========================================================================
648 # Following functions implement the commands
650 # Registry-related commands
651 #==========================================================================
653 def version(self, options, args):
655 display an SFA server version (GetVersion)
656 or version information about sfi itself
658 if options.version_local:
659 version=version_core()
661 if options.version_registry:
662 server=self.registry()
664 server = self.sliceapi()
665 result = server.GetVersion()
666 version = ReturnValue.get_value(result)
667 pprinter = PrettyPrinter(indent=4)
668 pprinter.pprint(version)
670 save_variable_to_file(version, options.file, options.fileformat)
672 def list(self, options, args):
674 list entries in named authority registry (List)
681 list = self.registry().List(hrn, self.my_credential_string)
683 raise Exception, "Not enough parameters for the 'list' command"
685 # filter on person, slice, site, node, etc.
686 # THis really should be in the self.filter_records funct def comment...
687 list = filter_records(options.type, list)
689 print "%s (%s)" % (record['hrn'], record['type'])
691 save_records_to_file(options.file, list, options.fileformat)
694 def show(self, options, args):
696 show details about named registry record (Resolve)
702 record_dicts = self.registry().Resolve(hrn, self.my_credential_string)
703 record_dicts = filter_records(options.type, record_dicts)
705 self.logger.error("No record of type %s"% options.type)
706 records = [ make_record (dict=record_dict) for record_dict in record_dicts ]
707 for record in records:
708 if (options.format == "text"): record.dump()
709 else: print record.save_as_xml()
711 save_records_to_file(options.file, record_dicts, options.fileformat)
714 def add(self, options, args):
715 "add record into registry from xml file (Register)"
716 auth_cred = self.my_authority_credential_string()
720 record_filepath = args[0]
721 rec_file = self.get_record_file(record_filepath)
722 record = load_record_from_file(rec_file).todict()
723 return self.registry().Register(record, auth_cred)
725 def update(self, options, args):
726 "update record into registry from xml file (Update)"
730 rec_file = self.get_record_file(args[0])
731 record = load_record_from_file(rec_file)
732 if record.type == "user":
733 if record.hrn == self.user:
734 cred = self.my_credential_string
736 cred = self.my_authority_credential_string()
737 elif record.type in ["slice"]:
739 cred = self.slice_credential_string(record.hrn)
740 except ServerException, e:
741 # XXX smbaker -- once we have better error return codes, update this
742 # to do something better than a string compare
743 if "Permission error" in e.args[0]:
744 cred = self.my_authority_credential_string()
747 elif record.type in ["authority"]:
748 cred = self.my_authority_credential_string()
749 elif record.type == 'node':
750 cred = self.my_authority_credential_string()
752 raise "unknown record type" + record.type
753 record_dict = record.todict()
754 return self.registry().Update(record_dict, cred)
756 def remove(self, options, args):
757 "remove registry record by name (Remove)"
758 auth_cred = self.my_authority_credential_string()
766 return self.registry().Remove(hrn, auth_cred, type)
768 # ==================================================================
769 # Slice-related commands
770 # ==================================================================
772 def slices(self, options, args):
773 "list instantiated slices (ListSlices) - returns urn's"
774 server = self.sliceapi()
776 creds = [self.my_credential_string]
778 delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority))
779 creds.append(delegated_cred)
780 # options and call_id when supported
782 api_options['call_id']=unique_call_id()
783 result = server.ListSlices(creds, *self.ois(server,api_options))
784 value = ReturnValue.get_value(result)
788 # show rspec for named slice
789 def resources(self, options, args):
791 with no arg, discover available resources, (ListResources)
792 or with an slice hrn, shows currently provisioned resources
794 server = self.sliceapi()
799 creds.append(self.slice_credential_string(args[0]))
801 creds.append(self.my_credential_string)
803 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
805 # no need to check if server accepts the options argument since the options has
806 # been a required argument since v1 API
808 # always send call_id to v2 servers
809 api_options ['call_id'] = unique_call_id()
810 # ask for cached value if available
811 api_options ['cached'] = True
814 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
816 api_options['info'] = options.info
818 if options.current == True:
819 api_options['cached'] = False
821 api_options['cached'] = True
822 if options.rspec_version:
823 version_manager = VersionManager()
824 server_version = self.get_cached_server_version(server)
825 if 'sfa' in server_version:
826 # just request the version the client wants
827 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
829 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
831 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
832 result = server.ListResources (creds, api_options)
833 value = ReturnValue.get_value(result)
834 if options.file is None:
835 display_rspec(value, options.format)
837 save_rspec_to_file(value, options.file)
840 def create(self, options, args):
842 create or update named slice with given rspec
844 server = self.sliceapi()
846 # xxx do we need to check usage (len(args)) ?
849 slice_urn = hrn_to_urn(slice_hrn, 'slice')
852 creds = [self.slice_credential_string(slice_hrn)]
853 delegated_cred = None
854 server_version = self.get_cached_server_version(server)
855 if server_version.get('interface') == 'slicemgr':
856 # delegate our cred to the slice manager
857 # do not delegate cred to slicemgr...not working at the moment
859 #if server_version.get('hrn'):
860 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
861 #elif server_version.get('urn'):
862 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
865 rspec_file = self.get_rspec_file(args[1])
866 rspec = open(rspec_file).read()
869 # need to pass along user keys to the aggregate.
871 # { urn: urn:publicid:IDN+emulab.net+user+alice
872 # keys: [<ssh key A>, <ssh key B>]
875 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
876 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
877 slice_record = slice_records[0]
878 user_hrns = slice_record['researcher']
879 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
880 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
882 if 'sfa' not in server_version:
883 users = pg_users_arg(user_records)
885 rspec.filter({'component_manager_id': server_version['urn']})
886 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
888 users = sfa_users_arg(user_records, slice_record)
890 # do not append users, keys, or slice tags. Anything
891 # not contained in this request will be removed from the slice
893 # CreateSliver has supported the options argument for a while now so it should
894 # be safe to assume this server support it
896 api_options ['append'] = False
897 api_options ['call_id'] = unique_call_id()
899 result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server, api_options))
900 value = ReturnValue.get_value(result)
901 if options.file is None:
904 save_rspec_to_file (value, options.file)
907 def delete(self, options, args):
909 delete named slice (DeleteSliver)
911 server = self.sliceapi()
915 slice_urn = hrn_to_urn(slice_hrn, 'slice')
918 slice_cred = self.slice_credential_string(slice_hrn)
921 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
922 creds.append(delegated_cred)
924 # options and call_id when supported
926 api_options ['call_id'] = unique_call_id()
927 result = server.DeleteSliver(slice_urn, creds, *self.ois(server, api_options ) )
928 # xxx no ReturnValue ??
931 def status(self, options, args):
933 retrieve slice status (SliverStatus)
935 server = self.sliceapi()
939 slice_urn = hrn_to_urn(slice_hrn, 'slice')
942 slice_cred = self.slice_credential_string(slice_hrn)
945 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
946 creds.append(delegated_cred)
948 # options and call_id when supported
950 api_options['call_id']=unique_call_id()
951 result = server.SliverStatus(slice_urn, creds, *self.ois(server,api_options))
952 value = ReturnValue.get_value(result)
955 save_variable_to_file(value, options.file, options.fileformat)
957 def start(self, options, args):
959 start named slice (Start)
961 server = self.sliceapi()
965 slice_urn = hrn_to_urn(slice_hrn, 'slice')
968 slice_cred = self.slice_credential_string(args[0])
971 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
972 creds.append(delegated_cred)
973 # xxx Thierry - does this not need an api_options as well ?
974 return server.Start(slice_urn, creds)
976 def stop(self, options, args):
978 stop named slice (Stop)
980 server = self.sliceapi()
983 slice_urn = hrn_to_urn(slice_hrn, 'slice')
985 slice_cred = self.slice_credential_string(args[0])
988 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
989 creds.append(delegated_cred)
990 return server.Stop(slice_urn, creds)
993 def reset(self, options, args):
995 reset named slice (reset_slice)
997 server = self.sliceapi()
1000 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1002 slice_cred = self.slice_credential_string(args[0])
1003 creds = [slice_cred]
1004 if options.delegate:
1005 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1006 creds.append(delegated_cred)
1007 return server.reset_slice(creds, slice_urn)
1009 def renew(self, options, args):
1011 renew slice (RenewSliver)
1013 server = self.sliceapi()
1016 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1018 slice_cred = self.slice_credential_string(args[0])
1019 creds = [slice_cred]
1020 if options.delegate:
1021 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1022 creds.append(delegated_cred)
1025 # options and call_id when supported
1027 api_options['call_id']=unique_call_id()
1028 result = server.RenewSliver(slice_urn, creds, time, *self.ois(server,api_options))
1029 value = ReturnValue.get_value(result)
1033 def shutdown(self, options, args):
1035 shutdown named slice (Shutdown)
1037 server = self.sliceapi()
1040 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1042 slice_cred = self.slice_credential_string(slice_hrn)
1043 creds = [slice_cred]
1044 if options.delegate:
1045 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1046 creds.append(delegated_cred)
1047 return server.Shutdown(slice_urn, creds)
1050 def get_ticket(self, options, args):
1052 get a ticket for the specified slice
1054 server = self.sliceapi()
1056 slice_hrn, rspec_path = args[0], args[1]
1057 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1059 slice_cred = self.slice_credential_string(slice_hrn)
1060 creds = [slice_cred]
1061 if options.delegate:
1062 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1063 creds.append(delegated_cred)
1065 rspec_file = self.get_rspec_file(rspec_path)
1066 rspec = open(rspec_file).read()
1067 # options and call_id when supported
1069 api_options['call_id']=unique_call_id()
1070 # get ticket at the server
1071 ticket_string = server.GetTicket(slice_urn, creds, rspec, *self.ois(server,api_options))
1073 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1074 self.logger.info("writing ticket to %s"%file)
1075 ticket = SfaTicket(string=ticket_string)
1076 ticket.save_to_file(filename=file, save_parents=True)
1078 def redeem_ticket(self, options, args):
1080 Connects to nodes in a slice and redeems a ticket
1081 (slice hrn is retrieved from the ticket)
1083 ticket_file = args[0]
1085 # get slice hrn from the ticket
1086 # use this to get the right slice credential
1087 ticket = SfaTicket(filename=ticket_file)
1089 ticket_string = ticket.save_to_string(save_parents=True)
1091 slice_hrn = ticket.gidObject.get_hrn()
1092 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1093 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1094 slice_cred = self.slice_credential_string(slice_hrn)
1096 # get a list of node hostnames from the RSpec
1097 tree = etree.parse(StringIO(ticket.rspec))
1098 root = tree.getroot()
1099 hostnames = root.xpath("./network/site/node/hostname/text()")
1101 # create an xmlrpc connection to the component manager at each of these
1102 # components and gall redeem_ticket
1104 for hostname in hostnames:
1106 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1107 cm_url="http://%s:%s/"%(hostname,CM_PORT)
1108 server = SfaServerProxy(cm_url, self.private_key, self.my_gid)
1109 server = self.server_proxy(hostname, CM_PORT, self.private_key,
1110 timeout=self.options.timeout, verbose=self.options.debug)
1111 server.RedeemTicket(ticket_string, slice_cred)
1112 self.logger.info("Success")
1113 except socket.gaierror:
1114 self.logger.error("redeem_ticket failed on %s: Component Manager not accepting requests"%hostname)
1115 except Exception, e:
1116 self.logger.log_exc(e.message)
1119 def create_gid(self, options, args):
1121 Create a GID (CreateGid)
1126 target_hrn = args[0]
1127 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.bootstrap.my_gid_string())
1129 filename = options.file
1131 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1132 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1133 GID(string=gid).save_to_file(filename)
1136 def delegate(self, options, args):
1138 (locally) create delegate credential for use by given hrn
1140 delegee_hrn = args[0]
1141 if options.delegate_user:
1142 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1143 elif options.delegate_slice:
1144 slice_cred = self.slice_credential_string(options.delegate_slice)
1145 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1147 self.logger.warning("Must specify either --user or --slice <hrn>")
1149 delegated_cred = Credential(string=cred)
1150 object_hrn = delegated_cred.get_gid_object().get_hrn()
1151 if options.delegate_user:
1152 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1153 + get_leaf(object_hrn) + ".cred")
1154 elif options.delegate_slice:
1155 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1156 + get_leaf(object_hrn) + ".cred")
1158 delegated_cred.save_to_file(dest_fn, save_parents=True)
1160 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1162 def get_trusted_certs(self, options, args):
1164 return uhe trusted certs at this interface (get_trusted_certs)
1166 trusted_certs = self.registry().get_trusted_certs()
1167 for trusted_cert in trusted_certs:
1168 gid = GID(string=trusted_cert)
1170 cert = Certificate(string=trusted_cert)
1171 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())