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
« 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"""
19from unittest import mock
21import ddt
22from oslo_config import cfg
23from oslo_serialization import jsonutils
24import requests
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
35CONF = cfg.CONF
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')
45 netapp_aiq.LOG.debug = mock.Mock()
46 netapp_aiq.LOG.error = mock.Mock()
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()
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()
71 def test__weigh_object(self):
72 self.assertRaises(NotImplementedError,
73 self.netapp_aiq_weigher._weigh_object,
74 "fake", "fake")
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"]))
101 res = self.netapp_aiq_weigher._weigh_active_iq(
102 fakes.FAKE_ACTIVE_IQ_WEIGHER_LIST, weight_properties)
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)
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"
129 res = self.netapp_aiq_weigher._get_url()
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)
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)
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)
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))
161 res = self.netapp_aiq_weigher._get_session_method('post')
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)
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))
182 self.netapp_aiq_weigher._call_active_iq(fake_path, "post",
183 body="fake_body")
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()
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)))
198 res = self.netapp_aiq_weigher._get_resource_keys(
199 fakes.FAKE_ACTIVE_IQ_WEIGHER_LIST)
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')
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)
214 res = self.netapp_aiq_weigher._get_resource_keys(
215 fakes.FAKE_ACTIVE_IQ_WEIGHER_LIST)
217 self.assertEqual([], res)
218 self.assertTrue(netapp_aiq.LOG.error.called)
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)))
227 res = self.netapp_aiq_weigher._balance_aggregates(
228 ['fake_key_1', 'fake_key_2', 0, 'fake_key_3'], 10, 'fake_uuid')
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)
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)
252 res = self.netapp_aiq_weigher._balance_aggregates(
253 ['fake_key_1', 'fake_key_2', 0, 'fake_key_3'], 10, 'fake_uuid')
255 self.assertEqual([], res)
256 self.assertTrue(netapp_aiq.LOG.error.called)
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
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]))
277 weighed_host = self.weight_handler.get_weighed_objects(
278 [netapp_aiq.NetAppAIQWeigher],
279 hosts,
280 weight_properties)[0]
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))
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')
294 weighed_host = self.weight_handler.get_weighed_objects(
295 [netapp_aiq.NetAppAIQWeigher],
296 hosts,
297 weight_properties)[0]
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))