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
186 self.authority = None
187 self.logger = sfi_logger
188 self.logger.enable_console()
189 self.available_names = [ tuple[0] for tuple in Sfi.available ]
190 self.available_dict = dict (Sfi.available)
192 # tuples command-name expected-args in the order in which they should appear in the help
195 ("list", "authority"),
198 ("update", "record"),
201 ("resources", "[slice_hrn]"),
202 ("create", "slice_hrn rspec"),
203 ("delete", "slice_hrn"),
204 ("status", "slice_hrn"),
205 ("start", "slice_hrn"),
206 ("stop", "slice_hrn"),
207 ("reset", "slice_hrn"),
208 ("renew", "slice_hrn time"),
209 ("shutdown", "slice_hrn"),
210 ("get_ticket", "slice_hrn rspec"),
211 ("redeem_ticket", "ticket"),
212 ("delegate", "name"),
213 ("create_gid", "[name]"),
214 ("get_trusted_certs", "cred"),
217 def print_command_help (self, options):
218 verbose=getattr(options,'verbose')
219 format3="%18s %-15s %s"
222 print format3%("command","cmd_args","description")
226 self.create_parser().print_help()
227 for command in self.available_names:
228 args=self.available_dict[command]
229 method=getattr(self,command,None)
231 if method: doc=getattr(method,'__doc__',"")
232 if not doc: doc="*** no doc found ***"
233 doc=doc.strip(" \t\n")
234 doc=doc.replace("\n","\n"+35*' ')
237 print format3%(command,args,doc)
239 self.create_cmd_parser(command).print_help()
241 def create_cmd_parser(self, command):
242 if command not in self.available_dict:
243 msg="Invalid command\n"
245 msg += ','.join(self.available_names)
246 self.logger.critical(msg)
249 parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \
250 % (command, self.available_dict[command]))
252 # user specifies remote aggregate/sm/component
253 if command in ("resources", "slices", "create", "delete", "start", "stop",
254 "restart", "shutdown", "get_ticket", "renew", "status"):
255 parser.add_option("-a", "--aggregate", dest="aggregate",
256 default=None, help="aggregate host")
257 parser.add_option("-p", "--port", dest="port",
258 default=AGGREGATE_PORT, help="aggregate port")
259 parser.add_option("-c", "--component", dest="component", default=None,
260 help="component hrn")
261 parser.add_option("-d", "--delegate", dest="delegate", default=None,
263 help="Include a credential delegated to the user's root"+\
264 "authority in set of credentials for this call")
266 # registy filter option
267 if command in ("list", "show", "remove"):
268 parser.add_option("-t", "--type", dest="type", type="choice",
269 help="type filter ([all]|user|slice|authority|node|aggregate)",
270 choices=("all", "user", "slice", "authority", "node", "aggregate"),
273 if command in ("resources"):
274 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
275 help="schema type and version of resulting RSpec")
276 parser.add_option("-f", "--format", dest="format", type="choice",
277 help="display format ([xml]|dns|ip)", default="xml",
278 choices=("xml", "dns", "ip"))
279 #panos: a new option to define the type of information about resources a user is interested in
280 parser.add_option("-i", "--info", dest="info",
281 help="optional component information", default=None)
284 # 'create' does return the new rspec, makes sense to save that too
285 if command in ("resources", "show", "list", "create_gid", 'create'):
286 parser.add_option("-o", "--output", dest="file",
287 help="output XML to file", metavar="FILE", default=None)
289 if command in ("show", "list"):
290 parser.add_option("-f", "--format", dest="format", type="choice",
291 help="display format ([text]|xml)", default="text",
292 choices=("text", "xml"))
294 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
295 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
296 choices=("xml", "xmllist", "hrnlist"))
298 if command in ("status", "version"):
299 parser.add_option("-o", "--output", dest="file",
300 help="output dictionary to file", metavar="FILE", default=None)
301 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
302 help="output file format ([text]|pickled)", default="text",
303 choices=("text","pickled"))
305 if command in ("delegate"):
306 parser.add_option("-u", "--user",
307 action="store_true", dest="delegate_user", default=False,
308 help="delegate user credential")
309 parser.add_option("-s", "--slice", dest="delegate_slice",
310 help="delegate slice credential", metavar="HRN", default=None)
312 if command in ("version"):
313 parser.add_option("-a", "--aggregate", dest="aggregate",
314 default=None, help="aggregate host")
315 parser.add_option("-p", "--port", dest="port",
316 default=AGGREGATE_PORT, help="aggregate port")
317 parser.add_option("-R","--registry-version",
318 action="store_true", dest="version_registry", default=False,
319 help="probe registry version instead of slicemgr")
320 parser.add_option("-l","--local",
321 action="store_true", dest="version_local", default=False,
322 help="display version of the local client")
327 def create_parser(self):
329 # Generate command line parser
330 parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
331 description="Commands: %s"%(" ".join(self.available_names)))
332 parser.add_option("-r", "--registry", dest="registry",
333 help="root registry", metavar="URL", default=None)
334 parser.add_option("-s", "--slicemgr", dest="sm",
335 help="slice manager", metavar="URL", default=None)
336 parser.add_option("-d", "--dir", dest="sfi_dir",
337 help="config & working directory - default is %default",
338 metavar="PATH", default=Sfi.default_sfi_dir())
339 parser.add_option("-u", "--user", dest="user",
340 help="user name", metavar="HRN", default=None)
341 parser.add_option("-a", "--auth", dest="auth",
342 help="authority name", metavar="HRN", default=None)
343 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
344 help="verbose mode - cumulative")
345 parser.add_option("-D", "--debug",
346 action="store_true", dest="debug", default=False,
347 help="Debug (xml-rpc) protocol messages")
348 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
349 help="RPC protocol (xmlrpc or soap)")
350 # would it make sense to use ~/.ssh/id_rsa as a default here ?
351 parser.add_option("-k", "--private-key",
352 action="store", dest="user_private_key", default=None,
353 help="point to the private key file to use if not yet installed in sfi_dir")
354 parser.add_option("-t", "--timeout", dest="timeout", default=None,
355 help="Amout of time to wait before timing out the request")
356 parser.add_option("-?", "--commands",
357 action="store_true", dest="command_help", default=False,
358 help="one page summary on commands & exit")
359 parser.disable_interspersed_args()
364 def print_help (self):
365 self.sfi_parser.print_help()
366 self.cmd_parser.print_help()
369 # Main: parse arguments and dispatch to command
371 def dispatch(self, command, cmd_opts, cmd_args):
372 return getattr(self, command)(cmd_opts, cmd_args)
375 self.sfi_parser = self.create_parser()
376 (options, args) = self.sfi_parser.parse_args()
377 if options.command_help:
378 self.print_command_help(options)
380 self.options = options
382 self.logger.setLevelFromOptVerbose(self.options.verbose)
385 self.logger.critical("No command given. Use -h for help.")
386 self.print_command_help(options)
390 self.cmd_parser = self.create_cmd_parser(command)
391 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
395 self.logger.info("Command=%s" % command)
398 self.dispatch(command, cmd_opts, cmd_args)
400 self.logger.critical ("Unknown command %s"%command)
407 def read_config(self):
408 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
410 config = Config (config_file)
412 self.logger.critical("Failed to read configuration file %s"%config_file)
413 self.logger.info("Make sure to remove the export clauses and to add quotes")
414 if self.options.verbose==0:
415 self.logger.info("Re-run with -v for more details")
417 self.logger.log_exc("Could not read config file %s"%config_file)
422 if (self.options.sm is not None):
423 self.sm_url = self.options.sm
424 elif hasattr(config, "SFI_SM"):
425 self.sm_url = config.SFI_SM
427 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
431 if (self.options.registry is not None):
432 self.reg_url = self.options.registry
433 elif hasattr(config, "SFI_REGISTRY"):
434 self.reg_url = config.SFI_REGISTRY
436 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
441 if (self.options.user is not None):
442 self.user = self.options.user
443 elif hasattr(config, "SFI_USER"):
444 self.user = config.SFI_USER
446 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
450 if (self.options.auth is not None):
451 self.authority = self.options.auth
452 elif hasattr(config, "SFI_AUTH"):
453 self.authority = config.SFI_AUTH
455 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
462 # Get various credential and spec files
464 # Establishes limiting conventions
465 # - conflates MAs and SAs
466 # - assumes last token in slice name is unique
468 # Bootstraps credentials
469 # - bootstrap user credential from self-signed certificate
470 # - bootstrap authority credential from user credential
471 # - bootstrap slice credential from user credential
474 # init self-signed cert, user credentials and gid
475 def bootstrap (self):
476 bootstrap = SfaClientBootstrap (self.user, self.reg_url, self.options.sfi_dir)
477 # if -k is provided, use this to initialize private key
478 if self.options.user_private_key:
479 bootstrap.init_private_key_if_missing (self.options.user_private_key)
481 # trigger legacy compat code if needed
482 # the name has changed from just <leaf>.pkey to <hrn>.pkey
483 if not os.path.isfile(bootstrap.private_key_filename()):
484 self.logger.info ("private key not found, trying legacy name")
486 legacy_private_key = os.path.join (self.options.sfi_dir, "%s.pkey"%get_leaf(self.user))
487 self.logger.debug("legacy_private_key=%s"%legacy_private_key)
488 bootstrap.init_private_key_if_missing (legacy_private_key)
489 self.logger.info("Copied private key from legacy location %s"%legacy_private_key)
491 self.logger.log_exc("Can't find private key ")
495 bootstrap.bootstrap_my_gid()
496 # extract what's needed
497 self.private_key = bootstrap.private_key()
498 self.my_credential_string = bootstrap.my_credential_string ()
499 self.my_gid = bootstrap.my_gid ()
500 self.bootstrap = bootstrap
503 def my_authority_credential_string(self):
504 if not self.authority:
505 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
507 return self.bootstrap.authority_credential_string (self.authority)
509 def slice_credential_string(self, name):
510 return self.bootstrap.slice_credential_string (name)
512 # xxx should be supported by sfaclientbootstrap as well
513 def delegate_cred(self, object_cred, hrn, type='authority'):
514 # the gid and hrn of the object we are delegating
515 if isinstance(object_cred, str):
516 object_cred = Credential(string=object_cred)
517 object_gid = object_cred.get_gid_object()
518 object_hrn = object_gid.get_hrn()
520 if not object_cred.get_privileges().get_all_delegate():
521 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
524 # the delegating user's gid
525 caller_gidfile = self.my_gid()
527 # the gid of the user who will be delegated to
528 delegee_gid = self.bootstrap.gid(hrn,type)
529 delegee_hrn = delegee_gid.get_hrn()
530 dcred = object_cred.delegate(delegee_gid, self.private_key, caller_gidfile)
531 return dcred.save_to_string(save_parents=True)
534 # Management of the servers
538 if not hasattr (self, 'registry_proxy'):
539 self.logger.info("Contacting Registry at: %s"%self.reg_url)
540 self.registry_proxy = SfaServerProxy(self.reg_url, self.private_key, self.my_gid,
541 timeout=self.options.timeout, verbose=self.options.debug)
542 return self.registry_proxy
545 if not hasattr (self, 'slicemgr_proxy'):
546 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
547 self.slicemgr_proxy = SfaServerProxy(self.sm_url, self.private_key, self.my_gid,
548 timeout=self.options.timeout, verbose=self.options.debug)
549 return self.slicemgr_proxy
551 # all this c... messing with hosts and urls and other -a -c -p options sounds just plain wrong
552 # couldn't we just have people select their slice API url with -s no matter what else ?
553 def server_proxy(self, host, port, keyfile, certfile):
555 Return an instance of an xmlrpc server connection
557 # port is appended onto the domain, before the path. Should look like:
558 # http://domain:port/path
559 host_parts = host.split('/')
560 host_parts[0] = host_parts[0] + ":" + str(port)
561 url = "http://%s" % "/".join(host_parts)
562 return SfaServerProxy(url, keyfile, certfile, timeout=self.options.timeout,
563 verbose=self.options.debug)
565 # xxx opts could be retrieved in self.options
566 def server_proxy_from_opts(self, opts):
568 Return instance of an xmlrpc connection to a slice manager, aggregate
569 or component server depending on the specified opts
571 # direct connection to the nodes component manager interface
572 if hasattr(opts, 'component') and opts.component:
573 server = self.component_proxy_from_hrn(opts.component)
574 # direct connection to an aggregate
575 elif hasattr(opts, 'aggregate') and opts.aggregate:
576 server = self.server_proxy(opts.aggregate, opts.port, self.private_key, self.my_gid)
578 server = self.slicemgr()
581 def component_proxy_from_hrn(self, hrn):
582 # direct connection to the nodes component manager interface
583 records = self.registry.Resolve(hrn, self.my_credential_string)
584 records = filter_records('node', records)
586 self.logger.warning("No such component:%r"% hrn)
589 return self.server_proxy(record['hostname'], CM_PORT, self.private_key, self.my_gid)
592 def get_cached_server_version(self, server):
593 # check local cache first
596 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
597 cache_key = server.url + "-version"
599 cache = Cache(cache_file)
602 self.logger.info("Local cache not found at: %s" % cache_file)
605 version = cache.get(cache_key)
608 result = server.GetVersion()
609 version= ReturnValue.get_value(result)
610 # cache version for 24 hours
611 cache.add(cache_key, version, ttl= 60*60*24)
612 self.logger.info("Updating cache file %s" % cache_file)
613 cache.save_to_file(cache_file)
618 ######################################## miscell utilities
619 def get_rspec_file(self, rspec):
620 if (os.path.isabs(rspec)):
623 file = os.path.join(self.options.sfi_dir, rspec)
624 if (os.path.isfile(file)):
627 self.logger.critical("No such rspec file %s"%rspec)
630 def get_record_file(self, record):
631 if (os.path.isabs(record)):
634 file = os.path.join(self.options.sfi_dir, record)
635 if (os.path.isfile(file)):
638 self.logger.critical("No such registry record file %s"%record)
642 #==========================================================================
643 # Following functions implement the commands
645 # Registry-related commands
646 #==========================================================================
648 def version(self, opts, args):
650 display an SFA server version (GetVersion)
651 or version information about sfi itself
653 if opts.version_local:
654 version=version_core()
656 if opts.version_registry:
657 server=self.registry()
659 server = self.server_proxy_from_opts(opts)
660 result = server.GetVersion()
661 version = ReturnValue.get_value(result)
662 for (k,v) in version.iteritems():
663 print "%-20s: %s"%(k,v)
665 save_variable_to_file(version, opts.file, opts.fileformat)
667 def list(self, opts, args):
669 list entries in named authority registry (List)
676 list = self.registry().List(hrn, self.my_credential_string)
678 raise Exception, "Not enough parameters for the 'list' command"
680 # filter on person, slice, site, node, etc.
681 # THis really should be in the self.filter_records funct def comment...
682 list = filter_records(opts.type, list)
684 print "%s (%s)" % (record['hrn'], record['type'])
686 save_records_to_file(opts.file, list, opts.fileformat)
689 def show(self, opts, args):
691 show details about named registry record (Resolve)
697 records = self.registry().Resolve(hrn, self.my_credential_string)
698 records = filter_records(opts.type, records)
700 self.logger.error("No record of type %s"% opts.type)
701 for record in records:
702 if record['type'] in ['user']:
703 record = UserRecord(dict=record)
704 elif record['type'] in ['slice']:
705 record = SliceRecord(dict=record)
706 elif record['type'] in ['node']:
707 record = NodeRecord(dict=record)
708 elif record['type'].startswith('authority'):
709 record = AuthorityRecord(dict=record)
711 record = SfaRecord(dict=record)
712 if (opts.format == "text"):
715 print record.save_to_string()
717 save_records_to_file(opts.file, records, opts.fileformat)
720 def add(self, opts, args):
721 "add record into registry from xml file (Register)"
722 auth_cred = self.my_authority_credential_string()
726 record_filepath = args[0]
727 rec_file = self.get_record_file(record_filepath)
728 record = load_record_from_file(rec_file).as_dict()
729 return self.registry().Register(record, auth_cred)
731 def update(self, opts, args):
732 "update record into registry from xml file (Update)"
736 rec_file = self.get_record_file(args[0])
737 record = load_record_from_file(rec_file)
738 if record['type'] == "user":
739 if record.get_name() == self.user:
740 cred = self.my_credential_string
742 cred = self.my_authority_credential_string()
743 elif record['type'] in ["slice"]:
745 cred = self.slice_credential_string(record.get_name())
746 except ServerException, e:
747 # XXX smbaker -- once we have better error return codes, update this
748 # to do something better than a string compare
749 if "Permission error" in e.args[0]:
750 cred = self.my_authority_credential_string()
753 elif record.get_type() in ["authority"]:
754 cred = self.my_authority_credential_string()
755 elif record.get_type() == 'node':
756 cred = self.my_authority_credential_string()
758 raise "unknown record type" + record.get_type()
759 record = record.as_dict()
760 return self.registry().Update(record, cred)
762 def remove(self, opts, args):
763 "remove registry record by name (Remove)"
764 auth_cred = self.my_authority_credential_string()
772 return self.registry().Remove(hrn, auth_cred, type)
774 # ==================================================================
775 # Slice-related commands
776 # ==================================================================
778 def slices(self, opts, args):
779 "list instantiated slices (ListSlices) - returns urn's"
780 creds = [self.my_credential_string]
782 delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority))
783 creds.append(delegated_cred)
784 server = self.server_proxy_from_opts(opts)
786 api_options ['call_id'] = unique_call_id()
787 result = server.ListSlices(creds, api_options)
788 value = ReturnValue.get_value(result)
792 # show rspec for named slice
793 def resources(self, opts, args):
795 with no arg, discover available resources,
796 or currently provisioned resources (ListResources)
798 server = self.server_proxy_from_opts(opts)
801 api_options ['call_id'] = unique_call_id()
802 #panos add info api_options
804 api_options['info'] = opts.info
807 cred = self.slice_credential_string(args[0])
809 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
811 cred = self.my_credential_string
815 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
816 creds.append(delegated_cred)
817 if opts.rspec_version:
818 version_manager = VersionManager()
819 server_version = self.get_cached_server_version(server)
820 if 'sfa' in server_version:
821 # just request the version the client wants
822 api_options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
824 # this must be a protogeni aggregate. We should request a v2 ad rspec
825 # regardless of what the client user requested
826 api_options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
828 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
830 result = server.ListResources(creds, api_options)
831 value = ReturnValue.get_value(result)
832 if opts.file is None:
833 display_rspec(value, opts.format)
835 save_rspec_to_file(value, opts.file)
838 def create(self, opts, args):
840 create or update named slice with given rspec
842 server = self.server_proxy_from_opts(opts)
843 server_version = self.get_cached_server_version(server)
845 slice_urn = hrn_to_urn(slice_hrn, 'slice')
846 slice_cred = self.slice_credential_string(slice_hrn)
847 delegated_cred = None
848 if server_version.get('interface') == 'slicemgr':
849 # delegate our cred to the slice manager
850 # do not delegate cred to slicemgr...not working at the moment
852 #if server_version.get('hrn'):
853 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
854 #elif server_version.get('urn'):
855 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
857 rspec_file = self.get_rspec_file(args[1])
858 rspec = open(rspec_file).read()
860 # need to pass along user keys to the aggregate.
862 # { urn: urn:publicid:IDN+emulab.net+user+alice
863 # keys: [<ssh key A>, <ssh key B>]
866 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
867 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
868 slice_record = slice_records[0]
869 user_hrns = slice_record['researcher']
870 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
871 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
873 if 'sfa' not in server_version:
874 users = pg_users_arg(user_records)
876 rspec.filter({'component_manager_id': server_version['urn']})
877 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
880 users = sfa_users_arg(user_records, slice_record)
883 creds.append(delegated_cred)
884 # do not append users, keys, or slice tags. Anything
885 # not contained in this request will be removed from the slice
887 api_options ['append'] = False
888 api_options ['call_id'] = unique_call_id()
889 result = server.CreateSliver(slice_urn, creds, rspec, users, api_options)
890 value = ReturnValue.get_value(result)
891 if opts.file is None:
894 save_rspec_to_file (value, opts.file)
897 def delete(self, opts, args):
899 delete named slice (DeleteSliver)
902 slice_urn = hrn_to_urn(slice_hrn, 'slice')
903 slice_cred = self.slice_credential_string(slice_hrn)
906 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
907 creds.append(delegated_cred)
908 server = self.server_proxy_from_opts(opts)
910 api_options ['call_id'] = unique_call_id()
911 return server.DeleteSliver(slice_urn, creds, api_options)
913 def status(self, opts, args):
915 retrieve slice status (SliverStatus)
918 slice_urn = hrn_to_urn(slice_hrn, 'slice')
919 slice_cred = self.slice_credential_string(slice_hrn)
922 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
923 creds.append(delegated_cred)
924 server = self.server_proxy_from_opts(opts)
926 api_options ['call_id'] = unique_call_id()
927 result = server.SliverStatus(slice_urn, creds, api_options)
928 value = ReturnValue.get_value(result)
931 save_variable_to_file(value, opts.file, opts.fileformat)
933 def start(self, opts, args):
935 start named slice (Start)
938 slice_urn = hrn_to_urn(slice_hrn, 'slice')
939 slice_cred = self.slice_credential_string(args[0])
942 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
943 creds.append(delegated_cred)
944 server = self.server_proxy_from_opts(opts)
945 return server.Start(slice_urn, creds)
947 def stop(self, opts, args):
949 stop named slice (Stop)
952 slice_urn = hrn_to_urn(slice_hrn, 'slice')
953 slice_cred = self.slice_credential_string(args[0])
956 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
957 creds.append(delegated_cred)
958 server = self.server_proxy_from_opts(opts)
959 return server.Stop(slice_urn, creds)
962 def reset(self, opts, args):
964 reset named slice (reset_slice)
967 slice_urn = hrn_to_urn(slice_hrn, 'slice')
968 server = self.server_proxy_from_opts(opts)
969 slice_cred = self.slice_credential_string(args[0])
972 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
973 creds.append(delegated_cred)
974 return server.reset_slice(creds, slice_urn)
976 def renew(self, opts, args):
978 renew slice (RenewSliver)
981 slice_urn = hrn_to_urn(slice_hrn, 'slice')
982 server = self.server_proxy_from_opts(opts)
983 slice_cred = self.slice_credential_string(args[0])
986 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
987 creds.append(delegated_cred)
990 api_options ['call_id'] = unique_call_id()
991 result = server.RenewSliver(slice_urn, creds, time, api_options)
992 value = ReturnValue.get_value(result)
996 def shutdown(self, opts, args):
998 shutdown named slice (Shutdown)
1001 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1002 slice_cred = self.slice_credential_string(slice_hrn)
1003 creds = [slice_cred]
1005 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1006 creds.append(delegated_cred)
1007 server = self.server_proxy_from_opts(opts)
1008 return server.Shutdown(slice_urn, creds)
1011 def get_ticket(self, opts, args):
1013 get a ticket for the specified slice
1015 slice_hrn, rspec_path = args[0], args[1]
1016 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1017 slice_cred = self.slice_credential_string(slice_hrn)
1018 creds = [slice_cred]
1020 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1021 creds.append(delegated_cred)
1022 rspec_file = self.get_rspec_file(rspec_path)
1023 rspec = open(rspec_file).read()
1024 server = self.server_proxy_from_opts(opts)
1025 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1026 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1027 self.logger.info("writing ticket to %s"%file)
1028 ticket = SfaTicket(string=ticket_string)
1029 ticket.save_to_file(filename=file, save_parents=True)
1031 def redeem_ticket(self, opts, args):
1033 Connects to nodes in a slice and redeems a ticket
1034 (slice hrn is retrieved from the ticket)
1036 ticket_file = args[0]
1038 # get slice hrn from the ticket
1039 # use this to get the right slice credential
1040 ticket = SfaTicket(filename=ticket_file)
1042 slice_hrn = ticket.gidObject.get_hrn()
1043 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1044 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1045 slice_cred = self.slice_credential_string(slice_hrn)
1047 # get a list of node hostnames from the RSpec
1048 tree = etree.parse(StringIO(ticket.rspec))
1049 root = tree.getroot()
1050 hostnames = root.xpath("./network/site/node/hostname/text()")
1052 # create an xmlrpc connection to the component manager at each of these
1053 # components and gall redeem_ticket
1055 for hostname in hostnames:
1057 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1058 server = self.server_proxy(hostname, CM_PORT, self.private_key, \
1059 self.my_gid, verbose=self.options.debug)
1060 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1061 self.logger.info("Success")
1062 except socket.gaierror:
1063 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1064 except Exception, e:
1065 self.logger.log_exc(e.message)
1068 def create_gid(self, opts, args):
1070 Create a GID (CreateGid)
1075 target_hrn = args[0]
1076 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.bootstrap.my_gid_string())
1078 filename = opts.file
1080 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1081 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1082 GID(string=gid).save_to_file(filename)
1085 def delegate(self, opts, args):
1087 (locally) create delegate credential for use by given hrn
1089 delegee_hrn = args[0]
1090 if opts.delegate_user:
1091 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1092 elif opts.delegate_slice:
1093 slice_cred = self.slice_credential_string(opts.delegate_slice)
1094 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1096 self.logger.warning("Must specify either --user or --slice <hrn>")
1098 delegated_cred = Credential(string=cred)
1099 object_hrn = delegated_cred.get_gid_object().get_hrn()
1100 if opts.delegate_user:
1101 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1102 + get_leaf(object_hrn) + ".cred")
1103 elif opts.delegate_slice:
1104 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1105 + get_leaf(object_hrn) + ".cred")
1107 delegated_cred.save_to_file(dest_fn, save_parents=True)
1109 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1111 def get_trusted_certs(self, opts, args):
1113 return uhe trusted certs at this interface (get_trusted_certs)
1115 trusted_certs = self.registry().get_trusted_certs()
1116 for trusted_cert in trusted_certs:
1117 gid = GID(string=trusted_cert)
1119 cert = Certificate(string=trusted_cert)
1120 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())