3 # sfi -- slice-based facility interface
5 # xxx NOTE this will soon be reviewed to take advantage of sfaclientlib
16 from lxml import etree
17 from StringIO import StringIO
18 from optparse import OptionParser
20 from sfa.trust.certificate import Keypair, Certificate
21 from sfa.trust.gid import GID
22 from sfa.trust.credential import Credential
23 from sfa.trust.sfaticket import SfaTicket
25 from sfa.util.sfalogging import sfi_logger
26 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn
27 from sfa.util.config import Config
28 from sfa.util.version import version_core
29 from sfa.util.cache import Cache
31 from sfa.storage.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
33 from sfa.rspecs.rspec import RSpec
34 from sfa.rspecs.rspec_converter import RSpecConverter
35 from sfa.rspecs.version_manager import VersionManager
36 from sfa.client.return_value import ReturnValue
38 import sfa.client.sfaprotocol as sfaprotocol
39 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
44 # utility methods here
46 def display_rspec(rspec, format='rspec'):
48 tree = etree.parse(StringIO(rspec))
50 result = root.xpath("./network/site/node/hostname/text()")
51 elif format in ['ip']:
52 # The IP address is not yet part of the new RSpec
53 # so this doesn't do anything yet.
54 tree = etree.parse(StringIO(rspec))
56 result = root.xpath("./network/site/node/ipv4/text()")
63 def display_list(results):
64 for result in results:
67 def display_records(recordList, dump=False):
68 ''' Print all fields in the record'''
69 for record in recordList:
70 display_record(record, dump)
72 def display_record(record, dump=False):
76 info = record.getdict()
77 print "%s (%s)" % (info['hrn'], info['type'])
81 def filter_records(type, records):
83 for record in records:
84 if (record['type'] == type) or (type == "all"):
85 filtered_records.append(record)
86 return filtered_records
90 def save_variable_to_file(var, filename, format="text"):
91 f = open(filename, "w")
94 elif format == "pickled":
95 f.write(pickle.dumps(var))
97 # this should never happen
98 print "unknown output format", format
101 def save_rspec_to_file(rspec, filename):
102 if not filename.endswith(".rspec"):
103 filename = filename + ".rspec"
104 f = open(filename, 'w')
109 def save_records_to_file(filename, recordList, format="xml"):
112 for record in recordList:
114 save_record_to_file(filename + "." + str(index), record)
116 save_record_to_file(filename, record)
118 elif format == "xmllist":
119 f = open(filename, "w")
120 f.write("<recordlist>\n")
121 for record in recordList:
122 record = SfaRecord(dict=record)
123 f.write('<record hrn="' + record.get_name() + '" type="' + record.get_type() + '" />\n')
124 f.write("</recordlist>\n")
126 elif format == "hrnlist":
127 f = open(filename, "w")
128 for record in recordList:
129 record = SfaRecord(dict=record)
130 f.write(record.get_name() + "\n")
133 # this should never happen
134 print "unknown output format", format
136 def save_record_to_file(filename, record):
137 if record['type'] in ['user']:
138 record = UserRecord(dict=record)
139 elif record['type'] in ['slice']:
140 record = SliceRecord(dict=record)
141 elif record['type'] in ['node']:
142 record = NodeRecord(dict=record)
143 elif record['type'] in ['authority', 'ma', 'sa']:
144 record = AuthorityRecord(dict=record)
146 record = SfaRecord(dict=record)
147 str = record.save_to_string()
148 f=codecs.open(filename, encoding='utf-8',mode="w")
155 def load_record_from_file(filename):
156 f=codecs.open(filename, encoding="utf-8", mode="r")
159 record = SfaRecord(string=str)
164 def unique_call_id(): return uuid.uuid4().urn
168 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user']
170 # dummy to meet Sfi's expectations for its 'options' field
171 # i.e. s/t we can do setattr on
175 def __init__ (self,options=None):
176 if options is None: options=Sfi.DummyOptions()
177 for opt in Sfi.required_options:
178 if not hasattr(options,opt): setattr(options,opt,None)
179 if not hasattr(options,'sfi_dir'): options.sfi_dir=os.path.expanduser("~/.sfi/")
180 # xxx oops, this is dangerous, sounds like ww sometimes have discrepency
181 # would be safer to remove self.sfi_dir altogether
182 self.sfi_dir = options.sfi_dir
183 self.options = options
187 self.authority = None
188 self.hashrequest = False
189 self.logger = sfi_logger
190 self.logger.enable_console()
192 def create_cmd_parser(self, command, additional_cmdargs=None):
193 cmdargs = {"list": "authority",
198 "aggregates": "[name]",
199 "registries": "[name]",
200 "create_gid": "[name]",
202 "get_trusted_certs": "cred",
204 "resources": "[name]",
205 "create": "name rspec",
206 "get_ticket": "name rspec",
207 "redeem_ticket": "ticket",
219 if additional_cmdargs:
220 cmdargs.update(additional_cmdargs)
222 if command not in cmdargs:
223 msg="Invalid command\n"
225 msg += ','.join(cmdargs.keys())
226 self.logger.critical(msg)
229 parser = OptionParser(usage="sfi [sfi_options] %s [options] %s" \
230 % (command, cmdargs[command]))
232 # user specifies remote aggregate/sm/component
233 if command in ("resources", "slices", "create", "delete", "start", "stop",
234 "restart", "shutdown", "get_ticket", "renew", "status"):
235 parser.add_option("-a", "--aggregate", dest="aggregate",
236 default=None, help="aggregate host")
237 parser.add_option("-p", "--port", dest="port",
238 default=AGGREGATE_PORT, help="aggregate port")
239 parser.add_option("-c", "--component", dest="component", default=None,
240 help="component hrn")
241 parser.add_option("-d", "--delegate", dest="delegate", default=None,
243 help="Include a credential delegated to the user's root"+\
244 "authority in set of credentials for this call")
246 # registy filter option
247 if command in ("list", "show", "remove"):
248 parser.add_option("-t", "--type", dest="type", type="choice",
249 help="type filter ([all]|user|slice|authority|node|aggregate)",
250 choices=("all", "user", "slice", "authority", "node", "aggregate"),
253 if command in ("resources"):
254 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
255 help="schema type and version of resulting RSpec")
256 parser.add_option("-f", "--format", dest="format", type="choice",
257 help="display format ([xml]|dns|ip)", default="xml",
258 choices=("xml", "dns", "ip"))
259 #panos: a new option to define the type of information about resources a user is interested in
260 parser.add_option("-i", "--info", dest="info",
261 help="optional component information", default=None)
264 # 'create' does return the new rspec, makes sense to save that too
265 if command in ("resources", "show", "list", "create_gid", 'create'):
266 parser.add_option("-o", "--output", dest="file",
267 help="output XML to file", metavar="FILE", default=None)
269 if command in ("show", "list"):
270 parser.add_option("-f", "--format", dest="format", type="choice",
271 help="display format ([text]|xml)", default="text",
272 choices=("text", "xml"))
274 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
275 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
276 choices=("xml", "xmllist", "hrnlist"))
278 if command in ("status", "version"):
279 parser.add_option("-o", "--output", dest="file",
280 help="output dictionary to file", metavar="FILE", default=None)
281 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
282 help="output file format ([text]|pickled)", default="text",
283 choices=("text","pickled"))
285 if command in ("delegate"):
286 parser.add_option("-u", "--user",
287 action="store_true", dest="delegate_user", default=False,
288 help="delegate user credential")
289 parser.add_option("-s", "--slice", dest="delegate_slice",
290 help="delegate slice credential", metavar="HRN", default=None)
292 if command in ("version"):
293 parser.add_option("-a", "--aggregate", dest="aggregate",
294 default=None, help="aggregate host")
295 parser.add_option("-p", "--port", dest="port",
296 default=AGGREGATE_PORT, help="aggregate port")
297 parser.add_option("-R","--registry-version",
298 action="store_true", dest="version_registry", default=False,
299 help="probe registry version instead of slicemgr")
300 parser.add_option("-l","--local",
301 action="store_true", dest="version_local", default=False,
302 help="display version of the local client")
307 def create_parser(self):
309 # Generate command line parser
310 parser = OptionParser(usage="sfi [options] command [command_options] [command_args]",
311 description="Commands: gid,list,show,remove,add,update,nodes,slices,resources,create,delete,start,stop,reset")
312 parser.add_option("-r", "--registry", dest="registry",
313 help="root registry", metavar="URL", default=None)
314 parser.add_option("-s", "--slicemgr", dest="sm",
315 help="slice manager", metavar="URL", default=None)
316 default_sfi_dir = os.path.expanduser("~/.sfi/")
317 parser.add_option("-d", "--dir", dest="sfi_dir",
318 help="config & working directory - default is " + default_sfi_dir,
319 metavar="PATH", default=default_sfi_dir)
320 parser.add_option("-u", "--user", dest="user",
321 help="user name", metavar="HRN", default=None)
322 parser.add_option("-a", "--auth", dest="auth",
323 help="authority name", metavar="HRN", default=None)
324 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
325 help="verbose mode - cumulative")
326 parser.add_option("-D", "--debug",
327 action="store_true", dest="debug", default=False,
328 help="Debug (xml-rpc) protocol messages")
329 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
330 help="RPC protocol (xmlrpc or soap)")
331 parser.add_option("-k", "--hashrequest",
332 action="store_true", dest="hashrequest", default=False,
333 help="Create a hash of the request that will be authenticated on the server")
334 parser.add_option("-t", "--timeout", dest="timeout", default=None,
335 help="Amout of time tom wait before timing out the request")
336 parser.disable_interspersed_args()
341 def read_config(self):
342 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
344 config = Config (config_file)
346 self.logger.critical("Failed to read configuration file %s"%config_file)
347 self.logger.info("Make sure to remove the export clauses and to add quotes")
348 if self.options.verbose==0:
349 self.logger.info("Re-run with -v for more details")
351 self.logger.log_exc("Could not read config file %s"%config_file)
356 if (self.options.sm is not None):
357 self.sm_url = self.options.sm
358 elif hasattr(config, "SFI_SM"):
359 self.sm_url = config.SFI_SM
361 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
365 if (self.options.registry is not None):
366 self.reg_url = self.options.registry
367 elif hasattr(config, "SFI_REGISTRY"):
368 self.reg_url = config.SFI_REGISTRY
370 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
375 if (self.options.user is not None):
376 self.user = self.options.user
377 elif hasattr(config, "SFI_USER"):
378 self.user = config.SFI_USER
380 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
384 if (self.options.auth is not None):
385 self.authority = self.options.auth
386 elif hasattr(config, "SFI_AUTH"):
387 self.authority = config.SFI_AUTH
389 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
397 # Establish Connection to SliceMgr and Registry Servers
399 def set_servers(self):
402 # Get key and certificate
403 key_file = self.get_key_file()
404 cert_file = self.get_cert_file(key_file)
405 self.key_file = key_file
406 self.cert_file = cert_file
407 self.cert = GID(filename=cert_file)
408 self.logger.info("Contacting Registry at: %s"%self.reg_url)
409 self.registry = sfaprotocol.server_proxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
410 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
411 self.slicemgr = sfaprotocol.server_proxy(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
414 def get_cached_server_version(self, server):
415 # check local cache first
418 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
419 cache_key = server.url + "-version"
421 cache = Cache(cache_file)
424 self.logger.info("Local cache not found at: %s" % cache_file)
427 version = cache.get(cache_key)
430 result = server.GetVersion()
431 version= ReturnValue.get_value(result)
432 # cache version for 24 hours
433 cache.add(cache_key, version, ttl= 60*60*24)
434 self.logger.info("Updating cache file %s" % cache_file)
435 cache.save_to_file(cache_file)
440 def server_supports_options_arg(self, server):
442 Returns true if server support the optional call_id arg, false otherwise.
444 server_version = self.get_cached_server_version(server)
445 if 'sfa' in server_version and 'code_tag' in server_version:
446 code_tag = server_version['code_tag']
447 code_tag_parts = code_tag.split("-")
449 version_parts = code_tag_parts[0].split(".")
450 major, minor = version_parts[0], version_parts[1]
451 rev = code_tag_parts[1]
458 # Get various credential and spec files
460 # Establishes limiting conventions
461 # - conflates MAs and SAs
462 # - assumes last token in slice name is unique
464 # Bootstraps credentials
465 # - bootstrap user credential from self-signed certificate
466 # - bootstrap authority credential from user credential
467 # - bootstrap slice credential from user credential
471 def get_key_file(self):
472 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
473 if (os.path.isfile(file)):
476 self.logger.error("Key file %s does not exist"%file)
480 def get_cert_file(self, key_file):
482 cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
483 if (os.path.isfile(cert_file)):
484 # we'd perfer to use Registry issued certs instead of self signed certs.
485 # if this is a Registry cert (GID) then we are done
486 gid = GID(filename=cert_file)
490 # generate self signed certificate
491 k = Keypair(filename=key_file)
492 cert = Certificate(subject=self.user)
494 cert.set_issuer(k, self.user)
496 self.logger.info("Writing self-signed certificate to %s"%cert_file)
497 cert.save_to_file(cert_file)
499 # try to get registry issued cert
501 self.logger.info("Getting Registry issued cert")
503 # *hack. need to set registyr before _get_gid() is called
504 self.registry = sfaprotocol.server_proxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
505 gid = self._get_gid(type='user')
507 self.logger.info("Writing certificate to %s"%cert_file)
508 gid.save_to_file(cert_file)
510 self.logger.info("Failed to download Registry issued cert")
514 def get_cached_gid(self, file):
519 if (os.path.isfile(file)):
520 gid = GID(filename=file)
524 def get_gid(self, opts, args):
526 Get the specify gid and save it to file
531 gid = self._get_gid(hrn)
532 self.logger.debug("Sfi.get_gid-> %s" % gid.save_to_string(save_parents=True))
535 def _get_gid(self, hrn=None, type=None):
537 git_gid helper. Retrive the gid from the registry and save it to file.
543 gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
544 gid = self.get_cached_gid(gidfile)
546 user_cred = self.get_user_cred()
547 records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
549 raise RecordNotFound(args[0])
554 if type == rec['type']:
557 raise RecordNotFound(args[0])
559 gid = GID(string=record['gid'])
560 self.logger.info("Writing gid to %s"%gidfile)
561 gid.save_to_file(filename=gidfile)
565 def get_cached_credential(self, file):
567 Return a cached credential only if it hasn't expired.
569 if (os.path.isfile(file)):
570 credential = Credential(filename=file)
571 # make sure it isnt expired
572 if not credential.get_expiration or \
573 datetime.datetime.today() < credential.get_expiration():
577 def get_user_cred(self):
578 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
579 return self.get_cred(file, 'user', self.user)
581 def get_auth_cred(self):
582 if not self.authority:
583 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
585 file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
586 return self.get_cred(file, 'authority', self.authority)
588 def get_slice_cred(self, name):
589 file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
590 return self.get_cred(file, 'slice', name)
592 def get_cred(self, file, type, hrn):
593 # attempt to load a cached credential
594 cred = self.get_cached_credential(file)
597 cert_string = self.cert.save_to_string(save_parents=True)
598 user_name = self.user.replace(self.authority + ".", '')
599 if user_name.count(".") > 0:
600 user_name = user_name.replace(".", '_')
601 self.user = self.authority + "." + user_name
602 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
604 # bootstrap slice credential from user credential
605 user_cred = self.get_user_cred().save_to_string(save_parents=True)
606 cred_str = self.registry.GetCredential(user_cred, hrn, type)
609 self.logger.critical("Failed to get %s credential" % type)
612 cred = Credential(string=cred_str)
613 cred.save_to_file(file, save_parents=True)
614 self.logger.info("Writing %s credential to %s" %(type, file))
619 def get_rspec_file(self, rspec):
620 if (os.path.isabs(rspec)):
623 file = os.path.join(self.options.sfi_dir, rspec)
624 if (os.path.isfile(file)):
627 self.logger.critical("No such rspec file %s"%rspec)
630 def get_record_file(self, record):
631 if (os.path.isabs(record)):
634 file = os.path.join(self.options.sfi_dir, record)
635 if (os.path.isfile(file)):
638 self.logger.critical("No such registry record file %s"%record)
641 def load_publickey_string(self, fn):
643 key_string = f.read()
645 # if the filename is a private key file, then extract the public key
646 if "PRIVATE KEY" in key_string:
647 outfn = tempfile.mktemp()
648 cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
651 key_string = f.read()
657 def get_component_proxy_from_hrn(self, hrn):
658 # direct connection to the nodes component manager interface
659 user_cred = self.get_user_cred().save_to_string(save_parents=True)
660 records = self.registry.Resolve(hrn, user_cred)
661 records = filter_records('node', records)
663 self.logger.warning("No such component:%r"% opts.component)
666 return self.server_proxy(record['hostname'], CM_PORT, self.key_file, self.cert_file)
668 def server_proxy(self, host, port, keyfile, certfile):
670 Return an instance of an xmlrpc server connection
672 # port is appended onto the domain, before the path. Should look like:
673 # http://domain:port/path
674 host_parts = host.split('/')
675 host_parts[0] = host_parts[0] + ":" + str(port)
676 url = "http://%s" % "/".join(host_parts)
677 return sfaprotocol.server_proxy(url, keyfile, certfile, timeout=self.options.timeout, verbose=self.options.debug)
679 # xxx opts could be retrieved in self.options
680 def server_proxy_from_opts(self, opts):
682 Return instance of an xmlrpc connection to a slice manager, aggregate
683 or component server depending on the specified opts
685 server = self.slicemgr
686 # direct connection to an aggregate
687 if hasattr(opts, 'aggregate') and opts.aggregate:
688 server = self.server_proxy(opts.aggregate, opts.port, self.key_file, self.cert_file)
689 # direct connection to the nodes component manager interface
690 if hasattr(opts, 'component') and opts.component:
691 server = self.get_component_proxy_from_hrn(opts.component)
694 #==========================================================================
695 # Following functions implement the commands
697 # Registry-related commands
698 #==========================================================================
700 def dispatch(self, command, cmd_opts, cmd_args):
701 return getattr(self, command)(cmd_opts, cmd_args)
703 def create_gid(self, opts, args):
708 user_cred = self.get_user_cred().save_to_string(save_parents=True)
709 gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
713 filename = os.sep.join([self.sfi_dir, '%s.gid' % target_hrn])
714 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
715 GID(string=gid).save_to_file(filename)
718 # list entires in named authority registry
719 def list(self, opts, args):
724 user_cred = self.get_user_cred().save_to_string(save_parents=True)
726 list = self.registry.List(hrn, user_cred)
728 raise Exception, "Not enough parameters for the 'list' command"
730 # filter on person, slice, site, node, etc.
731 # THis really should be in the self.filter_records funct def comment...
732 list = filter_records(opts.type, list)
734 print "%s (%s)" % (record['hrn'], record['type'])
736 save_records_to_file(opts.file, list, opts.fileformat)
739 # show named registry record
740 def show(self, opts, args):
745 user_cred = self.get_user_cred().save_to_string(save_parents=True)
746 records = self.registry.Resolve(hrn, user_cred)
747 records = filter_records(opts.type, records)
749 print "No record of type", opts.type
750 for record in records:
751 if record['type'] in ['user']:
752 record = UserRecord(dict=record)
753 elif record['type'] in ['slice']:
754 record = SliceRecord(dict=record)
755 elif record['type'] in ['node']:
756 record = NodeRecord(dict=record)
757 elif record['type'].startswith('authority'):
758 record = AuthorityRecord(dict=record)
760 record = SfaRecord(dict=record)
761 if (opts.format == "text"):
764 print record.save_to_string()
766 save_records_to_file(opts.file, records, opts.fileformat)
769 def delegate(self, opts, args):
771 delegee_hrn = args[0]
772 if opts.delegate_user:
773 user_cred = self.get_user_cred()
774 cred = self.delegate_cred(user_cred, delegee_hrn)
775 elif opts.delegate_slice:
776 slice_cred = self.get_slice_cred(opts.delegate_slice)
777 cred = self.delegate_cred(slice_cred, delegee_hrn)
779 self.logger.warning("Must specify either --user or --slice <hrn>")
781 delegated_cred = Credential(string=cred)
782 object_hrn = delegated_cred.get_gid_object().get_hrn()
783 if opts.delegate_user:
784 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
785 + get_leaf(object_hrn) + ".cred")
786 elif opts.delegate_slice:
787 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
788 + get_leaf(object_hrn) + ".cred")
790 delegated_cred.save_to_file(dest_fn, save_parents=True)
792 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
794 def delegate_cred(self, object_cred, hrn):
795 # the gid and hrn of the object we are delegating
796 if isinstance(object_cred, str):
797 object_cred = Credential(string=object_cred)
798 object_gid = object_cred.get_gid_object()
799 object_hrn = object_gid.get_hrn()
801 if not object_cred.get_privileges().get_all_delegate():
802 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
805 # the delegating user's gid
806 caller_gid = self._get_gid(self.user)
807 caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
809 # the gid of the user who will be delegated to
810 delegee_gid = self._get_gid(hrn)
811 delegee_hrn = delegee_gid.get_hrn()
812 delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
813 delegee_gid.save_to_file(filename=delegee_gidfile)
814 dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
815 return dcred.save_to_string(save_parents=True)
817 # removed named registry record
818 # - have to first retrieve the record to be removed
819 def remove(self, opts, args):
820 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
828 return self.registry.Remove(hrn, auth_cred, type)
830 # add named registry record
831 def add(self, opts, args):
832 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
836 record_filepath = args[0]
837 rec_file = self.get_record_file(record_filepath)
838 record = load_record_from_file(rec_file).as_dict()
839 return self.registry.Register(record, auth_cred)
841 # update named registry entry
842 def update(self, opts, args):
843 user_cred = self.get_user_cred()
847 rec_file = self.get_record_file(args[0])
848 record = load_record_from_file(rec_file)
849 if record['type'] == "user":
850 if record.get_name() == user_cred.get_gid_object().get_hrn():
851 cred = user_cred.save_to_string(save_parents=True)
853 cred = self.get_auth_cred().save_to_string(save_parents=True)
854 elif record['type'] in ["slice"]:
856 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
857 except sfaprotocol.ServerException, e:
858 # XXX smbaker -- once we have better error return codes, update this
859 # to do something better than a string compare
860 if "Permission error" in e.args[0]:
861 cred = self.get_auth_cred().save_to_string(save_parents=True)
864 elif record.get_type() in ["authority"]:
865 cred = self.get_auth_cred().save_to_string(save_parents=True)
866 elif record.get_type() == 'node':
867 cred = self.get_auth_cred().save_to_string(save_parents=True)
869 raise "unknown record type" + record.get_type()
870 record = record.as_dict()
871 return self.registry.Update(record, cred)
873 def get_trusted_certs(self, opts, args):
875 return uhe trusted certs at this interface
877 trusted_certs = self.registry.get_trusted_certs()
878 for trusted_cert in trusted_certs:
879 gid = GID(string=trusted_cert)
881 cert = Certificate(string=trusted_cert)
882 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
885 def aggregates(self, opts, args):
887 return a list of details about known aggregates
889 user_cred = self.get_user_cred().save_to_string(save_parents=True)
894 result = self.registry.get_aggregates(user_cred, hrn)
898 def registries(self, opts, args):
900 return a list of details about known registries
902 user_cred = self.get_user_cred().save_to_string(save_parents=True)
906 result = self.registry.get_registries(user_cred, hrn)
911 # ==================================================================
912 # Slice-related commands
913 # ==================================================================
915 def version(self, opts, args):
916 if opts.version_local:
917 version=version_core()
919 if opts.version_registry:
922 server = self.server_proxy_from_opts(opts)
923 result = server.GetVersion()
924 version = ReturnValue.get_value(result)
925 for (k,v) in version.iteritems():
926 print "%-20s: %s"%(k,v)
928 save_variable_to_file(version, opts.file, opts.fileformat)
930 # list instantiated slices
931 def slices(self, opts, args):
933 list instantiated slices
935 user_cred = self.get_user_cred().save_to_string(save_parents=True)
938 delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
939 creds.append(delegated_cred)
940 server = self.server_proxy_from_opts(opts)
942 if self.server_supports_options_arg(server):
943 options = {'call_id': unique_call_id()}
944 call_args.append(options)
945 result = server.ListSlices(*call_args)
946 value = ReturnValue.get_value(result)
950 # show rspec for named slice
951 def resources(self, opts, args):
952 user_cred = self.get_user_cred().save_to_string(save_parents=True)
953 server = self.server_proxy_from_opts(opts)
955 options = {'call_id': unique_call_id()}
956 #panos add info options
958 options['info'] = opts.info
961 cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
963 options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
969 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
970 creds.append(delegated_cred)
971 if opts.rspec_version:
972 version_manager = VersionManager()
973 server_version = self.get_cached_server_version(server)
974 if 'sfa' in server_version:
975 # just request the version the client wants
976 options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
978 # this must be a protogeni aggregate. We should request a v2 ad rspec
979 # regardless of what the client user requested
980 options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
982 options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
984 call_args = [creds, options]
985 result = server.ListResources(*call_args)
986 value = ReturnValue.get_value(result)
987 if opts.file is None:
988 display_rspec(value, opts.format)
990 save_rspec_to_file(value, opts.file)
993 # created named slice with given rspec
994 def create(self, opts, args):
995 server = self.server_proxy_from_opts(opts)
996 server_version = self.get_cached_server_version(server)
998 slice_urn = hrn_to_urn(slice_hrn, 'slice')
999 user_cred = self.get_user_cred()
1000 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1002 if hasattr(opts, 'aggregate') and opts.aggregate:
1003 delegated_cred = None
1005 # delegate the cred to the callers root authority
1006 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)+'.slicemanager')
1007 #delegated_cred = self.delegate_cred(slice_cred, get_authority(slice_hrn))
1008 #creds.append(delegated_cred)
1010 rspec_file = self.get_rspec_file(args[1])
1011 rspec = open(rspec_file).read()
1013 # need to pass along user keys to the aggregate.
1015 # { urn: urn:publicid:IDN+emulab.net+user+alice
1016 # keys: [<ssh key A>, <ssh key B>]
1019 slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
1020 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1021 slice_record = slice_records[0]
1022 user_hrns = slice_record['researcher']
1023 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1024 user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
1026 if 'sfa' not in server_version:
1027 users = pg_users_arg(user_records)
1028 rspec = RSpec(rspec)
1029 rspec.filter({'component_manager_id': server_version['urn']})
1030 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1031 creds = [slice_cred]
1033 users = sfa_users_arg(user_records, slice_record)
1034 creds = [slice_cred]
1036 creds.append(delegated_cred)
1037 call_args = [slice_urn, creds, rspec, users]
1038 if self.server_supports_options_arg(server):
1039 options = {'call_id': unique_call_id()}
1040 call_args.append(options)
1041 result = server.CreateSliver(*call_args)
1042 value = ReturnValue.get_value(result)
1043 if opts.file is None:
1046 save_rspec_to_file (value, opts.file)
1049 # get a ticket for the specified slice
1050 def get_ticket(self, opts, args):
1051 slice_hrn, rspec_path = args[0], args[1]
1052 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1053 user_cred = self.get_user_cred()
1054 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1055 creds = [slice_cred]
1057 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1058 creds.append(delegated_cred)
1059 rspec_file = self.get_rspec_file(rspec_path)
1060 rspec = open(rspec_file).read()
1061 server = self.server_proxy_from_opts(opts)
1062 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1063 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1064 self.logger.info("writing ticket to %s"%file)
1065 ticket = SfaTicket(string=ticket_string)
1066 ticket.save_to_file(filename=file, save_parents=True)
1068 def redeem_ticket(self, opts, args):
1069 ticket_file = args[0]
1071 # get slice hrn from the ticket
1072 # use this to get the right slice credential
1073 ticket = SfaTicket(filename=ticket_file)
1075 slice_hrn = ticket.gidObject.get_hrn()
1076 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1077 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1078 user_cred = self.get_user_cred()
1079 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1081 # get a list of node hostnames from the RSpec
1082 tree = etree.parse(StringIO(ticket.rspec))
1083 root = tree.getroot()
1084 hostnames = root.xpath("./network/site/node/hostname/text()")
1086 # create an xmlrpc connection to the component manager at each of these
1087 # components and gall redeem_ticket
1089 for hostname in hostnames:
1091 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1092 server = self.server_proxy(hostname, CM_PORT, self.key_file, \
1093 self.cert_file, self.options.debug)
1094 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1095 self.logger.info("Success")
1096 except socket.gaierror:
1097 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1098 except Exception, e:
1099 self.logger.log_exc(e.message)
1102 # delete named slice
1103 def delete(self, opts, args):
1105 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1106 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1107 creds = [slice_cred]
1109 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1110 creds.append(delegated_cred)
1111 server = self.server_proxy_from_opts(opts)
1112 call_args = [slice_urn, creds]
1113 if self.server_supports_options_arg(server):
1114 options = {'call_id': unique_call_id()}
1115 call_args.append(options)
1116 return server.DeleteSliver(*call_args)
1119 def start(self, opts, args):
1121 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1122 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1123 creds = [slice_cred]
1125 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1126 creds.append(delegated_cred)
1127 server = self.server_proxy_from_opts(opts)
1128 return server.Start(slice_urn, creds)
1131 def stop(self, opts, args):
1133 slice_urn = hrn_to_urn(slice_hrn, 'slice')
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)
1139 server = self.server_proxy_from_opts(opts)
1140 return server.Stop(slice_urn, creds)
1143 def reset(self, opts, args):
1145 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1146 server = self.server_proxy_from_opts(opts)
1147 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1148 creds = [slice_cred]
1150 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1151 creds.append(delegated_cred)
1152 return server.reset_slice(creds, slice_urn)
1154 def renew(self, opts, args):
1156 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1157 server = self.server_proxy_from_opts(opts)
1158 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1159 creds = [slice_cred]
1161 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1162 creds.append(delegated_cred)
1165 call_args = [slice_urn, creds, time]
1166 if self.server_supports_options_arg(server):
1167 options = {'call_id': unique_call_id()}
1168 call_args.append(options)
1169 result = server.RenewSliver(*call_args)
1170 value = ReturnValue.get_value(result)
1174 def status(self, opts, args):
1176 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1177 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1178 creds = [slice_cred]
1180 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1181 creds.append(delegated_cred)
1182 server = self.server_proxy_from_opts(opts)
1183 call_args = [slice_urn, creds]
1184 if self.server_supports_options_arg(server):
1185 options = {'call_id': unique_call_id()}
1186 call_args.append(options)
1187 result = server.SliverStatus(*call_args)
1188 value = ReturnValue.get_value(result)
1191 save_variable_to_file(value, opts.file, opts.fileformat)
1194 def shutdown(self, opts, args):
1196 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1197 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1198 creds = [slice_cred]
1200 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1201 creds.append(delegated_cred)
1202 server = self.server_proxy_from_opts(opts)
1203 return server.Shutdown(slice_urn, creds)
1205 def print_help (self):
1206 self.sfi_parser.print_help()
1207 self.cmd_parser.print_help()
1210 # Main: parse arguments and dispatch to command
1213 self.sfi_parser = self.create_parser()
1214 (options, args) = self.sfi_parser.parse_args()
1215 self.options = options
1217 self.logger.setLevelFromOptVerbose(self.options.verbose)
1218 if options.hashrequest:
1219 self.hashrequest = True
1222 self.logger.critical("No command given. Use -h for help.")
1226 self.cmd_parser = self.create_cmd_parser(command)
1227 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
1230 self.logger.info("Command=%s" % command)
1231 if command in ("resources"):
1232 self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
1233 elif command in ("list", "show", "remove"):
1234 self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
1235 self.logger.debug('cmd_args %s' % cmd_args)
1238 self.dispatch(command, cmd_opts, cmd_args)
1240 self.logger.critical ("Unknown command %s"%command)
1246 if __name__ == "__main__":