Merge branch 'develop' into features/astakos
[kamaki] / kamaki / cli / config.py
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
36 from collections import defaultdict
37 from ConfigParser import RawConfigParser, NoOptionError, NoSectionError
38
39 try:
40     from collections import OrderedDict
41 except ImportError:
42     from ordereddict import OrderedDict
43
44
45 # Path to the file that stores the configuration
46 CONFIG_PATH = os.path.expanduser('~/.kamakirc')
47 HISTORY_PATH = os.path.expanduser('~/.kamaki.history')
48
49 # Name of a shell variable to bypass the CONFIG_PATH value
50 CONFIG_ENV = 'KAMAKI_CONFIG'
51
52 HEADER = """
53 # Kamaki configuration file
54 """
55
56 DEFAULTS = {
57     'global': {
58         'colors': 'off',
59         'token': ''
60     },
61     'config': {
62         'cli': 'config_cli',
63         'description': 'Configuration commands'
64     },
65     'history': {
66         'cli': 'history_cli',
67         'file': HISTORY_PATH
68     },
69     'store': {
70         'cli': 'pithos_cli',
71         'url': 'https://pithos.okeanos.grnet.gr/v1'
72     },
73     'compute': {
74         'url': 'https://cyclades.okeanos.grnet.gr/api/v1.1'
75     },
76     'server': {
77         'cli': 'cyclades_cli'
78     },
79     'flavor': {
80         'cli': 'cyclades_cli'
81     },
82     'network': {
83         'cli': 'cyclades_cli'
84     },
85     'image': {
86         'cli': 'image_cli',
87         'url': 'https://cyclades.okeanos.grnet.gr/plankton'
88     },
89     'astakos': {
90         'cli': 'astakos_cli',
91         'url': 'https://accounts.okeanos.grnet.gr'
92     }
93 }
94
95
96 class Config(RawConfigParser):
97     def __init__(self, path=None):
98         RawConfigParser.__init__(self, dict_type=OrderedDict)
99         self.path = path or os.environ.get(CONFIG_ENV, CONFIG_PATH)
100         self._overrides = defaultdict(dict)
101         self._load_defaults()
102         self.read(self.path)
103
104     def _load_defaults(self):
105         for section, options in DEFAULTS.items():
106             for option, val in options.items():
107                 self.set(section, option, val)
108
109     def reload(self):
110         self = self.__init__(self.path)
111
112     def apis(self):
113         return [api for api in self.sections() if api != 'global']
114
115     def get(self, section, option):
116         value = self._overrides.get(section, {}).get(option)
117         if value is not None:
118             return value
119
120         try:
121             return RawConfigParser.get(self, section, option)
122         except (NoSectionError, NoOptionError):
123             return DEFAULTS.get(section, {}).get(option)
124
125     def set(self, section, option, value):
126         if section not in RawConfigParser.sections(self):
127             self.add_section(section)
128         RawConfigParser.set(self, section, option, value)
129
130     def remove_option(self, section, option):
131         try:
132             RawConfigParser.remove_option(self, section, option)
133         except NoSectionError:
134             pass
135
136     def items(self, section, include_defaults=False):
137         try:
138             d = dict(DEFAULTS[section]) if include_defaults else {}
139         except KeyError:
140             d = {}
141         try:
142             d.update(RawConfigParser.items(self, section))
143         except NoSectionError:
144             pass
145         return d.items()
146
147     def override(self, section, option, value):
148         self._overrides[section][option] = value
149
150     def write(self):
151         with open(self.path, 'w') as f:
152             os.chmod(self.path, 0600)
153             f.write(HEADER.lstrip())
154             f.flush()
155             RawConfigParser.write(self, f)