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
32 from sfa.client.return_value import ReturnValue
34 from sfa.client.sfaserverproxy import SfaServerProxy, ServerException
35 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
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_options_arg(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 if 'sfa' in server_version and 'code_tag' in server_version:
519 code_tag = server_version['code_tag']
520 code_tag_parts = code_tag.split("-")
522 version_parts = code_tag_parts[0].split(".")
523 major, minor = version_parts[0], version_parts[1]
524 rev = code_tag_parts[1]
531 # Get various credential and spec files
533 # Establishes limiting conventions
534 # - conflates MAs and SAs
535 # - assumes last token in slice name is unique
537 # Bootstraps credentials
538 # - bootstrap user credential from self-signed certificate
539 # - bootstrap authority credential from user credential
540 # - bootstrap slice credential from user credential
544 def get_key_file(self):
545 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
546 if (os.path.isfile(file)):
549 self.logger.error("Key file %s does not exist"%file)
553 def get_cert_file(self, key_file):
555 cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
556 if (os.path.isfile(cert_file)):
557 # we'd perfer to use Registry issued certs instead of self signed certs.
558 # if this is a Registry cert (GID) then we are done
559 gid = GID(filename=cert_file)
563 # generate self signed certificate
564 k = Keypair(filename=key_file)
565 cert = Certificate(subject=self.user)
567 cert.set_issuer(k, self.user)
569 self.logger.info("Writing self-signed certificate to %s"%cert_file)
570 cert.save_to_file(cert_file)
572 # try to get registry issued cert
574 self.logger.info("Getting Registry issued cert")
576 # *hack. need to set registry before _get_gid() is called
577 self.registry = SfaServerProxy(self.reg_url, key_file, cert_file,
578 timeout=self.options.timeout, verbose=self.options.debug)
579 gid = self._get_gid(type='user')
581 self.logger.info("Writing certificate to %s"%cert_file)
582 gid.save_to_file(cert_file)
584 self.logger.info("Failed to download Registry issued cert")
588 def get_cached_gid(self, file):
593 if (os.path.isfile(file)):
594 gid = GID(filename=file)
599 # def get_gid(self, opts, args):
600 # """ Get the specify gid and save it to file """
604 # gid = self._get_gid(hrn)
605 # self.logger.debug("Sfi.get_gid-> %s" % gid.save_to_string(save_parents=True))
608 def _get_gid(self, hrn=None, type=None):
610 git_gid helper. Retrive the gid from the registry and save it to file.
616 gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
617 gid = self.get_cached_gid(gidfile)
619 user_cred = self.get_user_cred()
620 records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
622 raise RecordNotFound(args[0])
627 if type == rec['type']:
630 raise RecordNotFound(args[0])
632 gid = GID(string=record['gid'])
633 self.logger.info("Writing gid to %s"%gidfile)
634 gid.save_to_file(filename=gidfile)
638 def get_cached_credential(self, file):
640 Return a cached credential only if it hasn't expired.
642 if (os.path.isfile(file)):
643 credential = Credential(filename=file)
644 # make sure it isnt expired
645 if not credential.get_expiration or \
646 datetime.datetime.today() < credential.get_expiration():
650 def get_user_cred(self):
651 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
652 return self.get_cred(file, 'user', self.user)
654 def get_auth_cred(self):
655 if not self.authority:
656 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
658 file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
659 return self.get_cred(file, 'authority', self.authority)
661 def get_slice_cred(self, name):
662 file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
663 return self.get_cred(file, 'slice', name)
665 def get_cred(self, file, type, hrn):
666 # attempt to load a cached credential
667 cred = self.get_cached_credential(file)
670 cert_string = self.cert.save_to_string(save_parents=True)
671 user_name = self.user.replace(self.authority + ".", '')
672 if user_name.count(".") > 0:
673 user_name = user_name.replace(".", '_')
674 self.user = self.authority + "." + user_name
675 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
677 # bootstrap slice credential from user credential
678 user_cred = self.get_user_cred().save_to_string(save_parents=True)
679 cred_str = self.registry.GetCredential(user_cred, hrn, type)
682 self.logger.critical("Failed to get %s credential" % type)
685 cred = Credential(string=cred_str)
686 cred.save_to_file(file, save_parents=True)
687 self.logger.info("Writing %s credential to %s" %(type, file))
692 def delegate_cred(self, object_cred, hrn):
693 # the gid and hrn of the object we are delegating
694 if isinstance(object_cred, str):
695 object_cred = Credential(string=object_cred)
696 object_gid = object_cred.get_gid_object()
697 object_hrn = object_gid.get_hrn()
699 if not object_cred.get_privileges().get_all_delegate():
700 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
703 # the delegating user's gid
704 caller_gid = self._get_gid(self.user)
705 caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
707 # the gid of the user who will be delegated to
708 delegee_gid = self._get_gid(hrn)
709 delegee_hrn = delegee_gid.get_hrn()
710 delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
711 delegee_gid.save_to_file(filename=delegee_gidfile)
712 dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
713 return dcred.save_to_string(save_parents=True)
715 ######################################## miscell utilities
716 def get_rspec_file(self, rspec):
717 if (os.path.isabs(rspec)):
720 file = os.path.join(self.options.sfi_dir, rspec)
721 if (os.path.isfile(file)):
724 self.logger.critical("No such rspec file %s"%rspec)
727 def get_record_file(self, record):
728 if (os.path.isabs(record)):
731 file = os.path.join(self.options.sfi_dir, record)
732 if (os.path.isfile(file)):
735 self.logger.critical("No such registry record file %s"%record)
739 def get_component_proxy_from_hrn(self, hrn):
740 # direct connection to the nodes component manager interface
741 user_cred = self.get_user_cred().save_to_string(save_parents=True)
742 records = self.registry.Resolve(hrn, user_cred)
743 records = filter_records('node', records)
745 self.logger.warning("No such component:%r"% opts.component)
748 return self.server_proxy(record['hostname'], CM_PORT, self.key_file, self.cert_file)
750 def server_proxy(self, host, port, keyfile, certfile):
752 Return an instance of an xmlrpc server connection
754 # port is appended onto the domain, before the path. Should look like:
755 # http://domain:port/path
756 host_parts = host.split('/')
757 host_parts[0] = host_parts[0] + ":" + str(port)
758 url = "http://%s" % "/".join(host_parts)
759 return SfaServerProxy(url, keyfile, certfile, timeout=self.options.timeout,
760 verbose=self.options.debug)
762 # xxx opts could be retrieved in self.options
763 def server_proxy_from_opts(self, opts):
765 Return instance of an xmlrpc connection to a slice manager, aggregate
766 or component server depending on the specified opts
768 server = self.slicemgr
769 # direct connection to an aggregate
770 if hasattr(opts, 'aggregate') and opts.aggregate:
771 server = self.server_proxy(opts.aggregate, opts.port, self.key_file, self.cert_file)
772 # direct connection to the nodes component manager interface
773 if hasattr(opts, 'component') and opts.component:
774 server = self.get_component_proxy_from_hrn(opts.component)
777 #==========================================================================
778 # Following functions implement the commands
780 # Registry-related commands
781 #==========================================================================
783 def version(self, opts, args):
785 display an SFA server version (GetVersion)
786 or version information about sfi itself
788 if opts.version_local:
789 version=version_core()
791 if opts.version_registry:
794 server = self.server_proxy_from_opts(opts)
795 result = server.GetVersion()
796 version = ReturnValue.get_value(result)
797 for (k,v) in version.iteritems():
798 print "%-20s: %s"%(k,v)
800 save_variable_to_file(version, opts.file, opts.fileformat)
802 def list(self, opts, args):
804 list entries in named authority registry (List)
810 user_cred = self.get_user_cred().save_to_string(save_parents=True)
812 list = self.registry.List(hrn, user_cred)
814 raise Exception, "Not enough parameters for the 'list' command"
816 # filter on person, slice, site, node, etc.
817 # THis really should be in the self.filter_records funct def comment...
818 list = filter_records(opts.type, list)
820 print "%s (%s)" % (record['hrn'], record['type'])
822 save_records_to_file(opts.file, list, opts.fileformat)
825 def show(self, opts, args):
827 show details about named registry record (Resolve)
833 user_cred = self.get_user_cred().save_to_string(save_parents=True)
834 records = self.registry.Resolve(hrn, user_cred)
835 records = filter_records(opts.type, records)
837 self.logger.error("No record of type %s"% opts.type)
838 for record in records:
839 if record['type'] in ['user']:
840 record = UserRecord(dict=record)
841 elif record['type'] in ['slice']:
842 record = SliceRecord(dict=record)
843 elif record['type'] in ['node']:
844 record = NodeRecord(dict=record)
845 elif record['type'].startswith('authority'):
846 record = AuthorityRecord(dict=record)
848 record = SfaRecord(dict=record)
849 if (opts.format == "text"):
852 print record.save_to_string()
854 save_records_to_file(opts.file, records, opts.fileformat)
857 def add(self, opts, args):
858 "add record into registry from xml file (Register)"
859 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
863 record_filepath = args[0]
864 rec_file = self.get_record_file(record_filepath)
865 record = load_record_from_file(rec_file).as_dict()
866 return self.registry.Register(record, auth_cred)
868 def update(self, opts, args):
869 "update record into registry from xml file (Update)"
870 user_cred = self.get_user_cred()
874 rec_file = self.get_record_file(args[0])
875 record = load_record_from_file(rec_file)
876 if record['type'] == "user":
877 if record.get_name() == user_cred.get_gid_object().get_hrn():
878 cred = user_cred.save_to_string(save_parents=True)
880 cred = self.get_auth_cred().save_to_string(save_parents=True)
881 elif record['type'] in ["slice"]:
883 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
884 except ServerException, e:
885 # XXX smbaker -- once we have better error return codes, update this
886 # to do something better than a string compare
887 if "Permission error" in e.args[0]:
888 cred = self.get_auth_cred().save_to_string(save_parents=True)
891 elif record.get_type() in ["authority"]:
892 cred = self.get_auth_cred().save_to_string(save_parents=True)
893 elif record.get_type() == 'node':
894 cred = self.get_auth_cred().save_to_string(save_parents=True)
896 raise "unknown record type" + record.get_type()
897 record = record.as_dict()
898 return self.registry.Update(record, cred)
900 def remove(self, opts, args):
901 "remove registry record by name (Remove)"
902 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
910 return self.registry.Remove(hrn, auth_cred, type)
912 # ==================================================================
913 # Slice-related commands
914 # ==================================================================
916 def slices(self, opts, args):
917 "list instantiated slices (ListSlices) - returns urn's"
918 user_cred = self.get_user_cred().save_to_string(save_parents=True)
921 delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
922 creds.append(delegated_cred)
923 server = self.server_proxy_from_opts(opts)
925 if self.server_supports_options_arg(server):
926 options = {'call_id': unique_call_id()}
927 call_args.append(options)
928 result = server.ListSlices(*call_args)
929 value = ReturnValue.get_value(result)
933 # show rspec for named slice
934 def resources(self, opts, args):
936 with no arg, discover available resources,
937 or currently provisioned resources (ListResources)
939 user_cred = self.get_user_cred().save_to_string(save_parents=True)
940 server = self.server_proxy_from_opts(opts)
942 options = {'call_id': unique_call_id()}
943 #panos add info options
945 options['info'] = opts.info
948 cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
950 options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
956 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
957 creds.append(delegated_cred)
958 if opts.rspec_version:
959 version_manager = VersionManager()
960 server_version = self.get_cached_server_version(server)
961 if 'sfa' in server_version:
962 # just request the version the client wants
963 options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
965 # this must be a protogeni aggregate. We should request a v2 ad rspec
966 # regardless of what the client user requested
967 options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
969 options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
971 call_args = [creds, options]
972 result = server.ListResources(*call_args)
973 value = ReturnValue.get_value(result)
974 if opts.file is None:
975 display_rspec(value, opts.format)
977 save_rspec_to_file(value, opts.file)
980 def create(self, opts, args):
982 create or update named slice with given rspec
984 server = self.server_proxy_from_opts(opts)
985 server_version = self.get_cached_server_version(server)
987 slice_urn = hrn_to_urn(slice_hrn, 'slice')
988 user_cred = self.get_user_cred()
989 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
990 delegated_cred = None
991 if server_version.get('interface') == 'slicemgr':
992 # delegate our cred to the slice manager
993 # do not delegate cred to slicemgr...not working at the moment
995 #if server_version.get('hrn'):
996 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
997 #elif server_version.get('urn'):
998 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1000 rspec_file = self.get_rspec_file(args[1])
1001 rspec = open(rspec_file).read()
1003 # need to pass along user keys to the aggregate.
1005 # { urn: urn:publicid:IDN+emulab.net+user+alice
1006 # keys: [<ssh key A>, <ssh key B>]
1009 slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
1010 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1011 slice_record = slice_records[0]
1012 user_hrns = slice_record['researcher']
1013 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1014 user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
1016 if 'sfa' not in server_version:
1017 users = pg_users_arg(user_records)
1018 rspec = RSpec(rspec)
1019 rspec.filter({'component_manager_id': server_version['urn']})
1020 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1021 creds = [slice_cred]
1023 users = sfa_users_arg(user_records, slice_record)
1024 creds = [slice_cred]
1026 creds.append(delegated_cred)
1027 # do not append users, keys, or slice tags. Anything
1028 # not contained in this request will be removed from the slice
1029 options = {'append': False}
1030 if self.server_supports_options_arg(server):
1031 options['call_id'] = unique_call_id()
1032 call_args = [slice_urn, creds, rspec, users, options]
1033 result = server.CreateSliver(*call_args)
1034 value = ReturnValue.get_value(result)
1035 if opts.file is None:
1038 save_rspec_to_file (value, opts.file)
1041 def delete(self, opts, args):
1043 delete named slice (DeleteSliver)
1046 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1047 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1048 creds = [slice_cred]
1050 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1051 creds.append(delegated_cred)
1052 server = self.server_proxy_from_opts(opts)
1053 call_args = [slice_urn, creds]
1054 if self.server_supports_options_arg(server):
1055 options = {'call_id': unique_call_id()}
1056 call_args.append(options)
1057 return server.DeleteSliver(*call_args)
1059 def status(self, opts, args):
1061 retrieve slice status (SliverStatus)
1064 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1065 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1066 creds = [slice_cred]
1068 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1069 creds.append(delegated_cred)
1070 server = self.server_proxy_from_opts(opts)
1071 call_args = [slice_urn, creds]
1072 if self.server_supports_options_arg(server):
1073 options = {'call_id': unique_call_id()}
1074 call_args.append(options)
1075 result = server.SliverStatus(*call_args)
1076 value = ReturnValue.get_value(result)
1079 save_variable_to_file(value, opts.file, opts.fileformat)
1081 def start(self, opts, args):
1083 start named slice (Start)
1086 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1087 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1088 creds = [slice_cred]
1090 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1091 creds.append(delegated_cred)
1092 server = self.server_proxy_from_opts(opts)
1093 return server.Start(slice_urn, creds)
1095 def stop(self, opts, args):
1097 stop named slice (Stop)
1100 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1101 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1102 creds = [slice_cred]
1104 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1105 creds.append(delegated_cred)
1106 server = self.server_proxy_from_opts(opts)
1107 return server.Stop(slice_urn, creds)
1110 def reset(self, opts, args):
1112 reset named slice (reset_slice)
1115 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1116 server = self.server_proxy_from_opts(opts)
1117 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1118 creds = [slice_cred]
1120 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1121 creds.append(delegated_cred)
1122 return server.reset_slice(creds, slice_urn)
1124 def renew(self, opts, args):
1126 renew slice (RenewSliver)
1129 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1130 server = self.server_proxy_from_opts(opts)
1131 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1132 creds = [slice_cred]
1134 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1135 creds.append(delegated_cred)
1138 call_args = [slice_urn, creds, time]
1139 if self.server_supports_options_arg(server):
1140 options = {'call_id': unique_call_id()}
1141 call_args.append(options)
1142 result = server.RenewSliver(*call_args)
1143 value = ReturnValue.get_value(result)
1147 def shutdown(self, opts, args):
1149 shutdown named slice (Shutdown)
1152 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1153 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1154 creds = [slice_cred]
1156 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1157 creds.append(delegated_cred)
1158 server = self.server_proxy_from_opts(opts)
1159 return server.Shutdown(slice_urn, creds)
1162 def get_ticket(self, opts, args):
1164 get a ticket for the specified slice
1166 slice_hrn, rspec_path = args[0], args[1]
1167 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1168 user_cred = self.get_user_cred()
1169 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1170 creds = [slice_cred]
1172 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1173 creds.append(delegated_cred)
1174 rspec_file = self.get_rspec_file(rspec_path)
1175 rspec = open(rspec_file).read()
1176 server = self.server_proxy_from_opts(opts)
1177 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1178 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1179 self.logger.info("writing ticket to %s"%file)
1180 ticket = SfaTicket(string=ticket_string)
1181 ticket.save_to_file(filename=file, save_parents=True)
1183 def redeem_ticket(self, opts, args):
1185 Connects to nodes in a slice and redeems a ticket
1186 (slice hrn is retrieved from the ticket)
1188 ticket_file = args[0]
1190 # get slice hrn from the ticket
1191 # use this to get the right slice credential
1192 ticket = SfaTicket(filename=ticket_file)
1194 slice_hrn = ticket.gidObject.get_hrn()
1195 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1196 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1197 user_cred = self.get_user_cred()
1198 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1200 # get a list of node hostnames from the RSpec
1201 tree = etree.parse(StringIO(ticket.rspec))
1202 root = tree.getroot()
1203 hostnames = root.xpath("./network/site/node/hostname/text()")
1205 # create an xmlrpc connection to the component manager at each of these
1206 # components and gall redeem_ticket
1208 for hostname in hostnames:
1210 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1211 server = self.server_proxy(hostname, CM_PORT, self.key_file, \
1212 self.cert_file, self.options.debug)
1213 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1214 self.logger.info("Success")
1215 except socket.gaierror:
1216 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1217 except Exception, e:
1218 self.logger.log_exc(e.message)
1221 def create_gid(self, opts, args):
1223 Create a GID (CreateGid)
1228 target_hrn = args[0]
1229 user_cred = self.get_user_cred().save_to_string(save_parents=True)
1230 gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
1232 filename = opts.file
1234 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1235 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1236 GID(string=gid).save_to_file(filename)
1239 def delegate(self, opts, args):
1241 (locally) create delegate credential for use by given hrn
1243 delegee_hrn = args[0]
1244 if opts.delegate_user:
1245 user_cred = self.get_user_cred()
1246 cred = self.delegate_cred(user_cred, delegee_hrn)
1247 elif opts.delegate_slice:
1248 slice_cred = self.get_slice_cred(opts.delegate_slice)
1249 cred = self.delegate_cred(slice_cred, delegee_hrn)
1251 self.logger.warning("Must specify either --user or --slice <hrn>")
1253 delegated_cred = Credential(string=cred)
1254 object_hrn = delegated_cred.get_gid_object().get_hrn()
1255 if opts.delegate_user:
1256 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1257 + get_leaf(object_hrn) + ".cred")
1258 elif opts.delegate_slice:
1259 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1260 + get_leaf(object_hrn) + ".cred")
1262 delegated_cred.save_to_file(dest_fn, save_parents=True)
1264 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1266 def get_trusted_certs(self, opts, args):
1268 return uhe trusted certs at this interface (get_trusted_certs)
1270 trusted_certs = self.registry.get_trusted_certs()
1271 for trusted_cert in trusted_certs:
1272 gid = GID(string=trusted_cert)
1274 cert = Certificate(string=trusted_cert)
1275 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())