3 # sfi -- slice-based facility interface
5 # xxx NOTE this will soon be reviewed to take advantage of sfaclientlib
15 from lxml import etree
16 from StringIO import StringIO
17 from optparse import OptionParser
19 from sfa.trust.certificate import Keypair, Certificate
20 from sfa.trust.gid import GID
21 from sfa.trust.credential import Credential
22 from sfa.trust.sfaticket import SfaTicket
24 from sfa.util.sfalogging import sfi_logger
25 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn
26 from sfa.util.config import Config
27 from sfa.util.version import version_core
28 from sfa.util.cache import Cache
30 from sfa.storage.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
32 from sfa.rspecs.rspec import RSpec
33 from sfa.rspecs.rspec_converter import RSpecConverter
34 from sfa.rspecs.version_manager import VersionManager
35 from sfa.client.return_value import ReturnValue
37 import sfa.client.sfaprotocol as sfaprotocol
38 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
43 # utility methods here
45 def display_rspec(rspec, format='rspec'):
47 tree = etree.parse(StringIO(rspec))
49 result = root.xpath("./network/site/node/hostname/text()")
50 elif format in ['ip']:
51 # The IP address is not yet part of the new RSpec
52 # so this doesn't do anything yet.
53 tree = etree.parse(StringIO(rspec))
55 result = root.xpath("./network/site/node/ipv4/text()")
62 def display_list(results):
63 for result in results:
66 def display_records(recordList, dump=False):
67 ''' Print all fields in the record'''
68 for record in recordList:
69 display_record(record, dump)
71 def display_record(record, dump=False):
75 info = record.getdict()
76 print "%s (%s)" % (info['hrn'], info['type'])
80 def filter_records(type, records):
82 for record in records:
83 if (record['type'] == type) or (type == "all"):
84 filtered_records.append(record)
85 return filtered_records
89 def save_variable_to_file(var, filename, format="text"):
90 f = open(filename, "w")
93 elif format == "pickled":
94 f.write(pickle.dumps(var))
96 # this should never happen
97 print "unknown output format", format
100 def save_rspec_to_file(rspec, filename):
101 if not filename.endswith(".rspec"):
102 filename = filename + ".rspec"
103 f = open(filename, 'w')
108 def save_records_to_file(filename, recordList, format="xml"):
111 for record in recordList:
113 save_record_to_file(filename + "." + str(index), record)
115 save_record_to_file(filename, record)
117 elif format == "xmllist":
118 f = open(filename, "w")
119 f.write("<recordlist>\n")
120 for record in recordList:
121 record = SfaRecord(dict=record)
122 f.write('<record hrn="' + record.get_name() + '" type="' + record.get_type() + '" />\n')
123 f.write("</recordlist>\n")
125 elif format == "hrnlist":
126 f = open(filename, "w")
127 for record in recordList:
128 record = SfaRecord(dict=record)
129 f.write(record.get_name() + "\n")
132 # this should never happen
133 print "unknown output format", format
135 def save_record_to_file(filename, record):
136 if record['type'] in ['user']:
137 record = UserRecord(dict=record)
138 elif record['type'] in ['slice']:
139 record = SliceRecord(dict=record)
140 elif record['type'] in ['node']:
141 record = NodeRecord(dict=record)
142 elif record['type'] in ['authority', 'ma', 'sa']:
143 record = AuthorityRecord(dict=record)
145 record = SfaRecord(dict=record)
146 str = record.save_to_string()
147 f=codecs.open(filename, encoding='utf-8',mode="w")
154 def load_record_from_file(filename):
155 f=codecs.open(filename, encoding="utf-8", mode="r")
158 record = SfaRecord(string=str)
163 def unique_call_id(): return uuid.uuid4().urn
167 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user']
170 def default_sfi_dir ():
171 if os.path.isfile("./sfi_config"):
174 return os.path.expanduser("~/.sfi/")
176 # dummy to meet Sfi's expectations for its 'options' field
177 # i.e. s/t we can do setattr on
181 def __init__ (self,options=None):
182 if options is None: options=Sfi.DummyOptions()
183 for opt in Sfi.required_options:
184 if not hasattr(options,opt): setattr(options,opt,None)
185 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
186 self.options = options
190 self.authority = None
191 self.hashrequest = False
192 self.logger = sfi_logger
193 self.logger.enable_console()
194 self.available_names = [ tuple[0] for tuple in Sfi.available ]
195 self.available_dict = dict (Sfi.available)
197 # tuples command-name expected-args in the order in which they should appear in the help
200 ("list", "authority"),
203 ("update", "record"),
206 ("resources", "[slice_hrn]"),
207 ("create", "slice_hrn rspec"),
208 ("delete", "slice_hrn"),
209 ("status", "slice_hrn"),
210 ("start", "slice_hrn"),
211 ("stop", "slice_hrn"),
212 ("reset", "slice_hrn"),
213 ("renew", "slice_hrn time"),
214 ("shutdown", "slice_hrn"),
215 ("get_ticket", "slice_hrn rspec"),
216 ("redeem_ticket", "ticket"),
217 ("delegate", "name"),
218 ("create_gid", "[name]"),
219 ("get_trusted_certs", "cred"),
222 def print_command_help (self, options):
223 verbose=getattr(options,'verbose')
224 format3="%18s %-15s %s"
227 print format3%("command","cmd_args","description")
231 self.create_parser().print_help()
232 for command in self.available_names:
233 args=self.available_dict[command]
234 method=getattr(self,command,None)
236 if method: doc=getattr(method,'__doc__',"")
237 if not doc: doc="*** no doc found ***"
238 doc=doc.strip(" \t\n")
239 doc=doc.replace("\n","\n"+35*' ')
242 print format3%(command,args,doc)
244 self.create_cmd_parser(command).print_help()
246 def create_cmd_parser(self, command):
247 if command not in self.available_dict:
248 msg="Invalid command\n"
250 msg += ','.join(self.available_names)
251 self.logger.critical(msg)
254 parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \
255 % (command, self.available_dict[command]))
257 # user specifies remote aggregate/sm/component
258 if command in ("resources", "slices", "create", "delete", "start", "stop",
259 "restart", "shutdown", "get_ticket", "renew", "status"):
260 parser.add_option("-a", "--aggregate", dest="aggregate",
261 default=None, help="aggregate host")
262 parser.add_option("-p", "--port", dest="port",
263 default=AGGREGATE_PORT, help="aggregate port")
264 parser.add_option("-c", "--component", dest="component", default=None,
265 help="component hrn")
266 parser.add_option("-d", "--delegate", dest="delegate", default=None,
268 help="Include a credential delegated to the user's root"+\
269 "authority in set of credentials for this call")
271 # registy filter option
272 if command in ("list", "show", "remove"):
273 parser.add_option("-t", "--type", dest="type", type="choice",
274 help="type filter ([all]|user|slice|authority|node|aggregate)",
275 choices=("all", "user", "slice", "authority", "node", "aggregate"),
278 if command in ("resources"):
279 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
280 help="schema type and version of resulting RSpec")
281 parser.add_option("-f", "--format", dest="format", type="choice",
282 help="display format ([xml]|dns|ip)", default="xml",
283 choices=("xml", "dns", "ip"))
284 #panos: a new option to define the type of information about resources a user is interested in
285 parser.add_option("-i", "--info", dest="info",
286 help="optional component information", default=None)
289 # 'create' does return the new rspec, makes sense to save that too
290 if command in ("resources", "show", "list", "create_gid", 'create'):
291 parser.add_option("-o", "--output", dest="file",
292 help="output XML to file", metavar="FILE", default=None)
294 if command in ("show", "list"):
295 parser.add_option("-f", "--format", dest="format", type="choice",
296 help="display format ([text]|xml)", default="text",
297 choices=("text", "xml"))
299 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
300 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
301 choices=("xml", "xmllist", "hrnlist"))
303 if command in ("status", "version"):
304 parser.add_option("-o", "--output", dest="file",
305 help="output dictionary to file", metavar="FILE", default=None)
306 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
307 help="output file format ([text]|pickled)", default="text",
308 choices=("text","pickled"))
310 if command in ("delegate"):
311 parser.add_option("-u", "--user",
312 action="store_true", dest="delegate_user", default=False,
313 help="delegate user credential")
314 parser.add_option("-s", "--slice", dest="delegate_slice",
315 help="delegate slice credential", metavar="HRN", default=None)
317 if command in ("version"):
318 parser.add_option("-a", "--aggregate", dest="aggregate",
319 default=None, help="aggregate host")
320 parser.add_option("-p", "--port", dest="port",
321 default=AGGREGATE_PORT, help="aggregate port")
322 parser.add_option("-R","--registry-version",
323 action="store_true", dest="version_registry", default=False,
324 help="probe registry version instead of slicemgr")
325 parser.add_option("-l","--local",
326 action="store_true", dest="version_local", default=False,
327 help="display version of the local client")
332 def create_parser(self):
334 # Generate command line parser
335 parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
336 description="Commands: %s"%(" ".join(self.available_names)))
337 parser.add_option("-r", "--registry", dest="registry",
338 help="root registry", metavar="URL", default=None)
339 parser.add_option("-s", "--slicemgr", dest="sm",
340 help="slice manager", metavar="URL", default=None)
341 parser.add_option("-d", "--dir", dest="sfi_dir",
342 help="config & working directory - default is %default",
343 metavar="PATH", default=Sfi.default_sfi_dir())
344 parser.add_option("-u", "--user", dest="user",
345 help="user name", metavar="HRN", default=None)
346 parser.add_option("-a", "--auth", dest="auth",
347 help="authority name", metavar="HRN", default=None)
348 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
349 help="verbose mode - cumulative")
350 parser.add_option("-D", "--debug",
351 action="store_true", dest="debug", default=False,
352 help="Debug (xml-rpc) protocol messages")
353 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
354 help="RPC protocol (xmlrpc or soap)")
355 parser.add_option("-k", "--hashrequest",
356 action="store_true", dest="hashrequest", default=False,
357 help="Create a hash of the request that will be authenticated on the server")
358 parser.add_option("-t", "--timeout", dest="timeout", default=None,
359 help="Amout of time to wait before timing out the request")
360 parser.add_option("-?", "--commands",
361 action="store_true", dest="command_help", default=False,
362 help="one page summary on commands & exit")
363 parser.disable_interspersed_args()
368 def print_help (self):
369 self.sfi_parser.print_help()
370 self.cmd_parser.print_help()
373 # Main: parse arguments and dispatch to command
375 def dispatch(self, command, cmd_opts, cmd_args):
376 return getattr(self, command)(cmd_opts, cmd_args)
379 self.sfi_parser = self.create_parser()
380 (options, args) = self.sfi_parser.parse_args()
381 if options.command_help:
382 self.print_command_help(options)
384 self.options = options
386 self.logger.setLevelFromOptVerbose(self.options.verbose)
387 if options.hashrequest:
388 self.hashrequest = True
391 self.logger.critical("No command given. Use -h for help.")
392 self.print_command_help(options)
396 self.cmd_parser = self.create_cmd_parser(command)
397 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
400 self.logger.info("Command=%s" % command)
401 if command in ("resources"):
402 self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
403 elif command in ("list", "show", "remove"):
404 self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
405 self.logger.debug('cmd_args %s' % cmd_args)
408 self.dispatch(command, cmd_opts, cmd_args)
410 self.logger.critical ("Unknown command %s"%command)
417 def read_config(self):
418 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
420 config = Config (config_file)
422 self.logger.critical("Failed to read configuration file %s"%config_file)
423 self.logger.info("Make sure to remove the export clauses and to add quotes")
424 if self.options.verbose==0:
425 self.logger.info("Re-run with -v for more details")
427 self.logger.log_exc("Could not read config file %s"%config_file)
432 if (self.options.sm is not None):
433 self.sm_url = self.options.sm
434 elif hasattr(config, "SFI_SM"):
435 self.sm_url = config.SFI_SM
437 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
441 if (self.options.registry is not None):
442 self.reg_url = self.options.registry
443 elif hasattr(config, "SFI_REGISTRY"):
444 self.reg_url = config.SFI_REGISTRY
446 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
451 if (self.options.user is not None):
452 self.user = self.options.user
453 elif hasattr(config, "SFI_USER"):
454 self.user = config.SFI_USER
456 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
460 if (self.options.auth is not None):
461 self.authority = self.options.auth
462 elif hasattr(config, "SFI_AUTH"):
463 self.authority = config.SFI_AUTH
465 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
473 # Establish Connection to SliceMgr and Registry Servers
475 def set_servers(self):
478 # Get key and certificate
479 key_file = self.get_key_file()
480 cert_file = self.get_cert_file(key_file)
481 self.key_file = key_file
482 self.cert_file = cert_file
483 self.cert = GID(filename=cert_file)
484 self.logger.info("Contacting Registry at: %s"%self.reg_url)
485 self.registry = sfaprotocol.server_proxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
486 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
487 self.slicemgr = sfaprotocol.server_proxy(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
490 def get_cached_server_version(self, server):
491 # check local cache first
494 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
495 cache_key = server.url + "-version"
497 cache = Cache(cache_file)
500 self.logger.info("Local cache not found at: %s" % cache_file)
503 version = cache.get(cache_key)
506 result = server.GetVersion()
507 version= ReturnValue.get_value(result)
508 # cache version for 24 hours
509 cache.add(cache_key, version, ttl= 60*60*24)
510 self.logger.info("Updating cache file %s" % cache_file)
511 cache.save_to_file(cache_file)
516 def server_supports_options_arg(self, server):
518 Returns true if server support the optional call_id arg, false otherwise.
520 server_version = self.get_cached_server_version(server)
521 if 'sfa' in server_version and 'code_tag' in server_version:
522 code_tag = server_version['code_tag']
523 code_tag_parts = code_tag.split("-")
525 version_parts = code_tag_parts[0].split(".")
526 major, minor = version_parts[0], version_parts[1]
527 rev = code_tag_parts[1]
534 # Get various credential and spec files
536 # Establishes limiting conventions
537 # - conflates MAs and SAs
538 # - assumes last token in slice name is unique
540 # Bootstraps credentials
541 # - bootstrap user credential from self-signed certificate
542 # - bootstrap authority credential from user credential
543 # - bootstrap slice credential from user credential
547 def get_key_file(self):
548 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
549 if (os.path.isfile(file)):
552 self.logger.error("Key file %s does not exist"%file)
556 def get_cert_file(self, key_file):
558 cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
559 if (os.path.isfile(cert_file)):
560 # we'd perfer to use Registry issued certs instead of self signed certs.
561 # if this is a Registry cert (GID) then we are done
562 gid = GID(filename=cert_file)
566 # generate self signed certificate
567 k = Keypair(filename=key_file)
568 cert = Certificate(subject=self.user)
570 cert.set_issuer(k, self.user)
572 self.logger.info("Writing self-signed certificate to %s"%cert_file)
573 cert.save_to_file(cert_file)
575 # try to get registry issued cert
577 self.logger.info("Getting Registry issued cert")
579 # *hack. need to set registry before _get_gid() is called
580 self.registry = sfaprotocol.server_proxy(self.reg_url, key_file, cert_file,
581 timeout=self.options.timeout, verbose=self.options.debug)
582 gid = self._get_gid(type='user')
584 self.logger.info("Writing certificate to %s"%cert_file)
585 gid.save_to_file(cert_file)
587 self.logger.info("Failed to download Registry issued cert")
591 def get_cached_gid(self, file):
596 if (os.path.isfile(file)):
597 gid = GID(filename=file)
602 # def get_gid(self, opts, args):
603 # """ Get the specify gid and save it to file """
607 # gid = self._get_gid(hrn)
608 # self.logger.debug("Sfi.get_gid-> %s" % gid.save_to_string(save_parents=True))
611 def _get_gid(self, hrn=None, type=None):
613 git_gid helper. Retrive the gid from the registry and save it to file.
619 gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
620 gid = self.get_cached_gid(gidfile)
622 user_cred = self.get_user_cred()
623 records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
625 raise RecordNotFound(args[0])
630 if type == rec['type']:
633 raise RecordNotFound(args[0])
635 gid = GID(string=record['gid'])
636 self.logger.info("Writing gid to %s"%gidfile)
637 gid.save_to_file(filename=gidfile)
641 def get_cached_credential(self, file):
643 Return a cached credential only if it hasn't expired.
645 if (os.path.isfile(file)):
646 credential = Credential(filename=file)
647 # make sure it isnt expired
648 if not credential.get_expiration or \
649 datetime.datetime.today() < credential.get_expiration():
653 def get_user_cred(self):
654 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
655 return self.get_cred(file, 'user', self.user)
657 def get_auth_cred(self):
658 if not self.authority:
659 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
661 file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
662 return self.get_cred(file, 'authority', self.authority)
664 def get_slice_cred(self, name):
665 file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
666 return self.get_cred(file, 'slice', name)
668 def get_cred(self, file, type, hrn):
669 # attempt to load a cached credential
670 cred = self.get_cached_credential(file)
673 cert_string = self.cert.save_to_string(save_parents=True)
674 user_name = self.user.replace(self.authority + ".", '')
675 if user_name.count(".") > 0:
676 user_name = user_name.replace(".", '_')
677 self.user = self.authority + "." + user_name
678 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
680 # bootstrap slice credential from user credential
681 user_cred = self.get_user_cred().save_to_string(save_parents=True)
682 cred_str = self.registry.GetCredential(user_cred, hrn, type)
685 self.logger.critical("Failed to get %s credential" % type)
688 cred = Credential(string=cred_str)
689 cred.save_to_file(file, save_parents=True)
690 self.logger.info("Writing %s credential to %s" %(type, file))
695 def delegate_cred(self, object_cred, hrn):
696 # the gid and hrn of the object we are delegating
697 if isinstance(object_cred, str):
698 object_cred = Credential(string=object_cred)
699 object_gid = object_cred.get_gid_object()
700 object_hrn = object_gid.get_hrn()
702 if not object_cred.get_privileges().get_all_delegate():
703 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
706 # the delegating user's gid
707 caller_gid = self._get_gid(self.user)
708 caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
710 # the gid of the user who will be delegated to
711 delegee_gid = self._get_gid(hrn)
712 delegee_hrn = delegee_gid.get_hrn()
713 delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
714 delegee_gid.save_to_file(filename=delegee_gidfile)
715 dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
716 return dcred.save_to_string(save_parents=True)
718 ######################################## miscell utilities
719 def get_rspec_file(self, rspec):
720 if (os.path.isabs(rspec)):
723 file = os.path.join(self.options.sfi_dir, rspec)
724 if (os.path.isfile(file)):
727 self.logger.critical("No such rspec file %s"%rspec)
730 def get_record_file(self, record):
731 if (os.path.isabs(record)):
734 file = os.path.join(self.options.sfi_dir, record)
735 if (os.path.isfile(file)):
738 self.logger.critical("No such registry record file %s"%record)
742 def get_component_proxy_from_hrn(self, hrn):
743 # direct connection to the nodes component manager interface
744 user_cred = self.get_user_cred().save_to_string(save_parents=True)
745 records = self.registry.Resolve(hrn, user_cred)
746 records = filter_records('node', records)
748 self.logger.warning("No such component:%r"% opts.component)
751 return self.server_proxy(record['hostname'], CM_PORT, self.key_file, self.cert_file)
753 def server_proxy(self, host, port, keyfile, certfile):
755 Return an instance of an xmlrpc server connection
757 # port is appended onto the domain, before the path. Should look like:
758 # http://domain:port/path
759 host_parts = host.split('/')
760 host_parts[0] = host_parts[0] + ":" + str(port)
761 url = "http://%s" % "/".join(host_parts)
762 return sfaprotocol.server_proxy(url, keyfile, certfile, timeout=self.options.timeout,
763 verbose=self.options.debug)
765 # xxx opts could be retrieved in self.options
766 def server_proxy_from_opts(self, opts):
768 Return instance of an xmlrpc connection to a slice manager, aggregate
769 or component server depending on the specified opts
771 server = self.slicemgr
772 # direct connection to an aggregate
773 if hasattr(opts, 'aggregate') and opts.aggregate:
774 server = self.server_proxy(opts.aggregate, opts.port, self.key_file, self.cert_file)
775 # direct connection to the nodes component manager interface
776 if hasattr(opts, 'component') and opts.component:
777 server = self.get_component_proxy_from_hrn(opts.component)
780 #==========================================================================
781 # Following functions implement the commands
783 # Registry-related commands
784 #==========================================================================
786 def version(self, opts, args):
788 display an SFA server version (GetVersion)
789 or version information about sfi itself
791 if opts.version_local:
792 version=version_core()
794 if opts.version_registry:
797 server = self.server_proxy_from_opts(opts)
798 result = server.GetVersion()
799 version = ReturnValue.get_value(result)
800 for (k,v) in version.iteritems():
801 print "%-20s: %s"%(k,v)
803 save_variable_to_file(version, opts.file, opts.fileformat)
805 def list(self, opts, args):
807 list entries in named authority registry (List)
813 user_cred = self.get_user_cred().save_to_string(save_parents=True)
815 list = self.registry.List(hrn, user_cred)
817 raise Exception, "Not enough parameters for the 'list' command"
819 # filter on person, slice, site, node, etc.
820 # THis really should be in the self.filter_records funct def comment...
821 list = filter_records(opts.type, list)
823 print "%s (%s)" % (record['hrn'], record['type'])
825 save_records_to_file(opts.file, list, opts.fileformat)
828 def show(self, opts, args):
830 show details about named registry record (Resolve)
836 user_cred = self.get_user_cred().save_to_string(save_parents=True)
837 records = self.registry.Resolve(hrn, user_cred)
838 records = filter_records(opts.type, records)
840 self.logger.error("No record of type %s"% opts.type)
841 for record in records:
842 if record['type'] in ['user']:
843 record = UserRecord(dict=record)
844 elif record['type'] in ['slice']:
845 record = SliceRecord(dict=record)
846 elif record['type'] in ['node']:
847 record = NodeRecord(dict=record)
848 elif record['type'].startswith('authority'):
849 record = AuthorityRecord(dict=record)
851 record = SfaRecord(dict=record)
852 if (opts.format == "text"):
855 print record.save_to_string()
857 save_records_to_file(opts.file, records, opts.fileformat)
860 def add(self, opts, args):
861 "add record into registry from xml file (Register)"
862 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
866 record_filepath = args[0]
867 rec_file = self.get_record_file(record_filepath)
868 record = load_record_from_file(rec_file).as_dict()
869 return self.registry.Register(record, auth_cred)
871 def update(self, opts, args):
872 "update record into registry from xml file (Update)"
873 user_cred = self.get_user_cred()
877 rec_file = self.get_record_file(args[0])
878 record = load_record_from_file(rec_file)
879 if record['type'] == "user":
880 if record.get_name() == user_cred.get_gid_object().get_hrn():
881 cred = user_cred.save_to_string(save_parents=True)
883 cred = self.get_auth_cred().save_to_string(save_parents=True)
884 elif record['type'] in ["slice"]:
886 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
887 except sfaprotocol.ServerException, e:
888 # XXX smbaker -- once we have better error return codes, update this
889 # to do something better than a string compare
890 if "Permission error" in e.args[0]:
891 cred = self.get_auth_cred().save_to_string(save_parents=True)
894 elif record.get_type() in ["authority"]:
895 cred = self.get_auth_cred().save_to_string(save_parents=True)
896 elif record.get_type() == 'node':
897 cred = self.get_auth_cred().save_to_string(save_parents=True)
899 raise "unknown record type" + record.get_type()
900 record = record.as_dict()
901 return self.registry.Update(record, cred)
903 def remove(self, opts, args):
904 "remove registry record by name (Remove)"
905 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
913 return self.registry.Remove(hrn, auth_cred, type)
915 # ==================================================================
916 # Slice-related commands
917 # ==================================================================
919 def slices(self, opts, args):
920 "list instantiated slices (ListSlices) - returns urn's"
921 user_cred = self.get_user_cred().save_to_string(save_parents=True)
924 delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
925 creds.append(delegated_cred)
926 server = self.server_proxy_from_opts(opts)
928 if self.server_supports_options_arg(server):
929 options = {'call_id': unique_call_id()}
930 call_args.append(options)
931 result = server.ListSlices(*call_args)
932 value = ReturnValue.get_value(result)
936 # show rspec for named slice
937 def resources(self, opts, args):
939 with no arg, discover available resources,
940 or currently provisioned resources (ListResources)
942 user_cred = self.get_user_cred().save_to_string(save_parents=True)
943 server = self.server_proxy_from_opts(opts)
945 options = {'call_id': unique_call_id()}
946 #panos add info options
948 options['info'] = opts.info
951 cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
953 options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
959 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
960 creds.append(delegated_cred)
961 if opts.rspec_version:
962 version_manager = VersionManager()
963 server_version = self.get_cached_server_version(server)
964 if 'sfa' in server_version:
965 # just request the version the client wants
966 options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
968 # this must be a protogeni aggregate. We should request a v2 ad rspec
969 # regardless of what the client user requested
970 options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
972 options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
974 call_args = [creds, options]
975 result = server.ListResources(*call_args)
976 value = ReturnValue.get_value(result)
977 if opts.file is None:
978 display_rspec(value, opts.format)
980 save_rspec_to_file(value, opts.file)
983 def create(self, opts, args):
985 create or update named slice with given rspec
987 server = self.server_proxy_from_opts(opts)
988 server_version = self.get_cached_server_version(server)
990 slice_urn = hrn_to_urn(slice_hrn, 'slice')
991 user_cred = self.get_user_cred()
992 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
993 delegated_cred = None
994 if server_version.get('interface') == 'slicemgr':
995 # delegate our cred to the slice manager
996 # do not delegate cred to slicemgr...not working at the moment
998 #if server_version.get('hrn'):
999 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
1000 #elif server_version.get('urn'):
1001 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
1003 rspec_file = self.get_rspec_file(args[1])
1004 rspec = open(rspec_file).read()
1006 # need to pass along user keys to the aggregate.
1008 # { urn: urn:publicid:IDN+emulab.net+user+alice
1009 # keys: [<ssh key A>, <ssh key B>]
1012 slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
1013 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1014 slice_record = slice_records[0]
1015 user_hrns = slice_record['researcher']
1016 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1017 user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
1019 if 'sfa' not in server_version:
1020 users = pg_users_arg(user_records)
1021 rspec = RSpec(rspec)
1022 rspec.filter({'component_manager_id': server_version['urn']})
1023 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1024 creds = [slice_cred]
1026 users = sfa_users_arg(user_records, slice_record)
1027 creds = [slice_cred]
1029 creds.append(delegated_cred)
1030 call_args = [slice_urn, creds, rspec, users]
1031 if self.server_supports_options_arg(server):
1032 options = {'call_id': unique_call_id()}
1033 call_args.append(options)
1034 result = server.CreateSliver(*call_args)
1035 value = ReturnValue.get_value(result)
1036 if opts.file is None:
1039 save_rspec_to_file (value, opts.file)
1042 def delete(self, opts, args):
1044 delete named slice (DeleteSliver)
1047 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1048 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1049 creds = [slice_cred]
1051 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1052 creds.append(delegated_cred)
1053 server = self.server_proxy_from_opts(opts)
1054 call_args = [slice_urn, creds]
1055 if self.server_supports_options_arg(server):
1056 options = {'call_id': unique_call_id()}
1057 call_args.append(options)
1058 return server.DeleteSliver(*call_args)
1060 def status(self, opts, args):
1062 retrieve slice status (SliverStatus)
1065 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1066 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1067 creds = [slice_cred]
1069 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1070 creds.append(delegated_cred)
1071 server = self.server_proxy_from_opts(opts)
1072 call_args = [slice_urn, creds]
1073 if self.server_supports_options_arg(server):
1074 options = {'call_id': unique_call_id()}
1075 call_args.append(options)
1076 result = server.SliverStatus(*call_args)
1077 value = ReturnValue.get_value(result)
1080 save_variable_to_file(value, opts.file, opts.fileformat)
1082 def start(self, opts, args):
1084 start named slice (Start)
1087 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1088 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1089 creds = [slice_cred]
1091 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1092 creds.append(delegated_cred)
1093 server = self.server_proxy_from_opts(opts)
1094 return server.Start(slice_urn, creds)
1096 def stop(self, opts, args):
1098 stop named slice (Stop)
1101 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1102 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1103 creds = [slice_cred]
1105 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1106 creds.append(delegated_cred)
1107 server = self.server_proxy_from_opts(opts)
1108 return server.Stop(slice_urn, creds)
1111 def reset(self, opts, args):
1113 reset named slice (reset_slice)
1116 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1117 server = self.server_proxy_from_opts(opts)
1118 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1119 creds = [slice_cred]
1121 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1122 creds.append(delegated_cred)
1123 return server.reset_slice(creds, slice_urn)
1125 def renew(self, opts, args):
1127 renew slice (RenewSliver)
1130 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1131 server = self.server_proxy_from_opts(opts)
1132 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1133 creds = [slice_cred]
1135 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1136 creds.append(delegated_cred)
1139 call_args = [slice_urn, creds, time]
1140 if self.server_supports_options_arg(server):
1141 options = {'call_id': unique_call_id()}
1142 call_args.append(options)
1143 result = server.RenewSliver(*call_args)
1144 value = ReturnValue.get_value(result)
1148 def shutdown(self, opts, args):
1150 shutdown named slice (Shutdown)
1153 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1154 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1155 creds = [slice_cred]
1157 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1158 creds.append(delegated_cred)
1159 server = self.server_proxy_from_opts(opts)
1160 return server.Shutdown(slice_urn, creds)
1163 def get_ticket(self, opts, args):
1165 get a ticket for the specified slice
1167 slice_hrn, rspec_path = args[0], args[1]
1168 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1169 user_cred = self.get_user_cred()
1170 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1171 creds = [slice_cred]
1173 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1174 creds.append(delegated_cred)
1175 rspec_file = self.get_rspec_file(rspec_path)
1176 rspec = open(rspec_file).read()
1177 server = self.server_proxy_from_opts(opts)
1178 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1179 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1180 self.logger.info("writing ticket to %s"%file)
1181 ticket = SfaTicket(string=ticket_string)
1182 ticket.save_to_file(filename=file, save_parents=True)
1184 def redeem_ticket(self, opts, args):
1186 Connects to nodes in a slice and redeems a ticket
1187 (slice hrn is retrieved from the ticket)
1189 ticket_file = args[0]
1191 # get slice hrn from the ticket
1192 # use this to get the right slice credential
1193 ticket = SfaTicket(filename=ticket_file)
1195 slice_hrn = ticket.gidObject.get_hrn()
1196 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1197 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1198 user_cred = self.get_user_cred()
1199 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1201 # get a list of node hostnames from the RSpec
1202 tree = etree.parse(StringIO(ticket.rspec))
1203 root = tree.getroot()
1204 hostnames = root.xpath("./network/site/node/hostname/text()")
1206 # create an xmlrpc connection to the component manager at each of these
1207 # components and gall redeem_ticket
1209 for hostname in hostnames:
1211 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1212 server = self.server_proxy(hostname, CM_PORT, self.key_file, \
1213 self.cert_file, self.options.debug)
1214 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1215 self.logger.info("Success")
1216 except socket.gaierror:
1217 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1218 except Exception, e:
1219 self.logger.log_exc(e.message)
1222 def create_gid(self, opts, args):
1224 Create a GID (CreateGid)
1229 target_hrn = args[0]
1230 user_cred = self.get_user_cred().save_to_string(save_parents=True)
1231 gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
1233 filename = opts.file
1235 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1236 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1237 GID(string=gid).save_to_file(filename)
1240 def delegate(self, opts, args):
1242 (locally) create delegate credential for use by given hrn
1244 delegee_hrn = args[0]
1245 if opts.delegate_user:
1246 user_cred = self.get_user_cred()
1247 cred = self.delegate_cred(user_cred, delegee_hrn)
1248 elif opts.delegate_slice:
1249 slice_cred = self.get_slice_cred(opts.delegate_slice)
1250 cred = self.delegate_cred(slice_cred, delegee_hrn)
1252 self.logger.warning("Must specify either --user or --slice <hrn>")
1254 delegated_cred = Credential(string=cred)
1255 object_hrn = delegated_cred.get_gid_object().get_hrn()
1256 if opts.delegate_user:
1257 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1258 + get_leaf(object_hrn) + ".cred")
1259 elif opts.delegate_slice:
1260 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1261 + get_leaf(object_hrn) + ".cred")
1263 delegated_cred.save_to_file(dest_fn, save_parents=True)
1265 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1267 def get_trusted_certs(self, opts, args):
1269 return uhe trusted certs at this interface (get_trusted_certs)
1271 trusted_certs = self.registry.get_trusted_certs()
1272 for trusted_cert in trusted_certs:
1273 gid = GID(string=trusted_cert)
1275 cert = Certificate(string=trusted_cert)
1276 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
1279 if __name__ == "__main__":