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

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.""" 

16 

17import sys 

18from unittest import mock 

19 

20sys.modules["purity_fb"] = mock.Mock() 

21 

22from manila.common import constants 

23from manila import exception 

24from manila.share.drivers.purestorage import flashblade 

25from manila import test 

26 

27 

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] 

56 

57 

58def _create_mock__getitem__(mock): 

59 def mock__getitem__(self, key, default=None): 

60 return getattr(mock, key, default) 

61 

62 return mock__getitem__ 

63 

64 

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) 

69 

70test_snapshot = mock.Mock(id=_MOCK_SNAPSHOT_ID, share=test_nfs_share) 

71test_snapshot.__getitem__ = _create_mock__getitem__(test_snapshot) 

72 

73 

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) 

79 

80 

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 

89 

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 ) 

96 

97 self.driver = flashblade.FlashBladeShareDriver( 

98 configuration=self.configuration 

99 ) 

100 

101 self._sys = self._flashblade_mock() 

102 

103 self._sys.api_version = mock.Mock() 

104 

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 

108 

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 ) 

130 

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 

146 

147 def _raise_purity_fb(self, *args, **kwargs): 

148 raise FakePurityFBException() 

149 

150 def _fake_safe_get(self, value): 

151 return getattr(self.configuration, value, None) 

152 

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 

158 

159 

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) 

165 

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 ) 

171 

172 def test_empty_auth_parameters(self): 

173 self.configuration.flashblade_api = "" 

174 self.assertRaises( 

175 exception.BadConfigurationException, self.driver.do_setup, None 

176 ) 

177 

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 ) 

186 

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) 

202 

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) 

207 

208 def test_delete_share(self): 

209 self.mock_object(self.driver, "_get_flashblade_filesystem_by_name") 

210 

211 self.driver.delete_share(None, test_nfs_share) 

212 

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 ) 

230 

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") 

234 

235 self.driver.delete_share(None, test_nfs_share) 

236 

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 

252 

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) 

261 

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 ) 

268 

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 ) 

275 

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 ) 

292 

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 ) 

303 

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 

315 

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 ) 

328 

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) 

341 

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 ] 

357 

358 expected_rule_map = { 

359 "09960614-8574-4e03-89cf-7cf267b0bd08": {"state": "error"}, 

360 "09960614-8574-4e03-89cf-7cf267b0bd09": {"state": "active"}, 

361 } 

362 

363 rule_map = self.driver.update_access( 

364 None, test_nfs_share, access_rules, [], [], [] 

365 ) 

366 self.assertEqual(expected_rule_map, rule_map) 

367 

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) 

378 

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 )