3 # sfi -- slice-based facility interface
16 from lxml import etree
17 from StringIO import StringIO
18 from types import StringTypes, ListType
19 from optparse import OptionParser
20 from sfa.client.client_helper import pg_users_arg, sfa_users_arg, sfa_to_pg_users_arg
21 from sfa.util.sfalogging import sfi_logger
22 from sfa.trust.certificate import Keypair, Certificate
23 from sfa.trust.gid import GID
24 from sfa.trust.credential import Credential
25 from sfa.util.sfaticket import SfaTicket
26 from sfa.util.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
27 from sfa.rspecs.rspec import RSpec
28 from sfa.rspecs.rspec_converter import RSpecConverter
29 from sfa.util.xrn import Xrn, get_leaf, get_authority, hrn_to_urn
30 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
31 from sfa.util.config import Config
32 from sfa.util.version import version_core
33 from sfa.util.cache import Cache
34 from sfa.rspecs.version_manager import VersionManager
39 # utility methods here
41 def display_rspec(rspec, format='rspec'):
43 tree = etree.parse(StringIO(rspec))
45 result = root.xpath("./network/site/node/hostname/text()")
46 elif format in ['ip']:
47 # The IP address is not yet part of the new RSpec
48 # so this doesn't do anything yet.
49 tree = etree.parse(StringIO(rspec))
51 result = root.xpath("./network/site/node/ipv4/text()")
58 def display_list(results):
59 for result in results:
62 def display_records(recordList, dump=False):
63 ''' Print all fields in the record'''
64 for record in recordList:
65 display_record(record, dump)
67 def display_record(record, dump=False):
71 info = record.getdict()
72 print "%s (%s)" % (info['hrn'], info['type'])
76 def filter_records(type, records):
78 for record in records:
79 if (record['type'] == type) or (type == "all"):
80 filtered_records.append(record)
81 return filtered_records
85 def save_variable_to_file(var, filename, format="text"):
86 f = open(filename, "w")
89 elif format == "pickled":
90 f.write(pickle.dumps(var))
92 # this should never happen
93 print "unknown output format", format
96 def save_rspec_to_file(rspec, filename):
97 if not filename.endswith(".rspec"):
98 filename = filename + ".rspec"
99 f = open(filename, 'w')
104 def save_records_to_file(filename, recordList, format="xml"):
107 for record in recordList:
109 save_record_to_file(filename + "." + str(index), record)
111 save_record_to_file(filename, record)
113 elif format == "xmllist":
114 f = open(filename, "w")
115 f.write("<recordlist>\n")
116 for record in recordList:
117 record = SfaRecord(dict=record)
118 f.write('<record hrn="' + record.get_name() + '" type="' + record.get_type() + '" />\n')
119 f.write("</recordlist>\n");
121 elif format == "hrnlist":
122 f = open(filename, "w")
123 for record in recordList:
124 record = SfaRecord(dict=record)
125 f.write(record.get_name() + "\n")
128 # this should never happen
129 print "unknown output format", format
131 def save_record_to_file(filename, record):
132 if record['type'] in ['user']:
133 record = UserRecord(dict=record)
134 elif record['type'] in ['slice']:
135 record = SliceRecord(dict=record)
136 elif record['type'] in ['node']:
137 record = NodeRecord(dict=record)
138 elif record['type'] in ['authority', 'ma', 'sa']:
139 record = AuthorityRecord(dict=record)
141 record = SfaRecord(dict=record)
142 str = record.save_to_string()
143 f=codecs.open(filename, encoding='utf-8',mode="w")
150 def load_record_from_file(filename):
151 f=codecs.open(filename, encoding="utf-8", mode="r")
154 record = SfaRecord(string=str)
159 def unique_call_id(): return uuid.uuid4().urn
163 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user']
165 # dummy to meet Sfi's expectations for its 'options' field
166 # i.e. s/t we can do setattr on
170 def __init__ (self,options=None):
171 if options is None: options=Sfi.DummyOptions()
172 for opt in Sfi.required_options:
173 if not hasattr(options,opt): setattr(options,opt,None)
174 if not hasattr(options,'sfi_dir'): options.sfi_dir=os.path.expanduser("~/.sfi/")
175 # xxx oops, this is dangerous, sounds like ww sometimes have discrepency
176 # would be safer to remove self.sfi_dir altogether
177 self.sfi_dir = options.sfi_dir
178 self.options = options
182 self.authority = None
183 self.hashrequest = False
184 self.logger = sfi_logger
185 self.logger.enable_console()
187 def create_cmd_parser(self, command, additional_cmdargs=None):
188 cmdargs = {"list": "authority",
193 "aggregates": "[name]",
194 "registries": "[name]",
195 "create_gid": "[name]",
197 "get_trusted_certs": "cred",
199 "resources": "[name]",
200 "create": "name rspec",
201 "get_ticket": "name rspec",
202 "redeem_ticket": "ticket",
214 if additional_cmdargs:
215 cmdargs.update(additional_cmdargs)
217 if command not in cmdargs:
218 msg="Invalid command\n"
220 msg += ','.join(cmdargs.keys())
221 self.logger.critical(msg)
224 parser = OptionParser(usage="sfi [sfi_options] %s [options] %s" \
225 % (command, cmdargs[command]))
227 # user specifies remote aggregate/sm/component
228 if command in ("resources", "slices", "create", "delete", "start", "stop",
229 "restart", "shutdown", "get_ticket", "renew", "status"):
230 parser.add_option("-a", "--aggregate", dest="aggregate",
231 default=None, help="aggregate host")
232 parser.add_option("-p", "--port", dest="port",
233 default=AGGREGATE_PORT, help="aggregate port")
234 parser.add_option("-c", "--component", dest="component", default=None,
235 help="component hrn")
236 parser.add_option("-d", "--delegate", dest="delegate", default=None,
238 help="Include a credential delegated to the user's root"+\
239 "authority in set of credentials for this call")
241 # registy filter option
242 if command in ("list", "show", "remove"):
243 parser.add_option("-t", "--type", dest="type", type="choice",
244 help="type filter ([all]|user|slice|authority|node|aggregate)",
245 choices=("all", "user", "slice", "authority", "node", "aggregate"),
248 if command in ("resources"):
249 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
250 help="schema type and version of resulting RSpec")
251 parser.add_option("-f", "--format", dest="format", type="choice",
252 help="display format ([xml]|dns|ip)", default="xml",
253 choices=("xml", "dns", "ip"))
254 #panos: a new option to define the type of information about resources a user is interested in
255 parser.add_option("-i", "--info", dest="info",
256 help="optional component information", default=None)
259 # 'create' does return the new rspec, makes sense to save that too
260 if command in ("resources", "show", "list", "create_gid", 'create'):
261 parser.add_option("-o", "--output", dest="file",
262 help="output XML to file", metavar="FILE", default=None)
264 if command in ("show", "list"):
265 parser.add_option("-f", "--format", dest="format", type="choice",
266 help="display format ([text]|xml)", default="text",
267 choices=("text", "xml"))
269 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
270 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
271 choices=("xml", "xmllist", "hrnlist"))
273 if command in ("status"):
274 parser.add_option("-o", "--output", dest="file",
275 help="output dictionary to file", metavar="FILE", default=None)
276 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
277 help="output file format ([text]|pickled)", default="text",
278 choices=("text","pickled"))
280 if command in ("delegate"):
281 parser.add_option("-u", "--user",
282 action="store_true", dest="delegate_user", default=False,
283 help="delegate user credential")
284 parser.add_option("-s", "--slice", dest="delegate_slice",
285 help="delegate slice credential", metavar="HRN", default=None)
287 if command in ("version"):
288 parser.add_option("-a", "--aggregate", dest="aggregate",
289 default=None, help="aggregate host")
290 parser.add_option("-p", "--port", dest="port",
291 default=AGGREGATE_PORT, help="aggregate port")
292 parser.add_option("-R","--registry-version",
293 action="store_true", dest="version_registry", default=False,
294 help="probe registry version instead of slicemgr")
295 parser.add_option("-l","--local",
296 action="store_true", dest="version_local", default=False,
297 help="display version of the local client")
302 def create_parser(self):
304 # Generate command line parser
305 parser = OptionParser(usage="sfi [options] command [command_options] [command_args]",
306 description="Commands: gid,list,show,remove,add,update,nodes,slices,resources,create,delete,start,stop,reset")
307 parser.add_option("-r", "--registry", dest="registry",
308 help="root registry", metavar="URL", default=None)
309 parser.add_option("-s", "--slicemgr", dest="sm",
310 help="slice manager", metavar="URL", default=None)
311 default_sfi_dir = os.path.expanduser("~/.sfi/")
312 parser.add_option("-d", "--dir", dest="sfi_dir",
313 help="config & working directory - default is " + default_sfi_dir,
314 metavar="PATH", default=default_sfi_dir)
315 parser.add_option("-u", "--user", dest="user",
316 help="user name", metavar="HRN", default=None)
317 parser.add_option("-a", "--auth", dest="auth",
318 help="authority name", metavar="HRN", default=None)
319 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
320 help="verbose mode - cumulative")
321 parser.add_option("-D", "--debug",
322 action="store_true", dest="debug", default=False,
323 help="Debug (xml-rpc) protocol messages")
324 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
325 help="RPC protocol (xmlrpc or soap)")
326 parser.add_option("-k", "--hashrequest",
327 action="store_true", dest="hashrequest", default=False,
328 help="Create a hash of the request that will be authenticated on the server")
329 parser.add_option("-t", "--timeout", dest="timeout", default=None,
330 help="Amout of time tom wait before timing out the request")
331 parser.disable_interspersed_args()
336 def read_config(self):
337 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
339 config = Config (config_file)
341 self.logger.critical("Failed to read configuration file %s"%config_file)
342 self.logger.info("Make sure to remove the export clauses and to add quotes")
343 if self.options.verbose==0:
344 self.logger.info("Re-run with -v for more details")
346 self.logger.log_exc("Could not read config file %s"%config_file)
351 if (self.options.sm is not None):
352 self.sm_url = self.options.sm
353 elif hasattr(config, "SFI_SM"):
354 self.sm_url = config.SFI_SM
356 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
360 if (self.options.registry is not None):
361 self.reg_url = self.options.registry
362 elif hasattr(config, "SFI_REGISTRY"):
363 self.reg_url = config.SFI_REGISTRY
365 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
370 if (self.options.user is not None):
371 self.user = self.options.user
372 elif hasattr(config, "SFI_USER"):
373 self.user = config.SFI_USER
375 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
379 if (self.options.auth is not None):
380 self.authority = self.options.auth
381 elif hasattr(config, "SFI_AUTH"):
382 self.authority = config.SFI_AUTH
384 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
392 # Establish Connection to SliceMgr and Registry Servers
394 def set_servers(self):
397 # Get key and certificate
398 key_file = self.get_key_file()
399 cert_file = self.get_cert_file(key_file)
400 self.key = Keypair(filename=key_file)
401 self.key_file = key_file
402 self.cert_file = cert_file
403 self.cert = GID(filename=cert_file)
404 self.logger.info("Contacting Registry at: %s"%self.reg_url)
405 self.registry = xmlrpcprotocol.get_server(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
406 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
407 self.slicemgr = xmlrpcprotocol.get_server(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
410 def get_cached_server_version(self, server):
411 # check local cache first
414 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
415 cache_key = server.url + "-version"
417 cache = Cache(cache_file)
420 self.logger.info("Local cache not found at: %s" % cache_file)
423 version = cache.get(cache_key)
426 version = server.GetVersion()
427 # cache version for 24 hours
428 cache.add(cache_key, version, ttl= 60*60*24)
429 self.logger.info("Updating cache file %s" % cache_file)
430 cache.save_to_file(cache_file)
436 def server_supports_call_id_arg(self, server):
438 Returns true if server support the optional call_id arg, false otherwise.
440 server_version = self.get_cached_server_version(server)
441 if 'sfa' in server_version and 'code_tag' in server_version:
442 code_tag = server_version['code_tag']
443 code_tag_parts = code_tag.split("-")
445 version_parts = code_tag_parts[0].split(".")
446 major, minor = version_parts[0], version_parts[1]
447 rev = code_tag_parts[1]
449 if int(minor) > 0 or int(rev) > 20:
454 # Get various credential and spec files
456 # Establishes limiting conventions
457 # - conflates MAs and SAs
458 # - assumes last token in slice name is unique
460 # Bootstraps credentials
461 # - bootstrap user credential from self-signed certificate
462 # - bootstrap authority credential from user credential
463 # - bootstrap slice credential from user credential
467 def get_key_file(self):
468 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
469 if (os.path.isfile(file)):
472 self.logger.error("Key file %s does not exist"%file)
476 def get_cert_file(self, key_file):
478 cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
479 if (os.path.isfile(cert_file)):
480 # we'd perfer to use Registry issued certs instead of self signed certs.
481 # if this is a Registry cert (GID) then we are done
482 gid = GID(filename=cert_file)
486 # generate self signed certificate
487 k = Keypair(filename=key_file)
488 cert = Certificate(subject=self.user)
490 cert.set_issuer(k, self.user)
492 self.logger.info("Writing self-signed certificate to %s"%cert_file)
493 cert.save_to_file(cert_file)
495 # try to get registry issued cert
497 self.logger.info("Getting Registry issued cert")
499 # *hack. need to set registyr before _get_gid() is called
500 self.registry = xmlrpcprotocol.get_server(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
501 gid = self._get_gid(type='user')
503 self.logger.info("Writing certificate to %s"%cert_file)
504 gid.save_to_file(cert_file)
506 self.logger.info("Failed to download Registry issued cert")
510 def get_cached_gid(self, file):
515 if (os.path.isfile(file)):
516 gid = GID(filename=file)
520 def get_gid(self, opts, args):
522 Get the specify gid and save it to file
527 gid = self._get_gid(hrn)
528 self.logger.debug("Sfi.get_gid-> %s",gid.save_to_string(save_parents=True))
531 def _get_gid(self, hrn=None, type=None):
533 git_gid helper. Retrive the gid from the registry and save it to file.
539 gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
541 gid = self.get_cached_gid(gidfile)
543 user_cred = self.get_user_cred()
544 records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
546 raise RecordNotFound(args[0])
551 if type == rec['type']:
554 raise RecordNotFound(args[0])
556 gid = GID(string=record['gid'])
557 self.logger.info("Writing gid to %s"%gidfile)
558 gid.save_to_file(filename=gidfile)
562 def get_cached_credential(self, file):
564 Return a cached credential only if it hasn't expired.
566 if (os.path.isfile(file)):
567 credential = Credential(filename=file)
568 # make sure it isnt expired
569 if not credential.get_expiration or \
570 datetime.datetime.today() < credential.get_expiration():
574 def get_user_cred(self):
575 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
576 return self.get_cred(file, 'user', self.user)
578 def get_auth_cred(self):
579 if not self.authority:
580 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
582 file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
583 return self.get_cred(file, 'authority', self.authority)
585 def get_slice_cred(self, name):
586 file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
587 return self.get_cred(file, 'slice', name)
589 def get_cred(self, file, type, hrn):
590 # attempt to load a cached credential
591 cred = self.get_cached_credential(file)
594 cert_string = self.cert.save_to_string(save_parents=True)
595 user_name = self.user.replace(self.authority + ".", '')
596 if user_name.count(".") > 0:
597 user_name = user_name.replace(".", '_')
598 self.user = self.authority + "." + user_name
599 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
601 # bootstrap slice credential from user credential
602 user_cred = self.get_user_cred().save_to_string(save_parents=True)
603 cred_str = self.registry.GetCredential(user_cred, hrn, type)
606 self.logger.critical("Failed to get %s credential" % type)
609 cred = Credential(string=cred_str)
610 cred.save_to_file(file, save_parents=True)
611 self.logger.info("Writing %s credential to %s" %(type, file))
616 def get_rspec_file(self, rspec):
617 if (os.path.isabs(rspec)):
620 file = os.path.join(self.options.sfi_dir, rspec)
621 if (os.path.isfile(file)):
624 self.logger.critical("No such rspec file %s"%rspec)
627 def get_record_file(self, record):
628 if (os.path.isabs(record)):
631 file = os.path.join(self.options.sfi_dir, record)
632 if (os.path.isfile(file)):
635 self.logger.critical("No such registry record file %s"%record)
638 def load_publickey_string(self, fn):
640 key_string = f.read()
642 # if the filename is a private key file, then extract the public key
643 if "PRIVATE KEY" in key_string:
644 outfn = tempfile.mktemp()
645 cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
648 key_string = f.read()
654 def get_component_server_from_hrn(self, hrn):
655 # direct connection to the nodes component manager interface
656 user_cred = self.get_user_cred().save_to_string(save_parents=True)
657 records = self.registry.Resolve(hrn, user_cred)
658 records = filter_records('node', records)
660 self.logger.warning("No such component:%r"% opts.component)
663 return self.get_server(record['hostname'], CM_PORT, self.key_file, self.cert_file)
665 def get_server(self, host, port, keyfile, certfile):
667 Return an instance of an xmlrpc server connection
669 # port is appended onto the domain, before the path. Should look like:
670 # http://domain:port/path
671 host_parts = host.split('/')
672 host_parts[0] = host_parts[0] + ":" + str(port)
673 url = "http://%s" % "/".join(host_parts)
674 return xmlrpcprotocol.get_server(url, keyfile, certfile, timeout=self.options.timeout, verbose=self.options.debug)
676 # xxx opts could be retrieved in self.options
677 def get_server_from_opts(self, opts):
679 Return instance of an xmlrpc connection to a slice manager, aggregate
680 or component server depending on the specified opts
682 server = self.slicemgr
683 # direct connection to an aggregate
684 if hasattr(opts, 'aggregate') and opts.aggregate:
685 server = self.get_server(opts.aggregate, opts.port, self.key_file, self.cert_file)
686 # direct connection to the nodes component manager interface
687 if hasattr(opts, 'component') and opts.component:
688 server = self.get_component_server_from_hrn(opts.component)
691 #==========================================================================
692 # Following functions implement the commands
694 # Registry-related commands
695 #==========================================================================
697 def dispatch(self, command, cmd_opts, cmd_args):
698 return getattr(self, command)(cmd_opts, cmd_args)
700 def create_gid(self, opts, args):
705 user_cred = self.get_user_cred().save_to_string(save_parents=True)
706 gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
710 filename = os.sep.join([self.sfi_dir, '%s.gid' % target_hrn])
711 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
712 GID(string=gid).save_to_file(filename)
715 # list entires in named authority registry
716 def list(self, opts, args):
721 user_cred = self.get_user_cred().save_to_string(save_parents=True)
723 list = self.registry.List(hrn, user_cred)
725 raise Exception, "Not enough parameters for the 'list' command"
727 # filter on person, slice, site, node, etc.
728 # THis really should be in the self.filter_records funct def comment...
729 list = filter_records(opts.type, list)
731 print "%s (%s)" % (record['hrn'], record['type'])
733 save_records_to_file(opts.file, list, opts.fileformat)
736 # show named registry record
737 def show(self, opts, args):
742 user_cred = self.get_user_cred().save_to_string(save_parents=True)
743 records = self.registry.Resolve(hrn, user_cred)
744 records = filter_records(opts.type, records)
746 print "No record of type", opts.type
747 for record in records:
748 if record['type'] in ['user']:
749 record = UserRecord(dict=record)
750 elif record['type'] in ['slice']:
751 record = SliceRecord(dict=record)
752 elif record['type'] in ['node']:
753 record = NodeRecord(dict=record)
754 elif record['type'].startswith('authority'):
755 record = AuthorityRecord(dict=record)
757 record = SfaRecord(dict=record)
758 if (opts.format == "text"):
761 print record.save_to_string()
763 save_records_to_file(opts.file, records, opts.fileformat)
766 def delegate(self, opts, args):
768 delegee_hrn = args[0]
769 if opts.delegate_user:
770 user_cred = self.get_user_cred()
771 cred = self.delegate_cred(user_cred, delegee_hrn)
772 elif opts.delegate_slice:
773 slice_cred = self.get_slice_cred(opts.delegate_slice)
774 cred = self.delegate_cred(slice_cred, delegee_hrn)
776 self.logger.warning("Must specify either --user or --slice <hrn>")
778 delegated_cred = Credential(string=cred)
779 object_hrn = delegated_cred.get_gid_object().get_hrn()
780 if opts.delegate_user:
781 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
782 + get_leaf(object_hrn) + ".cred")
783 elif opts.delegate_slice:
784 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
785 + get_leaf(object_hrn) + ".cred")
787 delegated_cred.save_to_file(dest_fn, save_parents=True)
789 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
791 def delegate_cred(self, object_cred, hrn):
792 # the gid and hrn of the object we are delegating
793 if isinstance(object_cred, str):
794 object_cred = Credential(string=object_cred)
795 object_gid = object_cred.get_gid_object()
796 object_hrn = object_gid.get_hrn()
798 if not object_cred.get_privileges().get_all_delegate():
799 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
802 # the delegating user's gid
803 caller_gid = self._get_gid(self.user)
804 caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
806 # the gid of the user who will be delegated to
807 delegee_gid = self._get_gid(hrn)
808 delegee_hrn = delegee_gid.get_hrn()
809 delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
810 delegee_gid.save_to_file(filename=delegee_gidfile)
811 dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
812 return dcred.save_to_string(save_parents=True)
814 # removed named registry record
815 # - have to first retrieve the record to be removed
816 def remove(self, opts, args):
817 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
825 return self.registry.Remove(hrn, auth_cred, type)
827 # add named registry record
828 def add(self, opts, args):
829 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
833 record_filepath = args[0]
834 rec_file = self.get_record_file(record_filepath)
835 record = load_record_from_file(rec_file).as_dict()
836 return self.registry.Register(record, auth_cred)
838 # update named registry entry
839 def update(self, opts, args):
840 user_cred = self.get_user_cred()
844 rec_file = self.get_record_file(args[0])
845 record = load_record_from_file(rec_file)
846 if record['type'] == "user":
847 if record.get_name() == user_cred.get_gid_object().get_hrn():
848 cred = user_cred.save_to_string(save_parents=True)
850 cred = self.get_auth_cred().save_to_string(save_parents=True)
851 elif record['type'] in ["slice"]:
853 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
854 except xmlrpcprotocol.ServerException, e:
855 # XXX smbaker -- once we have better error return codes, update this
856 # to do something better than a string compare
857 if "Permission error" in e.args[0]:
858 cred = self.get_auth_cred().save_to_string(save_parents=True)
861 elif record.get_type() in ["authority"]:
862 cred = self.get_auth_cred().save_to_string(save_parents=True)
863 elif record.get_type() == 'node':
864 cred = self.get_auth_cred().save_to_string(save_parents=True)
866 raise "unknown record type" + record.get_type()
867 record = record.as_dict()
868 return self.registry.Update(record, cred)
870 def get_trusted_certs(self, opts, args):
872 return uhe trusted certs at this interface
874 trusted_certs = self.registry.get_trusted_certs()
875 for trusted_cert in trusted_certs:
876 gid = GID(string=trusted_cert)
878 cert = Certificate(string=trusted_cert)
879 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
882 def aggregates(self, opts, args):
884 return a list of details about known aggregates
886 user_cred = self.get_user_cred().save_to_string(save_parents=True)
891 result = self.registry.get_aggregates(user_cred, hrn)
895 def registries(self, opts, args):
897 return a list of details about known registries
899 user_cred = self.get_user_cred().save_to_string(save_parents=True)
903 result = self.registry.get_registries(user_cred, hrn)
908 # ==================================================================
909 # Slice-related commands
910 # ==================================================================
912 def version(self, opts, args):
913 if opts.version_local:
914 version=version_core()
916 if opts.version_registry:
919 server = self.get_server_from_opts(opts)
920 version=server.GetVersion()
921 for (k,v) in version.iteritems():
922 print "%-20s: %s"%(k,v)
924 # list instantiated slices
925 def slices(self, opts, args):
927 list instantiated slices
929 user_cred = self.get_user_cred().save_to_string(save_parents=True)
932 delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
933 creds.append(delegated_cred)
934 server = self.get_server_from_opts(opts)
935 #results = server.ListSlices(creds, unique_call_id())
936 results = server.ListSlices(creds)
937 display_list(results)
940 # show rspec for named slice
941 def resources(self, opts, args):
942 user_cred = self.get_user_cred().save_to_string(save_parents=True)
943 server = self.slicemgr
945 server = self.get_server_from_opts(opts)
948 cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
950 call_options = {'geni_slice_urn': hrn_to_urn(hrn, 'slice')}
957 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
958 creds.append(delegated_cred)
959 if opts.rspec_version:
960 version_manager = VersionManager()
961 server_version = self.get_cached_server_version(server)
962 if 'sfa' in server_version:
963 # just request the version the client wants
964 call_options['rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
966 # this must be a protogeni aggregate. We should request a v2 ad rspec
967 # regardless of what the client user requested
968 call_options['rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
969 #panos add info options
971 call_options['info'] = opts.info
973 call_args = [creds, call_options]
974 if self.server_supports_call_id_arg(server):
975 call_args.append(unique_call_id())
976 result = server.ListResources(*call_args)
977 if opts.file is None:
978 display_rspec(result, opts.format)
980 save_rspec_to_file(result, opts.file)
983 # created named slice with given rspec
984 def create(self, opts, args):
985 server = self.get_server_from_opts(opts)
986 server_version = self.get_cached_server_version(server)
988 slice_urn = hrn_to_urn(slice_hrn, 'slice')
989 user_cred = self.get_user_cred()
990 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
993 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
994 creds.append(delegated_cred)
995 rspec_file = self.get_rspec_file(args[1])
996 rspec = open(rspec_file).read()
998 # need to pass along user keys to the aggregate.
1000 # { urn: urn:publicid:IDN+emulab.net+user+alice
1001 # keys: [<ssh key A>, <ssh key B>]
1004 slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
1005 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1006 slice_record = slice_records[0]
1007 user_hrns = slice_record['researcher']
1008 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1009 user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
1011 if 'sfa' not in server_version:
1012 users = pg_users_arg(user_records)
1013 rspec = RSpec(rspec)
1014 rspec.filter({'component_manager_id': server_version['urn']})
1015 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1017 users = sfa_users_arg(user_records, slice_record)
1018 call_args = [slice_urn, creds, rspec, users]
1019 if self.server_supports_call_id_arg(server):
1020 call_args.append(unique_call_id())
1022 result = server.CreateSliver(*call_args)
1023 if opts.file is None:
1026 save_rspec_to_file (result, opts.file)
1029 # get a ticket for the specified slice
1030 def get_ticket(self, opts, args):
1031 slice_hrn, rspec_path = args[0], args[1]
1032 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1033 user_cred = self.get_user_cred()
1034 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1035 creds = [slice_cred]
1037 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1038 creds.append(delegated_cred)
1039 rspec_file = self.get_rspec_file(rspec_path)
1040 rspec = open(rspec_file).read()
1041 server = self.get_server_from_opts(opts)
1042 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1043 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1044 self.logger.info("writing ticket to %s"%file)
1045 ticket = SfaTicket(string=ticket_string)
1046 ticket.save_to_file(filename=file, save_parents=True)
1048 def redeem_ticket(self, opts, args):
1049 ticket_file = args[0]
1051 # get slice hrn from the ticket
1052 # use this to get the right slice credential
1053 ticket = SfaTicket(filename=ticket_file)
1055 slice_hrn = ticket.gidObject.get_hrn()
1056 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1057 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1058 user_cred = self.get_user_cred()
1059 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1061 # get a list of node hostnames from the RSpec
1062 tree = etree.parse(StringIO(ticket.rspec))
1063 root = tree.getroot()
1064 hostnames = root.xpath("./network/site/node/hostname/text()")
1066 # create an xmlrpc connection to the component manager at each of these
1067 # components and gall redeem_ticket
1069 for hostname in hostnames:
1071 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1072 server = self.get_server(hostname, CM_PORT, self.key_file, \
1073 self.cert_file, self.options.debug)
1074 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1075 self.logger.info("Success")
1076 except socket.gaierror:
1077 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1078 except Exception, e:
1079 self.logger.log_exc(e.message)
1082 # delete named slice
1083 def delete(self, opts, args):
1085 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1086 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1087 creds = [slice_cred]
1089 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1090 creds.append(delegated_cred)
1091 server = self.get_server_from_opts(opts)
1093 call_args = [slice_urn, creds]
1094 if self.server_supports_call_id_arg(server):
1095 call_args.append(unique_call_id())
1096 return server.DeleteSliver(*call_args)
1099 def start(self, opts, args):
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.get_server_from_opts(opts)
1108 return server.Start(slice_urn, creds)
1111 def stop(self, opts, args):
1113 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1114 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1115 creds = [slice_cred]
1117 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1118 creds.append(delegated_cred)
1119 server = self.get_server_from_opts(opts)
1120 return server.Stop(slice_urn, creds)
1123 def reset(self, opts, args):
1125 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1126 server = self.get_server_from_opts(opts)
1127 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1128 creds = [slice_cred]
1130 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1131 creds.append(delegated_cred)
1132 return server.reset_slice(creds, slice_urn)
1134 def renew(self, opts, args):
1136 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1137 server = self.get_server_from_opts(opts)
1138 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1139 creds = [slice_cred]
1141 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1142 creds.append(delegated_cred)
1145 call_args = [slice_urn, creds, time]
1146 if self.server_supports_call_id_arg(server):
1147 call_args.append(unique_call_id())
1148 return server.RenewSliver(*call_args)
1151 def status(self, opts, args):
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.get_server_from_opts(opts)
1160 call_args = [slice_urn, creds]
1161 if self.server_supports_call_id_arg(server):
1162 call_args.append(unique_call_id())
1163 result = server.SliverStatus(*call_args)
1166 save_variable_to_file(result, opts.file, opts.fileformat)
1169 def shutdown(self, opts, args):
1171 slice_urn = hrn_to_urn(slice_hrn, 'slice')
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 server = self.get_server_from_opts(opts)
1178 return server.Shutdown(slice_urn, creds)
1180 def print_help (self):
1181 self.sfi_parser.print_help()
1182 self.cmd_parser.print_help()
1185 # Main: parse arguments and dispatch to command
1188 self.sfi_parser = self.create_parser()
1189 (options, args) = self.sfi_parser.parse_args()
1190 self.options = options
1192 self.logger.setLevelFromOptVerbose(self.options.verbose)
1193 if options.hashrequest:
1194 self.hashrequest = True
1197 self.logger.critical("No command given. Use -h for help.")
1201 self.cmd_parser = self.create_cmd_parser(command)
1202 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
1205 self.logger.info("Command=%s" % command)
1206 if command in ("resources"):
1207 self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
1208 elif command in ("list", "show", "remove"):
1209 self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
1210 self.logger.debug('cmd_args %s' % cmd_args)
1213 self.dispatch(command, cmd_opts, cmd_args)
1215 self.logger.critical ("Unknown command %s"%command)
1221 if __name__ == "__main__":