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 print >>sys.stderr, "\r\n \r\n \r\n WOOOOOO"
927 users = sfa_users_arg(user_records, slice_record)
929 # do not append users, keys, or slice tags. Anything
930 # not contained in this request will be removed from the slice
932 # CreateSliver has supported the options argument for a while now so it should
933 # be safe to assume this server support it
935 api_options ['append'] = False
936 api_options ['call_id'] = unique_call_id()
937 result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server, api_options))
938 value = ReturnValue.get_value(result)
940 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
941 if options.file is not None:
942 save_rspec_to_file (value, options.file)
943 if (self.options.raw is None) and (options.file is None):
948 def delete(self, options, args):
950 delete named slice (DeleteSliver)
952 server = self.sliceapi()
956 slice_urn = hrn_to_urn(slice_hrn, 'slice')
959 slice_cred = self.slice_credential_string(slice_hrn)
962 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
963 creds.append(delegated_cred)
965 # options and call_id when supported
967 api_options ['call_id'] = unique_call_id()
968 result = server.DeleteSliver(slice_urn, creds, *self.ois(server, api_options ) )
969 value = ReturnValue.get_value(result)
971 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
976 def status(self, options, args):
978 retrieve slice status (SliverStatus)
980 server = self.sliceapi()
984 slice_urn = hrn_to_urn(slice_hrn, 'slice')
987 slice_cred = self.slice_credential_string(slice_hrn)
990 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
991 creds.append(delegated_cred)
993 # options and call_id when supported
995 api_options['call_id']=unique_call_id()
996 result = server.SliverStatus(slice_urn, creds, *self.ois(server,api_options))
997 value = ReturnValue.get_value(result)
999 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1003 def start(self, options, args):
1005 start named slice (Start)
1007 server = self.sliceapi()
1011 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1014 slice_cred = self.slice_credential_string(args[0])
1015 creds = [slice_cred]
1016 if options.delegate:
1017 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1018 creds.append(delegated_cred)
1019 # xxx Thierry - does this not need an api_options as well ?
1020 result = server.Start(slice_urn, creds)
1021 value = ReturnValue.get_value(result)
1022 if self.options.raw:
1023 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1028 def stop(self, options, args):
1030 stop named slice (Stop)
1032 server = self.sliceapi()
1035 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1037 slice_cred = self.slice_credential_string(args[0])
1038 creds = [slice_cred]
1039 if options.delegate:
1040 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1041 creds.append(delegated_cred)
1042 result = server.Stop(slice_urn, creds)
1043 value = ReturnValue.get_value(result)
1044 if self.options.raw:
1045 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1051 def reset(self, options, args):
1053 reset named slice (reset_slice)
1055 server = self.sliceapi()
1058 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1060 slice_cred = self.slice_credential_string(args[0])
1061 creds = [slice_cred]
1062 if options.delegate:
1063 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1064 creds.append(delegated_cred)
1065 result = server.reset_slice(creds, slice_urn)
1066 value = ReturnValue.get_value(result)
1067 if self.options.raw:
1068 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1073 def renew(self, options, args):
1075 renew slice (RenewSliver)
1077 server = self.sliceapi()
1080 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1082 slice_cred = self.slice_credential_string(args[0])
1083 creds = [slice_cred]
1084 if options.delegate:
1085 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1086 creds.append(delegated_cred)
1089 # options and call_id when supported
1091 api_options['call_id']=unique_call_id()
1092 result = server.RenewSliver(slice_urn, creds, time, *self.ois(server,api_options))
1093 value = ReturnValue.get_value(result)
1094 if self.options.raw:
1095 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1101 def shutdown(self, options, args):
1103 shutdown named slice (Shutdown)
1105 server = self.sliceapi()
1108 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1110 slice_cred = self.slice_credential_string(slice_hrn)
1111 creds = [slice_cred]
1112 if options.delegate:
1113 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1114 creds.append(delegated_cred)
1115 result = server.Shutdown(slice_urn, creds)
1116 value = ReturnValue.get_value(result)
1117 if self.options.raw:
1118 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1124 def get_ticket(self, options, args):
1126 get a ticket for the specified slice
1128 server = self.sliceapi()
1130 slice_hrn, rspec_path = args[0], args[1]
1131 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1133 slice_cred = self.slice_credential_string(slice_hrn)
1134 creds = [slice_cred]
1135 if options.delegate:
1136 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1137 creds.append(delegated_cred)
1139 rspec_file = self.get_rspec_file(rspec_path)
1140 rspec = open(rspec_file).read()
1141 # options and call_id when supported
1143 api_options['call_id']=unique_call_id()
1144 # get ticket at the server
1145 ticket_string = server.GetTicket(slice_urn, creds, rspec, *self.ois(server,api_options))
1147 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1148 self.logger.info("writing ticket to %s"%file)
1149 ticket = SfaTicket(string=ticket_string)
1150 ticket.save_to_file(filename=file, save_parents=True)
1152 def redeem_ticket(self, options, args):
1154 Connects to nodes in a slice and redeems a ticket
1155 (slice hrn is retrieved from the ticket)
1157 ticket_file = args[0]
1159 # get slice hrn from the ticket
1160 # use this to get the right slice credential
1161 ticket = SfaTicket(filename=ticket_file)
1163 ticket_string = ticket.save_to_string(save_parents=True)
1165 slice_hrn = ticket.gidObject.get_hrn()
1166 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1167 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1168 slice_cred = self.slice_credential_string(slice_hrn)
1170 # get a list of node hostnames from the RSpec
1171 tree = etree.parse(StringIO(ticket.rspec))
1172 root = tree.getroot()
1173 hostnames = root.xpath("./network/site/node/hostname/text()")
1175 # create an xmlrpc connection to the component manager at each of these
1176 # components and gall redeem_ticket
1178 for hostname in hostnames:
1180 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1181 cm_url="http://%s:%s/"%(hostname,CM_PORT)
1182 server = SfaServerProxy(cm_url, self.private_key, self.my_gid)
1183 server = self.server_proxy(hostname, CM_PORT, self.private_key,
1184 timeout=self.options.timeout, verbose=self.options.debug)
1185 server.RedeemTicket(ticket_string, slice_cred)
1186 self.logger.info("Success")
1187 except socket.gaierror:
1188 self.logger.error("redeem_ticket failed on %s: Component Manager not accepting requests"%hostname)
1189 except Exception, e:
1190 self.logger.log_exc(e.message)
1193 def create_gid(self, options, args):
1195 Create a GID (CreateGid)
1200 target_hrn = args[0]
1201 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.client_bootstrap.my_gid_string())
1203 filename = options.file
1205 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1206 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1207 GID(string=gid).save_to_file(filename)
1210 def delegate(self, options, args):
1212 (locally) create delegate credential for use by given hrn
1214 delegee_hrn = args[0]
1215 if options.delegate_user:
1216 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1217 elif options.delegate_slice:
1218 slice_cred = self.slice_credential_string(options.delegate_slice)
1219 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1221 self.logger.warning("Must specify either --user or --slice <hrn>")
1223 delegated_cred = Credential(string=cred)
1224 object_hrn = delegated_cred.get_gid_object().get_hrn()
1225 if options.delegate_user:
1226 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1227 + get_leaf(object_hrn) + ".cred")
1228 elif options.delegate_slice:
1229 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1230 + get_leaf(object_hrn) + ".cred")
1232 delegated_cred.save_to_file(dest_fn, save_parents=True)
1234 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1236 def get_trusted_certs(self, options, args):
1238 return uhe trusted certs at this interface (get_trusted_certs)
1240 trusted_certs = self.registry().get_trusted_certs()
1241 for trusted_cert in trusted_certs:
1242 gid = GID(string=trusted_cert)
1244 cert = Certificate(string=trusted_cert)
1245 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
1248 def config (self, options, args):
1249 "Display contents of current config"