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.model import RegRecord, RegAuthority, RegUser, RegSlice, RegNode
33 from sfa.storage.model import make_record
35 from sfa.rspecs.rspec import RSpec
36 from sfa.rspecs.rspec_converter import RSpecConverter
37 from sfa.rspecs.version_manager import VersionManager
39 from sfa.client.sfaclientlib import SfaClientBootstrap
40 from sfa.client.sfaserverproxy import SfaServerProxy, ServerException
41 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
42 from sfa.client.return_value import ReturnValue
46 # utility methods here
48 def display_rspec(rspec, format='rspec'):
50 tree = etree.parse(StringIO(rspec))
52 result = root.xpath("./network/site/node/hostname/text()")
53 elif format in ['ip']:
54 # The IP address is not yet part of the new RSpec
55 # so this doesn't do anything yet.
56 tree = etree.parse(StringIO(rspec))
58 result = root.xpath("./network/site/node/ipv4/text()")
65 def display_list(results):
66 for result in results:
69 def display_records(recordList, dump=False):
70 ''' Print all fields in the record'''
71 for record in recordList:
72 display_record(record, dump)
74 def display_record(record, dump=False):
78 info = record.getdict()
79 print "%s (%s)" % (info['hrn'], info['type'])
83 def filter_records(type, records):
85 for record in records:
86 if (record['type'] == type) or (type == "all"):
87 filtered_records.append(record)
88 return filtered_records
92 def save_raw_to_file(var, filename, format="text", banner=None):
94 # if filename is "-", send it to stdout
97 f = open(filename, "w")
102 elif format == "pickled":
103 f.write(pickle.dumps(var))
104 elif format == "json":
105 if hasattr(json, "dumps"):
106 f.write(json.dumps(var)) # python 2.6
108 f.write(json.write(var)) # python 2.5
110 # this should never happen
111 print "unknown output format", format
113 f.write('\n'+banner+"\n")
115 def save_rspec_to_file(rspec, filename):
116 if not filename.endswith(".rspec"):
117 filename = filename + ".rspec"
118 f = open(filename, 'w')
123 def save_records_to_file(filename, record_dicts, format="xml"):
126 for record_dict in record_dicts:
128 save_record_to_file(filename + "." + str(index), record_dict)
130 save_record_to_file(filename, record_dict)
132 elif format == "xmllist":
133 f = open(filename, "w")
134 f.write("<recordlist>\n")
135 for record_dict in record_dicts:
136 record_obj=make_record (dict=record_dict)
137 f.write('<record hrn="' + record_obj.hrn + '" type="' + record_obj.type + '" />\n')
138 f.write("</recordlist>\n")
140 elif format == "hrnlist":
141 f = open(filename, "w")
142 for record_dict in record_dicts:
143 record_obj=make_record (dict=record_dict)
144 f.write(record_obj.hrn + "\n")
147 # this should never happen
148 print "unknown output format", format
150 def save_record_to_file(filename, record_dict):
151 rec_record = make_record (dict=record_dict)
152 str = record.save_to_string()
153 f=codecs.open(filename, encoding='utf-8',mode="w")
160 def load_record_from_file(filename):
161 f=codecs.open(filename, encoding="utf-8", mode="r")
162 xml_string = f.read()
164 return make_record (xml=xml_string)
168 def unique_call_id(): return uuid.uuid4().urn
172 # dirty hack to make this class usable from the outside
173 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user', 'user_private_key']
176 def default_sfi_dir ():
177 if os.path.isfile("./sfi_config"):
180 return os.path.expanduser("~/.sfi/")
182 # dummy to meet Sfi's expectations for its 'options' field
183 # i.e. s/t we can do setattr on
187 def __init__ (self,options=None):
188 if options is None: options=Sfi.DummyOptions()
189 for opt in Sfi.required_options:
190 if not hasattr(options,opt): setattr(options,opt,None)
191 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
192 self.options = options
194 self.authority = None
195 self.logger = sfi_logger
196 self.logger.enable_console()
197 self.available_names = [ tuple[0] for tuple in Sfi.available ]
198 self.available_dict = dict (Sfi.available)
200 # tuples command-name expected-args in the order in which they should appear in the help
203 ("list", "authority"),
206 ("update", "record"),
209 ("resources", "[slice_hrn]"),
210 ("create", "slice_hrn rspec"),
211 ("delete", "slice_hrn"),
212 ("status", "slice_hrn"),
213 ("start", "slice_hrn"),
214 ("stop", "slice_hrn"),
215 ("reset", "slice_hrn"),
216 ("renew", "slice_hrn time"),
217 ("shutdown", "slice_hrn"),
218 ("get_ticket", "slice_hrn rspec"),
219 ("redeem_ticket", "ticket"),
220 ("delegate", "name"),
221 ("create_gid", "[name]"),
222 ("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"))
305 if command in ("delegate"):
306 parser.add_option("-u", "--user",
307 action="store_true", dest="delegate_user", default=False,
308 help="delegate user credential")
309 parser.add_option("-s", "--slice", dest="delegate_slice",
310 help="delegate slice credential", metavar="HRN", default=None)
312 if command in ("version"):
313 parser.add_option("-R","--registry-version",
314 action="store_true", dest="version_registry", default=False,
315 help="probe registry version instead of sliceapi")
316 parser.add_option("-l","--local",
317 action="store_true", dest="version_local", default=False,
318 help="display version of the local client")
323 def create_parser(self):
325 # Generate command line parser
326 parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
327 description="Commands: %s"%(" ".join(self.available_names)))
328 parser.add_option("-r", "--registry", dest="registry",
329 help="root registry", metavar="URL", default=None)
330 parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
331 help="slice API - in general a SM URL, but can be used to talk to an aggregate")
332 parser.add_option("-R", "--raw", dest="raw", default=None,
333 help="Save raw, unparsed server response to a file")
334 parser.add_option("", "--rawformat", dest="rawformat", type="choice",
335 help="raw file format ([text]|pickled|json)", default="text",
336 choices=("text","pickled","json"))
337 parser.add_option("", "--rawbanner", dest="rawbanner", default=None,
338 help="text string to write before and after raw output")
339 parser.add_option("-d", "--dir", dest="sfi_dir",
340 help="config & working directory - default is %default",
341 metavar="PATH", default=Sfi.default_sfi_dir())
342 parser.add_option("-u", "--user", dest="user",
343 help="user name", metavar="HRN", default=None)
344 parser.add_option("-a", "--auth", dest="auth",
345 help="authority name", metavar="HRN", default=None)
346 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
347 help="verbose mode - cumulative")
348 parser.add_option("-D", "--debug",
349 action="store_true", dest="debug", default=False,
350 help="Debug (xml-rpc) protocol messages")
351 # would it make sense to use ~/.ssh/id_rsa as a default here ?
352 parser.add_option("-k", "--private-key",
353 action="store", dest="user_private_key", default=None,
354 help="point to the private key file to use if not yet installed in sfi_dir")
355 parser.add_option("-t", "--timeout", dest="timeout", default=None,
356 help="Amout of time to wait before timing out the request")
357 parser.add_option("-?", "--commands",
358 action="store_true", dest="command_help", default=False,
359 help="one page summary on commands & exit")
360 parser.disable_interspersed_args()
365 def print_help (self):
366 self.sfi_parser.print_help()
367 self.command_parser.print_help()
370 # Main: parse arguments and dispatch to command
372 def dispatch(self, command, command_options, command_args):
373 return getattr(self, command)(command_options, command_args)
376 self.sfi_parser = self.create_parser()
377 (options, args) = self.sfi_parser.parse_args()
378 if options.command_help:
379 self.print_command_help(options)
381 self.options = options
383 self.logger.setLevelFromOptVerbose(self.options.verbose)
386 self.logger.critical("No command given. Use -h for help.")
387 self.print_command_help(options)
391 self.command_parser = self.create_command_parser(command)
392 (command_options, command_args) = self.command_parser.parse_args(args[1:])
393 self.command_options = command_options
397 self.logger.info("Command=%s" % command)
400 self.dispatch(command, command_options, command_args)
402 self.logger.critical ("Unknown command %s"%command)
409 def read_config(self):
410 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
412 config = Config (config_file)
414 self.logger.critical("Failed to read configuration file %s"%config_file)
415 self.logger.info("Make sure to remove the export clauses and to add quotes")
416 if self.options.verbose==0:
417 self.logger.info("Re-run with -v for more details")
419 self.logger.log_exc("Could not read config file %s"%config_file)
424 if (self.options.sm is not None):
425 self.sm_url = self.options.sm
426 elif hasattr(config, "SFI_SM"):
427 self.sm_url = config.SFI_SM
429 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
433 if (self.options.registry is not None):
434 self.reg_url = self.options.registry
435 elif hasattr(config, "SFI_REGISTRY"):
436 self.reg_url = config.SFI_REGISTRY
438 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
442 if (self.options.user is not None):
443 self.user = self.options.user
444 elif hasattr(config, "SFI_USER"):
445 self.user = config.SFI_USER
447 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
451 if (self.options.auth is not None):
452 self.authority = self.options.auth
453 elif hasattr(config, "SFI_AUTH"):
454 self.authority = config.SFI_AUTH
456 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
463 # Get various credential and spec files
465 # Establishes limiting conventions
466 # - conflates MAs and SAs
467 # - assumes last token in slice name is unique
469 # Bootstraps credentials
470 # - bootstrap user credential from self-signed certificate
471 # - bootstrap authority credential from user credential
472 # - bootstrap slice credential from user credential
475 # init self-signed cert, user credentials and gid
476 def bootstrap (self):
477 bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir)
478 # if -k is provided, use this to initialize private key
479 if self.options.user_private_key:
480 bootstrap.init_private_key_if_missing (self.options.user_private_key)
482 # trigger legacy compat code if needed
483 # the name has changed from just <leaf>.pkey to <hrn>.pkey
484 if not os.path.isfile(bootstrap.private_key_filename()):
485 self.logger.info ("private key not found, trying legacy name")
487 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%get_leaf(self.user))
488 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
489 bootstrap.init_private_key_if_missing (legacy_private_key)
490 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
492 self.logger.log_exc("Can't find private key ")
496 bootstrap.bootstrap_my_gid()
497 # extract what's needed
498 self.private_key = bootstrap.private_key()
499 self.my_credential_string = bootstrap.my_credential_string ()
500 self.my_gid = bootstrap.my_gid ()
501 self.bootstrap = bootstrap
504 def my_authority_credential_string(self):
505 if not self.authority:
506 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
508 return self.bootstrap.authority_credential_string (self.authority)
510 def slice_credential_string(self, name):
511 return self.bootstrap.slice_credential_string (name)
513 # xxx should be supported by sfaclientbootstrap as well
514 def delegate_cred(self, object_cred, hrn, type='authority'):
515 # the gid and hrn of the object we are delegating
516 if isinstance(object_cred, str):
517 object_cred = Credential(string=object_cred)
518 object_gid = object_cred.get_gid_object()
519 object_hrn = object_gid.get_hrn()
521 if not object_cred.get_privileges().get_all_delegate():
522 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
525 # the delegating user's gid
526 caller_gidfile = self.my_gid()
528 # the gid of the user who will be delegated to
529 delegee_gid = self.bootstrap.gid(hrn,type)
530 delegee_hrn = delegee_gid.get_hrn()
531 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
532 return dcred.save_to_string(save_parents=True)
535 # Management of the servers
540 if not hasattr (self, 'registry_proxy'):
541 self.logger.info("Contacting Registry at: %s"%self.reg_url)
542 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
543 timeout=self.options.timeout, verbose=self.options.debug)
544 return self.registry_proxy
548 if not hasattr (self, 'sliceapi_proxy'):
549 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
550 if hasattr(self.command_options,'component') and self.command_options.component:
551 # resolve the hrn at the registry
552 node_hrn = self.command_options.component
553 records = self.registry().Resolve(node_hrn, self.my_credential_string)
554 records = filter_records('node', records)
556 self.logger.warning("No such component:%r"% opts.component)
558 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
559 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
561 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
562 if not self.sm_url.startswith('http://') or self.sm_url.startswith('https://'):
563 self.sm_url = 'http://' + self.sm_url
564 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
565 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
566 timeout=self.options.timeout, verbose=self.options.debug)
567 return self.sliceapi_proxy
569 def get_cached_server_version(self, server):
570 # check local cache first
573 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
574 cache_key = server.url + "-version"
576 cache = Cache(cache_file)
579 self.logger.info("Local cache not found at: %s" % cache_file)
582 version = cache.get(cache_key)
585 result = server.GetVersion()
586 version= ReturnValue.get_value(result)
587 # cache version for 20 minutes
588 cache.add(cache_key, version, ttl= 60*20)
589 self.logger.info("Updating cache file %s" % cache_file)
590 cache.save_to_file(cache_file)
594 ### resurrect this temporarily so we can support V1 aggregates for a while
595 def server_supports_options_arg(self, server):
597 Returns true if server support the optional call_id arg, false otherwise.
599 server_version = self.get_cached_server_version(server)
601 # xxx need to rewrite this
602 if int(server_version.get('geni_api')) >= 2:
606 def server_supports_call_id_arg(self, server):
607 server_version = self.get_cached_server_version(server)
609 if 'sfa' in server_version and 'code_tag' in server_version:
610 code_tag = server_version['code_tag']
611 code_tag_parts = code_tag.split("-")
612 version_parts = code_tag_parts[0].split(".")
613 major, minor = version_parts[0], version_parts[1]
614 rev = code_tag_parts[1]
615 if int(major) == 1 and minor == 0 and build >= 22:
619 ### ois = options if supported
620 # to be used in something like serverproxy.Method (arg1, arg2, *self.ois(api_options))
621 def ois (self, server, option_dict):
622 if self.server_supports_options_arg (server):
624 elif self.server_supports_call_id_arg (server):
625 return [ unique_call_id () ]
629 ### cis = call_id if supported - like ois
630 def cis (self, server):
631 if self.server_supports_call_id_arg (server):
632 return [ unique_call_id ]
636 ######################################## miscell utilities
637 def get_rspec_file(self, rspec):
638 if (os.path.isabs(rspec)):
641 file = os.path.join(self.options.sfi_dir, rspec)
642 if (os.path.isfile(file)):
645 self.logger.critical("No such rspec file %s"%rspec)
648 def get_record_file(self, record):
649 if (os.path.isabs(record)):
652 file = os.path.join(self.options.sfi_dir, record)
653 if (os.path.isfile(file)):
656 self.logger.critical("No such registry record file %s"%record)
660 #==========================================================================
661 # Following functions implement the commands
663 # Registry-related commands
664 #==========================================================================
666 def version(self, options, args):
668 display an SFA server version (GetVersion)
669 or version information about sfi itself
671 if options.version_local:
672 version=version_core()
674 if options.version_registry:
675 server=self.registry()
677 server = self.sliceapi()
678 result = server.GetVersion()
679 version = ReturnValue.get_value(result)
681 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
683 pprinter = PrettyPrinter(indent=4)
684 pprinter.pprint(version)
686 def list(self, options, args):
688 list entries in named authority registry (List)
695 list = self.registry().List(hrn, self.my_credential_string)
697 raise Exception, "Not enough parameters for the 'list' command"
699 # filter on person, slice, site, node, etc.
700 # THis really should be in the self.filter_records funct def comment...
701 list = filter_records(options.type, list)
703 print "%s (%s)" % (record['hrn'], record['type'])
705 save_records_to_file(options.file, list, options.fileformat)
708 def show(self, options, args):
710 show details about named registry record (Resolve)
716 record_dicts = self.registry().Resolve(hrn, self.my_credential_string)
717 record_dicts = filter_records(options.type, record_dicts)
719 self.logger.error("No record of type %s"% options.type)
720 records = [ make_record (dict=record_dict) for record_dict in record_dicts ]
721 for record in records:
722 if (options.format == "text"): record.dump()
723 else: print record.save_as_xml()
725 save_records_to_file(options.file, record_dicts, options.fileformat)
728 def add(self, options, args):
729 "add record into registry from xml file (Register)"
730 auth_cred = self.my_authority_credential_string()
734 record_filepath = args[0]
735 rec_file = self.get_record_file(record_filepath)
736 record = load_record_from_file(rec_file).todict()
737 return self.registry().Register(record, auth_cred)
739 def update(self, options, args):
740 "update record into registry from xml file (Update)"
744 rec_file = self.get_record_file(args[0])
745 record = load_record_from_file(rec_file)
746 if record.type == "user":
747 if record.hrn == self.user:
748 cred = self.my_credential_string
750 cred = self.my_authority_credential_string()
751 elif record.type in ["slice"]:
753 cred = self.slice_credential_string(record.hrn)
754 except ServerException, e:
755 # XXX smbaker -- once we have better error return codes, update this
756 # to do something better than a string compare
757 if "Permission error" in e.args[0]:
758 cred = self.my_authority_credential_string()
761 elif record.type in ["authority"]:
762 cred = self.my_authority_credential_string()
763 elif record.type == 'node':
764 cred = self.my_authority_credential_string()
766 raise "unknown record type" + record.type
767 record_dict = record.todict()
768 return self.registry().Update(record_dict, cred)
770 def remove(self, options, args):
771 "remove registry record by name (Remove)"
772 auth_cred = self.my_authority_credential_string()
780 return self.registry().Remove(hrn, auth_cred, type)
782 # ==================================================================
783 # Slice-related commands
784 # ==================================================================
786 def slices(self, options, args):
787 "list instantiated slices (ListSlices) - returns urn's"
788 server = self.sliceapi()
790 creds = [self.my_credential_string]
792 delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority))
793 creds.append(delegated_cred)
794 # options and call_id when supported
796 api_options['call_id']=unique_call_id()
797 result = server.ListSlices(creds, *self.ois(server,api_options))
798 value = ReturnValue.get_value(result)
800 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
805 # show rspec for named slice
806 def resources(self, options, args):
808 with no arg, discover available resources, (ListResources)
809 or with an slice hrn, shows currently provisioned resources
811 server = self.sliceapi()
816 creds.append(self.slice_credential_string(args[0]))
818 creds.append(self.my_credential_string)
820 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
822 # no need to check if server accepts the options argument since the options has
823 # been a required argument since v1 API
825 # always send call_id to v2 servers
826 api_options ['call_id'] = unique_call_id()
827 # ask for cached value if available
828 api_options ['cached'] = True
831 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
833 api_options['info'] = options.info
835 if options.current == True:
836 api_options['cached'] = False
838 api_options['cached'] = True
839 if options.rspec_version:
840 version_manager = VersionManager()
841 server_version = self.get_cached_server_version(server)
842 if 'sfa' in server_version:
843 # just request the version the client wants
844 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
846 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
848 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
849 result = server.ListResources (creds, api_options)
850 value = ReturnValue.get_value(result)
852 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
853 if options.file is not None:
854 save_rspec_to_file(value, options.file)
855 if (self.options.raw is None) and (options.file is None):
856 display_rspec(value, options.format)
860 def create(self, options, args):
862 create or update named slice with given rspec
864 server = self.sliceapi()
866 # xxx do we need to check usage (len(args)) ?
869 slice_urn = hrn_to_urn(slice_hrn, 'slice')
872 creds = [self.slice_credential_string(slice_hrn)]
873 delegated_cred = None
874 server_version = self.get_cached_server_version(server)
875 if server_version.get('interface') == 'slicemgr':
876 # delegate our cred to the slice manager
877 # do not delegate cred to slicemgr...not working at the moment
879 #if server_version.get('hrn'):
880 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
881 #elif server_version.get('urn'):
882 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
885 rspec_file = self.get_rspec_file(args[1])
886 rspec = open(rspec_file).read()
889 # need to pass along user keys to the aggregate.
891 # { urn: urn:publicid:IDN+emulab.net+user+alice
892 # keys: [<ssh key A>, <ssh key B>]
895 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
896 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
897 slice_record = slice_records[0]
898 user_hrns = slice_record['researcher']
899 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
900 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
902 if 'sfa' not in server_version:
903 users = pg_users_arg(user_records)
905 rspec.filter({'component_manager_id': server_version['urn']})
906 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
908 print >>sys.stderr, "\r\n \r\n \r\n WOOOOOO"
909 users = sfa_users_arg(user_records, slice_record)
911 # do not append users, keys, or slice tags. Anything
912 # not contained in this request will be removed from the slice
914 # CreateSliver has supported the options argument for a while now so it should
915 # be safe to assume this server support it
917 api_options ['append'] = False
918 api_options ['call_id'] = unique_call_id()
920 result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server, api_options))
921 value = ReturnValue.get_value(result)
923 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
924 if options.file is not None:
925 save_rspec_to_file (value, options.file)
926 if (self.options.raw is None) and (options.file is None):
931 def delete(self, options, args):
933 delete named slice (DeleteSliver)
935 server = self.sliceapi()
939 slice_urn = hrn_to_urn(slice_hrn, 'slice')
942 slice_cred = self.slice_credential_string(slice_hrn)
945 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
946 creds.append(delegated_cred)
948 # options and call_id when supported
950 api_options ['call_id'] = unique_call_id()
951 result = server.DeleteSliver(slice_urn, creds, *self.ois(server, api_options ) )
952 value = ReturnValue.get_value(result)
954 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
959 def status(self, options, args):
961 retrieve slice status (SliverStatus)
963 server = self.sliceapi()
967 slice_urn = hrn_to_urn(slice_hrn, 'slice')
970 slice_cred = self.slice_credential_string(slice_hrn)
973 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
974 creds.append(delegated_cred)
976 # options and call_id when supported
978 api_options['call_id']=unique_call_id()
979 result = server.SliverStatus(slice_urn, creds, *self.ois(server,api_options))
980 value = ReturnValue.get_value(result)
982 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
986 def start(self, options, args):
988 start named slice (Start)
990 server = self.sliceapi()
994 slice_urn = hrn_to_urn(slice_hrn, 'slice')
997 slice_cred = self.slice_credential_string(args[0])
1000 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1001 creds.append(delegated_cred)
1002 # xxx Thierry - does this not need an api_options as well ?
1003 result = server.Start(slice_urn, creds)
1004 value = ReturnValue.get_value(result)
1005 if self.options.raw:
1006 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1011 def stop(self, options, args):
1013 stop named slice (Stop)
1015 server = self.sliceapi()
1018 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1020 slice_cred = self.slice_credential_string(args[0])
1021 creds = [slice_cred]
1022 if options.delegate:
1023 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1024 creds.append(delegated_cred)
1025 result = server.Stop(slice_urn, creds)
1026 value = ReturnValue.get_value(result)
1027 if self.options.raw:
1028 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1034 def reset(self, options, args):
1036 reset named slice (reset_slice)
1038 server = self.sliceapi()
1041 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1043 slice_cred = self.slice_credential_string(args[0])
1044 creds = [slice_cred]
1045 if options.delegate:
1046 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1047 creds.append(delegated_cred)
1048 result = server.reset_slice(creds, slice_urn)
1049 value = ReturnValue.get_value(result)
1050 if self.options.raw:
1051 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1056 def renew(self, options, args):
1058 renew slice (RenewSliver)
1060 server = self.sliceapi()
1063 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1065 slice_cred = self.slice_credential_string(args[0])
1066 creds = [slice_cred]
1067 if options.delegate:
1068 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1069 creds.append(delegated_cred)
1072 # options and call_id when supported
1074 api_options['call_id']=unique_call_id()
1075 result = server.RenewSliver(slice_urn, creds, time, *self.ois(server,api_options))
1076 value = ReturnValue.get_value(result)
1077 if self.options.raw:
1078 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1084 def shutdown(self, options, args):
1086 shutdown named slice (Shutdown)
1088 server = self.sliceapi()
1091 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1093 slice_cred = self.slice_credential_string(slice_hrn)
1094 creds = [slice_cred]
1095 if options.delegate:
1096 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1097 creds.append(delegated_cred)
1098 result = server.Shutdown(slice_urn, creds)
1099 value = ReturnValue.get_value(result)
1100 if self.options.raw:
1101 save_raw_to_file(result, self.options.raw, self.options.rawformat, self.options.rawbanner)
1107 def get_ticket(self, options, args):
1109 get a ticket for the specified slice
1111 server = self.sliceapi()
1113 slice_hrn, rspec_path = args[0], args[1]
1114 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1116 slice_cred = self.slice_credential_string(slice_hrn)
1117 creds = [slice_cred]
1118 if options.delegate:
1119 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1120 creds.append(delegated_cred)
1122 rspec_file = self.get_rspec_file(rspec_path)
1123 rspec = open(rspec_file).read()
1124 # options and call_id when supported
1126 api_options['call_id']=unique_call_id()
1127 # get ticket at the server
1128 ticket_string = server.GetTicket(slice_urn, creds, rspec, *self.ois(server,api_options))
1130 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1131 self.logger.info("writing ticket to %s"%file)
1132 ticket = SfaTicket(string=ticket_string)
1133 ticket.save_to_file(filename=file, save_parents=True)
1135 def redeem_ticket(self, options, args):
1137 Connects to nodes in a slice and redeems a ticket
1138 (slice hrn is retrieved from the ticket)
1140 ticket_file = args[0]
1142 # get slice hrn from the ticket
1143 # use this to get the right slice credential
1144 ticket = SfaTicket(filename=ticket_file)
1146 ticket_string = ticket.save_to_string(save_parents=True)
1148 slice_hrn = ticket.gidObject.get_hrn()
1149 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1150 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1151 slice_cred = self.slice_credential_string(slice_hrn)
1153 # get a list of node hostnames from the RSpec
1154 tree = etree.parse(StringIO(ticket.rspec))
1155 root = tree.getroot()
1156 hostnames = root.xpath("./network/site/node/hostname/text()")
1158 # create an xmlrpc connection to the component manager at each of these
1159 # components and gall redeem_ticket
1161 for hostname in hostnames:
1163 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1164 cm_url="http://%s:%s/"%(hostname,CM_PORT)
1165 server = SfaServerProxy(cm_url, self.private_key, self.my_gid)
1166 server = self.server_proxy(hostname, CM_PORT, self.private_key,
1167 timeout=self.options.timeout, verbose=self.options.debug)
1168 server.RedeemTicket(ticket_string, slice_cred)
1169 self.logger.info("Success")
1170 except socket.gaierror:
1171 self.logger.error("redeem_ticket failed on %s: Component Manager not accepting requests"%hostname)
1172 except Exception, e:
1173 self.logger.log_exc(e.message)
1176 def create_gid(self, options, args):
1178 Create a GID (CreateGid)
1183 target_hrn = args[0]
1184 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.bootstrap.my_gid_string())
1186 filename = options.file
1188 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1189 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1190 GID(string=gid).save_to_file(filename)
1193 def delegate(self, options, args):
1195 (locally) create delegate credential for use by given hrn
1197 delegee_hrn = args[0]
1198 if options.delegate_user:
1199 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1200 elif options.delegate_slice:
1201 slice_cred = self.slice_credential_string(options.delegate_slice)
1202 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1204 self.logger.warning("Must specify either --user or --slice <hrn>")
1206 delegated_cred = Credential(string=cred)
1207 object_hrn = delegated_cred.get_gid_object().get_hrn()
1208 if options.delegate_user:
1209 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1210 + get_leaf(object_hrn) + ".cred")
1211 elif options.delegate_slice:
1212 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1213 + get_leaf(object_hrn) + ".cred")
1215 delegated_cred.save_to_file(dest_fn, save_parents=True)
1217 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1219 def get_trusted_certs(self, options, args):
1221 return uhe trusted certs at this interface (get_trusted_certs)
1223 trusted_certs = self.registry().get_trusted_certs()
1224 for trusted_cert in trusted_certs:
1225 gid = GID(string=trusted_cert)
1227 cert = Certificate(string=trusted_cert)
1228 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())