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()
982 options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
984 call_args = [creds, options]
985 result = server.ListResources(*call_args)
986 value = ReturnValue.get_value(result)
987 if opts.file is None:
988 display_rspec(value, opts.format)
990 save_rspec_to_file(value, opts.file)
993 # created named slice with given rspec
994 def create(self, opts, args):
995 server = self.server_proxy_from_opts(opts)
996 server_version = self.get_cached_server_version(server)
998 slice_urn = hrn_to_urn(slice_hrn, 'slice')
999 user_cred = self.get_user_cred()
1000 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1002 if hasattr(opts, 'aggregate') and opts.aggregate:
1003 delegated_cred = None
1005 # delegate the cred to the callers root authority
1006 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)+'.slicemanager')
1007 #delegated_cred = self.delegate_cred(slice_cred, get_authority(slice_hrn))
1008 #creds.append(delegated_cred)
1010 rspec_file = self.get_rspec_file(args[1])
1011 rspec = open(rspec_file).read()
1013 # need to pass along user keys to the aggregate.
1015 # { urn: urn:publicid:IDN+emulab.net+user+alice
1016 # keys: [<ssh key A>, <ssh key B>]
1019 slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
1020 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1021 slice_record = slice_records[0]
1022 user_hrns = slice_record['researcher']
1023 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1024 user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
1026 if 'sfa' not in server_version:
1027 users = pg_users_arg(user_records)
1028 rspec = RSpec(rspec)
1029 rspec.filter({'component_manager_id': server_version['urn']})
1030 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1031 creds = [slice_cred]
1033 users = sfa_users_arg(user_records, slice_record)
1034 creds = [slice_cred]
1036 creds.append(delegated_cred)
1037 call_args = [slice_urn, creds, rspec, users]
1038 if self.server_supports_options_arg(server):
1039 options = {'call_id': unique_call_id()}
1040 call_args.append(options)
1041 result = server.CreateSliver(*call_args)
1042 value = ReturnValue.get_value(result)
1043 if opts.file is None:
1046 save_rspec_to_file (value, opts.file)
1049 # get a ticket for the specified slice
1050 def get_ticket(self, opts, args):
1051 slice_hrn, rspec_path = args[0], args[1]
1052 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1053 user_cred = self.get_user_cred()
1054 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1055 creds = [slice_cred]
1057 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1058 creds.append(delegated_cred)
1059 rspec_file = self.get_rspec_file(rspec_path)
1060 rspec = open(rspec_file).read()
1061 server = self.server_proxy_from_opts(opts)
1062 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1063 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1064 self.logger.info("writing ticket to %s"%file)
1065 ticket = SfaTicket(string=ticket_string)
1066 ticket.save_to_file(filename=file, save_parents=True)
1068 def redeem_ticket(self, opts, args):
1069 ticket_file = args[0]
1071 # get slice hrn from the ticket
1072 # use this to get the right slice credential
1073 ticket = SfaTicket(filename=ticket_file)
1075 slice_hrn = ticket.gidObject.get_hrn()
1076 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1077 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1078 user_cred = self.get_user_cred()
1079 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1081 # get a list of node hostnames from the RSpec
1082 tree = etree.parse(StringIO(ticket.rspec))
1083 root = tree.getroot()
1084 hostnames = root.xpath("./network/site/node/hostname/text()")
1086 # create an xmlrpc connection to the component manager at each of these
1087 # components and gall redeem_ticket
1089 for hostname in hostnames:
1091 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1092 server = self.server_proxy(hostname, CM_PORT, self.key_file, \
1093 self.cert_file, self.options.debug)
1094 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1095 self.logger.info("Success")
1096 except socket.gaierror:
1097 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1098 except Exception, e:
1099 self.logger.log_exc(e.message)
1102 # delete named slice
1103 def delete(self, opts, args):
1105 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1106 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1107 creds = [slice_cred]
1109 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1110 creds.append(delegated_cred)
1111 server = self.server_proxy_from_opts(opts)
1112 call_args = [slice_urn, creds]
1113 if self.server_supports_options_arg(server):
1114 options = {'call_id': unique_call_id()}
1115 call_args.append(options)
1116 return server.DeleteSliver(*call_args)
1119 def start(self, opts, args):
1121 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1122 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1123 creds = [slice_cred]
1125 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1126 creds.append(delegated_cred)
1127 server = self.server_proxy_from_opts(opts)
1128 return server.Start(slice_urn, creds)
1131 def stop(self, opts, args):
1133 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1134 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1135 creds = [slice_cred]
1137 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1138 creds.append(delegated_cred)
1139 server = self.server_proxy_from_opts(opts)
1140 return server.Stop(slice_urn, creds)
1143 def reset(self, opts, args):
1145 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1146 server = self.server_proxy_from_opts(opts)
1147 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1148 creds = [slice_cred]
1150 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1151 creds.append(delegated_cred)
1152 return server.reset_slice(creds, slice_urn)
1154 def renew(self, opts, args):
1156 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1157 server = self.server_proxy_from_opts(opts)
1158 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1159 creds = [slice_cred]
1161 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1162 creds.append(delegated_cred)
1165 call_args = [slice_urn, creds, time]
1166 if self.server_supports_options_arg(server):
1167 options = {'call_id': unique_call_id()}
1168 call_args.append(options)
1169 result = server.RenewSliver(*call_args)
1170 value = ReturnValue.get_value(result)
1174 def status(self, opts, args):
1176 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1177 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1178 creds = [slice_cred]
1180 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1181 creds.append(delegated_cred)
1182 server = self.server_proxy_from_opts(opts)
1183 call_args = [slice_urn, creds]
1184 if self.server_supports_options_arg(server):
1185 options = {'call_id': unique_call_id()}
1186 call_args.append(options)
1187 result = server.SliverStatus(*call_args)
1188 value = ReturnValue.get_value(result)
1191 save_variable_to_file(value, opts.file, opts.fileformat)
1194 def shutdown(self, opts, args):
1196 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1197 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1198 creds = [slice_cred]
1200 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1201 creds.append(delegated_cred)
1202 server = self.server_proxy_from_opts(opts)
1203 return server.Shutdown(slice_urn, creds)
1205 def print_help (self):
1206 self.sfi_parser.print_help()
1207 self.cmd_parser.print_help()
1210 # Main: parse arguments and dispatch to command
1213 self.sfi_parser = self.create_parser()
1214 (options, args) = self.sfi_parser.parse_args()
1215 self.options = options
1217 self.logger.setLevelFromOptVerbose(self.options.verbose)
1218 if options.hashrequest:
1219 self.hashrequest = True
1222 self.logger.critical("No command given. Use -h for help.")
1226 self.cmd_parser = self.create_cmd_parser(command)
1227 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
1230 self.logger.info("Command=%s" % command)
1231 if command in ("resources"):
1232 self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
1233 elif command in ("list", "show", "remove"):
1234 self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
1235 self.logger.debug('cmd_args %s' % cmd_args)
1238 self.dispatch(command, cmd_opts, cmd_args)
1240 self.logger.critical ("Unknown command %s"%command)
1246 if __name__ == "__main__":