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 rec_record = Record(dict=record_dict)
151 str = record.save_to_string()
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"),
224 def print_command_help (self, options):
225 verbose=getattr(options,'verbose')
226 format3="%18s %-15s %s"
229 print format3%("command","cmd_args","description")
233 self.create_parser().print_help()
234 for command in self.available_names:
235 args=self.available_dict[command]
236 method=getattr(self,command,None)
238 if method: doc=getattr(method,'__doc__',"")
239 if not doc: doc="*** no doc found ***"
240 doc=doc.strip(" \t\n")
241 doc=doc.replace("\n","\n"+35*' ')
244 print format3%(command,args,doc)
246 self.create_command_parser(command).print_help()
248 def create_command_parser(self, command):
249 if command not in self.available_dict:
250 msg="Invalid command\n"
252 msg += ','.join(self.available_names)
253 self.logger.critical(msg)
256 parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \
257 % (command, self.available_dict[command]))
259 # user specifies remote aggregate/sm/component
260 if command in ("resources", "slices", "create", "delete", "start", "stop",
261 "restart", "shutdown", "get_ticket", "renew", "status"):
262 parser.add_option("-d", "--delegate", dest="delegate", default=None,
264 help="Include a credential delegated to the user's root"+\
265 "authority in set of credentials for this call")
267 # registy filter option
268 if command in ("list", "show", "remove"):
269 parser.add_option("-t", "--type", dest="type", type="choice",
270 help="type filter ([all]|user|slice|authority|node|aggregate)",
271 choices=("all", "user", "slice", "authority", "node", "aggregate"),
273 if command in ("resources"):
275 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
276 help="schema type and version of resulting RSpec")
277 # disable/enable cached rspecs
278 parser.add_option("-c", "--current", dest="current", default=False,
280 help="Request the current rspec bypassing the cache. Cached rspecs are returned by default")
282 parser.add_option("-f", "--format", dest="format", type="choice",
283 help="display format ([xml]|dns|ip)", default="xml",
284 choices=("xml", "dns", "ip"))
285 #panos: a new option to define the type of information about resources a user is interested in
286 parser.add_option("-i", "--info", dest="info",
287 help="optional component information", default=None)
290 # 'create' does return the new rspec, makes sense to save that too
291 if command in ("resources", "show", "list", "create_gid", 'create'):
292 parser.add_option("-o", "--output", dest="file",
293 help="output XML to file", metavar="FILE", default=None)
295 if command in ("show", "list"):
296 parser.add_option("-f", "--format", dest="format", type="choice",
297 help="display format ([text]|xml)", default="text",
298 choices=("text", "xml"))
300 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
301 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
302 choices=("xml", "xmllist", "hrnlist"))
304 if command in ("delegate"):
305 parser.add_option("-u", "--user",
306 action="store_true", dest="delegate_user", default=False,
307 help="delegate user credential")
308 parser.add_option("-s", "--slice", dest="delegate_slice",
309 help="delegate slice credential", metavar="HRN", default=None)
311 if command in ("version"):
312 parser.add_option("-R","--registry-version",
313 action="store_true", dest="version_registry", default=False,
314 help="probe registry version instead of sliceapi")
315 parser.add_option("-l","--local",
316 action="store_true", dest="version_local", default=False,
317 help="display version of the local client")
322 def create_parser(self):
324 # Generate command line parser
325 parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
326 description="Commands: %s"%(" ".join(self.available_names)))
327 parser.add_option("-r", "--registry", dest="registry",
328 help="root registry", metavar="URL", default=None)
329 parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
330 help="slice API - in general a SM URL, but can be used to talk to an aggregate")
331 parser.add_option("-R", "--raw", dest="raw", default=None,
332 help="Save raw, unparsed server response to a file")
333 parser.add_option("", "--rawformat", dest="rawformat", type="choice",
334 help="raw file format ([text]|pickled|json)", default="text",
335 choices=("text","pickled","json"))
336 parser.add_option("", "--rawbanner", dest="rawbanner", default=None,
337 help="text string to write before and after raw output")
338 parser.add_option("-d", "--dir", dest="sfi_dir",
339 help="config & working directory - default is %default",
340 metavar="PATH", default=Sfi.default_sfi_dir())
341 parser.add_option("-u", "--user", dest="user",
342 help="user name", metavar="HRN", default=None)
343 parser.add_option("-a", "--auth", dest="auth",
344 help="authority name", metavar="HRN", default=None)
345 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
346 help="verbose mode - cumulative")
347 parser.add_option("-D", "--debug",
348 action="store_true", dest="debug", default=False,
349 help="Debug (xml-rpc) protocol messages")
350 # would it make sense to use ~/.ssh/id_rsa as a default here ?
351 parser.add_option("-k", "--private-key",
352 action="store", dest="user_private_key", default=None,
353 help="point to the private key file to use if not yet installed in sfi_dir")
354 parser.add_option("-t", "--timeout", dest="timeout", default=None,
355 help="Amout of time to wait before timing out the request")
356 parser.add_option("-?", "--commands",
357 action="store_true", dest="command_help", default=False,
358 help="one page summary on commands & exit")
359 parser.disable_interspersed_args()
364 def print_help (self):
365 self.sfi_parser.print_help()
366 self.command_parser.print_help()
369 # Main: parse arguments and dispatch to command
371 def dispatch(self, command, command_options, command_args):
372 return getattr(self, command)(command_options, command_args)
375 self.sfi_parser = self.create_parser()
376 (options, args) = self.sfi_parser.parse_args()
377 if options.command_help:
378 self.print_command_help(options)
380 self.options = options
382 self.logger.setLevelFromOptVerbose(self.options.verbose)
385 self.logger.critical("No command given. Use -h for help.")
386 self.print_command_help(options)
390 self.command_parser = self.create_command_parser(command)
391 (command_options, command_args) = self.command_parser.parse_args(args[1:])
392 self.command_options = command_options
396 self.logger.info("Command=%s" % command)
399 self.dispatch(command, command_options, command_args)
401 self.logger.critical ("Unknown command %s"%command)
408 def read_config(self):
409 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
411 config = Config (config_file)
413 self.logger.critical("Failed to read configuration file %s"%config_file)
414 self.logger.info("Make sure to remove the export clauses and to add quotes")
415 if self.options.verbose==0:
416 self.logger.info("Re-run with -v for more details")
418 self.logger.log_exc("Could not read config file %s"%config_file)
423 if (self.options.sm is not None):
424 self.sm_url = self.options.sm
425 elif hasattr(config, "SFI_SM"):
426 self.sm_url = config.SFI_SM
428 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
432 if (self.options.registry is not None):
433 self.reg_url = self.options.registry
434 elif hasattr(config, "SFI_REGISTRY"):
435 self.reg_url = config.SFI_REGISTRY
437 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
441 if (self.options.user is not None):
442 self.user = self.options.user
443 elif hasattr(config, "SFI_USER"):
444 self.user = config.SFI_USER
446 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
450 if (self.options.auth is not None):
451 self.authority = self.options.auth
452 elif hasattr(config, "SFI_AUTH"):
453 self.authority = config.SFI_AUTH
455 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
462 # Get various credential and spec files
464 # Establishes limiting conventions
465 # - conflates MAs and SAs
466 # - assumes last token in slice name is unique
468 # Bootstraps credentials
469 # - bootstrap user credential from self-signed certificate
470 # - bootstrap authority credential from user credential
471 # - bootstrap slice credential from user credential
474 # init self-signed cert, user credentials and gid
475 def bootstrap (self):
476 bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir)
477 # if -k is provided, use this to initialize private key
478 if self.options.user_private_key:
479 bootstrap.init_private_key_if_missing (self.options.user_private_key)
481 # trigger legacy compat code if needed
482 # the name has changed from just <leaf>.pkey to <hrn>.pkey
483 if not os.path.isfile(bootstrap.private_key_filename()):
484 self.logger.info ("private key not found, trying legacy name")
486 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%get_leaf(self.user))
487 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
488 bootstrap.init_private_key_if_missing (legacy_private_key)
489 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
491 self.logger.log_exc("Can't find private key ")
495 bootstrap.bootstrap_my_gid()
496 # extract what's needed
497 self.private_key = bootstrap.private_key()
498 self.my_credential_string = bootstrap.my_credential_string ()
499 self.my_gid = bootstrap.my_gid ()
500 self.bootstrap = bootstrap
503 def my_authority_credential_string(self):
504 if not self.authority:
505 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
507 return self.bootstrap.authority_credential_string (self.authority)
509 def slice_credential_string(self, name):
510 return self.bootstrap.slice_credential_string (name)
512 # xxx should be supported by sfaclientbootstrap as well
513 def delegate_cred(self, object_cred, hrn, type='authority'):
514 # the gid and hrn of the object we are delegating
515 if isinstance(object_cred, str):
516 object_cred = Credential(string=object_cred)
517 object_gid = object_cred.get_gid_object()
518 object_hrn = object_gid.get_hrn()
520 if not object_cred.get_privileges().get_all_delegate():
521 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
524 # the delegating user's gid
525 caller_gidfile = self.my_gid()
527 # the gid of the user who will be delegated to
528 delegee_gid = self.bootstrap.gid(hrn,type)
529 delegee_hrn = delegee_gid.get_hrn()
530 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
531 return dcred.save_to_string(save_parents=True)
534 # Management of the servers
539 if not hasattr (self, 'registry_proxy'):
540 self.logger.info("Contacting Registry at: %s"%self.reg_url)
541 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
542 timeout=self.options.timeout, verbose=self.options.debug)
543 return self.registry_proxy
547 if not hasattr (self, 'sliceapi_proxy'):
548 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
549 if hasattr(self.command_options,'component') and self.command_options.component:
550 # resolve the hrn at the registry
551 node_hrn = self.command_options.component
552 records = self.registry().Resolve(node_hrn, self.my_credential_string)
553 records = filter_records('node', records)
555 self.logger.warning("No such component:%r"% opts.component)
557 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
558 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
560 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
561 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
562 self.sm_url = 'http://' + self.sm_url
563 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
564 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
565 timeout=self.options.timeout, verbose=self.options.debug)
566 return self.sliceapi_proxy
568 def get_cached_server_version(self, server):
569 # check local cache first
572 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
573 cache_key = server.url + "-version"
575 cache = Cache(cache_file)
578 self.logger.info("Local cache not found at: %s" % cache_file)
581 version = cache.get(cache_key)
584 result = server.GetVersion()
585 version= ReturnValue.get_value(result)
586 # cache version for 20 minutes
587 cache.add(cache_key, version, ttl= 60*20)
588 self.logger.info("Updating cache file %s" % cache_file)
589 cache.save_to_file(cache_file)
593 ### resurrect this temporarily so we can support V1 aggregates for a while
594 def server_supports_options_arg(self, server):
596 Returns true if server support the optional call_id arg, false otherwise.
598 server_version = self.get_cached_server_version(server)
600 # xxx need to rewrite this
601 if int(server_version.get('geni_api')) >= 2:
605 def server_supports_call_id_arg(self, server):
606 server_version = self.get_cached_server_version(server)
608 if 'sfa' in server_version and 'code_tag' in server_version:
609 code_tag = server_version['code_tag']
610 code_tag_parts = code_tag.split("-")
611 version_parts = code_tag_parts[0].split(".")
612 major, minor = version_parts[0], version_parts[1]
613 rev = code_tag_parts[1]
614 if int(major) == 1 and minor == 0 and build >= 22:
618 ### ois = options if supported
619 # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
620 def ois (self, server, option_dict):
621 if self.server_supports_options_arg (server):
623 elif self.server_supports_call_id_arg (server):
624 return [ unique_call_id () ]
628 ### cis = call_id if supported - like ois
629 def cis (self, server):
630 if self.server_supports_call_id_arg (server):
631 return [ unique_call_id ]
635 ######################################## miscell utilities
636 def get_rspec_file(self, rspec):
637 if (os.path.isabs(rspec)):
640 file = os.path.join(self.options.sfi_dir, rspec)
641 if (os.path.isfile(file)):
644 self.logger.critical("No such rspec file %s"%rspec)
647 def get_record_file(self, record):
648 if (os.path.isabs(record)):
651 file = os.path.join(self.options.sfi_dir, record)
652 if (os.path.isfile(file)):
655 self.logger.critical("No such registry record file %s"%record)
659 #==========================================================================
660 # Following functions implement the commands
662 # Registry-related commands
663 #==========================================================================
665 def version(self, options, args):
667 display an SFA server version (GetVersion)
668 or version information about sfi itself
670 if options.version_local:
671 version=version_core()
673 if options.version_registry:
674 server=self.registry()
676 server = self.sliceapi()
677 result = server.GetVersion()
678 version = ReturnValue.get_value(result)
680 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
682 pprinter = PrettyPrinter(indent=4)
683 pprinter.pprint(version)
685 def list(self, options, args):
687 list entries in named authority registry (List)
694 list = self.registry().List(hrn, self.my_credential_string)
696 raise Exception, "Not enough parameters for the 'list' command"
698 # filter on person, slice, site, node, etc.
699 # THis really should be in the self.filter_records funct def comment...
700 list = filter_records(options.type, list)
702 print "%s (%s)" % (record['hrn'], record['type'])
704 save_records_to_file(options.file, list, options.fileformat)
707 def show(self, options, args):
709 show details about named registry record (Resolve)
715 record_dicts = self.registry().Resolve(hrn, self.my_credential_string)
716 record_dicts = filter_records(options.type, record_dicts)
718 self.logger.error("No record of type %s"% options.type)
719 records = [ Record(dict=record_dict) for record_dict in record_dicts ]
720 for record in records:
721 if (options.format == "text"): record.dump()
722 else: print record.save_as_xml()
724 save_records_to_file(options.file, record_dicts, options.fileformat)
727 def add(self, options, args):
728 "add record into registry from xml file (Register)"
729 auth_cred = self.my_authority_credential_string()
733 record_filepath = args[0]
734 rec_file = self.get_record_file(record_filepath)
735 record = load_record_from_file(rec_file).todict()
736 return self.registry().Register(record, auth_cred)
738 def update(self, options, args):
739 "update record into registry from xml file (Update)"
743 rec_file = self.get_record_file(args[0])
744 record = load_record_from_file(rec_file)
745 if record.type == "user":
746 if record.hrn == self.user:
747 cred = self.my_credential_string
749 cred = self.my_authority_credential_string()
750 elif record.type in ["slice"]:
752 cred = self.slice_credential_string(record.hrn)
753 except ServerException, e:
754 # XXX smbaker -- once we have better error return codes, update this
755 # to do something better than a string compare
756 if "Permission error" in e.args[0]:
757 cred = self.my_authority_credential_string()
760 elif record.type in ["authority"]:
761 cred = self.my_authority_credential_string()
762 elif record.type == 'node':
763 cred = self.my_authority_credential_string()
765 raise "unknown record type" + record.type
766 record_dict = record.todict()
767 return self.registry().Update(record_dict, cred)
769 def remove(self, options, args):
770 "remove registry record by name (Remove)"
771 auth_cred = self.my_authority_credential_string()
779 return self.registry().Remove(hrn, auth_cred, type)
781 # ==================================================================
782 # Slice-related commands
783 # ==================================================================
785 def slices(self, options, args):
786 "list instantiated slices (ListSlices) - returns urn's"
787 server = self.sliceapi()
789 creds = [self.my_credential_string]
791 delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority))
792 creds.append(delegated_cred)
793 # options and call_id when supported
795 api_options['call_id']=unique_call_id()
796 result = server.ListSlices(creds, *self.ois(server,api_options))
797 value = ReturnValue.get_value(result)
799 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
804 # show rspec for named slice
805 def resources(self, options, args):
807 with no arg, discover available resources, (ListResources)
808 or with an slice hrn, shows currently provisioned resources
810 server = self.sliceapi()
815 creds.append(self.slice_credential_string(args[0]))
817 creds.append(self.my_credential_string)
819 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
821 # no need to check if server accepts the options argument since the options has
822 # been a required argument since v1 API
824 # always send call_id to v2 servers
825 api_options ['call_id'] = unique_call_id()
826 # ask for cached value if available
827 api_options ['cached'] = True
830 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
832 api_options['info'] = options.info
834 if options.current == True:
835 api_options['cached'] = False
837 api_options['cached'] = True
838 if options.rspec_version:
839 version_manager = VersionManager()
840 server_version = self.get_cached_server_version(server)
841 if 'sfa' in server_version:
842 # just request the version the client wants
843 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
845 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
847 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
848 result = server.ListResources (creds, api_options)
849 value = ReturnValue.get_value(result)
851 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
852 if options.file is not None:
853 save_rspec_to_file(value, options.file)
854 if (self.options.raw is None) and (options.file is None):
855 display_rspec(value, options.format)
859 def create(self, options, args):
861 create or update named slice with given rspec
863 server = self.sliceapi()
865 # xxx do we need to check usage (len(args)) ?
868 slice_urn = hrn_to_urn(slice_hrn, 'slice')
871 creds = [self.slice_credential_string(slice_hrn)]
872 delegated_cred = None
873 server_version = self.get_cached_server_version(server)
874 if server_version.get('interface') == 'slicemgr':
875 # delegate our cred to the slice manager
876 # do not delegate cred to slicemgr...not working at the moment
878 #if server_version.get('hrn'):
879 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
880 #elif server_version.get('urn'):
881 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
884 rspec_file = self.get_rspec_file(args[1])
885 rspec = open(rspec_file).read()
888 # need to pass along user keys to the aggregate.
890 # { urn: urn:publicid:IDN+emulab.net+user+alice
891 # keys: [<ssh key A>, <ssh key B>]
894 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
895 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
896 slice_record = slice_records[0]
897 user_hrns = slice_record['researcher']
898 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
899 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
901 if 'sfa' not in server_version:
902 users = pg_users_arg(user_records)
904 rspec.filter({'component_manager_id': server_version['urn']})
905 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
907 print >>sys.stderr, "\r\n \r\n \r\n WOOOOOO"
908 users = sfa_users_arg(user_records, slice_record)
910 # do not append users, keys, or slice tags. Anything
911 # not contained in this request will be removed from the slice
913 # CreateSliver has supported the options argument for a while now so it should
914 # be safe to assume this server support it
916 api_options ['append'] = False
917 api_options ['call_id'] = unique_call_id()
919 result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server, api_options))
920 value = ReturnValue.get_value(result)
922 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
923 if options.file is not None:
924 save_rspec_to_file (value, options.file)
925 if (self.options.raw is None) and (options.file is None):
930 def delete(self, options, args):
932 delete named slice (DeleteSliver)
934 server = self.sliceapi()
938 slice_urn = hrn_to_urn(slice_hrn, 'slice')
941 slice_cred = self.slice_credential_string(slice_hrn)
944 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
945 creds.append(delegated_cred)
947 # options and call_id when supported
949 api_options ['call_id'] = unique_call_id()
950 result = server.DeleteSliver(slice_urn, creds, *self.ois(server, api_options ) )
951 value = ReturnValue.get_value(result)
953 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
958 def status(self, options, args):
960 retrieve slice status (SliverStatus)
962 server = self.sliceapi()
966 slice_urn = hrn_to_urn(slice_hrn, 'slice')
969 slice_cred = self.slice_credential_string(slice_hrn)
972 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
973 creds.append(delegated_cred)
975 # options and call_id when supported
977 api_options['call_id']=unique_call_id()
978 result = server.SliverStatus(slice_urn, creds, *self.ois(server,api_options))
979 value = ReturnValue.get_value(result)
981 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
985 def start(self, options, args):
987 start named slice (Start)
989 server = self.sliceapi()
993 slice_urn = hrn_to_urn(slice_hrn, 'slice')
996 slice_cred = self.slice_credential_string(args[0])
999 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1000 creds.append(delegated_cred)
1001 # xxx Thierry - does this not need an api_options as well ?
1002 result = server.Start(slice_urn, creds)
1003 value = ReturnValue.get_value(result)
1004 if self.options.raw:
1005 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1010 def stop(self, options, args):
1012 stop named slice (Stop)
1014 server = self.sliceapi()
1017 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1019 slice_cred = self.slice_credential_string(args[0])
1020 creds = [slice_cred]
1021 if options.delegate:
1022 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1023 creds.append(delegated_cred)
1024 result = server.Stop(slice_urn, creds)
1025 value = ReturnValue.get_value(result)
1026 if self.options.raw:
1027 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1033 def reset(self, options, args):
1035 reset named slice (reset_slice)
1037 server = self.sliceapi()
1040 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1042 slice_cred = self.slice_credential_string(args[0])
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 result = server.reset_slice(creds, slice_urn)
1048 value = ReturnValue.get_value(result)
1049 if self.options.raw:
1050 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1055 def renew(self, options, args):
1057 renew slice (RenewSliver)
1059 server = self.sliceapi()
1062 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1064 slice_cred = self.slice_credential_string(args[0])
1065 creds = [slice_cred]
1066 if options.delegate:
1067 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1068 creds.append(delegated_cred)
1071 # options and call_id when supported
1073 api_options['call_id']=unique_call_id()
1074 result = server.RenewSliver(slice_urn, creds, time, *self.ois(server,api_options))
1075 value = ReturnValue.get_value(result)
1076 if self.options.raw:
1077 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1083 def shutdown(self, options, args):
1085 shutdown named slice (Shutdown)
1087 server = self.sliceapi()
1090 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1092 slice_cred = self.slice_credential_string(slice_hrn)
1093 creds = [slice_cred]
1094 if options.delegate:
1095 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1096 creds.append(delegated_cred)
1097 result = server.Shutdown(slice_urn, creds)
1098 value = ReturnValue.get_value(result)
1099 if self.options.raw:
1100 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1106 def get_ticket(self, options, args):
1108 get a ticket for the specified slice
1110 server = self.sliceapi()
1112 slice_hrn, rspec_path = args[0], args[1]
1113 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1115 slice_cred = self.slice_credential_string(slice_hrn)
1116 creds = [slice_cred]
1117 if options.delegate:
1118 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1119 creds.append(delegated_cred)
1121 rspec_file = self.get_rspec_file(rspec_path)
1122 rspec = open(rspec_file).read()
1123 # options and call_id when supported
1125 api_options['call_id']=unique_call_id()
1126 # get ticket at the server
1127 ticket_string = server.GetTicket(slice_urn, creds, rspec, *self.ois(server,api_options))
1129 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1130 self.logger.info("writing ticket to %s"%file)
1131 ticket = SfaTicket(string=ticket_string)
1132 ticket.save_to_file(filename=file, save_parents=True)
1134 def redeem_ticket(self, options, args):
1136 Connects to nodes in a slice and redeems a ticket
1137 (slice hrn is retrieved from the ticket)
1139 ticket_file = args[0]
1141 # get slice hrn from the ticket
1142 # use this to get the right slice credential
1143 ticket = SfaTicket(filename=ticket_file)
1145 ticket_string = ticket.save_to_string(save_parents=True)
1147 slice_hrn = ticket.gidObject.get_hrn()
1148 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1149 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1150 slice_cred = self.slice_credential_string(slice_hrn)
1152 # get a list of node hostnames from the RSpec
1153 tree = etree.parse(StringIO(ticket.rspec))
1154 root = tree.getroot()
1155 hostnames = root.xpath("./network/site/node/hostname/text()")
1157 # create an xmlrpc connection to the component manager at each of these
1158 # components and gall redeem_ticket
1160 for hostname in hostnames:
1162 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1163 cm_url="http://%s:%s/"%(hostname,CM_PORT)
1164 server = SfaServerProxy(cm_url, self.private_key, self.my_gid)
1165 server = self.server_proxy(hostname, CM_PORT, self.private_key,
1166 timeout=self.options.timeout, verbose=self.options.debug)
1167 server.RedeemTicket(ticket_string, slice_cred)
1168 self.logger.info("Success")
1169 except socket.gaierror:
1170 self.logger.error("redeem_ticket failed on %s: Component Manager not accepting requests"%hostname)
1171 except Exception, e:
1172 self.logger.log_exc(e.message)
1175 def create_gid(self, options, args):
1177 Create a GID (CreateGid)
1182 target_hrn = args[0]
1183 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.bootstrap.my_gid_string())
1185 filename = options.file
1187 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1188 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1189 GID(string=gid).save_to_file(filename)
1192 def delegate(self, options, args):
1194 (locally) create delegate credential for use by given hrn
1196 delegee_hrn = args[0]
1197 if options.delegate_user:
1198 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1199 elif options.delegate_slice:
1200 slice_cred = self.slice_credential_string(options.delegate_slice)
1201 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1203 self.logger.warning("Must specify either --user or --slice <hrn>")
1205 delegated_cred = Credential(string=cred)
1206 object_hrn = delegated_cred.get_gid_object().get_hrn()
1207 if options.delegate_user:
1208 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1209 + get_leaf(object_hrn) + ".cred")
1210 elif options.delegate_slice:
1211 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1212 + get_leaf(object_hrn) + ".cred")
1214 delegated_cred.save_to_file(dest_fn, save_parents=True)
1216 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1218 def get_trusted_certs(self, options, args):
1220 return uhe trusted certs at this interface (get_trusted_certs)
1222 trusted_certs = self.registry().get_trusted_certs()
1223 for trusted_cert in trusted_certs:
1224 gid = GID(string=trusted_cert)
1226 cert = Certificate(string=trusted_cert)
1227 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())