summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2018-11-27 03:38:15 +0000
committerGerrit Code Review <review@openstack.org>2018-11-27 03:38:15 +0000
commite8fa9e3272025f53984d76065ad78ff1b02a5237 (patch)
tree10bb88ed633ab09d8f9e633ae9044425d776223a
parent7e63503ebec8b31b0b47925126b4cc39707a7633 (diff)
parentedd6810c3ddfa9b23febda7d6198bd1089ab3f2f (diff)
Merge "Wrap Flask into oslo.service"
-rw-r--r--ironic_inspector/cmd/all.py12
-rw-r--r--ironic_inspector/common/service_utils.py6
-rw-r--r--ironic_inspector/conf/default.py8
-rw-r--r--ironic_inspector/test/unit/test_wsgi_service.py187
-rw-r--r--ironic_inspector/wsgi_service.py101
-rw-r--r--lower-constraints.txt2
-rw-r--r--releasenotes/notes/deprecate-ssl-opts-40ce8f4618c786ef.yaml7
-rw-r--r--requirements.txt1
-rw-r--r--tools/config-generator.conf5
9 files changed, 94 insertions, 235 deletions
diff --git a/ironic_inspector/cmd/all.py b/ironic_inspector/cmd/all.py
index c300cca..c88459b 100644
--- a/ironic_inspector/cmd/all.py
+++ b/ironic_inspector/cmd/all.py
@@ -14,16 +14,24 @@
14 14
15import sys 15import sys
16 16
17from oslo_config import cfg
18from oslo_service import service
19
20from ironic_inspector.common.rpc_service import RPCService
17from ironic_inspector.common import service_utils 21from ironic_inspector.common import service_utils
18from ironic_inspector import wsgi_service 22from ironic_inspector import wsgi_service
19 23
24CONF = cfg.CONF
25
20 26
21def main(args=sys.argv[1:]): 27def main(args=sys.argv[1:]):
22 # Parse config file and command line options, then start logging 28 # Parse config file and command line options, then start logging
23 service_utils.prepare_service(args) 29 service_utils.prepare_service(args)
24 30
25 server = wsgi_service.WSGIService() 31 launcher = service.ServiceLauncher(CONF, restart_method='mutate')
26 server.run() 32 launcher.launch_service(wsgi_service.WSGIService())
33 launcher.launch_service(RPCService(CONF.host))
34 launcher.wait()
27 35
28 36
29if __name__ == '__main__': 37if __name__ == '__main__':
diff --git a/ironic_inspector/common/service_utils.py b/ironic_inspector/common/service_utils.py
index ba09428..2f37cb9 100644
--- a/ironic_inspector/common/service_utils.py
+++ b/ironic_inspector/common/service_utils.py
@@ -12,6 +12,7 @@
12 12
13from oslo_config import cfg 13from oslo_config import cfg
14from oslo_log import log 14from oslo_log import log
15from oslo_service import sslutils
15 16
16from ironic_inspector.conf import opts 17from ironic_inspector.conf import opts
17 18
@@ -26,5 +27,10 @@ def prepare_service(args=None):
26 opts.parse_args(args) 27 opts.parse_args(args)
27 log.setup(CONF, 'ironic_inspector') 28 log.setup(CONF, 'ironic_inspector')
28 29
30 # TODO(kaifeng) Remove deprecated options at T* cycle.
31 sslutils.register_opts(CONF)
32 CONF.set_default('cert_file', CONF.ssl_cert_path, group='ssl')
33 CONF.set_default('key_file', CONF.ssl_key_path, group='ssl')
34
29 LOG.debug("Configuration:") 35 LOG.debug("Configuration:")
30 CONF.log_opt_values(LOG, log.DEBUG) 36 CONF.log_opt_values(LOG, log.DEBUG)
diff --git a/ironic_inspector/conf/default.py b/ironic_inspector/conf/default.py
index ef2b9c2..5567fb5 100644
--- a/ironic_inspector/conf/default.py
+++ b/ironic_inspector/conf/default.py
@@ -52,9 +52,15 @@ _OPTS = [
52 help=_('SSL Enabled/Disabled')), 52 help=_('SSL Enabled/Disabled')),
53 cfg.StrOpt('ssl_cert_path', 53 cfg.StrOpt('ssl_cert_path',
54 default='', 54 default='',
55 deprecated_for_removal=True,
56 deprecated_reason=_('This option will be superseded by '
57 '[ssl]cert_file.'),
55 help=_('Path to SSL certificate')), 58 help=_('Path to SSL certificate')),
56 cfg.StrOpt('ssl_key_path', 59 cfg.StrOpt('ssl_key_path',
57 default='', 60 default='',
61 deprecated_for_removal=True,
62 deprecated_reason=_('This option will be superseded by '
63 '[ssl]key_file.'),
58 help=_('Path to SSL key')), 64 help=_('Path to SSL key')),
59 cfg.IntOpt('max_concurrency', 65 cfg.IntOpt('max_concurrency',
60 default=1000, min=2, 66 default=1000, min=2,
@@ -78,7 +84,7 @@ _OPTS = [
78 help=_('Whether the current installation of ironic-inspector ' 84 help=_('Whether the current installation of ironic-inspector '
79 'can manage PXE booting of nodes. If set to False, ' 85 'can manage PXE booting of nodes. If set to False, '
80 'the API will reject introspection requests with ' 86 'the API will reject introspection requests with '
81 'manage_boot missing or set to True.')) 87 'manage_boot missing or set to True.')),
82] 88]
83 89
84 90
diff --git a/ironic_inspector/test/unit/test_wsgi_service.py b/ironic_inspector/test/unit/test_wsgi_service.py
index 3cb83db..7f39c55 100644
--- a/ironic_inspector/test/unit/test_wsgi_service.py
+++ b/ironic_inspector/test/unit/test_wsgi_service.py
@@ -11,15 +11,12 @@
11# See the License for the specific language governing permissions and 11# See the License for the specific language governing permissions and
12# limitations under the License. 12# limitations under the License.
13 13
14import ssl
15import sys
16import unittest
17
18import eventlet # noqa 14import eventlet # noqa
19import fixtures 15import fixtures
20import mock 16import mock
21from oslo_config import cfg 17from oslo_config import cfg
22 18
19from ironic_inspector.common import service_utils
23from ironic_inspector.test import base as test_base 20from ironic_inspector.test import base as test_base
24from ironic_inspector import wsgi_service 21from ironic_inspector import wsgi_service
25 22
@@ -32,9 +29,12 @@ class BaseWSGITest(test_base.BaseTest):
32 super(BaseWSGITest, self).setUp() 29 super(BaseWSGITest, self).setUp()
33 self.app = self.useFixture(fixtures.MockPatchObject( 30 self.app = self.useFixture(fixtures.MockPatchObject(
34 wsgi_service.app, 'app', autospec=True)).mock 31 wsgi_service.app, 'app', autospec=True)).mock
32 self.server = self.useFixture(fixtures.MockPatchObject(
33 wsgi_service.wsgi, 'Server', autospec=True)).mock
35 self.mock_log = self.useFixture(fixtures.MockPatchObject( 34 self.mock_log = self.useFixture(fixtures.MockPatchObject(
36 wsgi_service, 'LOG')).mock 35 wsgi_service, 'LOG')).mock
37 self.service = wsgi_service.WSGIService() 36 self.service = wsgi_service.WSGIService()
37 self.service.server = self.server
38 38
39 39
40class TestWSGIServiceInitMiddleware(BaseWSGITest): 40class TestWSGIServiceInitMiddleware(BaseWSGITest):
@@ -66,174 +66,55 @@ class TestWSGIServiceInitMiddleware(BaseWSGITest):
66 self.mock_add_cors_middleware.assert_called_once_with(self.app) 66 self.mock_add_cors_middleware.assert_called_once_with(self.app)
67 67
68 68
69class TestWSGIServiceRun(BaseWSGITest): 69class TestWSGIService(BaseWSGITest):
70 def setUp(self): 70 def setUp(self):
71 super(TestWSGIServiceRun, self).setUp() 71 super(TestWSGIService, self).setUp()
72 self.mock__init_middleware = self.useFixture(fixtures.MockPatchObject( 72 self.mock__init_middleware = self.useFixture(fixtures.MockPatchObject(
73 self.service, '_init_middleware')).mock 73 self.service, '_init_middleware')).mock
74 self.mock__create_ssl_context = self.useFixture(
75 fixtures.MockPatchObject(self.service, '_create_ssl_context')).mock
76 self.mock_shutdown = self.useFixture(fixtures.MockPatchObject(
77 self.service, 'shutdown')).mock
78 74
79 # 'positive' settings 75 # 'positive' settings
80 CONF.set_override('listen_address', '42.42.42.42') 76 CONF.set_override('listen_address', '42.42.42.42')
81 CONF.set_override('listen_port', 42) 77 CONF.set_override('listen_port', 42)
82 78
83 def test_run(self): 79 def test_start(self):
84 self.service.run() 80 self.service.start()
85 81
86 self.mock__create_ssl_context.assert_called_once_with()
87 self.mock__init_middleware.assert_called_once_with() 82 self.mock__init_middleware.assert_called_once_with()
88 self.app.run.assert_called_once_with( 83 self.server.start.assert_called_once_with()
89 host=CONF.listen_address, port=CONF.listen_port,
90 ssl_context=self.mock__create_ssl_context.return_value)
91 self.mock_shutdown.assert_called_once_with()
92 84
93 def test_run_no_ssl_context(self): 85 def test_stop(self):
94 self.mock__create_ssl_context.return_value = None 86 self.service.stop()
87 self.server.stop.assert_called_once_with()
95 88
96 self.service.run() 89 def test_wait(self):
97 self.mock__create_ssl_context.assert_called_once_with() 90 self.service.wait()
98 self.mock__init_middleware.assert_called_once_with() 91 self.server.wait.assert_called_once_with()
99 self.app.run.assert_called_once_with(
100 host=CONF.listen_address, port=CONF.listen_port)
101 self.mock_shutdown.assert_called_once_with()
102 92
103 def test_run_app_error(self): 93 def test_reset(self):
104 class MyError(Exception): 94 self.service.reset()
105 pass 95 self.server.reset.assert_called_once_with()
106 96
107 error = MyError('Oops!')
108 self.app.run.side_effect = error
109 self.service.run()
110 97
111 self.mock__create_ssl_context.assert_called_once_with() 98@mock.patch.object(service_utils.log, 'register_options', autospec=True)
112 self.mock__init_middleware.assert_called_once_with() 99class TestSSLOptions(test_base.BaseTest):
113 self.app.run.assert_called_once_with(
114 host=CONF.listen_address, port=CONF.listen_port,
115 ssl_context=self.mock__create_ssl_context.return_value)
116 self.mock_shutdown.assert_called_once_with(error=str(error))
117 100
101 def test_use_deprecated_options(self, mock_log):
102 CONF.set_override('ssl_cert_path', 'fake_cert_file')
103 CONF.set_override('ssl_key_path', 'fake_key_file')
118 104
119class TestWSGIServiceShutdown(BaseWSGITest): 105 service_utils.prepare_service()
120 def setUp(self):
121 super(TestWSGIServiceShutdown, self).setUp()
122 self.service = wsgi_service.WSGIService()
123 self.mock_rpc_service = mock.MagicMock()
124 self.service.rpc_service = self.mock_rpc_service
125 self.mock_exit = self.useFixture(fixtures.MockPatchObject(
126 wsgi_service.sys, 'exit')).mock
127 106
128 def test_shutdown(self): 107 self.assertEqual(CONF.ssl.cert_file, 'fake_cert_file')
129 class MyError(Exception): 108 self.assertEqual(CONF.ssl.key_file, 'fake_key_file')
130 pass
131 error = MyError('Oops!')
132 109
133 self.service.shutdown(error=error) 110 def test_use_ssl_options(self, mock_log):
134 self.mock_rpc_service.stop.assert_called_once_with() 111 CONF.set_override('ssl_cert_path', 'fake_cert_file')
135 self.mock_exit.assert_called_once_with(error) 112 CONF.set_override('ssl_key_path', 'fake_key_file')
136 113
114 service_utils.prepare_service()
137 115
138class TestCreateSSLContext(test_base.BaseTest): 116 CONF.set_override('cert_file', 'fake_new_cert', 'ssl')
139 def setUp(self): 117 CONF.set_override('key_file', 'fake_new_key', 'ssl')
140 super(TestCreateSSLContext, self).setUp()
141 self.app = mock.Mock()
142 self.service = wsgi_service.WSGIService()
143 118
144 def test_use_ssl_false(self): 119 self.assertEqual(CONF.ssl.cert_file, 'fake_new_cert')
145 CONF.set_override('use_ssl', False) 120 self.assertEqual(CONF.ssl.key_file, 'fake_new_key')
146 con = self.service._create_ssl_context()
147 self.assertIsNone(con)
148
149 @mock.patch.object(sys, 'version_info')
150 def test_old_python_returns_none(self, mock_version_info):
151 mock_version_info.__lt__.return_value = True
152 CONF.set_override('use_ssl', True)
153 con = self.service._create_ssl_context()
154 self.assertIsNone(con)
155
156 @unittest.skipIf(sys.version_info[:3] < (2, 7, 9),
157 'This feature is unsupported in this version of python '
158 'so the tests will be skipped')
159 @mock.patch.object(ssl, 'create_default_context', autospec=True)
160 def test_use_ssl_true(self, mock_cdc):
161 CONF.set_override('use_ssl', True)
162 m_con = mock_cdc()
163 con = self.service._create_ssl_context()
164 self.assertEqual(m_con, con)
165
166 @unittest.skipIf(sys.version_info[:3] < (2, 7, 9),
167 'This feature is unsupported in this version of python '
168 'so the tests will be skipped')
169 @mock.patch.object(ssl, 'create_default_context', autospec=True)
170 def test_only_key_path_provided(self, mock_cdc):
171 CONF.set_override('use_ssl', True)
172 CONF.set_override('ssl_key_path', '/some/fake/path')
173 mock_context = mock_cdc()
174 con = self.service._create_ssl_context()
175 self.assertEqual(mock_context, con)
176 self.assertFalse(mock_context.load_cert_chain.called)
177
178 @unittest.skipIf(sys.version_info[:3] < (2, 7, 9),
179 'This feature is unsupported in this version of python '
180 'so the tests will be skipped')
181 @mock.patch.object(ssl, 'create_default_context', autospec=True)
182 def test_only_cert_path_provided(self, mock_cdc):
183 CONF.set_override('use_ssl', True)
184 CONF.set_override('ssl_cert_path', '/some/fake/path')
185 mock_context = mock_cdc()
186 con = self.service._create_ssl_context()
187 self.assertEqual(mock_context, con)
188 self.assertFalse(mock_context.load_cert_chain.called)
189
190 @unittest.skipIf(sys.version_info[:3] < (2, 7, 9),
191 'This feature is unsupported in this version of python '
192 'so the tests will be skipped')
193 @mock.patch.object(ssl, 'create_default_context', autospec=True)
194 def test_both_paths_provided(self, mock_cdc):
195 key_path = '/some/fake/path/key'
196 cert_path = '/some/fake/path/cert'
197 CONF.set_override('use_ssl', True)
198 CONF.set_override('ssl_key_path', key_path)
199 CONF.set_override('ssl_cert_path', cert_path)
200 mock_context = mock_cdc()
201 con = self.service._create_ssl_context()
202 self.assertEqual(mock_context, con)
203 mock_context.load_cert_chain.assert_called_once_with(cert_path,
204 key_path)
205
206 @unittest.skipIf(sys.version_info[:3] < (2, 7, 9),
207 'This feature is unsupported in this version of python '
208 'so the tests will be skipped')
209 @mock.patch.object(ssl, 'create_default_context', autospec=True)
210 def test_load_cert_chain_fails(self, mock_cdc):
211 CONF.set_override('use_ssl', True)
212 key_path = '/some/fake/path/key'
213 cert_path = '/some/fake/path/cert'
214 CONF.set_override('use_ssl', True)
215 CONF.set_override('ssl_key_path', key_path)
216 CONF.set_override('ssl_cert_path', cert_path)
217 mock_context = mock_cdc()
218 mock_context.load_cert_chain.side_effect = IOError('Boom!')
219 con = self.service._create_ssl_context()
220 self.assertEqual(mock_context, con)
221 mock_context.load_cert_chain.assert_called_once_with(cert_path,
222 key_path)
223
224
225class TestWSGIServiceOnSigHup(BaseWSGITest):
226 def setUp(self):
227 super(TestWSGIServiceOnSigHup, self).setUp()
228 self.mock_spawn = self.useFixture(fixtures.MockPatchObject(
229 wsgi_service.eventlet, 'spawn')).mock
230 self.mock_mutate_conf = self.useFixture(fixtures.MockPatchObject(
231 wsgi_service.CONF, 'mutate_config_files')).mock
232
233 def test_on_sighup(self):
234 self.service._handle_sighup()
235 self.mock_spawn.assert_called_once_with(self.service._handle_sighup_bg)
236
237 def test_on_sighup_bg(self):
238 self.service._handle_sighup_bg()
239 self.mock_mutate_conf.assert_called_once_with()
diff --git a/ironic_inspector/wsgi_service.py b/ironic_inspector/wsgi_service.py
index 8513e16..b42e9c9 100644
--- a/ironic_inspector/wsgi_service.py
+++ b/ironic_inspector/wsgi_service.py
@@ -10,16 +10,11 @@
10# License for the specific language governing permissions and limitations 10# License for the specific language governing permissions and limitations
11# under the License. 11# under the License.
12 12
13import signal
14import ssl
15import sys
16
17import eventlet
18from oslo_config import cfg 13from oslo_config import cfg
19from oslo_log import log 14from oslo_log import log
20from oslo_service import service 15from oslo_service import service
16from oslo_service import wsgi
21 17
22from ironic_inspector.common.rpc_service import RPCService
23from ironic_inspector import main as app 18from ironic_inspector import main as app
24from ironic_inspector import utils 19from ironic_inspector import utils
25 20
@@ -27,21 +22,22 @@ LOG = log.getLogger(__name__)
27CONF = cfg.CONF 22CONF = cfg.CONF
28 23
29 24
30class WSGIService(object): 25class WSGIService(service.Service):
31 """Provides ability to launch API from wsgi app.""" 26 """Provides ability to launch API from wsgi app."""
32 27
33 def __init__(self): 28 def __init__(self):
34 self.app = app.app 29 self.app = app.app
35 signal.signal(signal.SIGHUP, self._handle_sighup) 30 self.server = wsgi.Server(CONF, 'ironic_inspector',
36 signal.signal(signal.SIGTERM, self._handle_sigterm) 31 self.app,
37 self.rpc_service = RPCService(CONF.host) 32 host=CONF.listen_address,
33 port=CONF.listen_port,
34 use_ssl=CONF.use_ssl)
38 35
39 def _init_middleware(self): 36 def _init_middleware(self):
40 """Initialize WSGI middleware. 37 """Initialize WSGI middleware.
41 38
42 :returns: None 39 :returns: None
43 """ 40 """
44
45 if CONF.auth_strategy != 'noauth': 41 if CONF.auth_strategy != 'noauth':
46 utils.add_auth_middleware(self.app) 42 utils.add_auth_middleware(self.app)
47 else: 43 else:
@@ -49,80 +45,31 @@ class WSGIService(object):
49 ' configuration') 45 ' configuration')
50 utils.add_cors_middleware(self.app) 46 utils.add_cors_middleware(self.app)
51 47
52 def _create_ssl_context(self): 48 def start(self):
53 if not CONF.use_ssl: 49 """Start serving this service using loaded configuration.
54 return
55
56 MIN_VERSION = (2, 7, 9)
57
58 if sys.version_info < MIN_VERSION:
59 LOG.warning(('Unable to use SSL in this version of Python: '
60 '%(current)s, please ensure your version of Python '
61 'is greater than %(min)s to enable this feature.'),
62 {'current': '.'.join(map(str, sys.version_info[:3])),
63 'min': '.'.join(map(str, MIN_VERSION))})
64 return
65
66 context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
67 if CONF.ssl_cert_path and CONF.ssl_key_path:
68 try:
69 context.load_cert_chain(CONF.ssl_cert_path, CONF.ssl_key_path)
70 except IOError as exc:
71 LOG.warning('Failed to load certificate or key from defined '
72 'locations: %(cert)s and %(key)s, will continue '
73 'to run with the default settings: %(exc)s',
74 {'cert': CONF.ssl_cert_path,
75 'key': CONF.ssl_key_path,
76 'exc': exc})
77 except ssl.SSLError as exc:
78 LOG.warning('There was a problem with the loaded certificate '
79 'and key, will continue to run with the default '
80 'settings: %s', exc)
81 return context
82
83 def shutdown(self, error=None):
84 """Stop serving API.
85 50
86 :returns: None 51 :returns: None
87 """ 52 """
88 LOG.debug('Shutting down') 53 self._init_middleware()
89 self.rpc_service.stop() 54 self.server.start()
90 sys.exit(error)
91 55
92 def run(self): 56 def stop(self):
93 """Start serving this service using loaded application. 57 """Stop serving this API.
94 58
95 :returns: None 59 :returns: None
96 """ 60 """
97 app_kwargs = {'host': CONF.listen_address, 61 self.server.stop()
98 'port': CONF.listen_port}
99
100 context = self._create_ssl_context()
101 if context:
102 app_kwargs['ssl_context'] = context
103
104 self._init_middleware()
105 62
106 LOG.info('Spawning RPC service') 63 def wait(self):
107 service.launch(CONF, self.rpc_service, 64 """Wait for the service to stop serving this API.
108 restart_method='mutate')
109 65
110 try: 66 :returns: None
111 self.app.run(**app_kwargs) 67 """
112 except Exception as e: 68 self.server.wait()
113 self.shutdown(error=str(e))
114 else:
115 self.shutdown()
116
117 def _handle_sighup_bg(self, *args):
118 """Reload config on SIGHUP."""
119 CONF.mutate_config_files()
120 69
121 def _handle_sighup(self, *args): 70 def reset(self):
122 eventlet.spawn(self._handle_sighup_bg, *args) 71 """Reset server greenpool size to default.
123 72
124 def _handle_sigterm(self, *args): 73 :returns: None
125 # This is a workaround to ensure that shutdown() is done when recieving 74 """
126 # SIGTERM. Raising KeyboardIntrerrupt which won't be caught by any 75 self.server.reset()
127 # 'except Exception' clauses.
128 raise KeyboardInterrupt
diff --git a/lower-constraints.txt b/lower-constraints.txt
index dc3c40f..64a46e8 100644
--- a/lower-constraints.txt
+++ b/lower-constraints.txt
@@ -73,7 +73,7 @@ oslo.middleware==3.31.0
73oslo.policy==1.30.0 73oslo.policy==1.30.0
74oslo.rootwrap==5.8.0 74oslo.rootwrap==5.8.0
75oslo.serialization==2.18.0 75oslo.serialization==2.18.0
76oslo.service==1.30.0 76oslo.service==1.24.0
77oslo.utils==3.33.0 77oslo.utils==3.33.0
78oslotest==3.2.0 78oslotest==3.2.0
79packaging==17.1 79packaging==17.1
diff --git a/releasenotes/notes/deprecate-ssl-opts-40ce8f4618c786ef.yaml b/releasenotes/notes/deprecate-ssl-opts-40ce8f4618c786ef.yaml
new file mode 100644
index 0000000..9c456f7
--- /dev/null
+++ b/releasenotes/notes/deprecate-ssl-opts-40ce8f4618c786ef.yaml
@@ -0,0 +1,7 @@
1---
2deprecations:
3 - |
4 Configuration options ``[DEFAULT]ssl_cert_path`` and
5 ``[DEFAULT]ssl_key_path`` are deprecated for ironic-inspector now uses
6 oslo.service as underlying HTTP service instead of Werkzeug. Please use
7 ``[ssl]cert_file`` and ``[ssl]key_file``. \ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index ef6c055..c0f4edc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -29,6 +29,7 @@ oslo.middleware>=3.31.0 # Apache-2.0
29oslo.policy>=1.30.0 # Apache-2.0 29oslo.policy>=1.30.0 # Apache-2.0
30oslo.rootwrap>=5.8.0 # Apache-2.0 30oslo.rootwrap>=5.8.0 # Apache-2.0
31oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 31oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
32oslo.service!=1.28.1,>=1.24.0 # Apache-2.0
32oslo.utils>=3.33.0 # Apache-2.0 33oslo.utils>=3.33.0 # Apache-2.0
33retrying!=1.3.0,>=1.2.3 # Apache-2.0 34retrying!=1.3.0,>=1.2.3 # Apache-2.0
34six>=1.10.0 # MIT 35six>=1.10.0 # MIT
diff --git a/tools/config-generator.conf b/tools/config-generator.conf
index 47fd222..b2663b9 100644
--- a/tools/config-generator.conf
+++ b/tools/config-generator.conf
@@ -4,6 +4,9 @@ namespace = ironic_inspector
4namespace = keystonemiddleware.auth_token 4namespace = keystonemiddleware.auth_token
5namespace = oslo.db 5namespace = oslo.db
6namespace = oslo.log 6namespace = oslo.log
7namespace = oslo.messaging
7namespace = oslo.middleware.cors 8namespace = oslo.middleware.cors
8namespace = oslo.policy 9namespace = oslo.policy
9namespace = oslo.messaging 10namespace = oslo.service.service
11namespace = oslo.service.sslutils
12namespace = oslo.service.wsgi \ No newline at end of file