3 # sfi -- slice-based facility interface
16 from lxml import etree
17 from StringIO import StringIO
18 from types import StringTypes, ListType
19 from optparse import OptionParser
20 from sfa.client.client_helper import pg_users_arg, sfa_users_arg, sfa_to_pg_users_arg
21 from sfa.util.sfalogging import sfi_logger
22 from sfa.trust.certificate import Keypair, Certificate
23 from sfa.trust.gid import GID
24 from sfa.trust.credential import Credential
25 from sfa.util.sfaticket import SfaTicket
26 from sfa.util.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
27 from sfa.rspecs.rspec import RSpec
28 from sfa.rspecs.rspec_converter import RSpecConverter
29 from sfa.util.xrn import Xrn, get_leaf, get_authority, hrn_to_urn
30 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
31 from sfa.util.config import Config
32 from sfa.util.version import version_core
33 from sfa.util.cache import Cache
34 from sfa.rspecs.version_manager import VersionManager
39 # utility methods here
41 def display_rspec(rspec, format='rspec'):
43 tree = etree.parse(StringIO(rspec))
45 result = root.xpath("./network/site/node/hostname/text()")
46 elif format in ['ip']:
47 # The IP address is not yet part of the new RSpec
48 # so this doesn't do anything yet.
49 tree = etree.parse(StringIO(rspec))
51 result = root.xpath("./network/site/node/ipv4/text()")
58 def display_list(results):
59 for result in results:
62 def display_records(recordList, dump=False):
63 ''' Print all fields in the record'''
64 for record in recordList:
65 display_record(record, dump)
67 def display_record(record, dump=False):
71 info = record.getdict()
72 print "%s (%s)" % (info['hrn'], info['type'])
76 def filter_records(type, records):
78 for record in records:
79 if (record['type'] == type) or (type == "all"):
80 filtered_records.append(record)
81 return filtered_records
85 def save_variable_to_file(var, filename, format="text"):
86 f = open(filename, "w")
89 elif format == "pickled":
90 f.write(pickle.dumps(var))
92 # this should never happen
93 print "unknown output format", format
96 def save_rspec_to_file(rspec, filename):
97 if not filename.endswith(".rspec"):
98 filename = filename + ".rspec"
99 f = open(filename, 'w')
104 def save_records_to_file(filename, recordList, format="xml"):
107 for record in recordList:
109 save_record_to_file(filename + "." + str(index), record)
111 save_record_to_file(filename, record)
113 elif format == "xmllist":
114 f = open(filename, "w")
115 f.write("<recordlist>\n")
116 for record in recordList:
117 record = SfaRecord(dict=record)
118 f.write('<record hrn="' + record.get_name() + '" type="' + record.get_type() + '" />\n')
119 f.write("</recordlist>\n");
121 elif format == "hrnlist":
122 f = open(filename, "w")
123 for record in recordList:
124 record = SfaRecord(dict=record)
125 f.write(record.get_name() + "\n")
128 # this should never happen
129 print "unknown output format", format
131 def save_record_to_file(filename, record):
132 if record['type'] in ['user']:
133 record = UserRecord(dict=record)
134 elif record['type'] in ['slice']:
135 record = SliceRecord(dict=record)
136 elif record['type'] in ['node']:
137 record = NodeRecord(dict=record)
138 elif record['type'] in ['authority', 'ma', 'sa']:
139 record = AuthorityRecord(dict=record)
141 record = SfaRecord(dict=record)
142 str = record.save_to_string()
143 f=codecs.open(filename, encoding='utf-8',mode="w")
150 def load_record_from_file(filename):
151 f=codecs.open(filename, encoding="utf-8", mode="r")
154 record = SfaRecord(string=str)
159 def unique_call_id(): return uuid.uuid4().urn
163 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user']
165 # dummy to meet Sfi's expectations for its 'options' field
166 # i.e. s/t we can do setattr on
170 def __init__ (self,options=None):
171 if options is None: options=Sfi.DummyOptions()
172 for opt in Sfi.required_options:
173 if not hasattr(options,opt): setattr(options,opt,None)
174 if not hasattr(options,'sfi_dir'): options.sfi_dir=os.path.expanduser("~/.sfi/")
175 # xxx oops, this is dangerous, sounds like ww sometimes have discrepency
176 # would be safer to remove self.sfi_dir altogether
177 self.sfi_dir = options.sfi_dir
178 self.options = options
182 self.authority = None
183 self.hashrequest = False
184 self.logger = sfi_logger
185 self.logger.enable_console()
187 def create_cmd_parser(self, command, additional_cmdargs=None):
188 cmdargs = {"list": "authority",
193 "aggregates": "[name]",
194 "registries": "[name]",
195 "create_gid": "[name]",
197 "get_trusted_certs": "cred",
199 "resources": "[name]",
200 "create": "name rspec",
201 "get_ticket": "name rspec",
202 "redeem_ticket": "ticket",
214 if additional_cmdargs:
215 cmdargs.update(additional_cmdargs)
217 if command not in cmdargs:
218 msg="Invalid command\n"
220 msg += ','.join(cmdargs.keys())
221 self.logger.critical(msg)
224 parser = OptionParser(usage="sfi [sfi_options] %s [options] %s" \
225 % (command, cmdargs[command]))
227 # user specifies remote aggregate/sm/component
228 if command in ("resources", "slices", "create", "delete", "start", "stop",
229 "restart", "shutdown", "get_ticket", "renew", "status"):
230 parser.add_option("-a", "--aggregate", dest="aggregate",
231 default=None, help="aggregate host")
232 parser.add_option("-p", "--port", dest="port",
233 default=AGGREGATE_PORT, help="aggregate port")
234 parser.add_option("-c", "--component", dest="component", default=None,
235 help="component hrn")
236 parser.add_option("-d", "--delegate", dest="delegate", default=None,
238 help="Include a credential delegated to the user's root"+\
239 "authority in set of credentials for this call")
241 # registy filter option
242 if command in ("list", "show", "remove"):
243 parser.add_option("-t", "--type", dest="type", type="choice",
244 help="type filter ([all]|user|slice|authority|node|aggregate)",
245 choices=("all", "user", "slice", "authority", "node", "aggregate"),
248 if command in ("resources"):
249 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
250 help="schema type and version of resulting RSpec")
251 parser.add_option("-f", "--format", dest="format", type="choice",
252 help="display format ([xml]|dns|ip)", default="xml",
253 choices=("xml", "dns", "ip"))
254 #panos: a new option to define the type of information about resources a user is interested in
255 parser.add_option("-i", "--info", dest="info",
256 help="optional component information", default=None)
259 # 'create' does return the new rspec, makes sense to save that too
260 if command in ("resources", "show", "list", "create_gid", 'create'):
261 parser.add_option("-o", "--output", dest="file",
262 help="output XML to file", metavar="FILE", default=None)
264 if command in ("show", "list"):
265 parser.add_option("-f", "--format", dest="format", type="choice",
266 help="display format ([text]|xml)", default="text",
267 choices=("text", "xml"))
269 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
270 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
271 choices=("xml", "xmllist", "hrnlist"))
273 if command in ("status"):
274 parser.add_option("-o", "--output", dest="file",
275 help="output dictionary to file", metavar="FILE", default=None)
276 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
277 help="output file format ([text]|pickled)", default="text",
278 choices=("text","pickled"))
280 if command in ("delegate"):
281 parser.add_option("-u", "--user",
282 action="store_true", dest="delegate_user", default=False,
283 help="delegate user credential")
284 parser.add_option("-s", "--slice", dest="delegate_slice",
285 help="delegate slice credential", metavar="HRN", default=None)
287 if command in ("version"):
288 parser.add_option("-a", "--aggregate", dest="aggregate",
289 default=None, help="aggregate host")
290 parser.add_option("-p", "--port", dest="port",
291 default=AGGREGATE_PORT, help="aggregate port")
292 parser.add_option("-R","--registry-version",
293 action="store_true", dest="version_registry", default=False,
294 help="probe registry version instead of slicemgr")
295 parser.add_option("-l","--local",
296 action="store_true", dest="version_local", default=False,
297 help="display version of the local client")
302 def create_parser(self):
304 # Generate command line parser
305 parser = OptionParser(usage="sfi [options] command [command_options] [command_args]",
306 description="Commands: gid,list,show,remove,add,update,nodes,slices,resources,create,delete,start,stop,reset")
307 parser.add_option("-r", "--registry", dest="registry",
308 help="root registry", metavar="URL", default=None)
309 parser.add_option("-s", "--slicemgr", dest="sm",
310 help="slice manager", metavar="URL", default=None)
311 default_sfi_dir = os.path.expanduser("~/.sfi/")
312 parser.add_option("-d", "--dir", dest="sfi_dir",
313 help="config & working directory - default is " + default_sfi_dir,
314 metavar="PATH", default=default_sfi_dir)
315 parser.add_option("-u", "--user", dest="user",
316 help="user name", metavar="HRN", default=None)
317 parser.add_option("-a", "--auth", dest="auth",
318 help="authority name", metavar="HRN", default=None)
319 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
320 help="verbose mode - cumulative")
321 parser.add_option("-D", "--debug",
322 action="store_true", dest="debug", default=False,
323 help="Debug (xml-rpc) protocol messages")
324 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
325 help="RPC protocol (xmlrpc or soap)")
326 parser.add_option("-k", "--hashrequest",
327 action="store_true", dest="hashrequest", default=False,
328 help="Create a hash of the request that will be authenticated on the server")
329 parser.add_option("-t", "--timeout", dest="timeout", default=None,
330 help="Amout of time tom wait before timing out the request")
331 parser.disable_interspersed_args()
336 def read_config(self):
337 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
339 config = Config (config_file)
341 self.logger.critical("Failed to read configuration file %s"%config_file)
342 self.logger.info("Make sure to remove the export clauses and to add quotes")
343 if self.options.verbose==0:
344 self.logger.info("Re-run with -v for more details")
346 self.logger.log_exc("Could not read config file %s"%config_file)
351 if (self.options.sm is not None):
352 self.sm_url = self.options.sm
353 elif hasattr(config, "SFI_SM"):
354 self.sm_url = config.SFI_SM
356 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
360 if (self.options.registry is not None):
361 self.reg_url = self.options.registry
362 elif hasattr(config, "SFI_REGISTRY"):
363 self.reg_url = config.SFI_REGISTRY
365 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
370 if (self.options.user is not None):
371 self.user = self.options.user
372 elif hasattr(config, "SFI_USER"):
373 self.user = config.SFI_USER
375 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
379 if (self.options.auth is not None):
380 self.authority = self.options.auth
381 elif hasattr(config, "SFI_AUTH"):
382 self.authority = config.SFI_AUTH
384 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
392 # Establish Connection to SliceMgr and Registry Servers
394 def set_servers(self):
397 # Get key and certificate
398 key_file = self.get_key_file()
399 cert_file = self.get_cert_file(key_file)
400 self.key = Keypair(filename=key_file)
401 self.key_file = key_file
402 self.cert_file = cert_file
403 self.cert = GID(filename=cert_file)
404 self.logger.info("Contacting Registry at: %s"%self.reg_url)
405 self.registry = xmlrpcprotocol.get_server(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
406 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
407 self.slicemgr = xmlrpcprotocol.get_server(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
410 def get_cached_server_version(self, server):
411 # check local cache first
414 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
415 cache_key = server.url + "-version"
417 cache = Cache(cache_file)
420 self.logger.info("Local cache not found at: %s" % cache_file)
423 version = cache.get(cache_key)
426 version = server.GetVersion()
427 # cache version for 24 hours
428 cache.add(cache_key, version, ttl= 60*60*24)
429 self.logger.info("Updating cache file %s" % cache_file)
430 cache.save_to_file(cache_file)
436 def server_supports_call_id_arg(self, server):
438 Returns true if server support the optional call_id arg, false otherwise.
440 server_version = self.get_cached_server_version(server)
441 if 'sfa' in server_version and 'code_tag' in server_version:
442 code_tag = server_version['code_tag']
443 code_tag_parts = code_tag.split("-")
445 version_parts = code_tag_parts[0].split(".")
446 major, minor = version_parts[0], version_parts[1]
447 rev = code_tag_parts[1]
449 if int(minor) > 0 or int(rev) > 20:
454 # Get various credential and spec files
456 # Establishes limiting conventions
457 # - conflates MAs and SAs
458 # - assumes last token in slice name is unique
460 # Bootstraps credentials
461 # - bootstrap user credential from self-signed certificate
462 # - bootstrap authority credential from user credential
463 # - bootstrap slice credential from user credential
467 def get_key_file(self):
468 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
469 if (os.path.isfile(file)):
472 self.logger.error("Key file %s does not exist"%file)
476 def get_cert_file(self, key_file):
478 cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
479 if (os.path.isfile(cert_file)):
480 # we'd perfer to use Registry issued certs instead of self signed certs.
481 # if this is a Registry cert (GID) then we are done
482 gid = GID(filename=cert_file)
486 # generate self signed certificate
487 k = Keypair(filename=key_file)
488 cert = Certificate(subject=self.user)
490 cert.set_issuer(k, self.user)
492 self.logger.info("Writing self-signed certificate to %s"%cert_file)
493 cert.save_to_file(cert_file)
495 # try to get registry issued cert
497 self.logger.info("Getting Registry issued cert")
499 # *hack. need to set registyr before _get_gid() is called
500 self.registry = xmlrpcprotocol.get_server(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
501 gid = self._get_gid(type='user')
503 self.logger.info("Writing certificate to %s"%cert_file)
504 gid.save_to_file(cert_file)
506 self.logger.info("Failed to download Registry issued cert")
510 def get_cached_gid(self, file):
515 if (os.path.isfile(file)):
516 gid = GID(filename=file)
520 def get_gid(self, opts, args):
522 Get the specify gid and save it to file
527 gid = self._get_gid(hrn)
528 self.logger.debug("Sfi.get_gid-> %s",gid.save_to_string(save_parents=True))
531 def _get_gid(self, hrn=None, type=None):
533 git_gid helper. Retrive the gid from the registry and save it to file.
539 gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
540 gid = self.get_cached_gid(gidfile)
542 user_cred = self.get_user_cred()
543 records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
545 raise RecordNotFound(args[0])
550 if type == rec['type']:
553 raise RecordNotFound(args[0])
555 gid = GID(string=record['gid'])
556 self.logger.info("Writing gid to %s"%gidfile)
557 gid.save_to_file(filename=gidfile)
561 def get_cached_credential(self, file):
563 Return a cached credential only if it hasn't expired.
565 if (os.path.isfile(file)):
566 credential = Credential(filename=file)
567 # make sure it isnt expired
568 if not credential.get_expiration or \
569 datetime.datetime.today() < credential.get_expiration():
573 def get_user_cred(self):
574 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
575 return self.get_cred(file, 'user', self.user)
577 def get_auth_cred(self):
578 if not self.authority:
579 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
581 file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
582 return self.get_cred(file, 'authority', self.authority)
584 def get_slice_cred(self, name):
585 file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
586 return self.get_cred(file, 'slice', name)
588 def get_cred(self, file, type, hrn):
589 # attempt to load a cached credential
590 cred = self.get_cached_credential(file)
593 cert_string = self.cert.save_to_string(save_parents=True)
594 user_name = self.user.replace(self.authority + ".", '')
595 if user_name.count(".") > 0:
596 user_name = user_name.replace(".", '_')
597 self.user = self.authority + "." + user_name
598 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
600 # bootstrap slice credential from user credential
601 user_cred = self.get_user_cred().save_to_string(save_parents=True)
602 cred_str = self.registry.GetCredential(user_cred, hrn, type)
605 self.logger.critical("Failed to get %s credential" % type)
608 cred = Credential(string=cred_str)
609 cred.save_to_file(file, save_parents=True)
610 self.logger.info("Writing %s credential to %s" %(type, file))
615 def get_rspec_file(self, rspec):
616 if (os.path.isabs(rspec)):
619 file = os.path.join(self.options.sfi_dir, rspec)
620 if (os.path.isfile(file)):
623 self.logger.critical("No such rspec file %s"%rspec)
626 def get_record_file(self, record):
627 if (os.path.isabs(record)):
630 file = os.path.join(self.options.sfi_dir, record)
631 if (os.path.isfile(file)):
634 self.logger.critical("No such registry record file %s"%record)
637 def load_publickey_string(self, fn):
639 key_string = f.read()
641 # if the filename is a private key file, then extract the public key
642 if "PRIVATE KEY" in key_string:
643 outfn = tempfile.mktemp()
644 cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
647 key_string = f.read()
653 def get_component_server_from_hrn(self, hrn):
654 # direct connection to the nodes component manager interface
655 user_cred = self.get_user_cred().save_to_string(save_parents=True)
656 records = self.registry.Resolve(hrn, user_cred)
657 records = filter_records('node', records)
659 self.logger.warning("No such component:%r"% opts.component)
662 return self.get_server(record['hostname'], CM_PORT, self.key_file, self.cert_file)
664 def get_server(self, host, port, keyfile, certfile):
666 Return an instance of an xmlrpc server connection
668 # port is appended onto the domain, before the path. Should look like:
669 # http://domain:port/path
670 host_parts = host.split('/')
671 host_parts[0] = host_parts[0] + ":" + str(port)
672 url = "http://%s" % "/".join(host_parts)
673 return xmlrpcprotocol.get_server(url, keyfile, certfile, timeout=self.options.timeout, verbose=self.options.debug)
675 # xxx opts could be retrieved in self.options
676 def get_server_from_opts(self, opts):
678 Return instance of an xmlrpc connection to a slice manager, aggregate
679 or component server depending on the specified opts
681 server = self.slicemgr
682 # direct connection to an aggregate
683 if hasattr(opts, 'aggregate') and opts.aggregate:
684 server = self.get_server(opts.aggregate, opts.port, self.key_file, self.cert_file)
685 # direct connection to the nodes component manager interface
686 if hasattr(opts, 'component') and opts.component:
687 server = self.get_component_server_from_hrn(opts.component)
690 #==========================================================================
691 # Following functions implement the commands
693 # Registry-related commands
694 #==========================================================================
696 def dispatch(self, command, cmd_opts, cmd_args):
697 return getattr(self, command)(cmd_opts, cmd_args)
699 def create_gid(self, opts, args):
704 user_cred = self.get_user_cred().save_to_string(save_parents=True)
705 gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
709 filename = os.sep.join([self.sfi_dir, '%s.gid' % target_hrn])
710 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
711 GID(string=gid).save_to_file(filename)
714 # list entires in named authority registry
715 def list(self, opts, args):
720 user_cred = self.get_user_cred().save_to_string(save_parents=True)
722 list = self.registry.List(hrn, user_cred)
724 raise Exception, "Not enough parameters for the 'list' command"
726 # filter on person, slice, site, node, etc.
727 # THis really should be in the self.filter_records funct def comment...
728 list = filter_records(opts.type, list)
730 print "%s (%s)" % (record['hrn'], record['type'])
732 save_records_to_file(opts.file, list, opts.fileformat)
735 # show named registry record
736 def show(self, opts, args):
741 user_cred = self.get_user_cred().save_to_string(save_parents=True)
742 records = self.registry.Resolve(hrn, user_cred)
743 records = filter_records(opts.type, records)
745 print "No record of type", opts.type
746 for record in records:
747 if record['type'] in ['user']:
748 record = UserRecord(dict=record)
749 elif record['type'] in ['slice']:
750 record = SliceRecord(dict=record)
751 elif record['type'] in ['node']:
752 record = NodeRecord(dict=record)
753 elif record['type'].startswith('authority'):
754 record = AuthorityRecord(dict=record)
756 record = SfaRecord(dict=record)
757 if (opts.format == "text"):
760 print record.save_to_string()
762 save_records_to_file(opts.file, records, opts.fileformat)
765 def delegate(self, opts, args):
767 delegee_hrn = args[0]
768 if opts.delegate_user:
769 user_cred = self.get_user_cred()
770 cred = self.delegate_cred(user_cred, delegee_hrn)
771 elif opts.delegate_slice:
772 slice_cred = self.get_slice_cred(opts.delegate_slice)
773 cred = self.delegate_cred(slice_cred, delegee_hrn)
775 self.logger.warning("Must specify either --user or --slice <hrn>")
777 delegated_cred = Credential(string=cred)
778 object_hrn = delegated_cred.get_gid_object().get_hrn()
779 if opts.delegate_user:
780 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
781 + get_leaf(object_hrn) + ".cred")
782 elif opts.delegate_slice:
783 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
784 + get_leaf(object_hrn) + ".cred")
786 delegated_cred.save_to_file(dest_fn, save_parents=True)
788 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
790 def delegate_cred(self, object_cred, hrn):
791 # the gid and hrn of the object we are delegating
792 if isinstance(object_cred, str):
793 object_cred = Credential(string=object_cred)
794 object_gid = object_cred.get_gid_object()
795 object_hrn = object_gid.get_hrn()
797 if not object_cred.get_privileges().get_all_delegate():
798 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
801 # the delegating user's gid
802 caller_gid = self._get_gid(self.user)
803 caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
805 # the gid of the user who will be delegated to
806 delegee_gid = self._get_gid(hrn)
807 delegee_hrn = delegee_gid.get_hrn()
808 delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
809 delegee_gid.save_to_file(filename=delegee_gidfile)
810 dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
811 return dcred.save_to_string(save_parents=True)
813 # removed named registry record
814 # - have to first retrieve the record to be removed
815 def remove(self, opts, args):
816 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
824 return self.registry.Remove(hrn, auth_cred, type)
826 # add named registry record
827 def add(self, opts, args):
828 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
832 record_filepath = args[0]
833 rec_file = self.get_record_file(record_filepath)
834 record = load_record_from_file(rec_file).as_dict()
835 return self.registry.Register(record, auth_cred)
837 # update named registry entry
838 def update(self, opts, args):
839 user_cred = self.get_user_cred()
843 rec_file = self.get_record_file(args[0])
844 record = load_record_from_file(rec_file)
845 if record['type'] == "user":
846 if record.get_name() == user_cred.get_gid_object().get_hrn():
847 cred = user_cred.save_to_string(save_parents=True)
849 cred = self.get_auth_cred().save_to_string(save_parents=True)
850 elif record['type'] in ["slice"]:
852 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
853 except xmlrpcprotocol.ServerException, e:
854 # XXX smbaker -- once we have better error return codes, update this
855 # to do something better than a string compare
856 if "Permission error" in e.args[0]:
857 cred = self.get_auth_cred().save_to_string(save_parents=True)
860 elif record.get_type() in ["authority"]:
861 cred = self.get_auth_cred().save_to_string(save_parents=True)
862 elif record.get_type() == 'node':
863 cred = self.get_auth_cred().save_to_string(save_parents=True)
865 raise "unknown record type" + record.get_type()
866 record = record.as_dict()
867 return self.registry.Update(record, cred)
869 def get_trusted_certs(self, opts, args):
871 return uhe trusted certs at this interface
873 trusted_certs = self.registry.get_trusted_certs()
874 for trusted_cert in trusted_certs:
875 gid = GID(string=trusted_cert)
877 cert = Certificate(string=trusted_cert)
878 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
881 def aggregates(self, opts, args):
883 return a list of details about known aggregates
885 user_cred = self.get_user_cred().save_to_string(save_parents=True)
890 result = self.registry.get_aggregates(user_cred, hrn)
894 def registries(self, opts, args):
896 return a list of details about known registries
898 user_cred = self.get_user_cred().save_to_string(save_parents=True)
902 result = self.registry.get_registries(user_cred, hrn)
907 # ==================================================================
908 # Slice-related commands
909 # ==================================================================
911 def version(self, opts, args):
912 if opts.version_local:
913 version=version_core()
915 if opts.version_registry:
918 server = self.get_server_from_opts(opts)
919 version=server.GetVersion()
920 for (k,v) in version.iteritems():
921 print "%-20s: %s"%(k,v)
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.get_server_from_opts(opts)
934 #results = server.ListSlices(creds, unique_call_id())
935 results = server.ListSlices(creds)
936 display_list(results)
939 # show rspec for named slice
940 def resources(self, opts, args):
941 user_cred = self.get_user_cred().save_to_string(save_parents=True)
942 server = self.slicemgr
944 server = self.get_server_from_opts(opts)
947 cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
949 call_options = {'geni_slice_urn': hrn_to_urn(hrn, 'slice')}
956 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
957 creds.append(delegated_cred)
958 if opts.rspec_version:
959 version_manager = VersionManager()
960 server_version = self.get_cached_server_version(server)
961 if 'sfa' in server_version:
962 # just request the version the client wants
963 call_options['rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
965 # this must be a protogeni aggregate. We should request a v2 ad rspec
966 # regardless of what the client user requested
967 call_options['rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
968 #panos add info options
970 call_options['info'] = opts.info
972 call_args = [creds, call_options]
973 if self.server_supports_call_id_arg(server):
974 call_args.append(unique_call_id())
975 result = server.ListResources(*call_args)
976 if opts.file is None:
977 display_rspec(result, opts.format)
979 save_rspec_to_file(result, opts.file)
982 # created named slice with given rspec
983 def create(self, opts, args):
984 server = self.get_server_from_opts(opts)
985 server_version = self.get_cached_server_version(server)
987 slice_urn = hrn_to_urn(slice_hrn, 'slice')
988 user_cred = self.get_user_cred()
989 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
991 # always include a credential thats delegated to the callers root authority
992 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)+'.slicemanager')
993 creds.append(delegated_cred)
994 rspec_file = self.get_rspec_file(args[1])
995 rspec = open(rspec_file).read()
997 # need to pass along user keys to the aggregate.
999 # { urn: urn:publicid:IDN+emulab.net+user+alice
1000 # keys: [<ssh key A>, <ssh key B>]
1003 slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
1004 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1005 slice_record = slice_records[0]
1006 user_hrns = slice_record['researcher']
1007 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1008 user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
1010 if 'sfa' not in server_version:
1011 users = pg_users_arg(user_records)
1012 rspec = RSpec(rspec)
1013 rspec.filter({'component_manager_id': server_version['urn']})
1014 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1016 users = sfa_users_arg(user_records, slice_record)
1017 call_args = [slice_urn, creds, rspec, users]
1018 if self.server_supports_call_id_arg(server):
1019 call_args.append(unique_call_id())
1021 result = server.CreateSliver(*call_args)
1022 if opts.file is None:
1025 save_rspec_to_file (result, opts.file)
1028 # get a ticket for the specified slice
1029 def get_ticket(self, opts, args):
1030 slice_hrn, rspec_path = args[0], args[1]
1031 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1032 user_cred = self.get_user_cred()
1033 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1034 creds = [slice_cred]
1036 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1037 creds.append(delegated_cred)
1038 rspec_file = self.get_rspec_file(rspec_path)
1039 rspec = open(rspec_file).read()
1040 server = self.get_server_from_opts(opts)
1041 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1042 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1043 self.logger.info("writing ticket to %s"%file)
1044 ticket = SfaTicket(string=ticket_string)
1045 ticket.save_to_file(filename=file, save_parents=True)
1047 def redeem_ticket(self, opts, args):
1048 ticket_file = args[0]
1050 # get slice hrn from the ticket
1051 # use this to get the right slice credential
1052 ticket = SfaTicket(filename=ticket_file)
1054 slice_hrn = ticket.gidObject.get_hrn()
1055 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1056 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1057 user_cred = self.get_user_cred()
1058 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1060 # get a list of node hostnames from the RSpec
1061 tree = etree.parse(StringIO(ticket.rspec))
1062 root = tree.getroot()
1063 hostnames = root.xpath("./network/site/node/hostname/text()")
1065 # create an xmlrpc connection to the component manager at each of these
1066 # components and gall redeem_ticket
1068 for hostname in hostnames:
1070 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1071 server = self.get_server(hostname, CM_PORT, self.key_file, \
1072 self.cert_file, self.options.debug)
1073 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1074 self.logger.info("Success")
1075 except socket.gaierror:
1076 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1077 except Exception, e:
1078 self.logger.log_exc(e.message)
1081 # delete named slice
1082 def delete(self, opts, args):
1084 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1085 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1086 creds = [slice_cred]
1088 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1089 creds.append(delegated_cred)
1090 server = self.get_server_from_opts(opts)
1092 call_args = [slice_urn, creds]
1093 if self.server_supports_call_id_arg(server):
1094 call_args.append(unique_call_id())
1095 return server.DeleteSliver(*call_args)
1098 def start(self, opts, args):
1100 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1101 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1102 creds = [slice_cred]
1104 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1105 creds.append(delegated_cred)
1106 server = self.get_server_from_opts(opts)
1107 return server.Start(slice_urn, creds)
1110 def stop(self, opts, args):
1112 slice_urn = hrn_to_urn(slice_hrn, 'slice')
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 server = self.get_server_from_opts(opts)
1119 return server.Stop(slice_urn, creds)
1122 def reset(self, opts, args):
1124 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1125 server = self.get_server_from_opts(opts)
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 return server.reset_slice(creds, slice_urn)
1133 def renew(self, opts, args):
1135 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1136 server = self.get_server_from_opts(opts)
1137 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1138 creds = [slice_cred]
1140 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1141 creds.append(delegated_cred)
1144 call_args = [slice_urn, creds, time]
1145 if self.server_supports_call_id_arg(server):
1146 call_args.append(unique_call_id())
1147 return server.RenewSliver(*call_args)
1150 def status(self, opts, args):
1152 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1153 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1154 creds = [slice_cred]
1156 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1157 creds.append(delegated_cred)
1158 server = self.get_server_from_opts(opts)
1159 call_args = [slice_urn, creds]
1160 if self.server_supports_call_id_arg(server):
1161 call_args.append(unique_call_id())
1162 result = server.SliverStatus(*call_args)
1165 save_variable_to_file(result, opts.file, opts.fileformat)
1168 def shutdown(self, opts, args):
1170 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1171 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1172 creds = [slice_cred]
1174 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1175 creds.append(delegated_cred)
1176 server = self.get_server_from_opts(opts)
1177 return server.Shutdown(slice_urn, creds)
1179 def print_help (self):
1180 self.sfi_parser.print_help()
1181 self.cmd_parser.print_help()
1184 # Main: parse arguments and dispatch to command
1187 self.sfi_parser = self.create_parser()
1188 (options, args) = self.sfi_parser.parse_args()
1189 self.options = options
1191 self.logger.setLevelFromOptVerbose(self.options.verbose)
1192 if options.hashrequest:
1193 self.hashrequest = True
1196 self.logger.critical("No command given. Use -h for help.")
1200 self.cmd_parser = self.create_cmd_parser(command)
1201 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
1204 self.logger.info("Command=%s" % command)
1205 if command in ("resources"):
1206 self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
1207 elif command in ("list", "show", "remove"):
1208 self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
1209 self.logger.debug('cmd_args %s' % cmd_args)
1212 self.dispatch(command, cmd_opts, cmd_args)
1214 self.logger.critical ("Unknown command %s"%command)
1220 if __name__ == "__main__":