3 # sfi -- slice-based facility interface
13 from lxml import etree
14 from StringIO import StringIO
15 from optparse import OptionParser
16 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
17 from sfa.util.sfalogging import sfi_logger
18 from sfa.trust.certificate import Keypair, Certificate
19 from sfa.trust.gid import GID
20 from sfa.trust.credential import Credential
21 from sfa.trust.sfaticket import SfaTicket
22 from sfa.util.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
23 from sfa.rspecs.rspec import RSpec
24 from sfa.rspecs.rspec_converter import RSpecConverter
25 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn
26 import sfa.client.xmlrpcprotocol as xmlrpcprotocol
27 from sfa.util.config import Config
28 from sfa.util.version import version_core
29 from sfa.util.cache import Cache
30 from sfa.rspecs.version_manager import VersionManager
31 from sfa.client.return_value import ReturnValue
36 # utility methods here
38 def display_rspec(rspec, format='rspec'):
40 tree = etree.parse(StringIO(rspec))
42 result = root.xpath("./network/site/node/hostname/text()")
43 elif format in ['ip']:
44 # The IP address is not yet part of the new RSpec
45 # so this doesn't do anything yet.
46 tree = etree.parse(StringIO(rspec))
48 result = root.xpath("./network/site/node/ipv4/text()")
55 def display_list(results):
56 for result in results:
59 def display_records(recordList, dump=False):
60 ''' Print all fields in the record'''
61 for record in recordList:
62 display_record(record, dump)
64 def display_record(record, dump=False):
68 info = record.getdict()
69 print "%s (%s)" % (info['hrn'], info['type'])
73 def filter_records(type, records):
75 for record in records:
76 if (record['type'] == type) or (type == "all"):
77 filtered_records.append(record)
78 return filtered_records
82 def save_variable_to_file(var, filename, format="text"):
83 f = open(filename, "w")
86 elif format == "pickled":
87 f.write(pickle.dumps(var))
89 # this should never happen
90 print "unknown output format", format
93 def save_rspec_to_file(rspec, filename):
94 if not filename.endswith(".rspec"):
95 filename = filename + ".rspec"
96 f = open(filename, 'w')
101 def save_records_to_file(filename, recordList, format="xml"):
104 for record in recordList:
106 save_record_to_file(filename + "." + str(index), record)
108 save_record_to_file(filename, record)
110 elif format == "xmllist":
111 f = open(filename, "w")
112 f.write("<recordlist>\n")
113 for record in recordList:
114 record = SfaRecord(dict=record)
115 f.write('<record hrn="' + record.get_name() + '" type="' + record.get_type() + '" />\n')
116 f.write("</recordlist>\n")
118 elif format == "hrnlist":
119 f = open(filename, "w")
120 for record in recordList:
121 record = SfaRecord(dict=record)
122 f.write(record.get_name() + "\n")
125 # this should never happen
126 print "unknown output format", format
128 def save_record_to_file(filename, record):
129 if record['type'] in ['user']:
130 record = UserRecord(dict=record)
131 elif record['type'] in ['slice']:
132 record = SliceRecord(dict=record)
133 elif record['type'] in ['node']:
134 record = NodeRecord(dict=record)
135 elif record['type'] in ['authority', 'ma', 'sa']:
136 record = AuthorityRecord(dict=record)
138 record = SfaRecord(dict=record)
139 str = record.save_to_string()
140 f=codecs.open(filename, encoding='utf-8',mode="w")
147 def load_record_from_file(filename):
148 f=codecs.open(filename, encoding="utf-8", mode="r")
151 record = SfaRecord(string=str)
156 def unique_call_id(): return uuid.uuid4().urn
160 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user']
162 # dummy to meet Sfi's expectations for its 'options' field
163 # i.e. s/t we can do setattr on
167 def __init__ (self,options=None):
168 if options is None: options=Sfi.DummyOptions()
169 for opt in Sfi.required_options:
170 if not hasattr(options,opt): setattr(options,opt,None)
171 if not hasattr(options,'sfi_dir'): options.sfi_dir=os.path.expanduser("~/.sfi/")
172 # xxx oops, this is dangerous, sounds like ww sometimes have discrepency
173 # would be safer to remove self.sfi_dir altogether
174 self.sfi_dir = options.sfi_dir
175 self.options = options
179 self.authority = None
180 self.hashrequest = False
181 self.logger = sfi_logger
182 self.logger.enable_console()
184 def create_cmd_parser(self, command, additional_cmdargs=None):
185 cmdargs = {"list": "authority",
190 "aggregates": "[name]",
191 "registries": "[name]",
192 "create_gid": "[name]",
194 "get_trusted_certs": "cred",
196 "resources": "[name]",
197 "create": "name rspec",
198 "get_ticket": "name rspec",
199 "redeem_ticket": "ticket",
211 if additional_cmdargs:
212 cmdargs.update(additional_cmdargs)
214 if command not in cmdargs:
215 msg="Invalid command\n"
217 msg += ','.join(cmdargs.keys())
218 self.logger.critical(msg)
221 parser = OptionParser(usage="sfi [sfi_options] %s [options] %s" \
222 % (command, cmdargs[command]))
224 # user specifies remote aggregate/sm/component
225 if command in ("resources", "slices", "create", "delete", "start", "stop",
226 "restart", "shutdown", "get_ticket", "renew", "status"):
227 parser.add_option("-a", "--aggregate", dest="aggregate",
228 default=None, help="aggregate host")
229 parser.add_option("-p", "--port", dest="port",
230 default=AGGREGATE_PORT, help="aggregate port")
231 parser.add_option("-c", "--component", dest="component", default=None,
232 help="component hrn")
233 parser.add_option("-d", "--delegate", dest="delegate", default=None,
235 help="Include a credential delegated to the user's root"+\
236 "authority in set of credentials for this call")
238 # registy filter option
239 if command in ("list", "show", "remove"):
240 parser.add_option("-t", "--type", dest="type", type="choice",
241 help="type filter ([all]|user|slice|authority|node|aggregate)",
242 choices=("all", "user", "slice", "authority", "node", "aggregate"),
245 if command in ("resources"):
246 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
247 help="schema type and version of resulting RSpec")
248 parser.add_option("-f", "--format", dest="format", type="choice",
249 help="display format ([xml]|dns|ip)", default="xml",
250 choices=("xml", "dns", "ip"))
251 #panos: a new option to define the type of information about resources a user is interested in
252 parser.add_option("-i", "--info", dest="info",
253 help="optional component information", default=None)
256 # 'create' does return the new rspec, makes sense to save that too
257 if command in ("resources", "show", "list", "create_gid", 'create'):
258 parser.add_option("-o", "--output", dest="file",
259 help="output XML to file", metavar="FILE", default=None)
261 if command in ("show", "list"):
262 parser.add_option("-f", "--format", dest="format", type="choice",
263 help="display format ([text]|xml)", default="text",
264 choices=("text", "xml"))
266 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
267 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
268 choices=("xml", "xmllist", "hrnlist"))
270 if command in ("status", "version"):
271 parser.add_option("-o", "--output", dest="file",
272 help="output dictionary to file", metavar="FILE", default=None)
273 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
274 help="output file format ([text]|pickled)", default="text",
275 choices=("text","pickled"))
277 if command in ("delegate"):
278 parser.add_option("-u", "--user",
279 action="store_true", dest="delegate_user", default=False,
280 help="delegate user credential")
281 parser.add_option("-s", "--slice", dest="delegate_slice",
282 help="delegate slice credential", metavar="HRN", default=None)
284 if command in ("version"):
285 parser.add_option("-a", "--aggregate", dest="aggregate",
286 default=None, help="aggregate host")
287 parser.add_option("-p", "--port", dest="port",
288 default=AGGREGATE_PORT, help="aggregate port")
289 parser.add_option("-R","--registry-version",
290 action="store_true", dest="version_registry", default=False,
291 help="probe registry version instead of slicemgr")
292 parser.add_option("-l","--local",
293 action="store_true", dest="version_local", default=False,
294 help="display version of the local client")
299 def create_parser(self):
301 # Generate command line parser
302 parser = OptionParser(usage="sfi [options] command [command_options] [command_args]",
303 description="Commands: gid,list,show,remove,add,update,nodes,slices,resources,create,delete,start,stop,reset")
304 parser.add_option("-r", "--registry", dest="registry",
305 help="root registry", metavar="URL", default=None)
306 parser.add_option("-s", "--slicemgr", dest="sm",
307 help="slice manager", metavar="URL", default=None)
308 default_sfi_dir = os.path.expanduser("~/.sfi/")
309 parser.add_option("-d", "--dir", dest="sfi_dir",
310 help="config & working directory - default is " + default_sfi_dir,
311 metavar="PATH", default=default_sfi_dir)
312 parser.add_option("-u", "--user", dest="user",
313 help="user name", metavar="HRN", default=None)
314 parser.add_option("-a", "--auth", dest="auth",
315 help="authority name", metavar="HRN", default=None)
316 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
317 help="verbose mode - cumulative")
318 parser.add_option("-D", "--debug",
319 action="store_true", dest="debug", default=False,
320 help="Debug (xml-rpc) protocol messages")
321 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
322 help="RPC protocol (xmlrpc or soap)")
323 parser.add_option("-k", "--hashrequest",
324 action="store_true", dest="hashrequest", default=False,
325 help="Create a hash of the request that will be authenticated on the server")
326 parser.add_option("-t", "--timeout", dest="timeout", default=None,
327 help="Amout of time tom wait before timing out the request")
328 parser.disable_interspersed_args()
333 def read_config(self):
334 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
336 config = Config (config_file)
338 self.logger.critical("Failed to read configuration file %s"%config_file)
339 self.logger.info("Make sure to remove the export clauses and to add quotes")
340 if self.options.verbose==0:
341 self.logger.info("Re-run with -v for more details")
343 self.logger.log_exc("Could not read config file %s"%config_file)
348 if (self.options.sm is not None):
349 self.sm_url = self.options.sm
350 elif hasattr(config, "SFI_SM"):
351 self.sm_url = config.SFI_SM
353 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
357 if (self.options.registry is not None):
358 self.reg_url = self.options.registry
359 elif hasattr(config, "SFI_REGISTRY"):
360 self.reg_url = config.SFI_REGISTRY
362 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
367 if (self.options.user is not None):
368 self.user = self.options.user
369 elif hasattr(config, "SFI_USER"):
370 self.user = config.SFI_USER
372 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
376 if (self.options.auth is not None):
377 self.authority = self.options.auth
378 elif hasattr(config, "SFI_AUTH"):
379 self.authority = config.SFI_AUTH
381 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
389 # Establish Connection to SliceMgr and Registry Servers
391 def set_servers(self):
394 # Get key and certificate
395 key_file = self.get_key_file()
396 cert_file = self.get_cert_file(key_file)
397 self.key = Keypair(filename=key_file)
398 self.key_file = key_file
399 self.cert_file = cert_file
400 self.cert = GID(filename=cert_file)
401 self.logger.info("Contacting Registry at: %s"%self.reg_url)
402 self.registry = xmlrpcprotocol.server_proxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
403 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
404 self.slicemgr = xmlrpcprotocol.server_proxy(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
407 def get_cached_server_version(self, server):
408 # check local cache first
411 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
412 cache_key = server.url + "-version"
414 cache = Cache(cache_file)
417 self.logger.info("Local cache not found at: %s" % cache_file)
420 version = cache.get(cache_key)
423 result = server.GetVersion()
424 version= ReturnValue.get_value(result)
425 # cache version for 24 hours
426 cache.add(cache_key, version, ttl= 60*60*24)
427 self.logger.info("Updating cache file %s" % cache_file)
428 cache.save_to_file(cache_file)
433 def server_supports_options_arg(self, server):
435 Returns true if server support the optional call_id arg, false otherwise.
437 server_version = self.get_cached_server_version(server)
438 if 'sfa' in server_version and 'code_tag' in server_version:
439 code_tag = server_version['code_tag']
440 code_tag_parts = code_tag.split("-")
442 version_parts = code_tag_parts[0].split(".")
443 major, minor = version_parts[0], version_parts[1]
444 rev = code_tag_parts[1]
451 # Get various credential and spec files
453 # Establishes limiting conventions
454 # - conflates MAs and SAs
455 # - assumes last token in slice name is unique
457 # Bootstraps credentials
458 # - bootstrap user credential from self-signed certificate
459 # - bootstrap authority credential from user credential
460 # - bootstrap slice credential from user credential
464 def get_key_file(self):
465 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
466 if (os.path.isfile(file)):
469 self.logger.error("Key file %s does not exist"%file)
473 def get_cert_file(self, key_file):
475 cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
476 if (os.path.isfile(cert_file)):
477 # we'd perfer to use Registry issued certs instead of self signed certs.
478 # if this is a Registry cert (GID) then we are done
479 gid = GID(filename=cert_file)
483 # generate self signed certificate
484 k = Keypair(filename=key_file)
485 cert = Certificate(subject=self.user)
487 cert.set_issuer(k, self.user)
489 self.logger.info("Writing self-signed certificate to %s"%cert_file)
490 cert.save_to_file(cert_file)
492 # try to get registry issued cert
494 self.logger.info("Getting Registry issued cert")
496 # *hack. need to set registyr before _get_gid() is called
497 self.registry = xmlrpcprotocol.server_proxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
498 gid = self._get_gid(type='user')
500 self.logger.info("Writing certificate to %s"%cert_file)
501 gid.save_to_file(cert_file)
503 self.logger.info("Failed to download Registry issued cert")
507 def get_cached_gid(self, file):
512 if (os.path.isfile(file)):
513 gid = GID(filename=file)
517 def get_gid(self, opts, args):
519 Get the specify gid and save it to file
524 gid = self._get_gid(hrn)
525 self.logger.debug("Sfi.get_gid-> %s" % gid.save_to_string(save_parents=True))
528 def _get_gid(self, hrn=None, type=None):
530 git_gid helper. Retrive the gid from the registry and save it to file.
536 gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
537 gid = self.get_cached_gid(gidfile)
539 user_cred = self.get_user_cred()
540 records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
542 raise RecordNotFound(args[0])
547 if type == rec['type']:
550 raise RecordNotFound(args[0])
552 gid = GID(string=record['gid'])
553 self.logger.info("Writing gid to %s"%gidfile)
554 gid.save_to_file(filename=gidfile)
558 def get_cached_credential(self, file):
560 Return a cached credential only if it hasn't expired.
562 if (os.path.isfile(file)):
563 credential = Credential(filename=file)
564 # make sure it isnt expired
565 if not credential.get_expiration or \
566 datetime.datetime.today() < credential.get_expiration():
570 def get_user_cred(self):
571 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
572 return self.get_cred(file, 'user', self.user)
574 def get_auth_cred(self):
575 if not self.authority:
576 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
578 file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
579 return self.get_cred(file, 'authority', self.authority)
581 def get_slice_cred(self, name):
582 file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
583 return self.get_cred(file, 'slice', name)
585 def get_cred(self, file, type, hrn):
586 # attempt to load a cached credential
587 cred = self.get_cached_credential(file)
590 cert_string = self.cert.save_to_string(save_parents=True)
591 user_name = self.user.replace(self.authority + ".", '')
592 if user_name.count(".") > 0:
593 user_name = user_name.replace(".", '_')
594 self.user = self.authority + "." + user_name
595 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
597 # bootstrap slice credential from user credential
598 user_cred = self.get_user_cred().save_to_string(save_parents=True)
599 cred_str = self.registry.GetCredential(user_cred, hrn, type)
602 self.logger.critical("Failed to get %s credential" % type)
605 cred = Credential(string=cred_str)
606 cred.save_to_file(file, save_parents=True)
607 self.logger.info("Writing %s credential to %s" %(type, file))
612 def get_rspec_file(self, rspec):
613 if (os.path.isabs(rspec)):
616 file = os.path.join(self.options.sfi_dir, rspec)
617 if (os.path.isfile(file)):
620 self.logger.critical("No such rspec file %s"%rspec)
623 def get_record_file(self, record):
624 if (os.path.isabs(record)):
627 file = os.path.join(self.options.sfi_dir, record)
628 if (os.path.isfile(file)):
631 self.logger.critical("No such registry record file %s"%record)
634 def load_publickey_string(self, fn):
636 key_string = f.read()
638 # if the filename is a private key file, then extract the public key
639 if "PRIVATE KEY" in key_string:
640 outfn = tempfile.mktemp()
641 cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
644 key_string = f.read()
650 def get_component_proxy_from_hrn(self, hrn):
651 # direct connection to the nodes component manager interface
652 user_cred = self.get_user_cred().save_to_string(save_parents=True)
653 records = self.registry.Resolve(hrn, user_cred)
654 records = filter_records('node', records)
656 self.logger.warning("No such component:%r"% opts.component)
659 return self.server_proxy(record['hostname'], CM_PORT, self.key_file, self.cert_file)
661 def server_proxy(self, host, port, keyfile, certfile):
663 Return an instance of an xmlrpc server connection
665 # port is appended onto the domain, before the path. Should look like:
666 # http://domain:port/path
667 host_parts = host.split('/')
668 host_parts[0] = host_parts[0] + ":" + str(port)
669 url = "http://%s" % "/".join(host_parts)
670 return xmlrpcprotocol.server_proxy(url, keyfile, certfile, timeout=self.options.timeout, verbose=self.options.debug)
672 # xxx opts could be retrieved in self.options
673 def server_proxy_from_opts(self, opts):
675 Return instance of an xmlrpc connection to a slice manager, aggregate
676 or component server depending on the specified opts
678 server = self.slicemgr
679 # direct connection to an aggregate
680 if hasattr(opts, 'aggregate') and opts.aggregate:
681 server = self.server_proxy(opts.aggregate, opts.port, self.key_file, self.cert_file)
682 # direct connection to the nodes component manager interface
683 if hasattr(opts, 'component') and opts.component:
684 server = self.get_component_proxy_from_hrn(opts.component)
687 #==========================================================================
688 # Following functions implement the commands
690 # Registry-related commands
691 #==========================================================================
693 def dispatch(self, command, cmd_opts, cmd_args):
694 return getattr(self, command)(cmd_opts, cmd_args)
696 def create_gid(self, opts, args):
701 user_cred = self.get_user_cred().save_to_string(save_parents=True)
702 gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
706 filename = os.sep.join([self.sfi_dir, '%s.gid' % target_hrn])
707 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
708 GID(string=gid).save_to_file(filename)
711 # list entires in named authority registry
712 def list(self, opts, args):
717 user_cred = self.get_user_cred().save_to_string(save_parents=True)
719 list = self.registry.List(hrn, user_cred)
721 raise Exception, "Not enough parameters for the 'list' command"
723 # filter on person, slice, site, node, etc.
724 # THis really should be in the self.filter_records funct def comment...
725 list = filter_records(opts.type, list)
727 print "%s (%s)" % (record['hrn'], record['type'])
729 save_records_to_file(opts.file, list, opts.fileformat)
732 # show named registry record
733 def show(self, opts, args):
738 user_cred = self.get_user_cred().save_to_string(save_parents=True)
739 records = self.registry.Resolve(hrn, user_cred)
740 records = filter_records(opts.type, records)
742 print "No record of type", opts.type
743 for record in records:
744 if record['type'] in ['user']:
745 record = UserRecord(dict=record)
746 elif record['type'] in ['slice']:
747 record = SliceRecord(dict=record)
748 elif record['type'] in ['node']:
749 record = NodeRecord(dict=record)
750 elif record['type'].startswith('authority'):
751 record = AuthorityRecord(dict=record)
753 record = SfaRecord(dict=record)
754 if (opts.format == "text"):
757 print record.save_to_string()
759 save_records_to_file(opts.file, records, opts.fileformat)
762 def delegate(self, opts, args):
764 delegee_hrn = args[0]
765 if opts.delegate_user:
766 user_cred = self.get_user_cred()
767 cred = self.delegate_cred(user_cred, delegee_hrn)
768 elif opts.delegate_slice:
769 slice_cred = self.get_slice_cred(opts.delegate_slice)
770 cred = self.delegate_cred(slice_cred, delegee_hrn)
772 self.logger.warning("Must specify either --user or --slice <hrn>")
774 delegated_cred = Credential(string=cred)
775 object_hrn = delegated_cred.get_gid_object().get_hrn()
776 if opts.delegate_user:
777 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
778 + get_leaf(object_hrn) + ".cred")
779 elif opts.delegate_slice:
780 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
781 + get_leaf(object_hrn) + ".cred")
783 delegated_cred.save_to_file(dest_fn, save_parents=True)
785 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
787 def delegate_cred(self, object_cred, hrn):
788 # the gid and hrn of the object we are delegating
789 if isinstance(object_cred, str):
790 object_cred = Credential(string=object_cred)
791 object_gid = object_cred.get_gid_object()
792 object_hrn = object_gid.get_hrn()
794 if not object_cred.get_privileges().get_all_delegate():
795 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
798 # the delegating user's gid
799 caller_gid = self._get_gid(self.user)
800 caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
802 # the gid of the user who will be delegated to
803 delegee_gid = self._get_gid(hrn)
804 delegee_hrn = delegee_gid.get_hrn()
805 delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
806 delegee_gid.save_to_file(filename=delegee_gidfile)
807 dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
808 return dcred.save_to_string(save_parents=True)
810 # removed named registry record
811 # - have to first retrieve the record to be removed
812 def remove(self, opts, args):
813 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
821 return self.registry.Remove(hrn, auth_cred, type)
823 # add named registry record
824 def add(self, opts, args):
825 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
829 record_filepath = args[0]
830 rec_file = self.get_record_file(record_filepath)
831 record = load_record_from_file(rec_file).as_dict()
832 return self.registry.Register(record, auth_cred)
834 # update named registry entry
835 def update(self, opts, args):
836 user_cred = self.get_user_cred()
840 rec_file = self.get_record_file(args[0])
841 record = load_record_from_file(rec_file)
842 if record['type'] == "user":
843 if record.get_name() == user_cred.get_gid_object().get_hrn():
844 cred = user_cred.save_to_string(save_parents=True)
846 cred = self.get_auth_cred().save_to_string(save_parents=True)
847 elif record['type'] in ["slice"]:
849 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
850 except xmlrpcprotocol.ServerException, e:
851 # XXX smbaker -- once we have better error return codes, update this
852 # to do something better than a string compare
853 if "Permission error" in e.args[0]:
854 cred = self.get_auth_cred().save_to_string(save_parents=True)
857 elif record.get_type() in ["authority"]:
858 cred = self.get_auth_cred().save_to_string(save_parents=True)
859 elif record.get_type() == 'node':
860 cred = self.get_auth_cred().save_to_string(save_parents=True)
862 raise "unknown record type" + record.get_type()
863 record = record.as_dict()
864 return self.registry.Update(record, cred)
866 def get_trusted_certs(self, opts, args):
868 return uhe trusted certs at this interface
870 trusted_certs = self.registry.get_trusted_certs()
871 for trusted_cert in trusted_certs:
872 gid = GID(string=trusted_cert)
874 cert = Certificate(string=trusted_cert)
875 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
878 def aggregates(self, opts, args):
880 return a list of details about known aggregates
882 user_cred = self.get_user_cred().save_to_string(save_parents=True)
887 result = self.registry.get_aggregates(user_cred, hrn)
891 def registries(self, opts, args):
893 return a list of details about known registries
895 user_cred = self.get_user_cred().save_to_string(save_parents=True)
899 result = self.registry.get_registries(user_cred, hrn)
904 # ==================================================================
905 # Slice-related commands
906 # ==================================================================
908 def version(self, opts, args):
909 if opts.version_local:
910 version=version_core()
912 if opts.version_registry:
915 server = self.server_proxy_from_opts(opts)
916 result = server.GetVersion()
917 version = ReturnValue.get_value(result)
918 for (k,v) in version.iteritems():
919 print "%-20s: %s"%(k,v)
921 save_variable_to_file(version, opts.file, opts.fileformat)
923 # list instantiated slices
924 def slices(self, opts, args):
926 list instantiated slices
928 user_cred = self.get_user_cred().save_to_string(save_parents=True)
931 delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
932 creds.append(delegated_cred)
933 server = self.server_proxy_from_opts(opts)
935 if self.server_supports_options_arg(server):
936 options = {'call_id': unique_call_id()}
937 call_args.append(options)
938 result = server.ListSlices(*call_args)
939 value = ReturnValue.get_value(result)
943 # show rspec for named slice
944 def resources(self, opts, args):
945 user_cred = self.get_user_cred().save_to_string(save_parents=True)
946 server = self.slicemgr
947 server = self.server_proxy_from_opts(opts)
949 options = {'call_id': unique_call_id()}
950 #panos add info options
952 options['info'] = opts.info
955 cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
957 options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
963 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
964 creds.append(delegated_cred)
965 if opts.rspec_version:
966 version_manager = VersionManager()
967 server_version = self.get_cached_server_version(server)
968 if 'sfa' in server_version:
969 # just request the version the client wants
970 options['rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
972 # this must be a protogeni aggregate. We should request a v2 ad rspec
973 # regardless of what the client user requested
974 options['rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
976 call_args = [creds, options]
977 result = server.ListResources(*call_args)
978 value = ReturnValue.get_value(result)
979 if opts.file is None:
980 display_rspec(value, opts.format)
982 save_rspec_to_file(value, opts.file)
985 # created named slice with given rspec
986 def create(self, opts, args):
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)
994 if hasattr(opts, 'aggregate') and opts.aggregate:
995 delegated_cred = None
997 # delegate the cred to the callers root authority
998 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)+'.slicemanager')
999 #delegated_cred = self.delegate_cred(slice_cred, get_authority(slice_hrn))
1000 #creds.append(delegated_cred)
1002 rspec_file = self.get_rspec_file(args[1])
1003 rspec = open(rspec_file).read()
1005 # need to pass along user keys to the aggregate.
1007 # { urn: urn:publicid:IDN+emulab.net+user+alice
1008 # keys: [<ssh key A>, <ssh key B>]
1011 slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
1012 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1013 slice_record = slice_records[0]
1014 user_hrns = slice_record['researcher']
1015 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1016 user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
1018 if 'sfa' not in server_version:
1019 users = pg_users_arg(user_records)
1020 rspec = RSpec(rspec)
1021 rspec.filter({'component_manager_id': server_version['urn']})
1022 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1023 creds = [slice_cred]
1025 users = sfa_users_arg(user_records, slice_record)
1026 creds = [slice_cred]
1028 creds.append(delegated_cred)
1029 call_args = [slice_urn, creds, rspec, users]
1030 if self.server_supports_options_arg(server):
1031 options = {'call_id': unique_call_id()}
1032 call_args.append(options)
1033 result = server.CreateSliver(*call_args)
1034 value = ReturnValue.get_value(result)
1035 if opts.file is None:
1038 save_rspec_to_file (value, opts.file)
1041 # get a ticket for the specified slice
1042 def get_ticket(self, opts, args):
1043 slice_hrn, rspec_path = args[0], args[1]
1044 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1045 user_cred = self.get_user_cred()
1046 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1047 creds = [slice_cred]
1049 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1050 creds.append(delegated_cred)
1051 rspec_file = self.get_rspec_file(rspec_path)
1052 rspec = open(rspec_file).read()
1053 server = self.server_proxy_from_opts(opts)
1054 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1055 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1056 self.logger.info("writing ticket to %s"%file)
1057 ticket = SfaTicket(string=ticket_string)
1058 ticket.save_to_file(filename=file, save_parents=True)
1060 def redeem_ticket(self, opts, args):
1061 ticket_file = args[0]
1063 # get slice hrn from the ticket
1064 # use this to get the right slice credential
1065 ticket = SfaTicket(filename=ticket_file)
1067 slice_hrn = ticket.gidObject.get_hrn()
1068 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1069 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1070 user_cred = self.get_user_cred()
1071 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1073 # get a list of node hostnames from the RSpec
1074 tree = etree.parse(StringIO(ticket.rspec))
1075 root = tree.getroot()
1076 hostnames = root.xpath("./network/site/node/hostname/text()")
1078 # create an xmlrpc connection to the component manager at each of these
1079 # components and gall redeem_ticket
1081 for hostname in hostnames:
1083 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1084 server = self.server_proxy(hostname, CM_PORT, self.key_file, \
1085 self.cert_file, self.options.debug)
1086 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1087 self.logger.info("Success")
1088 except socket.gaierror:
1089 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1090 except Exception, e:
1091 self.logger.log_exc(e.message)
1094 # delete named slice
1095 def delete(self, opts, args):
1097 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1098 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1099 creds = [slice_cred]
1101 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1102 creds.append(delegated_cred)
1103 server = self.server_proxy_from_opts(opts)
1104 call_args = [slice_urn, creds]
1105 if self.server_supports_options_arg(server):
1106 options = {'call_id': unique_call_id()}
1107 call_args.append(options)
1108 return server.DeleteSliver(*call_args)
1111 def start(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.server_proxy_from_opts(opts)
1120 return server.Start(slice_urn, creds)
1123 def stop(self, opts, args):
1125 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1126 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1127 creds = [slice_cred]
1129 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1130 creds.append(delegated_cred)
1131 server = self.server_proxy_from_opts(opts)
1132 return server.Stop(slice_urn, creds)
1135 def reset(self, opts, args):
1137 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1138 server = self.server_proxy_from_opts(opts)
1139 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1140 creds = [slice_cred]
1142 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1143 creds.append(delegated_cred)
1144 return server.reset_slice(creds, slice_urn)
1146 def renew(self, opts, args):
1148 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1149 server = self.server_proxy_from_opts(opts)
1150 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1151 creds = [slice_cred]
1153 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1154 creds.append(delegated_cred)
1157 call_args = [slice_urn, creds, time]
1158 if self.server_supports_options_arg(server):
1159 options = {'call_id': unique_call_id()}
1160 call_args.append(options)
1161 result = server.RenewSliver(*call_args)
1162 value = ReturnValue.get_value(result)
1166 def status(self, opts, args):
1168 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1169 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1170 creds = [slice_cred]
1172 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1173 creds.append(delegated_cred)
1174 server = self.server_proxy_from_opts(opts)
1175 call_args = [slice_urn, creds]
1176 if self.server_supports_options_arg(server):
1177 options = {'call_id': unique_call_id()}
1178 call_args.append(options)
1179 result = server.SliverStatus(*call_args)
1180 value = ReturnValue.get_value(result)
1183 save_variable_to_file(value, opts.file, opts.fileformat)
1186 def shutdown(self, opts, args):
1188 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1189 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1190 creds = [slice_cred]
1192 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1193 creds.append(delegated_cred)
1194 server = self.server_proxy_from_opts(opts)
1195 return server.Shutdown(slice_urn, creds)
1197 def print_help (self):
1198 self.sfi_parser.print_help()
1199 self.cmd_parser.print_help()
1202 # Main: parse arguments and dispatch to command
1205 self.sfi_parser = self.create_parser()
1206 (options, args) = self.sfi_parser.parse_args()
1207 self.options = options
1209 self.logger.setLevelFromOptVerbose(self.options.verbose)
1210 if options.hashrequest:
1211 self.hashrequest = True
1214 self.logger.critical("No command given. Use -h for help.")
1218 self.cmd_parser = self.create_cmd_parser(command)
1219 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
1222 self.logger.info("Command=%s" % command)
1223 if command in ("resources"):
1224 self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
1225 elif command in ("list", "show", "remove"):
1226 self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
1227 self.logger.debug('cmd_args %s' % cmd_args)
1230 self.dispatch(command, cmd_opts, cmd_args)
1232 self.logger.critical ("Unknown command %s"%command)
1238 if __name__ == "__main__":