X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Fclient%2Fsfi.py;h=293e5bdd7de5d3f5acd1e04137939181526620bc;hb=f6f1bf872dd9700136c54d5b14fc3e3aa9b18b7d;hp=bc6cc60f106bda1fe138581d20149b7ae295817b;hpb=5f4f788db1dd59e8a5968e9118ff1feda89389dc;p=sfa.git diff --git a/sfa/client/sfi.py b/sfa/client/sfi.py index bc6cc60f..293e5bdd 100755 --- a/sfa/client/sfi.py +++ b/sfa/client/sfi.py @@ -10,13 +10,14 @@ import traceback import socket import random import datetime +import zlib +import codecs from lxml import etree from StringIO import StringIO from types import StringTypes, ListType from optparse import OptionParser -import zlib -from sfa.util.sfalogging import sfa_logger,sfa_logger_goes_to_console +from sfa.util.sfalogging import sfi_logger from sfa.trust.certificate import Keypair, Certificate from sfa.trust.gid import GID from sfa.trust.credential import Credential @@ -26,6 +27,9 @@ from sfa.util.xrn import Xrn, get_leaf, get_authority, hrn_to_urn import sfa.util.xmlrpcprotocol as xmlrpcprotocol from sfa.util.config import Config from sfa.util.version import version_core +from sfa.util.cache import Cache +from sfa.rspecs.rspec_version import RSpecVersion +from sfa.rspecs.pg_rspec import pg_rspec_request_version AGGREGATE_PORT=12346 CM_PORT=12346 @@ -106,13 +110,17 @@ def save_record_to_file(filename, record): else: record = SfaRecord(dict=record) str = record.save_to_string() - file(filename, "w").write(str) + f=codecs.open(filename, encoding='utf-8',mode="w") + f.write(str) + f.close() return # load methods def load_record_from_file(filename): - str = file(filename, "r").read() + f=codecs.open(filename, encoding="utf-8", mode="r") + str = f.read() + f.close() record = SfaRecord(string=str) return record @@ -134,14 +142,17 @@ class Sfi: for opt in Sfi.required_options: if not hasattr(options,opt): setattr(options,opt,None) if not hasattr(options,'sfi_dir'): options.sfi_dir=os.path.expanduser("~/.sfi/") + # xxx oops, this is dangerous, sounds like ww sometimes have discrepency + # would be safer to remove self.sfi_dir altogether + self.sfi_dir = options.sfi_dir self.options = options self.slicemgr = None self.registry = None self.user = None self.authority = None self.hashrequest = False - sfa_logger_goes_to_console() - self.logger=sfa_logger() + self.logger = sfi_logger + self.logger.enable_console() def create_cmd_parser(self, command, additional_cmdargs=None): cmdargs = {"list": "authority", @@ -151,6 +162,7 @@ class Sfi: "update": "record", "aggregates": "[name]", "registries": "[name]", + "create_gid": "[name]", "get_gid": [], "get_trusted_certs": "cred", "slices": "", @@ -204,11 +216,17 @@ class Sfi: default="all") # display formats if command in ("resources"): + parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1", + help="schema type and version of resulting RSpec") parser.add_option("-f", "--format", dest="format", type="choice", help="display format ([xml]|dns|ip)", default="xml", choices=("xml", "dns", "ip")) + #panos: a new option to define the type of information about resources a user is interested in + parser.add_option("-i", "--info", dest="info", + help="optional component information", default=None) - if command in ("resources", "show", "list"): + + if command in ("resources", "show", "list", "create_gid"): parser.add_option("-o", "--output", dest="file", help="output XML to file", metavar="FILE", default=None) @@ -225,6 +243,10 @@ class Sfi: help="delegate slice credential", metavar="HRN", default=None) if command in ("version"): + parser.add_option("-a", "--aggregate", dest="aggregate", + default=None, help="aggregate host") + parser.add_option("-p", "--port", dest="port", + default=AGGREGATE_PORT, help="aggregate port") parser.add_option("-R","--registry-version", action="store_true", dest="version_registry", default=False, help="probe registry version instead of slicemgr") @@ -262,13 +284,15 @@ class Sfi: parser.add_option("-k", "--hashrequest", action="store_true", dest="hashrequest", default=False, help="Create a hash of the request that will be authenticated on the server") + parser.add_option("-t", "--timeout", dest="timeout", default=None, + help="Amout of time tom wait before timing out the request") parser.disable_interspersed_args() return parser - + def read_config(self): - config_file = self.options.sfi_dir + os.sep + "sfi_config" + config_file = os.path.join(self.options.sfi_dir,"sfi_config") try: config = Config (config_file) except: @@ -334,15 +358,56 @@ class Sfi: self.key = Keypair(filename=key_file) self.key_file = key_file self.cert_file = cert_file - self.cert = Certificate(filename=cert_file) - # Establish connection to server(s) + self.cert = GID(filename=cert_file) self.logger.info("Contacting Registry at: %s"%self.reg_url) - self.registry = xmlrpcprotocol.get_server(self.reg_url, key_file, cert_file, self.options) + self.registry = xmlrpcprotocol.get_server(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug) self.logger.info("Contacting Slice Manager at: %s"%self.sm_url) - self.slicemgr = xmlrpcprotocol.get_server(self.sm_url, key_file, cert_file, self.options) - + self.slicemgr = xmlrpcprotocol.get_server(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug) return - + + def get_cached_server_version(self, server): + # check local cache first + cache = None + version = None + cache_file = os.path.join(self.options.sfi_dir,'sfi_cache.dat') + cache_key = server.url + "-version" + try: + cache = Cache(cache_file) + except IOError: + cache = Cache() + self.logger.info("Local cache not found at: %s" % cache_file) + + if cache: + version = cache.get(cache_key) + + if not version: + version = server.GetVersion() + # cache version for 24 hours + cache.add(cache_key, version, ttl= 60*60*24) + self.logger.info("Updating cache file %s" % cache_file) + cache.save_to_file(cache_file) + + + return version + + + def server_supports_call_id_arg(self, server): + """ + Returns true if server support the optional call_id arg, false otherwise. + """ + server_version = self.get_cached_server_version(server) + if 'sfa' in server_version: + code_tag = server_version['code_tag'] + code_tag_parts = code_tag.split("-") + + version_parts = code_tag_parts[0].split(".") + major, minor = version_parts[0], version_parts[1] + rev = code_tag_parts[1] + if int(major) > 1: + if int(minor) > 0 or int(rev) > 20: + return True + return False + # # Get various credential and spec files # @@ -368,27 +433,37 @@ class Sfi: def get_cert_file(self, key_file): - file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert") - if (os.path.isfile(file)): - # use existing cert if it exists - return file - else: - try: - # attempt to use gid as the cert. - gid = self._get_gid() - self.logger.info("Writing certificate to %s"%file) - gid.save_to_file(file) - except: - # generate self signed certificate - k = Keypair(filename=key_file) - cert = Certificate(subject=self.user) - cert.set_pubkey(k) - cert.set_issuer(k, self.user) - cert.sign() - self.logger.info("Writing self-signed certificate to %s"%file) - cert.save_to_file(file) - - return file + cert_file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert") + if (os.path.isfile(cert_file)): + # we'd perfer to use Registry issued certs instead of self signed certs. + # if this is a Registry cert (GID) then we are done + gid = GID(filename=cert_file) + if gid.get_urn(): + return cert_file + + # generate self signed certificate + k = Keypair(filename=key_file) + cert = Certificate(subject=self.user) + cert.set_pubkey(k) + cert.set_issuer(k, self.user) + cert.sign() + self.logger.info("Writing self-signed certificate to %s"%cert_file) + cert.save_to_file(cert_file) + self.cert = cert + # try to get registry issued cert + try: + self.logger.info("Getting Registry issued cert") + self.read_config() + # *hack. need to set registyr before _get_gid() is called + self.registry = xmlrpcprotocol.get_server(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug) + gid = self._get_gid(type='user') + self.registry = None + self.logger.info("Writing certificate to %s"%cert_file) + gid.save_to_file(cert_file) + except: + self.logger.info("Failed to download Registry issued cert") + + return cert_file def get_cached_gid(self, file): """ @@ -411,7 +486,7 @@ class Sfi: self.logger.debug("Sfi.get_gid-> %s",gid.save_to_string(save_parents=True)) return gid - def _get_gid(self, hrn=None): + def _get_gid(self, hrn=None, type=None): """ git_gid helper. Retrive the gid from the registry and save it to file. """ @@ -420,13 +495,23 @@ class Sfi: hrn = self.user gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid") + print gidfile gid = self.get_cached_gid(gidfile) if not gid: user_cred = self.get_user_cred() records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True)) if not records: raise RecordNotFound(args[0]) - gid = GID(string=records[0]['gid']) + record = records[0] + if type: + record=None + for rec in records: + if type == rec['type']: + record = rec + if not record: + raise RecordNotFound(args[0]) + + gid = GID(string=record['gid']) self.logger.info("Writing gid to %s"%gidfile) gid.save_to_file(filename=gidfile) return gid @@ -494,7 +579,7 @@ class Sfi: if (os.path.isfile(file)): return file else: - self.logger.critical("No such rspec file"%rspec) + self.logger.critical("No such rspec file %s"%rspec) sys.exit(1) def get_record_file(self, record): @@ -544,7 +629,7 @@ class Sfi: host_parts = host.split('/') host_parts[0] = host_parts[0] + ":" + str(port) url = "http://%s" % "/".join(host_parts) - return xmlrpcprotocol.get_server(url, keyfile, certfile, self.options) + return xmlrpcprotocol.get_server(url, keyfile, certfile, timeout=self.options.timeout, verbose=self.options.debug) # xxx opts could be retrieved in self.options def get_server_from_opts(self, opts): @@ -569,7 +654,22 @@ class Sfi: def dispatch(self, command, cmd_opts, cmd_args): return getattr(self, command)(cmd_opts, cmd_args) - + + def create_gid(self, opts, args): + if len(args) < 1: + self.print_help() + sys.exit(1) + target_hrn = args[0] + user_cred = self.get_user_cred().save_to_string(save_parents=True) + gid = self.registry.CreateGid(user_cred, target_hrn, self.cert.save_to_string()) + if opts.file: + filename = opts.file + else: + filename = os.sep.join([self.sfi_dir, '%s.gid' % target_hrn]) + self.logger.info("writing %s gid to %s" % (target_hrn, filename)) + GID(string=gid).save_to_file(filename) + + # list entires in named authority registry def list(self, opts, args): if len(args)!= 1: @@ -602,6 +702,7 @@ class Sfi: hrn = args[0] user_cred = self.get_user_cred().save_to_string(save_parents=True) records = self.registry.Resolve(hrn, user_cred) + print records records = filter_records(opts.type, records) if not records: print "No record of type", opts.type @@ -612,7 +713,7 @@ class Sfi: record = SliceRecord(dict=record) elif record['type'] in ['node']: record = NodeRecord(dict=record) - elif record['type'] in ['authority', 'ma', 'sa']: + elif record['type'].startswith('authority'): record = AuthorityRecord(dict=record) else: record = SfaRecord(dict=record) @@ -620,7 +721,6 @@ class Sfi: record.dump() else: print record.save_to_string() - if opts.file: file = opts.file if not file.startswith(os.sep): @@ -738,6 +838,8 @@ class Sfi: """ trusted_certs = self.registry.get_trusted_certs() for trusted_cert in trusted_certs: + gid = GID(string=trusted_cert) + gid.dump() cert = Certificate(string=trusted_cert) self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject()) return @@ -771,7 +873,6 @@ class Sfi: # ================================================================== # Slice-related commands # ================================================================== - def version(self, opts, args): if opts.version_local: @@ -796,7 +897,8 @@ class Sfi: delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority)) creds.append(delegated_cred) server = self.get_server_from_opts(opts) - results = server.ListSlices(creds, unique_call_id()) + #results = server.ListSlices(creds, unique_call_id()) + results = server.ListSlices(creds) display_list(results) return @@ -810,7 +912,7 @@ class Sfi: if args: cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True) hrn = args[0] - call_options = {'geni_slice_urn': hrn_to_urn(hrn, 'slice')} + call_options = {'geni_slice_urn': hrn_to_urn(hrn, 'slice')} else: cred = user_cred hrn = None @@ -818,8 +920,24 @@ class Sfi: creds = [cred] if opts.delegate: delegated_cred = self.delegate_cred(cred, get_authority(self.authority)) - creds.append(delegated_cred) - result = server.ListResources(creds, call_options,unique_call_id()) + creds.append(delegated_cred) + if opts.rspec_version: + server_version = self.get_cached_server_version(server) + if 'sfa' in server_version: + # just request the version the client wants + call_options['rspec_version'] = dict(RSpecVersion(opts.rspec_version)) + else: + # this must be a protogeni aggregate. We should request a v2 ad rspec + # regardless of what the client user requested + call_options['rspec_version'] = dict(pg_rspec_request_version) + #panos add info options + if opts.info: + call_options['info'] = opts.info + + call_args = [creds, call_options] + if self.server_supports_call_id_arg(server): + call_args.append(unique_call_id()) + result = server.ListResources(*call_args) format = opts.format if opts.file is None: display_rspec(result, format) @@ -832,6 +950,8 @@ class Sfi: # created named slice with given rspec def create(self, opts, args): + server = self.get_server_from_opts(opts) + server_version = self.get_cached_server_version(server) slice_hrn = args[0] slice_urn = hrn_to_urn(slice_hrn, 'slice') user_cred = self.get_user_cred() @@ -842,8 +962,49 @@ class Sfi: creds.append(delegated_cred) rspec_file = self.get_rspec_file(args[1]) rspec = open(rspec_file).read() - server = self.get_server_from_opts(opts) - result = server.CreateSliver(slice_urn, creds, rspec, [], unique_call_id()) + + # need to pass along user keys to the aggregate. + # users = [ + # { urn: urn:publicid:IDN+emulab.net+user+alice + # keys: [, ] + # }] + users = [] + all_keys = [] + all_key_ids = [] + slice_records = self.registry.Resolve(slice_urn, [user_cred.save_to_string(save_parents=True)]) + if slice_records and 'researcher' in slice_records[0]: + slice_record = slice_records[0] + user_hrns = slice_record['researcher'] + user_urns = [hrn_to_urn(hrn, 'user') for hrn in user_hrns] + user_records = self.registry.Resolve(user_urns, [user_cred.save_to_string(save_parents=True)]) + for user_record in user_records: + if user_record['type'] != 'user': + continue + #user = {'urn': user_cred.get_gid_caller().get_urn(),'keys': []} + user = {'urn': user_cred.get_gid_caller().get_urn(), # + 'keys': user_record['keys'], + 'email': user_record['email'], # needed for MyPLC + 'person_id': user_record['person_id'], # needed for MyPLC + 'first_name': user_record['first_name'], # needed for MyPLC + 'last_name': user_record['last_name'], # needed for MyPLC + 'slice_record': slice_record, # needed for legacy refresh peer + 'key_ids': user_record['key_ids'] # needed for legacy refresh peer + } + users.append(user) + all_keys.extend(user_record['keys']) + all_key_ids.extend(user_record['key_ids']) + # ProtoGeni Aggregates will only install the keys of the user that is issuing the + # request. So we will add all to the current caller's list of keys + if 'sfa' not in server_version: + for user in users: + if user['urn'] == user_cred.get_gid_caller().get_urn(): + user['keys'] = all_keys + + call_args = [slice_urn, creds, rspec, users] + if self.server_supports_call_id_arg(server): + call_args.append(unique_call_id()) + + result = server.CreateSliver(*call_args) print result return result @@ -910,8 +1071,12 @@ class Sfi: delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) creds.append(delegated_cred) server = self.get_server_from_opts(opts) - return server.DeleteSliver(slice_urn, creds, unique_call_id()) - + + call_args = [slice_urn, creds] + if self.server_supports_call_id_arg(server): + call_args.append(unique_call_id()) + return server.DeleteSliver(*call_args) + # start named slice def start(self, opts, args): slice_hrn = args[0] @@ -958,7 +1123,11 @@ class Sfi: delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) creds.append(delegated_cred) time = args[1] - return server.RenewSliver(slice_urn, creds, time, unique_call_id()) + + call_args = [slice_urn, creds, time] + if self.server_supports_call_id_arg(server): + call_args.append(unique_call_id()) + return server.RenewSliver(*call_args) def status(self, opts, args): @@ -970,7 +1139,10 @@ class Sfi: delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)) creds.append(delegated_cred) server = self.get_server_from_opts(opts) - print server.SliverStatus(slice_urn, creds, unique_call_id()) + call_args = [slice_urn, creds] + if self.server_supports_call_id_arg(server): + call_args.append(unique_call_id()) + print server.SliverStatus(*call_args) def shutdown(self, opts, args): @@ -1009,18 +1181,18 @@ class Sfi: (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:]) self.set_servers() - self.logger.info("Command=%s" % command) if command in ("resources"): self.logger.debug("resources cmd_opts %s" % cmd_opts.format) elif command in ("list", "show", "remove"): self.logger.debug("cmd_opts.type %s" % cmd_opts.type) - self.logger.debug('cmd_args %s',cmd_args) + self.logger.debug('cmd_args %s' % cmd_args) try: self.dispatch(command, cmd_opts, cmd_args) except KeyError: self.logger.critical ("Unknown command %s"%command) + raise sys.exit(1) return