Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / config.py @ f17d6cb5

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 a624a072 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 320aac17 Stavros Sachtouris
        #  'floating_cli': 'cyclades'
92 7493ccb6 Stavros Sachtouris
    },
93 6144f3d4 Stavros Sachtouris
    CLOUD_PREFIX:
94 3f0eae61 Stavros Sachtouris
    {
95 144b3551 Stavros Sachtouris
        #'default': {
96 144b3551 Stavros Sachtouris
        #    'url': '',
97 144b3551 Stavros Sachtouris
        #    'token': ''
98 144b3551 Stavros Sachtouris
        #    'pithos_type': 'object-store',
99 144b3551 Stavros Sachtouris
        #    'pithos_version': 'v1',
100 144b3551 Stavros Sachtouris
        #    'cyclades_type': 'compute',
101 144b3551 Stavros Sachtouris
        #    'cyclades_version': 'v2.0',
102 144b3551 Stavros Sachtouris
        #    'plankton_type': 'image',
103 144b3551 Stavros Sachtouris
        #    'plankton_version': '',
104 144b3551 Stavros Sachtouris
        #    'astakos_type': 'identity',
105 144b3551 Stavros Sachtouris
        #    'astakos_version': 'v2.0'
106 144b3551 Stavros Sachtouris
        #}
107 7493ccb6 Stavros Sachtouris
    }
108 7493ccb6 Stavros Sachtouris
}
109 7493ccb6 Stavros Sachtouris
110 7493ccb6 Stavros Sachtouris
111 e1c18867 Stavros Sachtouris
try:
112 e1c18867 Stavros Sachtouris
    import astakosclient
113 fa382f9e Stavros Sachtouris
    DEFAULTS['global'].update(dict(astakos_cli='snf-astakos'))
114 e1c18867 Stavros Sachtouris
except ImportError:
115 e1c18867 Stavros Sachtouris
    pass
116 e1c18867 Stavros Sachtouris
117 e1c18867 Stavros Sachtouris
118 7493ccb6 Stavros Sachtouris
class Config(RawConfigParser):
119 3f0eae61 Stavros Sachtouris
    def __init__(self, path=None, with_defaults=True):
120 7493ccb6 Stavros Sachtouris
        RawConfigParser.__init__(self, dict_type=OrderedDict)
121 7493ccb6 Stavros Sachtouris
        self.path = path or os.environ.get(CONFIG_ENV, CONFIG_PATH)
122 7493ccb6 Stavros Sachtouris
        self._overrides = defaultdict(dict)
123 3f0eae61 Stavros Sachtouris
        if with_defaults:
124 3f0eae61 Stavros Sachtouris
            self._load_defaults()
125 7493ccb6 Stavros Sachtouris
        self.read(self.path)
126 7493ccb6 Stavros Sachtouris
127 ce9ccb40 Stavros Sachtouris
        for section in self.sections():
128 144b3551 Stavros Sachtouris
            r = self._cloud_name(section)
129 ce9ccb40 Stavros Sachtouris
            if r:
130 ce9ccb40 Stavros Sachtouris
                for k, v in self.items(section):
131 144b3551 Stavros Sachtouris
                    self.set_cloud(r, k, v)
132 ce9ccb40 Stavros Sachtouris
                self.remove_section(section)
133 14b9a185 Stavros Sachtouris
134 3f0eae61 Stavros Sachtouris
    @staticmethod
135 144b3551 Stavros Sachtouris
    def _cloud_name(full_section_name):
136 29be4062 Stavros Sachtouris
        if not full_section_name.startswith(CLOUD_PREFIX + ' '):
137 29be4062 Stavros Sachtouris
            return None
138 a624a072 Stavros Sachtouris
        matcher = match(CLOUD_PREFIX + ' "([~@#$:\-\w]+)"', full_section_name)
139 29be4062 Stavros Sachtouris
        if matcher:
140 29be4062 Stavros Sachtouris
            return matcher.groups()[0]
141 29be4062 Stavros Sachtouris
        else:
142 29be4062 Stavros Sachtouris
            icn = full_section_name[len(CLOUD_PREFIX) + 1:]
143 29be4062 Stavros Sachtouris
            raise InvalidCloudNameError('Invalid Cloud Name %s' % icn)
144 3f0eae61 Stavros Sachtouris
145 14b9a185 Stavros Sachtouris
    def rescue_old_file(self):
146 ce9ccb40 Stavros Sachtouris
        lost_terms = []
147 ce9ccb40 Stavros Sachtouris
        global_terms = DEFAULTS['global'].keys()
148 ce9ccb40 Stavros Sachtouris
        translations = dict(
149 ce9ccb40 Stavros Sachtouris
            config=dict(serv='', cmd='config'),
150 ce9ccb40 Stavros Sachtouris
            history=dict(serv='', cmd='history'),
151 ce9ccb40 Stavros Sachtouris
            pithos=dict(serv='pithos', cmd='file'),
152 ce9ccb40 Stavros Sachtouris
            file=dict(serv='pithos', cmd='file'),
153 ce9ccb40 Stavros Sachtouris
            store=dict(serv='pithos', cmd='file'),
154 ce9ccb40 Stavros Sachtouris
            storage=dict(serv='pithos', cmd='file'),
155 ce9ccb40 Stavros Sachtouris
            image=dict(serv='plankton', cmd='image'),
156 ce9ccb40 Stavros Sachtouris
            plankton=dict(serv='plankton', cmd='image'),
157 ce9ccb40 Stavros Sachtouris
            compute=dict(serv='compute', cmd=''),
158 ce9ccb40 Stavros Sachtouris
            cyclades=dict(serv='compute', cmd='server'),
159 ce9ccb40 Stavros Sachtouris
            server=dict(serv='compute', cmd='server'),
160 ce9ccb40 Stavros Sachtouris
            flavor=dict(serv='compute', cmd='flavor'),
161 ce9ccb40 Stavros Sachtouris
            network=dict(serv='compute', cmd='network'),
162 ce9ccb40 Stavros Sachtouris
            astakos=dict(serv='astakos', cmd='user'),
163 ce9ccb40 Stavros Sachtouris
            user=dict(serv='astakos', cmd='user'),
164 ce9ccb40 Stavros Sachtouris
        )
165 ce9ccb40 Stavros Sachtouris
166 6144f3d4 Stavros Sachtouris
        self.set('global', 'default_' + CLOUD_PREFIX, 'default')
167 ce9ccb40 Stavros Sachtouris
        for s in self.sections():
168 ce9ccb40 Stavros Sachtouris
            if s in ('global'):
169 ce9ccb40 Stavros Sachtouris
                # global.url, global.token -->
170 144b3551 Stavros Sachtouris
                # cloud.default.url, cloud.default.token
171 ce9ccb40 Stavros Sachtouris
                for term in set(self.keys(s)).difference(global_terms):
172 ce9ccb40 Stavros Sachtouris
                    if term not in ('url', 'token'):
173 ce9ccb40 Stavros Sachtouris
                        lost_terms.append('%s.%s = %s' % (
174 ce9ccb40 Stavros Sachtouris
                            s, term, self.get(s, term)))
175 ce9ccb40 Stavros Sachtouris
                        self.remove_option(s, term)
176 ce9ccb40 Stavros Sachtouris
                        continue
177 ce9ccb40 Stavros Sachtouris
                    gval = self.get(s, term)
178 c825ddc9 Stavros Sachtouris
                    try:
179 c825ddc9 Stavros Sachtouris
                        cval = self.get_cloud('default', term)
180 c825ddc9 Stavros Sachtouris
                    except KeyError:
181 c825ddc9 Stavros Sachtouris
                        cval = ''
182 ce9ccb40 Stavros Sachtouris
                    if gval and cval and (
183 ce9ccb40 Stavros Sachtouris
                        gval.lower().strip('/') != cval.lower().strip('/')):
184 ce9ccb40 Stavros Sachtouris
                            raise CLISyntaxError(
185 ce9ccb40 Stavros Sachtouris
                                'Conflicting values for default %s' % term,
186 ce9ccb40 Stavros Sachtouris
                                importance=2, details=[
187 ce9ccb40 Stavros Sachtouris
                                    ' global.%s:  %s' % (term, gval),
188 6144f3d4 Stavros Sachtouris
                                    ' %s.default.%s:  %s' % (
189 6144f3d4 Stavros Sachtouris
                                        CLOUD_PREFIX, term, cval),
190 ce9ccb40 Stavros Sachtouris
                                    'Please remove one of them manually:',
191 ce9ccb40 Stavros Sachtouris
                                    ' /config delete global.%s' % term,
192 ce9ccb40 Stavros Sachtouris
                                    ' or'
193 6144f3d4 Stavros Sachtouris
                                    ' /config delete %s.default.%s' % (
194 6144f3d4 Stavros Sachtouris
                                        CLOUD_PREFIX, term),
195 ce9ccb40 Stavros Sachtouris
                                    'and try again'])
196 ce9ccb40 Stavros Sachtouris
                    elif gval:
197 6144f3d4 Stavros Sachtouris
                        print('... rescue %s.%s => %s.default.%s' % (
198 6144f3d4 Stavros Sachtouris
                            s, term, CLOUD_PREFIX, term))
199 144b3551 Stavros Sachtouris
                        self.set_cloud('default', term, gval)
200 ce9ccb40 Stavros Sachtouris
                    self.remove_option(s, term)
201 ce9ccb40 Stavros Sachtouris
            # translation for <service> or <command> settings
202 ce9ccb40 Stavros Sachtouris
            # <service> or <command group> settings --> translation --> global
203 ce9ccb40 Stavros Sachtouris
            elif s in translations:
204 ce9ccb40 Stavros Sachtouris
205 ce9ccb40 Stavros Sachtouris
                if s in ('history',):
206 ce9ccb40 Stavros Sachtouris
                    k = 'file'
207 ce9ccb40 Stavros Sachtouris
                    v = self.get(s, k)
208 ce9ccb40 Stavros Sachtouris
                    if v:
209 ce9ccb40 Stavros Sachtouris
                        print('... rescue %s.%s => global.%s_%s' % (
210 ce9ccb40 Stavros Sachtouris
                            s, k, s, k))
211 ce9ccb40 Stavros Sachtouris
                        self.set('global', '%s_%s' % (s, k), v)
212 ce9ccb40 Stavros Sachtouris
                        self.remove_option(s, k)
213 ce9ccb40 Stavros Sachtouris
214 ce9ccb40 Stavros Sachtouris
                trn = translations[s]
215 ce9ccb40 Stavros Sachtouris
                for k, v in self.items(s, False):
216 ce9ccb40 Stavros Sachtouris
                    if v and k in ('cli',):
217 ce9ccb40 Stavros Sachtouris
                        print('... rescue %s.%s => global.%s_cli' % (
218 ce9ccb40 Stavros Sachtouris
                            s, k, trn['cmd']))
219 144b3551 Stavros Sachtouris
                        self.set('global', '%s_cli' % trn['cmd'], v)
220 144b3551 Stavros Sachtouris
                    elif k in ('container',) and trn['serv'] in ('pithos',):
221 6144f3d4 Stavros Sachtouris
                        print('... rescue %s.%s => %s.default.pithos_%s' % (
222 6144f3d4 Stavros Sachtouris
                                    s, k, CLOUD_PREFIX, k))
223 144b3551 Stavros Sachtouris
                        self.set_cloud('default', 'pithos_%s' % k, v)
224 144b3551 Stavros Sachtouris
                    else:
225 ce9ccb40 Stavros Sachtouris
                        lost_terms.append('%s.%s = %s' % (s, k, v))
226 ce9ccb40 Stavros Sachtouris
                self.remove_section(s)
227 ce9ccb40 Stavros Sachtouris
        #  self.pretty_print()
228 ce9ccb40 Stavros Sachtouris
        return lost_terms
229 ce9ccb40 Stavros Sachtouris
230 ce9ccb40 Stavros Sachtouris
    def pretty_print(self):
231 ce9ccb40 Stavros Sachtouris
        for s in self.sections():
232 ce9ccb40 Stavros Sachtouris
            print s
233 ce9ccb40 Stavros Sachtouris
            for k, v in self.items(s):
234 ce9ccb40 Stavros Sachtouris
                if isinstance(v, dict):
235 ce9ccb40 Stavros Sachtouris
                    print '\t', k, '=> {'
236 ce9ccb40 Stavros Sachtouris
                    for ki, vi in v.items():
237 ce9ccb40 Stavros Sachtouris
                        print '\t\t', ki, '=>', vi
238 ce9ccb40 Stavros Sachtouris
                    print('\t}')
239 ce9ccb40 Stavros Sachtouris
                else:
240 ce9ccb40 Stavros Sachtouris
                    print '\t', k, '=>', v
241 14b9a185 Stavros Sachtouris
242 3f0eae61 Stavros Sachtouris
    def guess_version(self):
243 144b3551 Stavros Sachtouris
        """
244 144b3551 Stavros Sachtouris
        :returns: (float) version of the config file or 0.0 if unrecognized
245 144b3551 Stavros Sachtouris
        """
246 3f0eae61 Stavros Sachtouris
        checker = Config(self.path, with_defaults=False)
247 3f0eae61 Stavros Sachtouris
        sections = checker.sections()
248 144b3551 Stavros Sachtouris
        log.warning('Config file heuristic 1: old global section ?')
249 3f0eae61 Stavros Sachtouris
        if 'global' in sections:
250 3f0eae61 Stavros Sachtouris
            if checker.get('global', 'url') or checker.get('global', 'token'):
251 3f0eae61 Stavros Sachtouris
                log.warning('..... config file has an old global section')
252 c825ddc9 Stavros Sachtouris
                return 0.8
253 14b9a185 Stavros Sachtouris
        log.warning('........ nope')
254 df0045d8 Stavros Sachtouris
        log.warning('Config file heuristic 2: Any cloud sections ?')
255 6144f3d4 Stavros Sachtouris
        if CLOUD_PREFIX in sections:
256 6144f3d4 Stavros Sachtouris
            for r in self.keys(CLOUD_PREFIX):
257 144b3551 Stavros Sachtouris
                log.warning('... found cloud "%s"' % r)
258 c825ddc9 Stavros Sachtouris
                return 0.9
259 df0045d8 Stavros Sachtouris
        log.warning('........ nope')
260 3f0eae61 Stavros Sachtouris
        log.warning('All heuristics failed, cannot decide')
261 df0045d8 Stavros Sachtouris
        return 0.9
262 3f0eae61 Stavros Sachtouris
263 144b3551 Stavros Sachtouris
    def get_cloud(self, cloud, option):
264 ce9ccb40 Stavros Sachtouris
        """
265 144b3551 Stavros Sachtouris
        :param cloud: (str) cloud alias
266 ce9ccb40 Stavros Sachtouris

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

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

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

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

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

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

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