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
29 from sfa.storage.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
31 from sfa.rspecs.rspec import RSpec
32 from sfa.rspecs.rspec_converter import RSpecConverter
33 from sfa.rspecs.version_manager import VersionManager
34 from sfa.client.return_value import ReturnValue
36 import sfa.client.xmlrpcprotocol as xmlrpcprotocol
37 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
42 # utility methods here
44 def display_rspec(rspec, format='rspec'):
46 tree = etree.parse(StringIO(rspec))
48 result = root.xpath("./network/site/node/hostname/text()")
49 elif format in ['ip']:
50 # The IP address is not yet part of the new RSpec
51 # so this doesn't do anything yet.
52 tree = etree.parse(StringIO(rspec))
54 result = root.xpath("./network/site/node/ipv4/text()")
61 def display_list(results):
62 for result in results:
65 def display_records(recordList, dump=False):
66 ''' Print all fields in the record'''
67 for record in recordList:
68 display_record(record, dump)
70 def display_record(record, dump=False):
74 info = record.getdict()
75 print "%s (%s)" % (info['hrn'], info['type'])
79 def filter_records(type, records):
81 for record in records:
82 if (record['type'] == type) or (type == "all"):
83 filtered_records.append(record)
84 return filtered_records
88 def save_variable_to_file(var, filename, format="text"):
89 f = open(filename, "w")
92 elif format == "pickled":
93 f.write(pickle.dumps(var))
95 # this should never happen
96 print "unknown output format", format
99 def save_rspec_to_file(rspec, filename):
100 if not filename.endswith(".rspec"):
101 filename = filename + ".rspec"
102 f = open(filename, 'w')
107 def save_records_to_file(filename, recordList, format="xml"):
110 for record in recordList:
112 save_record_to_file(filename + "." + str(index), record)
114 save_record_to_file(filename, record)
116 elif format == "xmllist":
117 f = open(filename, "w")
118 f.write("<recordlist>\n")
119 for record in recordList:
120 record = SfaRecord(dict=record)
121 f.write('<record hrn="' + record.get_name() + '" type="' + record.get_type() + '" />\n')
122 f.write("</recordlist>\n")
124 elif format == "hrnlist":
125 f = open(filename, "w")
126 for record in recordList:
127 record = SfaRecord(dict=record)
128 f.write(record.get_name() + "\n")
131 # this should never happen
132 print "unknown output format", format
134 def save_record_to_file(filename, record):
135 if record['type'] in ['user']:
136 record = UserRecord(dict=record)
137 elif record['type'] in ['slice']:
138 record = SliceRecord(dict=record)
139 elif record['type'] in ['node']:
140 record = NodeRecord(dict=record)
141 elif record['type'] in ['authority', 'ma', 'sa']:
142 record = AuthorityRecord(dict=record)
144 record = SfaRecord(dict=record)
145 str = record.save_to_string()
146 f=codecs.open(filename, encoding='utf-8',mode="w")
153 def load_record_from_file(filename):
154 f=codecs.open(filename, encoding="utf-8", mode="r")
157 record = SfaRecord(string=str)
162 def unique_call_id(): return uuid.uuid4().urn
166 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user']
168 # dummy to meet Sfi's expectations for its 'options' field
169 # i.e. s/t we can do setattr on
173 def __init__ (self,options=None):
174 if options is None: options=Sfi.DummyOptions()
175 for opt in Sfi.required_options:
176 if not hasattr(options,opt): setattr(options,opt,None)
177 if not hasattr(options,'sfi_dir'): options.sfi_dir=os.path.expanduser("~/.sfi/")
178 # xxx oops, this is dangerous, sounds like ww sometimes have discrepency
179 # would be safer to remove self.sfi_dir altogether
180 self.sfi_dir = options.sfi_dir
181 self.options = options
185 self.authority = None
186 self.hashrequest = False
187 self.logger = sfi_logger
188 self.logger.enable_console()
190 def create_cmd_parser(self, command, additional_cmdargs=None):
191 cmdargs = {"list": "authority",
196 "aggregates": "[name]",
197 "registries": "[name]",
198 "create_gid": "[name]",
200 "get_trusted_certs": "cred",
202 "resources": "[name]",
203 "create": "name rspec",
204 "get_ticket": "name rspec",
205 "redeem_ticket": "ticket",
217 if additional_cmdargs:
218 cmdargs.update(additional_cmdargs)
220 if command not in cmdargs:
221 msg="Invalid command\n"
223 msg += ','.join(cmdargs.keys())
224 self.logger.critical(msg)
227 parser = OptionParser(usage="sfi [sfi_options] %s [options] %s" \
228 % (command, cmdargs[command]))
230 # user specifies remote aggregate/sm/component
231 if command in ("resources", "slices", "create", "delete", "start", "stop",
232 "restart", "shutdown", "get_ticket", "renew", "status"):
233 parser.add_option("-a", "--aggregate", dest="aggregate",
234 default=None, help="aggregate host")
235 parser.add_option("-p", "--port", dest="port",
236 default=AGGREGATE_PORT, help="aggregate port")
237 parser.add_option("-c", "--component", dest="component", default=None,
238 help="component hrn")
239 parser.add_option("-d", "--delegate", dest="delegate", default=None,
241 help="Include a credential delegated to the user's root"+\
242 "authority in set of credentials for this call")
244 # registy filter option
245 if command in ("list", "show", "remove"):
246 parser.add_option("-t", "--type", dest="type", type="choice",
247 help="type filter ([all]|user|slice|authority|node|aggregate)",
248 choices=("all", "user", "slice", "authority", "node", "aggregate"),
251 if command in ("resources"):
252 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
253 help="schema type and version of resulting RSpec")
254 parser.add_option("-f", "--format", dest="format", type="choice",
255 help="display format ([xml]|dns|ip)", default="xml",
256 choices=("xml", "dns", "ip"))
257 #panos: a new option to define the type of information about resources a user is interested in
258 parser.add_option("-i", "--info", dest="info",
259 help="optional component information", default=None)
262 # 'create' does return the new rspec, makes sense to save that too
263 if command in ("resources", "show", "list", "create_gid", 'create'):
264 parser.add_option("-o", "--output", dest="file",
265 help="output XML to file", metavar="FILE", default=None)
267 if command in ("show", "list"):
268 parser.add_option("-f", "--format", dest="format", type="choice",
269 help="display format ([text]|xml)", default="text",
270 choices=("text", "xml"))
272 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
273 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
274 choices=("xml", "xmllist", "hrnlist"))
276 if command in ("status", "version"):
277 parser.add_option("-o", "--output", dest="file",
278 help="output dictionary to file", metavar="FILE", default=None)
279 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
280 help="output file format ([text]|pickled)", default="text",
281 choices=("text","pickled"))
283 if command in ("delegate"):
284 parser.add_option("-u", "--user",
285 action="store_true", dest="delegate_user", default=False,
286 help="delegate user credential")
287 parser.add_option("-s", "--slice", dest="delegate_slice",
288 help="delegate slice credential", metavar="HRN", default=None)
290 if command in ("version"):
291 parser.add_option("-a", "--aggregate", dest="aggregate",
292 default=None, help="aggregate host")
293 parser.add_option("-p", "--port", dest="port",
294 default=AGGREGATE_PORT, help="aggregate port")
295 parser.add_option("-R","--registry-version",
296 action="store_true", dest="version_registry", default=False,
297 help="probe registry version instead of slicemgr")
298 parser.add_option("-l","--local",
299 action="store_true", dest="version_local", default=False,
300 help="display version of the local client")
305 def create_parser(self):
307 # Generate command line parser
308 parser = OptionParser(usage="sfi [options] command [command_options] [command_args]",
309 description="Commands: gid,list,show,remove,add,update,nodes,slices,resources,create,delete,start,stop,reset")
310 parser.add_option("-r", "--registry", dest="registry",
311 help="root registry", metavar="URL", default=None)
312 parser.add_option("-s", "--slicemgr", dest="sm",
313 help="slice manager", metavar="URL", default=None)
314 default_sfi_dir = os.path.expanduser("~/.sfi/")
315 parser.add_option("-d", "--dir", dest="sfi_dir",
316 help="config & working directory - default is " + default_sfi_dir,
317 metavar="PATH", default=default_sfi_dir)
318 parser.add_option("-u", "--user", dest="user",
319 help="user name", metavar="HRN", default=None)
320 parser.add_option("-a", "--auth", dest="auth",
321 help="authority name", metavar="HRN", default=None)
322 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
323 help="verbose mode - cumulative")
324 parser.add_option("-D", "--debug",
325 action="store_true", dest="debug", default=False,
326 help="Debug (xml-rpc) protocol messages")
327 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
328 help="RPC protocol (xmlrpc or soap)")
329 parser.add_option("-k", "--hashrequest",
330 action="store_true", dest="hashrequest", default=False,
331 help="Create a hash of the request that will be authenticated on the server")
332 parser.add_option("-t", "--timeout", dest="timeout", default=None,
333 help="Amout of time tom wait before timing out the request")
334 parser.disable_interspersed_args()
339 def read_config(self):
340 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
342 config = Config (config_file)
344 self.logger.critical("Failed to read configuration file %s"%config_file)
345 self.logger.info("Make sure to remove the export clauses and to add quotes")
346 if self.options.verbose==0:
347 self.logger.info("Re-run with -v for more details")
349 self.logger.log_exc("Could not read config file %s"%config_file)
354 if (self.options.sm is not None):
355 self.sm_url = self.options.sm
356 elif hasattr(config, "SFI_SM"):
357 self.sm_url = config.SFI_SM
359 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
363 if (self.options.registry is not None):
364 self.reg_url = self.options.registry
365 elif hasattr(config, "SFI_REGISTRY"):
366 self.reg_url = config.SFI_REGISTRY
368 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
373 if (self.options.user is not None):
374 self.user = self.options.user
375 elif hasattr(config, "SFI_USER"):
376 self.user = config.SFI_USER
378 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
382 if (self.options.auth is not None):
383 self.authority = self.options.auth
384 elif hasattr(config, "SFI_AUTH"):
385 self.authority = config.SFI_AUTH
387 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
395 # Establish Connection to SliceMgr and Registry Servers
397 def set_servers(self):
400 # Get key and certificate
401 key_file = self.get_key_file()
402 cert_file = self.get_cert_file(key_file)
403 self.key = Keypair(filename=key_file)
404 self.key_file = key_file
405 self.cert_file = cert_file
406 self.cert = GID(filename=cert_file)
407 self.logger.info("Contacting Registry at: %s"%self.reg_url)
408 self.registry = xmlrpcprotocol.server_proxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
409 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
410 self.slicemgr = xmlrpcprotocol.server_proxy(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
413 def get_cached_server_version(self, server):
414 # check local cache first
417 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
418 cache_key = server.url + "-version"
420 cache = Cache(cache_file)
423 self.logger.info("Local cache not found at: %s" % cache_file)
426 version = cache.get(cache_key)
429 result = server.GetVersion()
430 version= ReturnValue.get_value(result)
431 # cache version for 24 hours
432 cache.add(cache_key, version, ttl= 60*60*24)
433 self.logger.info("Updating cache file %s" % cache_file)
434 cache.save_to_file(cache_file)
439 def server_supports_options_arg(self, server):
441 Returns true if server support the optional call_id arg, false otherwise.
443 server_version = self.get_cached_server_version(server)
444 if 'sfa' in server_version and 'code_tag' in server_version:
445 code_tag = server_version['code_tag']
446 code_tag_parts = code_tag.split("-")
448 version_parts = code_tag_parts[0].split(".")
449 major, minor = version_parts[0], version_parts[1]
450 rev = code_tag_parts[1]
457 # Get various credential and spec files
459 # Establishes limiting conventions
460 # - conflates MAs and SAs
461 # - assumes last token in slice name is unique
463 # Bootstraps credentials
464 # - bootstrap user credential from self-signed certificate
465 # - bootstrap authority credential from user credential
466 # - bootstrap slice credential from user credential
470 def get_key_file(self):
471 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
472 if (os.path.isfile(file)):
475 self.logger.error("Key file %s does not exist"%file)
479 def get_cert_file(self, key_file):
481 cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
482 if (os.path.isfile(cert_file)):
483 # we'd perfer to use Registry issued certs instead of self signed certs.
484 # if this is a Registry cert (GID) then we are done
485 gid = GID(filename=cert_file)
489 # generate self signed certificate
490 k = Keypair(filename=key_file)
491 cert = Certificate(subject=self.user)
493 cert.set_issuer(k, self.user)
495 self.logger.info("Writing self-signed certificate to %s"%cert_file)
496 cert.save_to_file(cert_file)
498 # try to get registry issued cert
500 self.logger.info("Getting Registry issued cert")
502 # *hack. need to set registyr before _get_gid() is called
503 self.registry = xmlrpcprotocol.server_proxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
504 gid = self._get_gid(type='user')
506 self.logger.info("Writing certificate to %s"%cert_file)
507 gid.save_to_file(cert_file)
509 self.logger.info("Failed to download Registry issued cert")
513 def get_cached_gid(self, file):
518 if (os.path.isfile(file)):
519 gid = GID(filename=file)
523 def get_gid(self, opts, args):
525 Get the specify gid and save it to file
530 gid = self._get_gid(hrn)
531 self.logger.debug("Sfi.get_gid-> %s" % gid.save_to_string(save_parents=True))
534 def _get_gid(self, hrn=None, type=None):
536 git_gid helper. Retrive the gid from the registry and save it to file.
542 gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
543 gid = self.get_cached_gid(gidfile)
545 user_cred = self.get_user_cred()
546 records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
548 raise RecordNotFound(args[0])
553 if type == rec['type']:
556 raise RecordNotFound(args[0])
558 gid = GID(string=record['gid'])
559 self.logger.info("Writing gid to %s"%gidfile)
560 gid.save_to_file(filename=gidfile)
564 def get_cached_credential(self, file):
566 Return a cached credential only if it hasn't expired.
568 if (os.path.isfile(file)):
569 credential = Credential(filename=file)
570 # make sure it isnt expired
571 if not credential.get_expiration or \
572 datetime.datetime.today() < credential.get_expiration():
576 def get_user_cred(self):
577 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
578 return self.get_cred(file, 'user', self.user)
580 def get_auth_cred(self):
581 if not self.authority:
582 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
584 file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
585 return self.get_cred(file, 'authority', self.authority)
587 def get_slice_cred(self, name):
588 file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
589 return self.get_cred(file, 'slice', name)
591 def get_cred(self, file, type, hrn):
592 # attempt to load a cached credential
593 cred = self.get_cached_credential(file)
596 cert_string = self.cert.save_to_string(save_parents=True)
597 user_name = self.user.replace(self.authority + ".", '')
598 if user_name.count(".") > 0:
599 user_name = user_name.replace(".", '_')
600 self.user = self.authority + "." + user_name
601 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
603 # bootstrap slice credential from user credential
604 user_cred = self.get_user_cred().save_to_string(save_parents=True)
605 cred_str = self.registry.GetCredential(user_cred, hrn, type)
608 self.logger.critical("Failed to get %s credential" % type)
611 cred = Credential(string=cred_str)
612 cred.save_to_file(file, save_parents=True)
613 self.logger.info("Writing %s credential to %s" %(type, file))
618 def get_rspec_file(self, rspec):
619 if (os.path.isabs(rspec)):
622 file = os.path.join(self.options.sfi_dir, rspec)
623 if (os.path.isfile(file)):
626 self.logger.critical("No such rspec file %s"%rspec)
629 def get_record_file(self, record):
630 if (os.path.isabs(record)):
633 file = os.path.join(self.options.sfi_dir, record)
634 if (os.path.isfile(file)):
637 self.logger.critical("No such registry record file %s"%record)
640 def load_publickey_string(self, fn):
642 key_string = f.read()
644 # if the filename is a private key file, then extract the public key
645 if "PRIVATE KEY" in key_string:
646 outfn = tempfile.mktemp()
647 cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
650 key_string = f.read()
656 def get_component_proxy_from_hrn(self, hrn):
657 # direct connection to the nodes component manager interface
658 user_cred = self.get_user_cred().save_to_string(save_parents=True)
659 records = self.registry.Resolve(hrn, user_cred)
660 records = filter_records('node', records)
662 self.logger.warning("No such component:%r"% opts.component)
665 return self.server_proxy(record['hostname'], CM_PORT, self.key_file, self.cert_file)
667 def server_proxy(self, host, port, keyfile, certfile):
669 Return an instance of an xmlrpc server connection
671 # port is appended onto the domain, before the path. Should look like:
672 # http://domain:port/path
673 host_parts = host.split('/')
674 host_parts[0] = host_parts[0] + ":" + str(port)
675 url = "http://%s" % "/".join(host_parts)
676 return xmlrpcprotocol.server_proxy(url, keyfile, certfile, timeout=self.options.timeout, verbose=self.options.debug)
678 # xxx opts could be retrieved in self.options
679 def server_proxy_from_opts(self, opts):
681 Return instance of an xmlrpc connection to a slice manager, aggregate
682 or component server depending on the specified opts
684 server = self.slicemgr
685 # direct connection to an aggregate
686 if hasattr(opts, 'aggregate') and opts.aggregate:
687 server = self.server_proxy(opts.aggregate, opts.port, self.key_file, self.cert_file)
688 # direct connection to the nodes component manager interface
689 if hasattr(opts, 'component') and opts.component:
690 server = self.get_component_proxy_from_hrn(opts.component)
693 #==========================================================================
694 # Following functions implement the commands
696 # Registry-related commands
697 #==========================================================================
699 def dispatch(self, command, cmd_opts, cmd_args):
700 return getattr(self, command)(cmd_opts, cmd_args)
702 def create_gid(self, opts, args):
707 user_cred = self.get_user_cred().save_to_string(save_parents=True)
708 gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
712 filename = os.sep.join([self.sfi_dir, '%s.gid' % target_hrn])
713 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
714 GID(string=gid).save_to_file(filename)
717 # list entires in named authority registry
718 def list(self, opts, args):
723 user_cred = self.get_user_cred().save_to_string(save_parents=True)
725 list = self.registry.List(hrn, user_cred)
727 raise Exception, "Not enough parameters for the 'list' command"
729 # filter on person, slice, site, node, etc.
730 # THis really should be in the self.filter_records funct def comment...
731 list = filter_records(opts.type, list)
733 print "%s (%s)" % (record['hrn'], record['type'])
735 save_records_to_file(opts.file, list, opts.fileformat)
738 # show named registry record
739 def show(self, opts, args):
744 user_cred = self.get_user_cred().save_to_string(save_parents=True)
745 records = self.registry.Resolve(hrn, user_cred)
746 records = filter_records(opts.type, records)
748 print "No record of type", opts.type
749 for record in records:
750 if record['type'] in ['user']:
751 record = UserRecord(dict=record)
752 elif record['type'] in ['slice']:
753 record = SliceRecord(dict=record)
754 elif record['type'] in ['node']:
755 record = NodeRecord(dict=record)
756 elif record['type'].startswith('authority'):
757 record = AuthorityRecord(dict=record)
759 record = SfaRecord(dict=record)
760 if (opts.format == "text"):
763 print record.save_to_string()
765 save_records_to_file(opts.file, records, opts.fileformat)
768 def delegate(self, opts, args):
770 delegee_hrn = args[0]
771 if opts.delegate_user:
772 user_cred = self.get_user_cred()
773 cred = self.delegate_cred(user_cred, delegee_hrn)
774 elif opts.delegate_slice:
775 slice_cred = self.get_slice_cred(opts.delegate_slice)
776 cred = self.delegate_cred(slice_cred, delegee_hrn)
778 self.logger.warning("Must specify either --user or --slice <hrn>")
780 delegated_cred = Credential(string=cred)
781 object_hrn = delegated_cred.get_gid_object().get_hrn()
782 if opts.delegate_user:
783 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
784 + get_leaf(object_hrn) + ".cred")
785 elif opts.delegate_slice:
786 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
787 + get_leaf(object_hrn) + ".cred")
789 delegated_cred.save_to_file(dest_fn, save_parents=True)
791 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
793 def delegate_cred(self, object_cred, hrn):
794 # the gid and hrn of the object we are delegating
795 if isinstance(object_cred, str):
796 object_cred = Credential(string=object_cred)
797 object_gid = object_cred.get_gid_object()
798 object_hrn = object_gid.get_hrn()
800 if not object_cred.get_privileges().get_all_delegate():
801 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
804 # the delegating user's gid
805 caller_gid = self._get_gid(self.user)
806 caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
808 # the gid of the user who will be delegated to
809 delegee_gid = self._get_gid(hrn)
810 delegee_hrn = delegee_gid.get_hrn()
811 delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
812 delegee_gid.save_to_file(filename=delegee_gidfile)
813 dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
814 return dcred.save_to_string(save_parents=True)
816 # removed named registry record
817 # - have to first retrieve the record to be removed
818 def remove(self, opts, args):
819 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
827 return self.registry.Remove(hrn, auth_cred, type)
829 # add named registry record
830 def add(self, opts, args):
831 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
835 record_filepath = args[0]
836 rec_file = self.get_record_file(record_filepath)
837 record = load_record_from_file(rec_file).as_dict()
838 return self.registry.Register(record, auth_cred)
840 # update named registry entry
841 def update(self, opts, args):
842 user_cred = self.get_user_cred()
846 rec_file = self.get_record_file(args[0])
847 record = load_record_from_file(rec_file)
848 if record['type'] == "user":
849 if record.get_name() == user_cred.get_gid_object().get_hrn():
850 cred = user_cred.save_to_string(save_parents=True)
852 cred = self.get_auth_cred().save_to_string(save_parents=True)
853 elif record['type'] in ["slice"]:
855 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
856 except xmlrpcprotocol.ServerException, e:
857 # XXX smbaker -- once we have better error return codes, update this
858 # to do something better than a string compare
859 if "Permission error" in e.args[0]:
860 cred = self.get_auth_cred().save_to_string(save_parents=True)
863 elif record.get_type() in ["authority"]:
864 cred = self.get_auth_cred().save_to_string(save_parents=True)
865 elif record.get_type() == 'node':
866 cred = self.get_auth_cred().save_to_string(save_parents=True)
868 raise "unknown record type" + record.get_type()
869 record = record.as_dict()
870 return self.registry.Update(record, cred)
872 def get_trusted_certs(self, opts, args):
874 return uhe trusted certs at this interface
876 trusted_certs = self.registry.get_trusted_certs()
877 for trusted_cert in trusted_certs:
878 gid = GID(string=trusted_cert)
880 cert = Certificate(string=trusted_cert)
881 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
884 def aggregates(self, opts, args):
886 return a list of details about known aggregates
888 user_cred = self.get_user_cred().save_to_string(save_parents=True)
893 result = self.registry.get_aggregates(user_cred, hrn)
897 def registries(self, opts, args):
899 return a list of details about known registries
901 user_cred = self.get_user_cred().save_to_string(save_parents=True)
905 result = self.registry.get_registries(user_cred, hrn)
910 # ==================================================================
911 # Slice-related commands
912 # ==================================================================
914 def version(self, opts, args):
915 if opts.version_local:
916 version=version_core()
918 if opts.version_registry:
921 server = self.server_proxy_from_opts(opts)
922 result = server.GetVersion()
923 version = ReturnValue.get_value(result)
924 for (k,v) in version.iteritems():
925 print "%-20s: %s"%(k,v)
927 save_variable_to_file(version, opts.file, opts.fileformat)
929 # list instantiated slices
930 def slices(self, opts, args):
932 list instantiated slices
934 user_cred = self.get_user_cred().save_to_string(save_parents=True)
937 delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
938 creds.append(delegated_cred)
939 server = self.server_proxy_from_opts(opts)
941 if self.server_supports_options_arg(server):
942 options = {'call_id': unique_call_id()}
943 call_args.append(options)
944 result = server.ListSlices(*call_args)
945 value = ReturnValue.get_value(result)
949 # show rspec for named slice
950 def resources(self, opts, args):
951 user_cred = self.get_user_cred().save_to_string(save_parents=True)
952 server = self.slicemgr
953 server = self.server_proxy_from_opts(opts)
955 options = {'call_id': unique_call_id()}
956 #panos add info options
958 options['info'] = opts.info
961 cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
963 options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
969 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
970 creds.append(delegated_cred)
971 if opts.rspec_version:
972 version_manager = VersionManager()
973 server_version = self.get_cached_server_version(server)
974 if 'sfa' in server_version:
975 # just request the version the client wants
976 options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
978 # this must be a protogeni aggregate. We should request a v2 ad rspec
979 # regardless of what the client user requested
980 options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
981 options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
983 call_args = [creds, options]
984 result = server.ListResources(*call_args)
985 value = ReturnValue.get_value(result)
986 if opts.file is None:
987 display_rspec(value, opts.format)
989 save_rspec_to_file(value, opts.file)
992 # created named slice with given rspec
993 def create(self, opts, args):
994 server = self.server_proxy_from_opts(opts)
995 server_version = self.get_cached_server_version(server)
997 slice_urn = hrn_to_urn(slice_hrn, 'slice')
998 user_cred = self.get_user_cred()
999 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1001 if hasattr(opts, 'aggregate') and opts.aggregate:
1002 delegated_cred = None
1004 # delegate the cred to the callers root authority
1005 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)+'.slicemanager')
1006 #delegated_cred = self.delegate_cred(slice_cred, get_authority(slice_hrn))
1007 #creds.append(delegated_cred)
1009 rspec_file = self.get_rspec_file(args[1])
1010 rspec = open(rspec_file).read()
1012 # need to pass along user keys to the aggregate.
1014 # { urn: urn:publicid:IDN+emulab.net+user+alice
1015 # keys: [<ssh key A>, <ssh key B>]
1018 slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
1019 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1020 slice_record = slice_records[0]
1021 user_hrns = slice_record['researcher']
1022 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1023 user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
1025 if 'sfa' not in server_version:
1026 users = pg_users_arg(user_records)
1027 rspec = RSpec(rspec)
1028 rspec.filter({'component_manager_id': server_version['urn']})
1029 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1030 creds = [slice_cred]
1032 users = sfa_users_arg(user_records, slice_record)
1033 creds = [slice_cred]
1035 creds.append(delegated_cred)
1036 call_args = [slice_urn, creds, rspec, users]
1037 if self.server_supports_options_arg(server):
1038 options = {'call_id': unique_call_id()}
1039 call_args.append(options)
1040 result = server.CreateSliver(*call_args)
1041 value = ReturnValue.get_value(result)
1042 if opts.file is None:
1045 save_rspec_to_file (value, opts.file)
1048 # get a ticket for the specified slice
1049 def get_ticket(self, opts, args):
1050 slice_hrn, rspec_path = args[0], args[1]
1051 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1052 user_cred = self.get_user_cred()
1053 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1054 creds = [slice_cred]
1056 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1057 creds.append(delegated_cred)
1058 rspec_file = self.get_rspec_file(rspec_path)
1059 rspec = open(rspec_file).read()
1060 server = self.server_proxy_from_opts(opts)
1061 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1062 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1063 self.logger.info("writing ticket to %s"%file)
1064 ticket = SfaTicket(string=ticket_string)
1065 ticket.save_to_file(filename=file, save_parents=True)
1067 def redeem_ticket(self, opts, args):
1068 ticket_file = args[0]
1070 # get slice hrn from the ticket
1071 # use this to get the right slice credential
1072 ticket = SfaTicket(filename=ticket_file)
1074 slice_hrn = ticket.gidObject.get_hrn()
1075 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1076 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1077 user_cred = self.get_user_cred()
1078 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1080 # get a list of node hostnames from the RSpec
1081 tree = etree.parse(StringIO(ticket.rspec))
1082 root = tree.getroot()
1083 hostnames = root.xpath("./network/site/node/hostname/text()")
1085 # create an xmlrpc connection to the component manager at each of these
1086 # components and gall redeem_ticket
1088 for hostname in hostnames:
1090 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1091 server = self.server_proxy(hostname, CM_PORT, self.key_file, \
1092 self.cert_file, self.options.debug)
1093 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1094 self.logger.info("Success")
1095 except socket.gaierror:
1096 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1097 except Exception, e:
1098 self.logger.log_exc(e.message)
1101 # delete named slice
1102 def delete(self, opts, args):
1104 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1105 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1106 creds = [slice_cred]
1108 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1109 creds.append(delegated_cred)
1110 server = self.server_proxy_from_opts(opts)
1111 call_args = [slice_urn, creds]
1112 if self.server_supports_options_arg(server):
1113 options = {'call_id': unique_call_id()}
1114 call_args.append(options)
1115 return server.DeleteSliver(*call_args)
1118 def start(self, opts, args):
1120 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1121 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1122 creds = [slice_cred]
1124 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1125 creds.append(delegated_cred)
1126 server = self.server_proxy_from_opts(opts)
1127 return server.Start(slice_urn, creds)
1130 def stop(self, opts, args):
1132 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1133 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1134 creds = [slice_cred]
1136 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1137 creds.append(delegated_cred)
1138 server = self.server_proxy_from_opts(opts)
1139 return server.Stop(slice_urn, creds)
1142 def reset(self, opts, args):
1144 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1145 server = self.server_proxy_from_opts(opts)
1146 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1147 creds = [slice_cred]
1149 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1150 creds.append(delegated_cred)
1151 return server.reset_slice(creds, slice_urn)
1153 def renew(self, opts, args):
1155 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1156 server = self.server_proxy_from_opts(opts)
1157 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1158 creds = [slice_cred]
1160 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1161 creds.append(delegated_cred)
1164 call_args = [slice_urn, creds, time]
1165 if self.server_supports_options_arg(server):
1166 options = {'call_id': unique_call_id()}
1167 call_args.append(options)
1168 result = server.RenewSliver(*call_args)
1169 value = ReturnValue.get_value(result)
1173 def status(self, opts, args):
1175 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1176 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1177 creds = [slice_cred]
1179 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1180 creds.append(delegated_cred)
1181 server = self.server_proxy_from_opts(opts)
1182 call_args = [slice_urn, creds]
1183 if self.server_supports_options_arg(server):
1184 options = {'call_id': unique_call_id()}
1185 call_args.append(options)
1186 result = server.SliverStatus(*call_args)
1187 value = ReturnValue.get_value(result)
1190 save_variable_to_file(value, opts.file, opts.fileformat)
1193 def shutdown(self, opts, args):
1195 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1196 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1197 creds = [slice_cred]
1199 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1200 creds.append(delegated_cred)
1201 server = self.server_proxy_from_opts(opts)
1202 return server.Shutdown(slice_urn, creds)
1204 def print_help (self):
1205 self.sfi_parser.print_help()
1206 self.cmd_parser.print_help()
1209 # Main: parse arguments and dispatch to command
1212 self.sfi_parser = self.create_parser()
1213 (options, args) = self.sfi_parser.parse_args()
1214 self.options = options
1216 self.logger.setLevelFromOptVerbose(self.options.verbose)
1217 if options.hashrequest:
1218 self.hashrequest = True
1221 self.logger.critical("No command given. Use -h for help.")
1225 self.cmd_parser = self.create_cmd_parser(command)
1226 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
1229 self.logger.info("Command=%s" % command)
1230 if command in ("resources"):
1231 self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
1232 elif command in ("list", "show", "remove"):
1233 self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
1234 self.logger.debug('cmd_args %s' % cmd_args)
1237 self.dispatch(command, cmd_opts, cmd_args)
1239 self.logger.critical ("Unknown command %s"%command)
1245 if __name__ == "__main__":