Coverage for manila/tests/scheduler/weighers/test_netapp_aiq.py: 99%

150 statements  

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

1# Copyright 2023 NetApp, Inc. 

2# All Rights Reserved. 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"); you may 

5# not use this file except in compliance with the License. You may obtain 

6# a copy of the License at 

7# 

8# http://www.apache.org/licenses/LICENSE-2.0 

9# 

10# Unless required by applicable law or agreed to in writing, software 

11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 

12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

13# License for the specific language governing permissions and limitations 

14# under the License. 

15""" 

16Tests For NetApp Active IQ Weigher. 

17""" 

18 

19from unittest import mock 

20 

21import ddt 

22from oslo_config import cfg 

23from oslo_serialization import jsonutils 

24import requests 

25 

26from manila import context 

27from manila import exception 

28from manila.scheduler.weighers import base_host 

29from manila.scheduler.weighers import netapp_aiq 

30from manila.share import utils 

31from manila import test 

32from manila.tests.scheduler import fakes 

33from manila.tests import utils as test_utils 

34 

35CONF = cfg.CONF 

36 

37 

38@ddt.ddt 

39class NetAppAIQWeigherTestCase(test.TestCase): 

40 def setUp(self): 

41 super(NetAppAIQWeigherTestCase, self).setUp() 

42 self.weight_handler = base_host.HostWeightHandler( 

43 'manila.scheduler.weighers') 

44 

45 netapp_aiq.LOG.debug = mock.Mock() 

46 netapp_aiq.LOG.error = mock.Mock() 

47 

48 self.mock_session = mock.Mock() 

49 self.mock_session.get = mock.Mock() 

50 self.mock_session.post = mock.Mock() 

51 self.mock_session.delete = mock.Mock() 

52 self.mock_session.patch = mock.Mock() 

53 self.mock_session.put = mock.Mock() 

54 

55 data = { 

56 'netapp_active_iq': { 

57 'aiq_hostname': "10.10.10.10", 

58 'aiq_transport_type': 'https', 

59 'aiq_ssl_verify': True, 

60 'aiq_ssl_cert_path': 'fake_cert', 

61 'aiq_username': 'fake_user', 

62 'aiq_password': 'fake_password', 

63 'aiq_eval_method': 1, 

64 'aiq_priority_order': 'ops' 

65 } 

66 } 

67 self.netapp_aiq_weigher = None 

68 with test_utils.create_temp_config_with_opts(data): 

69 self.netapp_aiq_weigher = netapp_aiq.NetAppAIQWeigher() 

70 

71 def test__weigh_object(self): 

72 self.assertRaises(NotImplementedError, 

73 self.netapp_aiq_weigher._weigh_object, 

74 "fake", "fake") 

75 

76 @ddt.data( 

77 {'resource_keys': ["fake_resource_key"], 'performance_level': None}, 

78 {'resource_keys': ["fake_resource_key"], 

79 'performance_level': "fake_psl"}, 

80 {'resource_keys': [], 'performance_level': 'fake_psl'}) 

81 @ddt.unpack 

82 def test__weigh_active_iq(self, resource_keys, performance_level): 

83 weight_properties = { 

84 'size': 1, 

85 'share_type': { 

86 'extra_specs': { 

87 "netapp:performance_service_level_name": "fake_name", 

88 } 

89 } 

90 } 

91 mock_get_psl_id = self.mock_object( 

92 self.netapp_aiq_weigher, '_get_performance_level_id', 

93 mock.Mock(return_value=performance_level)) 

94 mock_get_resource_keys = self.mock_object( 

95 self.netapp_aiq_weigher, '_get_resource_keys', 

96 mock.Mock(return_value=resource_keys)) 

97 mock_balance_aggregates = self.mock_object( 

98 self.netapp_aiq_weigher, '_balance_aggregates', 

99 mock.Mock(return_value=["1.0", "1.0"])) 

100 

101 res = self.netapp_aiq_weigher._weigh_active_iq( 

102 fakes.FAKE_ACTIVE_IQ_WEIGHER_LIST, weight_properties) 

103 

104 mock_get_psl_id.assert_called_once_with("fake_name") 

105 if not resource_keys or not performance_level: 

106 self.assertEqual([], res) 

107 else: 

108 self.assertEqual(["1.0", "1.0"], res) 

109 if performance_level: 

110 mock_get_resource_keys.assert_called_once_with( 

111 fakes.FAKE_ACTIVE_IQ_WEIGHER_LIST) 

112 else: 

113 mock_get_resource_keys.assert_not_called() 

114 if not resource_keys or not performance_level: 

115 mock_balance_aggregates.assert_not_called() 

116 else: 

117 mock_balance_aggregates.assert_called_once_with( 

118 resource_keys, 1, performance_level) 

119 

120 @ddt.data(True, False) 

121 def test__get_url(self, ipv6): 

122 if ipv6: 

123 self.netapp_aiq_weigher.host = "2001:db8::" 

124 else: 

125 self.netapp_aiq_weigher.host = "1.1.1.1" 

126 self.netapp_aiq_weigher.port = "fake_port" 

127 self.netapp_aiq_weigher.protocol = "fake_protocol" 

128 

129 res = self.netapp_aiq_weigher._get_url() 

130 

131 if ipv6: 

132 self.assertEqual('fake_protocol://[2001:db8::]:fake_port/api/', 

133 res) 

134 else: 

135 self.assertEqual('fake_protocol://1.1.1.1:fake_port/api/', 

136 res) 

137 

138 @ddt.data('get', 'post', 'delete', 'patch', 'put') 

139 def test__get_request_method(self, method): 

140 res = self.netapp_aiq_weigher._get_request_method( 

141 method, self.mock_session) 

142 

143 if method == 'get': 

144 self.assertEqual(self.mock_session.get, res) 

145 elif method == 'post': 

146 self.assertEqual(self.mock_session.post, res) 

147 elif method == 'delete': 

148 self.assertEqual(self.mock_session.delete, res) 

149 elif method == 'put': 

150 self.assertEqual(self.mock_session.put, res) 

151 elif method == 'patch': 151 ↛ exitline 151 didn't return from function 'test__get_request_method' because the condition on line 151 was always true

152 self.assertEqual(self.mock_session.patch, res) 

153 

154 def test__get_session_method(self): 

155 mock_session_builder = self.mock_object( 

156 requests, 'Session', mock.Mock(return_value=self.mock_session)) 

157 mock__get_request_method = self.mock_object( 

158 self.netapp_aiq_weigher, '_get_request_method', 

159 mock.Mock(return_value=self.mock_session.post)) 

160 

161 res = self.netapp_aiq_weigher._get_session_method('post') 

162 

163 self.assertEqual(self.mock_session.post, res) 

164 mock_session_builder.assert_called_once_with() 

165 mock__get_request_method.assert_called_once_with( 

166 'post', self.mock_session) 

167 

168 def test__call_active_iq(self): 

169 response = mock.Mock() 

170 response.content = "fake_response" 

171 response.status_code = "fake_code" 

172 mock_post = mock.Mock(return_value=response) 

173 mock__get_session_method = self.mock_object( 

174 self.netapp_aiq_weigher, '_get_session_method', 

175 mock.Mock(return_value=mock_post)) 

176 fake_url = "fake_url" 

177 fake_path = "/fake_path" 

178 mock__get_url = self.mock_object( 

179 self.netapp_aiq_weigher, '_get_url', 

180 mock.Mock(return_value=fake_url)) 

181 

182 self.netapp_aiq_weigher._call_active_iq(fake_path, "post", 

183 body="fake_body") 

184 

185 mock_post.assert_called_once_with(fake_url + fake_path, 

186 json="fake_body") 

187 self.assertTrue(netapp_aiq.LOG.debug.called) 

188 mock__get_session_method.assert_called_once_with("post") 

189 mock__get_url.assert_called_once_with() 

190 

191 @ddt.data({}, jsonutils.dumps( 

192 fakes.FAKE_ACTIVE_IQ_WEIGHER_AGGREGATES_RESPONSE)) 

193 def test__get_resource_keys(self, api_res): 

194 mock__call_active_iq = self.mock_object( 

195 self.netapp_aiq_weigher, '_call_active_iq', 

196 mock.Mock(return_value=(200, api_res))) 

197 

198 res = self.netapp_aiq_weigher._get_resource_keys( 

199 fakes.FAKE_ACTIVE_IQ_WEIGHER_LIST) 

200 

201 if api_res: 

202 self.assertEqual(['fake_key_1', 'fake_key_2', 'fake_key_3'], res) 

203 else: 

204 self.assertEqual([0, 0, 0], res) 

205 mock__call_active_iq.assert_called_once_with( 

206 'datacenter/storage/aggregates', 'get') 

207 

208 @ddt.data(mock.Mock(side_effect=exception.NotFound), 

209 mock.Mock(return_value=(400, "fake_res"))) 

210 def test__get_resource_keys_error(self, mock_cal): 

211 self.mock_object( 

212 self.netapp_aiq_weigher, '_call_active_iq', mock_cal) 

213 

214 res = self.netapp_aiq_weigher._get_resource_keys( 

215 fakes.FAKE_ACTIVE_IQ_WEIGHER_LIST) 

216 

217 self.assertEqual([], res) 

218 self.assertTrue(netapp_aiq.LOG.error.called) 

219 

220 @ddt.data([], jsonutils.dumps( 

221 fakes.FAKE_ACTIVE_IQ_WEIGHER_BALANCE_RESPONSE)) 

222 def test__balance_aggregates(self, api_res): 

223 mock__call_active_iq = self.mock_object( 

224 self.netapp_aiq_weigher, '_call_active_iq', 

225 mock.Mock(return_value=(200, api_res))) 

226 

227 res = self.netapp_aiq_weigher._balance_aggregates( 

228 ['fake_key_1', 'fake_key_2', 0, 'fake_key_3'], 10, 'fake_uuid') 

229 

230 if not api_res: 

231 self.assertEqual([0.0, 0.0, 0.0, 0.0], res) 

232 else: 

233 self.assertEqual([10.0, 20.0, 0.0, 0.0], res) 

234 fake_body = { 

235 "capacity": '10GB', 

236 "eval_method": 1, 

237 "opt_method": 0, 

238 "priority_order": ['ops'], 

239 "separate_flag": False, 

240 "resource_keys": ['fake_key_1', 'fake_key_2', 'fake_key_3'], 

241 "ssl_key": 'fake_uuid' 

242 } 

243 mock__call_active_iq.assert_called_once_with( 

244 'storage-provider/data-placement/balance', 'post', body=fake_body) 

245 

246 @ddt.data(mock.Mock(side_effect=exception.NotFound), 

247 mock.Mock(return_value=(400, "fake_res"))) 

248 def test__balance_aggregates_error(self, mock_cal): 

249 self.mock_object( 

250 self.netapp_aiq_weigher, '_call_active_iq', mock_cal) 

251 

252 res = self.netapp_aiq_weigher._balance_aggregates( 

253 ['fake_key_1', 'fake_key_2', 0, 'fake_key_3'], 10, 'fake_uuid') 

254 

255 self.assertEqual([], res) 

256 self.assertTrue(netapp_aiq.LOG.error.called) 

257 

258 @mock.patch('manila.db.api.IMPL.service_get_all_by_topic') 

259 def _get_all_hosts(self, _mock_service_get_all_by_topic, disabled=False): 

260 ctxt = context.get_admin_context() 

261 fakes.mock_host_manager_db_calls(_mock_service_get_all_by_topic, 

262 disabled=disabled) 

263 host_states = self.host_manager.get_all_host_states_share(ctxt) 

264 _mock_service_get_all_by_topic.assert_called_once_with( 

265 ctxt, CONF.share_topic, consider_disabled=False) 

266 return host_states 

267 

268 def test_weigh_objects_netapp_only(self): 

269 self.host_manager = fakes.FakeHostManagerNetAppOnly() 

270 hosts = self._get_all_hosts() # pylint: disable=no-value-for-parameter 

271 weight_properties = "fake_properties" 

272 mock_weigh_active_iq = self.mock_object( 

273 netapp_aiq.NetAppAIQWeigher, '_weigh_active_iq', 

274 # third host wins 

275 mock.Mock(return_value=[0.0, 0.0, 10.0, 0.0, 0.0, 0.0])) 

276 

277 weighed_host = self.weight_handler.get_weighed_objects( 

278 [netapp_aiq.NetAppAIQWeigher], 

279 hosts, 

280 weight_properties)[0] 

281 

282 mock_weigh_active_iq.assert_called() 

283 self.assertEqual(1.0, weighed_host.weight) 

284 self.assertEqual( 

285 'host3', utils.extract_host(weighed_host.obj.host)) 

286 

287 def test_weigh_objects_non_netapp_backends(self): 

288 self.host_manager = fakes.FakeHostManager() 

289 hosts = self._get_all_hosts() # pylint: disable=no-value-for-parameter 

290 weight_properties = "fake_properties" 

291 mock_weigh_active_iq = self.mock_object( 

292 netapp_aiq.NetAppAIQWeigher, '_weigh_active_iq') 

293 

294 weighed_host = self.weight_handler.get_weighed_objects( 

295 [netapp_aiq.NetAppAIQWeigher], 

296 hosts, 

297 weight_properties)[0] 

298 

299 mock_weigh_active_iq.assert_not_called() 

300 self.assertEqual(0.0, weighed_host.weight) 

301 self.assertEqual( 

302 'host1', utils.extract_host(weighed_host.obj.host))