Coverage for manila/tests/test_service.py: 100%
186 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
1# Copyright 2010 United States Government as represented by the
2# Administrator of the National Aeronautics and Space Administration.
3# Copyright 2014 NetApp, Inc.
4# Copyright 2014 Mirantis, Inc.
5#
6# All Rights Reserved.
7#
8# Licensed under the Apache License, Version 2.0 (the "License"); you may
9# not use this file except in compliance with the License. You may obtain
10# a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17# License for the specific language governing permissions and limitations
18# under the License.
20"""
21Unit Tests for remote procedure calls using queue
22"""
24from datetime import timedelta
25from unittest import mock
27import ddt
28from oslo_config import cfg
29from oslo_service import wsgi
30from oslo_utils import timeutils
32from manila import context
33from manila import db
34from manila import exception
35from manila import manager
36from manila import service
37from manila import test
38from manila import utils
40test_service_opts = [
41 cfg.StrOpt("fake_manager",
42 default="manila.tests.test_service.FakeManager",
43 help="Manager for testing"),
44 cfg.StrOpt("test_service_listen",
45 help="Host to bind test service to"),
46 cfg.IntOpt("test_service_listen_port",
47 default=0,
48 help="Port number to bind test service to"),
49]
51CONF = cfg.CONF
52CONF.register_opts(test_service_opts)
55class FakeManager(manager.Manager):
56 """Fake manager for tests."""
58 RPC_API_VERSION = "1.0"
60 def __init__(self, host=None, db_driver=None, service_name=None):
61 super(FakeManager, self).__init__(host=host, db_driver=db_driver)
63 def test_method(self):
64 return 'manager'
67class ExtendedService(service.Service):
68 def test_method(self):
69 return 'service'
72class ServiceManagerTestCase(test.TestCase):
73 """Test cases for Services."""
75 def test_message_gets_to_manager(self):
76 serv = service.Service('test', 'test', 'test', CONF.fake_manager)
77 serv.start()
78 self.assertEqual('manager', serv.test_method())
80 def test_override_manager_method(self):
81 serv = ExtendedService('test', 'test', 'test', CONF.fake_manager)
82 serv.start()
83 self.assertEqual('service', serv.test_method())
86class ServiceFlagsTestCase(test.TestCase):
87 def test_service_enabled_on_create_based_on_flag(self):
88 self.flags(enable_new_services=True)
89 host = 'foo'
90 binary = 'manila-fake'
91 app = service.Service.create(host=host, binary=binary)
92 app.start()
93 app.stop()
94 ref = db.service_get(context.get_admin_context(), app.service_id)
95 db.service_destroy(context.get_admin_context(), app.service_id)
96 self.assertFalse(ref['disabled'])
98 def test_service_disabled_on_create_based_on_flag(self):
99 self.flags(enable_new_services=False)
100 host = 'foo'
101 binary = 'manila-fake'
102 app = service.Service.create(host=host, binary=binary)
103 app.start()
104 app.stop()
105 ref = db.service_get(context.get_admin_context(), app.service_id)
106 db.service_destroy(context.get_admin_context(), app.service_id)
107 self.assertTrue(ref['disabled'])
110def fake_service_get_by_args(*args, **kwargs):
111 raise exception.NotFound()
114def fake_service_get(*args, **kwargs):
115 raise Exception()
118host = 'foo'
119binary = 'bar'
120topic = 'test'
121service_create = {
122 'host': host,
123 'binary': binary,
124 'topic': topic,
125 'state': 'up',
126 'report_count': 0,
127 'availability_zone': 'nova',
128}
129service_create_other_az = {
130 'host': host,
131 'binary': binary,
132 'topic': topic,
133 'state': 'up',
134 'report_count': 0,
135 'availability_zone': 'other-zone',
136}
137service_ref = {
138 'host': host,
139 'binary': binary,
140 'topic': topic,
141 'state': 'up',
142 'report_count': 0,
143 'availability_zone': {'name': 'nova'},
144 'id': 1,
145}
146service_ref_stopped = {
147 'host': host,
148 'binary': binary,
149 'topic': topic,
150 'state': 'stopped',
151 'report_count': 0,
152 'availability_zone': {'name': 'nova'},
153 'id': 1,
154}
157@ddt.ddt
158class ServiceTestCase(test.TestCase):
159 """Test cases for Services."""
161 def test_create(self):
162 app = service.Service.create(host='foo',
163 binary='manila-fake',
164 topic='fake')
165 self.assertTrue(app)
167 @ddt.data(True, False)
168 def test_periodic_tasks(self, raise_on_error):
169 serv = service.Service(host, binary, topic, CONF.fake_manager)
170 self.mock_object(
171 context,
172 'get_admin_context',
173 mock.Mock(side_effect=context.get_admin_context))
174 self.mock_object(serv.manager, 'periodic_tasks')
176 serv.periodic_tasks(raise_on_error=raise_on_error)
178 context.get_admin_context.assert_called_once_with()
179 serv.manager.periodic_tasks.assert_called_once_with(
180 utils.IsAMatcher(context.RequestContext),
181 raise_on_error=raise_on_error)
183 @mock.patch.object(service.db, 'service_get_by_args',
184 mock.Mock(side_effect=fake_service_get_by_args))
185 @mock.patch.object(service.db, 'service_create',
186 mock.Mock(return_value=service_ref))
187 @mock.patch.object(service.db, 'service_get',
188 mock.Mock(side_effect=fake_service_get))
189 def test_report_state_newly_disconnected(self):
190 serv = service.Service(host, binary, topic, CONF.fake_manager)
191 serv.start()
192 serv.report_state()
193 self.assertTrue(serv.model_disconnected)
194 service.db.service_get_by_args.assert_called_once_with(
195 mock.ANY, host, binary)
196 service.db.service_create.assert_called_once_with(
197 mock.ANY, service_create)
198 service.db.service_get.assert_called_once_with(mock.ANY, mock.ANY)
200 @mock.patch.object(service.db, 'service_get_by_args',
201 mock.Mock(side_effect=fake_service_get_by_args))
202 @mock.patch.object(service.db, 'service_create',
203 mock.Mock(return_value=service_ref))
204 @mock.patch.object(service.db, 'service_get',
205 mock.Mock(return_value=service_ref))
206 @mock.patch.object(service.db, 'service_update',
207 mock.Mock(return_value=service_ref.
208 update({'report_count': 1})))
209 @mock.patch.object(utils, 'service_is_up',
210 mock.Mock(return_value=True))
211 def test_report_state_newly_connected(self):
212 serv = service.Service(host, binary, topic, CONF.fake_manager)
213 serv.start()
214 serv.model_disconnected = True
215 serv.report_state()
216 self.assertFalse(serv.model_disconnected)
217 service.db.service_get_by_args.assert_called_once_with(
218 mock.ANY, host, binary)
219 service.db.service_create.assert_called_once_with(
220 mock.ANY, service_create)
221 service.db.service_get.assert_called_once_with(
222 mock.ANY, service_ref['id'])
223 service.db.service_update.assert_called_once_with(
224 mock.ANY, service_ref['id'], mock.ANY)
226 @mock.patch.object(service.db, 'service_get_by_args',
227 mock.Mock(side_effect=fake_service_get_by_args))
228 @mock.patch.object(service.db, 'service_create',
229 mock.Mock(return_value=service_ref))
230 @mock.patch.object(service.db, 'service_get',
231 mock.Mock(return_value=service_ref))
232 @mock.patch.object(service.db, 'service_update',
233 mock.Mock(return_value=service_ref.
234 update({'report_count': 1})))
235 @mock.patch.object(utils, 'service_is_up',
236 mock.Mock(return_value=True))
237 def test_report_state_newly_connected_different_az(self):
238 serv = service.Service(host, binary, topic, CONF.fake_manager)
239 serv.availability_zone = 'other-zone'
240 serv.start()
241 serv.model_disconnected = True
242 serv.report_state()
243 self.assertFalse(serv.model_disconnected)
244 service.db.service_get_by_args.assert_called_once_with(
245 mock.ANY, host, binary)
246 service.db.service_create.assert_called_once_with(
247 mock.ANY, service_create_other_az)
248 service.db.service_get.assert_called_once_with(
249 mock.ANY, service_ref['id'])
250 service.db.service_update.assert_called_once_with(
251 mock.ANY, service_ref['id'], mock.ANY)
253 @mock.patch.object(service.db, 'service_get_by_args',
254 mock.Mock(side_effect=fake_service_get_by_args))
255 @mock.patch.object(service.db, 'service_create',
256 mock.Mock(return_value=service_ref))
257 @mock.patch.object(service.db, 'service_get',
258 mock.Mock(side_effect=[exception.NotFound,
259 service_ref]))
260 @mock.patch.object(service.db, 'service_update',
261 mock.Mock(return_value=service_ref.
262 update({'report_count': 1})))
263 @mock.patch.object(utils, 'service_is_up',
264 mock.Mock(return_value=True))
265 def test_report_state_newly_connected_not_found(self):
266 serv = service.Service(host, binary, topic, CONF.fake_manager)
267 serv.start()
268 serv.model_disconnected = True
269 serv.report_state()
270 self.assertFalse(serv.model_disconnected)
271 service.db.service_get_by_args.assert_called_once_with(
272 mock.ANY, host, binary)
273 service.db.service_create.assert_has_calls([
274 mock.call(mock.ANY, service_create),
275 mock.call(mock.ANY, service_create)])
276 service.db.service_get.assert_has_calls([
277 mock.call(mock.ANY, service_ref['id']),
278 mock.call(mock.ANY, service_ref['id'])])
279 service.db.service_update.assert_called_once_with(
280 mock.ANY, service_ref['id'], mock.ANY)
282 def test_report_state_service_not_ready(self):
283 with mock.patch.object(service, 'db') as mock_db:
284 mock_db.service_get.return_value = service_ref
285 serv = service.Service(host, binary, topic, CONF.fake_manager)
286 serv.manager.is_service_ready = mock.Mock(return_value=False)
287 serv.start()
288 serv.report_state()
290 serv.manager.is_service_ready.assert_called_once()
292 @ddt.data(True, False)
293 def test_cleanup_services(self, cleanup_interval_done):
294 with mock.patch.object(service, 'db') as mock_db:
295 mock_db.service_get_all.return_value = [service_ref]
296 serv = service.Service(host, binary, topic, CONF.fake_manager)
297 serv.start()
298 serv.cleanup_services()
299 mock_db.service_destroy.assert_not_called()
301 if cleanup_interval_done:
302 service_ref_stopped['updated_at'] = (
303 timeutils.utcnow() - timedelta(minutes=10))
304 else:
305 service_ref_stopped['updated_at'] = timeutils.utcnow()
306 mock_db.service_get_all_by_topic.return_value = [
307 service_ref_stopped]
308 serv.stop()
309 serv.cleanup_services()
310 if cleanup_interval_done:
311 mock_db.service_destroy.assert_called_once_with(
312 mock.ANY, service_ref_stopped['id'])
315class TestWSGIService(test.TestCase):
317 def setUp(self):
318 super(TestWSGIService, self).setUp()
319 self.mock_object(wsgi.Loader, 'load_app')
320 self.test_service = service.WSGIService("test_service")
322 def test_service_random_port(self):
323 self.assertEqual(0, self.test_service.port)
324 self.test_service.start()
325 self.assertNotEqual(0, self.test_service.port)
326 self.test_service.stop()
327 wsgi.Loader.load_app.assert_called_once_with("test_service")
329 def test_reset_pool_size_to_default(self):
330 self.test_service.start()
332 # Stopping the service, which in turn sets pool size to 0
333 self.test_service.stop()
334 self.assertEqual(0, self.test_service.server._pool.size)
336 # Resetting pool size to default
337 self.test_service.reset()
338 self.test_service.start()
339 self.assertGreater(self.test_service.server._pool.size, 0)
340 wsgi.Loader.load_app.assert_called_once_with("test_service")
342 @mock.patch('oslo_service.wsgi.Server')
343 @mock.patch('oslo_service.wsgi.Loader')
344 def test_ssl_enabled(self, mock_loader, mock_server):
345 self.override_config('osapi_share_use_ssl', True)
347 service.WSGIService("osapi_share")
348 mock_server.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY,
349 port=mock.ANY, host=mock.ANY,
350 use_ssl=True)
352 self.assertTrue(mock_loader.called)