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 parser.add_option("-k", "--private-key",
353 action="store", dest="user_private_key", default=None,
354 help="point to the private key file to use if not yet installed in sfi_dir")
355 parser.add_option("-t", "--timeout", dest="timeout", default=None,
356 help="Amout of time to wait before timing out the request")
357 parser.add_option("-?", "--commands",
358 action="store_true", dest="command_help", default=False,
359 help="one page summary on commands & exit")
360 parser.disable_interspersed_args()
365 def print_help (self):
366 self.sfi_parser.print_help()
367 self.cmd_parser.print_help()
370 # Main: parse arguments and dispatch to command
372 def dispatch(self, command, cmd_opts, cmd_args):
373 return getattr(self, command)(cmd_opts, cmd_args)
376 self.sfi_parser = self.create_parser()
377 (options, args) = self.sfi_parser.parse_args()
378 if options.command_help:
379 self.print_command_help(options)
381 self.options = options
383 self.logger.setLevelFromOptVerbose(self.options.verbose)
386 self.logger.critical("No command given. Use -h for help.")
387 self.print_command_help(options)
391 self.cmd_parser = self.create_cmd_parser(command)
392 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
397 self.logger.info("Command=%s" % command)
400 self.dispatch(command, cmd_opts, cmd_args)
402 self.logger.critical ("Unknown command %s"%command)
409 def read_config(self):
410 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
412 config = Config (config_file)
414 self.logger.critical("Failed to read configuration file %s"%config_file)
415 self.logger.info("Make sure to remove the export clauses and to add quotes")
416 if self.options.verbose==0:
417 self.logger.info("Re-run with -v for more details")
419 self.logger.log_exc("Could not read config file %s"%config_file)
424 if (self.options.sm is not None):
425 self.sm_url = self.options.sm
426 elif hasattr(config, "SFI_SM"):
427 self.sm_url = config.SFI_SM
429 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
433 if (self.options.registry is not None):
434 self.reg_url = self.options.registry
435 elif hasattr(config, "SFI_REGISTRY"):
436 self.reg_url = config.SFI_REGISTRY
438 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
443 if (self.options.user is not None):
444 self.user = self.options.user
445 elif hasattr(config, "SFI_USER"):
446 self.user = config.SFI_USER
448 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
452 if (self.options.auth is not None):
453 self.authority = self.options.auth
454 elif hasattr(config, "SFI_AUTH"):
455 self.authority = config.SFI_AUTH
457 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
465 # Establish Connection to SliceMgr and Registry Servers
467 def set_servers(self):
469 # Get key and certificate
470 self.logger.info("Contacting Registry at: %s"%self.reg_url)
471 self.registry = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
472 timeout=self.options.timeout, verbose=self.options.debug)
473 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
474 self.slicemgr = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
475 timeout=self.options.timeout, verbose=self.options.debug)
478 def get_cached_server_version(self, server):
479 # check local cache first
482 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
483 cache_key = server.url + "-version"
485 cache = Cache(cache_file)
488 self.logger.info("Local cache not found at: %s" % cache_file)
491 version = cache.get(cache_key)
494 result = server.GetVersion()
495 version= ReturnValue.get_value(result)
496 # cache version for 24 hours
497 cache.add(cache_key, version, ttl= 60*60*24)
498 self.logger.info("Updating cache file %s" % cache_file)
499 cache.save_to_file(cache_file)
505 # Get various credential and spec files
507 # Establishes limiting conventions
508 # - conflates MAs and SAs
509 # - assumes last token in slice name is unique
511 # Bootstraps credentials
512 # - bootstrap user credential from self-signed certificate
513 # - bootstrap authority credential from user credential
514 # - bootstrap slice credential from user credential
517 # init self-signed cert, user credentials and gid
518 def bootstrap (self):
519 bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir)
520 # xxx todo : add a -k option to specify an external private key to install in workdir
521 if self.options.user_private_key:
522 bootstrap.init_private_key_if_missing (self.options.user_private_key)
524 # trigger legacy compat code if needed
525 if not os.path.isfile(bootstrap.private_key_filename()):
526 self.logger.info ("private key not found, trying legacy name")
528 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%get_leaf(self.user))
529 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
530 bootstrap.init_private_key_if_missing (legacy_private_key)
531 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
533 self.logger.log_exc("Can't find private key ")
537 bootstrap.bootstrap_my_gid()
538 # extract what's needed
539 self.private_key = bootstrap.private_key()
540 self.my_gid = bootstrap.my_gid ()
541 self.my_credential_string = bootstrap.my_credential_string ()
542 self.bootstrap = bootstrap
545 # xxx this too should be handled in bootstrap
546 def get_cached_credential(self, file):
548 Return a cached credential only if it hasn't expired.
550 if (os.path.isfile(file)):
551 credential = Credential(filename=file)
552 # make sure it isnt expired
553 if not credential.get_expiration or \
554 datetime.datetime.today() < credential.get_expiration():
558 def get_auth_cred(self):
559 if not self.authority:
560 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
562 return self.bootstrap.authority_credential_string (self.authority)
564 def get_slice_cred(self, name):
565 return self.bootstrap.slice_credential_string (name)
567 # should be supported by sfaclientbootstrap
568 def delegate_cred(self, object_cred, hrn, type='authority'):
569 # the gid and hrn of the object we are delegating
570 if isinstance(object_cred, str):
571 object_cred = Credential(string=object_cred)
572 object_gid = object_cred.get_gid_object()
573 object_hrn = object_gid.get_hrn()
575 if not object_cred.get_privileges().get_all_delegate():
576 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
579 # the delegating user's gid
580 caller_gidfile = self.my_gid()
582 # the gid of the user who will be delegated to
583 delegee_gid = self.bootstrap.gid(hrn,type)
584 delegee_hrn = delegee_gid.get_hrn()
585 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
586 return dcred.save_to_string(save_parents=True)
588 ######################################## miscell utilities
589 def get_rspec_file(self, rspec):
590 if (os.path.isabs(rspec)):
593 file = os.path.join(self.options.sfi_dir, rspec)
594 if (os.path.isfile(file)):
597 self.logger.critical("No such rspec file %s"%rspec)
600 def get_record_file(self, record):
601 if (os.path.isabs(record)):
604 file = os.path.join(self.options.sfi_dir, record)
605 if (os.path.isfile(file)):
608 self.logger.critical("No such registry record file %s"%record)
612 def get_component_proxy_from_hrn(self, hrn):
613 # direct connection to the nodes component manager interface
614 records = self.registry.Resolve(hrn, self.my_credential_string)
615 records = filter_records('node', records)
617 self.logger.warning("No such component:%r"% opts.component)
620 return self.server_proxy(record['hostname'], CM_PORT, self.private_key, self.my_gid)
622 def server_proxy(self, host, port, keyfile, certfile):
624 Return an instance of an xmlrpc server connection
626 # port is appended onto the domain, before the path. Should look like:
627 # http://domain:port/path
628 host_parts = host.split('/')
629 host_parts[0] = host_parts[0] + ":" + str(port)
630 url = "http://%s" % "/".join(host_parts)
631 return SfaServerProxy(url, keyfile, certfile, timeout=self.options.timeout,
632 verbose=self.options.debug)
634 # xxx opts could be retrieved in self.options
635 def server_proxy_from_opts(self, opts):
637 Return instance of an xmlrpc connection to a slice manager, aggregate
638 or component server depending on the specified opts
640 server = self.slicemgr
641 # direct connection to an aggregate
642 if hasattr(opts, 'aggregate') and opts.aggregate:
643 server = self.server_proxy(opts.aggregate, opts.port, self.private_key, self.my_gid)
644 # direct connection to the nodes component manager interface
645 if hasattr(opts, 'component') and opts.component:
646 server = self.get_component_proxy_from_hrn(opts.component)
649 #==========================================================================
650 # Following functions implement the commands
652 # Registry-related commands
653 #==========================================================================
655 def version(self, opts, args):
657 display an SFA server version (GetVersion)
658 or version information about sfi itself
660 if opts.version_local:
661 version=version_core()
663 if opts.version_registry:
666 server = self.server_proxy_from_opts(opts)
667 result = server.GetVersion()
668 version = ReturnValue.get_value(result)
669 for (k,v) in version.iteritems():
670 print "%-20s: %s"%(k,v)
672 save_variable_to_file(version, opts.file, opts.fileformat)
674 def list(self, opts, args):
676 list entries in named authority registry (List)
683 list = self.registry.List(hrn, self.my_credential_string)
685 raise Exception, "Not enough parameters for the 'list' command"
687 # filter on person, slice, site, node, etc.
688 # THis really should be in the self.filter_records funct def comment...
689 list = filter_records(opts.type, list)
691 print "%s (%s)" % (record['hrn'], record['type'])
693 save_records_to_file(opts.file, list, opts.fileformat)
696 def show(self, opts, args):
698 show details about named registry record (Resolve)
704 records = self.registry.Resolve(hrn, self.my_credential_string)
705 records = filter_records(opts.type, records)
707 self.logger.error("No record of type %s"% opts.type)
708 for record in records:
709 if record['type'] in ['user']:
710 record = UserRecord(dict=record)
711 elif record['type'] in ['slice']:
712 record = SliceRecord(dict=record)
713 elif record['type'] in ['node']:
714 record = NodeRecord(dict=record)
715 elif record['type'].startswith('authority'):
716 record = AuthorityRecord(dict=record)
718 record = SfaRecord(dict=record)
719 if (opts.format == "text"):
722 print record.save_to_string()
724 save_records_to_file(opts.file, records, opts.fileformat)
727 def add(self, opts, args):
728 "add record into registry from xml file (Register)"
729 auth_cred = self.get_auth_cred()
733 record_filepath = args[0]
734 rec_file = self.get_record_file(record_filepath)
735 record = load_record_from_file(rec_file).as_dict()
736 return self.registry.Register(record, auth_cred)
738 def update(self, opts, args):
739 "update record into registry from xml file (Update)"
743 rec_file = self.get_record_file(args[0])
744 record = load_record_from_file(rec_file)
745 if record['type'] == "user":
746 if record.get_name() == self.user:
747 cred = self.my_credential_string
749 cred = self.get_auth_cred()
750 elif record['type'] in ["slice"]:
752 cred = self.get_slice_cred(record.get_name())
753 except ServerException, e:
754 # XXX smbaker -- once we have better error return codes, update this
755 # to do something better than a string compare
756 if "Permission error" in e.args[0]:
757 cred = self.get_auth_cred()
760 elif record.get_type() in ["authority"]:
761 cred = self.get_auth_cred()
762 elif record.get_type() == 'node':
763 cred = self.get_auth_cred()
765 raise "unknown record type" + record.get_type()
766 record = record.as_dict()
767 return self.registry.Update(record, cred)
769 def remove(self, opts, args):
770 "remove registry record by name (Remove)"
771 auth_cred = self.get_auth_cred()
779 return self.registry.Remove(hrn, auth_cred, type)
781 # ==================================================================
782 # Slice-related commands
783 # ==================================================================
785 def slices(self, opts, args):
786 "list instantiated slices (ListSlices) - returns urn's"
787 creds = [self.my_credential_string]
789 delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority))
790 creds.append(delegated_cred)
791 server = self.server_proxy_from_opts(opts)
793 api_options ['call_id'] = unique_call_id()
794 result = server.ListSlices(creds, api_options)
795 value = ReturnValue.get_value(result)
799 # show rspec for named slice
800 def resources(self, opts, args):
802 with no arg, discover available resources,
803 or currently provisioned resources (ListResources)
805 server = self.server_proxy_from_opts(opts)
808 api_options ['call_id'] = unique_call_id()
809 #panos add info api_options
811 api_options['info'] = opts.info
814 cred = self.get_slice_cred(args[0])
816 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
818 cred = self.my_credential_string
822 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
823 creds.append(delegated_cred)
824 if opts.rspec_version:
825 version_manager = VersionManager()
826 server_version = self.get_cached_server_version(server)
827 if 'sfa' in server_version:
828 # just request the version the client wants
829 api_options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
831 # this must be a protogeni aggregate. We should request a v2 ad rspec
832 # regardless of what the client user requested
833 api_options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
835 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
837 result = server.ListResources(creds, api_options)
838 value = ReturnValue.get_value(result)
839 if opts.file is None:
840 display_rspec(value, opts.format)
842 save_rspec_to_file(value, opts.file)
845 def create(self, opts, args):
847 create or update named slice with given rspec
849 server = self.server_proxy_from_opts(opts)
850 server_version = self.get_cached_server_version(server)
852 slice_urn = hrn_to_urn(slice_hrn, 'slice')
853 slice_cred = self.get_slice_cred(slice_hrn)
854 delegated_cred = None
855 if server_version.get('interface') == 'slicemgr':
856 # delegate our cred to the slice manager
857 # do not delegate cred to slicemgr...not working at the moment
859 #if server_version.get('hrn'):
860 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
861 #elif server_version.get('urn'):
862 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
864 rspec_file = self.get_rspec_file(args[1])
865 rspec = open(rspec_file).read()
867 # need to pass along user keys to the aggregate.
869 # { urn: urn:publicid:IDN+emulab.net+user+alice
870 # keys: [<ssh key A>, <ssh key B>]
873 slice_records = self.registry.Resolve(slice_urn, [self.my_credential_string])
874 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
875 slice_record = slice_records[0]
876 user_hrns = slice_record['researcher']
877 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
878 user_records = self.registry.Resolve(user_urns, [self.my_credential_string])
880 if 'sfa' not in server_version:
881 users = pg_users_arg(user_records)
883 rspec.filter({'component_manager_id': server_version['urn']})
884 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
887 users = sfa_users_arg(user_records, slice_record)
890 creds.append(delegated_cred)
891 # do not append users, keys, or slice tags. Anything
892 # not contained in this request will be removed from the slice
894 api_options ['append'] = False
895 api_options ['call_id'] = unique_call_id()
896 result = server.CreateSliver(slice_urn, creds, rspec, users, api_options)
897 value = ReturnValue.get_value(result)
898 if opts.file is None:
901 save_rspec_to_file (value, opts.file)
904 def delete(self, opts, args):
906 delete named slice (DeleteSliver)
909 slice_urn = hrn_to_urn(slice_hrn, 'slice')
910 slice_cred = self.get_slice_cred(slice_hrn)
913 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
914 creds.append(delegated_cred)
915 server = self.server_proxy_from_opts(opts)
917 api_options ['call_id'] = unique_call_id()
918 return server.DeleteSliver(slice_urn, creds, api_options)
920 def status(self, opts, args):
922 retrieve slice status (SliverStatus)
925 slice_urn = hrn_to_urn(slice_hrn, 'slice')
926 slice_cred = self.get_slice_cred(slice_hrn)
929 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
930 creds.append(delegated_cred)
931 server = self.server_proxy_from_opts(opts)
933 api_options ['call_id'] = unique_call_id()
934 result = server.SliverStatus(slice_urn, creds, api_options)
935 value = ReturnValue.get_value(result)
938 save_variable_to_file(value, opts.file, opts.fileformat)
940 def start(self, opts, args):
942 start named slice (Start)
945 slice_urn = hrn_to_urn(slice_hrn, 'slice')
946 slice_cred = self.get_slice_cred(args[0])
949 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
950 creds.append(delegated_cred)
951 server = self.server_proxy_from_opts(opts)
952 return server.Start(slice_urn, creds)
954 def stop(self, opts, args):
956 stop named slice (Stop)
959 slice_urn = hrn_to_urn(slice_hrn, 'slice')
960 slice_cred = self.get_slice_cred(args[0])
963 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
964 creds.append(delegated_cred)
965 server = self.server_proxy_from_opts(opts)
966 return server.Stop(slice_urn, creds)
969 def reset(self, opts, args):
971 reset named slice (reset_slice)
974 slice_urn = hrn_to_urn(slice_hrn, 'slice')
975 server = self.server_proxy_from_opts(opts)
976 slice_cred = self.get_slice_cred(args[0])
979 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
980 creds.append(delegated_cred)
981 return server.reset_slice(creds, slice_urn)
983 def renew(self, opts, args):
985 renew slice (RenewSliver)
988 slice_urn = hrn_to_urn(slice_hrn, 'slice')
989 server = self.server_proxy_from_opts(opts)
990 slice_cred = self.get_slice_cred(args[0])
993 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
994 creds.append(delegated_cred)
997 api_options ['call_id'] = unique_call_id()
998 result = server.RenewSliver(slice_urn, creds, time, api_options)
999 value = ReturnValue.get_value(result)
1003 def shutdown(self, opts, args):
1005 shutdown named slice (Shutdown)
1008 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1009 slice_cred = self.get_slice_cred(slice_hrn)
1010 creds = [slice_cred]
1012 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1013 creds.append(delegated_cred)
1014 server = self.server_proxy_from_opts(opts)
1015 return server.Shutdown(slice_urn, creds)
1018 def get_ticket(self, opts, args):
1020 get a ticket for the specified slice
1022 slice_hrn, rspec_path = args[0], args[1]
1023 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1024 slice_cred = self.get_slice_cred(slice_hrn)
1025 creds = [slice_cred]
1027 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1028 creds.append(delegated_cred)
1029 rspec_file = self.get_rspec_file(rspec_path)
1030 rspec = open(rspec_file).read()
1031 server = self.server_proxy_from_opts(opts)
1032 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1033 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1034 self.logger.info("writing ticket to %s"%file)
1035 ticket = SfaTicket(string=ticket_string)
1036 ticket.save_to_file(filename=file, save_parents=True)
1038 def redeem_ticket(self, opts, args):
1040 Connects to nodes in a slice and redeems a ticket
1041 (slice hrn is retrieved from the ticket)
1043 ticket_file = args[0]
1045 # get slice hrn from the ticket
1046 # use this to get the right slice credential
1047 ticket = SfaTicket(filename=ticket_file)
1049 slice_hrn = ticket.gidObject.get_hrn()
1050 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1051 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1052 slice_cred = self.get_slice_cred(slice_hrn)
1054 # get a list of node hostnames from the RSpec
1055 tree = etree.parse(StringIO(ticket.rspec))
1056 root = tree.getroot()
1057 hostnames = root.xpath("./network/site/node/hostname/text()")
1059 # create an xmlrpc connection to the component manager at each of these
1060 # components and gall redeem_ticket
1062 for hostname in hostnames:
1064 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1065 server = self.server_proxy(hostname, CM_PORT, self.private_key, \
1066 self.my_gid, verbose=self.options.debug)
1067 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1068 self.logger.info("Success")
1069 except socket.gaierror:
1070 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1071 except Exception, e:
1072 self.logger.log_exc(e.message)
1075 def create_gid(self, opts, args):
1077 Create a GID (CreateGid)
1082 target_hrn = args[0]
1083 gid = self.registry.CreateGid(self.my_credential_string, target_hrn, self.bootstrap.my_gid_string())
1085 filename = opts.file
1087 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1088 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1089 GID(string=gid).save_to_file(filename)
1092 def delegate(self, opts, args):
1094 (locally) create delegate credential for use by given hrn
1096 delegee_hrn = args[0]
1097 if opts.delegate_user:
1098 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1099 elif opts.delegate_slice:
1100 slice_cred = self.get_slice_cred(opts.delegate_slice)
1101 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1103 self.logger.warning("Must specify either --user or --slice <hrn>")
1105 delegated_cred = Credential(string=cred)
1106 object_hrn = delegated_cred.get_gid_object().get_hrn()
1107 if opts.delegate_user:
1108 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1109 + get_leaf(object_hrn) + ".cred")
1110 elif opts.delegate_slice:
1111 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1112 + get_leaf(object_hrn) + ".cred")
1114 delegated_cred.save_to_file(dest_fn, save_parents=True)
1116 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1118 def get_trusted_certs(self, opts, args):
1120 return uhe trusted certs at this interface (get_trusted_certs)
1122 trusted_certs = self.registry.get_trusted_certs()
1123 for trusted_cert in trusted_certs:
1124 gid = GID(string=trusted_cert)
1126 cert = Certificate(string=trusted_cert)
1127 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())