Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / config.py @ 5482bc0a

History | View | Annotate | Download (6.3 kB)

1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import os
35
from logging import getLogger
36

    
37
from collections import defaultdict
38
from ConfigParser import RawConfigParser, NoOptionError, NoSectionError
39
from re import match
40

    
41
try:
42
    from collections import OrderedDict
43
except ImportError:
44
    from kamaki.clients.utils.ordereddict import OrderedDict
45

    
46

    
47
log = getLogger(__name__)
48

    
49
# Path to the file that stores the configuration
50
CONFIG_PATH = os.path.expanduser('~/.kamakirc')
51
HISTORY_PATH = os.path.expanduser('~/.kamaki.history')
52

    
53
# Name of a shell variable to bypass the CONFIG_PATH value
54
CONFIG_ENV = 'KAMAKI_CONFIG'
55

    
56
HEADER = """
57
# Kamaki configuration file v3 (kamaki >= v0.9)
58
"""
59

    
60
DEFAULTS = {
61
    'global': {
62
        'colors': 'off',
63
        'log_file': os.path.expanduser('~/.kamaki.log'),
64
        'log_token': 'off',
65
        'log_data': 'off',
66
        'max_threads': 7,
67
        'history_file': HISTORY_PATH,
68
        'user_cli': 'astakos',
69
        'file_cli': 'pithos',
70
        'server_cli': 'cyclades',
71
        'flavor_cli': 'cyclades',
72
        'network_cli': 'cyclades',
73
        'image_cli': 'image',
74
        'config_cli': 'config',
75
        'history_cli': 'history'
76
        #  Optional command specs:
77
        #  'livetest': 'livetest',
78
        #  'astakos': 'snf-astakos'
79
    },
80
    'remotes':
81
    {
82
        'default': {
83
            'url': '',
84
            'token': ''
85
            #'pithos_type': 'object-store',
86
            #'pithos_version': 'v1',
87
            #'cyclades_type': 'compute',
88
            #'cyclades_version': 'v2.0',
89
            #'plankton_type': 'image',
90
            #'plankton_version': '',
91
            #'astakos_type': 'identity',
92
            #'astakos_version': 'v2.0'
93
        }
94
    }
95
}
96

    
97

    
98
class Config(RawConfigParser):
99
    def __init__(self, path=None, with_defaults=True):
100
        RawConfigParser.__init__(self, dict_type=OrderedDict)
101
        self.path = path or os.environ.get(CONFIG_ENV, CONFIG_PATH)
102
        self._overrides = defaultdict(dict)
103
        if with_defaults:
104
            self._load_defaults()
105
        self.read(self.path)
106

    
107
    @staticmethod
108
    def _remote_name(full_section_name):
109
        matcher = match('remote "(\w+)"', full_section_name)
110
        return matcher.groups()[0] if matcher else None
111

    
112
    def guess_version(self):
113
        checker = Config(self.path, with_defaults=False)
114
        sections = checker.sections()
115
        log.warning('Config file heuristic 1: global section ?')
116
        v = 0.0
117
        if 'global' in sections:
118
            if checker.get('global', 'url') or checker.get('global', 'token'):
119
                log.warning('..... config file has an old global section')
120
                v = 2.0
121
        log.warning('Config file heuristic 2: at least 1 remote section ?')
122
        for section in sections:
123
            if self._remote_name(section):
124
                log.warning('... found %s section' % section)
125
                v = 3.0
126
        log.warning('All heuristics failed, cannot decide')
127
        del checker
128
        return v
129

    
130
    def _load_defaults(self):
131
        for section, options in DEFAULTS.items():
132
            for option, val in options.items():
133
                self.set(section, option, val)
134

    
135
    def _get_dict(self, section, include_defaults=True):
136
        try:
137
            d = dict(DEFAULTS[section]) if include_defaults else {}
138
        except KeyError:
139
            d = {}
140
        try:
141
            d.update(RawConfigParser.items(self, section))
142
        except NoSectionError:
143
            pass
144
        return d
145

    
146
    def reload(self):
147
        self = self.__init__(self.path)
148

    
149
    def get(self, section, option):
150
        value = self._overrides.get(section, {}).get(option)
151
        if value is not None:
152
            return value
153

    
154
        try:
155
            return RawConfigParser.get(self, section, option)
156
        except (NoSectionError, NoOptionError):
157
            return DEFAULTS.get(section, {}).get(option)
158

    
159
    def set(self, section, option, value):
160
        if section not in RawConfigParser.sections(self):
161
            self.add_section(section)
162
        RawConfigParser.set(self, section, option, value)
163

    
164
    def remove_option(self, section, option, also_remove_default=False):
165
        try:
166
            if also_remove_default:
167
                DEFAULTS[section].pop(option)
168
            RawConfigParser.remove_option(self, section, option)
169
        except NoSectionError:
170
            pass
171

    
172
    def keys(self, section, include_defaults=True):
173
        d = self._get_dict(section, include_defaults)
174
        return d.keys()
175

    
176
    def items(self, section, include_defaults=True):
177
        d = self._get_dict(section, include_defaults)
178
        return d.items()
179

    
180
    def override(self, section, option, value):
181
        self._overrides[section][option] = value
182

    
183
    def write(self):
184
        with open(self.path, 'w') as f:
185
            os.chmod(self.path, 0600)
186
            f.write(HEADER.lstrip())
187
            f.flush()
188
            RawConfigParser.write(self, f)