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.sfaserverproxy import SfaServerProxy, ServerException
34 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
35 from sfa.client.return_value import ReturnValue
40 # utility methods here
42 def display_rspec(rspec, format='rspec'):
44 tree = etree.parse(StringIO(rspec))
46 result = root.xpath("./network/site/node/hostname/text()")
47 elif format in ['ip']:
48 # The IP address is not yet part of the new RSpec
49 # so this doesn't do anything yet.
50 tree = etree.parse(StringIO(rspec))
52 result = root.xpath("./network/site/node/ipv4/text()")
59 def display_list(results):
60 for result in results:
63 def display_records(recordList, dump=False):
64 ''' Print all fields in the record'''
65 for record in recordList:
66 display_record(record, dump)
68 def display_record(record, dump=False):
72 info = record.getdict()
73 print "%s (%s)" % (info['hrn'], info['type'])
77 def filter_records(type, records):
79 for record in records:
80 if (record['type'] == type) or (type == "all"):
81 filtered_records.append(record)
82 return filtered_records
86 def save_variable_to_file(var, filename, format="text"):
87 f = open(filename, "w")
90 elif format == "pickled":
91 f.write(pickle.dumps(var))
93 # this should never happen
94 print "unknown output format", format
97 def save_rspec_to_file(rspec, filename):
98 if not filename.endswith(".rspec"):
99 filename = filename + ".rspec"
100 f = open(filename, 'w')
105 def save_records_to_file(filename, recordList, format="xml"):
108 for record in recordList:
110 save_record_to_file(filename + "." + str(index), record)
112 save_record_to_file(filename, record)
114 elif format == "xmllist":
115 f = open(filename, "w")
116 f.write("<recordlist>\n")
117 for record in recordList:
118 record = SfaRecord(dict=record)
119 f.write('<record hrn="' + record.get_name() + '" type="' + record.get_type() + '" />\n')
120 f.write("</recordlist>\n")
122 elif format == "hrnlist":
123 f = open(filename, "w")
124 for record in recordList:
125 record = SfaRecord(dict=record)
126 f.write(record.get_name() + "\n")
129 # this should never happen
130 print "unknown output format", format
132 def save_record_to_file(filename, record):
133 if record['type'] in ['user']:
134 record = UserRecord(dict=record)
135 elif record['type'] in ['slice']:
136 record = SliceRecord(dict=record)
137 elif record['type'] in ['node']:
138 record = NodeRecord(dict=record)
139 elif record['type'] in ['authority', 'ma', 'sa']:
140 record = AuthorityRecord(dict=record)
142 record = SfaRecord(dict=record)
143 str = record.save_to_string()
144 f=codecs.open(filename, encoding='utf-8',mode="w")
151 def load_record_from_file(filename):
152 f=codecs.open(filename, encoding="utf-8", mode="r")
155 record = SfaRecord(string=str)
160 def unique_call_id(): return uuid.uuid4().urn
164 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user']
167 def default_sfi_dir ():
168 if os.path.isfile("./sfi_config"):
171 return os.path.expanduser("~/.sfi/")
173 # dummy to meet Sfi's expectations for its 'options' field
174 # i.e. s/t we can do setattr on
178 def __init__ (self,options=None):
179 if options is None: options=Sfi.DummyOptions()
180 for opt in Sfi.required_options:
181 if not hasattr(options,opt): setattr(options,opt,None)
182 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
183 self.options = options
187 self.authority = None
188 self.hashrequest = False
189 self.logger = sfi_logger
190 self.logger.enable_console()
191 self.available_names = [ tuple[0] for tuple in Sfi.available ]
192 self.available_dict = dict (Sfi.available)
194 # tuples command-name expected-args in the order in which they should appear in the help
197 ("list", "authority"),
200 ("update", "record"),
203 ("resources", "[slice_hrn]"),
204 ("create", "slice_hrn rspec"),
205 ("delete", "slice_hrn"),
206 ("status", "slice_hrn"),
207 ("start", "slice_hrn"),
208 ("stop", "slice_hrn"),
209 ("reset", "slice_hrn"),
210 ("renew", "slice_hrn time"),
211 ("shutdown", "slice_hrn"),
212 ("get_ticket", "slice_hrn rspec"),
213 ("redeem_ticket", "ticket"),
214 ("delegate", "name"),
215 ("create_gid", "[name]"),
216 ("get_trusted_certs", "cred"),
219 def print_command_help (self, options):
220 verbose=getattr(options,'verbose')
221 format3="%18s %-15s %s"
224 print format3%("command","cmd_args","description")
228 self.create_parser().print_help()
229 for command in self.available_names:
230 args=self.available_dict[command]
231 method=getattr(self,command,None)
233 if method: doc=getattr(method,'__doc__',"")
234 if not doc: doc="*** no doc found ***"
235 doc=doc.strip(" \t\n")
236 doc=doc.replace("\n","\n"+35*' ')
239 print format3%(command,args,doc)
241 self.create_cmd_parser(command).print_help()
243 def create_cmd_parser(self, command):
244 if command not in self.available_dict:
245 msg="Invalid command\n"
247 msg += ','.join(self.available_names)
248 self.logger.critical(msg)
251 parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \
252 % (command, self.available_dict[command]))
254 # user specifies remote aggregate/sm/component
255 if command in ("resources", "slices", "create", "delete", "start", "stop",
256 "restart", "shutdown", "get_ticket", "renew", "status"):
257 parser.add_option("-a", "--aggregate", dest="aggregate",
258 default=None, help="aggregate host")
259 parser.add_option("-p", "--port", dest="port",
260 default=AGGREGATE_PORT, help="aggregate port")
261 parser.add_option("-c", "--component", dest="component", default=None,
262 help="component hrn")
263 parser.add_option("-d", "--delegate", dest="delegate", default=None,
265 help="Include a credential delegated to the user's root"+\
266 "authority in set of credentials for this call")
268 # registy filter option
269 if command in ("list", "show", "remove"):
270 parser.add_option("-t", "--type", dest="type", type="choice",
271 help="type filter ([all]|user|slice|authority|node|aggregate)",
272 choices=("all", "user", "slice", "authority", "node", "aggregate"),
275 if command in ("resources"):
276 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
277 help="schema type and version of resulting RSpec")
278 parser.add_option("-f", "--format", dest="format", type="choice",
279 help="display format ([xml]|dns|ip)", default="xml",
280 choices=("xml", "dns", "ip"))
281 #panos: a new option to define the type of information about resources a user is interested in
282 parser.add_option("-i", "--info", dest="info",
283 help="optional component information", default=None)
286 # 'create' does return the new rspec, makes sense to save that too
287 if command in ("resources", "show", "list", "create_gid", 'create'):
288 parser.add_option("-o", "--output", dest="file",
289 help="output XML to file", metavar="FILE", default=None)
291 if command in ("show", "list"):
292 parser.add_option("-f", "--format", dest="format", type="choice",
293 help="display format ([text]|xml)", default="text",
294 choices=("text", "xml"))
296 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
297 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
298 choices=("xml", "xmllist", "hrnlist"))
300 if command in ("status", "version"):
301 parser.add_option("-o", "--output", dest="file",
302 help="output dictionary to file", metavar="FILE", default=None)
303 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
304 help="output file format ([text]|pickled)", default="text",
305 choices=("text","pickled"))
307 if command in ("delegate"):
308 parser.add_option("-u", "--user",
309 action="store_true", dest="delegate_user", default=False,
310 help="delegate user credential")
311 parser.add_option("-s", "--slice", dest="delegate_slice",
312 help="delegate slice credential", metavar="HRN", default=None)
314 if command in ("version"):
315 parser.add_option("-a", "--aggregate", dest="aggregate",
316 default=None, help="aggregate host")
317 parser.add_option("-p", "--port", dest="port",
318 default=AGGREGATE_PORT, help="aggregate port")
319 parser.add_option("-R","--registry-version",
320 action="store_true", dest="version_registry", default=False,
321 help="probe registry version instead of slicemgr")
322 parser.add_option("-l","--local",
323 action="store_true", dest="version_local", default=False,
324 help="display version of the local client")
329 def create_parser(self):
331 # Generate command line parser
332 parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
333 description="Commands: %s"%(" ".join(self.available_names)))
334 parser.add_option("-r", "--registry", dest="registry",
335 help="root registry", metavar="URL", default=None)
336 parser.add_option("-s", "--slicemgr", dest="sm",
337 help="slice manager", metavar="URL", default=None)
338 parser.add_option("-d", "--dir", dest="sfi_dir",
339 help="config & working directory - default is %default",
340 metavar="PATH", default=Sfi.default_sfi_dir())
341 parser.add_option("-u", "--user", dest="user",
342 help="user name", metavar="HRN", default=None)
343 parser.add_option("-a", "--auth", dest="auth",
344 help="authority name", metavar="HRN", default=None)
345 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
346 help="verbose mode - cumulative")
347 parser.add_option("-D", "--debug",
348 action="store_true", dest="debug", default=False,
349 help="Debug (xml-rpc) protocol messages")
350 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
351 help="RPC protocol (xmlrpc or soap)")
352 parser.add_option("-k", "--hashrequest",
353 action="store_true", dest="hashrequest", default=False,
354 help="Create a hash of the request that will be authenticated on the server")
355 parser.add_option("-t", "--timeout", dest="timeout", default=None,
356 help="Amout of time to wait before timing out the request")
357 parser.add_option("-?", "--commands",
358 action="store_true", dest="command_help", default=False,
359 help="one page summary on commands & exit")
360 parser.disable_interspersed_args()
365 def print_help (self):
366 self.sfi_parser.print_help()
367 self.cmd_parser.print_help()
370 # Main: parse arguments and dispatch to command
372 def dispatch(self, command, cmd_opts, cmd_args):
373 return getattr(self, command)(cmd_opts, cmd_args)
376 self.sfi_parser = self.create_parser()
377 (options, args) = self.sfi_parser.parse_args()
378 if options.command_help:
379 self.print_command_help(options)
381 self.options = options
383 self.logger.setLevelFromOptVerbose(self.options.verbose)
384 if options.hashrequest:
385 self.hashrequest = True
388 self.logger.critical("No command given. Use -h for help.")
389 self.print_command_help(options)
393 self.cmd_parser = self.create_cmd_parser(command)
394 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
397 self.logger.info("Command=%s" % command)
398 if command in ("resources"):
399 self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
400 elif command in ("list", "show", "remove"):
401 self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
402 self.logger.debug('cmd_args %s' % cmd_args)
405 self.dispatch(command, cmd_opts, cmd_args)
407 self.logger.critical ("Unknown command %s"%command)
414 def read_config(self):
415 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
417 config = Config (config_file)
419 self.logger.critical("Failed to read configuration file %s"%config_file)
420 self.logger.info("Make sure to remove the export clauses and to add quotes")
421 if self.options.verbose==0:
422 self.logger.info("Re-run with -v for more details")
424 self.logger.log_exc("Could not read config file %s"%config_file)
429 if (self.options.sm is not None):
430 self.sm_url = self.options.sm
431 elif hasattr(config, "SFI_SM"):
432 self.sm_url = config.SFI_SM
434 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
438 if (self.options.registry is not None):
439 self.reg_url = self.options.registry
440 elif hasattr(config, "SFI_REGISTRY"):
441 self.reg_url = config.SFI_REGISTRY
443 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
448 if (self.options.user is not None):
449 self.user = self.options.user
450 elif hasattr(config, "SFI_USER"):
451 self.user = config.SFI_USER
453 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
457 if (self.options.auth is not None):
458 self.authority = self.options.auth
459 elif hasattr(config, "SFI_AUTH"):
460 self.authority = config.SFI_AUTH
462 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
470 # Establish Connection to SliceMgr and Registry Servers
472 def set_servers(self):
475 # Get key and certificate
476 key_file = self.get_key_file()
477 cert_file = self.get_cert_file(key_file)
478 self.key_file = key_file
479 self.cert_file = cert_file
480 self.cert = GID(filename=cert_file)
481 self.logger.info("Contacting Registry at: %s"%self.reg_url)
482 self.registry = SfaServerProxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
483 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
484 self.slicemgr = SfaServerProxy(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
487 def get_cached_server_version(self, server):
488 # check local cache first
491 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
492 cache_key = server.url + "-version"
494 cache = Cache(cache_file)
497 self.logger.info("Local cache not found at: %s" % cache_file)
500 version = cache.get(cache_key)
503 result = server.GetVersion()
504 version= ReturnValue.get_value(result)
505 # cache version for 24 hours
506 cache.add(cache_key, version, ttl= 60*60*24)
507 self.logger.info("Updating cache file %s" % cache_file)
508 cache.save_to_file(cache_file)
513 def server_supports_call_id(self, server):
515 Returns true if server support the optional call_id arg, false otherwise.
517 server_version = self.get_cached_server_version(server)
518 return server_version.has_key ('call_id_support')
521 # Get various credential and spec files
523 # Establishes limiting conventions
524 # - conflates MAs and SAs
525 # - assumes last token in slice name is unique
527 # Bootstraps credentials
528 # - bootstrap user credential from self-signed certificate
529 # - bootstrap authority credential from user credential
530 # - bootstrap slice credential from user credential
534 def get_key_file(self):
535 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
536 if (os.path.isfile(file)):
539 self.logger.error("Key file %s does not exist"%file)
543 def get_cert_file(self, key_file):
545 cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
546 if (os.path.isfile(cert_file)):
547 # we'd perfer to use Registry issued certs instead of self signed certs.
548 # if this is a Registry cert (GID) then we are done
549 gid = GID(filename=cert_file)
553 # generate self signed certificate
554 k = Keypair(filename=key_file)
555 cert = Certificate(subject=self.user)
557 cert.set_issuer(k, self.user)
559 self.logger.info("Writing self-signed certificate to %s"%cert_file)
560 cert.save_to_file(cert_file)
562 # try to get registry issued cert
564 self.logger.info("Getting Registry issued cert")
566 # *hack. need to set registry before _get_gid() is called
567 self.registry = SfaServerProxy(self.reg_url, key_file, cert_file,
568 timeout=self.options.timeout, verbose=self.options.debug)
569 gid = self._get_gid(type='user')
571 self.logger.info("Writing certificate to %s"%cert_file)
572 gid.save_to_file(cert_file)
574 self.logger.info("Failed to download Registry issued cert")
578 def get_cached_gid(self, file):
583 if (os.path.isfile(file)):
584 gid = GID(filename=file)
589 # def get_gid(self, opts, args):
590 # """ Get the specify gid and save it to file """
594 # gid = self._get_gid(hrn)
595 # self.logger.debug("Sfi.get_gid-> %s" % gid.save_to_string(save_parents=True))
598 def _get_gid(self, hrn=None, type=None):
600 git_gid helper. Retrive the gid from the registry and save it to file.
606 gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
607 gid = self.get_cached_gid(gidfile)
609 user_cred = self.get_user_cred()
610 records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
612 raise RecordNotFound(args[0])
617 if type == rec['type']:
620 raise RecordNotFound(args[0])
622 gid = GID(string=record['gid'])
623 self.logger.info("Writing gid to %s"%gidfile)
624 gid.save_to_file(filename=gidfile)
628 def get_cached_credential(self, file):
630 Return a cached credential only if it hasn't expired.
632 if (os.path.isfile(file)):
633 credential = Credential(filename=file)
634 # make sure it isnt expired
635 if not credential.get_expiration or \
636 datetime.datetime.today() < credential.get_expiration():
640 def get_user_cred(self):
641 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
642 return self.get_cred(file, 'user', self.user)
644 def get_auth_cred(self):
645 if not self.authority:
646 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
648 file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
649 return self.get_cred(file, 'authority', self.authority)
651 def get_slice_cred(self, name):
652 file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
653 return self.get_cred(file, 'slice', name)
655 def get_cred(self, file, type, hrn):
656 # attempt to load a cached credential
657 cred = self.get_cached_credential(file)
660 cert_string = self.cert.save_to_string(save_parents=True)
661 user_name = self.user.replace(self.authority + ".", '')
662 if user_name.count(".") > 0:
663 user_name = user_name.replace(".", '_')
664 self.user = self.authority + "." + user_name
665 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
667 # bootstrap slice credential from user credential
668 user_cred = self.get_user_cred().save_to_string(save_parents=True)
669 cred_str = self.registry.GetCredential(user_cred, hrn, type)
672 self.logger.critical("Failed to get %s credential" % type)
675 cred = Credential(string=cred_str)
676 cred.save_to_file(file, save_parents=True)
677 self.logger.info("Writing %s credential to %s" %(type, file))
682 def delegate_cred(self, object_cred, hrn):
683 # the gid and hrn of the object we are delegating
684 if isinstance(object_cred, str):
685 object_cred = Credential(string=object_cred)
686 object_gid = object_cred.get_gid_object()
687 object_hrn = object_gid.get_hrn()
689 if not object_cred.get_privileges().get_all_delegate():
690 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
693 # the delegating user's gid
694 caller_gid = self._get_gid(self.user)
695 caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
697 # the gid of the user who will be delegated to
698 delegee_gid = self._get_gid(hrn)
699 delegee_hrn = delegee_gid.get_hrn()
700 delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
701 delegee_gid.save_to_file(filename=delegee_gidfile)
702 dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
703 return dcred.save_to_string(save_parents=True)
705 ######################################## miscell utilities
706 def get_rspec_file(self, rspec):
707 if (os.path.isabs(rspec)):
710 file = os.path.join(self.options.sfi_dir, rspec)
711 if (os.path.isfile(file)):
714 self.logger.critical("No such rspec file %s"%rspec)
717 def get_record_file(self, record):
718 if (os.path.isabs(record)):
721 file = os.path.join(self.options.sfi_dir, record)
722 if (os.path.isfile(file)):
725 self.logger.critical("No such registry record file %s"%record)
729 def get_component_proxy_from_hrn(self, hrn):
730 # direct connection to the nodes component manager interface
731 user_cred = self.get_user_cred().save_to_string(save_parents=True)
732 records = self.registry.Resolve(hrn, user_cred)
733 records = filter_records('node', records)
735 self.logger.warning("No such component:%r"% opts.component)
738 return self.server_proxy(record['hostname'], CM_PORT, self.key_file, self.cert_file)
740 def server_proxy(self, host, port, keyfile, certfile):
742 Return an instance of an xmlrpc server connection
744 # port is appended onto the domain, before the path. Should look like:
745 # http://domain:port/path
746 host_parts = host.split('/')
747 host_parts[0] = host_parts[0] + ":" + str(port)
748 url = "http://%s" % "/".join(host_parts)
749 return SfaServerProxy(url, keyfile, certfile, timeout=self.options.timeout,
750 verbose=self.options.debug)
752 # xxx opts could be retrieved in self.options
753 def server_proxy_from_opts(self, opts):
755 Return instance of an xmlrpc connection to a slice manager, aggregate
756 or component server depending on the specified opts
758 server = self.slicemgr
759 # direct connection to an aggregate
760 if hasattr(opts, 'aggregate') and opts.aggregate:
761 server = self.server_proxy(opts.aggregate, opts.port, self.key_file, self.cert_file)
762 # direct connection to the nodes component manager interface
763 if hasattr(opts, 'component') and opts.component:
764 server = self.get_component_proxy_from_hrn(opts.component)
767 #==========================================================================
768 # Following functions implement the commands
770 # Registry-related commands
771 #==========================================================================
773 def version(self, opts, args):
775 display an SFA server version (GetVersion)
776 or version information about sfi itself
778 if opts.version_local:
779 version=version_core()
781 if opts.version_registry:
784 server = self.server_proxy_from_opts(opts)
785 result = server.GetVersion()
786 version = ReturnValue.get_value(result)
787 for (k,v) in version.iteritems():
788 print "%-20s: %s"%(k,v)
790 save_variable_to_file(version, opts.file, opts.fileformat)
792 def list(self, opts, args):
794 list entries in named authority registry (List)
800 user_cred = self.get_user_cred().save_to_string(save_parents=True)
802 list = self.registry.List(hrn, user_cred)
804 raise Exception, "Not enough parameters for the 'list' command"
806 # filter on person, slice, site, node, etc.
807 # THis really should be in the self.filter_records funct def comment...
808 list = filter_records(opts.type, list)
810 print "%s (%s)" % (record['hrn'], record['type'])
812 save_records_to_file(opts.file, list, opts.fileformat)
815 def show(self, opts, args):
817 show details about named registry record (Resolve)
823 user_cred = self.get_user_cred().save_to_string(save_parents=True)
824 records = self.registry.Resolve(hrn, user_cred)
825 records = filter_records(opts.type, records)
827 self.logger.error("No record of type %s"% opts.type)
828 for record in records:
829 if record['type'] in ['user']:
830 record = UserRecord(dict=record)
831 elif record['type'] in ['slice']:
832 record = SliceRecord(dict=record)
833 elif record['type'] in ['node']:
834 record = NodeRecord(dict=record)
835 elif record['type'].startswith('authority'):
836 record = AuthorityRecord(dict=record)
838 record = SfaRecord(dict=record)
839 if (opts.format == "text"):
842 print record.save_to_string()
844 save_records_to_file(opts.file, records, opts.fileformat)
847 def add(self, opts, args):
848 "add record into registry from xml file (Register)"
849 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
853 record_filepath = args[0]
854 rec_file = self.get_record_file(record_filepath)
855 record = load_record_from_file(rec_file).as_dict()
856 return self.registry.Register(record, auth_cred)
858 def update(self, opts, args):
859 "update record into registry from xml file (Update)"
860 user_cred = self.get_user_cred()
864 rec_file = self.get_record_file(args[0])
865 record = load_record_from_file(rec_file)
866 if record['type'] == "user":
867 if record.get_name() == user_cred.get_gid_object().get_hrn():
868 cred = user_cred.save_to_string(save_parents=True)
870 cred = self.get_auth_cred().save_to_string(save_parents=True)
871 elif record['type'] in ["slice"]:
873 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
874 except ServerException, e:
875 # XXX smbaker -- once we have better error return codes, update this
876 # to do something better than a string compare
877 if "Permission error" in e.args[0]:
878 cred = self.get_auth_cred().save_to_string(save_parents=True)
881 elif record.get_type() in ["authority"]:
882 cred = self.get_auth_cred().save_to_string(save_parents=True)
883 elif record.get_type() == 'node':
884 cred = self.get_auth_cred().save_to_string(save_parents=True)
886 raise "unknown record type" + record.get_type()
887 record = record.as_dict()
888 return self.registry.Update(record, cred)
890 def remove(self, opts, args):
891 "remove registry record by name (Remove)"
892 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
900 return self.registry.Remove(hrn, auth_cred, type)
902 # ==================================================================
903 # Slice-related commands
904 # ==================================================================
906 def slices(self, opts, args):
907 "list instantiated slices (ListSlices) - returns urn's"
908 user_cred = self.get_user_cred().save_to_string(save_parents=True)
911 delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
912 creds.append(delegated_cred)
913 server = self.server_proxy_from_opts(opts)
915 if self.server_supports_call_id(server):
916 options = {'call_id': unique_call_id()}
917 call_args.append(options)
918 result = server.ListSlices(*call_args)
919 value = ReturnValue.get_value(result)
923 # show rspec for named slice
924 def resources(self, opts, args):
926 with no arg, discover available resources,
927 or currently provisioned resources (ListResources)
929 user_cred = self.get_user_cred().save_to_string(save_parents=True)
930 server = self.server_proxy_from_opts(opts)
932 options = {'call_id': unique_call_id()}
933 #panos add info options
935 options['info'] = opts.info
938 cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
940 options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
946 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
947 creds.append(delegated_cred)
948 if opts.rspec_version:
949 version_manager = VersionManager()
950 server_version = self.get_cached_server_version(server)
951 if 'sfa' in server_version:
952 # just request the version the client wants
953 options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
955 # this must be a protogeni aggregate. We should request a v2 ad rspec
956 # regardless of what the client user requested
957 options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
959 options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
961 call_args = [creds, options]
962 result = server.ListResources(*call_args)
963 value = ReturnValue.get_value(result)
964 if opts.file is None:
965 display_rspec(value, opts.format)
967 save_rspec_to_file(value, opts.file)
970 def create(self, opts, args):
972 create or update named slice with given rspec
974 server = self.server_proxy_from_opts(opts)
975 server_version = self.get_cached_server_version(server)
977 slice_urn = hrn_to_urn(slice_hrn, 'slice')
978 user_cred = self.get_user_cred()
979 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
980 delegated_cred = None
981 if server_version.get('interface') == 'slicemgr':
982 # delegate our cred to the slice manager
983 # do not delegate cred to slicemgr...not working at the moment
985 #if server_version.get('hrn'):
986 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
987 #elif server_version.get('urn'):
988 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
990 rspec_file = self.get_rspec_file(args[1])
991 rspec = open(rspec_file).read()
993 # need to pass along user keys to the aggregate.
995 # { urn: urn:publicid:IDN+emulab.net+user+alice
996 # keys: [<ssh key A>, <ssh key B>]
999 slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
1000 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1001 slice_record = slice_records[0]
1002 user_hrns = slice_record['researcher']
1003 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1004 user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
1006 if 'sfa' not in server_version:
1007 users = pg_users_arg(user_records)
1008 rspec = RSpec(rspec)
1009 rspec.filter({'component_manager_id': server_version['urn']})
1010 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1011 creds = [slice_cred]
1013 users = sfa_users_arg(user_records, slice_record)
1014 creds = [slice_cred]
1016 creds.append(delegated_cred)
1017 # do not append users, keys, or slice tags. Anything
1018 # not contained in this request will be removed from the slice
1019 options = {'append': False}
1020 if self.server_supports_call_id(server):
1021 options['call_id'] = unique_call_id()
1022 call_args = [slice_urn, creds, rspec, users, options]
1023 result = server.CreateSliver(*call_args)
1024 value = ReturnValue.get_value(result)
1025 if opts.file is None:
1028 save_rspec_to_file (value, opts.file)
1031 def delete(self, opts, args):
1033 delete named slice (DeleteSliver)
1036 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1037 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1038 creds = [slice_cred]
1040 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1041 creds.append(delegated_cred)
1042 server = self.server_proxy_from_opts(opts)
1043 call_args = [slice_urn, creds]
1044 if self.server_supports_call_id(server):
1045 options = {'call_id': unique_call_id()}
1046 call_args.append(options)
1047 return server.DeleteSliver(*call_args)
1049 def status(self, opts, args):
1051 retrieve slice status (SliverStatus)
1054 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1055 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1056 creds = [slice_cred]
1058 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1059 creds.append(delegated_cred)
1060 server = self.server_proxy_from_opts(opts)
1061 call_args = [slice_urn, creds]
1062 if self.server_supports_call_id(server):
1063 options = {'call_id': unique_call_id()}
1064 call_args.append(options)
1065 result = server.SliverStatus(*call_args)
1066 value = ReturnValue.get_value(result)
1069 save_variable_to_file(value, opts.file, opts.fileformat)
1071 def start(self, opts, args):
1073 start named slice (Start)
1076 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1077 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1078 creds = [slice_cred]
1080 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1081 creds.append(delegated_cred)
1082 server = self.server_proxy_from_opts(opts)
1083 return server.Start(slice_urn, creds)
1085 def stop(self, opts, args):
1087 stop named slice (Stop)
1090 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1091 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1092 creds = [slice_cred]
1094 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1095 creds.append(delegated_cred)
1096 server = self.server_proxy_from_opts(opts)
1097 return server.Stop(slice_urn, creds)
1100 def reset(self, opts, args):
1102 reset named slice (reset_slice)
1105 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1106 server = self.server_proxy_from_opts(opts)
1107 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1108 creds = [slice_cred]
1110 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1111 creds.append(delegated_cred)
1112 return server.reset_slice(creds, slice_urn)
1114 def renew(self, opts, args):
1116 renew slice (RenewSliver)
1119 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1120 server = self.server_proxy_from_opts(opts)
1121 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1122 creds = [slice_cred]
1124 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1125 creds.append(delegated_cred)
1128 call_args = [slice_urn, creds, time]
1129 if self.server_supports_call_id(server):
1130 options = {'call_id': unique_call_id()}
1131 call_args.append(options)
1132 result = server.RenewSliver(*call_args)
1133 value = ReturnValue.get_value(result)
1137 def shutdown(self, opts, args):
1139 shutdown named slice (Shutdown)
1142 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1143 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1144 creds = [slice_cred]
1146 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1147 creds.append(delegated_cred)
1148 server = self.server_proxy_from_opts(opts)
1149 return server.Shutdown(slice_urn, creds)
1152 def get_ticket(self, opts, args):
1154 get a ticket for the specified slice
1156 slice_hrn, rspec_path = args[0], args[1]
1157 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1158 user_cred = self.get_user_cred()
1159 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1160 creds = [slice_cred]
1162 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1163 creds.append(delegated_cred)
1164 rspec_file = self.get_rspec_file(rspec_path)
1165 rspec = open(rspec_file).read()
1166 server = self.server_proxy_from_opts(opts)
1167 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1168 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1169 self.logger.info("writing ticket to %s"%file)
1170 ticket = SfaTicket(string=ticket_string)
1171 ticket.save_to_file(filename=file, save_parents=True)
1173 def redeem_ticket(self, opts, args):
1175 Connects to nodes in a slice and redeems a ticket
1176 (slice hrn is retrieved from the ticket)
1178 ticket_file = args[0]
1180 # get slice hrn from the ticket
1181 # use this to get the right slice credential
1182 ticket = SfaTicket(filename=ticket_file)
1184 slice_hrn = ticket.gidObject.get_hrn()
1185 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1186 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1187 user_cred = self.get_user_cred()
1188 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1190 # get a list of node hostnames from the RSpec
1191 tree = etree.parse(StringIO(ticket.rspec))
1192 root = tree.getroot()
1193 hostnames = root.xpath("./network/site/node/hostname/text()")
1195 # create an xmlrpc connection to the component manager at each of these
1196 # components and gall redeem_ticket
1198 for hostname in hostnames:
1200 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1201 server = self.server_proxy(hostname, CM_PORT, self.key_file, \
1202 self.cert_file, self.options.debug)
1203 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1204 self.logger.info("Success")
1205 except socket.gaierror:
1206 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1207 except Exception, e:
1208 self.logger.log_exc(e.message)
1211 def create_gid(self, opts, args):
1213 Create a GID (CreateGid)
1218 target_hrn = args[0]
1219 user_cred = self.get_user_cred().save_to_string(save_parents=True)
1220 gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
1222 filename = opts.file
1224 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1225 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1226 GID(string=gid).save_to_file(filename)
1229 def delegate(self, opts, args):
1231 (locally) create delegate credential for use by given hrn
1233 delegee_hrn = args[0]
1234 if opts.delegate_user:
1235 user_cred = self.get_user_cred()
1236 cred = self.delegate_cred(user_cred, delegee_hrn)
1237 elif opts.delegate_slice:
1238 slice_cred = self.get_slice_cred(opts.delegate_slice)
1239 cred = self.delegate_cred(slice_cred, delegee_hrn)
1241 self.logger.warning("Must specify either --user or --slice <hrn>")
1243 delegated_cred = Credential(string=cred)
1244 object_hrn = delegated_cred.get_gid_object().get_hrn()
1245 if opts.delegate_user:
1246 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1247 + get_leaf(object_hrn) + ".cred")
1248 elif opts.delegate_slice:
1249 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1250 + get_leaf(object_hrn) + ".cred")
1252 delegated_cred.save_to_file(dest_fn, save_parents=True)
1254 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1256 def get_trusted_certs(self, opts, args):
1258 return uhe trusted certs at this interface (get_trusted_certs)
1260 trusted_certs = self.registry.get_trusted_certs()
1261 for trusted_cert in trusted_certs:
1262 gid = GID(string=trusted_cert)
1264 cert = Certificate(string=trusted_cert)
1265 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())