summaryrefslogtreecommitdiffstats
path: root/ipaclient/remote_plugins/__init__.py
blob: 037dd6f671acf3587f1ecafb6da8c040153d3438 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#
# Copyright (C) 2016  FreeIPA Contributors see COPYING for license
#

import collections
import errno
import json
import locale
import os
import time

from . import compat
from . import schema
from ipaclient.plugins.rpcclient import rpcclient
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(schema.USER_CACHE_PATH, 'ipa', 'servers')

    def __init__(self, api):
        hostname = DNSName(api.env.server).ToASCII()
        self._path = os.path.join(self._DIR, hostname)
        self._force_check = api.env.force_schema_check
        self._dict = {}

        # copy-paste from ipalib/rpc.py
        try:
            self._language = (
                 locale.setlocale(locale.LC_ALL, '').split('.')[0].lower()
            )
        except locale.Error:
            self._language = 'en_us'

        self._read()

    def _read(self):
        try:
            with open(self._path, 'r') as sc:
                self._dict = json.load(sc)
        except Exception as e:
            if (isinstance(e, EnvironmentError) and
                    e.errno == errno.ENOENT):  # pylint: disable=no-member
                # ignore non-existent file, this happens when the cache was
                # erased or the server is contacted for the first time
                pass
            else:
                # warn that the file is unreadable, probably corrupted
                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._dict[key] = value

    def __delitem__(self, key):
        del self._dict[key]

    def __iter__(self):
        return iter(self._dict)

    def __len__(self):
        return len(self._dict)

    def update_validity(self, ttl=None):
        if ttl is None:
            ttl = 3600
        self['expiration'] = time.time() + ttl
        self['language'] = self._language
        self._write()

    def is_valid(self):
        if self._force_check:
            return False

        try:
            expiration = self._dict['expiration']
            language = self._dict['language']
        except KeyError:
            # if any of these is missing consider the entry expired
            return False

        if expiration < time.time():
            # validity passed
            return False

        if language != self._language:
            # language changed since last check
            return False

        return True


def get_package(api):
    if api.env.in_tree:
        # pylint: disable=import-error,ipa-forbidden-import
        from ipaserver import plugins
        # pylint: enable=import-error,ipa-forbidden-import
    else:
        try:
            plugins = api._remote_plugins
        except AttributeError:
            server_info = ServerInfo(api)

            client = rpcclient(api)
            client.finalize()

            try:
                plugins = schema.get_package(server_info, client)
            except schema.NotAvailable:
                plugins = compat.get_package(server_info, client)
            finally:
                if client.isconnected():
                    client.disconnect()

            object.__setattr__(api, '_remote_plugins', plugins)

    return plugins