2 # xxx NOTE this will soon be reviewed to take advantage of sfaclientlib
12 from lxml import etree
13 from StringIO import StringIO
14 from optparse import OptionParser
16 from sfa.trust.certificate import Keypair, Certificate
17 from sfa.trust.gid import GID
18 from sfa.trust.credential import Credential
19 from sfa.trust.sfaticket import SfaTicket
21 from sfa.util.sfalogging import sfi_logger
22 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn
23 from sfa.util.config import Config
24 from sfa.util.version import version_core
25 from sfa.util.cache import Cache
27 from sfa.storage.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
29 from sfa.rspecs.rspec import RSpec
30 from sfa.rspecs.rspec_converter import RSpecConverter
31 from sfa.rspecs.version_manager import VersionManager
33 from sfa.client.sfaclientlib import SfaClientBootstrap
34 from sfa.client.sfaserverproxy import SfaServerProxy, ServerException
35 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
36 from sfa.client.return_value import ReturnValue
40 # utility methods here
42 def display_rspec(rspec, format='rspec'):
44 tree = etree.parse(StringIO(rspec))
46 result = root.xpath("./network/site/node/hostname/text()")
47 elif format in ['ip']:
48 # The IP address is not yet part of the new RSpec
49 # so this doesn't do anything yet.
50 tree = etree.parse(StringIO(rspec))
52 result = root.xpath("./network/site/node/ipv4/text()")
59 def display_list(results):
60 for result in results:
63 def display_records(recordList, dump=False):
64 ''' Print all fields in the record'''
65 for record in recordList:
66 display_record(record, dump)
68 def display_record(record, dump=False):
72 info = record.getdict()
73 print "%s (%s)" % (info['hrn'], info['type'])
77 def filter_records(type, records):
79 for record in records:
80 if (record['type'] == type) or (type == "all"):
81 filtered_records.append(record)
82 return filtered_records
86 def save_variable_to_file(var, filename, format="text"):
87 f = open(filename, "w")
90 elif format == "pickled":
91 f.write(pickle.dumps(var))
93 # this should never happen
94 print "unknown output format", format
97 def save_rspec_to_file(rspec, filename):
98 if not filename.endswith(".rspec"):
99 filename = filename + ".rspec"
100 f = open(filename, 'w')
105 def save_records_to_file(filename, recordList, format="xml"):
108 for record in recordList:
110 save_record_to_file(filename + "." + str(index), record)
112 save_record_to_file(filename, record)
114 elif format == "xmllist":
115 f = open(filename, "w")
116 f.write("<recordlist>\n")
117 for record in recordList:
118 record = SfaRecord(dict=record)
119 f.write('<record hrn="' + record.get_name() + '" type="' + record.get_type() + '" />\n')
120 f.write("</recordlist>\n")
122 elif format == "hrnlist":
123 f = open(filename, "w")
124 for record in recordList:
125 record = SfaRecord(dict=record)
126 f.write(record.get_name() + "\n")
129 # this should never happen
130 print "unknown output format", format
132 def save_record_to_file(filename, record):
133 if record['type'] in ['user']:
134 record = UserRecord(dict=record)
135 elif record['type'] in ['slice']:
136 record = SliceRecord(dict=record)
137 elif record['type'] in ['node']:
138 record = NodeRecord(dict=record)
139 elif record['type'] in ['authority', 'ma', 'sa']:
140 record = AuthorityRecord(dict=record)
142 record = SfaRecord(dict=record)
143 str = record.save_to_string()
144 f=codecs.open(filename, encoding='utf-8',mode="w")
151 def load_record_from_file(filename):
152 f=codecs.open(filename, encoding="utf-8", mode="r")
155 record = SfaRecord(string=str)
160 def unique_call_id(): return uuid.uuid4().urn
164 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user']
167 def default_sfi_dir ():
168 if os.path.isfile("./sfi_config"):
171 return os.path.expanduser("~/.sfi/")
173 # dummy to meet Sfi's expectations for its 'options' field
174 # i.e. s/t we can do setattr on
178 def __init__ (self,options=None):
179 if options is None: options=Sfi.DummyOptions()
180 for opt in Sfi.required_options:
181 if not hasattr(options,opt): setattr(options,opt,None)
182 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
183 self.options = options
185 self.authority = None
186 self.logger = sfi_logger
187 self.logger.enable_console()
188 self.available_names = [ tuple[0] for tuple in Sfi.available ]
189 self.available_dict = dict (Sfi.available)
191 # tuples command-name expected-args in the order in which they should appear in the help
194 ("list", "authority"),
197 ("update", "record"),
200 ("resources", "[slice_hrn]"),
201 ("create", "slice_hrn rspec"),
202 ("delete", "slice_hrn"),
203 ("status", "slice_hrn"),
204 ("start", "slice_hrn"),
205 ("stop", "slice_hrn"),
206 ("reset", "slice_hrn"),
207 ("renew", "slice_hrn time"),
208 ("shutdown", "slice_hrn"),
209 ("get_ticket", "slice_hrn rspec"),
210 ("redeem_ticket", "ticket"),
211 ("delegate", "name"),
212 ("create_gid", "[name]"),
213 ("get_trusted_certs", "cred"),
216 def print_command_help (self, options):
217 verbose=getattr(options,'verbose')
218 format3="%18s %-15s %s"
221 print format3%("command","cmd_args","description")
225 self.create_parser().print_help()
226 for command in self.available_names:
227 args=self.available_dict[command]
228 method=getattr(self,command,None)
230 if method: doc=getattr(method,'__doc__',"")
231 if not doc: doc="*** no doc found ***"
232 doc=doc.strip(" \t\n")
233 doc=doc.replace("\n","\n"+35*' ')
236 print format3%(command,args,doc)
238 self.create_command_parser(command).print_help()
240 def create_command_parser(self, command):
241 if command not in self.available_dict:
242 msg="Invalid command\n"
244 msg += ','.join(self.available_names)
245 self.logger.critical(msg)
248 parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \
249 % (command, self.available_dict[command]))
251 # user specifies remote aggregate/sm/component
252 if command in ("resources", "slices", "create", "delete", "start", "stop",
253 "restart", "shutdown", "get_ticket", "renew", "status"):
254 parser.add_option("-c", "--component", dest="component", default=None,
255 help="component hrn")
256 parser.add_option("-d", "--delegate", dest="delegate", default=None,
258 help="Include a credential delegated to the user's root"+\
259 "authority in set of credentials for this call")
261 # registy filter option
262 if command in ("list", "show", "remove"):
263 parser.add_option("-t", "--type", dest="type", type="choice",
264 help="type filter ([all]|user|slice|authority|node|aggregate)",
265 choices=("all", "user", "slice", "authority", "node", "aggregate"),
268 if command in ("resources"):
269 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
270 help="schema type and version of resulting RSpec")
271 parser.add_option("-f", "--format", dest="format", type="choice",
272 help="display format ([xml]|dns|ip)", default="xml",
273 choices=("xml", "dns", "ip"))
274 #panos: a new option to define the type of information about resources a user is interested in
275 parser.add_option("-i", "--info", dest="info",
276 help="optional component information", default=None)
279 # 'create' does return the new rspec, makes sense to save that too
280 if command in ("resources", "show", "list", "create_gid", 'create'):
281 parser.add_option("-o", "--output", dest="file",
282 help="output XML to file", metavar="FILE", default=None)
284 if command in ("show", "list"):
285 parser.add_option("-f", "--format", dest="format", type="choice",
286 help="display format ([text]|xml)", default="text",
287 choices=("text", "xml"))
289 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
290 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
291 choices=("xml", "xmllist", "hrnlist"))
293 if command in ("status", "version"):
294 parser.add_option("-o", "--output", dest="file",
295 help="output dictionary to file", metavar="FILE", default=None)
296 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
297 help="output file format ([text]|pickled)", default="text",
298 choices=("text","pickled"))
300 if command in ("delegate"):
301 parser.add_option("-u", "--user",
302 action="store_true", dest="delegate_user", default=False,
303 help="delegate user credential")
304 parser.add_option("-s", "--slice", dest="delegate_slice",
305 help="delegate slice credential", metavar="HRN", default=None)
307 if command in ("version"):
308 parser.add_option("-R","--registry-version",
309 action="store_true", dest="version_registry", default=False,
310 help="probe registry version instead of sliceapi")
311 parser.add_option("-l","--local",
312 action="store_true", dest="version_local", default=False,
313 help="display version of the local client")
318 def create_parser(self):
320 # Generate command line parser
321 parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
322 description="Commands: %s"%(" ".join(self.available_names)))
323 parser.add_option("-r", "--registry", dest="registry",
324 help="root registry", metavar="URL", default=None)
325 parser.add_option("-s", "--sliceapi", dest="sm", default=None, metavar="URL",
326 help="slice API - in general a SM URL, but can be used to talk to an aggregate")
327 parser.add_option("-d", "--dir", dest="sfi_dir",
328 help="config & working directory - default is %default",
329 metavar="PATH", default=Sfi.default_sfi_dir())
330 parser.add_option("-u", "--user", dest="user",
331 help="user name", metavar="HRN", default=None)
332 parser.add_option("-a", "--auth", dest="auth",
333 help="authority name", metavar="HRN", default=None)
334 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
335 help="verbose mode - cumulative")
336 parser.add_option("-D", "--debug",
337 action="store_true", dest="debug", default=False,
338 help="Debug (xml-rpc) protocol messages")
339 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
340 help="RPC protocol (xmlrpc or soap)")
341 # would it make sense to use ~/.ssh/id_rsa as a default here ?
342 parser.add_option("-k", "--private-key",
343 action="store", dest="user_private_key", default=None,
344 help="point to the private key file to use if not yet installed in sfi_dir")
345 parser.add_option("-t", "--timeout", dest="timeout", default=None,
346 help="Amout of time to wait before timing out the request")
347 parser.add_option("-?", "--commands",
348 action="store_true", dest="command_help", default=False,
349 help="one page summary on commands & exit")
350 parser.disable_interspersed_args()
355 def print_help (self):
356 self.sfi_parser.print_help()
357 self.command_parser.print_help()
360 # Main: parse arguments and dispatch to command
362 def dispatch(self, command, command_options, command_args):
363 return getattr(self, command)(command_options, command_args)
366 self.sfi_parser = self.create_parser()
367 (options, args) = self.sfi_parser.parse_args()
368 if options.command_help:
369 self.print_command_help(options)
371 self.options = options
373 self.logger.setLevelFromOptVerbose(self.options.verbose)
376 self.logger.critical("No command given. Use -h for help.")
377 self.print_command_help(options)
381 self.command_parser = self.create_command_parser(command)
382 (command_options, command_args) = self.command_parser.parse_args(args[1:])
383 self.command_options = command_options
387 self.logger.info("Command=%s" % command)
390 self.dispatch(command, command_options, command_args)
392 self.logger.critical ("Unknown command %s"%command)
399 def read_config(self):
400 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
402 config = Config (config_file)
404 self.logger.critical("Failed to read configuration file %s"%config_file)
405 self.logger.info("Make sure to remove the export clauses and to add quotes")
406 if self.options.verbose==0:
407 self.logger.info("Re-run with -v for more details")
409 self.logger.log_exc("Could not read config file %s"%config_file)
414 if (self.options.sm is not None):
415 self.sm_url = self.options.sm
416 elif hasattr(config, "SFI_SM"):
417 self.sm_url = config.SFI_SM
419 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
423 if (self.options.registry is not None):
424 self.reg_url = self.options.registry
425 elif hasattr(config, "SFI_REGISTRY"):
426 self.reg_url = config.SFI_REGISTRY
428 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
432 if (self.options.user is not None):
433 self.user = self.options.user
434 elif hasattr(config, "SFI_USER"):
435 self.user = config.SFI_USER
437 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
441 if (self.options.auth is not None):
442 self.authority = self.options.auth
443 elif hasattr(config, "SFI_AUTH"):
444 self.authority = config.SFI_AUTH
446 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
453 # Get various credential and spec files
455 # Establishes limiting conventions
456 # - conflates MAs and SAs
457 # - assumes last token in slice name is unique
459 # Bootstraps credentials
460 # - bootstrap user credential from self-signed certificate
461 # - bootstrap authority credential from user credential
462 # - bootstrap slice credential from user credential
465 # init self-signed cert, user credentials and gid
466 def bootstrap (self):
467 bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir)
468 # if -k is provided, use this to initialize private key
469 if self.options.user_private_key:
470 bootstrap.init_private_key_if_missing (self.options.user_private_key)
472 # trigger legacy compat code if needed
473 # the name has changed from just <leaf>.pkey to <hrn>.pkey
474 if not os.path.isfile(bootstrap.private_key_filename()):
475 self.logger.info ("private key not found, trying legacy name")
477 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%get_leaf(self.user))
478 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
479 bootstrap.init_private_key_if_missing (legacy_private_key)
480 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
482 self.logger.log_exc("Can't find private key ")
486 bootstrap.bootstrap_my_gid()
487 # extract what's needed
488 self.private_key = bootstrap.private_key()
489 self.my_credential_string = bootstrap.my_credential_string ()
490 self.my_gid = bootstrap.my_gid ()
491 self.bootstrap = bootstrap
494 def my_authority_credential_string(self):
495 if not self.authority:
496 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
498 return self.bootstrap.authority_credential_string (self.authority)
500 def slice_credential_string(self, name):
501 return self.bootstrap.slice_credential_string (name)
503 # xxx should be supported by sfaclientbootstrap as well
504 def delegate_cred(self, object_cred, hrn, type='authority'):
505 # the gid and hrn of the object we are delegating
506 if isinstance(object_cred, str):
507 object_cred = Credential(string=object_cred)
508 object_gid = object_cred.get_gid_object()
509 object_hrn = object_gid.get_hrn()
511 if not object_cred.get_privileges().get_all_delegate():
512 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
515 # the delegating user's gid
516 caller_gidfile = self.my_gid()
518 # the gid of the user who will be delegated to
519 delegee_gid = self.bootstrap.gid(hrn,type)
520 delegee_hrn = delegee_gid.get_hrn()
521 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
522 return dcred.save_to_string(save_parents=True)
525 # Management of the servers
530 if not hasattr (self, 'registry_proxy'):
531 self.logger.info("Contacting Registry at: %s"%self.reg_url)
532 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
533 timeout=self.options.timeout, verbose=self.options.debug)
534 return self.registry_proxy
538 if not hasattr (self, 'sliceapi_proxy'):
539 # if the command exposes the --component option, figure it's hostname and connect at CM_PORT
540 if hasattr(self.command_options,'component') and self.command_options.component:
541 # resolve the hrn at the registry
542 node_hrn = self.command_options.component
543 records = self.registry().Resolve(node_hrn, self.my_credential_string)
544 records = filter_records('node', records)
546 self.logger.warning("No such component:%r"% opts.component)
548 cm_url = "http://%s:%d/"%(record['hostname'],CM_PORT)
549 self.sliceapi_proxy=SfaServerProxy(cm_url, self.private_key, self.my_gid)
551 # otherwise use what was provided as --sliceapi, or SFI_SM in the config
552 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
553 self.sliceapi_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
554 timeout=self.options.timeout, verbose=self.options.debug)
555 return self.sliceapi_proxy
557 def get_cached_server_version(self, server):
558 # check local cache first
561 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
562 cache_key = server.url + "-version"
564 cache = Cache(cache_file)
567 self.logger.info("Local cache not found at: %s" % cache_file)
570 version = cache.get(cache_key)
573 result = server.GetVersion()
574 version= ReturnValue.get_value(result)
575 # cache version for 20 minutes
576 cache.add(cache_key, version, ttl= 60*20)
577 self.logger.info("Updating cache file %s" % cache_file)
578 cache.save_to_file(cache_file)
582 ### resurrect this temporarily so we can support V1 aggregates for a while
583 def server_supports_options_arg(self, server):
585 Returns true if server support the optional call_id arg, false otherwise.
587 server_version = self.get_cached_server_version(server)
589 # xxx need to rewrite this
590 if int(server_version.get('geni_api')) >= 2:
594 def server_supports_call_id_arg(self, server):
595 server_version = self.get_cached_server_version(server)
597 if 'sfa' in server_version and 'code_tag' in server_version:
598 code_tag = server_version['code_tag']
599 code_tag_parts = code_tag.split("-")
600 version_parts = code_tag_parts[0].split(".")
601 major, minor = version_parts[0], version_parts[1]
602 rev = code_tag_parts[1]
603 if int(major) == 1 and minor == 0 and build >= 22:
607 ### ois = options if supported
608 def ois (self, server, option_dict):
609 if self.server_supports_options_arg (server) : return [option_dict]
612 ######################################## miscell utilities
613 def get_rspec_file(self, rspec):
614 if (os.path.isabs(rspec)):
617 file = os.path.join(self.options.sfi_dir, rspec)
618 if (os.path.isfile(file)):
621 self.logger.critical("No such rspec file %s"%rspec)
624 def get_record_file(self, record):
625 if (os.path.isabs(record)):
628 file = os.path.join(self.options.sfi_dir, record)
629 if (os.path.isfile(file)):
632 self.logger.critical("No such registry record file %s"%record)
636 #==========================================================================
637 # Following functions implement the commands
639 # Registry-related commands
640 #==========================================================================
642 def version(self, options, args):
644 display an SFA server version (GetVersion)
645 or version information about sfi itself
647 if options.version_local:
648 version=version_core()
650 if options.version_registry:
651 server=self.registry()
653 server = self.sliceapi()
654 result = server.GetVersion()
655 version = ReturnValue.get_value(result)
656 for (k,v) in version.iteritems():
657 print "%-20s: %s"%(k,v)
659 save_variable_to_file(version, options.file, options.fileformat)
661 def list(self, options, args):
663 list entries in named authority registry (List)
670 list = self.registry().List(hrn, self.my_credential_string)
672 raise Exception, "Not enough parameters for the 'list' command"
674 # filter on person, slice, site, node, etc.
675 # THis really should be in the self.filter_records funct def comment...
676 list = filter_records(options.type, list)
678 print "%s (%s)" % (record['hrn'], record['type'])
680 save_records_to_file(options.file, list, options.fileformat)
683 def show(self, options, args):
685 show details about named registry record (Resolve)
691 records = self.registry().Resolve(hrn, self.my_credential_string)
692 records = filter_records(options.type, records)
694 self.logger.error("No record of type %s"% options.type)
695 for record in records:
696 if record['type'] in ['user']:
697 record = UserRecord(dict=record)
698 elif record['type'] in ['slice']:
699 record = SliceRecord(dict=record)
700 elif record['type'] in ['node']:
701 record = NodeRecord(dict=record)
702 elif record['type'].startswith('authority'):
703 record = AuthorityRecord(dict=record)
705 record = SfaRecord(dict=record)
706 if (options.format == "text"):
709 print record.save_to_string()
711 save_records_to_file(options.file, records, options.fileformat)
714 def add(self, options, args):
715 "add record into registry from xml file (Register)"
716 auth_cred = self.my_authority_credential_string()
720 record_filepath = args[0]
721 rec_file = self.get_record_file(record_filepath)
722 record = load_record_from_file(rec_file).as_dict()
723 return self.registry().Register(record, auth_cred)
725 def update(self, options, args):
726 "update record into registry from xml file (Update)"
730 rec_file = self.get_record_file(args[0])
731 record = load_record_from_file(rec_file)
732 if record['type'] == "user":
733 if record.get_name() == self.user:
734 cred = self.my_credential_string
736 cred = self.my_authority_credential_string()
737 elif record['type'] in ["slice"]:
739 cred = self.slice_credential_string(record.get_name())
740 except ServerException, e:
741 # XXX smbaker -- once we have better error return codes, update this
742 # to do something better than a string compare
743 if "Permission error" in e.args[0]:
744 cred = self.my_authority_credential_string()
747 elif record.get_type() in ["authority"]:
748 cred = self.my_authority_credential_string()
749 elif record.get_type() == 'node':
750 cred = self.my_authority_credential_string()
752 raise "unknown record type" + record.get_type()
753 record = record.as_dict()
754 return self.registry().Update(record, cred)
756 def remove(self, options, args):
757 "remove registry record by name (Remove)"
758 auth_cred = self.my_authority_credential_string()
766 return self.registry().Remove(hrn, auth_cred, type)
768 # ==================================================================
769 # Slice-related commands
770 # ==================================================================
772 def slices(self, options, args):
773 "list instantiated slices (ListSlices) - returns urn's"
774 server = self.sliceapi()
776 creds = [self.my_credential_string]
778 delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority))
779 creds.append(delegated_cred)
780 call_args.append(creds)
781 if self.server_supports_options_arg(server):
783 api_options ['call_id'] = unique_call_id()
784 args.append(api_options)
785 elif self.server_supports_call_id_arg(server):
786 args.append(unique_call_id())
787 result = server.ListSlices(*call_args)
788 value = ReturnValue.get_value(result)
792 # show rspec for named slice
793 def resources(self, options, args):
795 with no arg, discover available resources,
796 or currently provisioned resources (ListResources)
798 server = self.sliceapi()
804 creds.append(self.slice_credential_string(args[0]))
806 creds.append(self.my_credential_string)
808 creds.append(self.delegate_cred(cred, get_authority(self.authority)))
809 call_args.append(creds)
811 # set options and callid
812 print self.server_supports_options_arg(server)
813 if self.server_supports_options_arg(server):
817 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
819 api_options['info'] = options.info
820 if options.rspec_version:
821 version_manager = VersionManager()
822 server_version = self.get_cached_server_version(server)
823 if 'sfa' in server_version:
824 # just request the version the client wants
825 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
827 # this must be a protogeni aggregate. We should request a v2 ad rspec
828 # regardless of what the client user requested
829 api_options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
831 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
832 api_options ['call_id'] = unique_call_id()
833 call_args.append(api_options)
837 call_args.append(hrn)
838 if self.server_supports_call_id_arg(server):
839 call_args.append(unique_call_id)
840 result = server.ListResources(*call_args)
841 value = ReturnValue.get_value(result)
842 if options.file is None:
843 display_rspec(value, options.format)
845 save_rspec_to_file(value, options.file)
848 def create(self, options, args):
850 create or update named slice with given rspec
852 server = self.sliceapi()
856 slice_urn = hrn_to_urn(slice_hrn, 'slice')
857 call_args.append(slice_urn)
859 creds = [self.slice_credential_string(slice_hrn)]
860 call_args.append(creds)
861 delegated_cred = None
862 server_version = self.get_cached_server_version(server)
863 if server_version.get('interface') == 'slicemgr':
864 # delegate our cred to the slice manager
865 # do not delegate cred to slicemgr...not working at the moment
867 #if server_version.get('hrn'):
868 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
869 #elif server_version.get('urn'):
870 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
873 rspec_file = self.get_rspec_file(args[1])
874 rspec = open(rspec_file).read()
876 # need to pass along user keys to the aggregate.
878 # { urn: urn:publicid:IDN+emulab.net+user+alice
879 # keys: [<ssh key A>, <ssh key B>]
882 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
883 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
884 slice_record = slice_records[0]
885 user_hrns = slice_record['researcher']
886 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
887 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
889 if 'sfa' not in server_version:
890 users = pg_users_arg(user_records)
892 rspec.filter({'component_manager_id': server_version['urn']})
893 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
895 users = sfa_users_arg(user_records, slice_record)
897 call_args.append(rspec)
898 call_args.append(users)
899 # do not append users, keys, or slice tags. Anything
900 # not contained in this request will be removed from the slice
902 # CreateSliver has supported the options argument for a while now so it should
903 # be safe to assume this server support it
905 api_options ['append'] = False
906 api_options ['call_id'] = unique_call_id()
907 call_args.append(api_options)
908 result = server.CreateSliver(*call_args)
909 value = ReturnValue.get_value(result)
910 if options.file is None:
913 save_rspec_to_file (value, options.file)
916 def delete(self, options, args):
918 delete named slice (DeleteSliver)
920 server = self.sliceapi()
924 slice_urn = hrn_to_urn(slice_hrn, 'slice')
925 call_args.append(slice_urn)
927 slice_cred = self.slice_credential_string(slice_hrn)
930 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
931 creds.append(delegated_cred)
932 call_args.append(creds)
933 if self.server_supports_options_arg(server):
935 api_options ['call_id'] = unique_call_id()
936 call_args.append(api_options)
937 elif self.server_supports_call_id_arg(server):
938 call_args.append(unique_call_id())
939 return server.DeleteSliver(*call_args)
941 def status(self, options, args):
943 retrieve slice status (SliverStatus)
945 server = self.sliceapi()
949 slice_urn = hrn_to_urn(slice_hrn, 'slice')
950 call_args.append(slice_urn)
952 slice_cred = self.slice_credential_string(slice_hrn)
955 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
956 creds.append(delegated_cred)
957 call_args.append(creds)
958 # set options and call id
959 if self.server_supports_options_arg(server):
961 api_options ['call_id'] = unique_call_id()
962 call_args.append(api_options)
963 elif self.server_supports_call_id_arg(server):
964 call_args.append(unique_call_id())
965 result = server.SliverStatus(*call_args)
966 value = ReturnValue.get_value(result)
969 save_variable_to_file(value, options.file, options.fileformat)
971 def start(self, options, args):
973 start named slice (Start)
975 server = self.sliceapi()
979 slice_urn = hrn_to_urn(slice_hrn, 'slice')
980 call_args.append(slice_urn)
981 slice_cred = self.slice_credential_string(args[0])
984 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
985 creds.append(delegated_cred)
986 # xxx Thierry - does this not need an api_options as well ?
987 return server.Start(slice_urn, creds)
989 def stop(self, options, args):
991 stop named slice (Stop)
994 slice_urn = hrn_to_urn(slice_hrn, 'slice')
995 slice_cred = self.slice_credential_string(args[0])
998 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
999 creds.append(delegated_cred)
1000 server = self.sliceapi()
1001 return server.Stop(slice_urn, creds)
1004 def reset(self, options, args):
1006 reset named slice (reset_slice)
1009 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1010 server = self.sliceapi()
1011 slice_cred = self.slice_credential_string(args[0])
1012 creds = [slice_cred]
1013 if options.delegate:
1014 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1015 creds.append(delegated_cred)
1016 return server.reset_slice(creds, slice_urn)
1018 def renew(self, options, args):
1020 renew slice (RenewSliver)
1022 server = self.sliceapi()
1026 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1027 call_args.append(slice_urn)
1029 slice_cred = self.slice_credential_string(args[0])
1030 creds = [slice_cred]
1031 if options.delegate:
1032 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1033 creds.append(delegated_cred)
1034 call_args.append(creds)
1036 call_args.append(time)
1037 if self.server_supports_options_arg(server):
1039 api_options ['call_id'] = unique_call_id()
1040 call_args.append(api_options)
1041 elif self.server_supports_call_id_arg(server):
1042 call_args.append(unique_call_id())
1043 result = server.RenewSliver(*call_args)
1044 value = ReturnValue.get_value(result)
1048 def shutdown(self, options, args):
1050 shutdown named slice (Shutdown)
1053 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1054 slice_cred = self.slice_credential_string(slice_hrn)
1055 creds = [slice_cred]
1056 if options.delegate:
1057 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1058 creds.append(delegated_cred)
1059 server = self.sliceapi()
1060 return server.Shutdown(slice_urn, creds)
1063 def get_ticket(self, options, args):
1065 get a ticket for the specified slice
1067 slice_hrn, rspec_path = args[0], args[1]
1068 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1069 slice_cred = self.slice_credential_string(slice_hrn)
1070 creds = [slice_cred]
1071 if options.delegate:
1072 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1073 creds.append(delegated_cred)
1074 rspec_file = self.get_rspec_file(rspec_path)
1075 rspec = open(rspec_file).read()
1076 server = self.sliceapi()
1077 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1078 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1079 self.logger.info("writing ticket to %s"%file)
1080 ticket = SfaTicket(string=ticket_string)
1081 ticket.save_to_file(filename=file, save_parents=True)
1083 def redeem_ticket(self, options, args):
1085 Connects to nodes in a slice and redeems a ticket
1086 (slice hrn is retrieved from the ticket)
1088 ticket_file = args[0]
1090 # get slice hrn from the ticket
1091 # use this to get the right slice credential
1092 ticket = SfaTicket(filename=ticket_file)
1094 slice_hrn = ticket.gidObject.get_hrn()
1095 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1096 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1097 slice_cred = self.slice_credential_string(slice_hrn)
1099 # get a list of node hostnames from the RSpec
1100 tree = etree.parse(StringIO(ticket.rspec))
1101 root = tree.getroot()
1102 hostnames = root.xpath("./network/site/node/hostname/text()")
1104 # create an xmlrpc connection to the component manager at each of these
1105 # components and gall redeem_ticket
1107 for hostname in hostnames:
1109 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1110 server = self.server_proxy(hostname, CM_PORT, self.private_key, \
1111 self.my_gid, verbose=self.options.debug)
1112 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1113 self.logger.info("Success")
1114 except socket.gaierror:
1115 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1116 except Exception, e:
1117 self.logger.log_exc(e.message)
1120 def create_gid(self, options, args):
1122 Create a GID (CreateGid)
1127 target_hrn = args[0]
1128 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.bootstrap.my_gid_string())
1130 filename = options.file
1132 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1133 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1134 GID(string=gid).save_to_file(filename)
1137 def delegate(self, options, args):
1139 (locally) create delegate credential for use by given hrn
1141 delegee_hrn = args[0]
1142 if options.delegate_user:
1143 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1144 elif options.delegate_slice:
1145 slice_cred = self.slice_credential_string(options.delegate_slice)
1146 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1148 self.logger.warning("Must specify either --user or --slice <hrn>")
1150 delegated_cred = Credential(string=cred)
1151 object_hrn = delegated_cred.get_gid_object().get_hrn()
1152 if options.delegate_user:
1153 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1154 + get_leaf(object_hrn) + ".cred")
1155 elif options.delegate_slice:
1156 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1157 + get_leaf(object_hrn) + ".cred")
1159 delegated_cred.save_to_file(dest_fn, save_parents=True)
1161 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1163 def get_trusted_certs(self, options, args):
1165 return uhe trusted certs at this interface (get_trusted_certs)
1167 trusted_certs = self.registry().get_trusted_certs()
1168 for trusted_cert in trusted_certs:
1169 gid = GID(string=trusted_cert)
1171 cert = Certificate(string=trusted_cert)
1172 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())