Statistics
| Branch: | Tag: | Revision:

root / kamaki / cli / config.py @ 3f0eae61

History | View | Annotate | Download (6.2 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
        if 'global' in sections:
117
            if checker.get('global', 'url') or checker.get('global', 'token'):
118
                log.warning('..... config file has an old global section')
119
                return 2.0
120
        log.warning('Config file heuristic 2: at least 1 remote section ?')
121
        for section in sections:
122
            if self._remote_name(section):
123
                log.warning('... found %s section' % section)
124
                return 3.0
125
        log.warning('All heuristics failed, cannot decide')
126
        return 0.0
127

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

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

    
144
    def reload(self):
145
        self = self.__init__(self.path)
146

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

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

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

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

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

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

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

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