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
16 from lxml import etree
17 from StringIO import StringIO
18 from optparse import OptionParser
19 from pprint import PrettyPrinter
21 from sfa.trust.certificate import Keypair, Certificate
22 from sfa.trust.gid import GID
23 from sfa.trust.credential import Credential
24 from sfa.trust.sfaticket import SfaTicket
26 from sfa.util.sfalogging import sfi_logger
27 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn
28 from sfa.util.config import Config
29 from sfa.util.version import version_core
30 from sfa.util.cache import Cache
32 from sfa.storage.record import 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_raw_to_file(var, filename, format="text", banner=None):
93 # if filename is "-", send it to stdout
96 f = open(filename, "w")
101 elif format == "pickled":
102 f.write(pickle.dumps(var))
103 elif format == "json":
104 if hasattr(json, "dumps"):
105 f.write(json.dumps(var)) # python 2.6
107 f.write(json.write(var)) # python 2.5
109 # this should never happen
110 print "unknown output format", format
112 f.write('\n'+banner+"\n")
114 def save_rspec_to_file(rspec, filename):
115 if not filename.endswith(".rspec"):
116 filename = filename + ".rspec"
117 f = open(filename, 'w')
122 def save_records_to_file(filename, record_dicts, format="xml"):
125 for record_dict in record_dicts:
127 save_record_to_file(filename + "." + str(index), record_dict)
129 save_record_to_file(filename, record_dict)
131 elif format == "xmllist":
132 f = open(filename, "w")
133 f.write("<recordlist>\n")
134 for record_dict in record_dicts:
135 record_obj=Record(dict=record_dict)
136 f.write('<record hrn="' + record_obj.hrn + '" type="' + record_obj.type + '" />\n')
137 f.write("</recordlist>\n")
139 elif format == "hrnlist":
140 f = open(filename, "w")
141 for record_dict in record_dicts:
142 record_obj=Record(dict=record_dict)
143 f.write(record_obj.hrn + "\n")
146 # this should never happen
147 print "unknown output format", format
149 def save_record_to_file(filename, record_dict):
150 record = Record(dict=record_dict)
151 xml = record.save_as_xml()
152 f=codecs.open(filename, encoding='utf-8',mode="w")
159 def load_record_from_file(filename):
160 f=codecs.open(filename, encoding="utf-8", mode="r")
161 xml_string = f.read()
163 return Record(xml=xml_string)
167 def unique_call_id(): return uuid.uuid4().urn
171 # dirty hack to make this class usable from the outside
172 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user', 'user_private_key']
175 def default_sfi_dir ():
176 if os.path.isfile("./sfi_config"):
179 return os.path.expanduser("~/.sfi/")
181 # dummy to meet Sfi's expectations for its 'options' field
182 # i.e. s/t we can do setattr on
186 def __init__ (self,options=None):
187 if options is None: options=Sfi.DummyOptions()
188 for opt in Sfi.required_options:
189 if not hasattr(options,opt): setattr(options,opt,None)
190 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
191 self.options = options
193 self.authority = None
194 self.logger = sfi_logger
195 self.logger.enable_console()
196 self.available_names = [ tuple[0] for tuple in Sfi.available ]
197 self.available_dict = dict (Sfi.available)
199 # tuples command-name expected-args in the order in which they should appear in the help
202 ("list", "authority"),
205 ("update", "record"),
208 ("resources", "[slice_hrn]"),
209 ("create", "slice_hrn rspec"),
210 ("delete", "slice_hrn"),
211 ("status", "slice_hrn"),
212 ("start", "slice_hrn"),
213 ("stop", "slice_hrn"),
214 ("reset", "slice_hrn"),
215 ("renew", "slice_hrn time"),
216 ("shutdown", "slice_hrn"),
217 ("get_ticket", "slice_hrn rspec"),
218 ("redeem_ticket", "ticket"),
219 ("delegate", "name"),
220 ("create_gid", "[name]"),
221 ("get_trusted_certs", "cred"),
225 def print_command_help (self, options):
226 verbose=getattr(options,'verbose')
227 format3="%18s %-15s %s"
230 print format3%("command","cmd_args","description")
234 self.create_parser().print_help()
235 for command in self.available_names:
236 args=self.available_dict[command]
237 method=getattr(self,command,None)
239 if method: doc=getattr(method,'__doc__',"")
240 if not doc: doc="*** no doc found ***"
241 doc=doc.strip(" \t\n")
242 doc=doc.replace("\n","\n"+35*' ')
245 print format3%(command,args,doc)
247 self.create_command_parser(command).print_help()
249 def create_command_parser(self, command):
250 if command not in self.available_dict:
251 msg="Invalid command\n"
253 msg += ','.join(self.available_names)
254 self.logger.critical(msg)
257 parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \
258 % (command, self.available_dict[command]))
260 # user specifies remote aggregate/sm/component
261 if command in ("resources", "slices", "create", "delete", "start", "stop",
262 "restart", "shutdown", "get_ticket", "renew", "status"):
263 parser.add_option("-d", "--delegate", dest="delegate", default=None,
265 help="Include a credential delegated to the user's root"+\
266 "authority in set of credentials for this call")
268 # registy filter option
269 if command in ("list", "show", "remove"):
270 parser.add_option("-t", "--type", dest="type", type="choice",
271 help="type filter ([all]|user|slice|authority|node|aggregate)",
272 choices=("all", "user", "slice", "authority", "node", "aggregate"),
274 if command in ("resources"):
276 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
277 help="schema type and version of resulting RSpec")
278 # disable/enable cached rspecs
279 parser.add_option("-c", "--current", dest="current", default=False,
281 help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
283 parser.add_option("-f", "--format", dest="format", type="choice",
284 help="display format ([xml]|dns|ip)", default="xml",
285 choices=("xml", "dns", "ip"))
286 #panos: a new option to define the type of information about resources a user is interested in
287 parser.add_option("-i", "--info", dest="info",
288 help="optional component information", default=None)
291 # 'create' does return the new rspec, makes sense to save that too
292 if command in ("resources", "show", "list", "create_gid", 'create'):
293 parser.add_option("-o", "--output", dest="file",
294 help="output XML to file", metavar="FILE", default=None)
296 if command in ("show", "list"):
297 parser.add_option("-f", "--format", dest="format", type="choice",
298 help="display format ([text]|xml)", default="text",
299 choices=("text", "xml"))
301 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
302 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
303 choices=("xml", "xmllist", "hrnlist"))
304 if command == 'list':
305 parser.add_option("-r", "--recursive", dest="recursive", action='store_true',
306 help="list all child records", default=False)
307 if command in ("delegate"):
308 parser.add_option("-u", "--user",
309 action="store_true", dest="delegate_user", default=False,
310 help="delegate user credential")
311 parser.add_option("-s", "--slice", dest="delegate_slice",
312 help="delegate slice credential", metavar="HRN", default=None)
314 if command in ("version"):
315 parser.add_option("-R","--registry-version",
316 action="store_true", dest="version_registry", default=False,
317 help="probe registry version instead of sliceapi")
318 parser.add_option("-l","--local",
319 action="store_true", dest="version_local", default=False,
320 help="display version of the local client")
325 def create_parser(self):
327 # Generate command line parser
328 parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
329 description="Commands: %s"%(" ".join(self.available_names)))
330 parser.add_option("-r", "--registry", dest="registry",
331 help="root registry", metavar="URL", default=None)
332 parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
333 help="slice API - in general a SM URL, but can be used to talk to an aggregate")
334 parser.add_option("-R", "--raw", dest="raw", default=None,
335 help="Save raw, unparsed server response to a file")
336 parser.add_option("", "--rawformat", dest="rawformat", type="choice",
337 help="raw file format ([text]|pickled|json)", default="text",
338 choices=("text","pickled","json"))
339 parser.add_option("", "--rawbanner", dest="rawbanner", default=None,
340 help="text string to write before and after raw output")
341 parser.add_option("-d", "--dir", dest="sfi_dir",
342 help="config & working directory - default is %default",
343 metavar="PATH", default=Sfi.default_sfi_dir())
344 parser.add_option("-u", "--user", dest="user",
345 help="user name", metavar="HRN", default=None)
346 parser.add_option("-a", "--auth", dest="auth",
347 help="authority name", metavar="HRN", default=None)
348 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
349 help="verbose mode - cumulative")
350 parser.add_option("-D", "--debug",
351 action="store_true", dest="debug", default=False,
352 help="Debug (xml-rpc) protocol messages")
353 # would it make sense to use ~/.ssh/id_rsa as a default here ?
354 parser.add_option("-k", "--private-key",
355 action="store", dest="user_private_key", default=None,
356 help="point to the private key file to use if not yet installed in sfi_dir")
357 parser.add_option("-t", "--timeout", dest="timeout", default=None,
358 help="Amout of time to wait before timing out the request")
359 parser.add_option("-?", "--commands",
360 action="store_true", dest="command_help", default=False,
361 help="one page summary on commands & exit")
362 parser.disable_interspersed_args()
367 def print_help (self):
368 self.sfi_parser.print_help()
369 self.command_parser.print_help()
372 # Main: parse arguments and dispatch to command
374 def dispatch(self, command, command_options, command_args):
375 return getattr(self, command)(command_options, command_args)
378 self.sfi_parser = self.create_parser()
379 (options, args) = self.sfi_parser.parse_args()
380 if options.command_help:
381 self.print_command_help(options)
383 self.options = options
385 self.logger.setLevelFromOptVerbose(self.options.verbose)
388 self.logger.critical("No command given. Use -h for help.")
389 self.print_command_help(options)
393 self.command_parser = self.create_command_parser(command)
394 (command_options, command_args) = self.command_parser.parse_args(args[1:])
395 self.command_options = command_options
399 self.logger.info("Command=%s" % command)
402 self.dispatch(command, command_options, command_args)
404 self.logger.critical ("Unknown command %s"%command)
411 def read_config(self):
412 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
414 config = Config (config_file)
416 self.logger.critical("Failed to read configuration file %s"%config_file)
417 self.logger.info("Make sure to remove the export clauses and to add quotes")
418 if self.options.verbose==0:
419 self.logger.info("Re-run with -v for more details")
421 self.logger.log_exc("Could not read config file %s"%config_file)
426 if (self.options.sm is not None):
427 self.sm_url = self.options.sm
428 elif hasattr(config, "SFI_SM"):
429 self.sm_url = config.SFI_SM
431 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
435 if (self.options.registry is not None):
436 self.reg_url = self.options.registry
437 elif hasattr(config, "SFI_REGISTRY"):
438 self.reg_url = config.SFI_REGISTRY
440 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
444 if (self.options.user is not None):
445 self.user = self.options.user
446 elif hasattr(config, "SFI_USER"):
447 self.user = config.SFI_USER
449 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
453 if (self.options.auth is not None):
454 self.authority = self.options.auth
455 elif hasattr(config, "SFI_AUTH"):
456 self.authority = config.SFI_AUTH
458 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
461 self.config_file=config_file
465 def show_config (self):
466 print "From configuration file %s"%self.config_file
469 ('SFI_AUTH','authority'),
471 ('SFI_REGISTRY','reg_url'),
473 for (external_name, internal_name) in flags:
474 print "%s='%s'"%(external_name,getattr(self,internal_name))
477 # Get various credential and spec files
479 # Establishes limiting conventions
480 # - conflates MAs and SAs
481 # - assumes last token in slice name is unique
483 # Bootstraps credentials
484 # - bootstrap user credential from self-signed certificate
485 # - bootstrap authority credential from user credential
486 # - bootstrap slice credential from user credential
489 # init self-signed cert, user credentials and gid
490 def bootstrap (self):
491 client_bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir)
492 # if -k is provided, use this to initialize private key
493 if self.options.user_private_key:
494 client_bootstrap.init_private_key_if_missing (self.options.user_private_key)
496 # trigger legacy compat code if needed
497 # the name has changed from just <leaf>.pkey to <hrn>.pkey
498 if not os.path.isfile(client_bootstrap.private_key_filename()):
499 self.logger.info ("private key not found, trying legacy name")
501 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%get_leaf(self.user))
502 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
503 client_bootstrap.init_private_key_if_missing (legacy_private_key)
504 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
506 self.logger.log_exc("Can't find private key ")
510 client_bootstrap.bootstrap_my_gid()
511 # extract what's needed
512 self.private_key = client_bootstrap.private_key()
513 self.my_credential_string = client_bootstrap.my_credential_string ()
514 self.my_gid = client_bootstrap.my_gid ()
515 self.client_bootstrap = client_bootstrap
518 def my_authority_credential_string(self):
519 if not self.authority:
520 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
522 return self.client_bootstrap.authority_credential_string (self.authority)
524 def slice_credential_string(self, name):
525 return self.client_bootstrap.slice_credential_string (name)
527 # xxx should be supported by sfaclientbootstrap as well
528 def delegate_cred(self, object_cred, hrn, type='authority'):
529 # the gid and hrn of the object we are delegating
530 if isinstance(object_cred, str):
531 object_cred = Credential(string=object_cred)
532 object_gid = object_cred.get_gid_object()
533 object_hrn = object_gid.get_hrn()
535 if not object_cred.get_privileges().get_all_delegate():
536 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
539 # the delegating user's gid
540 caller_gidfile = self.my_gid()
542 # the gid of the user who will be delegated to
543 delegee_gid = self.client_bootstrap.gid(hrn,type)
544 delegee_hrn = delegee_gid.get_hrn()
545 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
546 return dcred.save_to_string(save_parents=True)
549 # Management of the servers
554 if not hasattr (self, 'registry_proxy'):
555 self.logger.info("Contacting Registry at: %s"%self.reg_url)
556 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
557 timeout=self.options.timeout, verbose=self.options.debug)
558 return self.registry_proxy
562 if not hasattr (self, 'sliceapi_proxy'):
563 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
564 if hasattr(self.command_options,'component') and self.command_options.component:
565 # resolve the hrn at the registry
566 node_hrn = self.command_options.component
567 records = self.registry().Resolve(node_hrn, self.my_credential_string)
568 records = filter_records('node', records)
570 self.logger.warning("No such component:%r"% opts.component)
572 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
573 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
575 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
576 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
577 self.sm_url = 'http://' + self.sm_url
578 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
579 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
580 timeout=self.options.timeout, verbose=self.options.debug)
581 return self.sliceapi_proxy
583 def get_cached_server_version(self, server):
584 # check local cache first
587 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
588 cache_key = server.url + "-version"
590 cache = Cache(cache_file)
593 self.logger.info("Local cache not found at: %s" % cache_file)
596 version = cache.get(cache_key)
599 result = server.GetVersion()
600 version= ReturnValue.get_value(result)
601 # cache version for 20 minutes
602 cache.add(cache_key, version, ttl= 60*20)
603 self.logger.info("Updating cache file %s" % cache_file)
604 cache.save_to_file(cache_file)
608 ### resurrect this temporarily so we can support V1 aggregates for a while
609 def server_supports_options_arg(self, server):
611 Returns true if server support the optional call_id arg, false otherwise.
613 server_version = self.get_cached_server_version(server)
615 # xxx need to rewrite this
616 if int(server_version.get('geni_api')) >= 2:
620 def server_supports_call_id_arg(self, server):
621 server_version = self.get_cached_server_version(server)
623 if 'sfa' in server_version and 'code_tag' in server_version:
624 code_tag = server_version['code_tag']
625 code_tag_parts = code_tag.split("-")
626 version_parts = code_tag_parts[0].split(".")
627 major, minor = version_parts[0], version_parts[1]
628 rev = code_tag_parts[1]
629 if int(major) == 1 and minor == 0 and build >= 22:
633 ### ois = options if supported
634 # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
635 def ois (self, server, option_dict):
636 if self.server_supports_options_arg (server):
638 elif self.server_supports_call_id_arg (server):
639 return [ unique_call_id () ]
643 ### cis = call_id if supported - like ois
644 def cis (self, server):
645 if self.server_supports_call_id_arg (server):
646 return [ unique_call_id ]
650 ######################################## miscell utilities
651 def get_rspec_file(self, rspec):
652 if (os.path.isabs(rspec)):
655 file = os.path.join(self.options.sfi_dir, rspec)
656 if (os.path.isfile(file)):
659 self.logger.critical("No such rspec file %s"%rspec)
662 def get_record_file(self, record):
663 if (os.path.isabs(record)):
666 file = os.path.join(self.options.sfi_dir, record)
667 if (os.path.isfile(file)):
670 self.logger.critical("No such registry record file %s"%record)
674 #==========================================================================
675 # Following functions implement the commands
677 # Registry-related commands
678 #==========================================================================
680 def version(self, options, args):
682 display an SFA server version (GetVersion)
683 or version information about sfi itself
685 if options.version_local:
686 version=version_core()
688 if options.version_registry:
689 server=self.registry()
691 server = self.sliceapi()
692 result = server.GetVersion()
693 version = ReturnValue.get_value(result)
695 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
697 pprinter = PrettyPrinter(indent=4)
698 pprinter.pprint(version)
700 def list(self, options, args):
702 list entries in named authority registry (List)
709 if options.recursive:
710 opts['recursive'] = options.recursive
713 list = self.registry().List(hrn, self.my_credential_string, options)
715 raise Exception, "Not enough parameters for the 'list' command"
717 # filter on person, slice, site, node, etc.
718 # THis really should be in the self.filter_records funct def comment...
719 list = filter_records(options.type, list)
721 print "%s (%s)" % (record['hrn'], record['type'])
723 save_records_to_file(options.file, list, options.fileformat)
726 def show(self, options, args):
728 show details about named registry record (Resolve)
734 record_dicts = self.registry().Resolve(hrn, self.my_credential_string)
735 record_dicts = filter_records(options.type, record_dicts)
737 self.logger.error("No record of type %s"% options.type)
738 records = [ Record(dict=record_dict) for record_dict in record_dicts ]
739 for record in records:
740 if (options.format == "text"): record.dump()
741 else: print record.save_as_xml()
743 save_records_to_file(options.file, record_dicts, options.fileformat)
746 def add(self, options, args):
747 "add record into registry from xml file (Register)"
748 auth_cred = self.my_authority_credential_string()
752 record_filepath = args[0]
753 rec_file = self.get_record_file(record_filepath)
754 record = load_record_from_file(rec_file).todict()
755 return self.registry().Register(record, auth_cred)
757 def update(self, options, args):
758 "update record into registry from xml file (Update)"
762 rec_file = self.get_record_file(args[0])
763 record = load_record_from_file(rec_file)
764 if record.type == "user":
765 if record.hrn == self.user:
766 cred = self.my_credential_string
768 cred = self.my_authority_credential_string()
769 elif record.type in ["slice"]:
771 cred = self.slice_credential_string(record.hrn)
772 except ServerException, e:
773 # XXX smbaker -- once we have better error return codes, update this
774 # to do something better than a string compare
775 if "Permission error" in e.args[0]:
776 cred = self.my_authority_credential_string()
779 elif record.type in ["authority"]:
780 cred = self.my_authority_credential_string()
781 elif record.type == 'node':
782 cred = self.my_authority_credential_string()
784 raise "unknown record type" + record.type
785 record_dict = record.todict()
786 return self.registry().Update(record_dict, cred)
788 def remove(self, options, args):
789 "remove registry record by name (Remove)"
790 auth_cred = self.my_authority_credential_string()
798 return self.registry().Remove(hrn, auth_cred, type)
800 # ==================================================================
801 # Slice-related commands
802 # ==================================================================
804 def slices(self, options, args):
805 "list instantiated slices (ListSlices) - returns urn's"
806 server = self.sliceapi()
808 creds = [self.my_credential_string]
810 delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority))
811 creds.append(delegated_cred)
812 # options and call_id when supported
814 api_options['call_id']=unique_call_id()
815 result = server.ListSlices(creds, *self.ois(server,api_options))
816 value = ReturnValue.get_value(result)
818 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
823 # show rspec for named slice
824 def resources(self, options, args):
826 with no arg, discover available resources, (ListResources)
827 or with an slice hrn, shows currently provisioned resources
829 server = self.sliceapi()
834 creds.append(self.slice_credential_string(args[0]))
836 creds.append(self.my_credential_string)
838 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
840 # no need to check if server accepts the options argument since the options has
841 # been a required argument since v1 API
843 # always send call_id to v2 servers
844 api_options ['call_id'] = unique_call_id()
845 # ask for cached value if available
846 api_options ['cached'] = True
849 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
851 api_options['info'] = options.info
853 if options.current == True:
854 api_options['cached'] = False
856 api_options['cached'] = True
857 if options.rspec_version:
858 version_manager = VersionManager()
859 server_version = self.get_cached_server_version(server)
860 if 'sfa' in server_version:
861 # just request the version the client wants
862 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
864 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
866 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
867 result = server.ListResources (creds, api_options)
868 value = ReturnValue.get_value(result)
870 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
871 if options.file is not None:
872 save_rspec_to_file(value, options.file)
873 if (self.options.raw is None) and (options.file is None):
874 display_rspec(value, options.format)
878 def create(self, options, args):
880 create or update named slice with given rspec
882 server = self.sliceapi()
884 # xxx do we need to check usage (len(args)) ?
887 slice_urn = hrn_to_urn(slice_hrn, 'slice')
890 creds = [self.slice_credential_string(slice_hrn)]
891 delegated_cred = None
892 server_version = self.get_cached_server_version(server)
893 if server_version.get('interface') == 'slicemgr':
894 # delegate our cred to the slice manager
895 # do not delegate cred to slicemgr...not working at the moment
897 #if server_version.get('hrn'):
898 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
899 #elif server_version.get('urn'):
900 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
903 rspec_file = self.get_rspec_file(args[1])
904 rspec = open(rspec_file).read()
907 # need to pass along user keys to the aggregate.
909 # { urn: urn:publicid:IDN+emulab.net+user+alice
910 # keys: [<ssh key A>, <ssh key B>]
913 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
914 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
915 slice_record = slice_records[0]
916 user_hrns = slice_record['researcher']
917 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
918 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
920 if 'sfa' not in server_version:
921 users = pg_users_arg(user_records)
923 rspec.filter({'component_manager_id': server_version['urn']})
924 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
926 users = sfa_users_arg(user_records, slice_record)
928 # do not append users, keys, or slice tags. Anything
929 # not contained in this request will be removed from the slice
931 # CreateSliver has supported the options argument for a while now so it should
932 # be safe to assume this server support it
934 api_options ['append'] = False
935 api_options ['call_id'] = unique_call_id()
936 result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server, api_options))
937 value = ReturnValue.get_value(result)
939 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
940 if options.file is not None:
941 save_rspec_to_file (value, options.file)
942 if (self.options.raw is None) and (options.file is None):
947 def delete(self, options, args):
949 delete named slice (DeleteSliver)
951 server = self.sliceapi()
955 slice_urn = hrn_to_urn(slice_hrn, 'slice')
958 slice_cred = self.slice_credential_string(slice_hrn)
961 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
962 creds.append(delegated_cred)
964 # options and call_id when supported
966 api_options ['call_id'] = unique_call_id()
967 result = server.DeleteSliver(slice_urn, creds, *self.ois(server, api_options ) )
968 value = ReturnValue.get_value(result)
970 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
975 def status(self, options, args):
977 retrieve slice status (SliverStatus)
979 server = self.sliceapi()
983 slice_urn = hrn_to_urn(slice_hrn, 'slice')
986 slice_cred = self.slice_credential_string(slice_hrn)
989 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
990 creds.append(delegated_cred)
992 # options and call_id when supported
994 api_options['call_id']=unique_call_id()
995 result = server.SliverStatus(slice_urn, creds, *self.ois(server,api_options))
996 value = ReturnValue.get_value(result)
998 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1002 def start(self, options, args):
1004 start named slice (Start)
1006 server = self.sliceapi()
1010 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1013 slice_cred = self.slice_credential_string(args[0])
1014 creds = [slice_cred]
1015 if options.delegate:
1016 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1017 creds.append(delegated_cred)
1018 # xxx Thierry - does this not need an api_options as well ?
1019 result = server.Start(slice_urn, creds)
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 def stop(self, options, args):
1029 stop named slice (Stop)
1031 server = self.sliceapi()
1034 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1036 slice_cred = self.slice_credential_string(args[0])
1037 creds = [slice_cred]
1038 if options.delegate:
1039 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1040 creds.append(delegated_cred)
1041 result = server.Stop(slice_urn, creds)
1042 value = ReturnValue.get_value(result)
1043 if self.options.raw:
1044 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1050 def reset(self, options, args):
1052 reset named slice (reset_slice)
1054 server = self.sliceapi()
1057 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1059 slice_cred = self.slice_credential_string(args[0])
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)
1064 result = server.reset_slice(creds, slice_urn)
1065 value = ReturnValue.get_value(result)
1066 if self.options.raw:
1067 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1072 def renew(self, options, args):
1074 renew slice (RenewSliver)
1076 server = self.sliceapi()
1079 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1081 slice_cred = self.slice_credential_string(args[0])
1082 creds = [slice_cred]
1083 if options.delegate:
1084 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1085 creds.append(delegated_cred)
1088 # options and call_id when supported
1090 api_options['call_id']=unique_call_id()
1091 result = server.RenewSliver(slice_urn, creds, time, *self.ois(server,api_options))
1092 value = ReturnValue.get_value(result)
1093 if self.options.raw:
1094 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1100 def shutdown(self, options, args):
1102 shutdown named slice (Shutdown)
1104 server = self.sliceapi()
1107 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1109 slice_cred = self.slice_credential_string(slice_hrn)
1110 creds = [slice_cred]
1111 if options.delegate:
1112 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1113 creds.append(delegated_cred)
1114 result = server.Shutdown(slice_urn, creds)
1115 value = ReturnValue.get_value(result)
1116 if self.options.raw:
1117 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1123 def get_ticket(self, options, args):
1125 get a ticket for the specified slice
1127 server = self.sliceapi()
1129 slice_hrn, rspec_path = args[0], args[1]
1130 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1132 slice_cred = self.slice_credential_string(slice_hrn)
1133 creds = [slice_cred]
1134 if options.delegate:
1135 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1136 creds.append(delegated_cred)
1138 rspec_file = self.get_rspec_file(rspec_path)
1139 rspec = open(rspec_file).read()
1140 # options and call_id when supported
1142 api_options['call_id']=unique_call_id()
1143 # get ticket at the server
1144 ticket_string = server.GetTicket(slice_urn, creds, rspec, *self.ois(server,api_options))
1146 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1147 self.logger.info("writing ticket to %s"%file)
1148 ticket = SfaTicket(string=ticket_string)
1149 ticket.save_to_file(filename=file, save_parents=True)
1151 def redeem_ticket(self, options, args):
1153 Connects to nodes in a slice and redeems a ticket
1154 (slice hrn is retrieved from the ticket)
1156 ticket_file = args[0]
1158 # get slice hrn from the ticket
1159 # use this to get the right slice credential
1160 ticket = SfaTicket(filename=ticket_file)
1162 ticket_string = ticket.save_to_string(save_parents=True)
1164 slice_hrn = ticket.gidObject.get_hrn()
1165 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1166 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1167 slice_cred = self.slice_credential_string(slice_hrn)
1169 # get a list of node hostnames from the RSpec
1170 tree = etree.parse(StringIO(ticket.rspec))
1171 root = tree.getroot()
1172 hostnames = root.xpath("./network/site/node/hostname/text()")
1174 # create an xmlrpc connection to the component manager at each of these
1175 # components and gall redeem_ticket
1177 for hostname in hostnames:
1179 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1180 cm_url="http://%s:%s/"%(hostname,CM_PORT)
1181 server = SfaServerProxy(cm_url, self.private_key, self.my_gid)
1182 server = self.server_proxy(hostname, CM_PORT, self.private_key,
1183 timeout=self.options.timeout, verbose=self.options.debug)
1184 server.RedeemTicket(ticket_string, slice_cred)
1185 self.logger.info("Success")
1186 except socket.gaierror:
1187 self.logger.error("redeem_ticket failed on %s: Component Manager not accepting requests"%hostname)
1188 except Exception, e:
1189 self.logger.log_exc(e.message)
1192 def create_gid(self, options, args):
1194 Create a GID (CreateGid)
1199 target_hrn = args[0]
1200 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.client_bootstrap.my_gid_string())
1202 filename = options.file
1204 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1205 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1206 GID(string=gid).save_to_file(filename)
1209 def delegate(self, options, args):
1211 (locally) create delegate credential for use by given hrn
1213 delegee_hrn = args[0]
1214 if options.delegate_user:
1215 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1216 elif options.delegate_slice:
1217 slice_cred = self.slice_credential_string(options.delegate_slice)
1218 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1220 self.logger.warning("Must specify either --user or --slice <hrn>")
1222 delegated_cred = Credential(string=cred)
1223 object_hrn = delegated_cred.get_gid_object().get_hrn()
1224 if options.delegate_user:
1225 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1226 + get_leaf(object_hrn) + ".cred")
1227 elif options.delegate_slice:
1228 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1229 + get_leaf(object_hrn) + ".cred")
1231 delegated_cred.save_to_file(dest_fn, save_parents=True)
1233 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1235 def get_trusted_certs(self, options, args):
1237 return uhe trusted certs at this interface (get_trusted_certs)
1239 trusted_certs = self.registry().get_trusted_certs()
1240 for trusted_cert in trusted_certs:
1241 gid = GID(string=trusted_cert)
1243 cert = Certificate(string=trusted_cert)
1244 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
1247 def config (self, options, args):
1248 "Display contents of current config"