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)
617 ### resurrect this temporarily
618 def server_supports_options_arg(self, server):
620 Returns true if server support the optional call_id arg, false otherwise.
622 server_version = self.get_cached_server_version(server)
624 # need to rewrite this
625 if 'sfa' in server_version and 'code_tag' in server_version:
626 code_tag = server_version['code_tag']
627 code_tag_parts = code_tag.split("-")
629 version_parts = code_tag_parts[0].split(".")
630 major, minor = version_parts[0], version_parts[1]
631 rev = code_tag_parts[1]
637 ### ois = options if supported
638 def ois (self, server, option_dict):
639 if self.server_supports_options_arg (server) : return [option_dict]
642 ######################################## miscell utilities
643 def get_rspec_file(self, rspec):
644 if (os.path.isabs(rspec)):
647 file = os.path.join(self.options.sfi_dir, rspec)
648 if (os.path.isfile(file)):
651 self.logger.critical("No such rspec file %s"%rspec)
654 def get_record_file(self, record):
655 if (os.path.isabs(record)):
658 file = os.path.join(self.options.sfi_dir, record)
659 if (os.path.isfile(file)):
662 self.logger.critical("No such registry record file %s"%record)
666 #==========================================================================
667 # Following functions implement the commands
669 # Registry-related commands
670 #==========================================================================
672 def version(self, opts, args):
674 display an SFA server version (GetVersion)
675 or version information about sfi itself
677 if opts.version_local:
678 version=version_core()
680 if opts.version_registry:
681 server=self.registry()
683 server = self.server_proxy_from_opts(opts)
684 result = server.GetVersion()
685 version = ReturnValue.get_value(result)
686 for (k,v) in version.iteritems():
687 print "%-20s: %s"%(k,v)
689 save_variable_to_file(version, opts.file, opts.fileformat)
691 def list(self, opts, args):
693 list entries in named authority registry (List)
700 list = self.registry().List(hrn, self.my_credential_string)
702 raise Exception, "Not enough parameters for the 'list' command"
704 # filter on person, slice, site, node, etc.
705 # THis really should be in the self.filter_records funct def comment...
706 list = filter_records(opts.type, list)
708 print "%s (%s)" % (record['hrn'], record['type'])
710 save_records_to_file(opts.file, list, opts.fileformat)
713 def show(self, opts, args):
715 show details about named registry record (Resolve)
721 records = self.registry().Resolve(hrn, self.my_credential_string)
722 records = filter_records(opts.type, records)
724 self.logger.error("No record of type %s"% opts.type)
725 for record in records:
726 if record['type'] in ['user']:
727 record = UserRecord(dict=record)
728 elif record['type'] in ['slice']:
729 record = SliceRecord(dict=record)
730 elif record['type'] in ['node']:
731 record = NodeRecord(dict=record)
732 elif record['type'].startswith('authority'):
733 record = AuthorityRecord(dict=record)
735 record = SfaRecord(dict=record)
736 if (opts.format == "text"):
739 print record.save_to_string()
741 save_records_to_file(opts.file, records, opts.fileformat)
744 def add(self, opts, args):
745 "add record into registry from xml file (Register)"
746 auth_cred = self.my_authority_credential_string()
750 record_filepath = args[0]
751 rec_file = self.get_record_file(record_filepath)
752 record = load_record_from_file(rec_file).as_dict()
753 return self.registry().Register(record, auth_cred)
755 def update(self, opts, args):
756 "update record into registry from xml file (Update)"
760 rec_file = self.get_record_file(args[0])
761 record = load_record_from_file(rec_file)
762 if record['type'] == "user":
763 if record.get_name() == self.user:
764 cred = self.my_credential_string
766 cred = self.my_authority_credential_string()
767 elif record['type'] in ["slice"]:
769 cred = self.slice_credential_string(record.get_name())
770 except ServerException, e:
771 # XXX smbaker -- once we have better error return codes, update this
772 # to do something better than a string compare
773 if "Permission error" in e.args[0]:
774 cred = self.my_authority_credential_string()
777 elif record.get_type() in ["authority"]:
778 cred = self.my_authority_credential_string()
779 elif record.get_type() == 'node':
780 cred = self.my_authority_credential_string()
782 raise "unknown record type" + record.get_type()
783 record = record.as_dict()
784 return self.registry().Update(record, cred)
786 def remove(self, opts, args):
787 "remove registry record by name (Remove)"
788 auth_cred = self.my_authority_credential_string()
796 return self.registry().Remove(hrn, auth_cred, type)
798 # ==================================================================
799 # Slice-related commands
800 # ==================================================================
802 def slices(self, opts, args):
803 "list instantiated slices (ListSlices) - returns urn's"
804 creds = [self.my_credential_string]
806 delegated_cred = self.delegate_cred(self.my_credential_string, get_authority(self.authority))
807 creds.append(delegated_cred)
808 server = self.server_proxy_from_opts(opts)
810 api_options ['call_id'] = unique_call_id()
811 result = server.ListSlices(creds, *self.ois(server,api_options))
812 value = ReturnValue.get_value(result)
816 # show rspec for named slice
817 def resources(self, opts, args):
819 with no arg, discover available resources,
820 or currently provisioned resources (ListResources)
822 server = self.server_proxy_from_opts(opts)
825 api_options ['call_id'] = unique_call_id()
826 #panos add info api_options
828 api_options['info'] = opts.info
831 cred = self.slice_credential_string(args[0])
833 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
835 cred = self.my_credential_string
839 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
840 creds.append(delegated_cred)
841 if opts.rspec_version:
842 version_manager = VersionManager()
843 server_version = self.get_cached_server_version(server)
844 if 'sfa' in server_version:
845 # just request the version the client wants
846 api_options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
848 # this must be a protogeni aggregate. We should request a v2 ad rspec
849 # regardless of what the client user requested
850 api_options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
852 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
854 result = server.ListResources(creds, *self.ois(server,api_options))
855 value = ReturnValue.get_value(result)
856 if opts.file is None:
857 display_rspec(value, opts.format)
859 save_rspec_to_file(value, opts.file)
862 def create(self, opts, args):
864 create or update named slice with given rspec
866 server = self.server_proxy_from_opts(opts)
867 server_version = self.get_cached_server_version(server)
869 slice_urn = hrn_to_urn(slice_hrn, 'slice')
870 slice_cred = self.slice_credential_string(slice_hrn)
871 delegated_cred = None
872 if server_version.get('interface') == 'slicemgr':
873 # delegate our cred to the slice manager
874 # do not delegate cred to slicemgr...not working at the moment
876 #if server_version.get('hrn'):
877 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
878 #elif server_version.get('urn'):
879 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
881 rspec_file = self.get_rspec_file(args[1])
882 rspec = open(rspec_file).read()
884 # need to pass along user keys to the aggregate.
886 # { urn: urn:publicid:IDN+emulab.net+user+alice
887 # keys: [<ssh key A>, <ssh key B>]
890 slice_records = self.registry().Resolve(slice_urn, [self.my_credential_string])
891 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
892 slice_record = slice_records[0]
893 user_hrns = slice_record['researcher']
894 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
895 user_records = self.registry().Resolve(user_urns, [self.my_credential_string])
897 if 'sfa' not in server_version:
898 users = pg_users_arg(user_records)
900 rspec.filter({'component_manager_id': server_version['urn']})
901 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
904 users = sfa_users_arg(user_records, slice_record)
907 creds.append(delegated_cred)
908 # do not append users, keys, or slice tags. Anything
909 # not contained in this request will be removed from the slice
911 api_options ['append'] = False
912 api_options ['call_id'] = unique_call_id()
913 result = server.CreateSliver(slice_urn, creds, rspec, users, *self.ois(server,api_options))
914 value = ReturnValue.get_value(result)
915 if opts.file is None:
918 save_rspec_to_file (value, opts.file)
921 def delete(self, opts, args):
923 delete named slice (DeleteSliver)
926 slice_urn = hrn_to_urn(slice_hrn, 'slice')
927 slice_cred = self.slice_credential_string(slice_hrn)
930 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
931 creds.append(delegated_cred)
932 server = self.server_proxy_from_opts(opts)
934 api_options ['call_id'] = unique_call_id()
935 return server.DeleteSliver(slice_urn, creds, *self.ois(server,api_options))
937 def status(self, opts, args):
939 retrieve slice status (SliverStatus)
942 slice_urn = hrn_to_urn(slice_hrn, 'slice')
943 slice_cred = self.slice_credential_string(slice_hrn)
946 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
947 creds.append(delegated_cred)
948 server = self.server_proxy_from_opts(opts)
950 api_options ['call_id'] = unique_call_id()
951 result = server.SliverStatus(slice_urn, creds, *self.ois(server,api_options))
952 value = ReturnValue.get_value(result)
955 save_variable_to_file(value, opts.file, opts.fileformat)
957 def start(self, opts, args):
959 start named slice (Start)
962 slice_urn = hrn_to_urn(slice_hrn, 'slice')
963 slice_cred = self.slice_credential_string(args[0])
966 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
967 creds.append(delegated_cred)
968 server = self.server_proxy_from_opts(opts)
969 # xxx Thierry - does this not need an api_options as well
970 return server.Start(slice_urn, creds)
972 def stop(self, opts, args):
974 stop named slice (Stop)
977 slice_urn = hrn_to_urn(slice_hrn, 'slice')
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)
983 server = self.server_proxy_from_opts(opts)
984 return server.Stop(slice_urn, creds)
987 def reset(self, opts, args):
989 reset named slice (reset_slice)
992 slice_urn = hrn_to_urn(slice_hrn, 'slice')
993 server = self.server_proxy_from_opts(opts)
994 slice_cred = self.slice_credential_string(args[0])
997 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
998 creds.append(delegated_cred)
999 return server.reset_slice(creds, slice_urn)
1001 def renew(self, opts, args):
1003 renew slice (RenewSliver)
1006 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1007 server = self.server_proxy_from_opts(opts)
1008 slice_cred = self.slice_credential_string(args[0])
1009 creds = [slice_cred]
1011 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1012 creds.append(delegated_cred)
1015 api_options ['call_id'] = unique_call_id()
1016 result = server.RenewSliver(slice_urn, creds, time, *self.ois(server,api_options))
1017 value = ReturnValue.get_value(result)
1021 def shutdown(self, opts, args):
1023 shutdown named slice (Shutdown)
1026 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1027 slice_cred = self.slice_credential_string(slice_hrn)
1028 creds = [slice_cred]
1030 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1031 creds.append(delegated_cred)
1032 server = self.server_proxy_from_opts(opts)
1033 return server.Shutdown(slice_urn, creds)
1036 def get_ticket(self, opts, args):
1038 get a ticket for the specified slice
1040 slice_hrn, rspec_path = args[0], args[1]
1041 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1042 slice_cred = self.slice_credential_string(slice_hrn)
1043 creds = [slice_cred]
1045 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1046 creds.append(delegated_cred)
1047 rspec_file = self.get_rspec_file(rspec_path)
1048 rspec = open(rspec_file).read()
1049 server = self.server_proxy_from_opts(opts)
1050 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1051 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1052 self.logger.info("writing ticket to %s"%file)
1053 ticket = SfaTicket(string=ticket_string)
1054 ticket.save_to_file(filename=file, save_parents=True)
1056 def redeem_ticket(self, opts, args):
1058 Connects to nodes in a slice and redeems a ticket
1059 (slice hrn is retrieved from the ticket)
1061 ticket_file = args[0]
1063 # get slice hrn from the ticket
1064 # use this to get the right slice credential
1065 ticket = SfaTicket(filename=ticket_file)
1067 slice_hrn = ticket.gidObject.get_hrn()
1068 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1069 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1070 slice_cred = self.slice_credential_string(slice_hrn)
1072 # get a list of node hostnames from the RSpec
1073 tree = etree.parse(StringIO(ticket.rspec))
1074 root = tree.getroot()
1075 hostnames = root.xpath("./network/site/node/hostname/text()")
1077 # create an xmlrpc connection to the component manager at each of these
1078 # components and gall redeem_ticket
1080 for hostname in hostnames:
1082 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1083 server = self.server_proxy(hostname, CM_PORT, self.private_key, \
1084 self.my_gid, verbose=self.options.debug)
1085 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1086 self.logger.info("Success")
1087 except socket.gaierror:
1088 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1089 except Exception, e:
1090 self.logger.log_exc(e.message)
1093 def create_gid(self, opts, args):
1095 Create a GID (CreateGid)
1100 target_hrn = args[0]
1101 gid = self.registry().CreateGid(self.my_credential_string, target_hrn, self.bootstrap.my_gid_string())
1103 filename = opts.file
1105 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1106 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1107 GID(string=gid).save_to_file(filename)
1110 def delegate(self, opts, args):
1112 (locally) create delegate credential for use by given hrn
1114 delegee_hrn = args[0]
1115 if opts.delegate_user:
1116 cred = self.delegate_cred(self.my_credential_string, delegee_hrn, 'user')
1117 elif opts.delegate_slice:
1118 slice_cred = self.slice_credential_string(opts.delegate_slice)
1119 cred = self.delegate_cred(slice_cred, delegee_hrn, 'slice')
1121 self.logger.warning("Must specify either --user or --slice <hrn>")
1123 delegated_cred = Credential(string=cred)
1124 object_hrn = delegated_cred.get_gid_object().get_hrn()
1125 if opts.delegate_user:
1126 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1127 + get_leaf(object_hrn) + ".cred")
1128 elif opts.delegate_slice:
1129 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1130 + get_leaf(object_hrn) + ".cred")
1132 delegated_cred.save_to_file(dest_fn, save_parents=True)
1134 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1136 def get_trusted_certs(self, opts, args):
1138 return uhe trusted certs at this interface (get_trusted_certs)
1140 trusted_certs = self.registry().get_trusted_certs()
1141 for trusted_cert in trusted_certs:
1142 gid = GID(string=trusted_cert)
1144 cert = Certificate(string=trusted_cert)
1145 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())