Coverage for manila/tests/share/drivers/netapp/dataontap/client/test_rest_api.py: 99%
223 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 2022 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 REST API layer
17"""
19from unittest import mock
21import ddt
22from oslo_serialization import jsonutils
23from oslo_utils import netutils
24import requests
25from requests import auth
27from manila.share.drivers.netapp.dataontap.client import api as legacy_api
28from manila.share.drivers.netapp.dataontap.client import rest_api as netapp_api
29from manila import test
30from manila.tests.share.drivers.netapp.dataontap.client import fakes as fake
33@ddt.ddt
34class NetAppRestApiServerTests(test.TestCase):
35 """Test case for NetApp REST API server methods."""
36 def setUp(self):
37 self.rest_client = netapp_api.RestNaServer('127.0.0.1')
38 super(NetAppRestApiServerTests, self).setUp()
40 @ddt.data(None, 'my_cert')
41 def test__init__ssl_verify(self, ssl_cert_path):
42 client = netapp_api.RestNaServer('127.0.0.1',
43 ssl_cert_path=ssl_cert_path)
45 if ssl_cert_path:
46 self.assertEqual(ssl_cert_path, client._ssl_verify)
47 else:
48 self.assertTrue(client._ssl_verify)
50 @ddt.data(None, 'ftp')
51 def test_set_transport_type_value_error(self, transport_type):
52 self.assertRaises(ValueError, self.rest_client.set_transport_type,
53 transport_type)
55 @ddt.data('!&', '80na', '')
56 def test_set_port__value_error(self, port):
57 self.assertRaises(ValueError, self.rest_client.set_port, port)
59 @ddt.data(
60 {'port': None, 'protocol': 'http', 'expected_port': '80'},
61 {'port': None, 'protocol': 'https', 'expected_port': '443'},
62 {'port': '111', 'protocol': None, 'expected_port': '111'}
63 )
64 @ddt.unpack
65 def test_set_port(self, port, protocol, expected_port):
66 self.rest_client._protocol = protocol
68 self.rest_client.set_port(port=port)
70 self.assertEqual(expected_port, self.rest_client._port)
72 @ddt.data('!&', '80na', '')
73 def test_set_timeout_value_error(self, timeout):
74 self.assertRaises(ValueError, self.rest_client.set_timeout, timeout)
76 @ddt.data({'params': {'major': 1, 'minor': '20a'}},
77 {'params': {'major': '20a', 'minor': 1}},
78 {'params': {'major': '!*', 'minor': '20a'}})
79 @ddt.unpack
80 def test_set_api_version_value_error(self, params):
81 self.assertRaises(ValueError, self.rest_client.set_api_version,
82 **params)
84 def test_set_api_version_valid(self):
85 args = {'major': '20', 'minor': 1}
87 self.rest_client.set_api_version(**args)
89 self.assertEqual(self.rest_client._api_major_version, 20)
90 self.assertEqual(self.rest_client._api_minor_version, 1)
91 self.assertEqual(self.rest_client._api_version, "20.1")
93 def test_invoke_successfully_naapi_error(self):
94 self.mock_object(self.rest_client, '_build_headers')
95 self.mock_object(self.rest_client, '_get_base_url',
96 mock.Mock(return_value=''))
97 self.mock_object(
98 self.rest_client, 'send_http_request',
99 mock.Mock(return_value=(10, fake.ERROR_RESPONSE_REST)))
101 self.assertRaises(legacy_api.NaApiError,
102 self.rest_client.invoke_successfully,
103 fake.FAKE_ACTION_URL, 'get')
105 @ddt.data(None, {'fields': 'fake_fields'})
106 def test_invoke_successfully(self, query):
107 mock_build_header = self.mock_object(
108 self.rest_client, '_build_headers',
109 mock.Mock(return_value=fake.FAKE_HTTP_HEADER))
110 mock_base = self.mock_object(
111 self.rest_client, '_get_base_url',
112 mock.Mock(return_value=fake.FAKE_BASE_URL))
113 mock_add_query = self.mock_object(
114 self.rest_client, '_add_query_params_to_url',
115 mock.Mock(return_value=fake.FAKE_ACTION_URL))
116 http_code = 200
117 mock_send_http = self.mock_object(
118 self.rest_client, 'send_http_request',
119 mock.Mock(return_value=(http_code, fake.NO_RECORDS_RESPONSE_REST)))
121 code, response = self.rest_client.invoke_successfully(
122 fake.FAKE_ACTION_URL, 'get', body=fake.FAKE_HTTP_BODY, query=query,
123 enable_tunneling=True)
125 self.assertEqual(response, fake.NO_RECORDS_RESPONSE_REST)
126 self.assertEqual(code, http_code)
127 mock_build_header.assert_called_once_with(True)
128 mock_base.assert_called_once_with()
129 self.assertEqual(bool(query), mock_add_query.called)
130 mock_send_http.assert_called_once_with(
131 'get',
132 fake.FAKE_BASE_URL + fake.FAKE_ACTION_URL, fake.FAKE_HTTP_BODY,
133 fake.FAKE_HTTP_HEADER)
135 @ddt.data(
136 {'error': requests.HTTPError(), 'raised': legacy_api.NaApiError},
137 {'error': Exception, 'raised': legacy_api.NaApiError})
138 @ddt.unpack
139 def test_send_http_request_http_error(self, error, raised):
140 self.mock_object(netapp_api, 'LOG')
141 self.mock_object(self.rest_client, '_build_session')
142 self.rest_client._session = mock.Mock()
143 self.mock_object(
144 self.rest_client, '_get_request_method', mock.Mock(
145 return_value=mock.Mock(side_effect=error)))
147 self.assertRaises(raised, self.rest_client.send_http_request,
148 'get', fake.FAKE_ACTION_URL, fake.FAKE_HTTP_BODY,
149 fake.FAKE_HTTP_HEADER)
151 @ddt.data(
152 {
153 'resp_content': fake.NO_RECORDS_RESPONSE_REST,
154 'body': fake.FAKE_HTTP_BODY,
155 'timeout': 10,
156 },
157 {
158 'resp_content': fake.NO_RECORDS_RESPONSE_REST,
159 'body': fake.FAKE_HTTP_BODY,
160 'timeout': None,
161 },
162 {
163 'resp_content': fake.NO_RECORDS_RESPONSE_REST,
164 'body': None,
165 'timeout': None,
166 },
167 {
168 'resp_content': None,
169 'body': None,
170 'timeout': None,
171 }
172 )
173 @ddt.unpack
174 def test_send_http_request(self, resp_content, body, timeout):
175 if timeout:
176 self.rest_client._timeout = timeout
177 self.mock_object(netapp_api, 'LOG')
178 mock_json_dumps = self.mock_object(
179 jsonutils, 'dumps', mock.Mock(return_value='fake_dump_body'))
180 mock_build_session = self.mock_object(
181 self.rest_client, '_build_session')
182 _mock_session = mock.Mock()
183 self.rest_client._session = _mock_session
184 response = mock.Mock()
185 response.content = resp_content
186 response.status_code = 10
187 mock_post = mock.Mock(return_value=response)
188 mock_get_request_method = self.mock_object(
189 self.rest_client, '_get_request_method', mock.Mock(
190 return_value=mock_post))
191 mock_json_loads = self.mock_object(
192 jsonutils, 'loads',
193 mock.Mock(return_value='fake_loads_response'))
195 code, res = self.rest_client.send_http_request(
196 'post', fake.FAKE_ACTION_URL, body, fake.FAKE_HTTP_HEADER)
198 expected_res = 'fake_loads_response' if resp_content else {}
199 self.assertEqual(expected_res, res)
200 self.assertEqual(10, code)
201 self.assertEqual(bool(body), mock_json_dumps.called)
202 self.assertEqual(bool(resp_content), mock_json_loads.called)
203 mock_build_session.assert_called_once_with(fake.FAKE_HTTP_HEADER)
204 mock_get_request_method.assert_called_once_with('post', _mock_session)
205 expected_data = 'fake_dump_body' if body else {}
206 if timeout:
207 mock_post.assert_called_once_with(
208 fake.FAKE_ACTION_URL, data=expected_data, timeout=timeout)
209 else:
210 mock_post.assert_called_once_with(fake.FAKE_ACTION_URL,
211 data=expected_data)
213 @ddt.data(
214 {'host': '192.168.1.0', 'port': '80', 'protocol': 'http'},
215 {'host': '0.0.0.0', 'port': '443', 'protocol': 'https'},
216 {'host': '::ffff:8', 'port': '80', 'protocol': 'http'},
217 {'host': 'fdf8:f53b:82e4::53', 'port': '443', 'protocol': 'https'})
218 @ddt.unpack
219 def test__get_base_url(self, host, port, protocol):
220 client = netapp_api.RestNaServer(host, port=port,
221 transport_type=protocol)
222 expected_host = netutils.escape_ipv6(host)
223 expected_url = '%s://%s:%s/api' % (protocol, expected_host, port)
225 url = client._get_base_url()
227 self.assertEqual(expected_url, url)
229 def test__add_query_params_to_url(self):
230 formatted_url = self.rest_client._add_query_params_to_url(
231 fake.FAKE_ACTION_URL, fake.FAKE_HTTP_QUERY)
233 expected_formatted_url = fake.FAKE_ACTION_URL
234 expected_formatted_url += fake.FAKE_FORMATTED_HTTP_QUERY
235 self.assertEqual(expected_formatted_url, formatted_url)
237 @ddt.data('post', 'get', 'put', 'delete', 'patch')
238 def test_get_request_method(self, method):
239 _mock_session = mock.Mock()
240 _mock_session.post = mock.Mock()
241 _mock_session.get = mock.Mock()
242 _mock_session.put = mock.Mock()
243 _mock_session.delete = mock.Mock()
244 _mock_session.patch = mock.Mock()
246 res = self.rest_client._get_request_method(method, _mock_session)
248 expected_method = getattr(_mock_session, method)
249 self.assertEqual(expected_method, res)
251 def test__str__(self):
252 fake_host = 'fake_host'
253 client = netapp_api.RestNaServer(fake_host)
255 expected_str = "server: %s" % fake_host
256 self.assertEqual(expected_str, str(client))
258 def test_get_transport_type(self):
259 expected_protocol = 'fake_protocol'
260 self.rest_client._protocol = expected_protocol
262 res = self.rest_client.get_transport_type()
264 self.assertEqual(expected_protocol, res)
266 @ddt.data(None, ('1', '0'))
267 def test_get_api_version(self, api_version):
268 if api_version:
269 self.rest_client._api_version = str(api_version)
270 (self.rest_client._api_major_version, _) = api_version
271 (_, self.rest_client._api_minor_version) = api_version
273 res = self.rest_client.get_api_version()
275 self.assertEqual(api_version, res)
277 @ddt.data(None, '9.10')
278 def test_get_ontap_version(self, ontap_version):
279 if ontap_version:
280 self.rest_client._ontap_version = ontap_version
282 res = self.rest_client.get_ontap_version()
284 self.assertEqual(ontap_version, res)
286 def test_set_vserver(self):
287 expected_vserver = 'fake_vserver'
288 self.rest_client.set_vserver(expected_vserver)
290 self.assertEqual(expected_vserver, self.rest_client._vserver)
292 def test_get_vserver(self):
293 expected_vserver = 'fake_vserver'
294 self.rest_client._vserver = expected_vserver
296 res = self.rest_client.get_vserver()
298 self.assertEqual(expected_vserver, res)
300 def test__build_session_with_basic_auth(self):
301 """Tests whether build session works with """
302 """default(basic auth) parameters"""
303 fake_session = mock.Mock()
304 mock_requests_session = self.mock_object(
305 requests, 'Session', mock.Mock(return_value=fake_session))
306 mock_auth = self.mock_object(
307 self.rest_client, '_create_basic_auth_handler',
308 mock.Mock(return_value='fake_auth'))
309 self.rest_client._ssl_verify = 'fake_ssl'
311 self.rest_client._build_session(fake.FAKE_HTTP_HEADER)
313 self.assertEqual(fake_session, self.rest_client._session)
314 self.assertEqual('fake_auth', self.rest_client._session.auth)
315 self.assertEqual('fake_ssl', self.rest_client._session.verify)
316 self.assertEqual(fake.FAKE_HTTP_HEADER,
317 self.rest_client._session.headers)
318 mock_requests_session.assert_called_once_with()
319 mock_auth.assert_called_once_with()
321 def test__build_session__certificate_auth(self):
322 """Tests whether build session works with """
323 """valid certificate parameters"""
324 self.rest_client._private_key_file = 'fake_key.pem'
325 self.rest_client._certificate_file = 'fake_cert.pem'
326 self.rest_client._certificate_host_validation = False
327 fake_session = mock.Mock()
328 mock_requests_session = self.mock_object(
329 requests, 'Session', mock.Mock(return_value=fake_session))
330 mock_cert = self.mock_object(
331 self.rest_client, '_create_certificate_auth_handler',
332 mock.Mock(return_value=('fake_cert', 'fake_verify')))
334 self.rest_client._build_session(fake.FAKE_HTTP_HEADER)
336 self.assertEqual(fake_session, self.rest_client._session)
337 self.assertEqual(('fake_cert', 'fake_verify'),
338 (self.rest_client._session.cert,
339 self.rest_client._session.verify))
340 self.assertEqual(fake.FAKE_HTTP_HEADER,
341 self.rest_client._session.headers)
342 mock_requests_session.assert_called_once_with()
343 mock_cert.assert_called_once_with()
345 @ddt.data(True, False)
346 def test__build_headers(self, enable_tunneling):
347 self.rest_client._vserver = fake.VSERVER_NAME
349 res = self.rest_client._build_headers(enable_tunneling)
351 expected = {
352 "Accept": "application/json",
353 "Content-Type": "application/json"
354 }
355 if enable_tunneling:
356 expected["X-Dot-SVM-Name"] = fake.VSERVER_NAME
357 self.assertEqual(expected, res)
359 def test__create_basic_auth_handler(self):
360 username = 'fake_username'
361 password = 'fake_password'
362 client = netapp_api.RestNaServer('10.1.1.1', username=username,
363 password=password)
365 res = client._create_basic_auth_handler()
367 expected = auth.HTTPBasicAuth(username, password)
368 self.assertEqual(expected.__dict__, res.__dict__)
370 def test__create_certificate_auth_handler_default(self):
371 """Test whether create certificate auth handler """
372 """works with default params"""
373 self.rest_client._private_key_file = 'fake_key.pem'
374 self.rest_client._certificate_file = 'fake_cert.pem'
375 self.rest_client._certificate_host_validation = False
376 cert = self.rest_client._certificate_file, \
377 self.rest_client._private_key_file
378 self.rest_client._session = mock.Mock()
379 if not self.rest_client._certificate_host_validation: 379 ↛ 381line 379 didn't jump to line 381 because the condition on line 379 was always true
380 self.assertFalse(self.rest_client._certificate_host_validation)
381 res = self.rest_client._create_certificate_auth_handler()
382 self.assertEqual(res,
383 (cert, self.rest_client._certificate_host_validation))
385 def test__create_certificate_auth_handler_with_host_validation(self):
386 """Test whether create certificate auth handler """
387 """works with host validation enabled"""
388 self.rest_client._private_key_file = 'fake_key.pem'
389 self.rest_client._certificate_file = 'fake_cert.pem'
390 self.rest_client._ca_certificate_file = 'fake_ca_cert.crt'
391 self.rest_client._certificate_host_validation = True
392 cert = self.rest_client._certificate_file, \
393 self.rest_client._private_key_file
394 self.rest_client._session = mock.Mock()
395 if self.rest_client._certificate_host_validation: 395 ↛ 397line 395 didn't jump to line 397 because the condition on line 395 was always true
396 self.assertTrue(self.rest_client._certificate_host_validation)
397 res = self.rest_client._create_certificate_auth_handler()
398 self.assertEqual(res, (cert, self.rest_client._ca_certificate_file))