3 # sfi -- slice-based facility interface
5 # xxx NOTE this will soon be reviewed to take advantage of sfaclientlib
15 from lxml import etree
16 from StringIO import StringIO
17 from optparse import OptionParser
19 from sfa.trust.certificate import Keypair, Certificate
20 from sfa.trust.gid import GID
21 from sfa.trust.credential import Credential
22 from sfa.trust.sfaticket import SfaTicket
24 from sfa.util.sfalogging import sfi_logger
25 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn
26 from sfa.util.config import Config
27 from sfa.util.version import version_core
28 from sfa.util.cache import Cache
30 from sfa.storage.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
32 from sfa.rspecs.rspec import RSpec
33 from sfa.rspecs.rspec_converter import RSpecConverter
34 from sfa.rspecs.version_manager import VersionManager
35 from sfa.client.return_value import ReturnValue
37 import sfa.client.sfaprotocol as sfaprotocol
38 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
43 # utility methods here
45 def display_rspec(rspec, format='rspec'):
47 tree = etree.parse(StringIO(rspec))
49 result = root.xpath("./network/site/node/hostname/text()")
50 elif format in ['ip']:
51 # The IP address is not yet part of the new RSpec
52 # so this doesn't do anything yet.
53 tree = etree.parse(StringIO(rspec))
55 result = root.xpath("./network/site/node/ipv4/text()")
62 def display_list(results):
63 for result in results:
66 def display_records(recordList, dump=False):
67 ''' Print all fields in the record'''
68 for record in recordList:
69 display_record(record, dump)
71 def display_record(record, dump=False):
75 info = record.getdict()
76 print "%s (%s)" % (info['hrn'], info['type'])
80 def filter_records(type, records):
82 for record in records:
83 if (record['type'] == type) or (type == "all"):
84 filtered_records.append(record)
85 return filtered_records
89 def save_variable_to_file(var, filename, format="text"):
90 f = open(filename, "w")
93 elif format == "pickled":
94 f.write(pickle.dumps(var))
96 # this should never happen
97 print "unknown output format", format
100 def save_rspec_to_file(rspec, filename):
101 if not filename.endswith(".rspec"):
102 filename = filename + ".rspec"
103 f = open(filename, 'w')
108 def save_records_to_file(filename, recordList, format="xml"):
111 for record in recordList:
113 save_record_to_file(filename + "." + str(index), record)
115 save_record_to_file(filename, record)
117 elif format == "xmllist":
118 f = open(filename, "w")
119 f.write("<recordlist>\n")
120 for record in recordList:
121 record = SfaRecord(dict=record)
122 f.write('<record hrn="' + record.get_name() + '" type="' + record.get_type() + '" />\n')
123 f.write("</recordlist>\n")
125 elif format == "hrnlist":
126 f = open(filename, "w")
127 for record in recordList:
128 record = SfaRecord(dict=record)
129 f.write(record.get_name() + "\n")
132 # this should never happen
133 print "unknown output format", format
135 def save_record_to_file(filename, record):
136 if record['type'] in ['user']:
137 record = UserRecord(dict=record)
138 elif record['type'] in ['slice']:
139 record = SliceRecord(dict=record)
140 elif record['type'] in ['node']:
141 record = NodeRecord(dict=record)
142 elif record['type'] in ['authority', 'ma', 'sa']:
143 record = AuthorityRecord(dict=record)
145 record = SfaRecord(dict=record)
146 str = record.save_to_string()
147 f=codecs.open(filename, encoding='utf-8',mode="w")
154 def load_record_from_file(filename):
155 f=codecs.open(filename, encoding="utf-8", mode="r")
158 record = SfaRecord(string=str)
163 def unique_call_id(): return uuid.uuid4().urn
167 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user']
170 def default_sfi_dir ():
171 if os.path.isfile("./sfi_config"):
174 return os.path.expanduser("~/.sfi/")
176 # dummy to meet Sfi's expectations for its 'options' field
177 # i.e. s/t we can do setattr on
181 def __init__ (self,options=None):
182 if options is None: options=Sfi.DummyOptions()
183 for opt in Sfi.required_options:
184 if not hasattr(options,opt): setattr(options,opt,None)
185 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
186 self.options = options
190 self.authority = None
191 self.hashrequest = False
192 self.logger = sfi_logger
193 self.logger.enable_console()
194 self.available_names = [ tuple[0] for tuple in Sfi.available ]
195 self.available_dict = dict (Sfi.available)
197 # tuples command-name expected-args in the order in which they should appear in the help
200 ("list", "authority"),
203 ("update", "record"),
206 ("resources", "[slice_hrn]"),
207 ("create", "slice_hrn rspec"),
208 ("delete", "slice_hrn"),
209 ("status", "slice_hrn"),
210 ("start", "slice_hrn"),
211 ("stop", "slice_hrn"),
212 ("reset", "slice_hrn"),
213 ("renew", "slice_hrn time"),
214 ("shutdown", "slice_hrn"),
215 ("get_ticket", "slice_hrn rspec"),
216 ("redeem_ticket", "ticket"),
217 ("delegate", "name"),
218 ("create_gid", "[name]"),
219 ("get_trusted_certs", "cred"),
222 def print_command_help (self):
223 print "%18s %-15s %s"%("command","args","description")
225 for command in self.available_names:
226 args=self.available_dict[command]
227 method=getattr(self,command,None)
229 if method: doc=getattr(method,'__doc__',"")
230 if not doc: doc="*** no doc found ***"
231 doc=doc.strip(" \t\n")
232 doc=doc.replace("\n","\n"+35*' ')
233 print "%18s %-15s %s"%(command,args,doc)
235 def create_cmd_parser(self, command):
236 if command not in self.available_dict:
237 msg="Invalid command\n"
239 msg += ','.join(self.available_names)
240 self.logger.critical(msg)
243 parser = OptionParser(usage="sfi [sfi_options] %s [options] %s" \
244 % (command, self.available_dict[command]))
246 # user specifies remote aggregate/sm/component
247 if command in ("resources", "slices", "create", "delete", "start", "stop",
248 "restart", "shutdown", "get_ticket", "renew", "status"):
249 parser.add_option("-a", "--aggregate", dest="aggregate",
250 default=None, help="aggregate host")
251 parser.add_option("-p", "--port", dest="port",
252 default=AGGREGATE_PORT, help="aggregate port")
253 parser.add_option("-c", "--component", dest="component", default=None,
254 help="component hrn")
255 parser.add_option("-d", "--delegate", dest="delegate", default=None,
257 help="Include a credential delegated to the user's root"+\
258 "authority in set of credentials for this call")
260 # registy filter option
261 if command in ("list", "show", "remove"):
262 parser.add_option("-t", "--type", dest="type", type="choice",
263 help="type filter ([all]|user|slice|authority|node|aggregate)",
264 choices=("all", "user", "slice", "authority", "node", "aggregate"),
267 if command in ("resources"):
268 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
269 help="schema type and version of resulting RSpec")
270 parser.add_option("-f", "--format", dest="format", type="choice",
271 help="display format ([xml]|dns|ip)", default="xml",
272 choices=("xml", "dns", "ip"))
273 #panos: a new option to define the type of information about resources a user is interested in
274 parser.add_option("-i", "--info", dest="info",
275 help="optional component information", default=None)
278 # 'create' does return the new rspec, makes sense to save that too
279 if command in ("resources", "show", "list", "create_gid", 'create'):
280 parser.add_option("-o", "--output", dest="file",
281 help="output XML to file", metavar="FILE", default=None)
283 if command in ("show", "list"):
284 parser.add_option("-f", "--format", dest="format", type="choice",
285 help="display format ([text]|xml)", default="text",
286 choices=("text", "xml"))
288 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
289 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
290 choices=("xml", "xmllist", "hrnlist"))
292 if command in ("status", "version"):
293 parser.add_option("-o", "--output", dest="file",
294 help="output dictionary to file", metavar="FILE", default=None)
295 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
296 help="output file format ([text]|pickled)", default="text",
297 choices=("text","pickled"))
299 if command in ("delegate"):
300 parser.add_option("-u", "--user",
301 action="store_true", dest="delegate_user", default=False,
302 help="delegate user credential")
303 parser.add_option("-s", "--slice", dest="delegate_slice",
304 help="delegate slice credential", metavar="HRN", default=None)
306 if command in ("version"):
307 parser.add_option("-a", "--aggregate", dest="aggregate",
308 default=None, help="aggregate host")
309 parser.add_option("-p", "--port", dest="port",
310 default=AGGREGATE_PORT, help="aggregate port")
311 parser.add_option("-R","--registry-version",
312 action="store_true", dest="version_registry", default=False,
313 help="probe registry version instead of slicemgr")
314 parser.add_option("-l","--local",
315 action="store_true", dest="version_local", default=False,
316 help="display version of the local client")
321 def create_parser(self):
323 # Generate command line parser
324 parser = OptionParser(usage="sfi [options] command [command_options] [command_args]",
325 description="Commands: %s"%(" ".join(self.available_names)))
326 parser.add_option("-r", "--registry", dest="registry",
327 help="root registry", metavar="URL", default=None)
328 parser.add_option("-s", "--slicemgr", dest="sm",
329 help="slice manager", metavar="URL", default=None)
330 parser.add_option("-d", "--dir", dest="sfi_dir",
331 help="config & working directory - default is " + Sfi.default_sfi_dir(),
332 metavar="PATH", default=Sfi.default_sfi_dir())
333 parser.add_option("-u", "--user", dest="user",
334 help="user name", metavar="HRN", default=None)
335 parser.add_option("-a", "--auth", dest="auth",
336 help="authority name", metavar="HRN", default=None)
337 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
338 help="verbose mode - cumulative")
339 parser.add_option("-D", "--debug",
340 action="store_true", dest="debug", default=False,
341 help="Debug (xml-rpc) protocol messages")
342 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
343 help="RPC protocol (xmlrpc or soap)")
344 parser.add_option("-k", "--hashrequest",
345 action="store_true", dest="hashrequest", default=False,
346 help="Create a hash of the request that will be authenticated on the server")
347 parser.add_option("-t", "--timeout", dest="timeout", default=None,
348 help="Amout of time to wait before timing out the request")
349 parser.add_option("-?", "--commands",
350 action="store_true", dest="command_help", default=False,
351 help="one page summary on commands & exit")
352 parser.disable_interspersed_args()
357 def print_help (self):
358 self.sfi_parser.print_help()
359 self.cmd_parser.print_help()
362 # Main: parse arguments and dispatch to command
364 def dispatch(self, command, cmd_opts, cmd_args):
365 return getattr(self, command)(cmd_opts, cmd_args)
368 self.sfi_parser = self.create_parser()
369 (options, args) = self.sfi_parser.parse_args()
370 if options.command_help:
371 self.print_command_help()
373 self.options = options
375 self.logger.setLevelFromOptVerbose(self.options.verbose)
376 if options.hashrequest:
377 self.hashrequest = True
380 self.logger.critical("No command given. Use -h for help.")
384 self.cmd_parser = self.create_cmd_parser(command)
385 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
388 self.logger.info("Command=%s" % command)
389 if command in ("resources"):
390 self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
391 elif command in ("list", "show", "remove"):
392 self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
393 self.logger.debug('cmd_args %s' % cmd_args)
396 self.dispatch(command, cmd_opts, cmd_args)
398 self.logger.critical ("Unknown command %s"%command)
405 def read_config(self):
406 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
408 config = Config (config_file)
410 self.logger.critical("Failed to read configuration file %s"%config_file)
411 self.logger.info("Make sure to remove the export clauses and to add quotes")
412 if self.options.verbose==0:
413 self.logger.info("Re-run with -v for more details")
415 self.logger.log_exc("Could not read config file %s"%config_file)
420 if (self.options.sm is not None):
421 self.sm_url = self.options.sm
422 elif hasattr(config, "SFI_SM"):
423 self.sm_url = config.SFI_SM
425 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
429 if (self.options.registry is not None):
430 self.reg_url = self.options.registry
431 elif hasattr(config, "SFI_REGISTRY"):
432 self.reg_url = config.SFI_REGISTRY
434 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
439 if (self.options.user is not None):
440 self.user = self.options.user
441 elif hasattr(config, "SFI_USER"):
442 self.user = config.SFI_USER
444 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
448 if (self.options.auth is not None):
449 self.authority = self.options.auth
450 elif hasattr(config, "SFI_AUTH"):
451 self.authority = config.SFI_AUTH
453 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
461 # Establish Connection to SliceMgr and Registry Servers
463 def set_servers(self):
466 # Get key and certificate
467 key_file = self.get_key_file()
468 cert_file = self.get_cert_file(key_file)
469 self.key_file = key_file
470 self.cert_file = cert_file
471 self.cert = GID(filename=cert_file)
472 self.logger.info("Contacting Registry at: %s"%self.reg_url)
473 self.registry = sfaprotocol.server_proxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
474 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
475 self.slicemgr = sfaprotocol.server_proxy(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
478 def get_cached_server_version(self, server):
479 # check local cache first
482 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
483 cache_key = server.url + "-version"
485 cache = Cache(cache_file)
488 self.logger.info("Local cache not found at: %s" % cache_file)
491 version = cache.get(cache_key)
494 result = server.GetVersion()
495 version= ReturnValue.get_value(result)
496 # cache version for 24 hours
497 cache.add(cache_key, version, ttl= 60*60*24)
498 self.logger.info("Updating cache file %s" % cache_file)
499 cache.save_to_file(cache_file)
504 def server_supports_options_arg(self, server):
506 Returns true if server support the optional call_id arg, false otherwise.
508 server_version = self.get_cached_server_version(server)
509 if 'sfa' in server_version and 'code_tag' in server_version:
510 code_tag = server_version['code_tag']
511 code_tag_parts = code_tag.split("-")
513 version_parts = code_tag_parts[0].split(".")
514 major, minor = version_parts[0], version_parts[1]
515 rev = code_tag_parts[1]
522 # Get various credential and spec files
524 # Establishes limiting conventions
525 # - conflates MAs and SAs
526 # - assumes last token in slice name is unique
528 # Bootstraps credentials
529 # - bootstrap user credential from self-signed certificate
530 # - bootstrap authority credential from user credential
531 # - bootstrap slice credential from user credential
535 def get_key_file(self):
536 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
537 if (os.path.isfile(file)):
540 self.logger.error("Key file %s does not exist"%file)
544 def get_cert_file(self, key_file):
546 cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
547 if (os.path.isfile(cert_file)):
548 # we'd perfer to use Registry issued certs instead of self signed certs.
549 # if this is a Registry cert (GID) then we are done
550 gid = GID(filename=cert_file)
554 # generate self signed certificate
555 k = Keypair(filename=key_file)
556 cert = Certificate(subject=self.user)
558 cert.set_issuer(k, self.user)
560 self.logger.info("Writing self-signed certificate to %s"%cert_file)
561 cert.save_to_file(cert_file)
563 # try to get registry issued cert
565 self.logger.info("Getting Registry issued cert")
567 # *hack. need to set registry before _get_gid() is called
568 self.registry = sfaprotocol.server_proxy(self.reg_url, key_file, cert_file,
569 timeout=self.options.timeout, verbose=self.options.debug)
570 gid = self._get_gid(type='user')
572 self.logger.info("Writing certificate to %s"%cert_file)
573 gid.save_to_file(cert_file)
575 self.logger.info("Failed to download Registry issued cert")
579 def get_cached_gid(self, file):
584 if (os.path.isfile(file)):
585 gid = GID(filename=file)
590 # def get_gid(self, opts, args):
591 # """ Get the specify gid and save it to file """
595 # gid = self._get_gid(hrn)
596 # self.logger.debug("Sfi.get_gid-> %s" % gid.save_to_string(save_parents=True))
599 def _get_gid(self, hrn=None, type=None):
601 git_gid helper. Retrive the gid from the registry and save it to file.
607 gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
608 gid = self.get_cached_gid(gidfile)
610 user_cred = self.get_user_cred()
611 records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
613 raise RecordNotFound(args[0])
618 if type == rec['type']:
621 raise RecordNotFound(args[0])
623 gid = GID(string=record['gid'])
624 self.logger.info("Writing gid to %s"%gidfile)
625 gid.save_to_file(filename=gidfile)
629 def get_cached_credential(self, file):
631 Return a cached credential only if it hasn't expired.
633 if (os.path.isfile(file)):
634 credential = Credential(filename=file)
635 # make sure it isnt expired
636 if not credential.get_expiration or \
637 datetime.datetime.today() < credential.get_expiration():
641 def get_user_cred(self):
642 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
643 return self.get_cred(file, 'user', self.user)
645 def get_auth_cred(self):
646 if not self.authority:
647 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
649 file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
650 return self.get_cred(file, 'authority', self.authority)
652 def get_slice_cred(self, name):
653 file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
654 return self.get_cred(file, 'slice', name)
656 def get_cred(self, file, type, hrn):
657 # attempt to load a cached credential
658 cred = self.get_cached_credential(file)
661 cert_string = self.cert.save_to_string(save_parents=True)
662 user_name = self.user.replace(self.authority + ".", '')
663 if user_name.count(".") > 0:
664 user_name = user_name.replace(".", '_')
665 self.user = self.authority + "." + user_name
666 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
668 # bootstrap slice credential from user credential
669 user_cred = self.get_user_cred().save_to_string(save_parents=True)
670 cred_str = self.registry.GetCredential(user_cred, hrn, type)
673 self.logger.critical("Failed to get %s credential" % type)
676 cred = Credential(string=cred_str)
677 cred.save_to_file(file, save_parents=True)
678 self.logger.info("Writing %s credential to %s" %(type, file))
683 def delegate_cred(self, object_cred, hrn):
684 # the gid and hrn of the object we are delegating
685 if isinstance(object_cred, str):
686 object_cred = Credential(string=object_cred)
687 object_gid = object_cred.get_gid_object()
688 object_hrn = object_gid.get_hrn()
690 if not object_cred.get_privileges().get_all_delegate():
691 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
694 # the delegating user's gid
695 caller_gid = self._get_gid(self.user)
696 caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
698 # the gid of the user who will be delegated to
699 delegee_gid = self._get_gid(hrn)
700 delegee_hrn = delegee_gid.get_hrn()
701 delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
702 delegee_gid.save_to_file(filename=delegee_gidfile)
703 dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
704 return dcred.save_to_string(save_parents=True)
706 ######################################## miscell utilities
707 def get_rspec_file(self, rspec):
708 if (os.path.isabs(rspec)):
711 file = os.path.join(self.options.sfi_dir, rspec)
712 if (os.path.isfile(file)):
715 self.logger.critical("No such rspec file %s"%rspec)
718 def get_record_file(self, record):
719 if (os.path.isabs(record)):
722 file = os.path.join(self.options.sfi_dir, record)
723 if (os.path.isfile(file)):
726 self.logger.critical("No such registry record file %s"%record)
730 def get_component_proxy_from_hrn(self, hrn):
731 # direct connection to the nodes component manager interface
732 user_cred = self.get_user_cred().save_to_string(save_parents=True)
733 records = self.registry.Resolve(hrn, user_cred)
734 records = filter_records('node', records)
736 self.logger.warning("No such component:%r"% opts.component)
739 return self.server_proxy(record['hostname'], CM_PORT, self.key_file, self.cert_file)
741 def server_proxy(self, host, port, keyfile, certfile):
743 Return an instance of an xmlrpc server connection
745 # port is appended onto the domain, before the path. Should look like:
746 # http://domain:port/path
747 host_parts = host.split('/')
748 host_parts[0] = host_parts[0] + ":" + str(port)
749 url = "http://%s" % "/".join(host_parts)
750 return sfaprotocol.server_proxy(url, keyfile, certfile, timeout=self.options.timeout,
751 verbose=self.options.debug)
753 # xxx opts could be retrieved in self.options
754 def server_proxy_from_opts(self, opts):
756 Return instance of an xmlrpc connection to a slice manager, aggregate
757 or component server depending on the specified opts
759 server = self.slicemgr
760 # direct connection to an aggregate
761 if hasattr(opts, 'aggregate') and opts.aggregate:
762 server = self.server_proxy(opts.aggregate, opts.port, self.key_file, self.cert_file)
763 # direct connection to the nodes component manager interface
764 if hasattr(opts, 'component') and opts.component:
765 server = self.get_component_proxy_from_hrn(opts.component)
768 #==========================================================================
769 # Following functions implement the commands
771 # Registry-related commands
772 #==========================================================================
774 def version(self, opts, args):
776 display an SFA server version (GetVersion)
777 or version information about sfi itself
779 if opts.version_local:
780 version=version_core()
782 if opts.version_registry:
785 server = self.server_proxy_from_opts(opts)
786 result = server.GetVersion()
787 version = ReturnValue.get_value(result)
788 for (k,v) in version.iteritems():
789 print "%-20s: %s"%(k,v)
791 save_variable_to_file(version, opts.file, opts.fileformat)
793 def list(self, opts, args):
795 list entries in named authority registry (List)
801 user_cred = self.get_user_cred().save_to_string(save_parents=True)
803 list = self.registry.List(hrn, user_cred)
805 raise Exception, "Not enough parameters for the 'list' command"
807 # filter on person, slice, site, node, etc.
808 # THis really should be in the self.filter_records funct def comment...
809 list = filter_records(opts.type, list)
811 print "%s (%s)" % (record['hrn'], record['type'])
813 save_records_to_file(opts.file, list, opts.fileformat)
816 def show(self, opts, args):
818 show details about named registry record (Resolve)
824 user_cred = self.get_user_cred().save_to_string(save_parents=True)
825 records = self.registry.Resolve(hrn, user_cred)
826 records = filter_records(opts.type, records)
828 self.logger.error("No record of type %s"% opts.type)
829 for record in records:
830 if record['type'] in ['user']:
831 record = UserRecord(dict=record)
832 elif record['type'] in ['slice']:
833 record = SliceRecord(dict=record)
834 elif record['type'] in ['node']:
835 record = NodeRecord(dict=record)
836 elif record['type'].startswith('authority'):
837 record = AuthorityRecord(dict=record)
839 record = SfaRecord(dict=record)
840 if (opts.format == "text"):
843 print record.save_to_string()
845 save_records_to_file(opts.file, records, opts.fileformat)
848 def add(self, opts, args):
849 "add record into registry from xml file (Register)"
850 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
854 record_filepath = args[0]
855 rec_file = self.get_record_file(record_filepath)
856 record = load_record_from_file(rec_file).as_dict()
857 return self.registry.Register(record, auth_cred)
859 def update(self, opts, args):
860 "update record into registry from xml file (Update)"
861 user_cred = self.get_user_cred()
865 rec_file = self.get_record_file(args[0])
866 record = load_record_from_file(rec_file)
867 if record['type'] == "user":
868 if record.get_name() == user_cred.get_gid_object().get_hrn():
869 cred = user_cred.save_to_string(save_parents=True)
871 cred = self.get_auth_cred().save_to_string(save_parents=True)
872 elif record['type'] in ["slice"]:
874 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
875 except sfaprotocol.ServerException, e:
876 # XXX smbaker -- once we have better error return codes, update this
877 # to do something better than a string compare
878 if "Permission error" in e.args[0]:
879 cred = self.get_auth_cred().save_to_string(save_parents=True)
882 elif record.get_type() in ["authority"]:
883 cred = self.get_auth_cred().save_to_string(save_parents=True)
884 elif record.get_type() == 'node':
885 cred = self.get_auth_cred().save_to_string(save_parents=True)
887 raise "unknown record type" + record.get_type()
888 record = record.as_dict()
889 return self.registry.Update(record, cred)
891 def remove(self, opts, args):
892 "remove registry record by name (Remove)"
893 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
901 return self.registry.Remove(hrn, auth_cred, type)
903 # ==================================================================
904 # Slice-related commands
905 # ==================================================================
907 def slices(self, opts, args):
908 "list instantiated slices (ListSlices) - returns urn's"
909 user_cred = self.get_user_cred().save_to_string(save_parents=True)
912 delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
913 creds.append(delegated_cred)
914 server = self.server_proxy_from_opts(opts)
916 if self.server_supports_options_arg(server):
917 options = {'call_id': unique_call_id()}
918 call_args.append(options)
919 result = server.ListSlices(*call_args)
920 value = ReturnValue.get_value(result)
924 # show rspec for named slice
925 def resources(self, opts, args):
927 with no arg, discover available resources,
928 or currently provisioned resources (ListResources)
930 user_cred = self.get_user_cred().save_to_string(save_parents=True)
931 server = self.server_proxy_from_opts(opts)
933 options = {'call_id': unique_call_id()}
934 #panos add info options
936 options['info'] = opts.info
939 cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
941 options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
947 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
948 creds.append(delegated_cred)
949 if opts.rspec_version:
950 version_manager = VersionManager()
951 server_version = self.get_cached_server_version(server)
952 if 'sfa' in server_version:
953 # just request the version the client wants
954 options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
956 # this must be a protogeni aggregate. We should request a v2 ad rspec
957 # regardless of what the client user requested
958 options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
960 options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
962 call_args = [creds, options]
963 result = server.ListResources(*call_args)
964 value = ReturnValue.get_value(result)
965 if opts.file is None:
966 display_rspec(value, opts.format)
968 save_rspec_to_file(value, opts.file)
971 def create(self, opts, args):
973 create or update named slice with given rspec
975 server = self.server_proxy_from_opts(opts)
976 server_version = self.get_cached_server_version(server)
978 slice_urn = hrn_to_urn(slice_hrn, 'slice')
979 user_cred = self.get_user_cred()
980 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
981 delegated_cred = None
982 if server_version.get('interface') == 'slicemgr':
983 # delegate our cred to the slice manager
984 # do not delegate cred to slicemgr...not working at the moment
986 #if server_version.get('hrn'):
987 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
988 #elif server_version.get('urn'):
989 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
991 rspec_file = self.get_rspec_file(args[1])
992 rspec = open(rspec_file).read()
994 # need to pass along user keys to the aggregate.
996 # { urn: urn:publicid:IDN+emulab.net+user+alice
997 # keys: [<ssh key A>, <ssh key B>]
1000 slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
1001 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
1002 slice_record = slice_records[0]
1003 user_hrns = slice_record['researcher']
1004 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
1005 user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
1007 if 'sfa' not in server_version:
1008 users = pg_users_arg(user_records)
1009 rspec = RSpec(rspec)
1010 rspec.filter({'component_manager_id': server_version['urn']})
1011 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
1012 creds = [slice_cred]
1014 users = sfa_users_arg(user_records, slice_record)
1015 creds = [slice_cred]
1017 creds.append(delegated_cred)
1018 call_args = [slice_urn, creds, rspec, users]
1019 if self.server_supports_options_arg(server):
1020 options = {'call_id': unique_call_id()}
1021 call_args.append(options)
1022 result = server.CreateSliver(*call_args)
1023 value = ReturnValue.get_value(result)
1024 if opts.file is None:
1027 save_rspec_to_file (value, opts.file)
1030 def delete(self, opts, args):
1032 delete named slice (DeleteSliver)
1035 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1036 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1037 creds = [slice_cred]
1039 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1040 creds.append(delegated_cred)
1041 server = self.server_proxy_from_opts(opts)
1042 call_args = [slice_urn, creds]
1043 if self.server_supports_options_arg(server):
1044 options = {'call_id': unique_call_id()}
1045 call_args.append(options)
1046 return server.DeleteSliver(*call_args)
1048 def status(self, opts, args):
1050 retrieve slice status (SliverStatus)
1053 slice_urn = hrn_to_urn(slice_hrn, 'slice')
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 server = self.server_proxy_from_opts(opts)
1060 call_args = [slice_urn, creds]
1061 if self.server_supports_options_arg(server):
1062 options = {'call_id': unique_call_id()}
1063 call_args.append(options)
1064 result = server.SliverStatus(*call_args)
1065 value = ReturnValue.get_value(result)
1068 save_variable_to_file(value, opts.file, opts.fileformat)
1070 def start(self, opts, args):
1072 start named slice (Start)
1075 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1076 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1077 creds = [slice_cred]
1079 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1080 creds.append(delegated_cred)
1081 server = self.server_proxy_from_opts(opts)
1082 return server.Start(slice_urn, creds)
1084 def stop(self, opts, args):
1086 stop named slice (Stop)
1089 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1090 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1091 creds = [slice_cred]
1093 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1094 creds.append(delegated_cred)
1095 server = self.server_proxy_from_opts(opts)
1096 return server.Stop(slice_urn, creds)
1099 def reset(self, opts, args):
1101 reset named slice (reset_slice)
1104 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1105 server = self.server_proxy_from_opts(opts)
1106 slice_cred = self.get_slice_cred(args[0]).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 return server.reset_slice(creds, slice_urn)
1113 def renew(self, opts, args):
1115 renew slice (RenewSliver)
1118 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1119 server = self.server_proxy_from_opts(opts)
1120 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1121 creds = [slice_cred]
1123 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1124 creds.append(delegated_cred)
1127 call_args = [slice_urn, creds, time]
1128 if self.server_supports_options_arg(server):
1129 options = {'call_id': unique_call_id()}
1130 call_args.append(options)
1131 result = server.RenewSliver(*call_args)
1132 value = ReturnValue.get_value(result)
1136 def shutdown(self, opts, args):
1138 shutdown named slice (Shutdown)
1141 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1142 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1143 creds = [slice_cred]
1145 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1146 creds.append(delegated_cred)
1147 server = self.server_proxy_from_opts(opts)
1148 return server.Shutdown(slice_urn, creds)
1151 def get_ticket(self, opts, args):
1153 get a ticket for the specified slice
1155 slice_hrn, rspec_path = args[0], args[1]
1156 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1157 user_cred = self.get_user_cred()
1158 slice_cred = self.get_slice_cred(slice_hrn).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)
1163 rspec_file = self.get_rspec_file(rspec_path)
1164 rspec = open(rspec_file).read()
1165 server = self.server_proxy_from_opts(opts)
1166 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1167 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1168 self.logger.info("writing ticket to %s"%file)
1169 ticket = SfaTicket(string=ticket_string)
1170 ticket.save_to_file(filename=file, save_parents=True)
1172 def redeem_ticket(self, opts, args):
1174 Connects to nodes in a slice and redeems a ticket
1175 (slice hrn is retrieved from the ticket)
1177 ticket_file = args[0]
1179 # get slice hrn from the ticket
1180 # use this to get the right slice credential
1181 ticket = SfaTicket(filename=ticket_file)
1183 slice_hrn = ticket.gidObject.get_hrn()
1184 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1185 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1186 user_cred = self.get_user_cred()
1187 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1189 # get a list of node hostnames from the RSpec
1190 tree = etree.parse(StringIO(ticket.rspec))
1191 root = tree.getroot()
1192 hostnames = root.xpath("./network/site/node/hostname/text()")
1194 # create an xmlrpc connection to the component manager at each of these
1195 # components and gall redeem_ticket
1197 for hostname in hostnames:
1199 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1200 server = self.server_proxy(hostname, CM_PORT, self.key_file, \
1201 self.cert_file, self.options.debug)
1202 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1203 self.logger.info("Success")
1204 except socket.gaierror:
1205 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1206 except Exception, e:
1207 self.logger.log_exc(e.message)
1210 def create_gid(self, opts, args):
1212 Create a GID (CreateGid)
1217 target_hrn = args[0]
1218 user_cred = self.get_user_cred().save_to_string(save_parents=True)
1219 gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
1221 filename = opts.file
1223 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1224 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1225 GID(string=gid).save_to_file(filename)
1228 def delegate(self, opts, args):
1230 (locally) create delegate credential for use by given hrn
1232 delegee_hrn = args[0]
1233 if opts.delegate_user:
1234 user_cred = self.get_user_cred()
1235 cred = self.delegate_cred(user_cred, delegee_hrn)
1236 elif opts.delegate_slice:
1237 slice_cred = self.get_slice_cred(opts.delegate_slice)
1238 cred = self.delegate_cred(slice_cred, delegee_hrn)
1240 self.logger.warning("Must specify either --user or --slice <hrn>")
1242 delegated_cred = Credential(string=cred)
1243 object_hrn = delegated_cred.get_gid_object().get_hrn()
1244 if opts.delegate_user:
1245 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1246 + get_leaf(object_hrn) + ".cred")
1247 elif opts.delegate_slice:
1248 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1249 + get_leaf(object_hrn) + ".cred")
1251 delegated_cred.save_to_file(dest_fn, save_parents=True)
1253 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1255 def get_trusted_certs(self, opts, args):
1257 return uhe trusted certs at this interface (get_trusted_certs)
1259 trusted_certs = self.registry.get_trusted_certs()
1260 for trusted_cert in trusted_certs:
1261 gid = GID(string=trusted_cert)
1263 cert = Certificate(string=trusted_cert)
1264 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
1267 if __name__ == "__main__":