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
41 # utility methods here
43 def display_rspec(rspec, format='rspec'):
45 tree = etree.parse(StringIO(rspec))
47 result = root.xpath("./network/site/node/hostname/text()")
48 elif format in ['ip']:
49 # The IP address is not yet part of the new RSpec
50 # so this doesn't do anything yet.
51 tree = etree.parse(StringIO(rspec))
53 result = root.xpath("./network/site/node/ipv4/text()")
60 def display_list(results):
61 for result in results:
64 def display_records(recordList, dump=False):
65 ''' Print all fields in the record'''
66 for record in recordList:
67 display_record(record, dump)
69 def display_record(record, dump=False):
73 info = record.getdict()
74 print "%s (%s)" % (info['hrn'], info['type'])
78 def filter_records(type, records):
80 for record in records:
81 if (record['type'] == type) or (type == "all"):
82 filtered_records.append(record)
83 return filtered_records
87 def save_variable_to_file(var, filename, format="text"):
88 f = open(filename, "w")
91 elif format == "pickled":
92 f.write(pickle.dumps(var))
94 # this should never happen
95 print "unknown output format", format
98 def save_rspec_to_file(rspec, filename):
99 if not filename.endswith(".rspec"):
100 filename = filename + ".rspec"
101 f = open(filename, 'w')
106 def save_records_to_file(filename, recordList, format="xml"):
109 for record in recordList:
111 save_record_to_file(filename + "." + str(index), record)
113 save_record_to_file(filename, record)
115 elif format == "xmllist":
116 f = open(filename, "w")
117 f.write("<recordlist>\n")
118 for record in recordList:
119 record = SfaRecord(dict=record)
120 f.write('<record hrn="' + record.get_name() + '" type="' + record.get_type() + '" />\n')
121 f.write("</recordlist>\n")
123 elif format == "hrnlist":
124 f = open(filename, "w")
125 for record in recordList:
126 record = SfaRecord(dict=record)
127 f.write(record.get_name() + "\n")
130 # this should never happen
131 print "unknown output format", format
133 def save_record_to_file(filename, record):
134 if record['type'] in ['user']:
135 record = UserRecord(dict=record)
136 elif record['type'] in ['slice']:
137 record = SliceRecord(dict=record)
138 elif record['type'] in ['node']:
139 record = NodeRecord(dict=record)
140 elif record['type'] in ['authority', 'ma', 'sa']:
141 record = AuthorityRecord(dict=record)
143 record = SfaRecord(dict=record)
144 str = record.save_to_string()
145 f=codecs.open(filename, encoding='utf-8',mode="w")
152 def load_record_from_file(filename):
153 f=codecs.open(filename, encoding="utf-8", mode="r")
156 record = SfaRecord(string=str)
161 def unique_call_id(): return uuid.uuid4().urn
165 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user']
168 def default_sfi_dir ():
169 if os.path.isfile("./sfi_config"):
172 return os.path.expanduser("~/.sfi/")
174 # dummy to meet Sfi's expectations for its 'options' field
175 # i.e. s/t we can do setattr on
179 def __init__ (self,options=None):
180 if options is None: options=Sfi.DummyOptions()
181 for opt in Sfi.required_options:
182 if not hasattr(options,opt): setattr(options,opt,None)
183 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
184 self.options = options
188 self.authority = None
189 self.logger = sfi_logger
190 self.logger.enable_console()
191 self.available_names = [ tuple[0] for tuple in Sfi.available ]
192 self.available_dict = dict (Sfi.available)
194 # tuples command-name expected-args in the order in which they should appear in the help
197 ("list", "authority"),
200 ("update", "record"),
203 ("resources", "[slice_hrn]"),
204 ("create", "slice_hrn rspec"),
205 ("delete", "slice_hrn"),
206 ("status", "slice_hrn"),
207 ("start", "slice_hrn"),
208 ("stop", "slice_hrn"),
209 ("reset", "slice_hrn"),
210 ("renew", "slice_hrn time"),
211 ("shutdown", "slice_hrn"),
212 ("get_ticket", "slice_hrn rspec"),
213 ("redeem_ticket", "ticket"),
214 ("delegate", "name"),
215 ("create_gid", "[name]"),
216 ("get_trusted_certs", "cred"),
219 def print_command_help (self, options):
220 verbose=getattr(options,'verbose')
221 format3="%18s %-15s %s"
224 print format3%("command","cmd_args","description")
228 self.create_parser().print_help()
229 for command in self.available_names:
230 args=self.available_dict[command]
231 method=getattr(self,command,None)
233 if method: doc=getattr(method,'__doc__',"")
234 if not doc: doc="*** no doc found ***"
235 doc=doc.strip(" \t\n")
236 doc=doc.replace("\n","\n"+35*' ')
239 print format3%(command,args,doc)
241 self.create_cmd_parser(command).print_help()
243 def create_cmd_parser(self, command):
244 if command not in self.available_dict:
245 msg="Invalid command\n"
247 msg += ','.join(self.available_names)
248 self.logger.critical(msg)
251 parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \
252 % (command, self.available_dict[command]))
254 # user specifies remote aggregate/sm/component
255 if command in ("resources", "slices", "create", "delete", "start", "stop",
256 "restart", "shutdown", "get_ticket", "renew", "status"):
257 parser.add_option("-a", "--aggregate", dest="aggregate",
258 default=None, help="aggregate host")
259 parser.add_option("-p", "--port", dest="port",
260 default=AGGREGATE_PORT, help="aggregate port")
261 parser.add_option("-c", "--component", dest="component", default=None,
262 help="component hrn")
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"),
275 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 parser.add_option("-f", "--format", dest="format", type="choice",
279 help="display format ([xml]|dns|ip)", default="xml",
280 choices=("xml", "dns", "ip"))
281 #panos: a new option to define the type of information about resources a user is interested in
282 parser.add_option("-i", "--info", dest="info",
283 help="optional component information", default=None)
286 # 'create' does return the new rspec, makes sense to save that too
287 if command in ("resources", "show", "list", "create_gid", 'create'):
288 parser.add_option("-o", "--output", dest="file",
289 help="output XML to file", metavar="FILE", default=None)
291 if command in ("show", "list"):
292 parser.add_option("-f", "--format", dest="format", type="choice",
293 help="display format ([text]|xml)", default="text",
294 choices=("text", "xml"))
296 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
297 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
298 choices=("xml", "xmllist", "hrnlist"))
300 if command in ("status", "version"):
301 parser.add_option("-o", "--output", dest="file",
302 help="output dictionary to file", metavar="FILE", default=None)
303 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
304 help="output file format ([text]|pickled)", default="text",
305 choices=("text","pickled"))
307 if command in ("delegate"):
308 parser.add_option("-u", "--user",
309 action="store_true", dest="delegate_user", default=False,
310 help="delegate user credential")
311 parser.add_option("-s", "--slice", dest="delegate_slice",
312 help="delegate slice credential", metavar="HRN", default=None)
314 if command in ("version"):
315 parser.add_option("-a", "--aggregate", dest="aggregate",
316 default=None, help="aggregate host")
317 parser.add_option("-p", "--port", dest="port",
318 default=AGGREGATE_PORT, help="aggregate port")
319 parser.add_option("-R","--registry-version",
320 action="store_true", dest="version_registry", default=False,
321 help="probe registry version instead of slicemgr")
322 parser.add_option("-l","--local",
323 action="store_true", dest="version_local", default=False,
324 help="display version of the local client")
329 def create_parser(self):
331 # Generate command line parser
332 parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
333 description="Commands: %s"%(" ".join(self.available_names)))
334 parser.add_option("-r", "--registry", dest="registry",
335 help="root registry", metavar="URL", default=None)
336 parser.add_option("-s", "--slicemgr", dest="sm",
337 help="slice manager", metavar="URL", default=None)
338 parser.add_option("-d", "--dir", dest="sfi_dir",
339 help="config & working directory - default is %default",
340 metavar="PATH", default=Sfi.default_sfi_dir())
341 parser.add_option("-u", "--user", dest="user",
342 help="user name", metavar="HRN", default=None)
343 parser.add_option("-a", "--auth", dest="auth",
344 help="authority name", metavar="HRN", default=None)
345 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
346 help="verbose mode - cumulative")
347 parser.add_option("-D", "--debug",
348 action="store_true", dest="debug", default=False,
349 help="Debug (xml-rpc) protocol messages")
350 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
351 help="RPC protocol (xmlrpc or soap)")
352 # would it make sense to use ~/.ssh/id_rsa as a default here ?
353 parser.add_option("-k", "--private-key",
354 action="store", dest="user_private_key", default=None,
355 help="point to the private key file to use if not yet installed in sfi_dir")
356 parser.add_option("-t", "--timeout", dest="timeout", default=None,
357 help="Amout of time to wait before timing out the request")
358 parser.add_option("-?", "--commands",
359 action="store_true", dest="command_help", default=False,
360 help="one page summary on commands & exit")
361 parser.disable_interspersed_args()
366 def print_help (self):
367 self.sfi_parser.print_help()
368 self.cmd_parser.print_help()
371 # Main: parse arguments and dispatch to command
373 def dispatch(self, command, cmd_opts, cmd_args):
374 return getattr(self, command)(cmd_opts, cmd_args)
377 self.sfi_parser = self.create_parser()
378 (options, args) = self.sfi_parser.parse_args()
379 if options.command_help:
380 self.print_command_help(options)
382 self.options = options
384 self.logger.setLevelFromOptVerbose(self.options.verbose)
387 self.logger.critical("No command given. Use -h for help.")
388 self.print_command_help(options)
392 self.cmd_parser = self.create_cmd_parser(command)
393 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
398 self.logger.info("Command=%s" % command)
401 self.dispatch(command, cmd_opts, cmd_args)
403 self.logger.critical ("Unknown command %s"%command)
410 def read_config(self):
411 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
413 config = Config (config_file)
415 self.logger.critical("Failed to read configuration file %s"%config_file)
416 self.logger.info("Make sure to remove the export clauses and to add quotes")
417 if self.options.verbose==0:
418 self.logger.info("Re-run with -v for more details")
420 self.logger.log_exc("Could not read config file %s"%config_file)
425 if (self.options.sm is not None):
426 self.sm_url = self.options.sm
427 elif hasattr(config, "SFI_SM"):
428 self.sm_url = config.SFI_SM
430 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
434 if (self.options.registry is not None):
435 self.reg_url = self.options.registry
436 elif hasattr(config, "SFI_REGISTRY"):
437 self.reg_url = config.SFI_REGISTRY
439 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
444 if (self.options.user is not None):
445 self.user = self.options.user
446 elif hasattr(config, "SFI_USER"):
447 self.user = config.SFI_USER
449 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
453 if (self.options.auth is not None):
454 self.authority = self.options.auth
455 elif hasattr(config, "SFI_AUTH"):
456 self.authority = config.SFI_AUTH
458 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
466 # Establish Connection to SliceMgr and Registry Servers
468 def set_servers(self):
470 # Get key and certificate
471 self.logger.info("Contacting Registry at: %s"%self.reg_url)
472 self.registry = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
473 timeout=self.options.timeout, verbose=self.options.debug)
474 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
475 self.slicemgr = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
476 timeout=self.options.timeout, verbose=self.options.debug)
479 def get_cached_server_version(self, server):
480 # check local cache first
483 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
484 cache_key = server.url + "-version"
486 cache = Cache(cache_file)
489 self.logger.info("Local cache not found at: %s" % cache_file)
492 version = cache.get(cache_key)
495 result = server.GetVersion()
496 version= ReturnValue.get_value(result)
497 # cache version for 24 hours
498 cache.add(cache_key, version, ttl= 60*60*24)
499 self.logger.info("Updating cache file %s" % cache_file)
500 cache.save_to_file(cache_file)
506 # Get various credential and spec files
508 # Establishes limiting conventions
509 # - conflates MAs and SAs
510 # - assumes last token in slice name is unique
512 # Bootstraps credentials
513 # - bootstrap user credential from self-signed certificate
514 # - bootstrap authority credential from user credential
515 # - bootstrap slice credential from user credential
518 # init self-signed cert, user credentials and gid
519 def bootstrap (self):
520 bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir)
521 # if -k is provided, use this to initialize private key
522 if self.options.user_private_key:
523 bootstrap.init_private_key_if_missing (self.options.user_private_key)
525 # trigger legacy compat code if needed
526 # the name has changed from just <leaf>.pkey to <hrn>.pkey
527 if not os.path.isfile(bootstrap.private_key_filename()):
528 self.logger.info ("private key not found, trying legacy name")
530 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%get_leaf(self.user))
531 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
532 bootstrap.init_private_key_if_missing (legacy_private_key)
533 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
535 self.logger.log_exc("Can't find private key ")
539 bootstrap.bootstrap_my_gid()
540 # extract what's needed
541 self.private_key = bootstrap.private_key()
542 self.my_credential_string = bootstrap.my_credential_string ()
543 self.my_gid = bootstrap.my_gid ()
544 self.bootstrap = bootstrap
547 # xxx this too should be handled in bootstrap
548 def get_cached_credential(self, file):
550 Return a cached credential only if it hasn't expired.
552 if (os.path.isfile(file)):
553 credential = Credential(filename=file)
554 # make sure it isnt expired
555 if not credential.get_expiration or \
556 datetime.datetime.today() < credential.get_expiration():
560 def get_auth_cred(self):
561 if not self.authority:
562 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
564 return self.bootstrap.authority_credential_string (self.authority)
566 def get_slice_cred(self, name):
567 return self.bootstrap.slice_credential_string (name)
569 # xxx should be supported by sfaclientbootstrap as well
570 def delegate_cred(self, object_cred, hrn, type='authority'):
571 # the gid and hrn of the object we are delegating
572 if isinstance(object_cred, str):
573 object_cred = Credential(string=object_cred)
574 object_gid = object_cred.get_gid_object()
575 object_hrn = object_gid.get_hrn()
577 if not object_cred.get_privileges().get_all_delegate():
578 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
581 # the delegating user's gid
582 caller_gidfile = self.my_gid()
584 # the gid of the user who will be delegated to
585 delegee_gid = self.bootstrap.gid(hrn,type)
586 delegee_hrn = delegee_gid.get_hrn()
587 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
588 return dcred.save_to_string(save_parents=True)
590 ######################################## miscell utilities
591 def get_rspec_file(self, rspec):
592 if (os.path.isabs(rspec)):
595 file = os.path.join(self.options.sfi_dir, rspec)
596 if (os.path.isfile(file)):
599 self.logger.critical("No such rspec file %s"%rspec)
602 def get_record_file(self, record):
603 if (os.path.isabs(record)):
606 file = os.path.join(self.options.sfi_dir, record)
607 if (os.path.isfile(file)):
610 self.logger.critical("No such registry record file %s"%record)
613 def get_component_proxy_from_hrn(self, hrn):
614 # direct connection to the nodes component manager interface
615 records = self.registry.Resolve(hrn, self.my_credential_string)
616 records = filter_records('node', records)
618 self.logger.warning("No such component:%r"% hrn)
621 return self.server_proxy(record['hostname'], CM_PORT, self.private_key, self.my_gid)
623 def server_proxy(self, host, port, keyfile, certfile):
625 Return an instance of an xmlrpc server connection
627 # port is appended onto the domain, before the path. Should look like:
628 # http://domain:port/path
629 host_parts = host.split('/')
630 host_parts[0] = host_parts[0] + ":" + str(port)
631 url = "http://%s" % "/".join(host_parts)
632 return SfaServerProxy(url, keyfile, certfile, timeout=self.options.timeout,
633 verbose=self.options.debug)
635 # xxx opts could be retrieved in self.options
636 def server_proxy_from_opts(self, opts):
638 Return instance of an xmlrpc connection to a slice manager, aggregate
639 or component server depending on the specified opts
641 server = self.slicemgr
642 # direct connection to an aggregate
643 if hasattr(opts, 'aggregate') and opts.aggregate:
644 server = self.server_proxy(opts.aggregate, opts.port, self.private_key, self.my_gid)
645 # direct connection to the nodes component manager interface
646 if hasattr(opts, 'component') and opts.component:
647 server = self.get_component_proxy_from_hrn(opts.component)
650 #==========================================================================
651 # Following functions implement the commands
653 # Registry-related commands
654 #==========================================================================
656 def version(self, opts, args):
658 display an SFA server version (GetVersion)
659 or version information about sfi itself
661 if opts.version_local:
662 version=version_core()
664 if opts.version_registry:
667 server = self.server_proxy_from_opts(opts)
668 result = server.GetVersion()
669 version = ReturnValue.get_value(result)
670 for (k,v) in version.iteritems():
671 print "%-20s: %s"%(k,v)
673 save_variable_to_file(version, opts.file, opts.fileformat)
675 def list(self, opts, args):
677 list entries in named authority registry (List)
684 list = self.registry.List(hrn, self.my_credential_string)
686 raise Exception, "Not enough parameters for the 'list' command"
688 # filter on person, slice, site, node, etc.
689 # THis really should be in the self.filter_records funct def comment...
690 list = filter_records(opts.type, list)
692 print "%s (%s)" % (record['hrn'], record['type'])
694 save_records_to_file(opts.file, list, opts.fileformat)
697 def show(self, opts, args):
699 show details about named registry record (Resolve)
705 records = self.registry.Resolve(hrn, self.my_credential_string)
706 records = filter_records(opts.type, records)
708 self.logger.error("No record of type %s"% opts.type)
709 for record in records:
710 if record['type'] in ['user']:
711 record = UserRecord(dict=record)
712 elif record['type'] in ['slice']:
713 record = SliceRecord(dict=record)
714 elif record['type'] in ['node']:
715 record = NodeRecord(dict=record)
716 elif record['type'].startswith('authority'):
717 record = AuthorityRecord(dict=record)
719 record = SfaRecord(dict=record)
720 if (opts.format == "text"):
723 print record.save_to_string()
725 save_records_to_file(opts.file, records, opts.fileformat)
728 def add(self, opts, args):
729 "add record into registry from xml file (Register)"
730 auth_cred = self.get_auth_cred()
734 record_filepath = args[0]
735 rec_file = self.get_record_file(record_filepath)
736 record = load_record_from_file(rec_file).as_dict()
737 return self.registry.Register(record, auth_cred)
739 def update(self, opts, 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.get_name() == self.user:
748 cred = self.my_credential_string
750 cred = self.get_auth_cred()
751 elif record['type'] in ["slice"]:
753 cred = self.get_slice_cred(record.get_name())
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.get_auth_cred()
761 elif record.get_type() in ["authority"]:
762 cred = self.get_auth_cred()
763 elif record.get_type() == 'node':
764 cred = self.get_auth_cred()
766 raise "unknown record type" + record.get_type()
767 record = record.as_dict()
768 return self.registry.Update(record, cred)
770 def remove(self, opts, args):
771 "remove registry record by name (Remove)"
772 auth_cred = self.get_auth_cred()
780 return self.registry.Remove(hrn, auth_cred, type)
782 # ==================================================================
783 # Slice-related commands
784 # ==================================================================
786 def slices(self, opts, args):
787 "list instantiated slices (ListSlices) - returns urn's"
788 creds = [self.my_credential_string]
790 delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority))
791 creds.append(delegated_cred)
792 server = self.server_proxy_from_opts(opts)
794 api_options ['call_id'] = unique_call_id()
795 result = server.ListSlices(creds, api_options)
796 value = ReturnValue.get_value(result)
800 # show rspec for named slice
801 def resources(self, opts, args):
803 with no arg, discover available resources,
804 or currently provisioned resources (ListResources)
806 server = self.server_proxy_from_opts(opts)
809 api_options ['call_id'] = unique_call_id()
810 #panos add info api_options
812 api_options['info'] = opts.info
815 cred = self.get_slice_cred(args[0])
817 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
819 cred = self.my_credential_string
823 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
824 creds.append(delegated_cred)
825 if opts.rspec_version:
826 version_manager = VersionManager()
827 server_version = self.get_cached_server_version(server)
828 if 'sfa' in server_version:
829 # just request the version the client wants
830 api_options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
832 # this must be a protogeni aggregate. We should request a v2 ad rspec
833 # regardless of what the client user requested
834 api_options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
836 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
838 result = server.ListResources(creds, api_options)
839 value = ReturnValue.get_value(result)
840 if opts.file is None:
841 display_rspec(value, opts.format)
843 save_rspec_to_file(value, opts.file)
846 def create(self, opts, args):
848 create or update named slice with given rspec
850 server = self.server_proxy_from_opts(opts)
851 server_version = self.get_cached_server_version(server)
853 slice_urn = hrn_to_urn(slice_hrn, 'slice')
854 slice_cred = self.get_slice_cred(slice_hrn)
855 delegated_cred = None
856 if server_version.get('interface') == 'slicemgr':
857 # delegate our cred to the slice manager
858 # do not delegate cred to slicemgr...not working at the moment
860 #if server_version.get('hrn'):
861 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
862 #elif server_version.get('urn'):
863 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
865 rspec_file = self.get_rspec_file(args[1])
866 rspec = open(rspec_file).read()
868 # need to pass along user keys to the aggregate.
870 # { urn: urn:publicid:IDN+emulab.net+user+alice
871 # keys: [<ssh key A>, <ssh key B>]
874 slice_records = self.registry.Resolve(slice_urn, [self.my_credential_string])
875 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
876 slice_record = slice_records[0]
877 user_hrns = slice_record['researcher']
878 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
879 user_records = self.registry.Resolve(user_urns, [self.my_credential_string])
881 if 'sfa' not in server_version:
882 users = pg_users_arg(user_records)
884 rspec.filter({'component_manager_id': server_version['urn']})
885 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
888 users = sfa_users_arg(user_records, slice_record)
891 creds.append(delegated_cred)
892 # do not append users, keys, or slice tags. Anything
893 # not contained in this request will be removed from the slice
895 api_options ['append'] = False
896 api_options ['call_id'] = unique_call_id()
897 result = server.CreateSliver(slice_urn, creds, rspec, users, api_options)
898 value = ReturnValue.get_value(result)
899 if opts.file is None:
902 save_rspec_to_file (value, opts.file)
905 def delete(self, opts, args):
907 delete named slice (DeleteSliver)
910 slice_urn = hrn_to_urn(slice_hrn, 'slice')
911 slice_cred = self.get_slice_cred(slice_hrn)
914 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
915 creds.append(delegated_cred)
916 server = self.server_proxy_from_opts(opts)
918 api_options ['call_id'] = unique_call_id()
919 return server.DeleteSliver(slice_urn, creds, api_options)
921 def status(self, opts, args):
923 retrieve slice status (SliverStatus)
926 slice_urn = hrn_to_urn(slice_hrn, 'slice')
927 slice_cred = self.get_slice_cred(slice_hrn)
930 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
931 creds.append(delegated_cred)
932 server = self.server_proxy_from_opts(opts)
934 api_options ['call_id'] = unique_call_id()
935 result = server.SliverStatus(slice_urn, creds, api_options)
936 value = ReturnValue.get_value(result)
939 save_variable_to_file(value, opts.file, opts.fileformat)
941 def start(self, opts, args):
943 start named slice (Start)
946 slice_urn = hrn_to_urn(slice_hrn, 'slice')
947 slice_cred = self.get_slice_cred(args[0])
950 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
951 creds.append(delegated_cred)
952 server = self.server_proxy_from_opts(opts)
953 return server.Start(slice_urn, creds)
955 def stop(self, opts, args):
957 stop named slice (Stop)
960 slice_urn = hrn_to_urn(slice_hrn, 'slice')
961 slice_cred = self.get_slice_cred(args[0])
964 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
965 creds.append(delegated_cred)
966 server = self.server_proxy_from_opts(opts)
967 return server.Stop(slice_urn, creds)
970 def reset(self, opts, args):
972 reset named slice (reset_slice)
975 slice_urn = hrn_to_urn(slice_hrn, 'slice')
976 server = self.server_proxy_from_opts(opts)
977 slice_cred = self.get_slice_cred(args[0])
980 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
981 creds.append(delegated_cred)
982 return server.reset_slice(creds, slice_urn)
984 def renew(self, opts, args):
986 renew slice (RenewSliver)
989 slice_urn = hrn_to_urn(slice_hrn, 'slice')
990 server = self.server_proxy_from_opts(opts)
991 slice_cred = self.get_slice_cred(args[0])
994 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
995 creds.append(delegated_cred)
998 api_options ['call_id'] = unique_call_id()
999 result = server.RenewSliver(slice_urn, creds, time, api_options)
1000 value = ReturnValue.get_value(result)
1004 def shutdown(self, opts, args):
1006 shutdown named slice (Shutdown)
1009 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1010 slice_cred = self.get_slice_cred(slice_hrn)
1011 creds = [slice_cred]
1013 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1014 creds.append(delegated_cred)
1015 server = self.server_proxy_from_opts(opts)
1016 return server.Shutdown(slice_urn, creds)
1019 def get_ticket(self, opts, args):
1021 get a ticket for the specified slice
1023 slice_hrn, rspec_path = args[0], args[1]
1024 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1025 slice_cred = self.get_slice_cred(slice_hrn)
1026 creds = [slice_cred]
1028 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1029 creds.append(delegated_cred)
1030 rspec_file = self.get_rspec_file(rspec_path)
1031 rspec = open(rspec_file).read()
1032 server = self.server_proxy_from_opts(opts)
1033 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1034 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1035 self.logger.info("writing ticket to %s"%file)
1036 ticket = SfaTicket(string=ticket_string)
1037 ticket.save_to_file(filename=file, save_parents=True)
1039 def redeem_ticket(self, opts, args):
1041 Connects to nodes in a slice and redeems a ticket
1042 (slice hrn is retrieved from the ticket)
1044 ticket_file = args[0]
1046 # get slice hrn from the ticket
1047 # use this to get the right slice credential
1048 ticket = SfaTicket(filename=ticket_file)
1050 slice_hrn = ticket.gidObject.get_hrn()
1051 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1052 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1053 slice_cred = self.get_slice_cred(slice_hrn)
1055 # get a list of node hostnames from the RSpec
1056 tree = etree.parse(StringIO(ticket.rspec))
1057 root = tree.getroot()
1058 hostnames = root.xpath("./network/site/node/hostname/text()")
1060 # create an xmlrpc connection to the component manager at each of these
1061 # components and gall redeem_ticket
1063 for hostname in hostnames:
1065 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1066 server = self.server_proxy(hostname, CM_PORT, self.private_key, \
1067 self.my_gid, verbose=self.options.debug)
1068 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1069 self.logger.info("Success")
1070 except socket.gaierror:
1071 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1072 except Exception, e:
1073 self.logger.log_exc(e.message)
1076 def create_gid(self, opts, args):
1078 Create a GID (CreateGid)
1083 target_hrn = args[0]
1084 gid = self.registry.CreateGid(self.my_credential_string, target_hrn, self.bootstrap.my_gid_string())
1086 filename = opts.file
1088 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1089 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1090 GID(string=gid).save_to_file(filename)
1093 def delegate(self, opts, args):
1095 (locally) create delegate credential for use by given hrn
1097 delegee_hrn = args[0]
1098 if opts.delegate_user:
1099 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1100 elif opts.delegate_slice:
1101 slice_cred = self.get_slice_cred(opts.delegate_slice)
1102 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1104 self.logger.warning("Must specify either --user or --slice <hrn>")
1106 delegated_cred = Credential(string=cred)
1107 object_hrn = delegated_cred.get_gid_object().get_hrn()
1108 if opts.delegate_user:
1109 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1110 + get_leaf(object_hrn) + ".cred")
1111 elif opts.delegate_slice:
1112 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1113 + get_leaf(object_hrn) + ".cred")
1115 delegated_cred.save_to_file(dest_fn, save_parents=True)
1117 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1119 def get_trusted_certs(self, opts, args):
1121 return uhe trusted certs at this interface (get_trusted_certs)
1123 trusted_certs = self.registry.get_trusted_certs()
1124 for trusted_cert in trusted_certs:
1125 gid = GID(string=trusted_cert)
1127 cert = Certificate(string=trusted_cert)
1128 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())