2 # xxx NOTE this will soon be reviewed to take advantage of sfaclientlib
12 from lxml import etree
13 from StringIO import StringIO
14 from optparse import OptionParser
16 from sfa.trust.certificate import Keypair, Certificate
17 from sfa.trust.gid import GID
18 from sfa.trust.credential import Credential
19 from sfa.trust.sfaticket import SfaTicket
21 from sfa.util.sfalogging import sfi_logger
22 from sfa.util.xrn import get_leaf, get_authority, hrn_to_urn
23 from sfa.util.config import Config
24 from sfa.util.version import version_core
25 from sfa.util.cache import Cache
27 from sfa.storage.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
29 from sfa.rspecs.rspec import RSpec
30 from sfa.rspecs.rspec_converter import RSpecConverter
31 from sfa.rspecs.version_manager import VersionManager
33 from sfa.client.sfaserverproxy import SfaServerProxy, ServerException
34 from sfa.client.client_helper import pg_users_arg, sfa_users_arg
35 from sfa.client.return_value import ReturnValue
40 # utility methods here
42 def display_rspec(rspec, format='rspec'):
44 tree = etree.parse(StringIO(rspec))
46 result = root.xpath("./network/site/node/hostname/text()")
47 elif format in ['ip']:
48 # The IP address is not yet part of the new RSpec
49 # so this doesn't do anything yet.
50 tree = etree.parse(StringIO(rspec))
52 result = root.xpath("./network/site/node/ipv4/text()")
59 def display_list(results):
60 for result in results:
63 def display_records(recordList, dump=False):
64 ''' Print all fields in the record'''
65 for record in recordList:
66 display_record(record, dump)
68 def display_record(record, dump=False):
72 info = record.getdict()
73 print "%s (%s)" % (info['hrn'], info['type'])
77 def filter_records(type, records):
79 for record in records:
80 if (record['type'] == type) or (type == "all"):
81 filtered_records.append(record)
82 return filtered_records
86 def save_variable_to_file(var, filename, format="text"):
87 f = open(filename, "w")
90 elif format == "pickled":
91 f.write(pickle.dumps(var))
93 # this should never happen
94 print "unknown output format", format
97 def save_rspec_to_file(rspec, filename):
98 if not filename.endswith(".rspec"):
99 filename = filename + ".rspec"
100 f = open(filename, 'w')
105 def save_records_to_file(filename, recordList, format="xml"):
108 for record in recordList:
110 save_record_to_file(filename + "." + str(index), record)
112 save_record_to_file(filename, record)
114 elif format == "xmllist":
115 f = open(filename, "w")
116 f.write("<recordlist>\n")
117 for record in recordList:
118 record = SfaRecord(dict=record)
119 f.write('<record hrn="' + record.get_name() + '" type="' + record.get_type() + '" />\n')
120 f.write("</recordlist>\n")
122 elif format == "hrnlist":
123 f = open(filename, "w")
124 for record in recordList:
125 record = SfaRecord(dict=record)
126 f.write(record.get_name() + "\n")
129 # this should never happen
130 print "unknown output format", format
132 def save_record_to_file(filename, record):
133 if record['type'] in ['user']:
134 record = UserRecord(dict=record)
135 elif record['type'] in ['slice']:
136 record = SliceRecord(dict=record)
137 elif record['type'] in ['node']:
138 record = NodeRecord(dict=record)
139 elif record['type'] in ['authority', 'ma', 'sa']:
140 record = AuthorityRecord(dict=record)
142 record = SfaRecord(dict=record)
143 str = record.save_to_string()
144 f=codecs.open(filename, encoding='utf-8',mode="w")
151 def load_record_from_file(filename):
152 f=codecs.open(filename, encoding="utf-8", mode="r")
155 record = SfaRecord(string=str)
160 def unique_call_id(): return uuid.uuid4().urn
164 required_options=['verbose', 'debug', 'registry', 'sm', 'auth', 'user']
167 def default_sfi_dir ():
168 if os.path.isfile("./sfi_config"):
171 return os.path.expanduser("~/.sfi/")
173 # dummy to meet Sfi's expectations for its 'options' field
174 # i.e. s/t we can do setattr on
178 def __init__ (self,options=None):
179 if options is None: options=Sfi.DummyOptions()
180 for opt in Sfi.required_options:
181 if not hasattr(options,opt): setattr(options,opt,None)
182 if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
183 self.options = options
187 self.authority = None
188 self.hashrequest = False
189 self.logger = sfi_logger
190 self.logger.enable_console()
191 self.available_names = [ tuple[0] for tuple in Sfi.available ]
192 self.available_dict = dict (Sfi.available)
194 # tuples command-name expected-args in the order in which they should appear in the help
197 ("list", "authority"),
200 ("update", "record"),
203 ("resources", "[slice_hrn]"),
204 ("create", "slice_hrn rspec"),
205 ("delete", "slice_hrn"),
206 ("status", "slice_hrn"),
207 ("start", "slice_hrn"),
208 ("stop", "slice_hrn"),
209 ("reset", "slice_hrn"),
210 ("renew", "slice_hrn time"),
211 ("shutdown", "slice_hrn"),
212 ("get_ticket", "slice_hrn rspec"),
213 ("redeem_ticket", "ticket"),
214 ("delegate", "name"),
215 ("create_gid", "[name]"),
216 ("get_trusted_certs", "cred"),
219 def print_command_help (self, options):
220 verbose=getattr(options,'verbose')
221 format3="%18s %-15s %s"
224 print format3%("command","cmd_args","description")
228 self.create_parser().print_help()
229 for command in self.available_names:
230 args=self.available_dict[command]
231 method=getattr(self,command,None)
233 if method: doc=getattr(method,'__doc__',"")
234 if not doc: doc="*** no doc found ***"
235 doc=doc.strip(" \t\n")
236 doc=doc.replace("\n","\n"+35*' ')
239 print format3%(command,args,doc)
241 self.create_cmd_parser(command).print_help()
243 def create_cmd_parser(self, command):
244 if command not in self.available_dict:
245 msg="Invalid command\n"
247 msg += ','.join(self.available_names)
248 self.logger.critical(msg)
251 parser = OptionParser(usage="sfi [sfi_options] %s [cmd_options] %s" \
252 % (command, self.available_dict[command]))
254 # user specifies remote aggregate/sm/component
255 if command in ("resources", "slices", "create", "delete", "start", "stop",
256 "restart", "shutdown", "get_ticket", "renew", "status"):
257 parser.add_option("-a", "--aggregate", dest="aggregate",
258 default=None, help="aggregate host")
259 parser.add_option("-p", "--port", dest="port",
260 default=AGGREGATE_PORT, help="aggregate port")
261 parser.add_option("-c", "--component", dest="component", default=None,
262 help="component hrn")
263 parser.add_option("-d", "--delegate", dest="delegate", default=None,
265 help="Include a credential delegated to the user's root"+\
266 "authority in set of credentials for this call")
268 # registy filter option
269 if command in ("list", "show", "remove"):
270 parser.add_option("-t", "--type", dest="type", type="choice",
271 help="type filter ([all]|user|slice|authority|node|aggregate)",
272 choices=("all", "user", "slice", "authority", "node", "aggregate"),
275 if command in ("resources"):
276 parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
277 help="schema type and version of resulting RSpec")
278 parser.add_option("-f", "--format", dest="format", type="choice",
279 help="display format ([xml]|dns|ip)", default="xml",
280 choices=("xml", "dns", "ip"))
281 #panos: a new option to define the type of information about resources a user is interested in
282 parser.add_option("-i", "--info", dest="info",
283 help="optional component information", default=None)
286 # 'create' does return the new rspec, makes sense to save that too
287 if command in ("resources", "show", "list", "create_gid", 'create'):
288 parser.add_option("-o", "--output", dest="file",
289 help="output XML to file", metavar="FILE", default=None)
291 if command in ("show", "list"):
292 parser.add_option("-f", "--format", dest="format", type="choice",
293 help="display format ([text]|xml)", default="text",
294 choices=("text", "xml"))
296 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
297 help="output file format ([xml]|xmllist|hrnlist)", default="xml",
298 choices=("xml", "xmllist", "hrnlist"))
300 if command in ("status", "version"):
301 parser.add_option("-o", "--output", dest="file",
302 help="output dictionary to file", metavar="FILE", default=None)
303 parser.add_option("-F", "--fileformat", dest="fileformat", type="choice",
304 help="output file format ([text]|pickled)", default="text",
305 choices=("text","pickled"))
307 if command in ("delegate"):
308 parser.add_option("-u", "--user",
309 action="store_true", dest="delegate_user", default=False,
310 help="delegate user credential")
311 parser.add_option("-s", "--slice", dest="delegate_slice",
312 help="delegate slice credential", metavar="HRN", default=None)
314 if command in ("version"):
315 parser.add_option("-a", "--aggregate", dest="aggregate",
316 default=None, help="aggregate host")
317 parser.add_option("-p", "--port", dest="port",
318 default=AGGREGATE_PORT, help="aggregate port")
319 parser.add_option("-R","--registry-version",
320 action="store_true", dest="version_registry", default=False,
321 help="probe registry version instead of slicemgr")
322 parser.add_option("-l","--local",
323 action="store_true", dest="version_local", default=False,
324 help="display version of the local client")
329 def create_parser(self):
331 # Generate command line parser
332 parser = OptionParser(usage="sfi [sfi_options] command [cmd_options] [cmd_args]",
333 description="Commands: %s"%(" ".join(self.available_names)))
334 parser.add_option("-r", "--registry", dest="registry",
335 help="root registry", metavar="URL", default=None)
336 parser.add_option("-s", "--slicemgr", dest="sm",
337 help="slice manager", metavar="URL", default=None)
338 parser.add_option("-d", "--dir", dest="sfi_dir",
339 help="config & working directory - default is %default",
340 metavar="PATH", default=Sfi.default_sfi_dir())
341 parser.add_option("-u", "--user", dest="user",
342 help="user name", metavar="HRN", default=None)
343 parser.add_option("-a", "--auth", dest="auth",
344 help="authority name", metavar="HRN", default=None)
345 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
346 help="verbose mode - cumulative")
347 parser.add_option("-D", "--debug",
348 action="store_true", dest="debug", default=False,
349 help="Debug (xml-rpc) protocol messages")
350 parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
351 help="RPC protocol (xmlrpc or soap)")
352 parser.add_option("-k", "--hashrequest",
353 action="store_true", dest="hashrequest", default=False,
354 help="Create a hash of the request that will be authenticated on the server")
355 parser.add_option("-t", "--timeout", dest="timeout", default=None,
356 help="Amout of time to wait before timing out the request")
357 parser.add_option("-?", "--commands",
358 action="store_true", dest="command_help", default=False,
359 help="one page summary on commands & exit")
360 parser.disable_interspersed_args()
365 def print_help (self):
366 self.sfi_parser.print_help()
367 self.cmd_parser.print_help()
370 # Main: parse arguments and dispatch to command
372 def dispatch(self, command, cmd_opts, cmd_args):
373 return getattr(self, command)(cmd_opts, cmd_args)
376 self.sfi_parser = self.create_parser()
377 (options, args) = self.sfi_parser.parse_args()
378 if options.command_help:
379 self.print_command_help(options)
381 self.options = options
383 self.logger.setLevelFromOptVerbose(self.options.verbose)
384 if options.hashrequest:
385 self.hashrequest = True
388 self.logger.critical("No command given. Use -h for help.")
389 self.print_command_help(options)
393 self.cmd_parser = self.create_cmd_parser(command)
394 (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
397 self.logger.info("Command=%s" % command)
400 self.dispatch(command, cmd_opts, cmd_args)
402 self.logger.critical ("Unknown command %s"%command)
409 def read_config(self):
410 config_file = os.path.join(self.options.sfi_dir,"sfi_config")
412 config = Config (config_file)
414 self.logger.critical("Failed to read configuration file %s"%config_file)
415 self.logger.info("Make sure to remove the export clauses and to add quotes")
416 if self.options.verbose==0:
417 self.logger.info("Re-run with -v for more details")
419 self.logger.log_exc("Could not read config file %s"%config_file)
424 if (self.options.sm is not None):
425 self.sm_url = self.options.sm
426 elif hasattr(config, "SFI_SM"):
427 self.sm_url = config.SFI_SM
429 self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
433 if (self.options.registry is not None):
434 self.reg_url = self.options.registry
435 elif hasattr(config, "SFI_REGISTRY"):
436 self.reg_url = config.SFI_REGISTRY
438 self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
443 if (self.options.user is not None):
444 self.user = self.options.user
445 elif hasattr(config, "SFI_USER"):
446 self.user = config.SFI_USER
448 self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
452 if (self.options.auth is not None):
453 self.authority = self.options.auth
454 elif hasattr(config, "SFI_AUTH"):
455 self.authority = config.SFI_AUTH
457 self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
465 # Establish Connection to SliceMgr and Registry Servers
467 def set_servers(self):
470 # Get key and certificate
471 key_file = self.get_key_file()
472 cert_file = self.get_cert_file(key_file)
473 self.key_file = key_file
474 self.cert_file = cert_file
475 self.cert = GID(filename=cert_file)
476 self.logger.info("Contacting Registry at: %s"%self.reg_url)
477 self.registry = SfaServerProxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
478 self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
479 self.slicemgr = SfaServerProxy(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
482 def get_cached_server_version(self, server):
483 # check local cache first
486 cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat')
487 cache_key = server.url + "-version"
489 cache = Cache(cache_file)
492 self.logger.info("Local cache not found at: %s" % cache_file)
495 version = cache.get(cache_key)
498 result = server.GetVersion()
499 version= ReturnValue.get_value(result)
500 # cache version for 24 hours
501 cache.add(cache_key, version, ttl= 60*60*24)
502 self.logger.info("Updating cache file %s" % cache_file)
503 cache.save_to_file(cache_file)
509 # Get various credential and spec files
511 # Establishes limiting conventions
512 # - conflates MAs and SAs
513 # - assumes last token in slice name is unique
515 # Bootstraps credentials
516 # - bootstrap user credential from self-signed certificate
517 # - bootstrap authority credential from user credential
518 # - bootstrap slice credential from user credential
522 def get_key_file(self):
523 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
524 if (os.path.isfile(file)):
527 self.logger.error("Key file %s does not exist"%file)
531 def get_cert_file(self, key_file):
533 cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
534 if (os.path.isfile(cert_file)):
535 # we'd perfer to use Registry issued certs instead of self signed certs.
536 # if this is a Registry cert (GID) then we are done
537 gid = GID(filename=cert_file)
541 # generate self signed certificate
542 k = Keypair(filename=key_file)
543 cert = Certificate(subject=self.user)
545 cert.set_issuer(k, self.user)
547 self.logger.info("Writing self-signed certificate to %s"%cert_file)
548 cert.save_to_file(cert_file)
550 # try to get registry issued cert
552 self.logger.info("Getting Registry issued cert")
554 # *hack. need to set registry before _get_gid() is called
555 self.registry = SfaServerProxy(self.reg_url, key_file, cert_file,
556 timeout=self.options.timeout, verbose=self.options.debug)
557 gid = self._get_gid(type='user')
559 self.logger.info("Writing certificate to %s"%cert_file)
560 gid.save_to_file(cert_file)
562 self.logger.info("Failed to download Registry issued cert")
566 def get_cached_gid(self, file):
571 if (os.path.isfile(file)):
572 gid = GID(filename=file)
577 # def get_gid(self, opts, args):
578 # """ Get the specify gid and save it to file """
582 # gid = self._get_gid(hrn)
583 # self.logger.debug("Sfi.get_gid-> %s" % gid.save_to_string(save_parents=True))
586 def _get_gid(self, hrn=None, type=None):
588 git_gid helper. Retrive the gid from the registry and save it to file.
594 gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
595 gid = self.get_cached_gid(gidfile)
597 user_cred = self.get_user_cred()
598 records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
600 raise RecordNotFound(args[0])
605 if type == rec['type']:
608 raise RecordNotFound(args[0])
610 gid = GID(string=record['gid'])
611 self.logger.info("Writing gid to %s"%gidfile)
612 gid.save_to_file(filename=gidfile)
616 def get_cached_credential(self, file):
618 Return a cached credential only if it hasn't expired.
620 if (os.path.isfile(file)):
621 credential = Credential(filename=file)
622 # make sure it isnt expired
623 if not credential.get_expiration or \
624 datetime.datetime.today() < credential.get_expiration():
628 def get_user_cred(self):
629 file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
630 return self.get_cred(file, 'user', self.user)
632 def get_auth_cred(self):
633 if not self.authority:
634 self.logger.critical("no authority specified. Use -a or set SF_AUTH")
636 file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
637 return self.get_cred(file, 'authority', self.authority)
639 def get_slice_cred(self, name):
640 file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
641 return self.get_cred(file, 'slice', name)
643 def get_cred(self, file, type, hrn):
644 # attempt to load a cached credential
645 cred = self.get_cached_credential(file)
648 cert_string = self.cert.save_to_string(save_parents=True)
649 user_name = self.user.replace(self.authority + ".", '')
650 if user_name.count(".") > 0:
651 user_name = user_name.replace(".", '_')
652 self.user = self.authority + "." + user_name
653 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
655 # bootstrap slice credential from user credential
656 user_cred = self.get_user_cred().save_to_string(save_parents=True)
657 cred_str = self.registry.GetCredential(user_cred, hrn, type)
660 self.logger.critical("Failed to get %s credential" % type)
663 cred = Credential(string=cred_str)
664 cred.save_to_file(file, save_parents=True)
665 self.logger.info("Writing %s credential to %s" %(type, file))
670 def delegate_cred(self, object_cred, hrn):
671 # the gid and hrn of the object we are delegating
672 if isinstance(object_cred, str):
673 object_cred = Credential(string=object_cred)
674 object_gid = object_cred.get_gid_object()
675 object_hrn = object_gid.get_hrn()
677 if not object_cred.get_privileges().get_all_delegate():
678 self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
681 # the delegating user's gid
682 caller_gid = self._get_gid(self.user)
683 caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
685 # the gid of the user who will be delegated to
686 delegee_gid = self._get_gid(hrn)
687 delegee_hrn = delegee_gid.get_hrn()
688 delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
689 delegee_gid.save_to_file(filename=delegee_gidfile)
690 dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
691 return dcred.save_to_string(save_parents=True)
693 ######################################## miscell utilities
694 def get_rspec_file(self, rspec):
695 if (os.path.isabs(rspec)):
698 file = os.path.join(self.options.sfi_dir, rspec)
699 if (os.path.isfile(file)):
702 self.logger.critical("No such rspec file %s"%rspec)
705 def get_record_file(self, record):
706 if (os.path.isabs(record)):
709 file = os.path.join(self.options.sfi_dir, record)
710 if (os.path.isfile(file)):
713 self.logger.critical("No such registry record file %s"%record)
717 def get_component_proxy_from_hrn(self, hrn):
718 # direct connection to the nodes component manager interface
719 user_cred = self.get_user_cred().save_to_string(save_parents=True)
720 records = self.registry.Resolve(hrn, user_cred)
721 records = filter_records('node', records)
723 self.logger.warning("No such component:%r"% opts.component)
726 return self.server_proxy(record['hostname'], CM_PORT, self.key_file, self.cert_file)
728 def server_proxy(self, host, port, keyfile, certfile):
730 Return an instance of an xmlrpc server connection
732 # port is appended onto the domain, before the path. Should look like:
733 # http://domain:port/path
734 host_parts = host.split('/')
735 host_parts[0] = host_parts[0] + ":" + str(port)
736 url = "http://%s" % "/".join(host_parts)
737 return SfaServerProxy(url, keyfile, certfile, timeout=self.options.timeout,
738 verbose=self.options.debug)
740 # xxx opts could be retrieved in self.options
741 def server_proxy_from_opts(self, opts):
743 Return instance of an xmlrpc connection to a slice manager, aggregate
744 or component server depending on the specified opts
746 server = self.slicemgr
747 # direct connection to an aggregate
748 if hasattr(opts, 'aggregate') and opts.aggregate:
749 server = self.server_proxy(opts.aggregate, opts.port, self.key_file, self.cert_file)
750 # direct connection to the nodes component manager interface
751 if hasattr(opts, 'component') and opts.component:
752 server = self.get_component_proxy_from_hrn(opts.component)
755 #==========================================================================
756 # Following functions implement the commands
758 # Registry-related commands
759 #==========================================================================
761 def version(self, opts, args):
763 display an SFA server version (GetVersion)
764 or version information about sfi itself
766 if opts.version_local:
767 version=version_core()
769 if opts.version_registry:
772 server = self.server_proxy_from_opts(opts)
773 result = server.GetVersion()
774 version = ReturnValue.get_value(result)
775 for (k,v) in version.iteritems():
776 print "%-20s: %s"%(k,v)
778 save_variable_to_file(version, opts.file, opts.fileformat)
780 def list(self, opts, args):
782 list entries in named authority registry (List)
788 user_cred = self.get_user_cred().save_to_string(save_parents=True)
790 list = self.registry.List(hrn, user_cred)
792 raise Exception, "Not enough parameters for the 'list' command"
794 # filter on person, slice, site, node, etc.
795 # THis really should be in the self.filter_records funct def comment...
796 list = filter_records(opts.type, list)
798 print "%s (%s)" % (record['hrn'], record['type'])
800 save_records_to_file(opts.file, list, opts.fileformat)
803 def show(self, opts, args):
805 show details about named registry record (Resolve)
811 user_cred = self.get_user_cred().save_to_string(save_parents=True)
812 records = self.registry.Resolve(hrn, user_cred)
813 records = filter_records(opts.type, records)
815 self.logger.error("No record of type %s"% opts.type)
816 for record in records:
817 if record['type'] in ['user']:
818 record = UserRecord(dict=record)
819 elif record['type'] in ['slice']:
820 record = SliceRecord(dict=record)
821 elif record['type'] in ['node']:
822 record = NodeRecord(dict=record)
823 elif record['type'].startswith('authority'):
824 record = AuthorityRecord(dict=record)
826 record = SfaRecord(dict=record)
827 if (opts.format == "text"):
830 print record.save_to_string()
832 save_records_to_file(opts.file, records, opts.fileformat)
835 def add(self, opts, args):
836 "add record into registry from xml file (Register)"
837 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
841 record_filepath = args[0]
842 rec_file = self.get_record_file(record_filepath)
843 record = load_record_from_file(rec_file).as_dict()
844 return self.registry.Register(record, auth_cred)
846 def update(self, opts, args):
847 "update record into registry from xml file (Update)"
848 user_cred = self.get_user_cred()
852 rec_file = self.get_record_file(args[0])
853 record = load_record_from_file(rec_file)
854 if record['type'] == "user":
855 if record.get_name() == user_cred.get_gid_object().get_hrn():
856 cred = user_cred.save_to_string(save_parents=True)
858 cred = self.get_auth_cred().save_to_string(save_parents=True)
859 elif record['type'] in ["slice"]:
861 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
862 except ServerException, e:
863 # XXX smbaker -- once we have better error return codes, update this
864 # to do something better than a string compare
865 if "Permission error" in e.args[0]:
866 cred = self.get_auth_cred().save_to_string(save_parents=True)
869 elif record.get_type() in ["authority"]:
870 cred = self.get_auth_cred().save_to_string(save_parents=True)
871 elif record.get_type() == 'node':
872 cred = self.get_auth_cred().save_to_string(save_parents=True)
874 raise "unknown record type" + record.get_type()
875 record = record.as_dict()
876 return self.registry.Update(record, cred)
878 def remove(self, opts, args):
879 "remove registry record by name (Remove)"
880 auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
888 return self.registry.Remove(hrn, auth_cred, type)
890 # ==================================================================
891 # Slice-related commands
892 # ==================================================================
894 def slices(self, opts, args):
895 "list instantiated slices (ListSlices) - returns urn's"
896 user_cred = self.get_user_cred().save_to_string(save_parents=True)
899 delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
900 creds.append(delegated_cred)
901 server = self.server_proxy_from_opts(opts)
903 api_options ['call_id'] = unique_call_id()
904 result = server.ListSlices(creds, api_options)
905 value = ReturnValue.get_value(result)
909 # show rspec for named slice
910 def resources(self, opts, args):
912 with no arg, discover available resources,
913 or currently provisioned resources (ListResources)
915 user_cred = self.get_user_cred().save_to_string(save_parents=True)
916 server = self.server_proxy_from_opts(opts)
919 api_options ['call_id'] = unique_call_id()
920 #panos add info api_options
922 api_options['info'] = opts.info
925 cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
927 api_options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
933 delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
934 creds.append(delegated_cred)
935 if opts.rspec_version:
936 version_manager = VersionManager()
937 server_version = self.get_cached_server_version(server)
938 if 'sfa' in server_version:
939 # just request the version the client wants
940 api_options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
942 # this must be a protogeni aggregate. We should request a v2 ad rspec
943 # regardless of what the client user requested
944 api_options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()
946 api_options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
948 result = server.ListResources(creds, api_options)
949 value = ReturnValue.get_value(result)
950 if opts.file is None:
951 display_rspec(value, opts.format)
953 save_rspec_to_file(value, opts.file)
956 def create(self, opts, args):
958 create or update named slice with given rspec
960 server = self.server_proxy_from_opts(opts)
961 server_version = self.get_cached_server_version(server)
963 slice_urn = hrn_to_urn(slice_hrn, 'slice')
964 user_cred = self.get_user_cred()
965 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
966 delegated_cred = None
967 if server_version.get('interface') == 'slicemgr':
968 # delegate our cred to the slice manager
969 # do not delegate cred to slicemgr...not working at the moment
971 #if server_version.get('hrn'):
972 # delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
973 #elif server_version.get('urn'):
974 # delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
976 rspec_file = self.get_rspec_file(args[1])
977 rspec = open(rspec_file).read()
979 # need to pass along user keys to the aggregate.
981 # { urn: urn:publicid:IDN+emulab.net+user+alice
982 # keys: [<ssh key A>, <ssh key B>]
985 slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)])
986 if slice_records and 'researcher' in slice_records[0] and slice_records[0]['researcher']!=[]:
987 slice_record = slice_records[0]
988 user_hrns = slice_record['researcher']
989 user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns]
990 user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)])
992 if 'sfa' not in server_version:
993 users = pg_users_arg(user_records)
995 rspec.filter({'component_manager_id': server_version['urn']})
996 rspec = RSpecConverter.to_pg_rspec(rspec.toxml(), content_type='request')
999 users = sfa_users_arg(user_records, slice_record)
1000 creds = [slice_cred]
1002 creds.append(delegated_cred)
1003 # do not append users, keys, or slice tags. Anything
1004 # not contained in this request will be removed from the slice
1006 api_options ['append'] = False
1007 api_options ['call_id'] = unique_call_id()
1008 result = server.CreateSliver(slice_urn, creds, rspec, users, api_options)
1009 value = ReturnValue.get_value(result)
1010 if opts.file is None:
1013 save_rspec_to_file (value, opts.file)
1016 def delete(self, opts, args):
1018 delete named slice (DeleteSliver)
1021 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1022 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1023 creds = [slice_cred]
1025 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1026 creds.append(delegated_cred)
1027 server = self.server_proxy_from_opts(opts)
1029 api_options ['call_id'] = unique_call_id()
1030 return server.DeleteSliver(slice_urn, creds, api_options)
1032 def status(self, opts, args):
1034 retrieve slice status (SliverStatus)
1037 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1038 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1039 creds = [slice_cred]
1041 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1042 creds.append(delegated_cred)
1043 server = self.server_proxy_from_opts(opts)
1045 api_options ['call_id'] = unique_call_id()
1046 result = server.SliverStatus(slice_urn, creds, api_options)
1047 value = ReturnValue.get_value(result)
1050 save_variable_to_file(value, opts.file, opts.fileformat)
1052 def start(self, opts, args):
1054 start named slice (Start)
1057 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1058 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1059 creds = [slice_cred]
1061 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1062 creds.append(delegated_cred)
1063 server = self.server_proxy_from_opts(opts)
1064 return server.Start(slice_urn, creds)
1066 def stop(self, opts, args):
1068 stop named slice (Stop)
1071 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1072 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1073 creds = [slice_cred]
1075 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1076 creds.append(delegated_cred)
1077 server = self.server_proxy_from_opts(opts)
1078 return server.Stop(slice_urn, creds)
1081 def reset(self, opts, args):
1083 reset named slice (reset_slice)
1086 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1087 server = self.server_proxy_from_opts(opts)
1088 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1089 creds = [slice_cred]
1091 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1092 creds.append(delegated_cred)
1093 return server.reset_slice(creds, slice_urn)
1095 def renew(self, opts, args):
1097 renew slice (RenewSliver)
1100 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1101 server = self.server_proxy_from_opts(opts)
1102 slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
1103 creds = [slice_cred]
1105 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1106 creds.append(delegated_cred)
1109 api_options ['call_id'] = unique_call_id()
1110 result = server.RenewSliver(slice_urn, creds, time, api_options)
1111 value = ReturnValue.get_value(result)
1115 def shutdown(self, opts, args):
1117 shutdown named slice (Shutdown)
1120 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1121 slice_cred = self.get_slice_cred(slice_hrn).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.Shutdown(slice_urn, creds)
1130 def get_ticket(self, opts, args):
1132 get a ticket for the specified slice
1134 slice_hrn, rspec_path = args[0], args[1]
1135 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1136 user_cred = self.get_user_cred()
1137 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1138 creds = [slice_cred]
1140 delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
1141 creds.append(delegated_cred)
1142 rspec_file = self.get_rspec_file(rspec_path)
1143 rspec = open(rspec_file).read()
1144 server = self.server_proxy_from_opts(opts)
1145 ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
1146 file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
1147 self.logger.info("writing ticket to %s"%file)
1148 ticket = SfaTicket(string=ticket_string)
1149 ticket.save_to_file(filename=file, save_parents=True)
1151 def redeem_ticket(self, opts, args):
1153 Connects to nodes in a slice and redeems a ticket
1154 (slice hrn is retrieved from the ticket)
1156 ticket_file = args[0]
1158 # get slice hrn from the ticket
1159 # use this to get the right slice credential
1160 ticket = SfaTicket(filename=ticket_file)
1162 slice_hrn = ticket.gidObject.get_hrn()
1163 slice_urn = hrn_to_urn(slice_hrn, 'slice')
1164 #slice_hrn = ticket.attributes['slivers'][0]['hrn']
1165 user_cred = self.get_user_cred()
1166 slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
1168 # get a list of node hostnames from the RSpec
1169 tree = etree.parse(StringIO(ticket.rspec))
1170 root = tree.getroot()
1171 hostnames = root.xpath("./network/site/node/hostname/text()")
1173 # create an xmlrpc connection to the component manager at each of these
1174 # components and gall redeem_ticket
1176 for hostname in hostnames:
1178 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
1179 server = self.server_proxy(hostname, CM_PORT, self.key_file, \
1180 self.cert_file, self.options.debug)
1181 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
1182 self.logger.info("Success")
1183 except socket.gaierror:
1184 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
1185 except Exception, e:
1186 self.logger.log_exc(e.message)
1189 def create_gid(self, opts, args):
1191 Create a GID (CreateGid)
1196 target_hrn = args[0]
1197 user_cred = self.get_user_cred().save_to_string(save_parents=True)
1198 gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string())
1200 filename = opts.file
1202 filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
1203 self.logger.info("writing %s gid to %s" % (target_hrn, filename))
1204 GID(string=gid).save_to_file(filename)
1207 def delegate(self, opts, args):
1209 (locally) create delegate credential for use by given hrn
1211 delegee_hrn = args[0]
1212 if opts.delegate_user:
1213 user_cred = self.get_user_cred()
1214 cred = self.delegate_cred(user_cred, delegee_hrn)
1215 elif opts.delegate_slice:
1216 slice_cred = self.get_slice_cred(opts.delegate_slice)
1217 cred = self.delegate_cred(slice_cred, delegee_hrn)
1219 self.logger.warning("Must specify either --user or --slice <hrn>")
1221 delegated_cred = Credential(string=cred)
1222 object_hrn = delegated_cred.get_gid_object().get_hrn()
1223 if opts.delegate_user:
1224 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
1225 + get_leaf(object_hrn) + ".cred")
1226 elif opts.delegate_slice:
1227 dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
1228 + get_leaf(object_hrn) + ".cred")
1230 delegated_cred.save_to_file(dest_fn, save_parents=True)
1232 self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
1234 def get_trusted_certs(self, opts, args):
1236 return uhe trusted certs at this interface (get_trusted_certs)
1238 trusted_certs = self.registry.get_trusted_certs()
1239 for trusted_cert in trusted_certs:
1240 gid = GID(string=trusted_cert)
1242 cert = Certificate(string=trusted_cert)
1243 self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())