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 # do not append users, keys, or slice tags. Anything
1031 # not contained in this request will be removed from the slice
1032 options = {'append': False}
1033 if self.server_supports_options_arg(server):
1034 options['call_id'] = unique_call_id()
1035 call_args = [slice_urn, creds, rspec, users, options]
1036 result = server.CreateSliver(*call_args)
1037 value = ReturnValue.get_value(result)
1038 if opts.file is None:
1041 save_rspec_to_file (value, opts.file)
1044 def delete(self, opts, args):
1046 delete named slice (DeleteSliver)
1049 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1050 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1051 creds = [slice_cred]
1053 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1054 creds.append(delegated_cred)
1055 server = self.server_proxy_from_opts(opts)
1056 call_args = [slice_urn, creds]
1057 if self.server_supports_options_arg(server):
1058 options = {'call_id': unique_call_id()}
1059 call_args.append(options)
1060 return server.DeleteSliver(*call_args)
1062 def status(self, opts, args):
1064 retrieve slice status (SliverStatus)
1067 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1068 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1069 creds = [slice_cred]
1071 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1072 creds.append(delegated_cred)
1073 server = self.server_proxy_from_opts(opts)
1074 call_args = [slice_urn, creds]
1075 if self.server_supports_options_arg(server):
1076 options = {'call_id': unique_call_id()}
1077 call_args.append(options)
1078 result = server.SliverStatus(*call_args)
1079 value = ReturnValue.get_value(result)
1082 save_variable_to_file(value, opts.file, opts.fileformat)
1084 def start(self, opts, args):
1086 start named slice (Start)
1089 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1090 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1091 creds = [slice_cred]
1093 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1094 creds.append(delegated_cred)
1095 server = self.server_proxy_from_opts(opts)
1096 return server.Start(slice_urn, creds)
1098 def stop(self, opts, args):
1100 stop named slice (Stop)
1103 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1104 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1105 creds = [slice_cred]
1107 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1108 creds.append(delegated_cred)
1109 server = self.server_proxy_from_opts(opts)
1110 return server.Stop(slice_urn, creds)
1113 def reset(self, opts, args):
1115 reset named slice (reset_slice)
1118 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1119 server = self.server_proxy_from_opts(opts)
1120 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1121 creds = [slice_cred]
1123 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1124 creds.append(delegated_cred)
1125 return server.reset_slice(creds, slice_urn)
1127 def renew(self, opts, args):
1129 renew slice (RenewSliver)
1132 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1133 server = self.server_proxy_from_opts(opts)
1134 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1135 creds = [slice_cred]
1137 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1138 creds.append(delegated_cred)
1141 call_args = [slice_urn, creds, time]
1142 if self.server_supports_options_arg(server):
1143 options = {'call_id': unique_call_id()}
1144 call_args.append(options)
1145 result = server.RenewSliver(*call_args)
1146 value = ReturnValue.get_value(result)
1150 def shutdown(self, opts, args):
1152 shutdown named slice (Shutdown)
1155 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1156 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1157 creds = [slice_cred]
1159 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1160 creds.append(delegated_cred)
1161 server = self.server_proxy_from_opts(opts)
1162 return server.Shutdown(slice_urn, creds)
1165 def get_ticket(self, opts, args):
1167 get a ticket for the specified slice
1169 slice_hrn, rspec_path = args[0], args[1]
1170 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1171 user_cred = self.get_user_cred()
1172 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1173 creds = [slice_cred]
1175 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1176 creds.append(delegated_cred)
1177 rspec_file = self.get_rspec_file(rspec_path)
1178 rspec = open(rspec_file).read()
1179 server = self.server_proxy_from_opts(opts)
1180 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1181 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1182 self.logger.info("writing ticket to %s"%file)
1183 ticket = SfaTicket(string=ticket_string)
1184 ticket.save_to_file(filename=file, save_parents=True)
1186 def redeem_ticket(self, opts, args):
1188 Connects to nodes in a slice and redeems a ticket
1189 (slice hrn is retrieved from the ticket)
1191 ticket_file = args[0]
1193 # get slice hrn from the ticket
1194 # use this to get the right slice credential
1195 ticket = SfaTicket(filename=ticket_file)
1197 slice_hrn = ticket.gidObject.get_hrn()
1198 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1199 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1200 user_cred = self.get_user_cred()
1201 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1203 # get a list of node hostnames from the RSpec
1204 tree = etree.parse(StringIO(ticket.rspec))
1205 root = tree.getroot()
1206 hostnames = root.xpath("./network/site/node/hostname/text()")
1208 # create an xmlrpc connection to the component manager at each of these
1209 # components and gall redeem_ticket
1211 for hostname in hostnames:
1213 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1214 server = self.server_proxy(hostname, CM_PORT, self.key_file, \
1215 self.cert_file, self.options.debug)
1216 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1217 self.logger.info("Success")
1218 except socket.gaierror:
1219 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1220 except Exception, e:
1221 self.logger.log_exc(e.message)
1224 def create_gid(self, opts, args):
1226 Create a GID (CreateGid)
1231 target_hrn = args[0]
1232 user_cred = self.get_user_cred().save_to_string(save_parents=True)
1233 gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
1235 filename = opts.file
1237 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1238 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1239 GID(string=gid).save_to_file(filename)
1242 def delegate(self, opts, args):
1244 (locally) create delegate credential for use by given hrn
1246 delegee_hrn = args[0]
1247 if opts.delegate_user:
1248 user_cred = self.get_user_cred()
1249 cred = self.delegate_cred(user_cred, delegee_hrn)
1250 elif opts.delegate_slice:
1251 slice_cred = self.get_slice_cred(opts.delegate_slice)
1252 cred = self.delegate_cred(slice_cred, delegee_hrn)
1254 self.logger.warning("Must specify either --user or --slice <hrn>")
1256 delegated_cred = Credential(string=cred)
1257 object_hrn = delegated_cred.get_gid_object().get_hrn()
1258 if opts.delegate_user:
1259 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1260 + get_leaf(object_hrn) + ".cred")
1261 elif opts.delegate_slice:
1262 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1263 + get_leaf(object_hrn) + ".cred")
1265 delegated_cred.save_to_file(dest_fn, save_parents=True)
1267 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1269 def get_trusted_certs(self, opts, args):
1271 return uhe trusted certs at this interface (get_trusted_certs)
1273 trusted_certs = self.registry.get_trusted_certs()
1274 for trusted_cert in trusted_certs:
1275 gid = GID(string=trusted_cert)
1277 cert = Certificate(string=trusted_cert)
1278 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
1281 if __name__ == "__main__":