Coverage for manila/tests/api/v2/test_scheduler_stats.py: 100%

103 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2026-02-18 22:19 +0000

1# Copyright (c) 2015 Clinton Knight. All rights reserved. 

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 

15from unittest import mock 

16 

17import ddt 

18from oslo_utils import uuidutils 

19from webob import exc 

20 

21from manila.api.openstack import api_version_request as api_version 

22from manila.api.v2 import scheduler_stats 

23from manila import context 

24from manila import exception 

25from manila import policy 

26from manila.scheduler import rpcapi 

27from manila.share import share_types 

28from manila import test 

29from manila.tests.api import fakes 

30 

31 

32FAKE_POOLS = [ 

33 { 

34 'name': 'host1@backend1#pool1', 

35 'host': 'host1', 

36 'backend': 'backend1', 

37 'pool': 'pool1', 

38 'capabilities': { 

39 'updated': None, 

40 'total_capacity': 1024, 

41 'free_capacity': 100, 

42 'share_backend_name': 'pool1', 

43 'reserved_percentage': 0, 

44 'driver_version': '1.0.0', 

45 'storage_protocol': 'iSCSI', 

46 'qos': 'False', 

47 }, 

48 }, 

49 { 

50 'name': 'host1@backend1#pool2', 

51 'host': 'host1', 

52 'backend': 'backend1', 

53 'pool': 'pool2', 

54 'capabilities': { 

55 'updated': None, 

56 'total_capacity': 512, 

57 'free_capacity': 200, 

58 'share_backend_name': 'pool2', 

59 'reserved_percentage': 0, 

60 'driver_version': '1.0.1', 

61 'storage_protocol': 'iSER', 

62 'qos': 'True', 

63 }, 

64 }, 

65] 

66 

67 

68@ddt.ddt 

69class SchedulerStatsControllerTestCase(test.TestCase): 

70 def setUp(self): 

71 super(SchedulerStatsControllerTestCase, self).setUp() 

72 self.flags(host='fake') 

73 self.controller = scheduler_stats.SchedulerStatsController() 

74 self.resource_name = self.controller.resource_name 

75 self.ctxt = context.RequestContext('admin', 'fake', True) 

76 self.mock_policy_check = self.mock_object( 

77 policy, 'check_policy', mock.Mock(return_value=True)) 

78 

79 def test_pools_index(self): 

80 mock_get_pools = self.mock_object(rpcapi.SchedulerAPI, 

81 'get_pools', 

82 mock.Mock(return_value=FAKE_POOLS)) 

83 req = fakes.HTTPRequest.blank('/v2/scheduler_stats/pools') 

84 req.environ['manila.context'] = self.ctxt 

85 

86 result = self.controller.pools_index(req) 

87 

88 expected = { 

89 'pools': [ 

90 { 

91 'name': 'host1@backend1#pool1', 

92 'host': 'host1', 

93 'backend': 'backend1', 

94 'pool': 'pool1', 

95 }, 

96 { 

97 'name': 'host1@backend1#pool2', 

98 'host': 'host1', 

99 'backend': 'backend1', 

100 'pool': 'pool2', 

101 } 

102 ] 

103 } 

104 

105 self.assertDictEqual(result, expected) 

106 mock_get_pools.assert_called_once_with(self.ctxt, filters={}, 

107 cached=True) 

108 self.mock_policy_check.assert_called_once_with( 

109 self.ctxt, self.resource_name, 'index') 

110 

111 @ddt.data(('index', False), ('detail', True)) 

112 @ddt.unpack 

113 def test_pools_with_share_type_disabled(self, action, detail): 

114 mock_get_pools = self.mock_object(rpcapi.SchedulerAPI, 

115 'get_pools', 

116 mock.Mock(return_value=FAKE_POOLS)) 

117 

118 req = fakes.HTTPRequest.blank( 

119 f'/v2/scheduler-stats/pools/{action}' 

120 f'?backend=back1&host=host1&pool=pool1' 

121 ) 

122 req.environ['manila.context'] = self.ctxt 

123 

124 expected_filters = { 

125 'host': 'host1', 

126 'pool': 'pool1', 

127 'backend': 'back1', 

128 } 

129 

130 if detail: 

131 expected_result = {"pools": FAKE_POOLS} 

132 else: 

133 expected_result = { 

134 'pools': [ 

135 { 

136 'name': 'host1@backend1#pool1', 

137 'host': 'host1', 

138 'backend': 'backend1', 

139 'pool': 'pool1', 

140 }, 

141 { 

142 'name': 'host1@backend1#pool2', 

143 'host': 'host1', 

144 'backend': 'backend1', 

145 'pool': 'pool2', 

146 } 

147 ] 

148 } 

149 

150 result = self.controller._pools(req, action, False) 

151 

152 self.assertDictEqual(result, expected_result) 

153 mock_get_pools.assert_called_once_with(self.ctxt, 

154 filters=expected_filters, 

155 cached=True) 

156 

157 @ddt.data(('index', False, True), 

158 ('index', False, False), 

159 ('detail', True, True), 

160 ('detail', True, False)) 

161 @ddt.unpack 

162 def test_pools_with_share_type_enable(self, action, detail, uuid): 

163 mock_get_pools = self.mock_object(rpcapi.SchedulerAPI, 

164 'get_pools', 

165 mock.Mock(return_value=FAKE_POOLS)) 

166 

167 if uuid: 

168 share_type = uuidutils.generate_uuid() 

169 else: 

170 share_type = 'test_type' 

171 

172 self.mock_object( 

173 share_types, 'get_share_type_by_name_or_id', 

174 mock.Mock(return_value={'extra_specs': 

175 {'snapshot_support': True}})) 

176 

177 req = fakes.HTTPRequest.blank( 

178 f'/v2/scheduler-stats/pools/{action}' 

179 f'?backend=back1&host=host1&pool=pool1&share_type={share_type}' 

180 ) 

181 req.environ['manila.context'] = self.ctxt 

182 

183 expected_filters = { 

184 'host': 'host1', 

185 'pool': 'pool1', 

186 'backend': 'back1', 

187 'capabilities': { 

188 'snapshot_support': True 

189 } 

190 } 

191 

192 if detail: 

193 expected_result = {"pools": FAKE_POOLS} 

194 else: 

195 expected_result = { 

196 'pools': [ 

197 { 

198 'name': 'host1@backend1#pool1', 

199 'host': 'host1', 

200 'backend': 'backend1', 

201 'pool': 'pool1', 

202 }, 

203 { 

204 'name': 'host1@backend1#pool2', 

205 'host': 'host1', 

206 'backend': 'backend1', 

207 'pool': 'pool2', 

208 } 

209 ] 

210 } 

211 

212 result = self.controller._pools(req, action, True) 

213 

214 self.assertDictEqual(result, expected_result) 

215 mock_get_pools.assert_called_once_with(self.ctxt, 

216 filters=expected_filters, 

217 cached=True) 

218 

219 @ddt.data('index', 'detail') 

220 def test_pools_with_share_type_not_found(self, action): 

221 

222 req = fakes.HTTPRequest.blank( 

223 f'/v2/scheduler-stats/pools/{action}' 

224 f'?backend=.%2A&host=host1&pool=pool%2A&share_type=fake_name_1' 

225 ) 

226 

227 self.assertRaises(exc.HTTPBadRequest, 

228 self.controller._pools, 

229 req, action, True) 

230 

231 @ddt.data("1.0", "2.22", "2.23") 

232 def test_pools_index_with_filters(self, microversion): 

233 mock_get_pools = self.mock_object(rpcapi.SchedulerAPI, 

234 'get_pools', 

235 mock.Mock(return_value=FAKE_POOLS)) 

236 self.mock_object( 

237 share_types, 'get_share_type_by_name', 

238 mock.Mock(return_value={'extra_specs': 

239 {'snapshot_support': True}})) 

240 

241 req = fakes.HTTPRequest.blank( 

242 '/v2/scheduler-stats/pools/detail' 

243 '?backend=.%2A&host=host1&pool=pool%2A&share_type=test_type', 

244 version=microversion 

245 ) 

246 req.environ['manila.context'] = self.ctxt 

247 

248 result = self.controller.pools_index(req) 

249 

250 expected = { 

251 'pools': [ 

252 { 

253 'name': 'host1@backend1#pool1', 

254 'host': 'host1', 

255 'backend': 'backend1', 

256 'pool': 'pool1', 

257 }, 

258 { 

259 'name': 'host1@backend1#pool2', 

260 'host': 'host1', 

261 'backend': 'backend1', 

262 'pool': 'pool2', 

263 } 

264 ] 

265 } 

266 expected_filters = { 

267 'host': 'host1', 

268 'pool': 'pool*', 

269 'backend': '.*', 

270 'share_type': 'test_type', 

271 } 

272 if (api_version.APIVersionRequest(microversion) >= 

273 api_version.APIVersionRequest('2.23')): 

274 expected_filters.update( 

275 {'capabilities': {'snapshot_support': True}}) 

276 expected_filters.pop('share_type', None) 

277 

278 self.assertDictEqual(result, expected) 

279 mock_get_pools.assert_called_once_with(self.ctxt, 

280 filters=expected_filters, 

281 cached=True) 

282 self.mock_policy_check.assert_called_once_with( 

283 self.ctxt, self.resource_name, 'index') 

284 

285 def test_get_pools_detail(self): 

286 mock_get_pools = self.mock_object(rpcapi.SchedulerAPI, 

287 'get_pools', 

288 mock.Mock(return_value=FAKE_POOLS)) 

289 req = fakes.HTTPRequest.blank('/v2/scheduler_stats/pools/detail') 

290 req.environ['manila.context'] = self.ctxt 

291 

292 result = self.controller.pools_detail(req) 

293 

294 expected = { 

295 'pools': [ 

296 { 

297 'name': 'host1@backend1#pool1', 

298 'host': 'host1', 

299 'backend': 'backend1', 

300 'pool': 'pool1', 

301 'capabilities': { 

302 'updated': None, 

303 'total_capacity': 1024, 

304 'free_capacity': 100, 

305 'share_backend_name': 'pool1', 

306 'reserved_percentage': 0, 

307 'driver_version': '1.0.0', 

308 'storage_protocol': 'iSCSI', 

309 'qos': 'False', 

310 }, 

311 }, 

312 { 

313 'name': 'host1@backend1#pool2', 

314 'host': 'host1', 

315 'backend': 'backend1', 

316 'pool': 'pool2', 

317 'capabilities': { 

318 'updated': None, 

319 'total_capacity': 512, 

320 'free_capacity': 200, 

321 'share_backend_name': 'pool2', 

322 'reserved_percentage': 0, 

323 'driver_version': '1.0.1', 

324 'storage_protocol': 'iSER', 

325 'qos': 'True', 

326 }, 

327 }, 

328 ], 

329 } 

330 

331 self.assertDictEqual(expected, result) 

332 mock_get_pools.assert_called_once_with(self.ctxt, filters={}, 

333 cached=True) 

334 self.mock_policy_check.assert_called_once_with( 

335 self.ctxt, self.resource_name, 'detail') 

336 

337 @ddt.data('index', 'detail') 

338 def test_pools_forbidden(self, subresource): 

339 mock_get_pools = self.mock_object( 

340 rpcapi.SchedulerAPI, 'get_pools', 

341 mock.Mock(side_effect=exception.AdminRequired( 

342 "some traceback here"))) 

343 path = '/v2/scheduler_stats/pools' 

344 path = path + ('/%s' % subresource if subresource == 'detail' else '') 

345 req = fakes.HTTPRequest.blank(path) 

346 req.environ['manila.context'] = self.ctxt 

347 

348 self.assertRaises(exc.HTTPForbidden, 

349 getattr(self.controller, 'pools_%s' % subresource), 

350 req) 

351 mock_get_pools.assert_called_once_with(self.ctxt, 

352 filters={}, 

353 cached=True) 

354 

355 

356class SchedulerStatsTestCase(test.TestCase): 

357 

358 def test_create_resource(self): 

359 result = scheduler_stats.create_resource() 

360 self.assertIsInstance(result.controller, 

361 scheduler_stats.SchedulerStatsController)