Coverage for manila/tests/share/drivers/purestorage/test_flashblade.py: 99%
148 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 2021 Pure Storage 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"""Unit tests for Pure Storage FlashBlade driver."""
17import sys
18from unittest import mock
20sys.modules["purity_fb"] = mock.Mock()
22from manila.common import constants
23from manila import exception
24from manila.share.drivers.purestorage import flashblade
25from manila import test
28_MOCK_SHARE_ID = 1
29_MOCK_SNAPSHOT_ID = "snap"
30_MOCK_SHARE_SIZE = 4294967296
31_SINGLE_VIP_LOCATION = [
32 {
33 "path": 'mockfb2:/share-1-manila',
34 "is_admin_only": False,
35 "metadata": {
36 "preferred": True,
37 }
38 }
39]
40_DUAL_VIP_LOCATION = [
41 {
42 "path": 'mockfb2:/share-1-manila',
43 "is_admin_only": False,
44 "metadata": {
45 "preferred": True,
46 }
47 },
48 {
49 "path": 'mockfb3:/share-1-manila',
50 "is_admin_only": False,
51 "metadata": {
52 "preferred": False,
53 }
54 }
55]
58def _create_mock__getitem__(mock):
59 def mock__getitem__(self, key, default=None):
60 return getattr(mock, key, default)
62 return mock__getitem__
65test_nfs_share = mock.Mock(
66 id=_MOCK_SHARE_ID, size=_MOCK_SHARE_SIZE, share_proto="NFS"
67)
68test_nfs_share.__getitem__ = _create_mock__getitem__(test_nfs_share)
70test_snapshot = mock.Mock(id=_MOCK_SNAPSHOT_ID, share=test_nfs_share)
71test_snapshot.__getitem__ = _create_mock__getitem__(test_snapshot)
74class FakePurityFBException(Exception):
75 def __init__(self, message=None, error_code=None, *args):
76 self.message = message
77 self.error_code = error_code
78 super(FakePurityFBException, self).__init__(message, error_code, *args)
81class FlashBladeDriverTestCaseBase(test.TestCase):
82 def setUp(self):
83 super(FlashBladeDriverTestCaseBase, self).setUp()
84 self.configuration = mock.Mock()
85 self.configuration.flashblade_mgmt_vip = "mockfb1"
86 self.configuration.flashblade_data_vip = ["mockfb2"]
87 self.configuration.flashblade_api = "api"
88 self.configuration.flashblade_eradicate = True
90 self.configuration.driver_handles_share_servers = False
91 self._mock_filesystem = mock.Mock()
92 self.mock_object(self.configuration, "safe_get", self._fake_safe_get)
93 self.purity_fb = self._patch(
94 "manila.share.drivers.purestorage.flashblade.purity_fb"
95 )
97 self.driver = flashblade.FlashBladeShareDriver(
98 configuration=self.configuration
99 )
101 self._sys = self._flashblade_mock()
103 self._sys.api_version = mock.Mock()
105 self._sys.arrays.list_arrays_space = mock.Mock()
106 self.purity_fb.rest.ApiException = FakePurityFBException
107 self.purity_fb.PurityFb.return_value = self._sys
109 self.driver.do_setup(None)
110 self.mock_object(
111 self.driver,
112 "_resize_share",
113 mock.Mock(return_value="fake_dataset"),
114 )
115 self.mock_object(
116 self.driver,
117 "_make_source_name",
118 mock.Mock(return_value="fake_dataset"),
119 )
120 self.mock_object(
121 self.driver,
122 "_get_flashblade_filesystem_by_name",
123 mock.Mock(return_value="fake_dataset"),
124 )
125 self.mock_object(
126 self.driver,
127 "_get_flashblade_snapshot_by_name",
128 mock.Mock(return_value="fake_snapshot.snap"),
129 )
131 def _flashblade_mock(self):
132 result = mock.Mock()
133 self._mock_filesystem = mock.Mock()
134 result.file_systems.create_file_systems.return_value = (
135 self._mock_filesystem
136 )
137 result.file_systems.update_file_systems.return_value = (
138 self._mock_filesystem
139 )
140 result.file_systems.delete_file_systems.return_value = (
141 self._mock_filesystem
142 )
143 result.file_system_snapshots.create_file_system_snapshots\
144 .return_value = (self._mock_filesystem)
145 return result
147 def _raise_purity_fb(self, *args, **kwargs):
148 raise FakePurityFBException()
150 def _fake_safe_get(self, value):
151 return getattr(self.configuration, value, None)
153 def _patch(self, path, *args, **kwargs):
154 patcher = mock.patch(path, *args, **kwargs)
155 result = patcher.start()
156 self.addCleanup(patcher.stop)
157 return result
160class FlashBladeDriverTestCase(FlashBladeDriverTestCaseBase):
161 @mock.patch("manila.share.drivers.purestorage.flashblade.purity_fb", None)
162 def test_no_purity_fb_module(self):
163 self.assertRaises(exception.ManilaException,
164 self.driver.do_setup, None)
166 def test_no_auth_parameters(self):
167 self.configuration.flashblade_api = None
168 self.assertRaises(
169 exception.BadConfigurationException, self.driver.do_setup, None
170 )
172 def test_empty_auth_parameters(self):
173 self.configuration.flashblade_api = ""
174 self.assertRaises(
175 exception.BadConfigurationException, self.driver.do_setup, None
176 )
178 def test_create_share_incorrect_protocol(self):
179 test_nfs_share.share_proto = "CIFS"
180 self.assertRaises(
181 exception.InvalidShare,
182 self.driver.create_share,
183 None,
184 test_nfs_share,
185 )
187 def test_create_nfs_share(self):
188 location = self.driver.create_share(None, test_nfs_share)
189 self._sys.file_systems.create_file_systems.assert_called_once_with(
190 self.purity_fb.FileSystem(
191 name="share-%s-manila" % test_nfs_share["id"],
192 provisioned=test_nfs_share["size"],
193 hard_limit_enabled=True,
194 fast_remove_directory_enabled=True,
195 snapshot_directory_enabled=True,
196 nfs=self.purity_fb.NfsRule(
197 v3_enabled=True, rules="", v4_1_enabled=True
198 ),
199 )
200 )
201 self.assertEqual(_SINGLE_VIP_LOCATION, location)
203 def test_create_nfs_share_multiple_vips(self):
204 self.configuration.flashblade_data_vip.append("mockfb3")
205 location = self.driver.create_share(None, test_nfs_share)
206 self.assertEqual(_DUAL_VIP_LOCATION, location)
208 def test_delete_share(self):
209 self.mock_object(self.driver, "_get_flashblade_filesystem_by_name")
211 self.driver.delete_share(None, test_nfs_share)
213 share_name = "share-%s-manila" % test_nfs_share["id"]
214 self.driver._get_flashblade_filesystem_by_name.assert_called_once_with(
215 share_name
216 )
217 self._sys.file_systems.update_file_systems.assert_called_once_with(
218 name=share_name,
219 attributes=self.purity_fb.FileSystem(
220 nfs=self.purity_fb.NfsRule(
221 v3_enabled=False, v4_1_enabled=False
222 ),
223 smb=self.purity_fb.ProtocolRule(enabled=False),
224 destroyed=True,
225 ),
226 )
227 self._sys.file_systems.delete_file_systems.assert_called_once_with(
228 name=share_name
229 )
231 def test_delete_share_no_eradicate(self):
232 self.configuration.flashblade_eradicate = False
233 self.mock_object(self.driver, "_get_flashblade_filesystem_by_name")
235 self.driver.delete_share(None, test_nfs_share)
237 share_name = "share-%s-manila" % test_nfs_share["id"]
238 self.driver._get_flashblade_filesystem_by_name.assert_called_once_with(
239 share_name
240 )
241 self._sys.file_systems.update_file_systems.assert_called_once_with(
242 name=share_name,
243 attributes=self.purity_fb.FileSystem(
244 nfs=self.purity_fb.NfsRule(
245 v3_enabled=False, v4_1_enabled=False
246 ),
247 smb=self.purity_fb.ProtocolRule(enabled=False),
248 destroyed=True,
249 ),
250 )
251 assert not self._sys.file_systems.delete_file_systems.called
253 def test_delete_share_not_found(self):
254 self.mock_object(
255 self.driver,
256 "_get_flashblade_filesystem_by_name",
257 mock.Mock(side_effect=self.purity_fb.rest.ApiException),
258 )
259 mock_result = self.driver.delete_share(None, test_nfs_share)
260 self.assertIsNone(mock_result)
262 def test_extend_share(self):
263 self.driver.extend_share(test_nfs_share, _MOCK_SHARE_SIZE * 2)
264 self.driver._resize_share.assert_called_once_with(
265 test_nfs_share,
266 _MOCK_SHARE_SIZE * 2,
267 )
269 def test_shrink_share(self):
270 self.driver.shrink_share(test_nfs_share, _MOCK_SHARE_SIZE / 2)
271 self.driver._resize_share.assert_called_once_with(
272 test_nfs_share,
273 _MOCK_SHARE_SIZE / 2,
274 )
276 def test_shrink_share_over_consumed(self):
277 self.mock_object(
278 self.driver,
279 "_resize_share",
280 mock.Mock(
281 side_effect=exception.ShareShrinkingPossibleDataLoss(
282 share_id=test_nfs_share["id"]
283 )
284 ),
285 )
286 self.assertRaises(
287 exception.ShareShrinkingPossibleDataLoss,
288 self.driver.shrink_share,
289 test_nfs_share,
290 _MOCK_SHARE_SIZE / 2,
291 )
293 def test_create_snapshot(self):
294 self.mock_object(self.driver, "_get_flashblade_filesystem_by_name")
295 self.mock_object(self.driver, "_get_flashblade_snapshot_by_name")
296 self.mock_object(self.driver, "_make_source_name")
297 self.driver.create_snapshot(None, test_snapshot)
298 self._sys.file_system_snapshots.create_file_system_snapshots\
299 .assert_called_once_with(
300 suffix=self.purity_fb.SnapshotSuffix(test_snapshot["id"]),
301 sources=[mock.ANY],
302 )
304 def test_delete_snapshot_no_eradicate(self):
305 self.configuration.flashblade_eradicate = False
306 self.mock_object(self.driver, "_get_flashblade_snapshot_by_name")
307 self.driver.delete_snapshot(None, test_snapshot)
308 self._sys.file_system_snapshots.update_file_system_snapshots\
309 .assert_called_once_with(
310 name=mock.ANY,
311 attributes=self.purity_fb.FileSystemSnapshot(destroyed=True),
312 )
313 assert not self._sys.file_system_snapshots\
314 .delete_file_system_snapshots.called
316 def test_delete_snapshot(self):
317 self.mock_object(self.driver, "_get_flashblade_snapshot_by_name")
318 self.driver.delete_snapshot(None, test_snapshot)
319 self._sys.file_system_snapshots.update_file_system_snapshots\
320 .assert_called_once_with(
321 name=mock.ANY,
322 attributes=self.purity_fb.FileSystemSnapshot(destroyed=True),
323 )
324 self._sys.file_system_snapshots.delete_file_system_snapshots\
325 .assert_called_once_with(
326 name=mock.ANY
327 )
329 def test_delete_snapshot_not_found(self):
330 self.mock_object(
331 self.driver,
332 "_get_flashblade_snapshot_by_name",
333 mock.Mock(
334 side_effect=exception.ShareResourceNotFound(
335 share_id=test_nfs_share["id"]
336 )
337 ),
338 )
339 mock_result = self.driver.delete_snapshot(None, test_snapshot)
340 self.assertIsNone(mock_result)
342 def test_update_access_share(self):
343 access_rules = [
344 {
345 "access_level": constants.ACCESS_LEVEL_RO,
346 "access_to": "1.2.3.4",
347 "access_type": "ip",
348 "access_id": "09960614-8574-4e03-89cf-7cf267b0bd09",
349 },
350 {
351 "access_level": constants.ACCESS_LEVEL_RW,
352 "access_to": "1.2.3.5",
353 "access_type": "user",
354 "access_id": "09960614-8574-4e03-89cf-7cf267b0bd08",
355 },
356 ]
358 expected_rule_map = {
359 "09960614-8574-4e03-89cf-7cf267b0bd08": {"state": "error"},
360 "09960614-8574-4e03-89cf-7cf267b0bd09": {"state": "active"},
361 }
363 rule_map = self.driver.update_access(
364 None, test_nfs_share, access_rules, [], [], []
365 )
366 self.assertEqual(expected_rule_map, rule_map)
368 def test_revert_to_snapshot_bad_snapshot(self):
369 self.mock_object(
370 self.driver,
371 "_get_flashblade_filesystem_by_name",
372 mock.Mock(side_effect=self.purity_fb.rest.ApiException),
373 )
374 mock_result = self.driver.revert_to_snapshot(
375 None, test_snapshot, None, None
376 )
377 self.assertIsNone(mock_result)
379 def test_revert_to_snapshot(self):
380 self.mock_object(self.driver, "_get_flashblade_snapshot_by_name")
381 self.driver.revert_to_snapshot(None, test_snapshot, [], [])
382 self._sys.file_systems.create_file_systems.assert_called_once_with(
383 overwrite=True,
384 discard_non_snapshotted_data=True,
385 file_system=self.purity_fb.FileSystem(
386 name=test_nfs_share,
387 source=self.purity_fb.Reference(name=mock.ANY),
388 ),
389 )