3 # sfi -- slice-based facility interface
15 from lxml import etree
16 from StringIO import StringIO
17 from types import StringTypes, ListType
18 from optparse import OptionParser
20 from sfa.util.sfalogging import sfi_logger
21 from sfa.trust.certificate import Keypair, Certificate
22 from sfa.trust.gid import GID
23 from sfa.trust.credential import Credential
24 from sfa.util.sfaticket import SfaTicket
25 from sfa.util.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
26 from sfa.util.xrn import Xrn, get_leaf, get_authority, hrn_to_urn
27 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
28 from sfa.util.config import Config
29 from sfa.util.version import version_core
30 from sfa.util.cache import Cache
31 from sfa.rspecs.rspec_version import RSpecVersion
32 from sfa.rspecs.pg_rspec import pg_rspec_request_version
37 # utility methods here
39 def display_rspec(rspec, format='rspec'):
41 tree = etree.parse(StringIO(rspec))
43 result = root.xpath("./network/site/node/hostname/text()")
44 elif format in ['ip']:
45 # The IP address is not yet part of the new RSpec
46 # so this doesn't do anything yet.
47 tree = etree.parse(StringIO(rspec))
49 result = root.xpath("./network/site/node/ipv4/text()")
56 def display_list(results):
57 for result in results:
60 def display_records(recordList, dump=False):
61 ''' Print all fields in the record'''
62 for record in recordList:
63 display_record(record, dump)
65 def display_record(record, dump=False):
69 info = record.getdict()
70 print "%s (%s)" % (info['hrn'], info['type'])
74 def filter_records(type, records):
76 for record in records:
77 if (record['type'] == type) or (type == "all"):
78 filtered_records.append(record)
79 return filtered_records
83 def save_rspec_to_file(rspec, filename):
84 if not filename.endswith(".rspec"):
85 filename = filename + ".rspec"
87 f = open(filename, 'w')
92 def save_records_to_file(filename, recordList):
94 for record in recordList:
96 save_record_to_file(filename + "." + str(index), record)
98 save_record_to_file(filename, record)
101 def save_record_to_file(filename, record):
102 if record['type'] in ['user']:
103 record = UserRecord(dict=record)
104 elif record['type'] in ['slice']:
105 record = SliceRecord(dict=record)
106 elif record['type'] in ['node']:
107 record = NodeRecord(dict=record)
108 elif record['type'] in ['authority', 'ma', 'sa']:
109 record = AuthorityRecord(dict=record)
111 record = SfaRecord(dict=record)
112 str = record.save_to_string()
113 f=codecs.open(filename, encoding='utf-8',mode="w")
120 def load_record_from_file(filename):
121 f=codecs.open(filename, encoding="utf-8", mode="r")
124 record = SfaRecord(string=str)
129 def unique_call_id(): return uuid.uuid4().urn
133 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user']
135 # dummy to meet Sfi's expectations for its 'options' field
136 # i.e. s/t we can do setattr on
140 def __init__ (self,options=None):
141 if options is None: options=Sfi.DummyOptions()
142 for opt in Sfi.required_options:
143 if not hasattr(options,opt): setattr(options,opt,None)
144 if not hasattr(options,'sfi_dir'): options.sfi_dir=os.path.expanduser("~/.sfi/")
145 # xxx oops, this is dangerous, sounds like ww sometimes have discrepency
146 # would be safer to remove self.sfi_dir altogether
147 self.sfi_dir = options.sfi_dir
148 self.options = options
152 self.authority = None
153 self.hashrequest = False
154 self.logger = sfi_logger
155 self.logger.enable_console()
157 def create_cmd_parser(self, command, additional_cmdargs=None):
158 cmdargs = {"list": "authority",
163 "aggregates": "[name]",
164 "registries": "[name]",
165 "create_gid": "[name]",
167 "get_trusted_certs": "cred",
169 "resources": "[name]",
170 "create": "name rspec",
171 "get_ticket": "name rspec",
172 "redeem_ticket": "ticket",
184 if additional_cmdargs:
185 cmdargs.update(additional_cmdargs)
187 if command not in cmdargs:
188 msg="Invalid command\n"
190 msg += ','.join(cmdargs.keys())
191 self.logger.critical(msg)
194 parser = OptionParser(usage="sfi [sfi_options] %s [options] %s" \
195 % (command, cmdargs[command]))
197 # user specifies remote aggregate/sm/component
198 if command in ("resources", "slices", "create", "delete", "start", "stop",
199 "restart", "shutdown", "get_ticket", "renew", "status"):
200 parser.add_option("-a", "--aggregate", dest="aggregate",
201 default=None, help="aggregate host")
202 parser.add_option("-p", "--port", dest="port",
203 default=AGGREGATE_PORT, help="aggregate port")
204 parser.add_option("-c", "--component", dest="component", default=None,
205 help="component hrn")
206 parser.add_option("-d", "--delegate", dest="delegate", default=None,
208 help="Include a credential delegated to the user's root"+\
209 "authority in set of credentials for this call")
211 # registy filter option
212 if command in ("list", "show", "remove"):
213 parser.add_option("-t", "--type", dest="type", type="choice",
214 help="type filter ([all]|user|slice|authority|node|aggregate)",
215 choices=("all", "user", "slice", "authority", "node", "aggregate"),
218 if command in ("resources"):
219 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
220 help="schema type and version of resulting RSpec")
221 parser.add_option("-f", "--format", dest="format", type="choice",
222 help="display format ([xml]|dns|ip)", default="xml",
223 choices=("xml", "dns", "ip"))
224 #panos: a new option to define the type of information about resources a user is interested in
225 parser.add_option("-i", "--info", dest="info",
226 help="optional component information", default=None)
229 # 'create' does return the new rspec, makes sense to save that too
230 if command in ("resources", "show", "list", "create_gid", 'create'):
231 parser.add_option("-o", "--output", dest="file",
232 help="output XML to file", metavar="FILE", default=None)
234 if command in ("show", "list"):
235 parser.add_option("-f", "--format", dest="format", type="choice",
236 help="display format ([text]|xml)", default="text",
237 choices=("text", "xml"))
239 if command in ("delegate"):
240 parser.add_option("-u", "--user",
241 action="store_true", dest="delegate_user", default=False,
242 help="delegate user credential")
243 parser.add_option("-s", "--slice", dest="delegate_slice",
244 help="delegate slice credential", metavar="HRN", default=None)
246 if command in ("version"):
247 parser.add_option("-a", "--aggregate", dest="aggregate",
248 default=None, help="aggregate host")
249 parser.add_option("-p", "--port", dest="port",
250 default=AGGREGATE_PORT, help="aggregate port")
251 parser.add_option("-R","--registry-version",
252 action="store_true", dest="version_registry", default=False,
253 help="probe registry version instead of slicemgr")
254 parser.add_option("-l","--local",
255 action="store_true", dest="version_local", default=False,
256 help="display version of the local client")
261 def create_parser(self):
263 # Generate command line parser
264 parser = OptionParser(usage="sfi [options] command [command_options] [command_args]",
265 description="Commands: gid,list,show,remove,add,update,nodes,slices,resources,create,delete,start,stop,reset")
266 parser.add_option("-r", "--registry", dest="registry",
267 help="root registry", metavar="URL", default=None)
268 parser.add_option("-s", "--slicemgr", dest="sm",
269 help="slice manager", metavar="URL", default=None)
270 default_sfi_dir = os.path.expanduser("~/.sfi/")
271 parser.add_option("-d", "--dir", dest="sfi_dir",
272 help="config & working directory - default is " + default_sfi_dir,
273 metavar="PATH", default=default_sfi_dir)
274 parser.add_option("-u", "--user", dest="user",
275 help="user name", metavar="HRN", default=None)
276 parser.add_option("-a", "--auth", dest="auth",
277 help="authority name", metavar="HRN", default=None)
278 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
279 help="verbose mode - cumulative")
280 parser.add_option("-D", "--debug",
281 action="store_true", dest="debug", default=False,
282 help="Debug (xml-rpc) protocol messages")
283 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
284 help="RPC protocol (xmlrpc or soap)")
285 parser.add_option("-k", "--hashrequest",
286 action="store_true", dest="hashrequest", default=False,
287 help="Create a hash of the request that will be authenticated on the server")
288 parser.add_option("-t", "--timeout", dest="timeout", default=None,
289 help="Amout of time tom wait before timing out the request")
290 parser.disable_interspersed_args()
295 def read_config(self):
296 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
298 config = Config (config_file)
300 self.logger.critical("Failed to read configuration file %s"%config_file)
301 self.logger.info("Make sure to remove the export clauses and to add quotes")
302 if self.options.verbose==0:
303 self.logger.info("Re-run with -v for more details")
305 self.logger.log_exc("Could not read config file %s"%config_file)
310 if (self.options.sm is not None):
311 self.sm_url = self.options.sm
312 elif hasattr(config, "SFI_SM"):
313 self.sm_url = config.SFI_SM
315 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
319 if (self.options.registry is not None):
320 self.reg_url = self.options.registry
321 elif hasattr(config, "SFI_REGISTRY"):
322 self.reg_url = config.SFI_REGISTRY
324 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
329 if (self.options.user is not None):
330 self.user = self.options.user
331 elif hasattr(config, "SFI_USER"):
332 self.user = config.SFI_USER
334 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
338 if (self.options.auth is not None):
339 self.authority = self.options.auth
340 elif hasattr(config, "SFI_AUTH"):
341 self.authority = config.SFI_AUTH
343 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
351 # Establish Connection to SliceMgr and Registry Servers
353 def set_servers(self):
356 # Get key and certificate
357 key_file = self.get_key_file()
358 cert_file = self.get_cert_file(key_file)
359 self.key = Keypair(filename=key_file)
360 self.key_file = key_file
361 self.cert_file = cert_file
362 self.cert = GID(filename=cert_file)
363 self.logger.info("Contacting Registry at: %s"%self.reg_url)
364 self.registry = xmlrpcprotocol.get_server(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
365 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
366 self.slicemgr = xmlrpcprotocol.get_server(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
369 def get_cached_server_version(self, server):
370 # check local cache first
373 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
374 cache_key = server.url + "-version"
376 cache = Cache(cache_file)
379 self.logger.info("Local cache not found at: %s" % cache_file)
382 version = cache.get(cache_key)
385 version = server.GetVersion()
386 # cache version for 24 hours
387 cache.add(cache_key, version, ttl= 60*60*24)
388 self.logger.info("Updating cache file %s" % cache_file)
389 cache.save_to_file(cache_file)
395 def server_supports_call_id_arg(self, server):
397 Returns true if server support the optional call_id arg, false otherwise.
399 server_version = self.get_cached_server_version(server)
400 if 'sfa' in server_version:
401 code_tag = server_version['code_tag']
402 code_tag_parts = code_tag.split("-")
404 version_parts = code_tag_parts[0].split(".")
405 major, minor = version_parts[0], version_parts[1]
406 rev = code_tag_parts[1]
408 if int(minor) > 0 or int(rev) > 20:
413 # Get various credential and spec files
415 # Establishes limiting conventions
416 # - conflates MAs and SAs
417 # - assumes last token in slice name is unique
419 # Bootstraps credentials
420 # - bootstrap user credential from self-signed certificate
421 # - bootstrap authority credential from user credential
422 # - bootstrap slice credential from user credential
426 def get_key_file(self):
427 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
428 if (os.path.isfile(file)):
431 self.logger.error("Key file %s does not exist"%file)
435 def get_cert_file(self, key_file):
437 cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
438 if (os.path.isfile(cert_file)):
439 # we'd perfer to use Registry issued certs instead of self signed certs.
440 # if this is a Registry cert (GID) then we are done
441 gid = GID(filename=cert_file)
445 # generate self signed certificate
446 k = Keypair(filename=key_file)
447 cert = Certificate(subject=self.user)
449 cert.set_issuer(k, self.user)
451 self.logger.info("Writing self-signed certificate to %s"%cert_file)
452 cert.save_to_file(cert_file)
454 # try to get registry issued cert
456 self.logger.info("Getting Registry issued cert")
458 # *hack. need to set registyr before _get_gid() is called
459 self.registry = xmlrpcprotocol.get_server(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
460 gid = self._get_gid(type='user')
462 self.logger.info("Writing certificate to %s"%cert_file)
463 gid.save_to_file(cert_file)
465 self.logger.info("Failed to download Registry issued cert")
469 def get_cached_gid(self, file):
474 if (os.path.isfile(file)):
475 gid = GID(filename=file)
479 def get_gid(self, opts, args):
481 Get the specify gid and save it to file
486 gid = self._get_gid(hrn)
487 self.logger.debug("Sfi.get_gid-> %s",gid.save_to_string(save_parents=True))
490 def _get_gid(self, hrn=None, type=None):
492 git_gid helper. Retrive the gid from the registry and save it to file.
498 gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
500 gid = self.get_cached_gid(gidfile)
502 user_cred = self.get_user_cred()
503 records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
505 raise RecordNotFound(args[0])
510 if type == rec['type']:
513 raise RecordNotFound(args[0])
515 gid = GID(string=record['gid'])
516 self.logger.info("Writing gid to %s"%gidfile)
517 gid.save_to_file(filename=gidfile)
521 def get_cached_credential(self, file):
523 Return a cached credential only if it hasn't expired.
525 if (os.path.isfile(file)):
526 credential = Credential(filename=file)
527 # make sure it isnt expired
528 if not credential.get_expiration or \
529 datetime.datetime.today() < credential.get_expiration():
533 def get_user_cred(self):
534 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
535 return self.get_cred(file, 'user', self.user)
537 def get_auth_cred(self):
538 if not self.authority:
539 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
541 file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
542 return self.get_cred(file, 'authority', self.authority)
544 def get_slice_cred(self, name):
545 file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
546 return self.get_cred(file, 'slice', name)
548 def get_cred(self, file, type, hrn):
549 # attempt to load a cached credential
550 cred = self.get_cached_credential(file)
553 cert_string = self.cert.save_to_string(save_parents=True)
554 user_name = self.user.replace(self.authority + ".", '')
555 if user_name.count(".") > 0:
556 user_name = user_name.replace(".", '_')
557 self.user = self.authority + "." + user_name
558 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
560 # bootstrap slice credential from user credential
561 user_cred = self.get_user_cred().save_to_string(save_parents=True)
562 cred_str = self.registry.GetCredential(user_cred, hrn, type)
565 self.logger.critical("Failed to get %s credential" % type)
568 cred = Credential(string=cred_str)
569 cred.save_to_file(file, save_parents=True)
570 self.logger.info("Writing %s credential to %s" %(type, file))
575 def get_rspec_file(self, rspec):
576 if (os.path.isabs(rspec)):
579 file = os.path.join(self.options.sfi_dir, rspec)
580 if (os.path.isfile(file)):
583 self.logger.critical("No such rspec file %s"%rspec)
586 def get_record_file(self, record):
587 if (os.path.isabs(record)):
590 file = os.path.join(self.options.sfi_dir, record)
591 if (os.path.isfile(file)):
594 self.logger.critical("No such registry record file %s"%record)
597 def load_publickey_string(self, fn):
599 key_string = f.read()
601 # if the filename is a private key file, then extract the public key
602 if "PRIVATE KEY" in key_string:
603 outfn = tempfile.mktemp()
604 cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
607 key_string = f.read()
613 def get_component_server_from_hrn(self, hrn):
614 # direct connection to the nodes component manager interface
615 user_cred = self.get_user_cred().save_to_string(save_parents=True)
616 records = self.registry.Resolve(hrn, user_cred)
617 records = filter_records('node', records)
619 self.logger.warning("No such component:%r"% opts.component)
622 return self.get_server(record['hostname'], CM_PORT, self.key_file, self.cert_file)
624 def get_server(self, host, port, keyfile, certfile):
626 Return an instance of an xmlrpc server connection
628 # port is appended onto the domain, before the path. Should look like:
629 # http://domain:port/path
630 host_parts = host.split('/')
631 host_parts[0] = host_parts[0] + ":" + str(port)
632 url = "http://%s" % "/".join(host_parts)
633 return xmlrpcprotocol.get_server(url, keyfile, certfile, timeout=self.options.timeout, verbose=self.options.debug)
635 # xxx opts could be retrieved in self.options
636 def get_server_from_opts(self, opts):
638 Return instance of an xmlrpc connection to a slice manager, aggregate
639 or component server depending on the specified opts
641 server = self.slicemgr
642 # direct connection to an aggregate
643 if hasattr(opts, 'aggregate') and opts.aggregate:
644 server = self.get_server(opts.aggregate, opts.port, self.key_file, self.cert_file)
645 # direct connection to the nodes component manager interface
646 if hasattr(opts, 'component') and opts.component:
647 server = self.get_component_server_from_hrn(opts.component)
650 #==========================================================================
651 # Following functions implement the commands
653 # Registry-related commands
654 #==========================================================================
656 def dispatch(self, command, cmd_opts, cmd_args):
657 return getattr(self, command)(cmd_opts, cmd_args)
659 def create_gid(self, opts, args):
664 user_cred = self.get_user_cred().save_to_string(save_parents=True)
665 gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
669 filename = os.sep.join([self.sfi_dir, '%s.gid' % target_hrn])
670 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
671 GID(string=gid).save_to_file(filename)
674 # list entires in named authority registry
675 def list(self, opts, args):
680 user_cred = self.get_user_cred().save_to_string(save_parents=True)
682 list = self.registry.List(hrn, user_cred)
684 raise Exception, "Not enough parameters for the 'list' command"
686 # filter on person, slice, site, node, etc.
687 # THis really should be in the self.filter_records funct def comment...
688 list = filter_records(opts.type, list)
690 print "%s (%s)" % (record['hrn'], record['type'])
693 if not file.startswith(os.sep):
694 file = os.path.join(self.options.sfi_dir, file)
695 save_records_to_file(file, list)
698 # show named registry record
699 def show(self, opts, args):
704 user_cred = self.get_user_cred().save_to_string(save_parents=True)
705 records = self.registry.Resolve(hrn, user_cred)
707 records = filter_records(opts.type, records)
709 print "No record of type", opts.type
710 for record in records:
711 if record['type'] in ['user']:
712 record = UserRecord(dict=record)
713 elif record['type'] in ['slice']:
714 record = SliceRecord(dict=record)
715 elif record['type'] in ['node']:
716 record = NodeRecord(dict=record)
717 elif record['type'].startswith('authority'):
718 record = AuthorityRecord(dict=record)
720 record = SfaRecord(dict=record)
721 if (opts.format == "text"):
724 print record.save_to_string()
727 if not file.startswith(os.sep):
728 file = os.path.join(self.options.sfi_dir, file)
729 save_records_to_file(file, records)
732 def delegate(self, opts, args):
734 delegee_hrn = args[0]
735 if opts.delegate_user:
736 user_cred = self.get_user_cred()
737 cred = self.delegate_cred(user_cred, delegee_hrn)
738 elif opts.delegate_slice:
739 slice_cred = self.get_slice_cred(opts.delegate_slice)
740 cred = self.delegate_cred(slice_cred, delegee_hrn)
742 self.logger.warning("Must specify either --user or --slice <hrn>")
744 delegated_cred = Credential(string=cred)
745 object_hrn = delegated_cred.get_gid_object().get_hrn()
746 if opts.delegate_user:
747 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
748 + get_leaf(object_hrn) + ".cred")
749 elif opts.delegate_slice:
750 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
751 + get_leaf(object_hrn) + ".cred")
753 delegated_cred.save_to_file(dest_fn, save_parents=True)
755 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
757 def delegate_cred(self, object_cred, hrn):
758 # the gid and hrn of the object we are delegating
759 if isinstance(object_cred, str):
760 object_cred = Credential(string=object_cred)
761 object_gid = object_cred.get_gid_object()
762 object_hrn = object_gid.get_hrn()
764 if not object_cred.get_privileges().get_all_delegate():
765 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
768 # the delegating user's gid
769 caller_gid = self._get_gid(self.user)
770 caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
772 # the gid of the user who will be delegated to
773 delegee_gid = self._get_gid(hrn)
774 delegee_hrn = delegee_gid.get_hrn()
775 delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
776 delegee_gid.save_to_file(filename=delegee_gidfile)
777 dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
778 return dcred.save_to_string(save_parents=True)
780 # removed named registry record
781 # - have to first retrieve the record to be removed
782 def remove(self, opts, args):
783 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
791 return self.registry.Remove(hrn, auth_cred, type)
793 # add named registry record
794 def add(self, opts, args):
795 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
799 record_filepath = args[0]
800 rec_file = self.get_record_file(record_filepath)
801 record = load_record_from_file(rec_file).as_dict()
802 return self.registry.Register(record, auth_cred)
804 # update named registry entry
805 def update(self, opts, args):
806 user_cred = self.get_user_cred()
810 rec_file = self.get_record_file(args[0])
811 record = load_record_from_file(rec_file)
812 if record['type'] == "user":
813 if record.get_name() == user_cred.get_gid_object().get_hrn():
814 cred = user_cred.save_to_string(save_parents=True)
816 cred = self.get_auth_cred().save_to_string(save_parents=True)
817 elif record['type'] in ["slice"]:
819 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
820 except xmlrpcprotocol.ServerException, e:
821 # XXX smbaker -- once we have better error return codes, update this
822 # to do something better than a string compare
823 if "Permission error" in e.args[0]:
824 cred = self.get_auth_cred().save_to_string(save_parents=True)
827 elif record.get_type() in ["authority"]:
828 cred = self.get_auth_cred().save_to_string(save_parents=True)
829 elif record.get_type() == 'node':
830 cred = self.get_auth_cred().save_to_string(save_parents=True)
832 raise "unknown record type" + record.get_type()
833 record = record.as_dict()
834 return self.registry.Update(record, cred)
836 def get_trusted_certs(self, opts, args):
838 return uhe trusted certs at this interface
840 trusted_certs = self.registry.get_trusted_certs()
841 for trusted_cert in trusted_certs:
842 gid = GID(string=trusted_cert)
844 cert = Certificate(string=trusted_cert)
845 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
848 def aggregates(self, opts, args):
850 return a list of details about known aggregates
852 user_cred = self.get_user_cred().save_to_string(save_parents=True)
857 result = self.registry.get_aggregates(user_cred, hrn)
861 def registries(self, opts, args):
863 return a list of details about known registries
865 user_cred = self.get_user_cred().save_to_string(save_parents=True)
869 result = self.registry.get_registries(user_cred, hrn)
874 # ==================================================================
875 # Slice-related commands
876 # ==================================================================
878 def version(self, opts, args):
879 if opts.version_local:
880 version=version_core()
882 if opts.version_registry:
885 server = self.get_server_from_opts(opts)
886 version=server.GetVersion()
887 for (k,v) in version.iteritems():
888 print "%-20s: %s"%(k,v)
890 # list instantiated slices
891 def slices(self, opts, args):
893 list instantiated slices
895 user_cred = self.get_user_cred().save_to_string(save_parents=True)
898 delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
899 creds.append(delegated_cred)
900 server = self.get_server_from_opts(opts)
901 #results = server.ListSlices(creds, unique_call_id())
902 results = server.ListSlices(creds)
903 display_list(results)
906 # show rspec for named slice
907 def resources(self, opts, args):
908 user_cred = self.get_user_cred().save_to_string(save_parents=True)
909 server = self.slicemgr
911 server = self.get_server_from_opts(opts)
914 cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
916 call_options = {'geni_slice_urn': hrn_to_urn(hrn, 'slice')}
923 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
924 creds.append(delegated_cred)
925 if opts.rspec_version:
926 server_version = self.get_cached_server_version(server)
927 if 'sfa' in server_version:
928 # just request the version the client wants
929 call_options['rspec_version'] = dict(RSpecVersion(opts.rspec_version))
931 # this must be a protogeni aggregate. We should request a v2 ad rspec
932 # regardless of what the client user requested
933 call_options['rspec_version'] = dict(pg_rspec_request_version)
934 #panos add info options
936 call_options['info'] = opts.info
938 call_args = [creds, call_options]
939 if self.server_supports_call_id_arg(server):
940 call_args.append(unique_call_id())
941 result = server.ListResources(*call_args)
943 if opts.file is None:
944 display_rspec(result, format)
947 if not file.startswith(os.sep):
948 file = os.path.join(self.options.sfi_dir, file)
949 save_rspec_to_file(result, file)
952 # created named slice with given rspec
953 def create(self, opts, args):
954 server = self.get_server_from_opts(opts)
955 server_version = self.get_cached_server_version(server)
957 slice_urn = hrn_to_urn(slice_hrn, 'slice')
958 user_cred = self.get_user_cred()
959 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
962 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
963 creds.append(delegated_cred)
964 rspec_file = self.get_rspec_file(args[1])
965 rspec = open(rspec_file).read()
967 # need to pass along user keys to the aggregate.
969 # { urn: urn:publicid:IDN+emulab.net+user+alice
970 # keys: [<ssh key A>, <ssh key B>]
975 slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
976 if slice_records and 'researcher' in slice_records[0]:
977 slice_record = slice_records[0]
978 user_hrns = slice_record['researcher']
979 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
980 user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
981 for user_record in user_records:
982 if user_record['type'] != 'user':
984 #user = {'urn': user_cred.get_gid_caller().get_urn(),'keys': []}
985 user = {'urn': user_cred.get_gid_caller().get_urn(), #
986 'keys': user_record['keys'],
987 'email': user_record['email'], # needed for MyPLC
988 'person_id': user_record['person_id'], # needed for MyPLC
989 'first_name': user_record['first_name'], # needed for MyPLC
990 'last_name': user_record['last_name'], # needed for MyPLC
991 'slice_record': slice_record, # needed for legacy refresh peer
992 'key_ids': user_record['key_ids'] # needed for legacy refresh peer
995 all_keys.extend(user_record['keys'])
996 all_key_ids.extend(user_record['key_ids'])
997 # ProtoGeni Aggregates will only install the keys of the user that is issuing the
998 # request. So we will add all to the current caller's list of keys
999 if 'sfa' not in server_version:
1001 if user['urn'] == user_cred.get_gid_caller().get_urn():
1002 user['keys'] = all_keys
1004 call_args = [slice_urn, creds, rspec, users]
1005 if self.server_supports_call_id_arg(server):
1006 call_args.append(unique_call_id())
1008 result = server.CreateSliver(*call_args)
1009 if opts.file is None:
1012 save_rspec_to_file (result, opts.file)
1015 # get a ticket for the specified slice
1016 def get_ticket(self, opts, args):
1017 slice_hrn, rspec_path = args[0], args[1]
1018 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1019 user_cred = self.get_user_cred()
1020 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1021 creds = [slice_cred]
1023 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1024 creds.append(delegated_cred)
1025 rspec_file = self.get_rspec_file(rspec_path)
1026 rspec = open(rspec_file).read()
1027 server = self.get_server_from_opts(opts)
1028 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1029 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1030 self.logger.info("writing ticket to %s"%file)
1031 ticket = SfaTicket(string=ticket_string)
1032 ticket.save_to_file(filename=file, save_parents=True)
1034 def redeem_ticket(self, opts, args):
1035 ticket_file = args[0]
1037 # get slice hrn from the ticket
1038 # use this to get the right slice credential
1039 ticket = SfaTicket(filename=ticket_file)
1041 slice_hrn = ticket.gidObject.get_hrn()
1042 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1043 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1044 user_cred = self.get_user_cred()
1045 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1047 # get a list of node hostnames from the RSpec
1048 tree = etree.parse(StringIO(ticket.rspec))
1049 root = tree.getroot()
1050 hostnames = root.xpath("./network/site/node/hostname/text()")
1052 # create an xmlrpc connection to the component manager at each of these
1053 # components and gall redeem_ticket
1055 for hostname in hostnames:
1057 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1058 server = self.get_server(hostname, CM_PORT, self.key_file, \
1059 self.cert_file, self.options.debug)
1060 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1061 self.logger.info("Success")
1062 except socket.gaierror:
1063 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1064 except Exception, e:
1065 self.logger.log_exc(e.message)
1068 # delete named slice
1069 def delete(self, opts, args):
1071 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1072 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1073 creds = [slice_cred]
1075 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1076 creds.append(delegated_cred)
1077 server = self.get_server_from_opts(opts)
1079 call_args = [slice_urn, creds]
1080 if self.server_supports_call_id_arg(server):
1081 call_args.append(unique_call_id())
1082 return server.DeleteSliver(*call_args)
1085 def start(self, opts, args):
1087 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1088 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1089 creds = [slice_cred]
1091 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1092 creds.append(delegated_cred)
1093 server = self.get_server_from_opts(opts)
1094 return server.Start(slice_urn, creds)
1097 def stop(self, opts, args):
1099 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1100 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1101 creds = [slice_cred]
1103 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1104 creds.append(delegated_cred)
1105 server = self.get_server_from_opts(opts)
1106 return server.Stop(slice_urn, creds)
1109 def reset(self, opts, args):
1111 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1112 server = self.get_server_from_opts(opts)
1113 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1114 creds = [slice_cred]
1116 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1117 creds.append(delegated_cred)
1118 return server.reset_slice(creds, slice_urn)
1120 def renew(self, opts, args):
1122 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1123 server = self.get_server_from_opts(opts)
1124 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1125 creds = [slice_cred]
1127 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1128 creds.append(delegated_cred)
1131 call_args = [slice_urn, creds, time]
1132 if self.server_supports_call_id_arg(server):
1133 call_args.append(unique_call_id())
1134 return server.RenewSliver(*call_args)
1137 def status(self, opts, args):
1139 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1140 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1141 creds = [slice_cred]
1143 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1144 creds.append(delegated_cred)
1145 server = self.get_server_from_opts(opts)
1146 call_args = [slice_urn, creds]
1147 if self.server_supports_call_id_arg(server):
1148 call_args.append(unique_call_id())
1149 print server.SliverStatus(*call_args)
1152 def shutdown(self, opts, args):
1154 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1155 slice_cred = self.get_slice_cred(slice_hrn).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)
1160 server = self.get_server_from_opts(opts)
1161 return server.Shutdown(slice_urn, creds)
1163 def print_help (self):
1164 self.sfi_parser.print_help()
1165 self.cmd_parser.print_help()
1168 # Main: parse arguments and dispatch to command
1171 self.sfi_parser = self.create_parser()
1172 (options, args) = self.sfi_parser.parse_args()
1173 self.options = options
1175 self.logger.setLevelFromOptVerbose(self.options.verbose)
1176 if options.hashrequest:
1177 self.hashrequest = True
1180 self.logger.critical("No command given. Use -h for help.")
1184 self.cmd_parser = self.create_cmd_parser(command)
1185 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
1188 self.logger.info("Command=%s" % command)
1189 if command in ("resources"):
1190 self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
1191 elif command in ("list", "show", "remove"):
1192 self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
1193 self.logger.debug('cmd_args %s' % cmd_args)
1196 self.dispatch(command, cmd_opts, cmd_args)
1198 self.logger.critical ("Unknown command %s"%command)
1204 if __name__ == "__main__":