1#    Copyright 2014 Rackspace
2#
3#    Licensed under the Apache License, Version 2.0 (the "License"); you may
4#    not use this file except in compliance with the License. You may obtain
5#    a copy of the License at
6#
7#         http://www.apache.org/licenses/LICENSE-2.0
8#
9#    Unless required by applicable law or agreed to in writing, software
10#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12#    License for the specific language governing permissions and limitations
13#    under the License.
14
15import logging
16import sys
17
18from oslo_config import cfg
19from six.moves import configparser
20
21from glance_store import exceptions
22from glance_store.i18n import _, _LE
23
24swift_opts = [
25    cfg.StrOpt('default_swift_reference',
26               default="ref1",
27               help="""
28Reference to default Swift account/backing store parameters.
29
30Provide a string value representing a reference to the default set
31of parameters required for using swift account/backing store for
32image storage. The default reference value for this configuration
33option is 'ref1'. This configuration option dereferences the
34parameters and facilitates image storage in Swift storage backend
35every time a new image is added.
36
37Possible values:
38    * A valid string value
39
40Related options:
41    * None
42
43"""),
44    cfg.StrOpt('swift_store_auth_version', default='2',
45               help='Version of the authentication service to use. '
46                    'Valid versions are 2 and 3 for keystone and 1 '
47                    '(deprecated) for swauth and rackspace.',
48               deprecated_for_removal=True,
49               deprecated_reason="""
50The option 'auth_version' in the Swift back-end configuration file is
51used instead.
52"""),
53    cfg.StrOpt('swift_store_auth_address',
54               help='The address where the Swift authentication '
55                    'service is listening.',
56               deprecated_for_removal=True,
57               deprecated_reason="""
58The option 'auth_address' in the Swift back-end configuration file is
59used instead.
60"""),
61    cfg.StrOpt('swift_store_user', secret=True,
62               help='The user to authenticate against the Swift '
63                    'authentication service.',
64               deprecated_for_removal=True,
65               deprecated_reason="""
66The option 'user' in the Swift back-end configuration file is set instead.
67"""),
68    cfg.StrOpt('swift_store_key', secret=True,
69               help='Auth key for the user authenticating against the '
70                    'Swift authentication service.',
71               deprecated_for_removal=True,
72               deprecated_reason="""
73The option 'key' in the Swift back-end configuration file is used
74to set the authentication key instead.
75"""),
76    cfg.StrOpt('swift_store_config_file',
77               default=None,
78               help="""
79Absolute path to the file containing the swift account(s)
80configurations.
81
82Include a string value representing the path to a configuration
83file that has references for each of the configured Swift
84account(s)/backing stores. By default, no file path is specified
85and customized Swift referencing is disabled. Configuring this
86option is highly recommended while using Swift storage backend for
87image storage as it avoids storage of credentials in the database.
88
89NOTE: Please do not configure this option if you have set
90``swift_store_multi_tenant`` to ``True``.
91
92Possible values:
93    * String value representing an absolute path on the glance-api
94      node
95
96Related options:
97    * swift_store_multi_tenant
98
99"""),
100]
101
102_config_defaults = {'user_domain_id': 'default',
103                    'user_domain_name': 'default',
104                    'project_domain_id': 'default',
105                    'project_domain_name': 'default'}
106
107if sys.version_info >= (3, 2):
108    parser_class = configparser.ConfigParser
109else:
110    parser_class = configparser.SafeConfigParser
111
112
113class SwiftConfigParser(parser_class):
114
115    def get(self, *args, **kwargs):
116        value = super(parser_class, self).get(*args, **kwargs)
117        return self._process_quotes(value)
118
119    @staticmethod
120    def _process_quotes(value):
121        if value:
122            if value[0] in "\"'":
123                if len(value) == 1 or value[-1] != value[0]:
124                    raise ValueError('Non-closed quote: %s' %
125                                     value)
126                value = value[1:-1]
127        return value
128
129
130if sys.version_info >= (3,):
131    CONFIG = SwiftConfigParser(defaults=_config_defaults)
132else:
133    CONFIG = parser_class(defaults=_config_defaults)
134
135LOG = logging.getLogger(__name__)
136
137
138def is_multiple_swift_store_accounts_enabled(conf, backend=None):
139    if backend:
140        cfg_file = getattr(conf, backend).swift_store_config_file
141    else:
142        cfg_file = conf.glance_store.swift_store_config_file
143
144    if cfg_file is None:
145        return False
146    return True
147
148
149class SwiftParams(object):
150    def __init__(self, conf, backend=None):
151        self.conf = conf
152        self.backend_group = backend
153        if is_multiple_swift_store_accounts_enabled(
154                self.conf, backend=backend):
155            self.params = self._load_config()
156        else:
157            self.params = self._form_default_params()
158
159    def _form_default_params(self):
160        default = {}
161        if self.backend_group:
162            glance_store = getattr(self.conf, self.backend_group)
163        else:
164            glance_store = self.conf.glance_store
165        if (
166                glance_store.swift_store_user and
167                glance_store.swift_store_key and
168                glance_store.swift_store_auth_address
169        ):
170
171            default['user'] = glance_store.swift_store_user
172            default['key'] = glance_store.swift_store_key
173            default['auth_address'] = glance_store.swift_store_auth_address
174            default['project_domain_id'] = 'default'
175            default['project_domain_name'] = None
176            default['user_domain_id'] = 'default'
177            default['user_domain_name'] = None
178            default['auth_version'] = glance_store.swift_store_auth_version
179            return {glance_store.default_swift_reference: default}
180        return {}
181
182    def _load_config(self):
183        if self.backend_group:
184            scf = getattr(self.conf,
185                          self.backend_group).swift_store_config_file
186        else:
187            scf = self.conf.glance_store.swift_store_config_file
188        try:
189            conf_file = self.conf.find_file(scf)
190            CONFIG.read(conf_file)
191        except Exception as e:
192            msg = (_("swift config file "
193                     "%(conf)s:%(exc)s not found"),
194                   {'conf': scf,
195                    'exc': e})
196            LOG.error(msg)
197            raise exceptions.BadStoreConfiguration(store_name='swift',
198                                                   reason=msg)
199        account_params = {}
200        account_references = CONFIG.sections()
201
202        for ref in account_references:
203            reference = {}
204            try:
205                for param in ('auth_address',
206                              'user',
207                              'key',
208                              'project_domain_id',
209                              'project_domain_name',
210                              'user_domain_id',
211                              'user_domain_name'):
212                    reference[param] = CONFIG.get(ref, param)
213
214                try:
215                    reference['auth_version'] = CONFIG.get(ref, 'auth_version')
216                except configparser.NoOptionError:
217                    if self.backend_group:
218                        av = getattr(
219                            self.conf,
220                            self.backend_group).swift_store_auth_version
221                    else:
222                        av = self.conf.glance_store.swift_store_auth_version
223                    reference['auth_version'] = av
224
225                account_params[ref] = reference
226            except (ValueError, SyntaxError, configparser.NoOptionError):
227                LOG.exception(_LE("Invalid format of swift store config cfg"))
228        return account_params
229