diff --git a/ipaclient/remote_plugins/__init__.py b/ipaclient/remote_plugins/__init__.py index 6454a4f4e..444651d30 100644 --- a/ipaclient/remote_plugins/__init__.py +++ b/ipaclient/remote_plugins/__init__.py @@ -2,23 +2,90 @@ # Copyright (C) 2016 FreeIPA Contributors see COPYING for license # +import collections +import errno +import json +import os + from . import compat from . import schema from ipaclient.plugins.rpcclient import rpcclient +from ipaplatform.paths import paths +from ipapython.dnsutil import DNSName +from ipapython.ipa_log_manager import log_mgr + +logger = log_mgr.get_logger(__name__) + + +class ServerInfo(collections.MutableMapping): + _DIR = os.path.join(paths.USER_CACHE_PATH, 'ipa', 'servers') + + def __init__(self, api): + hostname = DNSName(api.env.server).ToASCII() + self._path = os.path.join(self._DIR, hostname) + self._dict = {} + self._dirty = False + + self._read() + + def __enter__(self): + return self + + def __exit__(self, *_exc_info): + if self._dirty: + self._write() + + def _read(self): + try: + with open(self._path, 'r') as sc: + self._dict = json.load(sc) + except EnvironmentError as e: + if e.errno != errno.ENOENT: + logger.warning('Failed to read server info: {}'.format(e)) + + def _write(self): + try: + try: + os.makedirs(self._DIR) + except EnvironmentError as e: + if e.errno != errno.EEXIST: + raise + with open(self._path, 'w') as sc: + json.dump(self._dict, sc) + except EnvironmentError as e: + logger.warning('Failed to write server info: {}'.format(e)) + + def __getitem__(self, key): + return self._dict[key] + + def __setitem__(self, key, value): + self._dirty = key not in self._dict or self._dict[key] != value + self._dict[key] = value + + def __delitem__(self, key): + del self._dict[key] + self._dirty = True + + def __iter__(self): + return iter(self._dict) + + def __len__(self): + return len(self._dict) def get_package(api): if api.env.in_tree: from ipaserver import plugins else: - client = rpcclient(api) - client.finalize() - try: - plugins = schema.get_package(api, client) - except schema.NotAvailable: - plugins = compat.get_package(api, client) - finally: - if client.isconnected(): - client.disconnect() + with ServerInfo(api) as server_info: + client = rpcclient(api) + client.finalize() + try: + plugins = schema.get_package(api, server_info, client) + except schema.NotAvailable: + plugins = compat.get_package(api, server_info, client) + finally: + if client.isconnected(): + client.disconnect() return plugins diff --git a/ipaclient/remote_plugins/compat.py b/ipaclient/remote_plugins/compat.py index aef5718fc..b6d099a07 100644 --- a/ipaclient/remote_plugins/compat.py +++ b/ipaclient/remote_plugins/compat.py @@ -31,23 +31,26 @@ class CompatObject(Object): pass -def get_package(api, client): - if not client.isconnected(): - client.connect(verbose=False) - - env = client.forward(u'env', u'api_version', version=u'2.0') +def get_package(api, server_info, client): try: - server_version = env['result']['api_version'] + server_version = server_info['version'] except KeyError: - ping = client.forward(u'ping', version=u'2.0') + if not client.isconnected(): + client.connect(verbose=False) + env = client.forward(u'env', u'api_version', version=u'2.0') try: - match = re.search(u'API version (2\.[0-9]+)', ping['summary']) + server_version = env['result']['api_version'] except KeyError: - match = None - if match is not None: - server_version = match.group(1) - else: - server_version = u'2.0' + ping = client.forward(u'ping', u'api_version', version=u'2.0') + try: + match = re.search(u'API version (2\.[0-9]+)', ping['summary']) + except KeyError: + match = None + if match is not None: + server_version = match.group(1) + else: + server_version = u'2.0' + server_info['version'] = server_version server_version = LooseVersion(server_version) package_names = {} diff --git a/ipaclient/remote_plugins/schema.py b/ipaclient/remote_plugins/schema.py index 9d5c08b85..a215452ea 100644 --- a/ipaclient/remote_plugins/schema.py +++ b/ipaclient/remote_plugins/schema.py @@ -19,6 +19,7 @@ from ipalib import errors, parameters, plugable from ipalib.frontend import Object from ipalib.output import Output from ipalib.parameters import DefaultFrom, Flag, Password, Str +from ipaplatform.paths import paths from ipapython.dn import DN from ipapython.dnsutil import DNSName from ipapython.ipa_log_manager import log_mgr @@ -55,17 +56,6 @@ _PARAMS = { 'str': parameters.Str, } -USER_CACHE_PATH = ( - os.environ.get('XDG_CACHE_HOME') or - os.path.join( - os.environ.get( - 'HOME', - os.path.expanduser('~') - ), - '.cache' - ) -) - logger = log_mgr.get_logger(__name__) @@ -359,62 +349,6 @@ class NotAvailable(Exception): pass -class ServerInfo(collections.MutableMapping): - _DIR = os.path.join(USER_CACHE_PATH, 'ipa', 'servers') - - def __init__(self, api): - hostname = DNSName(api.env.server).ToASCII() - self._path = os.path.join(self._DIR, hostname) - self._dict = {} - self._dirty = False - - self._read() - - def __enter__(self): - return self - - def __exit__(self, *_exc_info): - if self._dirty: - self._write() - - def _read(self): - try: - with open(self._path, 'r') as sc: - self._dict = json.load(sc) - except EnvironmentError as e: - if e.errno != errno.ENOENT: - logger.warning('Failed to read server info: {}'.format(e)) - - def _write(self): - try: - try: - os.makedirs(self._DIR) - except EnvironmentError as e: - if e.errno != errno.EEXIST: - raise - with open(self._path, 'w') as sc: - json.dump(self._dict, sc) - except EnvironmentError as e: - logger.warning('Failed to write server info: {}'.format(e)) - - def __getitem__(self, key): - return self._dict[key] - - def __setitem__(self, key, value): - self._dirty = key not in self._dict or self._dict[key] != value - self._dict[key] = value - - def __delitem__(self, key): - del self._dict[key] - self._dirty = True - - def __iter__(self): - return iter(self._dict) - - def __len__(self): - return len(self._dict) - - class Schema(object): """ Store and provide schema for commands and topics @@ -436,7 +370,7 @@ class Schema(object): """ namespaces = {'classes', 'commands', 'topics'} - _DIR = os.path.join(USER_CACHE_PATH, 'ipa', 'schema') + _DIR = os.path.join(paths.USER_CACHE_PATH, 'ipa', 'schema') def __init__(self, api, server_info, client): self._dict = {} @@ -618,13 +552,12 @@ class Schema(object): self._dict[ns][member].update(self._help[ns][member]) -def get_package(api, client): +def get_package(api, server_info, client): try: schema = api._schema except AttributeError: - with ServerInfo(api.env.hostname) as server_info: - schema = Schema(api, server_info, client) - object.__setattr__(api, '_schema', schema) + schema = Schema(api, server_info, client) + object.__setattr__(api, '_schema', schema) fingerprint = str(schema['fingerprint']) package_name = '{}${}'.format(__name__, fingerprint) diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py index bb0071a8e..b1fedf5d7 100644 --- a/ipaplatform/base/paths.py +++ b/ipaplatform/base/paths.py @@ -21,6 +21,8 @@ This base platform module exports default filesystem paths. ''' +import os + class BasePathNamespace(object): BASH = "/bin/bash" @@ -353,4 +355,17 @@ class BasePathNamespace(object): IPA_CUSTODIA_AUDIT_LOG = '/var/log/ipa-custodia.audit.log' IPA_GETKEYTAB = '/usr/sbin/ipa-getkeytab' + @property + def USER_CACHE_PATH(self): + return ( + os.environ.get('XDG_CACHE_HOME') or + os.path.join( + os.environ.get( + 'HOME', + os.path.expanduser('~') + ), + '.cache' + ) + ) + path_namespace = BasePathNamespace