Coverage for manila/tests/share/drivers/dell_emc/plugins/powerscale/test_powerscale_api.py: 99%
737 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 (c) 2015 EMC Corporation.
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.
16from unittest import mock
18import ddt
19from oslo_serialization import jsonutils as json
20import requests
21import requests_mock
22from urllib.parse import quote
24from manila import exception
25from manila.share.drivers.dell_emc.plugins.powerscale import powerscale_api
26from manila import test
29@ddt.ddt
30class PowerScaleApiTest(test.TestCase):
32 @mock.patch('manila.share.drivers.dell_emc.plugins.powerscale.'
33 'powerscale_api.PowerScaleApi.create_session')
34 def setUp(self, mockup_create_session):
35 super(PowerScaleApiTest, self).setUp()
37 mockup_create_session.return_value = True
38 self._mock_url = 'https://localhost:8080'
39 self.username = 'admin'
40 self.password = 'pwd'
41 self.dir_permission = '0777'
42 self.powerscale_api = powerscale_api.PowerScaleApi(
43 self._mock_url, self.username, self.password,
44 dir_permission=self.dir_permission
45 )
46 self.powerscale_api_threshold = powerscale_api.PowerScaleApi(
47 self._mock_url, self.username, self.password,
48 dir_permission=self.dir_permission,
49 threshold_limit=80
50 )
52 @mock.patch('manila.share.drivers.dell_emc.plugins.powerscale.'
53 'powerscale_api.PowerScaleApi.create_session')
54 def test__init__login_failure(self, mockup_create_session):
55 mockup_create_session.return_value = False
56 self.assertRaises(
57 exception.BadConfigurationException,
58 self.powerscale_api.__init__,
59 self._mock_url,
60 self.username,
61 self.password,
62 False,
63 None,
64 self.dir_permission
65 )
67 def test__verify_cert(self):
68 verify_cert = self.powerscale_api.verify_ssl_cert
69 certificate_path = self.powerscale_api.certificate_path
70 self.powerscale_api.verify_ssl_cert = True
71 self.powerscale_api.certificate_path = "fake_certificate_path"
72 self.assertEqual(self.powerscale_api._verify_cert,
73 self.powerscale_api.certificate_path)
74 self.powerscale_api.verify_ssl_cert = verify_cert
75 self.powerscale_api.certificate_path = certificate_path
77 @mock.patch('requests.Session.request')
78 def test_create_session_success(self, mock_request):
79 mock_response = mock.Mock()
80 mock_response.status_code = 201
81 mock_response.cookies = {'isisessid': 'test_session_token',
82 'isicsrf': 'test_csrf_token'}
83 mock_request.return_value = mock_response
84 result = self.powerscale_api.create_session(
85 self.username, self.password)
86 mock_request.assert_called_once_with(
87 'POST', self._mock_url + '/session/1/session',
88 headers={"Content-type": "application/json"},
89 data=json.dumps({"username": self.username,
90 "password": self.password,
91 "services": ["platform", "namespace"]}),
92 verify=False
93 )
94 self.assertTrue(result)
95 self.assertEqual(self.powerscale_api.session_token,
96 'test_session_token')
97 self.assertEqual(self.powerscale_api.csrf_token, 'test_csrf_token')
99 @mock.patch('requests.Session.request')
100 def test_create_session_failure(self, mock_request):
101 mock_response = mock.Mock()
102 mock_response.status_code = 401
103 mock_response.json.return_value = {
104 'message': 'Username or password is incorrect.'}
105 mock_request.return_value = mock_response
106 result = self.powerscale_api.create_session(
107 self.username, self.password)
108 self.assertFalse(result)
109 self.assertIsNone(self.powerscale_api.session_token)
110 self.assertIsNone(self.powerscale_api.csrf_token)
112 @ddt.data(False, True)
113 def test_create_directory(self, is_recursive):
114 with requests_mock.Mocker() as m:
115 path = '/ifs/test'
116 self.assertEqual(0, len(m.request_history))
117 self._add_create_directory_response(m, path, is_recursive)
119 r = self.powerscale_api.create_directory(path,
120 recursive=is_recursive)
122 self.assertTrue(r)
123 self.assertEqual(1, len(m.request_history))
124 request = m.request_history[0]
125 self._verify_dir_creation_request(request, path, is_recursive)
127 def test_create_directory_no_permission(self):
128 with requests_mock.Mocker() as m:
129 path = '/ifs/test'
130 self.powerscale_api.dir_permission = None
131 self.assertEqual(0, len(m.request_history))
132 self._add_create_directory_response(m, path, True)
134 r = self.powerscale_api.create_directory(path,
135 recursive=True)
137 self.powerscale_api.dir_permission = '0777'
138 self.assertTrue(r)
139 self.assertEqual(1, len(m.request_history))
140 request = m.request_history[0]
141 self.assertNotIn("x-isi-ifs-access-control", request.headers)
143 @requests_mock.mock()
144 def test_clone_snapshot(self, m):
145 snapshot_name = 'snapshot01'
146 fq_target_dir = '/ifs/admin/target'
148 self.assertEqual(0, len(m.request_history))
149 self._add_create_directory_response(m, fq_target_dir, False)
150 snapshots_json = (
151 '{"snapshots": '
152 '[{"name": "snapshot01", "path": "/ifs/admin/source"}]'
153 '}'
154 )
155 self._add_get_snapshot_response(m, snapshot_name, snapshots_json)
157 # In order to test cloning a snapshot, we build out a mock
158 # source directory tree. After the method under test is called we
159 # will verify the necessary calls are made to clone a snapshot.
160 source_dir_listing_json = (
161 '{"children": ['
162 '{"name": "dir1", "type": "container"},'
163 '{"name": "dir2", "type": "container"},'
164 '{"name": "file1", "type": "object"},'
165 '{"name": "file2", "type": "object"}'
166 ']}'
167 )
168 self._add_get_directory_listing_response(
169 m, '/ifs/.snapshot/{0}/admin/source'.format(snapshot_name),
170 source_dir_listing_json)
172 # Add request responses for creating directories and cloning files
173 # to the destination tree
174 self._add_file_clone_response(m, '/ifs/admin/target/file1',
175 snapshot_name)
176 self._add_file_clone_response(m, '/ifs/admin/target/file2',
177 snapshot_name)
178 self._add_create_directory_response(m, fq_target_dir + '/dir1', False)
179 self._add_get_directory_listing_response(
180 m, '/ifs/.snapshot/{0}/admin/source/dir1'.format(snapshot_name),
181 '{"children": ['
182 '{"name": "file11", "type": "object"}, '
183 '{"name": "file12", "type": "object"}'
184 ']}')
185 self._add_file_clone_response(m, '/ifs/admin/target/dir1/file11',
186 snapshot_name)
187 self._add_file_clone_response(m, '/ifs/admin/target/dir1/file12',
188 snapshot_name)
189 self._add_create_directory_response(m, fq_target_dir + '/dir2', False)
190 self._add_get_directory_listing_response(
191 m, '/ifs/.snapshot/{0}/admin/source/dir2'.format(snapshot_name),
192 '{"children": ['
193 '{"name": "file21", "type": "object"}, '
194 '{"name": "file22", "type": "object"}'
195 ']}')
196 self._add_file_clone_response(m, '/ifs/admin/target/dir2/file21',
197 snapshot_name)
198 self._add_file_clone_response(m, '/ifs/admin/target/dir2/file22',
199 snapshot_name)
201 # Call method under test
202 self.powerscale_api.clone_snapshot(snapshot_name, fq_target_dir, None)
204 # Verify calls needed to clone the source snapshot to the target dir
205 expected_calls = []
206 clone_path_list = [
207 'file1', 'file2', 'dir1/file11', 'dir1/file12',
208 'dir2/file21', 'dir2/file22']
209 for path in clone_path_list:
210 expected_call = PowerScaleApiTest.ExpectedCall(
211 PowerScaleApiTest.ExpectedCall.FILE_CLONE,
212 self._mock_url + '/namespace/ifs/admin/target/' + path,
213 ['/ifs/admin/target/' + path, '/ifs/admin/source/' + path,
214 snapshot_name])
215 expected_calls.append(expected_call)
216 dir_path_list = [
217 ('/dir1?recursive', '/dir1'),
218 ('/dir2?recursive', '/dir2'),
219 ('?recursive=', '')]
220 for url, path in dir_path_list:
221 expected_call = PowerScaleApiTest.ExpectedCall(
222 PowerScaleApiTest.ExpectedCall.DIR_CREATION,
223 self._mock_url + '/namespace/ifs/admin/target' + url,
224 ['/ifs/admin/target' + path, False])
225 expected_calls.append(expected_call)
227 self._verify_clone_snapshot_calls(expected_calls, m.request_history)
229 class ExpectedCall(object):
230 DIR_CREATION = 'dir_creation'
231 FILE_CLONE = 'file_clone'
233 def __init__(self, request_type, match_url, verify_args):
234 self.request_type = request_type
235 self.match_url = match_url
236 self.verify_args = verify_args
238 def _verify_clone_snapshot_calls(self, expected_calls, response_calls):
239 actual_calls = []
240 for call in response_calls:
241 actual_calls.append(call)
242 for expected_call in expected_calls:
243 # Match the expected call to the actual call, then verify
244 match_found = False
245 for call in actual_calls:
246 if call.url.startswith(expected_call.match_url):
247 match_found = True
248 if expected_call.request_type == 'dir_creation':
249 self._verify_dir_creation_request(
250 call, *expected_call.verify_args)
251 elif expected_call.request_type == 'file_clone': 251 ↛ 254line 251 didn't jump to line 254 because the condition on line 251 was always true
252 pass
253 else:
254 self.fail('Invalid request type')
255 actual_calls.remove(call)
256 self.assertTrue(match_found)
258 @requests_mock.mock()
259 def test_get_directory_listing(self, m):
260 self.assertEqual(0, len(m.request_history))
261 fq_dir_path = 'ifs/admin/test'
262 json_str = '{"my_json": "test123"}'
263 self._add_get_directory_listing_response(m, fq_dir_path, json_str)
265 actual_json = self.powerscale_api.get_directory_listing(fq_dir_path)
267 self.assertEqual(1, len(m.request_history))
268 self.assertEqual(json.loads(json_str), actual_json)
270 @ddt.data((200, True), (404, False))
271 def test_is_path_existent(self, data):
272 status_code, expected_return_value = data
273 with requests_mock.mock() as m:
274 self.assertEqual(0, len(m.request_history))
275 path = '/ifs/home/admin'
276 m.head('{0}/namespace{1}'.format(self._mock_url, path),
277 status_code=status_code)
279 r = self.powerscale_api.is_path_existent(path)
281 self.assertEqual(expected_return_value, r)
282 self.assertEqual(1, len(m.request_history))
284 @requests_mock.mock()
285 def test_is_path_existent_unexpected_error(self, m):
286 path = '/ifs/home/admin'
287 m.head('{0}/namespace{1}'.format(self._mock_url, path),
288 status_code=400)
290 self.assertRaises(
291 requests.exceptions.HTTPError,
292 self.powerscale_api.is_path_existent,
293 '/ifs/home/admin')
295 @ddt.data(
296 (200, '{"snapshots": [{"path": "/ifs/home/test"}]}',
297 {'path': '/ifs/home/test'}),
298 (404, '{"errors": []}', None)
299 )
300 def test_get_snapshot(self, data):
301 status_code, json_body, expected_return_value = data
302 with requests_mock.mock() as m:
303 self.assertEqual(0, len(m.request_history))
304 snapshot_name = 'foo1'
305 self._add_get_snapshot_response(m, snapshot_name, json_body,
306 status=status_code)
308 r = self.powerscale_api.get_snapshot(snapshot_name)
310 self.assertEqual(1, len(m.request_history))
311 self.assertEqual(expected_return_value, r)
313 def test_clone_snapshot_with_provider_location(self):
314 """clone_snapshot should use provider_location to get snapshot"""
315 self.powerscale_api.create_directory = mock.Mock()
316 self.powerscale_api.get_snapshot_id = mock.Mock()
317 self.powerscale_api.get_snapshot = mock.Mock()
318 self.powerscale_api._clone_directory_contents = mock.Mock()
319 fq_target_dir = '/ifs/data/clone1'
320 provider_location = '1234'
321 snapshot = {
322 'name': 'snap-123',
323 'path': '/ifs/data/share1'
324 }
325 self.powerscale_api.get_snapshot_id.return_value = snapshot
326 self.powerscale_api.clone_snapshot(
327 snapshot_name='ignored-snapshot-name',
328 fq_target_dir=fq_target_dir,
329 provider_location=provider_location
330 )
331 (self.powerscale_api.
332 create_directory.assert_called_once_with(fq_target_dir))
333 (self.powerscale_api.
334 get_snapshot_id.assert_called_once_with(provider_location))
335 self.powerscale_api.get_snapshot.assert_not_called()
337 @requests_mock.mock()
338 def test_get_snapshot_unexpected_error(self, m):
339 snapshot_name = 'foo1'
340 json_body = '{"snapshots": [{"path": "/ifs/home/test"}]}'
341 self._add_get_snapshot_response(
342 m, snapshot_name, json_body, status=400)
344 self.assertRaises(
345 requests.exceptions.HTTPError, self.powerscale_api.get_snapshot,
346 snapshot_name)
348 @requests_mock.mock()
349 def test_get_snapshots(self, m):
350 self.assertEqual(0, len(m.request_history))
351 snapshot_json = '{"snapshots": [{"path": "/ifs/home/test"}]}'
352 m.get('{0}/platform/1/snapshot/snapshots'.format(self._mock_url),
353 status_code=200, json=json.loads(snapshot_json))
355 r = self.powerscale_api.get_snapshots()
357 self.assertEqual(1, len(m.request_history))
358 self.assertEqual(json.loads(snapshot_json), r)
360 @requests_mock.mock()
361 def test_get_snapshots_error_occurred(self, m):
362 self.assertEqual(0, len(m.request_history))
363 m.get('{0}/platform/1/snapshot/snapshots'.format(self._mock_url),
364 status_code=404)
366 self.assertRaises(requests.exceptions.HTTPError,
367 self.powerscale_api.get_snapshots)
369 self.assertEqual(1, len(m.request_history))
371 @ddt.data(
372 ('/ifs/home/admin',
373 '{"exports": [{"id": 42, "paths": ["/ifs/home/admin"]}], "total": 1}',
374 42),
375 ('/ifs/home/test',
376 '{"exports": [], "total": 0}', None)
377 )
378 def test_lookup_nfs_export(self, data):
379 share_path, response_json, expected_return = data
380 with requests_mock.mock() as m:
381 self.assertEqual(0, len(m.request_history))
382 m.get('{0}/platform/12/protocols/nfs/exports?path={1}'
383 .format(self._mock_url,
384 share_path.replace('/', '%2F')),
385 json=json.loads(response_json))
387 r = self.powerscale_api.lookup_nfs_export(share_path)
389 self.assertEqual(1, len(m.request_history))
390 self.assertEqual(expected_return, r)
392 @requests_mock.mock()
393 def test_get_nfs_export(self, m):
394 self.assertEqual(0, len(m.request_history))
395 export_id = 42
396 response_json = '{"exports": [{"id": 1}]}'
397 status_code = 200
398 m.get('{0}/platform/1/protocols/nfs/exports/{1}'
399 .format(self._mock_url, export_id),
400 json=json.loads(response_json), status_code=status_code)
402 r = self.powerscale_api.get_nfs_export(export_id)
404 self.assertEqual(1, len(m.request_history))
405 self.assertEqual(json.loads('{"id": 1}'), r)
407 @requests_mock.mock()
408 def test_get_nfs_export_error(self, m):
409 self.assertEqual(0, len(m.request_history))
410 export_id = 3
411 response_json = '{}'
412 status_code = 404
413 m.get('{0}/platform/1/protocols/nfs/exports/{1}'
414 .format(self._mock_url, export_id),
415 json=json.loads(response_json), status_code=status_code)
417 r = self.powerscale_api.get_nfs_export(export_id)
419 self.assertEqual(1, len(m.request_history))
420 self.assertIsNone(r)
422 @requests_mock.mock()
423 def test_lookup_smb_share(self, m):
424 self.assertEqual(0, len(m.request_history))
425 share_name = 'my_smb_share'
426 share_json = '{"id": "my_smb_share"}'
427 response_json = '{{"shares": [{0}]}}'.format(share_json)
428 m.get('{0}/platform/1/protocols/smb/shares/{1}'
429 .format(self._mock_url, share_name), status_code=200,
430 json=json.loads(response_json))
432 r = self.powerscale_api.lookup_smb_share(share_name)
434 self.assertEqual(1, len(m.request_history))
435 self.assertEqual(json.loads(share_json), r)
437 @requests_mock.mock()
438 def test_lookup_smb_share_error(self, m):
439 self.assertEqual(0, len(m.request_history))
440 share_name = 'my_smb_share'
441 m.get('{0}/platform/1/protocols/smb/shares/{1}'.format(
442 self._mock_url, share_name), status_code=404)
444 r = self.powerscale_api.lookup_smb_share(share_name)
446 self.assertEqual(1, len(m.request_history))
447 self.assertIsNone(r)
449 @ddt.data((201, True), (404, False))
450 def test_create_nfs_export(self, data):
451 status_code, expected_return_value = data
452 with requests_mock.mock() as m:
453 self.assertEqual(0, len(m.request_history))
454 export_path = '/ifs/home/test'
455 m.post(self._mock_url + '/platform/1/protocols/nfs/exports',
456 status_code=status_code)
458 r = self.powerscale_api.create_nfs_export(export_path)
460 self.assertEqual(1, len(m.request_history))
461 call = m.request_history[0]
462 expected_request_body = '{"paths": ["/ifs/home/test"]}'
463 self.assertEqual(json.loads(expected_request_body),
464 json.loads(call.body))
465 self.assertEqual(expected_return_value, r)
467 @ddt.data((201, True), (404, False))
468 def test_create_smb_share(self, data):
469 status_code, expected_return_value = data
470 with requests_mock.mock() as m:
471 self.assertEqual(0, len(m.request_history))
472 share_name = 'my_smb_share'
473 share_path = '/ifs/home/admin/smb_share'
474 m.post(self._mock_url + '/platform/1/protocols/smb/shares',
475 status_code=status_code)
477 r = self.powerscale_api.create_smb_share(share_name, share_path)
479 self.assertEqual(expected_return_value, r)
480 self.assertEqual(1, len(m.request_history))
481 expected_request_data = {
482 'name': share_name,
483 'path': share_path,
484 'permissions': []
485 }
486 self.assertEqual(expected_request_data,
487 json.loads(m.request_history[0].body))
489 @requests_mock.mock()
490 def test_create_snapshot(self, m):
491 self.assertEqual(0, len(m.request_history))
492 snapshot_name = 'my_snapshot_01'
493 snapshot_path = '/ifs/home/admin'
494 m.post(
495 self._mock_url + '/platform/1/snapshot/snapshots',
496 status_code=201,
497 json={"id": 123, "name": snapshot_name, "path": snapshot_path}
498 )
500 r = self.powerscale_api.create_snapshot(snapshot_name, snapshot_path)
502 self.assertEqual(1, len(m.request_history))
503 self.assertTrue(r)
504 expected_request_body = json.loads(
505 '{{"name": "{0}", "path": "{1}"}}'
506 .format(snapshot_name, snapshot_path)
507 )
508 self.assertEqual(expected_request_body,
509 json.loads(m.request_history[0].body))
511 @requests_mock.mock()
512 def test_create_snapshot_error_case(self, m):
513 self.assertEqual(0, len(m.request_history))
514 snapshot_name = 'my_snapshot_01'
515 snapshot_path = '/ifs/home/admin'
516 m.post(self._mock_url + '/platform/1/snapshot/snapshots',
517 status_code=404)
519 self.assertIsNone(self.powerscale_api.create_snapshot(snapshot_name,
520 snapshot_path))
522 @ddt.data(True, False)
523 def test_delete_path(self, is_recursive_delete):
524 with requests_mock.mock() as m:
525 self.assertEqual(0, len(m.request_history))
526 fq_path = '/ifs/home/admin/test'
527 m.delete(self._mock_url + '/namespace' + fq_path + '?recursive='
528 + str(is_recursive_delete), status_code=204)
530 self.powerscale_api.delete_path(
531 fq_path, recursive=is_recursive_delete)
533 self.assertEqual(1, len(m.request_history))
535 @requests_mock.mock()
536 def test_delete_path_error_case(self, m):
537 fq_path = '/ifs/home/admin/test'
538 m.delete(self._mock_url + '/namespace' + fq_path + '?recursive=False',
539 status_code=403)
541 self.assertEqual(
542 self.powerscale_api.delete_path(
543 fq_path, recursive=False), False)
545 @ddt.data((204, True), (404, False))
546 def test_delete_nfs_share(self, data):
547 status_code, expected_return_value = data
548 with requests_mock.mock() as m:
549 self.assertEqual(0, len(m.request_history))
550 share_number = 42
551 m.delete('{0}/platform/1/protocols/nfs/exports/{1}'
552 .format(self._mock_url, share_number),
553 status_code=status_code)
555 r = self.powerscale_api.delete_nfs_share(share_number)
557 self.assertEqual(1, len(m.request_history))
558 self.assertEqual(expected_return_value, r)
560 @ddt.data((204, True), (404, False))
561 def test_delete_smb_shares(self, data):
562 status_code, expected_return_value = data
563 with requests_mock.mock() as m:
564 self.assertEqual(0, len(m.request_history))
566 share_name = 'smb_share_42'
567 m.delete('{0}/platform/1/protocols/smb/shares/{1}'
568 .format(self._mock_url, share_name),
569 status_code=status_code)
571 r = self.powerscale_api.delete_smb_share(share_name)
573 self.assertEqual(1, len(m.request_history))
574 self.assertEqual(expected_return_value, r)
576 @requests_mock.mock()
577 def test_delete_snapshot(self, m):
578 self.assertEqual(0, len(m.request_history))
579 m.delete(self._mock_url + '/platform/1/snapshot/snapshots/my_snapshot',
580 status_code=204)
582 self.powerscale_api.delete_snapshot("my_snapshot")
584 self.assertEqual(1, len(m.request_history))
586 @requests_mock.mock()
587 def test_delete_snapshot_error_case(self, m):
588 m.delete(self._mock_url + '/platform/1/snapshot/snapshots/my_snapshot',
589 status_code=403)
591 self.assertEqual(
592 self.powerscale_api.delete_snapshot("my_snapshot"), False)
594 @requests_mock.mock()
595 def test_quota_create(self, m):
596 quota_path = '/ifs/manila/test'
597 quota_size = 256
598 self.assertEqual(0, len(m.request_history))
599 m.post(self._mock_url + '/platform/1/quota/quotas', status_code=201)
601 self.powerscale_api.quota_create(quota_path, 'directory', quota_size)
603 self.assertEqual(1, len(m.request_history))
604 expected_request_json = {
605 'path': quota_path,
606 'type': 'directory',
607 'include_snapshots': False,
608 'thresholds_include_overhead': False,
609 'enforced': True,
610 'thresholds': {'hard': quota_size},
611 }
612 call_body = m.request_history[0].body
613 self.assertEqual(expected_request_json, json.loads(call_body))
615 @requests_mock.mock()
616 def test_quota_create_with_threshold(self, m):
617 quota_path = '/ifs/manila/test'
618 quota_size = 100
619 self.assertEqual(0, len(m.request_history))
620 m.post(self._mock_url + '/platform/1/quota/quotas', status_code=201)
621 self.powerscale_api_threshold.quota_create(
622 quota_path,
623 'directory',
624 quota_size
625 )
627 advisory_size = round(
628 (quota_size * self.powerscale_api_threshold.threshold_limit) / 100)
629 self.assertEqual(1, len(m.request_history))
630 expected_request_json = {
631 'path': quota_path,
632 'type': 'directory',
633 'include_snapshots': False,
634 'thresholds_include_overhead': False,
635 'enforced': True,
636 'thresholds': {'hard': quota_size,
637 'advisory': advisory_size},
638 }
639 call_body = m.request_history[0].body
640 self.assertEqual(expected_request_json, json.loads(call_body))
642 @requests_mock.mock()
643 def test_quota_create__path_does_not_exist(self, m):
644 quota_path = '/ifs/test2'
645 self.assertEqual(0, len(m.request_history))
646 m.post(self._mock_url + '/platform/1/quota/quotas', status_code=400)
648 self.assertRaises(
649 requests.exceptions.HTTPError,
650 self.powerscale_api.quota_create,
651 quota_path, 'directory', 2
652 )
654 @requests_mock.mock()
655 def test_quota_get(self, m):
656 self.assertEqual(0, len(m.request_history))
657 response_json = {'quotas': [{}]}
658 m.get(self._mock_url + '/platform/1/quota/quotas', json=response_json,
659 status_code=200)
660 quota_path = "/ifs/manila/test"
661 quota_type = "directory"
663 self.powerscale_api.quota_get(quota_path, quota_type)
665 self.assertEqual(1, len(m.request_history))
666 request_query_string = m.request_history[0].qs
667 expected_query_string = {'path': [quota_path]}
668 self.assertEqual(expected_query_string, request_query_string)
670 @requests_mock.mock()
671 def test_quota_get__path_does_not_exist(self, m):
672 self.assertEqual(0, len(m.request_history))
673 m.get(self._mock_url + '/platform/1/quota/quotas', status_code=404)
675 response = self.powerscale_api.quota_get(
676 '/ifs/does_not_exist', 'directory')
678 self.assertIsNone(response)
680 @requests_mock.mock()
681 def test_quota_modify(self, m):
682 self.assertEqual(0, len(m.request_history))
683 quota_id = "ADEF1G"
684 new_size = 1024
685 m.put('{0}/platform/1/quota/quotas/{1}'.format(
686 self._mock_url, quota_id), status_code=204)
688 self.powerscale_api.quota_modify_size(quota_id, new_size)
690 self.assertEqual(1, len(m.request_history))
691 expected_request_body = {'thresholds': {'hard': new_size}}
692 request_body = m.request_history[0].body
693 self.assertEqual(expected_request_body, json.loads(request_body))
695 @requests_mock.mock()
696 def test_quota_modify_with_threshold(self, m):
697 self.assertEqual(0, len(m.request_history))
698 quota_id = "ADEF1G"
699 new_size = 1024
700 advisory_size = round(
701 (new_size * self.powerscale_api_threshold.threshold_limit) / 100)
702 m.put('{0}/platform/1/quota/quotas/{1}'.format(
703 self._mock_url, quota_id), status_code=204)
704 self.powerscale_api_threshold.quota_modify_size(quota_id, new_size)
705 self.assertEqual(1, len(m.request_history))
706 expected_request_body = {'thresholds': {'hard': new_size,
707 'advisory': advisory_size}}
708 request_body = m.request_history[0].body
709 self.assertEqual(expected_request_body, json.loads(request_body))
711 @requests_mock.mock()
712 def test_quota_modify__given_id_does_not_exist(self, m):
713 quota_id = 'ADE2F'
714 m.put('{0}/platform/1/quota/quotas/{1}'.format(
715 self._mock_url, quota_id), status_code=404)
717 self.assertRaises(
718 requests.exceptions.HTTPError,
719 self.powerscale_api.quota_modify_size,
720 quota_id, 1024
721 )
723 @requests_mock.mock()
724 def test_quota_set__quota_already_exists(self, m):
725 self.assertEqual(0, len(m.request_history))
726 quota_path = '/ifs/manila/test'
727 quota_type = 'directory'
728 quota_size = 256
729 quota_id = 'AFE2C'
730 m.get('{0}/platform/1/quota/quotas'.format(
731 self._mock_url), json={'quotas': [{'id': quota_id}]},
732 status_code=200)
733 m.put(
734 '{0}/platform/1/quota/quotas/{1}'.format(self._mock_url, quota_id),
735 status_code=204
736 )
738 self.powerscale_api.quota_set(quota_path, quota_type, quota_size)
740 expected_quota_modify_json = {'thresholds': {'hard': quota_size}}
741 quota_put_json = json.loads(m.request_history[1].body)
742 self.assertEqual(expected_quota_modify_json, quota_put_json)
744 @requests_mock.mock()
745 def test_quota_set__quota_does_not_already_exist(self, m):
746 self.assertEqual(0, len(m.request_history))
747 m.get('{0}/platform/1/quota/quotas'.format(
748 self._mock_url), status_code=404)
749 m.post('{0}/platform/1/quota/quotas'.format(self._mock_url),
750 status_code=201)
751 quota_path = '/ifs/manila/test'
752 quota_type = 'directory'
753 quota_size = 256
755 self.powerscale_api.quota_set(quota_path, quota_type, quota_size)
757 # verify a call is made to create a quota
758 expected_create_json = {
759 str('path'): quota_path,
760 str('type'): 'directory',
761 str('include_snapshots'): False,
762 str('thresholds_include_overhead'): False,
763 str('enforced'): True,
764 str('thresholds'): {str('hard'): quota_size},
765 }
766 create_request_json = json.loads(m.request_history[1].body)
767 self.assertEqual(expected_create_json, create_request_json)
769 @requests_mock.mock()
770 def test_quota_set__path_does_not_already_exist(self, m):
771 m.get(self._mock_url + '/platform/1/quota/quotas', status_code=400)
773 e = self.assertRaises(
774 requests.exceptions.HTTPError,
775 self.powerscale_api.quota_set,
776 '/ifs/does_not_exist', 'directory', 2048
777 )
778 self.assertEqual(400, e.response.status_code)
780 def test_get_user_sid_success(self):
781 sid = {"id": "SID:S-1-22-1-0",
782 "name": "foo",
783 "type": "user"}
784 self.powerscale_api.auth_lookup_user = mock.MagicMock(
785 return_value={
786 "mapping": [{"user": {"sid": sid}}]
787 }
788 )
789 expected_sid = self.powerscale_api.get_user_sid('foo')
790 self.assertEqual(expected_sid, sid)
792 def test_get_user_sid_wrong_mappings(self):
793 self.powerscale_api.auth_lookup_user = mock.MagicMock(
794 return_value={
795 "mapping": [{"user": {"sid": 'fake_sid1'}},
796 {"user": {"sid": 'fake_sid2'}}]
797 }
798 )
799 expected_sid = self.powerscale_api.get_user_sid('foo')
800 self.assertIsNone(expected_sid)
802 def test_get_user_sid_user_not_found(self):
803 self.powerscale_api.auth_lookup_user = mock.MagicMock(
804 return_value=None
805 )
806 expected_sid = self.powerscale_api.get_user_sid('foo')
807 self.assertIsNone(expected_sid)
809 @requests_mock.mock()
810 def test_auth_lookup_user(self, m):
811 user = 'foo'
812 auth_url = '{0}/platform/1/auth/mapping/users/lookup?user={1}'.format(
813 self._mock_url, user)
814 example_sid = 'SID:S-1-5-21'
815 sid_json = {
816 'id': example_sid,
817 'name': user,
818 'type': 'user'
819 }
820 auth_json = {
821 'mapping': [
822 {'user': {'sid': sid_json}}
823 ]
824 }
825 m.get(auth_url, status_code=200, json=auth_json)
827 returned_auth_json = self.powerscale_api.auth_lookup_user(user)
828 self.assertEqual(auth_json, returned_auth_json)
830 @requests_mock.mock()
831 def test_auth_lookup_user_with_nonexistent_user(self, m):
832 user = 'nonexistent'
833 auth_url = '{0}/platform/1/auth/mapping/users/lookup?user={1}'.format(
834 self._mock_url, user)
835 m.get(auth_url, status_code=404)
836 self.assertIsNone(self.powerscale_api.auth_lookup_user(user))
838 @requests_mock.mock()
839 def test_auth_lookup_user_with_backend_error(self, m):
840 user = 'foo'
841 auth_url = '{0}/platform/1/auth/mapping/users/lookup?user={1}'.format(
842 self._mock_url, user)
843 m.get(auth_url, status_code=400)
844 self.assertIsNone(self.powerscale_api.auth_lookup_user(user))
846 def _add_create_directory_response(self, m, path, is_recursive):
847 url = '{0}/namespace{1}?recursive={2}'.format(
848 self._mock_url, path, str(is_recursive))
849 m.put(url, status_code=200)
851 def _add_file_clone_response(self, m, fq_dest_path, snapshot_name):
852 url = '{0}/namespace{1}?clone=true&snapshot={2}'.format(
853 self._mock_url, fq_dest_path, snapshot_name)
854 m.put(url)
856 def _add_get_directory_listing_response(self, m, fq_dir_path, json_str):
857 url = '{0}/namespace{1}?detail=default'.format(
858 self._mock_url, fq_dir_path)
859 m.get(url, json=json.loads(json_str), status_code=200)
861 def _add_get_snapshot_response(
862 self, m, snapshot_name, json_str, status=200):
863 url = '{0}/platform/1/snapshot/snapshots/{1}'.format(
864 self._mock_url, snapshot_name
865 )
866 m.get(url, status_code=status, json=json.loads(json_str))
868 def _verify_dir_creation_request(self, request, path, is_recursive):
869 self.assertEqual('PUT', request.method)
870 expected_url = '{0}/namespace{1}?recursive={2}'.format(
871 self._mock_url, path, str(is_recursive))
872 self.assertEqual(expected_url, request.url)
873 self.assertIn("x-isi-ifs-target-type", request.headers)
874 self.assertEqual("container",
875 request.headers['x-isi-ifs-target-type'])
876 self.assertIn("x-isi-ifs-access-control", request.headers)
877 self.assertEqual(self.dir_permission,
878 request.headers['x-isi-ifs-access-control'])
880 def _verify_clone_file_from_snapshot(
881 self, request, fq_file_path, fq_dest_path, snapshot_name):
882 self.assertEqual('PUT', request.method)
883 expected_url = '{0}/namespace{1}?clone=true&snapshot={2}'.format(
884 self._mock_url, fq_dest_path, snapshot_name
885 )
886 self.assertEqual(expected_url, request.request.url)
887 self.assertIn("x-isi-ifs-copy-source", request.headers)
888 self.assertEqual('/namespace' + fq_file_path,
889 request.headers['x-isi-ifs-copy-source'])
891 def test_modify_nfs_export_access_success(self):
892 self.powerscale_api.send_put_request = mock.MagicMock()
893 share_id = '123'
894 ro_ips = ['10.0.0.1', '10.0.0.2']
895 rw_ips = ['10.0.0.3', '10.0.0.4']
896 self.powerscale_api.modify_nfs_export_access(share_id, ro_ips, rw_ips)
897 expected_url = '{0}/platform/1/protocols/nfs/exports/{1}'.format(
898 self.powerscale_api.host_url, share_id)
899 expected_data = {'read_only_clients': ro_ips, 'clients': rw_ips}
900 self.powerscale_api.send_put_request.assert_called_once_with(
901 expected_url, data=expected_data)
903 def test_modify_nfs_export_access_no_ro_ips(self):
904 self.powerscale_api.send_put_request = mock.MagicMock()
905 share_id = '123'
906 rw_ips = ['10.0.0.3', '10.0.0.4']
907 self.powerscale_api.modify_nfs_export_access(share_id, None, rw_ips)
908 expected_url = '{0}/platform/1/protocols/nfs/exports/{1}'.format(
909 self.powerscale_api.host_url, share_id)
910 expected_data = {'clients': rw_ips}
911 self.powerscale_api.send_put_request.assert_called_once_with(
912 expected_url, data=expected_data)
914 def test_modify_nfs_export_access_no_rw_ips(self):
915 self.powerscale_api.send_put_request = mock.MagicMock()
916 share_id = '123'
917 ro_ips = ['10.0.0.1', '10.0.0.2']
918 self.powerscale_api.modify_nfs_export_access(share_id, ro_ips, None)
919 expected_url = '{0}/platform/1/protocols/nfs/exports/{1}'.format(
920 self.powerscale_api.host_url, share_id)
921 expected_data = {'read_only_clients': ro_ips}
922 self.powerscale_api.send_put_request.assert_called_once_with(
923 expected_url, data=expected_data)
925 @mock.patch('requests.Session.request')
926 def test_request_with_401_response(self, mock_request):
927 """Test sending a request with a 401 Unauthorized response."""
928 mock_request.return_value.status_code = 401
929 self.powerscale_api.create_session = mock.MagicMock(return_value=True)
930 self.powerscale_api.request('GET', 'http://example.com/api/data')
931 self.assertEqual(mock_request.call_count, 2)
933 def test_delete_quota_sends_delete_request(self):
934 self.powerscale_api.send_delete_request = mock.MagicMock()
935 quota_id = '123'
936 self.powerscale_api.delete_quota(quota_id)
937 self.powerscale_api.send_delete_request.assert_called_once_with(
938 '{0}/platform/1/quota/quotas/{1}'.format(
939 self.powerscale_api.host_url, quota_id)
940 )
942 def test_delete_quota_raises_exception_on_error(self):
943 quota_id = '123'
944 self.powerscale_api.send_delete_request = mock.MagicMock(
945 side_effect=requests.exceptions.HTTPError)
946 self.assertRaises(requests.exceptions.HTTPError,
947 self.powerscale_api.delete_quota,
948 quota_id)
950 def test_get_space_stats_success(self):
951 self.powerscale_api.send_get_request = mock.MagicMock()
952 self.powerscale_api.send_get_request.return_value.status_code = 200
953 self.powerscale_api.send_get_request.return_value.json.return_value = {
954 'stats': [
955 {'key': 'ifs.bytes.free', 'value': 1000},
956 {'key': 'ifs.bytes.total', 'value': 2000},
957 {'key': 'ifs.bytes.used', 'value': 500}
958 ]
959 }
960 result = self.powerscale_api.get_space_stats()
961 self.assertEqual(result, {'total': 2000, 'free': 1000, 'used': 500})
963 def test_get_space_stats_failure(self):
964 self.powerscale_api.send_get_request = mock.MagicMock()
965 self.powerscale_api.send_get_request.return_value.status_code = 400
966 self.assertRaises(exception.ShareBackendException,
967 self.powerscale_api.get_space_stats)
969 def test_get_allocated_space_success(self):
970 self.powerscale_api.send_get_request = mock.MagicMock()
971 self.powerscale_api.send_get_request.return_value.status_code = 200
972 self.powerscale_api.send_get_request.return_value.json.return_value = {
973 'quotas': [
974 {
975 'path': '/ifs/home',
976 'thresholds': {
977 'hard': None
978 }
979 },
980 {
981 'path': '/ifs/manila/CI-1d52ed66-a1ee-4b19-8f56-3706b',
982 'thresholds': {
983 'hard': 2147483648000
984 }
985 },
986 {
987 'path': '/ifs/manila/CI-0b622133-8b58-4a9f-ad1a-b8247',
988 'thresholds': {
989 'hard': 107374182400
990 }
991 },
992 {
993 'path': '/ifs/nilesh',
994 'thresholds': {
995 'hard': 10737418240
996 }
997 }
998 ]
999 }
1000 result = self.powerscale_api.get_allocated_space()
1001 self.assertEqual(result, 2110.0)
1003 def test_get_allocated_space_failure(self):
1004 self.powerscale_api.send_get_request = mock.MagicMock()
1005 self.powerscale_api.send_get_request.return_value.status_code = 400
1006 self.assertRaises(exception.ShareBackendException,
1007 self.powerscale_api.get_allocated_space)
1009 def test_get_cluster_version_success(self):
1010 self.powerscale_api.send_get_request = mock.MagicMock()
1011 self.powerscale_api.send_get_request.return_value.status_code = 200
1012 self.powerscale_api.send_get_request.return_value.json.return_value = {
1013 'nodes': [{'release': '1.0'}]}
1015 version = self.powerscale_api.get_cluster_version()
1016 self.assertEqual(version, '1.0')
1017 self.powerscale_api.send_get_request.assert_called_once_with(
1018 '{0}/platform/12/cluster/version'.format(
1019 self.powerscale_api.host_url)
1020 )
1022 def test_get_cluster_version_failure(self):
1023 self.powerscale_api.send_get_request = mock.MagicMock()
1024 self.powerscale_api.send_get_request.return_value.status_code = 404
1026 self.assertRaises(exception.ShareBackendException,
1027 self.powerscale_api.get_cluster_version)
1029 self.powerscale_api.send_get_request.assert_called_once_with(
1030 '{0}/platform/12/cluster/version'.format(
1031 self.powerscale_api.host_url)
1032 )
1034 def test_modify_smb_share_access_with_host_acl_and_smb_permission(self):
1035 self.powerscale_api.send_put_request = mock.MagicMock()
1036 share_name = 'my_share'
1037 host_acl = 'host1,host2'
1038 smb_permission = 'read'
1039 self.powerscale_api.modify_smb_share_access(
1040 share_name, host_acl, smb_permission)
1041 expected_url = '{0}/platform/1/protocols/smb/shares/{1}'.format(
1042 self.powerscale_api.host_url, share_name)
1043 expected_data = {'host_acl': host_acl, 'permissions': smb_permission}
1044 self.powerscale_api.send_put_request.assert_called_with(
1045 expected_url, data=expected_data)
1047 def test_modify_smb_share_access_with_host_acl_only(self):
1048 self.powerscale_api.send_put_request = mock.MagicMock()
1049 share_name = 'my_share'
1050 host_acl = 'host1,host2'
1051 self.powerscale_api.modify_smb_share_access(share_name, host_acl)
1052 expected_url = '{0}/platform/1/protocols/smb/shares/{1}'.format(
1053 self.powerscale_api.host_url, share_name)
1054 expected_data = {'host_acl': host_acl}
1055 self.powerscale_api.send_put_request.assert_called_with(
1056 expected_url, data=expected_data)
1058 def test_modify_smb_share_access_with_smb_permission_only(self):
1059 self.powerscale_api.send_put_request = mock.MagicMock()
1060 share_name = 'my_share'
1061 smb_permission = 'read'
1062 self.powerscale_api.modify_smb_share_access(
1063 share_name, permissions=smb_permission)
1064 expected_url = '{0}/platform/1/protocols/smb/shares/{1}'.format(
1065 self.powerscale_api.host_url, share_name)
1066 expected_data = {'permissions': smb_permission}
1067 self.powerscale_api.send_put_request.assert_called_with(
1068 expected_url, data=expected_data)
1070 def test_modify_smb_share_access_with_no_arguments(self):
1071 self.powerscale_api.send_put_request = mock.MagicMock()
1072 share_name = 'my_share'
1073 self.powerscale_api.modify_smb_share_access(share_name)
1074 expected_url = '{0}/platform/1/protocols/smb/shares/{1}'.format(
1075 self.powerscale_api.host_url, share_name)
1076 expected_data = {}
1077 self.powerscale_api.send_put_request.assert_called_with(
1078 expected_url, data=expected_data)
1080 def test_modify_smb_share_access_with_http_error(self):
1081 self.powerscale_api.send_put_request = mock.MagicMock(
1082 side_effect=requests.exceptions.HTTPError
1083 )
1084 share_name = 'my_share'
1085 host_acl = 'host1,host2'
1086 smb_permission = 'read'
1088 self.assertRaises(requests.exceptions.HTTPError,
1089 self.powerscale_api.modify_smb_share_access,
1090 share_name, host_acl, smb_permission)
1092 @requests_mock.mock()
1093 def test_get_snapshot_id_status_200_returns_first_snapshot(self, m):
1094 snap_id = "snap123"
1095 fake_snapshot = {'id': 'snap123', 'path': '/ifs/test'}
1097 url = self._mock_url + '/platform/1/snapshot/snapshots/' + snap_id
1099 m.get(url, status_code=200,
1100 json={'snapshots': [fake_snapshot]})
1102 result = self.powerscale_api.get_snapshot_id(snap_id)
1104 self.assertEqual(fake_snapshot, result)
1106 @requests_mock.mock()
1107 def test_get_snapshot_id_status_404_returns_none(self, m):
1108 snap_id = "snap404"
1109 url = self._mock_url + '/platform/1/snapshot/snapshots/' + snap_id
1111 # Provide a valid JSON body so r.json() doesn't fail
1112 m.get(url, status_code=404, json={})
1114 result = self.powerscale_api.get_snapshot_id(snap_id)
1116 self.assertIsNone(result)
1118 @requests_mock.mock()
1119 def test_get_snapshot_id_other_status_raises(self, m):
1120 snap_id = "snapFail"
1121 url = self._mock_url + '/platform/1/snapshot/snapshots/' + snap_id
1123 # Again, give it valid JSON so r.json() succeeds first
1124 m.get(url, status_code=500, json={"error": "Fake error"})
1126 self.assertRaises(
1127 requests.exceptions.HTTPError,
1128 self.powerscale_api.get_snapshot_id,
1129 snap_id,
1130 )
1132 @requests_mock.mock()
1133 def test_delete_snapshot_by_id_success_204(self, m):
1134 snapshot_id = "snap123"
1135 url = self._mock_url + '/platform/1/snapshot/snapshots/' + snapshot_id
1137 m.delete(url, status_code=204)
1139 result = self.powerscale_api.delete_snapshot_by_id(snapshot_id)
1140 self.assertTrue(result)
1142 @requests_mock.mock()
1143 def test_delete_snapshot_by_id_non_204(self, m):
1144 snapshot_id = "snap123"
1145 url = self._mock_url + '/platform/1/snapshot/snapshots/' + snapshot_id
1147 m.delete(url, status_code=409)
1149 result = self.powerscale_api.delete_snapshot_by_id(snapshot_id)
1150 self.assertFalse(result)
1152 @ddt.data((201, True), (400, False), (500, False))
1153 def test_create_snapshot_nfs_export(self, data):
1154 status_code, expected_return_value = data
1155 with requests_mock.mock() as m:
1156 self.assertEqual(0, len(m.request_history))
1157 export_path = "/ifs/manila-test/.snapshots/snap-001"
1158 url = self._mock_url + "/platform/22/protocols/nfs/exports"
1159 m.post(url, status_code=status_code)
1160 r = self.powerscale_api.create_snapshot_nfs_export(export_path)
1161 self.assertEqual(expected_return_value, r)
1162 self.assertEqual(1, len(m.request_history))
1163 call = m.request_history[0]
1164 expected_request_body = {
1165 "paths": [export_path],
1166 "read_only": True,
1167 "map_root": {"enabled": False},
1168 }
1169 self.assertEqual(expected_request_body, json.loads(call.body))
1170 self.assertEqual(url, call.url)
1172 @ddt.data((201, True), (404, False), (500, False))
1173 def test_create_snapshot_smb_export(self, data):
1174 status_code, expected_return_value = data
1175 with requests_mock.mock() as m:
1176 self.assertEqual(0, len(m.request_history))
1177 snapshot_name = "snap-001"
1178 share_path = "/ifs/manila-test/.snapshots/snap-001"
1179 url = self._mock_url + "/platform/1/protocols/smb/shares"
1180 m.post(url, status_code=status_code)
1181 r = self.powerscale_api.create_snapshot_smb_export(
1182 snapshot_name, share_path)
1183 self.assertEqual(expected_return_value, r)
1184 self.assertEqual(1, len(m.request_history))
1185 expected_request_data = {
1186 "name": snapshot_name,
1187 "path": share_path,
1188 }
1189 self.assertEqual(expected_request_data,
1190 json.loads(m.request_history[0].body))
1191 self.assertEqual(url, m.request_history[0].url)
1193 @requests_mock.mock()
1194 def test_create_nfs_export_aliases_success(self, m):
1195 self.assertEqual(0, len(m.request_history))
1196 alias_name = 'my_custom_name'
1197 alias_path = '/ifs/my_share'
1198 expected_url = (self.
1199 _mock_url +
1200 '/platform/12/protocols/nfs/aliases?zone=System')
1201 m.post(expected_url, status_code=201)
1202 result = (self.
1203 powerscale_api.create_nfs_export_aliases(alias_name,
1204 alias_path))
1205 self.assertTrue(result)
1206 self.assertEqual(1, len(m.request_history))
1207 expected_body = {"name": alias_name, "path": alias_path}
1208 self.assertEqual(expected_body, json.loads(m.request_history[0].body))
1210 @ddt.data(400, 404, 500)
1211 def test_create_nfs_export_aliases_error_codes(self, status):
1212 with requests_mock.mock() as m:
1213 self.assertEqual(0, len(m.request_history))
1214 alias_name = 'my_custom_name'
1215 alias_path = '/ifs/my_share'
1216 url = (self.
1217 _mock_url +
1218 '/platform/12/protocols/nfs/aliases?zone=System')
1219 m.post(url, status_code=status)
1220 result = (self.
1221 powerscale_api.create_nfs_export_aliases(alias_name,
1222 alias_path))
1223 self.assertFalse(result)
1224 self.assertEqual(1, len(m.request_history))
1226 @requests_mock.mock()
1227 def test_delete_nfs_export_aliases_success(self, m):
1228 self.assertEqual(0, len(m.request_history))
1229 alias_name = '/my_custom_name'
1230 encoded_alias = quote(alias_name, safe='')
1231 expected_url = (self._mock_url +
1232 '/platform/12/protocols/nfs/aliases/' +
1233 encoded_alias + '?zone=System')
1234 m.delete(expected_url, status_code=204)
1235 result = self.powerscale_api.delete_nfs_export_aliases(alias_name)
1236 self.assertTrue(result)
1237 self.assertEqual(1, len(m.request_history))
1239 @requests_mock.mock()
1240 def test_delete_nfs_export_aliases_not_found(self, m):
1241 self.assertEqual(0, len(m.request_history))
1242 alias_name = '/my_custom_name'
1243 encoded_alias = quote(alias_name, safe='')
1244 expected_url = (self._mock_url +
1245 '/platform/12/protocols/nfs/aliases/' +
1246 encoded_alias + '?zone=System')
1247 m.delete(expected_url, status_code=404)
1248 result = self.powerscale_api.delete_nfs_export_aliases(alias_name)
1249 self.assertFalse(result)
1250 self.assertEqual(1, len(m.request_history))
1252 @requests_mock.mock()
1253 def test_get_nfs_export_aliases_success(self, m):
1254 self.assertEqual(0, len(m.request_history))
1255 alias_name = '/my_custom_name'
1256 encoded_alias = quote(alias_name, safe='')
1257 expected_url = (self._mock_url +
1258 '/platform/12/protocols/nfs/aliases/' +
1259 encoded_alias + '?zone=System')
1260 expected_response = {
1261 "aliases": [
1262 {
1263 "id": alias_name,
1264 "name": alias_name,
1265 "path": "/ifs/my_share",
1266 "zone": "System"
1267 }
1268 ]
1269 }
1270 m.get(expected_url, status_code=200, json=expected_response)
1271 result = self.powerscale_api.get_nfs_export_aliases(alias_name)
1272 self.assertEqual(expected_response['aliases'][0], result)
1273 self.assertEqual(1, len(m.request_history))
1275 @ddt.data(400, 404, 500)
1276 def test_get_nfs_export_aliases_error_codes(self, status):
1277 with requests_mock.mock() as m:
1278 self.assertEqual(0, len(m.request_history))
1279 alias_name = '/my_custom_name'
1280 encoded_alias = quote(alias_name, safe='')
1281 expected_url = (self._mock_url +
1282 '/platform/12/protocols/nfs/aliases/' +
1283 encoded_alias + '?zone=System')
1284 m.get(expected_url, status_code=status)
1285 self.assertRaises(
1286 exception.ShareBackendException,
1287 self.powerscale_api.get_nfs_export_aliases,
1288 alias_name
1289 )
1290 self.assertEqual(1, len(m.request_history))