3 # sfi -- slice-based facility interface
14 from lxml import etree
15 from StringIO import StringIO
16 from optparse import OptionParser
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
23 from sfa.util.sfalogging import sfi_logger
24 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn
25 from sfa.util.config import Config
26 from sfa.util.version import version_core
27 from sfa.util.cache import Cache
28 from sfa.util.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
30 from sfa.rspecs.rspec import RSpec
31 from sfa.rspecs.rspec_converter import RSpecConverter
32 from sfa.rspecs.version_manager import VersionManager
33 from sfa.client.return_value import ReturnValue
35 import sfa.client.xmlrpcprotocol as xmlrpcprotocol
36 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
41 # utility methods here
43 def display_rspec(rspec, format='rspec'):
45 tree = etree.parse(StringIO(rspec))
47 result = root.xpath("./network/site/node/hostname/text()")
48 elif format in ['ip']:
49 # The IP address is not yet part of the new RSpec
50 # so this doesn't do anything yet.
51 tree = etree.parse(StringIO(rspec))
53 result = root.xpath("./network/site/node/ipv4/text()")
60 def display_list(results):
61 for result in results:
64 def display_records(recordList, dump=False):
65 ''' Print all fields in the record'''
66 for record in recordList:
67 display_record(record, dump)
69 def display_record(record, dump=False):
73 info = record.getdict()
74 print "%s (%s)" % (info['hrn'], info['type'])
78 def filter_records(type, records):
80 for record in records:
81 if (record['type'] == type) or (type == "all"):
82 filtered_records.append(record)
83 return filtered_records
87 def save_variable_to_file(var, filename, format="text"):
88 f = open(filename, "w")
91 elif format == "pickled":
92 f.write(pickle.dumps(var))
94 # this should never happen
95 print "unknown output format", format
98 def save_rspec_to_file(rspec, filename):
99 if not filename.endswith(".rspec"):
100 filename = filename + ".rspec"
101 f = open(filename, 'w')
106 def save_records_to_file(filename, recordList, format="xml"):
109 for record in recordList:
111 save_record_to_file(filename + "." + str(index), record)
113 save_record_to_file(filename, record)
115 elif format == "xmllist":
116 f = open(filename, "w")
117 f.write("<recordlist>\n")
118 for record in recordList:
119 record = SfaRecord(dict=record)
120 f.write('<record hrn="' + record.get_name() + '" type="' + record.get_type() + '" />\n')
121 f.write("</recordlist>\n")
123 elif format == "hrnlist":
124 f = open(filename, "w")
125 for record in recordList:
126 record = SfaRecord(dict=record)
127 f.write(record.get_name() + "\n")
130 # this should never happen
131 print "unknown output format", format
133 def save_record_to_file(filename, record):
134 if record['type'] in ['user']:
135 record = UserRecord(dict=record)
136 elif record['type'] in ['slice']:
137 record = SliceRecord(dict=record)
138 elif record['type'] in ['node']:
139 record = NodeRecord(dict=record)
140 elif record['type'] in ['authority', 'ma', 'sa']:
141 record = AuthorityRecord(dict=record)
143 record = SfaRecord(dict=record)
144 str = record.save_to_string()
145 f=codecs.open(filename, encoding='utf-8',mode="w")
152 def load_record_from_file(filename):
153 f=codecs.open(filename, encoding="utf-8", mode="r")
156 record = SfaRecord(string=str)
161 def unique_call_id(): return uuid.uuid4().urn
165 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user']
167 # dummy to meet Sfi's expectations for its 'options' field
168 # i.e. s/t we can do setattr on
172 def __init__ (self,options=None):
173 if options is None: options=Sfi.DummyOptions()
174 for opt in Sfi.required_options:
175 if not hasattr(options,opt): setattr(options,opt,None)
176 if not hasattr(options,'sfi_dir'): options.sfi_dir=os.path.expanduser("~/.sfi/")
177 # xxx oops, this is dangerous, sounds like ww sometimes have discrepency
178 # would be safer to remove self.sfi_dir altogether
179 self.sfi_dir = options.sfi_dir
180 self.options = options
184 self.authority = None
185 self.hashrequest = False
186 self.logger = sfi_logger
187 self.logger.enable_console()
189 def create_cmd_parser(self, command, additional_cmdargs=None):
190 cmdargs = {"list": "authority",
195 "aggregates": "[name]",
196 "registries": "[name]",
197 "create_gid": "[name]",
199 "get_trusted_certs": "cred",
201 "resources": "[name]",
202 "create": "name rspec",
203 "get_ticket": "name rspec",
204 "redeem_ticket": "ticket",
216 if additional_cmdargs:
217 cmdargs.update(additional_cmdargs)
219 if command not in cmdargs:
220 msg="Invalid command\n"
222 msg += ','.join(cmdargs.keys())
223 self.logger.critical(msg)
226 parser = OptionParser(usage="sfi [sfi_options] %s [options] %s" \
227 % (command, cmdargs[command]))
229 # user specifies remote aggregate/sm/component
230 if command in ("resources", "slices", "create", "delete", "start", "stop",
231 "restart", "shutdown", "get_ticket", "renew", "status"):
232 parser.add_option("-a", "--aggregate", dest="aggregate",
233 default=None, help="aggregate host")
234 parser.add_option("-p", "--port", dest="port",
235 default=AGGREGATE_PORT, help="aggregate port")
236 parser.add_option("-c", "--component", dest="component", default=None,
237 help="component hrn")
238 parser.add_option("-d", "--delegate", dest="delegate", default=None,
240 help="Include a credential delegated to the user's root"+\
241 "authority in set of credentials for this call")
243 # registy filter option
244 if command in ("list", "show", "remove"):
245 parser.add_option("-t", "--type", dest="type", type="choice",
246 help="type filter ([all]|user|slice|authority|node|aggregate)",
247 choices=("all", "user", "slice", "authority", "node", "aggregate"),
250 if command in ("resources"):
251 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
252 help="schema type and version of resulting RSpec")
253 parser.add_option("-f", "--format", dest="format", type="choice",
254 help="display format ([xml]|dns|ip)", default="xml",
255 choices=("xml", "dns", "ip"))
256 #panos: a new option to define the type of information about resources a user is interested in
257 parser.add_option("-i", "--info", dest="info",
258 help="optional component information", default=None)
261 # 'create' does return the new rspec, makes sense to save that too
262 if command in ("resources", "show", "list", "create_gid", 'create'):
263 parser.add_option("-o", "--output", dest="file",
264 help="output XML to file", metavar="FILE", default=None)
266 if command in ("show", "list"):
267 parser.add_option("-f", "--format", dest="format", type="choice",
268 help="display format ([text]|xml)", default="text",
269 choices=("text", "xml"))
271 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
272 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
273 choices=("xml", "xmllist", "hrnlist"))
275 if command in ("status", "version"):
276 parser.add_option("-o", "--output", dest="file",
277 help="output dictionary to file", metavar="FILE", default=None)
278 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
279 help="output file format ([text]|pickled)", default="text",
280 choices=("text","pickled"))
282 if command in ("delegate"):
283 parser.add_option("-u", "--user",
284 action="store_true", dest="delegate_user", default=False,
285 help="delegate user credential")
286 parser.add_option("-s", "--slice", dest="delegate_slice",
287 help="delegate slice credential", metavar="HRN", default=None)
289 if command in ("version"):
290 parser.add_option("-a", "--aggregate", dest="aggregate",
291 default=None, help="aggregate host")
292 parser.add_option("-p", "--port", dest="port",
293 default=AGGREGATE_PORT, help="aggregate port")
294 parser.add_option("-R","--registry-version",
295 action="store_true", dest="version_registry", default=False,
296 help="probe registry version instead of slicemgr")
297 parser.add_option("-l","--local",
298 action="store_true", dest="version_local", default=False,
299 help="display version of the local client")
304 def create_parser(self):
306 # Generate command line parser
307 parser = OptionParser(usage="sfi [options] command [command_options] [command_args]",
308 description="Commands: gid,list,show,remove,add,update,nodes,slices,resources,create,delete,start,stop,reset")
309 parser.add_option("-r", "--registry", dest="registry",
310 help="root registry", metavar="URL", default=None)
311 parser.add_option("-s", "--slicemgr", dest="sm",
312 help="slice manager", metavar="URL", default=None)
313 default_sfi_dir = os.path.expanduser("~/.sfi/")
314 parser.add_option("-d", "--dir", dest="sfi_dir",
315 help="config & working directory - default is " + default_sfi_dir,
316 metavar="PATH", default=default_sfi_dir)
317 parser.add_option("-u", "--user", dest="user",
318 help="user name", metavar="HRN", default=None)
319 parser.add_option("-a", "--auth", dest="auth",
320 help="authority name", metavar="HRN", default=None)
321 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
322 help="verbose mode - cumulative")
323 parser.add_option("-D", "--debug",
324 action="store_true", dest="debug", default=False,
325 help="Debug (xml-rpc) protocol messages")
326 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
327 help="RPC protocol (xmlrpc or soap)")
328 parser.add_option("-k", "--hashrequest",
329 action="store_true", dest="hashrequest", default=False,
330 help="Create a hash of the request that will be authenticated on the server")
331 parser.add_option("-t", "--timeout", dest="timeout", default=None,
332 help="Amout of time tom wait before timing out the request")
333 parser.disable_interspersed_args()
338 def read_config(self):
339 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
341 config = Config (config_file)
343 self.logger.critical("Failed to read configuration file %s"%config_file)
344 self.logger.info("Make sure to remove the export clauses and to add quotes")
345 if self.options.verbose==0:
346 self.logger.info("Re-run with -v for more details")
348 self.logger.log_exc("Could not read config file %s"%config_file)
353 if (self.options.sm is not None):
354 self.sm_url = self.options.sm
355 elif hasattr(config, "SFI_SM"):
356 self.sm_url = config.SFI_SM
358 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
362 if (self.options.registry is not None):
363 self.reg_url = self.options.registry
364 elif hasattr(config, "SFI_REGISTRY"):
365 self.reg_url = config.SFI_REGISTRY
367 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
372 if (self.options.user is not None):
373 self.user = self.options.user
374 elif hasattr(config, "SFI_USER"):
375 self.user = config.SFI_USER
377 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
381 if (self.options.auth is not None):
382 self.authority = self.options.auth
383 elif hasattr(config, "SFI_AUTH"):
384 self.authority = config.SFI_AUTH
386 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
394 # Establish Connection to SliceMgr and Registry Servers
396 def set_servers(self):
399 # Get key and certificate
400 key_file = self.get_key_file()
401 cert_file = self.get_cert_file(key_file)
402 self.key = Keypair(filename=key_file)
403 self.key_file = key_file
404 self.cert_file = cert_file
405 self.cert = GID(filename=cert_file)
406 self.logger.info("Contacting Registry at: %s"%self.reg_url)
407 self.registry = xmlrpcprotocol.server_proxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
408 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
409 self.slicemgr = xmlrpcprotocol.server_proxy(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
412 def get_cached_server_version(self, server):
413 # check local cache first
416 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
417 cache_key = server.url + "-version"
419 cache = Cache(cache_file)
422 self.logger.info("Local cache not found at: %s" % cache_file)
425 version = cache.get(cache_key)
428 result = server.GetVersion()
429 version= ReturnValue.get_value(result)
430 # cache version for 24 hours
431 cache.add(cache_key, version, ttl= 60*60*24)
432 self.logger.info("Updating cache file %s" % cache_file)
433 cache.save_to_file(cache_file)
438 def server_supports_options_arg(self, server):
440 Returns true if server support the optional call_id arg, false otherwise.
442 server_version = self.get_cached_server_version(server)
443 if 'sfa' in server_version and 'code_tag' in server_version:
444 code_tag = server_version['code_tag']
445 code_tag_parts = code_tag.split("-")
447 version_parts = code_tag_parts[0].split(".")
448 major, minor = version_parts[0], version_parts[1]
449 rev = code_tag_parts[1]
456 # Get various credential and spec files
458 # Establishes limiting conventions
459 # - conflates MAs and SAs
460 # - assumes last token in slice name is unique
462 # Bootstraps credentials
463 # - bootstrap user credential from self-signed certificate
464 # - bootstrap authority credential from user credential
465 # - bootstrap slice credential from user credential
469 def get_key_file(self):
470 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
471 if (os.path.isfile(file)):
474 self.logger.error("Key file %s does not exist"%file)
478 def get_cert_file(self, key_file):
480 cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
481 if (os.path.isfile(cert_file)):
482 # we'd perfer to use Registry issued certs instead of self signed certs.
483 # if this is a Registry cert (GID) then we are done
484 gid = GID(filename=cert_file)
488 # generate self signed certificate
489 k = Keypair(filename=key_file)
490 cert = Certificate(subject=self.user)
492 cert.set_issuer(k, self.user)
494 self.logger.info("Writing self-signed certificate to %s"%cert_file)
495 cert.save_to_file(cert_file)
497 # try to get registry issued cert
499 self.logger.info("Getting Registry issued cert")
501 # *hack. need to set registyr before _get_gid() is called
502 self.registry = xmlrpcprotocol.server_proxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
503 gid = self._get_gid(type='user')
505 self.logger.info("Writing certificate to %s"%cert_file)
506 gid.save_to_file(cert_file)
508 self.logger.info("Failed to download Registry issued cert")
512 def get_cached_gid(self, file):
517 if (os.path.isfile(file)):
518 gid = GID(filename=file)
522 def get_gid(self, opts, args):
524 Get the specify gid and save it to file
529 gid = self._get_gid(hrn)
530 self.logger.debug("Sfi.get_gid-> %s" % gid.save_to_string(save_parents=True))
533 def _get_gid(self, hrn=None, type=None):
535 git_gid helper. Retrive the gid from the registry and save it to file.
541 gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
542 gid = self.get_cached_gid(gidfile)
544 user_cred = self.get_user_cred()
545 records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
547 raise RecordNotFound(args[0])
552 if type == rec['type']:
555 raise RecordNotFound(args[0])
557 gid = GID(string=record['gid'])
558 self.logger.info("Writing gid to %s"%gidfile)
559 gid.save_to_file(filename=gidfile)
563 def get_cached_credential(self, file):
565 Return a cached credential only if it hasn't expired.
567 if (os.path.isfile(file)):
568 credential = Credential(filename=file)
569 # make sure it isnt expired
570 if not credential.get_expiration or \
571 datetime.datetime.today() < credential.get_expiration():
575 def get_user_cred(self):
576 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
577 return self.get_cred(file, 'user', self.user)
579 def get_auth_cred(self):
580 if not self.authority:
581 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
583 file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
584 return self.get_cred(file, 'authority', self.authority)
586 def get_slice_cred(self, name):
587 file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
588 return self.get_cred(file, 'slice', name)
590 def get_cred(self, file, type, hrn):
591 # attempt to load a cached credential
592 cred = self.get_cached_credential(file)
595 cert_string = self.cert.save_to_string(save_parents=True)
596 user_name = self.user.replace(self.authority + ".", '')
597 if user_name.count(".") > 0:
598 user_name = user_name.replace(".", '_')
599 self.user = self.authority + "." + user_name
600 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
602 # bootstrap slice credential from user credential
603 user_cred = self.get_user_cred().save_to_string(save_parents=True)
604 cred_str = self.registry.GetCredential(user_cred, hrn, type)
607 self.logger.critical("Failed to get %s credential" % type)
610 cred = Credential(string=cred_str)
611 cred.save_to_file(file, save_parents=True)
612 self.logger.info("Writing %s credential to %s" %(type, file))
617 def get_rspec_file(self, rspec):
618 if (os.path.isabs(rspec)):
621 file = os.path.join(self.options.sfi_dir, rspec)
622 if (os.path.isfile(file)):
625 self.logger.critical("No such rspec file %s"%rspec)
628 def get_record_file(self, record):
629 if (os.path.isabs(record)):
632 file = os.path.join(self.options.sfi_dir, record)
633 if (os.path.isfile(file)):
636 self.logger.critical("No such registry record file %s"%record)
639 def load_publickey_string(self, fn):
641 key_string = f.read()
643 # if the filename is a private key file, then extract the public key
644 if "PRIVATE KEY" in key_string:
645 outfn = tempfile.mktemp()
646 cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
649 key_string = f.read()
655 def get_component_proxy_from_hrn(self, hrn):
656 # direct connection to the nodes component manager interface
657 user_cred = self.get_user_cred().save_to_string(save_parents=True)
658 records = self.registry.Resolve(hrn, user_cred)
659 records = filter_records('node', records)
661 self.logger.warning("No such component:%r"% opts.component)
664 return self.server_proxy(record['hostname'], CM_PORT, self.key_file, self.cert_file)
666 def server_proxy(self, host, port, keyfile, certfile):
668 Return an instance of an xmlrpc server connection
670 # port is appended onto the domain, before the path. Should look like:
671 # http://domain:port/path
672 host_parts = host.split('/')
673 host_parts[0] = host_parts[0] + ":" + str(port)
674 url = "http://%s" % "/".join(host_parts)
675 return xmlrpcprotocol.server_proxy(url, keyfile, certfile, timeout=self.options.timeout, verbose=self.options.debug)
677 # xxx opts could be retrieved in self.options
678 def server_proxy_from_opts(self, opts):
680 Return instance of an xmlrpc connection to a slice manager, aggregate
681 or component server depending on the specified opts
683 server = self.slicemgr
684 # direct connection to an aggregate
685 if hasattr(opts, 'aggregate') and opts.aggregate:
686 server = self.server_proxy(opts.aggregate, opts.port, self.key_file, self.cert_file)
687 # direct connection to the nodes component manager interface
688 if hasattr(opts, 'component') and opts.component:
689 server = self.get_component_proxy_from_hrn(opts.component)
692 #==========================================================================
693 # Following functions implement the commands
695 # Registry-related commands
696 #==========================================================================
698 def dispatch(self, command, cmd_opts, cmd_args):
699 return getattr(self, command)(cmd_opts, cmd_args)
701 def create_gid(self, opts, args):
706 user_cred = self.get_user_cred().save_to_string(save_parents=True)
707 gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
711 filename = os.sep.join([self.sfi_dir, '%s.gid' % target_hrn])
712 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
713 GID(string=gid).save_to_file(filename)
716 # list entires in named authority registry
717 def list(self, opts, args):
722 user_cred = self.get_user_cred().save_to_string(save_parents=True)
724 list = self.registry.List(hrn, user_cred)
726 raise Exception, "Not enough parameters for the 'list' command"
728 # filter on person, slice, site, node, etc.
729 # THis really should be in the self.filter_records funct def comment...
730 list = filter_records(opts.type, list)
732 print "%s (%s)" % (record['hrn'], record['type'])
734 save_records_to_file(opts.file, list, opts.fileformat)
737 # show named registry record
738 def show(self, opts, args):
743 user_cred = self.get_user_cred().save_to_string(save_parents=True)
744 records = self.registry.Resolve(hrn, user_cred)
745 records = filter_records(opts.type, records)
747 print "No record of type", opts.type
748 for record in records:
749 if record['type'] in ['user']:
750 record = UserRecord(dict=record)
751 elif record['type'] in ['slice']:
752 record = SliceRecord(dict=record)
753 elif record['type'] in ['node']:
754 record = NodeRecord(dict=record)
755 elif record['type'].startswith('authority'):
756 record = AuthorityRecord(dict=record)
758 record = SfaRecord(dict=record)
759 if (opts.format == "text"):
762 print record.save_to_string()
764 save_records_to_file(opts.file, records, opts.fileformat)
767 def delegate(self, opts, args):
769 delegee_hrn = args[0]
770 if opts.delegate_user:
771 user_cred = self.get_user_cred()
772 cred = self.delegate_cred(user_cred, delegee_hrn)
773 elif opts.delegate_slice:
774 slice_cred = self.get_slice_cred(opts.delegate_slice)
775 cred = self.delegate_cred(slice_cred, delegee_hrn)
777 self.logger.warning("Must specify either --user or --slice <hrn>")
779 delegated_cred = Credential(string=cred)
780 object_hrn = delegated_cred.get_gid_object().get_hrn()
781 if opts.delegate_user:
782 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
783 + get_leaf(object_hrn) + ".cred")
784 elif opts.delegate_slice:
785 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
786 + get_leaf(object_hrn) + ".cred")
788 delegated_cred.save_to_file(dest_fn, save_parents=True)
790 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
792 def delegate_cred(self, object_cred, hrn):
793 # the gid and hrn of the object we are delegating
794 if isinstance(object_cred, str):
795 object_cred = Credential(string=object_cred)
796 object_gid = object_cred.get_gid_object()
797 object_hrn = object_gid.get_hrn()
799 if not object_cred.get_privileges().get_all_delegate():
800 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
803 # the delegating user's gid
804 caller_gid = self._get_gid(self.user)
805 caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
807 # the gid of the user who will be delegated to
808 delegee_gid = self._get_gid(hrn)
809 delegee_hrn = delegee_gid.get_hrn()
810 delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
811 delegee_gid.save_to_file(filename=delegee_gidfile)
812 dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
813 return dcred.save_to_string(save_parents=True)
815 # removed named registry record
816 # - have to first retrieve the record to be removed
817 def remove(self, opts, args):
818 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
826 return self.registry.Remove(hrn, auth_cred, type)
828 # add named registry record
829 def add(self, opts, args):
830 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
834 record_filepath = args[0]
835 rec_file = self.get_record_file(record_filepath)
836 record = load_record_from_file(rec_file).as_dict()
837 return self.registry.Register(record, auth_cred)
839 # update named registry entry
840 def update(self, opts, args):
841 user_cred = self.get_user_cred()
845 rec_file = self.get_record_file(args[0])
846 record = load_record_from_file(rec_file)
847 if record['type'] == "user":
848 if record.get_name() == user_cred.get_gid_object().get_hrn():
849 cred = user_cred.save_to_string(save_parents=True)
851 cred = self.get_auth_cred().save_to_string(save_parents=True)
852 elif record['type'] in ["slice"]:
854 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
855 except xmlrpcprotocol.ServerException, e:
856 # XXX smbaker -- once we have better error return codes, update this
857 # to do something better than a string compare
858 if "Permission error" in e.args[0]:
859 cred = self.get_auth_cred().save_to_string(save_parents=True)
862 elif record.get_type() in ["authority"]:
863 cred = self.get_auth_cred().save_to_string(save_parents=True)
864 elif record.get_type() == 'node':
865 cred = self.get_auth_cred().save_to_string(save_parents=True)
867 raise "unknown record type" + record.get_type()
868 record = record.as_dict()
869 return self.registry.Update(record, cred)
871 def get_trusted_certs(self, opts, args):
873 return uhe trusted certs at this interface
875 trusted_certs = self.registry.get_trusted_certs()
876 for trusted_cert in trusted_certs:
877 gid = GID(string=trusted_cert)
879 cert = Certificate(string=trusted_cert)
880 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
883 def aggregates(self, opts, args):
885 return a list of details about known aggregates
887 user_cred = self.get_user_cred().save_to_string(save_parents=True)
892 result = self.registry.get_aggregates(user_cred, hrn)
896 def registries(self, opts, args):
898 return a list of details about known registries
900 user_cred = self.get_user_cred().save_to_string(save_parents=True)
904 result = self.registry.get_registries(user_cred, hrn)
909 # ==================================================================
910 # Slice-related commands
911 # ==================================================================
913 def version(self, opts, args):
914 if opts.version_local:
915 version=version_core()
917 if opts.version_registry:
920 server = self.server_proxy_from_opts(opts)
921 result = server.GetVersion()
922 version = ReturnValue.get_value(result)
923 for (k,v) in version.iteritems():
924 print "%-20s: %s"%(k,v)
926 save_variable_to_file(version, opts.file, opts.fileformat)
928 # list instantiated slices
929 def slices(self, opts, args):
931 list instantiated slices
933 user_cred = self.get_user_cred().save_to_string(save_parents=True)
936 delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
937 creds.append(delegated_cred)
938 server = self.server_proxy_from_opts(opts)
940 if self.server_supports_options_arg(server):
941 options = {'call_id': unique_call_id()}
942 call_args.append(options)
943 result = server.ListSlices(*call_args)
944 value = ReturnValue.get_value(result)
948 # show rspec for named slice
949 def resources(self, opts, args):
950 user_cred = self.get_user_cred().save_to_string(save_parents=True)
951 server = self.slicemgr
952 server = self.server_proxy_from_opts(opts)
954 options = {'call_id': unique_call_id()}
955 #panos add info options
957 options['info'] = opts.info
960 cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
962 options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
968 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
969 creds.append(delegated_cred)
970 if opts.rspec_version:
971 version_manager = VersionManager()
972 server_version = self.get_cached_server_version(server)
973 if 'sfa' in server_version:
974 # just request the version the client wants
975 options['rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
977 # this must be a protogeni aggregate. We should request a v2 ad rspec
978 # regardless of what the client user requested
979 options['rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
981 call_args = [creds, options]
982 result = server.ListResources(*call_args)
983 value = ReturnValue.get_value(result)
984 if opts.file is None:
985 display_rspec(value, opts.format)
987 save_rspec_to_file(value, opts.file)
990 # created named slice with given rspec
991 def create(self, opts, args):
992 server = self.server_proxy_from_opts(opts)
993 server_version = self.get_cached_server_version(server)
995 slice_urn = hrn_to_urn(slice_hrn, 'slice')
996 user_cred = self.get_user_cred()
997 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
999 if hasattr(opts, 'aggregate') and opts.aggregate:
1000 delegated_cred = None
1002 # delegate the cred to the callers root authority
1003 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)+'.slicemanager')
1004 #delegated_cred = self.delegate_cred(slice_cred, get_authority(slice_hrn))
1005 #creds.append(delegated_cred)
1007 rspec_file = self.get_rspec_file(args[1])
1008 rspec = open(rspec_file).read()
1010 # need to pass along user keys to the aggregate.
1012 # { urn: urn:publicid:IDN+emulab.net+user+alice
1013 # keys: [<ssh key A>, <ssh key B>]
1016 slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
1017 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1018 slice_record = slice_records[0]
1019 user_hrns = slice_record['researcher']
1020 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1021 user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
1023 if 'sfa' not in server_version:
1024 users = pg_users_arg(user_records)
1025 rspec = RSpec(rspec)
1026 rspec.filter({'component_manager_id': server_version['urn']})
1027 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1028 creds = [slice_cred]
1030 users = sfa_users_arg(user_records, slice_record)
1031 creds = [slice_cred]
1033 creds.append(delegated_cred)
1034 call_args = [slice_urn, creds, rspec, users]
1035 if self.server_supports_options_arg(server):
1036 options = {'call_id': unique_call_id()}
1037 call_args.append(options)
1038 result = server.CreateSliver(*call_args)
1039 value = ReturnValue.get_value(result)
1040 if opts.file is None:
1043 save_rspec_to_file (value, opts.file)
1046 # get a ticket for the specified slice
1047 def get_ticket(self, opts, args):
1048 slice_hrn, rspec_path = args[0], args[1]
1049 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1050 user_cred = self.get_user_cred()
1051 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1052 creds = [slice_cred]
1054 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1055 creds.append(delegated_cred)
1056 rspec_file = self.get_rspec_file(rspec_path)
1057 rspec = open(rspec_file).read()
1058 server = self.server_proxy_from_opts(opts)
1059 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1060 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1061 self.logger.info("writing ticket to %s"%file)
1062 ticket = SfaTicket(string=ticket_string)
1063 ticket.save_to_file(filename=file, save_parents=True)
1065 def redeem_ticket(self, opts, args):
1066 ticket_file = args[0]
1068 # get slice hrn from the ticket
1069 # use this to get the right slice credential
1070 ticket = SfaTicket(filename=ticket_file)
1072 slice_hrn = ticket.gidObject.get_hrn()
1073 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1074 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1075 user_cred = self.get_user_cred()
1076 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1078 # get a list of node hostnames from the RSpec
1079 tree = etree.parse(StringIO(ticket.rspec))
1080 root = tree.getroot()
1081 hostnames = root.xpath("./network/site/node/hostname/text()")
1083 # create an xmlrpc connection to the component manager at each of these
1084 # components and gall redeem_ticket
1086 for hostname in hostnames:
1088 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1089 server = self.server_proxy(hostname, CM_PORT, self.key_file, \
1090 self.cert_file, self.options.debug)
1091 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1092 self.logger.info("Success")
1093 except socket.gaierror:
1094 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1095 except Exception, e:
1096 self.logger.log_exc(e.message)
1099 # delete named slice
1100 def delete(self, opts, args):
1102 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1103 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1104 creds = [slice_cred]
1106 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1107 creds.append(delegated_cred)
1108 server = self.server_proxy_from_opts(opts)
1109 call_args = [slice_urn, creds]
1110 if self.server_supports_options_arg(server):
1111 options = {'call_id': unique_call_id()}
1112 call_args.append(options)
1113 return server.DeleteSliver(*call_args)
1116 def start(self, opts, args):
1118 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1119 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1120 creds = [slice_cred]
1122 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1123 creds.append(delegated_cred)
1124 server = self.server_proxy_from_opts(opts)
1125 return server.Start(slice_urn, creds)
1128 def stop(self, opts, args):
1130 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1131 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1132 creds = [slice_cred]
1134 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1135 creds.append(delegated_cred)
1136 server = self.server_proxy_from_opts(opts)
1137 return server.Stop(slice_urn, creds)
1140 def reset(self, opts, args):
1142 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1143 server = self.server_proxy_from_opts(opts)
1144 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1145 creds = [slice_cred]
1147 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1148 creds.append(delegated_cred)
1149 return server.reset_slice(creds, slice_urn)
1151 def renew(self, opts, args):
1153 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1154 server = self.server_proxy_from_opts(opts)
1155 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1156 creds = [slice_cred]
1158 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1159 creds.append(delegated_cred)
1162 call_args = [slice_urn, creds, time]
1163 if self.server_supports_options_arg(server):
1164 options = {'call_id': unique_call_id()}
1165 call_args.append(options)
1166 result = server.RenewSliver(*call_args)
1167 value = ReturnValue.get_value(result)
1171 def status(self, opts, args):
1173 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1174 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1175 creds = [slice_cred]
1177 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1178 creds.append(delegated_cred)
1179 server = self.server_proxy_from_opts(opts)
1180 call_args = [slice_urn, creds]
1181 if self.server_supports_options_arg(server):
1182 options = {'call_id': unique_call_id()}
1183 call_args.append(options)
1184 result = server.SliverStatus(*call_args)
1185 value = ReturnValue.get_value(result)
1188 save_variable_to_file(value, opts.file, opts.fileformat)
1191 def shutdown(self, opts, args):
1193 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1194 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1195 creds = [slice_cred]
1197 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1198 creds.append(delegated_cred)
1199 server = self.server_proxy_from_opts(opts)
1200 return server.Shutdown(slice_urn, creds)
1202 def print_help (self):
1203 self.sfi_parser.print_help()
1204 self.cmd_parser.print_help()
1207 # Main: parse arguments and dispatch to command
1210 self.sfi_parser = self.create_parser()
1211 (options, args) = self.sfi_parser.parse_args()
1212 self.options = options
1214 self.logger.setLevelFromOptVerbose(self.options.verbose)
1215 if options.hashrequest:
1216 self.hashrequest = True
1219 self.logger.critical("No command given. Use -h for help.")
1223 self.cmd_parser = self.create_cmd_parser(command)
1224 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
1227 self.logger.info("Command=%s" % command)
1228 if command in ("resources"):
1229 self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
1230 elif command in ("list", "show", "remove"):
1231 self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
1232 self.logger.debug('cmd_args %s' % cmd_args)
1235 self.dispatch(command, cmd_opts, cmd_args)
1237 self.logger.critical ("Unknown command %s"%command)
1243 if __name__ == "__main__":