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 24 hours
576 cache.add(cache_key, version, ttl= 60*60*24)
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 'sfa' in server_version and 'code_tag' in server_version:
591 code_tag = server_version['code_tag']
592 code_tag_parts = code_tag.split("-")
594 version_parts = code_tag_parts[0].split(".")
595 major, minor = version_parts[0], version_parts[1]
596 rev = code_tag_parts[1]
602 ### ois = options if supported
603 def ois (self, server, option_dict):
604 if self.server_supports_options_arg (server) : return [option_dict]
607 ######################################## miscell utilities
608 def get_rspec_file(self, rspec):
609 if (os.path.isabs(rspec)):
612 file = os.path.join(self.options.sfi_dir, rspec)
613 if (os.path.isfile(file)):
616 self.logger.critical("No such rspec file %s"%rspec)
619 def get_record_file(self, record):
620 if (os.path.isabs(record)):
623 file = os.path.join(self.options.sfi_dir, record)
624 if (os.path.isfile(file)):
627 self.logger.critical("No such registry record file %s"%record)
631 #==========================================================================
632 # Following functions implement the commands
634 # Registry-related commands
635 #==========================================================================
637 def version(self, options, args):
639 display an SFA server version (GetVersion)
640 or version information about sfi itself
642 if options.version_local:
643 version=version_core()
645 if options.version_registry:
646 server=self.registry()
648 server = self.sliceapi()
649 result = server.GetVersion()
650 version = ReturnValue.get_value(result)
651 for (k,v) in version.iteritems():
652 print "%-20s: %s"%(k,v)
654 save_variable_to_file(version, options.file, options.fileformat)
656 def list(self, options, args):
658 list entries in named authority registry (List)
665 list = self.registry().List(hrn, self.my_credential_string)
667 raise Exception, "Not enough parameters for the 'list' command"
669 # filter on person, slice, site, node, etc.
670 # THis really should be in the self.filter_records funct def comment...
671 list = filter_records(options.type, list)
673 print "%s (%s)" % (record['hrn'], record['type'])
675 save_records_to_file(options.file, list, options.fileformat)
678 def show(self, options, args):
680 show details about named registry record (Resolve)
686 records = self.registry().Resolve(hrn, self.my_credential_string)
687 records = filter_records(options.type, records)
689 self.logger.error("No record of type %s"% options.type)
690 for record in records:
691 if record['type'] in ['user']:
692 record = UserRecord(dict=record)
693 elif record['type'] in ['slice']:
694 record = SliceRecord(dict=record)
695 elif record['type'] in ['node']:
696 record = NodeRecord(dict=record)
697 elif record['type'].startswith('authority'):
698 record = AuthorityRecord(dict=record)
700 record = SfaRecord(dict=record)
701 if (options.format == "text"):
704 print record.save_to_string()
706 save_records_to_file(options.file, records, options.fileformat)
709 def add(self, options, args):
710 "add record into registry from xml file (Register)"
711 auth_cred = self.my_authority_credential_string()
715 record_filepath = args[0]
716 rec_file = self.get_record_file(record_filepath)
717 record = load_record_from_file(rec_file).as_dict()
718 return self.registry().Register(record, auth_cred)
720 def update(self, options, args):
721 "update record into registry from xml file (Update)"
725 rec_file = self.get_record_file(args[0])
726 record = load_record_from_file(rec_file)
727 if record['type'] == "user":
728 if record.get_name() == self.user:
729 cred = self.my_credential_string
731 cred = self.my_authority_credential_string()
732 elif record['type'] in ["slice"]:
734 cred = self.slice_credential_string(record.get_name())
735 except ServerException, e:
736 # XXX smbaker -- once we have better error return codes, update this
737 # to do something better than a string compare
738 if "Permission error" in e.args[0]:
739 cred = self.my_authority_credential_string()
742 elif record.get_type() in ["authority"]:
743 cred = self.my_authority_credential_string()
744 elif record.get_type() == 'node':
745 cred = self.my_authority_credential_string()
747 raise "unknown record type" + record.get_type()
748 record = record.as_dict()
749 return self.registry().Update(record, cred)
751 def remove(self, options, args):
752 "remove registry record by name (Remove)"
753 auth_cred = self.my_authority_credential_string()
761 return self.registry().Remove(hrn, auth_cred, type)
763 # ==================================================================
764 # Slice-related commands
765 # ==================================================================
767 def slices(self, options, args):
768 "list instantiated slices (ListSlices) - returns urn's"
769 creds = [self.my_credential_string]
771 delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority))
772 creds.append(delegated_cred)
774 api_options ['call_id'] = unique_call_id()
775 server = self.sliceapi()
776 result = server.ListSlices(creds, *self.ois(server,api_options))
777 value = ReturnValue.get_value(result)
781 # show rspec for named slice
782 def resources(self, options, args):
784 with no arg, discover available resources,
785 or currently provisioned resources (ListResources)
788 api_options ['call_id'] = unique_call_id()
789 #panos add info api_options
791 api_options['info'] = options.info
794 cred = self.slice_credential_string(args[0])
796 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
798 cred = self.my_credential_string
800 server = self.sliceapi()
803 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
804 creds.append(delegated_cred)
805 if options.rspec_version:
806 version_manager = VersionManager()
807 server_version = self.get_cached_server_version(server)
808 if 'sfa' in server_version:
809 # just request the version the client wants
810 api_options['geni_rspec_version'] = version_manager.get_version(options.rspec_version).to_dict()
812 # this must be a protogeni aggregate. We should request a v2 ad rspec
813 # regardless of what the client user requested
814 api_options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
816 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
818 result = server.ListResources(creds, *self.ois(server,api_options))
819 value = ReturnValue.get_value(result)
820 if options.file is None:
821 display_rspec(value, options.format)
823 save_rspec_to_file(value, options.file)
826 def create(self, options, args):
828 create or update named slice with given rspec
830 server = self.sliceapi()
831 server_version = self.get_cached_server_version(server)
833 slice_urn = hrn_to_urn(slice_hrn, 'slice')
834 slice_cred = self.slice_credential_string(slice_hrn)
835 delegated_cred = None
836 if server_version.get('interface') == 'slicemgr':
837 # delegate our cred to the slice manager
838 # do not delegate cred to slicemgr...not working at the moment
840 #if server_version.get('hrn'):
841 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
842 #elif server_version.get('urn'):
843 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
845 rspec_file = self.get_rspec_file(args[1])
846 rspec = open(rspec_file).read()
848 # need to pass along user keys to the aggregate.
850 # { urn: urn:publicid:IDN+emulab.net+user+alice
851 # keys: [<ssh key A>, <ssh key B>]
854 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
855 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
856 slice_record = slice_records[0]
857 user_hrns = slice_record['researcher']
858 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
859 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
861 if 'sfa' not in server_version:
862 users = pg_users_arg(user_records)
864 rspec.filter({'component_manager_id': server_version['urn']})
865 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
868 users = sfa_users_arg(user_records, slice_record)
871 creds.append(delegated_cred)
872 # do not append users, keys, or slice tags. Anything
873 # not contained in this request will be removed from the slice
875 api_options ['append'] = False
876 api_options ['call_id'] = unique_call_id()
877 result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server,api_options))
878 value = ReturnValue.get_value(result)
879 if options.file is None:
882 save_rspec_to_file (value, options.file)
885 def delete(self, options, args):
887 delete named slice (DeleteSliver)
890 slice_urn = hrn_to_urn(slice_hrn, 'slice')
891 slice_cred = self.slice_credential_string(slice_hrn)
894 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
895 creds.append(delegated_cred)
896 server = self.sliceapi()
898 api_options ['call_id'] = unique_call_id()
899 return server.DeleteSliver(slice_urn, creds, *self.ois(server,api_options))
901 def status(self, options, args):
903 retrieve slice status (SliverStatus)
906 slice_urn = hrn_to_urn(slice_hrn, 'slice')
907 slice_cred = self.slice_credential_string(slice_hrn)
910 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
911 creds.append(delegated_cred)
912 server = self.sliceapi()
914 api_options ['call_id'] = unique_call_id()
915 result = server.SliverStatus(slice_urn, creds, *self.ois(server,api_options))
916 value = ReturnValue.get_value(result)
919 save_variable_to_file(value, options.file, options.fileformat)
921 def start(self, options, args):
923 start named slice (Start)
926 slice_urn = hrn_to_urn(slice_hrn, 'slice')
927 slice_cred = self.slice_credential_string(args[0])
930 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
931 creds.append(delegated_cred)
932 server = self.sliceapi()
933 # xxx Thierry - does this not need an api_options as well ?
934 return server.Start(slice_urn, creds)
936 def stop(self, options, args):
938 stop named slice (Stop)
941 slice_urn = hrn_to_urn(slice_hrn, 'slice')
942 slice_cred = self.slice_credential_string(args[0])
945 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
946 creds.append(delegated_cred)
947 server = self.sliceapi()
948 return server.Stop(slice_urn, creds)
951 def reset(self, options, args):
953 reset named slice (reset_slice)
956 slice_urn = hrn_to_urn(slice_hrn, 'slice')
957 server = self.sliceapi()
958 slice_cred = self.slice_credential_string(args[0])
961 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
962 creds.append(delegated_cred)
963 return server.reset_slice(creds, slice_urn)
965 def renew(self, options, args):
967 renew slice (RenewSliver)
970 slice_urn = hrn_to_urn(slice_hrn, 'slice')
971 server = self.sliceapi()
972 slice_cred = self.slice_credential_string(args[0])
975 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
976 creds.append(delegated_cred)
979 api_options ['call_id'] = unique_call_id()
980 result = server.RenewSliver(slice_urn, creds, time, *self.ois(server,api_options))
981 value = ReturnValue.get_value(result)
985 def shutdown(self, options, args):
987 shutdown named slice (Shutdown)
990 slice_urn = hrn_to_urn(slice_hrn, 'slice')
991 slice_cred = self.slice_credential_string(slice_hrn)
994 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
995 creds.append(delegated_cred)
996 server = self.sliceapi()
997 return server.Shutdown(slice_urn, creds)
1000 def get_ticket(self, options, args):
1002 get a ticket for the specified slice
1004 slice_hrn, rspec_path = args[0], args[1]
1005 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1006 slice_cred = self.slice_credential_string(slice_hrn)
1007 creds = [slice_cred]
1008 if options.delegate:
1009 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1010 creds.append(delegated_cred)
1011 rspec_file = self.get_rspec_file(rspec_path)
1012 rspec = open(rspec_file).read()
1013 server = self.sliceapi()
1014 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1015 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1016 self.logger.info("writing ticket to %s"%file)
1017 ticket = SfaTicket(string=ticket_string)
1018 ticket.save_to_file(filename=file, save_parents=True)
1020 def redeem_ticket(self, options, args):
1022 Connects to nodes in a slice and redeems a ticket
1023 (slice hrn is retrieved from the ticket)
1025 ticket_file = args[0]
1027 # get slice hrn from the ticket
1028 # use this to get the right slice credential
1029 ticket = SfaTicket(filename=ticket_file)
1031 slice_hrn = ticket.gidObject.get_hrn()
1032 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1033 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1034 slice_cred = self.slice_credential_string(slice_hrn)
1036 # get a list of node hostnames from the RSpec
1037 tree = etree.parse(StringIO(ticket.rspec))
1038 root = tree.getroot()
1039 hostnames = root.xpath("./network/site/node/hostname/text()")
1041 # create an xmlrpc connection to the component manager at each of these
1042 # components and gall redeem_ticket
1044 for hostname in hostnames:
1046 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1047 server = self.server_proxy(hostname, CM_PORT, self.private_key, \
1048 self.my_gid, verbose=self.options.debug)
1049 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1050 self.logger.info("Success")
1051 except socket.gaierror:
1052 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1053 except Exception, e:
1054 self.logger.log_exc(e.message)
1057 def create_gid(self, options, args):
1059 Create a GID (CreateGid)
1064 target_hrn = args[0]
1065 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.bootstrap.my_gid_string())
1067 filename = options.file
1069 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1070 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1071 GID(string=gid).save_to_file(filename)
1074 def delegate(self, options, args):
1076 (locally) create delegate credential for use by given hrn
1078 delegee_hrn = args[0]
1079 if options.delegate_user:
1080 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1081 elif options.delegate_slice:
1082 slice_cred = self.slice_credential_string(options.delegate_slice)
1083 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1085 self.logger.warning("Must specify either --user or --slice <hrn>")
1087 delegated_cred = Credential(string=cred)
1088 object_hrn = delegated_cred.get_gid_object().get_hrn()
1089 if options.delegate_user:
1090 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1091 + get_leaf(object_hrn) + ".cred")
1092 elif options.delegate_slice:
1093 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1094 + get_leaf(object_hrn) + ".cred")
1096 delegated_cred.save_to_file(dest_fn, save_parents=True)
1098 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1100 def get_trusted_certs(self, options, args):
1102 return uhe trusted certs at this interface (get_trusted_certs)
1104 trusted_certs = self.registry().get_trusted_certs()
1105 for trusted_cert in trusted_certs:
1106 gid = GID(string=trusted_cert)
1108 cert = Certificate(string=trusted_cert)
1109 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())