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 def my_authority_credential_string(self):
548 if not self.authority:
549 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
551 return self.bootstrap.authority_credential_string (self.authority)
553 def slice_credential_string(self, name):
554 return self.bootstrap.slice_credential_string (name)
556 # xxx should be supported by sfaclientbootstrap as well
557 def delegate_cred(self, object_cred, hrn, type='authority'):
558 # the gid and hrn of the object we are delegating
559 if isinstance(object_cred, str):
560 object_cred = Credential(string=object_cred)
561 object_gid = object_cred.get_gid_object()
562 object_hrn = object_gid.get_hrn()
564 if not object_cred.get_privileges().get_all_delegate():
565 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
568 # the delegating user's gid
569 caller_gidfile = self.my_gid()
571 # the gid of the user who will be delegated to
572 delegee_gid = self.bootstrap.gid(hrn,type)
573 delegee_hrn = delegee_gid.get_hrn()
574 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
575 return dcred.save_to_string(save_parents=True)
577 ######################################## miscell utilities
578 def get_rspec_file(self, rspec):
579 if (os.path.isabs(rspec)):
582 file = os.path.join(self.options.sfi_dir, rspec)
583 if (os.path.isfile(file)):
586 self.logger.critical("No such rspec file %s"%rspec)
589 def get_record_file(self, record):
590 if (os.path.isabs(record)):
593 file = os.path.join(self.options.sfi_dir, record)
594 if (os.path.isfile(file)):
597 self.logger.critical("No such registry record file %s"%record)
600 def get_component_proxy_from_hrn(self, hrn):
601 # direct connection to the nodes component manager interface
602 records = self.registry.Resolve(hrn, self.my_credential_string)
603 records = filter_records('node', records)
605 self.logger.warning("No such component:%r"% hrn)
608 return self.server_proxy(record['hostname'], CM_PORT, self.private_key, self.my_gid)
610 def server_proxy(self, host, port, keyfile, certfile):
612 Return an instance of an xmlrpc server connection
614 # port is appended onto the domain, before the path. Should look like:
615 # http://domain:port/path
616 host_parts = host.split('/')
617 host_parts[0] = host_parts[0] + ":" + str(port)
618 url = "http://%s" % "/".join(host_parts)
619 return SfaServerProxy(url, keyfile, certfile, timeout=self.options.timeout,
620 verbose=self.options.debug)
622 # xxx opts could be retrieved in self.options
623 def server_proxy_from_opts(self, opts):
625 Return instance of an xmlrpc connection to a slice manager, aggregate
626 or component server depending on the specified opts
628 server = self.slicemgr
629 # direct connection to an aggregate
630 if hasattr(opts, 'aggregate') and opts.aggregate:
631 server = self.server_proxy(opts.aggregate, opts.port, self.private_key, self.my_gid)
632 # direct connection to the nodes component manager interface
633 if hasattr(opts, 'component') and opts.component:
634 server = self.get_component_proxy_from_hrn(opts.component)
637 #==========================================================================
638 # Following functions implement the commands
640 # Registry-related commands
641 #==========================================================================
643 def version(self, opts, args):
645 display an SFA server version (GetVersion)
646 or version information about sfi itself
648 if opts.version_local:
649 version=version_core()
651 if opts.version_registry:
654 server = self.server_proxy_from_opts(opts)
655 result = server.GetVersion()
656 version = ReturnValue.get_value(result)
657 for (k,v) in version.iteritems():
658 print "%-20s: %s"%(k,v)
660 save_variable_to_file(version, opts.file, opts.fileformat)
662 def list(self, opts, args):
664 list entries in named authority registry (List)
671 list = self.registry.List(hrn, self.my_credential_string)
673 raise Exception, "Not enough parameters for the 'list' command"
675 # filter on person, slice, site, node, etc.
676 # THis really should be in the self.filter_records funct def comment...
677 list = filter_records(opts.type, list)
679 print "%s (%s)" % (record['hrn'], record['type'])
681 save_records_to_file(opts.file, list, opts.fileformat)
684 def show(self, opts, args):
686 show details about named registry record (Resolve)
692 records = self.registry.Resolve(hrn, self.my_credential_string)
693 records = filter_records(opts.type, records)
695 self.logger.error("No record of type %s"% opts.type)
696 for record in records:
697 if record['type'] in ['user']:
698 record = UserRecord(dict=record)
699 elif record['type'] in ['slice']:
700 record = SliceRecord(dict=record)
701 elif record['type'] in ['node']:
702 record = NodeRecord(dict=record)
703 elif record['type'].startswith('authority'):
704 record = AuthorityRecord(dict=record)
706 record = SfaRecord(dict=record)
707 if (opts.format == "text"):
710 print record.save_to_string()
712 save_records_to_file(opts.file, records, opts.fileformat)
715 def add(self, opts, args):
716 "add record into registry from xml file (Register)"
717 auth_cred = self.my_authority_credential_string()
721 record_filepath = args[0]
722 rec_file = self.get_record_file(record_filepath)
723 record = load_record_from_file(rec_file).as_dict()
724 return self.registry.Register(record, auth_cred)
726 def update(self, opts, args):
727 "update record into registry from xml file (Update)"
731 rec_file = self.get_record_file(args[0])
732 record = load_record_from_file(rec_file)
733 if record['type'] == "user":
734 if record.get_name() == self.user:
735 cred = self.my_credential_string
737 cred = self.my_authority_credential_string()
738 elif record['type'] in ["slice"]:
740 cred = self.slice_credential_string(record.get_name())
741 except ServerException, e:
742 # XXX smbaker -- once we have better error return codes, update this
743 # to do something better than a string compare
744 if "Permission error" in e.args[0]:
745 cred = self.my_authority_credential_string()
748 elif record.get_type() in ["authority"]:
749 cred = self.my_authority_credential_string()
750 elif record.get_type() == 'node':
751 cred = self.my_authority_credential_string()
753 raise "unknown record type" + record.get_type()
754 record = record.as_dict()
755 return self.registry.Update(record, cred)
757 def remove(self, opts, args):
758 "remove registry record by name (Remove)"
759 auth_cred = self.my_authority_credential_string()
767 return self.registry.Remove(hrn, auth_cred, type)
769 # ==================================================================
770 # Slice-related commands
771 # ==================================================================
773 def slices(self, opts, args):
774 "list instantiated slices (ListSlices) - returns urn's"
775 creds = [self.my_credential_string]
777 delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority))
778 creds.append(delegated_cred)
779 server = self.server_proxy_from_opts(opts)
781 api_options ['call_id'] = unique_call_id()
782 result = server.ListSlices(creds, api_options)
783 value = ReturnValue.get_value(result)
787 # show rspec for named slice
788 def resources(self, opts, args):
790 with no arg, discover available resources,
791 or currently provisioned resources (ListResources)
793 server = self.server_proxy_from_opts(opts)
796 api_options ['call_id'] = unique_call_id()
797 #panos add info api_options
799 api_options['info'] = opts.info
802 cred = self.slice_credential_string(args[0])
804 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
806 cred = self.my_credential_string
810 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
811 creds.append(delegated_cred)
812 if opts.rspec_version:
813 version_manager = VersionManager()
814 server_version = self.get_cached_server_version(server)
815 if 'sfa' in server_version:
816 # just request the version the client wants
817 api_options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
819 # this must be a protogeni aggregate. We should request a v2 ad rspec
820 # regardless of what the client user requested
821 api_options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
823 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
825 result = server.ListResources(creds, api_options)
826 value = ReturnValue.get_value(result)
827 if opts.file is None:
828 display_rspec(value, opts.format)
830 save_rspec_to_file(value, opts.file)
833 def create(self, opts, args):
835 create or update named slice with given rspec
837 server = self.server_proxy_from_opts(opts)
838 server_version = self.get_cached_server_version(server)
840 slice_urn = hrn_to_urn(slice_hrn, 'slice')
841 slice_cred = self.slice_credential_string(slice_hrn)
842 delegated_cred = None
843 if server_version.get('interface') == 'slicemgr':
844 # delegate our cred to the slice manager
845 # do not delegate cred to slicemgr...not working at the moment
847 #if server_version.get('hrn'):
848 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
849 #elif server_version.get('urn'):
850 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
852 rspec_file = self.get_rspec_file(args[1])
853 rspec = open(rspec_file).read()
855 # need to pass along user keys to the aggregate.
857 # { urn: urn:publicid:IDN+emulab.net+user+alice
858 # keys: [<ssh key A>, <ssh key B>]
861 slice_records = self.registry.Resolve(slice_urn, [self.my_credential_string])
862 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
863 slice_record = slice_records[0]
864 user_hrns = slice_record['researcher']
865 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
866 user_records = self.registry.Resolve(user_urns, [self.my_credential_string])
868 if 'sfa' not in server_version:
869 users = pg_users_arg(user_records)
871 rspec.filter({'component_manager_id': server_version['urn']})
872 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
875 users = sfa_users_arg(user_records, slice_record)
878 creds.append(delegated_cred)
879 # do not append users, keys, or slice tags. Anything
880 # not contained in this request will be removed from the slice
882 api_options ['append'] = False
883 api_options ['call_id'] = unique_call_id()
884 result = server.CreateSliver(slice_urn, creds, rspec, users, api_options)
885 value = ReturnValue.get_value(result)
886 if opts.file is None:
889 save_rspec_to_file (value, opts.file)
892 def delete(self, opts, args):
894 delete named slice (DeleteSliver)
897 slice_urn = hrn_to_urn(slice_hrn, 'slice')
898 slice_cred = self.slice_credential_string(slice_hrn)
901 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
902 creds.append(delegated_cred)
903 server = self.server_proxy_from_opts(opts)
905 api_options ['call_id'] = unique_call_id()
906 return server.DeleteSliver(slice_urn, creds, api_options)
908 def status(self, opts, args):
910 retrieve slice status (SliverStatus)
913 slice_urn = hrn_to_urn(slice_hrn, 'slice')
914 slice_cred = self.slice_credential_string(slice_hrn)
917 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
918 creds.append(delegated_cred)
919 server = self.server_proxy_from_opts(opts)
921 api_options ['call_id'] = unique_call_id()
922 result = server.SliverStatus(slice_urn, creds, api_options)
923 value = ReturnValue.get_value(result)
926 save_variable_to_file(value, opts.file, opts.fileformat)
928 def start(self, opts, args):
930 start named slice (Start)
933 slice_urn = hrn_to_urn(slice_hrn, 'slice')
934 slice_cred = self.slice_credential_string(args[0])
937 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
938 creds.append(delegated_cred)
939 server = self.server_proxy_from_opts(opts)
940 return server.Start(slice_urn, creds)
942 def stop(self, opts, args):
944 stop named slice (Stop)
947 slice_urn = hrn_to_urn(slice_hrn, 'slice')
948 slice_cred = self.slice_credential_string(args[0])
951 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
952 creds.append(delegated_cred)
953 server = self.server_proxy_from_opts(opts)
954 return server.Stop(slice_urn, creds)
957 def reset(self, opts, args):
959 reset named slice (reset_slice)
962 slice_urn = hrn_to_urn(slice_hrn, 'slice')
963 server = self.server_proxy_from_opts(opts)
964 slice_cred = self.slice_credential_string(args[0])
967 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
968 creds.append(delegated_cred)
969 return server.reset_slice(creds, slice_urn)
971 def renew(self, opts, args):
973 renew slice (RenewSliver)
976 slice_urn = hrn_to_urn(slice_hrn, 'slice')
977 server = self.server_proxy_from_opts(opts)
978 slice_cred = self.slice_credential_string(args[0])
981 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
982 creds.append(delegated_cred)
985 api_options ['call_id'] = unique_call_id()
986 result = server.RenewSliver(slice_urn, creds, time, api_options)
987 value = ReturnValue.get_value(result)
991 def shutdown(self, opts, args):
993 shutdown named slice (Shutdown)
996 slice_urn = hrn_to_urn(slice_hrn, 'slice')
997 slice_cred = self.slice_credential_string(slice_hrn)
1000 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1001 creds.append(delegated_cred)
1002 server = self.server_proxy_from_opts(opts)
1003 return server.Shutdown(slice_urn, creds)
1006 def get_ticket(self, opts, args):
1008 get a ticket for the specified slice
1010 slice_hrn, rspec_path = args[0], args[1]
1011 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1012 slice_cred = self.slice_credential_string(slice_hrn)
1013 creds = [slice_cred]
1015 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1016 creds.append(delegated_cred)
1017 rspec_file = self.get_rspec_file(rspec_path)
1018 rspec = open(rspec_file).read()
1019 server = self.server_proxy_from_opts(opts)
1020 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1021 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1022 self.logger.info("writing ticket to %s"%file)
1023 ticket = SfaTicket(string=ticket_string)
1024 ticket.save_to_file(filename=file, save_parents=True)
1026 def redeem_ticket(self, opts, args):
1028 Connects to nodes in a slice and redeems a ticket
1029 (slice hrn is retrieved from the ticket)
1031 ticket_file = args[0]
1033 # get slice hrn from the ticket
1034 # use this to get the right slice credential
1035 ticket = SfaTicket(filename=ticket_file)
1037 slice_hrn = ticket.gidObject.get_hrn()
1038 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1039 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1040 slice_cred = self.slice_credential_string(slice_hrn)
1042 # get a list of node hostnames from the RSpec
1043 tree = etree.parse(StringIO(ticket.rspec))
1044 root = tree.getroot()
1045 hostnames = root.xpath("./network/site/node/hostname/text()")
1047 # create an xmlrpc connection to the component manager at each of these
1048 # components and gall redeem_ticket
1050 for hostname in hostnames:
1052 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1053 server = self.server_proxy(hostname, CM_PORT, self.private_key, \
1054 self.my_gid, verbose=self.options.debug)
1055 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1056 self.logger.info("Success")
1057 except socket.gaierror:
1058 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1059 except Exception, e:
1060 self.logger.log_exc(e.message)
1063 def create_gid(self, opts, args):
1065 Create a GID (CreateGid)
1070 target_hrn = args[0]
1071 gid = self.registry.CreateGid(self.my_credential_string, target_hrn, self.bootstrap.my_gid_string())
1073 filename = opts.file
1075 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1076 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1077 GID(string=gid).save_to_file(filename)
1080 def delegate(self, opts, args):
1082 (locally) create delegate credential for use by given hrn
1084 delegee_hrn = args[0]
1085 if opts.delegate_user:
1086 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1087 elif opts.delegate_slice:
1088 slice_cred = self.slice_credential_string(opts.delegate_slice)
1089 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1091 self.logger.warning("Must specify either --user or --slice <hrn>")
1093 delegated_cred = Credential(string=cred)
1094 object_hrn = delegated_cred.get_gid_object().get_hrn()
1095 if opts.delegate_user:
1096 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1097 + get_leaf(object_hrn) + ".cred")
1098 elif opts.delegate_slice:
1099 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1100 + get_leaf(object_hrn) + ".cred")
1102 delegated_cred.save_to_file(dest_fn, save_parents=True)
1104 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1106 def get_trusted_certs(self, opts, args):
1108 return uhe trusted certs at this interface (get_trusted_certs)
1110 trusted_certs = self.registry.get_trusted_certs()
1111 for trusted_cert in trusted_certs:
1112 gid = GID(string=trusted_cert)
1114 cert = Certificate(string=trusted_cert)
1115 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())