Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / config.py @ 60c42f9f

History | View | Annotate | Download (14 kB)

1 e3f01d64 Stavros Sachtouris
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2 7493ccb6 Stavros Sachtouris
#
3 7493ccb6 Stavros Sachtouris
# Redistribution and use in source and binary forms, with or
4 7493ccb6 Stavros Sachtouris
# without modification, are permitted provided that the following
5 7493ccb6 Stavros Sachtouris
# conditions are met:
6 7493ccb6 Stavros Sachtouris
#
7 7493ccb6 Stavros Sachtouris
#   1. Redistributions of source code must retain the above
8 7493ccb6 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
9 7493ccb6 Stavros Sachtouris
#      disclaimer.
10 7493ccb6 Stavros Sachtouris
#
11 7493ccb6 Stavros Sachtouris
#   2. Redistributions in binary form must reproduce the above
12 7493ccb6 Stavros Sachtouris
#      copyright notice, this list of conditions and the following
13 7493ccb6 Stavros Sachtouris
#      disclaimer in the documentation and/or other materials
14 7493ccb6 Stavros Sachtouris
#      provided with the distribution.
15 7493ccb6 Stavros Sachtouris
#
16 7493ccb6 Stavros Sachtouris
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 7493ccb6 Stavros Sachtouris
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 7493ccb6 Stavros Sachtouris
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 7493ccb6 Stavros Sachtouris
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 7493ccb6 Stavros Sachtouris
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 7493ccb6 Stavros Sachtouris
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 7493ccb6 Stavros Sachtouris
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 7493ccb6 Stavros Sachtouris
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 7493ccb6 Stavros Sachtouris
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 7493ccb6 Stavros Sachtouris
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 7493ccb6 Stavros Sachtouris
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 7493ccb6 Stavros Sachtouris
# POSSIBILITY OF SUCH DAMAGE.
28 7493ccb6 Stavros Sachtouris
#
29 7493ccb6 Stavros Sachtouris
# The views and conclusions contained in the software and
30 7493ccb6 Stavros Sachtouris
# documentation are those of the authors and should not be
31 7493ccb6 Stavros Sachtouris
# interpreted as representing official policies, either expressed
32 7493ccb6 Stavros Sachtouris
# or implied, of GRNET S.A.
33 7493ccb6 Stavros Sachtouris
34 7493ccb6 Stavros Sachtouris
import os
35 3f0eae61 Stavros Sachtouris
from logging import getLogger
36 7493ccb6 Stavros Sachtouris
37 7493ccb6 Stavros Sachtouris
from collections import defaultdict
38 29be4062 Stavros Sachtouris
from ConfigParser import RawConfigParser, NoOptionError, NoSectionError, Error
39 3f0eae61 Stavros Sachtouris
from re import match
40 7493ccb6 Stavros Sachtouris
41 ce9ccb40 Stavros Sachtouris
from kamaki.cli.errors import CLISyntaxError
42 144b3551 Stavros Sachtouris
from kamaki import __version__
43 ce9ccb40 Stavros Sachtouris
44 7493ccb6 Stavros Sachtouris
try:
45 7493ccb6 Stavros Sachtouris
    from collections import OrderedDict
46 7493ccb6 Stavros Sachtouris
except ImportError:
47 1792ed1d Stavros Sachtouris
    from kamaki.clients.utils.ordereddict import OrderedDict
48 7493ccb6 Stavros Sachtouris
49 7493ccb6 Stavros Sachtouris
50 29be4062 Stavros Sachtouris
class InvalidCloudNameError(Error):
51 29be4062 Stavros Sachtouris
    """A valid cloud name is accepted by this regex: ([@#$:-\w]+)"""
52 29be4062 Stavros Sachtouris
53 29be4062 Stavros Sachtouris
54 3f0eae61 Stavros Sachtouris
log = getLogger(__name__)
55 3f0eae61 Stavros Sachtouris
56 7493ccb6 Stavros Sachtouris
# Path to the file that stores the configuration
57 7493ccb6 Stavros Sachtouris
CONFIG_PATH = os.path.expanduser('~/.kamakirc')
58 9f1d1fcf Stavros Sachtouris
HISTORY_PATH = os.path.expanduser('~/.kamaki.history')
59 6144f3d4 Stavros Sachtouris
CLOUD_PREFIX = 'cloud'
60 7493ccb6 Stavros Sachtouris
61 7493ccb6 Stavros Sachtouris
# Name of a shell variable to bypass the CONFIG_PATH value
62 7493ccb6 Stavros Sachtouris
CONFIG_ENV = 'KAMAKI_CONFIG'
63 7493ccb6 Stavros Sachtouris
64 144b3551 Stavros Sachtouris
version = ''
65 144b3551 Stavros Sachtouris
for c in '%s' % __version__:
66 144b3551 Stavros Sachtouris
    if c not in '0.123456789':
67 144b3551 Stavros Sachtouris
        break
68 144b3551 Stavros Sachtouris
    version += c
69 c825ddc9 Stavros Sachtouris
HEADER = '# Kamaki configuration file v%s\n' % version
70 7493ccb6 Stavros Sachtouris
71 7493ccb6 Stavros Sachtouris
DEFAULTS = {
72 7493ccb6 Stavros Sachtouris
    'global': {
73 144b3551 Stavros Sachtouris
        'default_cloud': '',
74 4f6a21f6 Stavros Sachtouris
        'colors': 'off',
75 edf00ab3 Stavros Sachtouris
        'log_file': os.path.expanduser('~/.kamaki.log'),
76 c5b9380c Stavros Sachtouris
        'log_token': 'off',
77 c5b9380c Stavros Sachtouris
        'log_data': 'off',
78 05e144e2 Stavros Sachtouris
        'max_threads': 7,
79 3f0eae61 Stavros Sachtouris
        'history_file': HISTORY_PATH,
80 3f0eae61 Stavros Sachtouris
        'user_cli': 'astakos',
81 3f0eae61 Stavros Sachtouris
        'file_cli': 'pithos',
82 3f0eae61 Stavros Sachtouris
        'server_cli': 'cyclades',
83 3f0eae61 Stavros Sachtouris
        'flavor_cli': 'cyclades',
84 3f0eae61 Stavros Sachtouris
        'network_cli': 'cyclades',
85 3f0eae61 Stavros Sachtouris
        'image_cli': 'image',
86 3f0eae61 Stavros Sachtouris
        'config_cli': 'config',
87 3f0eae61 Stavros Sachtouris
        'history_cli': 'history'
88 f5c28bfa Stavros Sachtouris
        #  Optional command specs:
89 ce9ccb40 Stavros Sachtouris
        #  'livetest_cli': 'livetest',
90 ce9ccb40 Stavros Sachtouris
        #  'astakos_cli': 'snf-astakos'
91 7493ccb6 Stavros Sachtouris
    },
92 6144f3d4 Stavros Sachtouris
    CLOUD_PREFIX:
93 3f0eae61 Stavros Sachtouris
    {
94 144b3551 Stavros Sachtouris
        #'default': {
95 144b3551 Stavros Sachtouris
        #    'url': '',
96 144b3551 Stavros Sachtouris
        #    'token': ''
97 144b3551 Stavros Sachtouris
        #    'pithos_type': 'object-store',
98 144b3551 Stavros Sachtouris
        #    'pithos_version': 'v1',
99 144b3551 Stavros Sachtouris
        #    'cyclades_type': 'compute',
100 144b3551 Stavros Sachtouris
        #    'cyclades_version': 'v2.0',
101 144b3551 Stavros Sachtouris
        #    'plankton_type': 'image',
102 144b3551 Stavros Sachtouris
        #    'plankton_version': '',
103 144b3551 Stavros Sachtouris
        #    'astakos_type': 'identity',
104 144b3551 Stavros Sachtouris
        #    'astakos_version': 'v2.0'
105 144b3551 Stavros Sachtouris
        #}
106 7493ccb6 Stavros Sachtouris
    }
107 7493ccb6 Stavros Sachtouris
}
108 7493ccb6 Stavros Sachtouris
109 7493ccb6 Stavros Sachtouris
110 e1c18867 Stavros Sachtouris
try:
111 e1c18867 Stavros Sachtouris
    import astakosclient
112 fa382f9e Stavros Sachtouris
    DEFAULTS['global'].update(dict(astakos_cli='snf-astakos'))
113 e1c18867 Stavros Sachtouris
except ImportError:
114 e1c18867 Stavros Sachtouris
    pass
115 e1c18867 Stavros Sachtouris
116 e1c18867 Stavros Sachtouris
117 7493ccb6 Stavros Sachtouris
class Config(RawConfigParser):
118 3f0eae61 Stavros Sachtouris
    def __init__(self, path=None, with_defaults=True):
119 7493ccb6 Stavros Sachtouris
        RawConfigParser.__init__(self, dict_type=OrderedDict)
120 7493ccb6 Stavros Sachtouris
        self.path = path or os.environ.get(CONFIG_ENV, CONFIG_PATH)
121 7493ccb6 Stavros Sachtouris
        self._overrides = defaultdict(dict)
122 3f0eae61 Stavros Sachtouris
        if with_defaults:
123 3f0eae61 Stavros Sachtouris
            self._load_defaults()
124 7493ccb6 Stavros Sachtouris
        self.read(self.path)
125 7493ccb6 Stavros Sachtouris
126 ce9ccb40 Stavros Sachtouris
        for section in self.sections():
127 144b3551 Stavros Sachtouris
            r = self._cloud_name(section)
128 ce9ccb40 Stavros Sachtouris
            if r:
129 ce9ccb40 Stavros Sachtouris
                for k, v in self.items(section):
130 144b3551 Stavros Sachtouris
                    self.set_cloud(r, k, v)
131 ce9ccb40 Stavros Sachtouris
                self.remove_section(section)
132 14b9a185 Stavros Sachtouris
133 3f0eae61 Stavros Sachtouris
    @staticmethod
134 144b3551 Stavros Sachtouris
    def _cloud_name(full_section_name):
135 29be4062 Stavros Sachtouris
        if not full_section_name.startswith(CLOUD_PREFIX + ' '):
136 29be4062 Stavros Sachtouris
            return None
137 29be4062 Stavros Sachtouris
        matcher = match(CLOUD_PREFIX + ' "([@#$:\-\w]+)"', full_section_name)
138 29be4062 Stavros Sachtouris
        if matcher:
139 29be4062 Stavros Sachtouris
            return matcher.groups()[0]
140 29be4062 Stavros Sachtouris
        else:
141 29be4062 Stavros Sachtouris
            icn = full_section_name[len(CLOUD_PREFIX) + 1:]
142 29be4062 Stavros Sachtouris
            raise InvalidCloudNameError('Invalid Cloud Name %s' % icn)
143 3f0eae61 Stavros Sachtouris
144 14b9a185 Stavros Sachtouris
    def rescue_old_file(self):
145 ce9ccb40 Stavros Sachtouris
        lost_terms = []
146 ce9ccb40 Stavros Sachtouris
        global_terms = DEFAULTS['global'].keys()
147 ce9ccb40 Stavros Sachtouris
        translations = dict(
148 ce9ccb40 Stavros Sachtouris
            config=dict(serv='', cmd='config'),
149 ce9ccb40 Stavros Sachtouris
            history=dict(serv='', cmd='history'),
150 ce9ccb40 Stavros Sachtouris
            pithos=dict(serv='pithos', cmd='file'),
151 ce9ccb40 Stavros Sachtouris
            file=dict(serv='pithos', cmd='file'),
152 ce9ccb40 Stavros Sachtouris
            store=dict(serv='pithos', cmd='file'),
153 ce9ccb40 Stavros Sachtouris
            storage=dict(serv='pithos', cmd='file'),
154 ce9ccb40 Stavros Sachtouris
            image=dict(serv='plankton', cmd='image'),
155 ce9ccb40 Stavros Sachtouris
            plankton=dict(serv='plankton', cmd='image'),
156 ce9ccb40 Stavros Sachtouris
            compute=dict(serv='compute', cmd=''),
157 ce9ccb40 Stavros Sachtouris
            cyclades=dict(serv='compute', cmd='server'),
158 ce9ccb40 Stavros Sachtouris
            server=dict(serv='compute', cmd='server'),
159 ce9ccb40 Stavros Sachtouris
            flavor=dict(serv='compute', cmd='flavor'),
160 ce9ccb40 Stavros Sachtouris
            network=dict(serv='compute', cmd='network'),
161 ce9ccb40 Stavros Sachtouris
            astakos=dict(serv='astakos', cmd='user'),
162 ce9ccb40 Stavros Sachtouris
            user=dict(serv='astakos', cmd='user'),
163 ce9ccb40 Stavros Sachtouris
        )
164 ce9ccb40 Stavros Sachtouris
165 6144f3d4 Stavros Sachtouris
        self.set('global', 'default_' + CLOUD_PREFIX, 'default')
166 ce9ccb40 Stavros Sachtouris
        for s in self.sections():
167 ce9ccb40 Stavros Sachtouris
            if s in ('global'):
168 ce9ccb40 Stavros Sachtouris
                # global.url, global.token -->
169 144b3551 Stavros Sachtouris
                # cloud.default.url, cloud.default.token
170 ce9ccb40 Stavros Sachtouris
                for term in set(self.keys(s)).difference(global_terms):
171 ce9ccb40 Stavros Sachtouris
                    if term not in ('url', 'token'):
172 ce9ccb40 Stavros Sachtouris
                        lost_terms.append('%s.%s = %s' % (
173 ce9ccb40 Stavros Sachtouris
                            s, term, self.get(s, term)))
174 ce9ccb40 Stavros Sachtouris
                        self.remove_option(s, term)
175 ce9ccb40 Stavros Sachtouris
                        continue
176 ce9ccb40 Stavros Sachtouris
                    gval = self.get(s, term)
177 c825ddc9 Stavros Sachtouris
                    try:
178 c825ddc9 Stavros Sachtouris
                        cval = self.get_cloud('default', term)
179 c825ddc9 Stavros Sachtouris
                    except KeyError:
180 c825ddc9 Stavros Sachtouris
                        cval = ''
181 ce9ccb40 Stavros Sachtouris
                    if gval and cval and (
182 ce9ccb40 Stavros Sachtouris
                        gval.lower().strip('/') != cval.lower().strip('/')):
183 ce9ccb40 Stavros Sachtouris
                            raise CLISyntaxError(
184 ce9ccb40 Stavros Sachtouris
                                'Conflicting values for default %s' % term,
185 ce9ccb40 Stavros Sachtouris
                                importance=2, details=[
186 ce9ccb40 Stavros Sachtouris
                                    ' global.%s:  %s' % (term, gval),
187 6144f3d4 Stavros Sachtouris
                                    ' %s.default.%s:  %s' % (
188 6144f3d4 Stavros Sachtouris
                                        CLOUD_PREFIX, term, cval),
189 ce9ccb40 Stavros Sachtouris
                                    'Please remove one of them manually:',
190 ce9ccb40 Stavros Sachtouris
                                    ' /config delete global.%s' % term,
191 ce9ccb40 Stavros Sachtouris
                                    ' or'
192 6144f3d4 Stavros Sachtouris
                                    ' /config delete %s.default.%s' % (
193 6144f3d4 Stavros Sachtouris
                                        CLOUD_PREFIX, term),
194 ce9ccb40 Stavros Sachtouris
                                    'and try again'])
195 ce9ccb40 Stavros Sachtouris
                    elif gval:
196 6144f3d4 Stavros Sachtouris
                        print('... rescue %s.%s => %s.default.%s' % (
197 6144f3d4 Stavros Sachtouris
                            s, term, CLOUD_PREFIX, term))
198 144b3551 Stavros Sachtouris
                        self.set_cloud('default', term, gval)
199 ce9ccb40 Stavros Sachtouris
                    self.remove_option(s, term)
200 ce9ccb40 Stavros Sachtouris
            # translation for <service> or <command> settings
201 ce9ccb40 Stavros Sachtouris
            # <service> or <command group> settings --> translation --> global
202 ce9ccb40 Stavros Sachtouris
            elif s in translations:
203 ce9ccb40 Stavros Sachtouris
204 ce9ccb40 Stavros Sachtouris
                if s in ('history',):
205 ce9ccb40 Stavros Sachtouris
                    k = 'file'
206 ce9ccb40 Stavros Sachtouris
                    v = self.get(s, k)
207 ce9ccb40 Stavros Sachtouris
                    if v:
208 ce9ccb40 Stavros Sachtouris
                        print('... rescue %s.%s => global.%s_%s' % (
209 ce9ccb40 Stavros Sachtouris
                            s, k, s, k))
210 ce9ccb40 Stavros Sachtouris
                        self.set('global', '%s_%s' % (s, k), v)
211 ce9ccb40 Stavros Sachtouris
                        self.remove_option(s, k)
212 ce9ccb40 Stavros Sachtouris
213 ce9ccb40 Stavros Sachtouris
                trn = translations[s]
214 ce9ccb40 Stavros Sachtouris
                for k, v in self.items(s, False):
215 ce9ccb40 Stavros Sachtouris
                    if v and k in ('cli',):
216 ce9ccb40 Stavros Sachtouris
                        print('... rescue %s.%s => global.%s_cli' % (
217 ce9ccb40 Stavros Sachtouris
                            s, k, trn['cmd']))
218 144b3551 Stavros Sachtouris
                        self.set('global', '%s_cli' % trn['cmd'], v)
219 144b3551 Stavros Sachtouris
                    elif k in ('container',) and trn['serv'] in ('pithos',):
220 6144f3d4 Stavros Sachtouris
                        print('... rescue %s.%s => %s.default.pithos_%s' % (
221 6144f3d4 Stavros Sachtouris
                                    s, k, CLOUD_PREFIX, k))
222 144b3551 Stavros Sachtouris
                        self.set_cloud('default', 'pithos_%s' % k, v)
223 144b3551 Stavros Sachtouris
                    else:
224 ce9ccb40 Stavros Sachtouris
                        lost_terms.append('%s.%s = %s' % (s, k, v))
225 ce9ccb40 Stavros Sachtouris
                self.remove_section(s)
226 ce9ccb40 Stavros Sachtouris
        #  self.pretty_print()
227 ce9ccb40 Stavros Sachtouris
        return lost_terms
228 ce9ccb40 Stavros Sachtouris
229 ce9ccb40 Stavros Sachtouris
    def pretty_print(self):
230 ce9ccb40 Stavros Sachtouris
        for s in self.sections():
231 ce9ccb40 Stavros Sachtouris
            print s
232 ce9ccb40 Stavros Sachtouris
            for k, v in self.items(s):
233 ce9ccb40 Stavros Sachtouris
                if isinstance(v, dict):
234 ce9ccb40 Stavros Sachtouris
                    print '\t', k, '=> {'
235 ce9ccb40 Stavros Sachtouris
                    for ki, vi in v.items():
236 ce9ccb40 Stavros Sachtouris
                        print '\t\t', ki, '=>', vi
237 ce9ccb40 Stavros Sachtouris
                    print('\t}')
238 ce9ccb40 Stavros Sachtouris
                else:
239 ce9ccb40 Stavros Sachtouris
                    print '\t', k, '=>', v
240 14b9a185 Stavros Sachtouris
241 3f0eae61 Stavros Sachtouris
    def guess_version(self):
242 144b3551 Stavros Sachtouris
        """
243 144b3551 Stavros Sachtouris
        :returns: (float) version of the config file or 0.0 if unrecognized
244 144b3551 Stavros Sachtouris
        """
245 3f0eae61 Stavros Sachtouris
        checker = Config(self.path, with_defaults=False)
246 3f0eae61 Stavros Sachtouris
        sections = checker.sections()
247 144b3551 Stavros Sachtouris
        log.warning('Config file heuristic 1: old global section ?')
248 3f0eae61 Stavros Sachtouris
        if 'global' in sections:
249 3f0eae61 Stavros Sachtouris
            if checker.get('global', 'url') or checker.get('global', 'token'):
250 3f0eae61 Stavros Sachtouris
                log.warning('..... config file has an old global section')
251 c825ddc9 Stavros Sachtouris
                return 0.8
252 14b9a185 Stavros Sachtouris
        log.warning('........ nope')
253 df0045d8 Stavros Sachtouris
        log.warning('Config file heuristic 2: Any cloud sections ?')
254 6144f3d4 Stavros Sachtouris
        if CLOUD_PREFIX in sections:
255 6144f3d4 Stavros Sachtouris
            for r in self.keys(CLOUD_PREFIX):
256 144b3551 Stavros Sachtouris
                log.warning('... found cloud "%s"' % r)
257 c825ddc9 Stavros Sachtouris
                return 0.9
258 df0045d8 Stavros Sachtouris
        log.warning('........ nope')
259 3f0eae61 Stavros Sachtouris
        log.warning('All heuristics failed, cannot decide')
260 df0045d8 Stavros Sachtouris
        return 0.9
261 3f0eae61 Stavros Sachtouris
262 144b3551 Stavros Sachtouris
    def get_cloud(self, cloud, option):
263 ce9ccb40 Stavros Sachtouris
        """
264 144b3551 Stavros Sachtouris
        :param cloud: (str) cloud alias
265 ce9ccb40 Stavros Sachtouris

266 144b3551 Stavros Sachtouris
        :param option: (str) option in cloud section
267 ce9ccb40 Stavros Sachtouris

268 ce9ccb40 Stavros Sachtouris
        :returns: (str) the value assigned on this option
269 ce9ccb40 Stavros Sachtouris

270 144b3551 Stavros Sachtouris
        :raises KeyError: if cloud or cloud's option does not exist
271 ce9ccb40 Stavros Sachtouris
        """
272 6144f3d4 Stavros Sachtouris
        r = self.get(CLOUD_PREFIX, cloud)
273 ce9ccb40 Stavros Sachtouris
        if not r:
274 144b3551 Stavros Sachtouris
            raise KeyError('Cloud "%s" does not exist' % cloud)
275 ce9ccb40 Stavros Sachtouris
        return r[option]
276 ce9ccb40 Stavros Sachtouris
277 362adf50 Stavros Sachtouris
    def get_global(self, option):
278 362adf50 Stavros Sachtouris
        return self.get('global', option)
279 362adf50 Stavros Sachtouris
280 144b3551 Stavros Sachtouris
    def set_cloud(self, cloud, option, value):
281 ce9ccb40 Stavros Sachtouris
        try:
282 6144f3d4 Stavros Sachtouris
            d = self.get(CLOUD_PREFIX, cloud) or dict()
283 ce9ccb40 Stavros Sachtouris
        except KeyError:
284 844a6bdb Stavros Sachtouris
            d = dict()
285 ce9ccb40 Stavros Sachtouris
        d[option] = value
286 6144f3d4 Stavros Sachtouris
        self.set(CLOUD_PREFIX, cloud, d)
287 362adf50 Stavros Sachtouris
288 362adf50 Stavros Sachtouris
    def set_global(self, option, value):
289 362adf50 Stavros Sachtouris
        self.set('global', option, value)
290 ce9ccb40 Stavros Sachtouris
291 7493ccb6 Stavros Sachtouris
    def _load_defaults(self):
292 7493ccb6 Stavros Sachtouris
        for section, options in DEFAULTS.items():
293 7493ccb6 Stavros Sachtouris
            for option, val in options.items():
294 7493ccb6 Stavros Sachtouris
                self.set(section, option, val)
295 7493ccb6 Stavros Sachtouris
296 f724cd35 Stavros Sachtouris
    def _get_dict(self, section, include_defaults=True):
297 f724cd35 Stavros Sachtouris
        try:
298 f724cd35 Stavros Sachtouris
            d = dict(DEFAULTS[section]) if include_defaults else {}
299 f724cd35 Stavros Sachtouris
        except KeyError:
300 f724cd35 Stavros Sachtouris
            d = {}
301 f724cd35 Stavros Sachtouris
        try:
302 f724cd35 Stavros Sachtouris
            d.update(RawConfigParser.items(self, section))
303 f724cd35 Stavros Sachtouris
        except NoSectionError:
304 f724cd35 Stavros Sachtouris
            pass
305 f724cd35 Stavros Sachtouris
        return d
306 f724cd35 Stavros Sachtouris
307 8eb8c540 Stavros Sachtouris
    def reload(self):
308 8eb8c540 Stavros Sachtouris
        self = self.__init__(self.path)
309 8eb8c540 Stavros Sachtouris
310 7493ccb6 Stavros Sachtouris
    def get(self, section, option):
311 114e19da Stavros Sachtouris
        """
312 144b3551 Stavros Sachtouris
        :param section: (str) HINT: for clouds, use cloud.<section>
313 114e19da Stavros Sachtouris

314 114e19da Stavros Sachtouris
        :param option: (str)
315 114e19da Stavros Sachtouris

316 114e19da Stavros Sachtouris
        :returns: (str) the value stored at section: {option: value}
317 114e19da Stavros Sachtouris
        """
318 7493ccb6 Stavros Sachtouris
        value = self._overrides.get(section, {}).get(option)
319 7493ccb6 Stavros Sachtouris
        if value is not None:
320 7493ccb6 Stavros Sachtouris
            return value
321 6144f3d4 Stavros Sachtouris
        prefix = CLOUD_PREFIX + '.'
322 144b3551 Stavros Sachtouris
        if section.startswith(prefix):
323 144b3551 Stavros Sachtouris
            return self.get_cloud(section[len(prefix):], option)
324 7493ccb6 Stavros Sachtouris
        try:
325 7493ccb6 Stavros Sachtouris
            return RawConfigParser.get(self, section, option)
326 7493ccb6 Stavros Sachtouris
        except (NoSectionError, NoOptionError):
327 7493ccb6 Stavros Sachtouris
            return DEFAULTS.get(section, {}).get(option)
328 7493ccb6 Stavros Sachtouris
329 7493ccb6 Stavros Sachtouris
    def set(self, section, option, value):
330 114e19da Stavros Sachtouris
        """
331 144b3551 Stavros Sachtouris
        :param section: (str) HINT: for remotes use cloud.<section>
332 114e19da Stavros Sachtouris

333 114e19da Stavros Sachtouris
        :param option: (str)
334 114e19da Stavros Sachtouris

335 114e19da Stavros Sachtouris
        :param value: str
336 114e19da Stavros Sachtouris
        """
337 6144f3d4 Stavros Sachtouris
        prefix = CLOUD_PREFIX + '.'
338 144b3551 Stavros Sachtouris
        if section.startswith(prefix):
339 29be4062 Stavros Sachtouris
            cloud = self._cloud_name(
340 29be4062 Stavros Sachtouris
                CLOUD_PREFIX + ' "' + section[len(prefix):] + '"')
341 29be4062 Stavros Sachtouris
            return self.set_cloud(cloud, option, value)
342 7493ccb6 Stavros Sachtouris
        if section not in RawConfigParser.sections(self):
343 7493ccb6 Stavros Sachtouris
            self.add_section(section)
344 7493ccb6 Stavros Sachtouris
        RawConfigParser.set(self, section, option, value)
345 7493ccb6 Stavros Sachtouris
346 3fe56be4 Stavros Sachtouris
    def remove_option(self, section, option, also_remove_default=False):
347 7493ccb6 Stavros Sachtouris
        try:
348 3fe56be4 Stavros Sachtouris
            if also_remove_default:
349 3fe56be4 Stavros Sachtouris
                DEFAULTS[section].pop(option)
350 7493ccb6 Stavros Sachtouris
            RawConfigParser.remove_option(self, section, option)
351 7493ccb6 Stavros Sachtouris
        except NoSectionError:
352 7493ccb6 Stavros Sachtouris
            pass
353 7493ccb6 Stavros Sachtouris
354 144b3551 Stavros Sachtouris
    def remote_from_cloud(self, cloud, option):
355 6144f3d4 Stavros Sachtouris
        d = self.get(CLOUD_PREFIX, cloud)
356 844a6bdb Stavros Sachtouris
        if isinstance(d, dict):
357 844a6bdb Stavros Sachtouris
            d.pop(option)
358 844a6bdb Stavros Sachtouris
359 f724cd35 Stavros Sachtouris
    def keys(self, section, include_defaults=True):
360 f724cd35 Stavros Sachtouris
        d = self._get_dict(section, include_defaults)
361 f724cd35 Stavros Sachtouris
        return d.keys()
362 f724cd35 Stavros Sachtouris
363 57ecec97 Stavros Sachtouris
    def items(self, section, include_defaults=True):
364 f724cd35 Stavros Sachtouris
        d = self._get_dict(section, include_defaults)
365 7493ccb6 Stavros Sachtouris
        return d.items()
366 7493ccb6 Stavros Sachtouris
367 7493ccb6 Stavros Sachtouris
    def override(self, section, option, value):
368 7493ccb6 Stavros Sachtouris
        self._overrides[section][option] = value
369 7493ccb6 Stavros Sachtouris
370 7493ccb6 Stavros Sachtouris
    def write(self):
371 35d47a6c Stavros Sachtouris
        cld_bu = self._get_dict(CLOUD_PREFIX)
372 6144f3d4 Stavros Sachtouris
        try:
373 6144f3d4 Stavros Sachtouris
            for r, d in self.items(CLOUD_PREFIX):
374 6144f3d4 Stavros Sachtouris
                for k, v in d.items():
375 6144f3d4 Stavros Sachtouris
                    self.set(CLOUD_PREFIX + ' "%s"' % r, k, v)
376 6144f3d4 Stavros Sachtouris
            self.remove_section(CLOUD_PREFIX)
377 6144f3d4 Stavros Sachtouris
378 6144f3d4 Stavros Sachtouris
            with open(self.path, 'w') as f:
379 6144f3d4 Stavros Sachtouris
                os.chmod(self.path, 0600)
380 6144f3d4 Stavros Sachtouris
                f.write(HEADER.lstrip())
381 6144f3d4 Stavros Sachtouris
                f.flush()
382 6144f3d4 Stavros Sachtouris
                RawConfigParser.write(self, f)
383 6144f3d4 Stavros Sachtouris
        finally:
384 6144f3d4 Stavros Sachtouris
            if CLOUD_PREFIX not in self.sections():
385 6144f3d4 Stavros Sachtouris
                self.add_section(CLOUD_PREFIX)
386 35d47a6c Stavros Sachtouris
            for cloud, d in cld_bu.items():
387 35d47a6c Stavros Sachtouris
                self.set(CLOUD_PREFIX, cloud, d)