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

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. 

19 

20""" 

21Unit Tests for remote procedure calls using queue 

22""" 

23 

24from datetime import timedelta 

25from unittest import mock 

26 

27import ddt 

28from oslo_config import cfg 

29from oslo_service import wsgi 

30from oslo_utils import timeutils 

31 

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 

39 

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] 

50 

51CONF = cfg.CONF 

52CONF.register_opts(test_service_opts) 

53 

54 

55class FakeManager(manager.Manager): 

56 """Fake manager for tests.""" 

57 

58 RPC_API_VERSION = "1.0" 

59 

60 def __init__(self, host=None, db_driver=None, service_name=None): 

61 super(FakeManager, self).__init__(host=host, db_driver=db_driver) 

62 

63 def test_method(self): 

64 return 'manager' 

65 

66 

67class ExtendedService(service.Service): 

68 def test_method(self): 

69 return 'service' 

70 

71 

72class ServiceManagerTestCase(test.TestCase): 

73 """Test cases for Services.""" 

74 

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()) 

79 

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()) 

84 

85 

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']) 

97 

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']) 

108 

109 

110def fake_service_get_by_args(*args, **kwargs): 

111 raise exception.NotFound() 

112 

113 

114def fake_service_get(*args, **kwargs): 

115 raise Exception() 

116 

117 

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} 

155 

156 

157@ddt.ddt 

158class ServiceTestCase(test.TestCase): 

159 """Test cases for Services.""" 

160 

161 def test_create(self): 

162 app = service.Service.create(host='foo', 

163 binary='manila-fake', 

164 topic='fake') 

165 self.assertTrue(app) 

166 

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') 

175 

176 serv.periodic_tasks(raise_on_error=raise_on_error) 

177 

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) 

182 

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) 

199 

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) 

225 

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) 

252 

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) 

281 

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() 

289 

290 serv.manager.is_service_ready.assert_called_once() 

291 

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() 

300 

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']) 

313 

314 

315class TestWSGIService(test.TestCase): 

316 

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") 

321 

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") 

328 

329 def test_reset_pool_size_to_default(self): 

330 self.test_service.start() 

331 

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) 

335 

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") 

341 

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) 

346 

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) 

351 

352 self.assertTrue(mock_loader.called)