Coverage for manila/tests/share/drivers/glusterfs/test_layout_volume.py: 99%

509 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2026-02-18 22:19 +0000

1# Copyright (c) 2014 Red Hat, 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 

16""" GlusterFS volume mapped share layout testcases. 

17""" 

18 

19import re 

20import shutil 

21import tempfile 

22from unittest import mock 

23 

24import ddt 

25from oslo_config import cfg 

26 

27from manila.common import constants 

28from manila import context 

29from manila import exception 

30from manila.privsep import os as privsep_os 

31from manila.share import configuration as config 

32from manila.share.drivers.glusterfs import common 

33from manila.share.drivers.glusterfs import layout_volume 

34from manila import test 

35from manila.tests import fake_utils 

36 

37 

38CONF = cfg.CONF 

39 

40 

41def new_share(**kwargs): 

42 share = { 

43 'id': 'fakeid', 

44 'name': 'fakename', 

45 'size': 1, 

46 'share_proto': 'glusterfs', 

47 } 

48 share.update(kwargs) 

49 return share 

50 

51 

52def glusterXMLOut(**kwargs): 

53 

54 template = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 

55<cliOutput> 

56 <opRet>%(ret)d</opRet> 

57 <opErrno>%(errno)d</opErrno> 

58 <opErrstr>fake error</opErrstr> 

59</cliOutput>""" 

60 

61 return template % kwargs, '' 

62 

63 

64FAKE_UUID1 = '11111111-1111-1111-1111-111111111111' 

65FAKE_UUID2 = '22222222-2222-2222-2222-222222222222' 

66 

67 

68@ddt.ddt 

69class GlusterfsVolumeMappedLayoutTestCase(test.TestCase): 

70 """Tests GlusterfsVolumeMappedLayout.""" 

71 

72 def setUp(self): 

73 super(GlusterfsVolumeMappedLayoutTestCase, self).setUp() 

74 fake_utils.stub_out_utils_execute(self) 

75 self._execute = fake_utils.fake_execute 

76 self._context = context.get_admin_context() 

77 

78 self.glusterfs_target1 = 'root@host1:/gv1' 

79 self.glusterfs_target2 = 'root@host2:/gv2' 

80 self.glusterfs_server1 = 'root@host1' 

81 self.glusterfs_server2 = 'root@host2' 

82 self.glusterfs_server1_volumes = 'manila-share-1-1G\nshare1' 

83 self.glusterfs_server2_volumes = 'manila-share-2-2G\nshare2' 

84 self.share1 = new_share( 

85 export_location=self.glusterfs_target1, 

86 status=constants.STATUS_AVAILABLE) 

87 self.share2 = new_share( 

88 export_location=self.glusterfs_target2, 

89 status=constants.STATUS_AVAILABLE) 

90 gmgr = common.GlusterManager 

91 self.gmgr1 = gmgr(self.glusterfs_server1, self._execute, None, None, 

92 requires={'volume': False}) 

93 self.gmgr2 = gmgr(self.glusterfs_server2, self._execute, None, None, 

94 requires={'volume': False}) 

95 self.glusterfs_volumes_dict = ( 

96 {'root@host1:/manila-share-1-1G': {'size': 1}, 

97 'root@host2:/manila-share-2-2G': {'size': 2}}) 

98 self.glusterfs_used_vols = set([ 

99 'root@host1:/manila-share-1-1G', 

100 'root@host2:/manila-share-2-2G']) 

101 

102 CONF.set_default('glusterfs_servers', 

103 [self.glusterfs_server1, self.glusterfs_server2]) 

104 CONF.set_default('glusterfs_server_password', 

105 'fake_password') 

106 CONF.set_default('glusterfs_path_to_private_key', 

107 '/fakepath/to/privatekey') 

108 CONF.set_default('glusterfs_volume_pattern', 

109 r'manila-share-\d+-#{size}G$') 

110 CONF.set_default('driver_handles_share_servers', False) 

111 

112 self.fake_driver = mock.Mock() 

113 self.mock_object(self.fake_driver, '_execute', 

114 self._execute) 

115 self.fake_driver.GLUSTERFS_VERSION_MIN = (3, 6) 

116 

117 self.fake_conf = config.Configuration(None) 

118 self.mock_object(tempfile, 'mkdtemp', 

119 mock.Mock(return_value='/tmp/tmpKGHKJ')) 

120 self.mock_object(common.GlusterManager, 'make_gluster_call') 

121 

122 self.fake_private_storage = mock.Mock() 

123 

124 with mock.patch.object(layout_volume.GlusterfsVolumeMappedLayout, 

125 '_glustermanager', 

126 side_effect=[self.gmgr1, self.gmgr2]): 

127 self._layout = layout_volume.GlusterfsVolumeMappedLayout( 

128 self.fake_driver, configuration=self.fake_conf, 

129 private_storage=self.fake_private_storage) 

130 self._layout.glusterfs_versions = {self.glusterfs_server1: ('3', '6'), 

131 self.glusterfs_server2: ('3', '7')} 

132 self.addCleanup(fake_utils.fake_execute_set_repliers, []) 

133 self.addCleanup(fake_utils.fake_execute_clear_log) 

134 

135 @ddt.data({"test_kwargs": {}, "requires": {"volume": True}}, 

136 {"test_kwargs": {'req_volume': False}, 

137 "requires": {"volume": False}}) 

138 @ddt.unpack 

139 def test_glustermanager(self, test_kwargs, requires): 

140 fake_obj = mock.Mock() 

141 self.mock_object(common, 'GlusterManager', 

142 mock.Mock(return_value=fake_obj)) 

143 

144 ret = self._layout._glustermanager(self.glusterfs_target1, 

145 **test_kwargs) 

146 

147 common.GlusterManager.assert_called_once_with( 

148 self.glusterfs_target1, self._execute, 

149 self._layout.configuration.glusterfs_path_to_private_key, 

150 self._layout.configuration.glusterfs_server_password, 

151 requires=requires) 

152 self.assertEqual(fake_obj, ret) 

153 

154 def test_compile_volume_pattern(self): 

155 volume_pattern = r'manila-share-\d+-(?P<size>\d+)G$' 

156 

157 ret = self._layout._compile_volume_pattern() 

158 

159 self.assertEqual(re.compile(volume_pattern), ret) 

160 

161 @ddt.data({'root@host1:/manila-share-1-1G': 'NONE', 

162 'root@host2:/manila-share-2-2G': None}, 

163 {'root@host1:/manila-share-1-1G': FAKE_UUID1, 

164 'root@host2:/manila-share-2-2G': None}, 

165 {'root@host1:/manila-share-1-1G': 'foobarbaz', 

166 'root@host2:/manila-share-2-2G': FAKE_UUID2}, 

167 {'root@host1:/manila-share-1-1G': FAKE_UUID1, 

168 'root@host2:/manila-share-2-2G': FAKE_UUID2}) 

169 def test_fetch_gluster_volumes(self, sharemark): 

170 vol1_qualified = 'root@host1:/manila-share-1-1G' 

171 gmgr_vol1 = common.GlusterManager(vol1_qualified) 

172 gmgr_vol1.get_vol_option = mock.Mock( 

173 return_value=sharemark[vol1_qualified]) 

174 vol2_qualified = 'root@host2:/manila-share-2-2G' 

175 gmgr_vol2 = common.GlusterManager(vol2_qualified) 

176 gmgr_vol2.get_vol_option = mock.Mock( 

177 return_value=sharemark[vol2_qualified]) 

178 self.mock_object( 

179 self.gmgr1, 'gluster_call', 

180 mock.Mock(return_value=(self.glusterfs_server1_volumes, ''))) 

181 self.mock_object( 

182 self.gmgr2, 'gluster_call', 

183 mock.Mock(return_value=(self.glusterfs_server2_volumes, ''))) 

184 _glustermanager_calls = (self.gmgr1, gmgr_vol1, self.gmgr2, gmgr_vol2) 

185 self.mock_object(self._layout, '_glustermanager', 

186 mock.Mock(side_effect=_glustermanager_calls)) 

187 expected_output = {} 

188 for q, d in self.glusterfs_volumes_dict.items(): 

189 if sharemark[q] not in (FAKE_UUID1, FAKE_UUID2): 

190 expected_output[q] = d 

191 

192 ret = self._layout._fetch_gluster_volumes() 

193 

194 test_args = ('volume', 'list') 

195 self.gmgr1.gluster_call.assert_called_once_with(*test_args, 

196 log=mock.ANY) 

197 self.gmgr2.gluster_call.assert_called_once_with(*test_args, 

198 log=mock.ANY) 

199 gmgr_vol1.get_vol_option.assert_called_once_with( 

200 'user.manila-share') 

201 gmgr_vol2.get_vol_option.assert_called_once_with( 

202 'user.manila-share') 

203 self.assertEqual(expected_output, ret) 

204 

205 def test_fetch_gluster_volumes_no_filter_used(self): 

206 vol1_qualified = 'root@host1:/manila-share-1-1G' 

207 gmgr_vol1 = common.GlusterManager(vol1_qualified) 

208 gmgr_vol1.get_vol_option = mock.Mock() 

209 vol2_qualified = 'root@host2:/manila-share-2-2G' 

210 gmgr_vol2 = common.GlusterManager(vol2_qualified) 

211 gmgr_vol2.get_vol_option = mock.Mock() 

212 self.mock_object( 

213 self.gmgr1, 'gluster_call', 

214 mock.Mock(return_value=(self.glusterfs_server1_volumes, ''))) 

215 self.mock_object( 

216 self.gmgr2, 'gluster_call', 

217 mock.Mock(return_value=(self.glusterfs_server2_volumes, ''))) 

218 _glustermanager_calls = (self.gmgr1, gmgr_vol1, self.gmgr2, gmgr_vol2) 

219 self.mock_object(self._layout, '_glustermanager', 

220 mock.Mock(side_effect=_glustermanager_calls)) 

221 expected_output = self.glusterfs_volumes_dict 

222 

223 ret = self._layout._fetch_gluster_volumes(filter_used=False) 

224 

225 test_args = ('volume', 'list') 

226 self.gmgr1.gluster_call.assert_called_once_with(*test_args, 

227 log=mock.ANY) 

228 self.gmgr2.gluster_call.assert_called_once_with(*test_args, 

229 log=mock.ANY) 

230 self.assertFalse(gmgr_vol1.get_vol_option.called) 

231 self.assertFalse(gmgr_vol2.get_vol_option.called) 

232 self.assertEqual(expected_output, ret) 

233 

234 def test_fetch_gluster_volumes_no_keymatch(self): 

235 vol1_qualified = 'root@host1:/manila-share-1' 

236 gmgr_vol1 = common.GlusterManager(vol1_qualified) 

237 gmgr_vol1.get_vol_option = mock.Mock(return_value=None) 

238 self._layout.configuration.glusterfs_servers = [self.glusterfs_server1] 

239 self.mock_object( 

240 self.gmgr1, 'gluster_call', 

241 mock.Mock(return_value=('manila-share-1', ''))) 

242 _glustermanager_calls = (self.gmgr1, gmgr_vol1) 

243 self.mock_object(self._layout, '_glustermanager', 

244 mock.Mock(side_effect=_glustermanager_calls)) 

245 self.mock_object(self._layout, 'volume_pattern', 

246 re.compile(r'manila-share-\d+(-(?P<size>\d+)G)?$')) 

247 expected_output = {'root@host1:/manila-share-1': {'size': None}} 

248 

249 ret = self._layout._fetch_gluster_volumes() 

250 

251 test_args = ('volume', 'list') 

252 self.gmgr1.gluster_call.assert_called_once_with(*test_args, 

253 log=mock.ANY) 

254 self.assertEqual(expected_output, ret) 

255 

256 def test_fetch_gluster_volumes_error(self): 

257 test_args = ('volume', 'list') 

258 

259 def raise_exception(*args, **kwargs): 

260 if args == test_args: 260 ↛ exitline 260 didn't return from function 'raise_exception' because the condition on line 260 was always true

261 raise exception.GlusterfsException() 

262 

263 self._layout.configuration.glusterfs_servers = [self.glusterfs_server1] 

264 self.mock_object(self.gmgr1, 'gluster_call', 

265 mock.Mock(side_effect=raise_exception)) 

266 self.mock_object(self._layout, '_glustermanager', 

267 mock.Mock(return_value=self.gmgr1)) 

268 self.mock_object(layout_volume.LOG, 'error') 

269 

270 self.assertRaises(exception.GlusterfsException, 

271 self._layout._fetch_gluster_volumes) 

272 

273 self.gmgr1.gluster_call.assert_called_once_with(*test_args, 

274 log=mock.ANY) 

275 

276 def test_do_setup(self): 

277 self._layout.configuration.glusterfs_servers = [self.glusterfs_server1] 

278 self.mock_object(self.gmgr1, 'get_gluster_version', 

279 mock.Mock(return_value=('3', '6'))) 

280 self.mock_object(self._layout, '_glustermanager', 

281 mock.Mock(return_value=self.gmgr1)) 

282 self.mock_object(self._layout, '_fetch_gluster_volumes', 

283 mock.Mock(return_value=self.glusterfs_volumes_dict)) 

284 self.mock_object(self._layout, '_check_mount_glusterfs') 

285 self._layout.gluster_used_vols = self.glusterfs_used_vols 

286 self.mock_object(layout_volume.LOG, 'warning') 

287 

288 self._layout.do_setup(self._context) 

289 

290 self._layout._fetch_gluster_volumes.assert_called_once_with( 

291 filter_used=False) 

292 self._layout._check_mount_glusterfs.assert_called_once_with() 

293 self.gmgr1.get_gluster_version.assert_called_once_with() 

294 

295 def test_do_setup_unsupported_glusterfs_version(self): 

296 self._layout.configuration.glusterfs_servers = [self.glusterfs_server1] 

297 self.mock_object(self.gmgr1, 'get_gluster_version', 

298 mock.Mock(return_value=('3', '5'))) 

299 self.mock_object(self._layout, '_glustermanager', 

300 mock.Mock(return_value=self.gmgr1)) 

301 

302 self.assertRaises(exception.GlusterfsException, 

303 self._layout.do_setup, self._context) 

304 

305 self.gmgr1.get_gluster_version.assert_called_once_with() 

306 

307 @ddt.data(exception.GlusterfsException, RuntimeError) 

308 def test_do_setup_get_gluster_version_fails(self, exc): 

309 def raise_exception(*args, **kwargs): 

310 raise exc 

311 

312 self._layout.configuration.glusterfs_servers = [self.glusterfs_server1] 

313 self.mock_object(self.gmgr1, 'get_gluster_version', 

314 mock.Mock(side_effect=raise_exception)) 

315 self.mock_object(self._layout, '_glustermanager', 

316 mock.Mock(return_value=self.gmgr1)) 

317 

318 self.assertRaises(exc, self._layout.do_setup, self._context) 

319 

320 self.gmgr1.get_gluster_version.assert_called_once_with() 

321 

322 def test_do_setup_glusterfs_no_volumes_provided_by_backend(self): 

323 self._layout.configuration.glusterfs_servers = [self.glusterfs_server1] 

324 self.mock_object(self.gmgr1, 'get_gluster_version', 

325 mock.Mock(return_value=('3', '6'))) 

326 self.mock_object(self._layout, '_glustermanager', 

327 mock.Mock(return_value=self.gmgr1)) 

328 self.mock_object(self._layout, '_fetch_gluster_volumes', 

329 mock.Mock(return_value={})) 

330 

331 self.assertRaises(exception.GlusterfsException, 

332 self._layout.do_setup, self._context) 

333 

334 self._layout._fetch_gluster_volumes.assert_called_once_with( 

335 filter_used=False) 

336 

337 def test_share_manager(self): 

338 self.mock_object(self._layout, '_glustermanager', 

339 mock.Mock(return_value=self.gmgr1)) 

340 self.mock_object(self._layout.private_storage, 

341 'get', mock.Mock(return_value='host1:/gv1')) 

342 

343 ret = self._layout._share_manager(self.share1) 

344 

345 self._layout.private_storage.get.assert_called_once_with( 

346 self.share1['id'], 'volume') 

347 self._layout._glustermanager.assert_called_once_with('host1:/gv1') 

348 self.assertEqual(self.gmgr1, ret) 

349 

350 def test_share_manager_no_privdata(self): 

351 self.mock_object(self._layout.private_storage, 

352 'get', mock.Mock(return_value=None)) 

353 

354 ret = self._layout._share_manager(self.share1) 

355 

356 self._layout.private_storage.get.assert_called_once_with( 

357 self.share1['id'], 'volume') 

358 self.assertIsNone(ret) 

359 

360 def test_ensure_share(self): 

361 share = self.share1 

362 gmgr1 = common.GlusterManager(self.glusterfs_target1, self._execute, 

363 None, None) 

364 gmgr1.set_vol_option = mock.Mock() 

365 self.mock_object(self._layout, '_share_manager', 

366 mock.Mock(return_value=gmgr1)) 

367 

368 self._layout.ensure_share(self._context, share) 

369 

370 self._layout._share_manager.assert_called_once_with(share) 

371 self.assertIn(self.glusterfs_target1, self._layout.gluster_used_vols) 

372 gmgr1.set_vol_option.assert_called_once_with( 

373 'user.manila-share', share['id']) 

374 

375 @ddt.data({"voldict": {"host:/share2G": {"size": 2}}, "used_vols": set(), 

376 "size": 1, "expected": "host:/share2G"}, 

377 {"voldict": {"host:/share2G": {"size": 2}}, "used_vols": set(), 

378 "size": 2, "expected": "host:/share2G"}, 

379 {"voldict": {"host:/share2G": {"size": 2}}, "used_vols": set(), 

380 "size": None, "expected": "host:/share2G"}, 

381 {"voldict": {"host:/share2G": {"size": 2}, 

382 "host:/share": {"size": None}}, 

383 "used_vols": set(["host:/share2G"]), "size": 1, 

384 "expected": "host:/share"}, 

385 {"voldict": {"host:/share2G": {"size": 2}, 

386 "host:/share": {"size": None}}, 

387 "used_vols": set(["host:/share2G"]), "size": 2, 

388 "expected": "host:/share"}, 

389 {"voldict": {"host:/share2G": {"size": 2}, 

390 "host:/share": {"size": None}}, 

391 "used_vols": set(["host:/share2G"]), "size": 3, 

392 "expected": "host:/share"}, 

393 {"voldict": {"host:/share2G": {"size": 2}, 

394 "host:/share": {"size": None}}, 

395 "used_vols": set(["host:/share2G"]), "size": None, 

396 "expected": "host:/share"}, 

397 {"voldict": {"host:/share": {}}, "used_vols": set(), "size": 1, 

398 "expected": "host:/share"}, 

399 {"voldict": {"host:/share": {}}, "used_vols": set(), 

400 "size": None, "expected": "host:/share"}) 

401 @ddt.unpack 

402 def test_pop_gluster_vol(self, voldict, used_vols, size, expected): 

403 gmgr = common.GlusterManager 

404 gmgr1 = gmgr(expected, self._execute, None, None) 

405 self._layout._fetch_gluster_volumes = mock.Mock(return_value=voldict) 

406 self._layout.gluster_used_vols = used_vols 

407 self._layout._glustermanager = mock.Mock(return_value=gmgr1) 

408 self._layout.volume_pattern_keys = list(voldict.values())[0].keys() 

409 

410 result = self._layout._pop_gluster_vol(size=size) 

411 

412 self.assertEqual(expected, result) 

413 self.assertIn(result, used_vols) 

414 self._layout._fetch_gluster_volumes.assert_called_once_with() 

415 self._layout._glustermanager.assert_called_once_with(result) 

416 

417 @ddt.data({"voldict": {"share2G": {"size": 2}}, 

418 "used_vols": set(), "size": 3}, 

419 {"voldict": {"share2G": {"size": 2}}, 

420 "used_vols": set(["share2G"]), "size": None}) 

421 @ddt.unpack 

422 def test_pop_gluster_vol_excp(self, voldict, used_vols, size): 

423 self._layout._fetch_gluster_volumes = mock.Mock(return_value=voldict) 

424 self._layout.gluster_used_vols = used_vols 

425 self._layout.volume_pattern_keys = list(voldict.values())[0].keys() 

426 

427 self.assertRaises(exception.GlusterfsException, 

428 self._layout._pop_gluster_vol, size=size) 

429 

430 self._layout._fetch_gluster_volumes.assert_called_once_with() 

431 self.assertFalse( 

432 self.fake_driver._setup_via_manager.called) 

433 

434 def test_push_gluster_vol(self): 

435 self._layout.gluster_used_vols = set([ 

436 self.glusterfs_target1, self.glusterfs_target2]) 

437 

438 self._layout._push_gluster_vol(self.glusterfs_target2) 

439 

440 self.assertEqual(1, len(self._layout.gluster_used_vols)) 

441 self.assertNotIn(self.glusterfs_target2, 

442 self._layout.gluster_used_vols) 

443 

444 def test_push_gluster_vol_excp(self): 

445 self._layout.gluster_used_vols = set([self.glusterfs_target1]) 

446 self._layout.gluster_unused_vols_dict = {} 

447 

448 self.assertRaises(exception.GlusterfsException, 

449 self._layout._push_gluster_vol, 

450 self.glusterfs_target2) 

451 

452 @ddt.data({'vers_minor': '6', 

453 'dirs_to_ignore': []}, 

454 {'vers_minor': '7', 

455 'dirs_to_ignore': ['/tmp/tmpKGHKJ/.trashcan', 

456 '/tmp/tmpKGHKJ/.trashcan/internal_op']}) 

457 @ddt.unpack 

458 def test_wipe_gluster_vol(self, vers_minor, dirs_to_ignore): 

459 tmpdir = '/tmp/tmpKGHKJ' 

460 gmgr = common.GlusterManager 

461 gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) 

462 self._layout.glusterfs_versions = { 

463 self.glusterfs_server1: ('3', vers_minor)} 

464 

465 self.mock_object(tempfile, 'mkdtemp', 

466 mock.Mock(return_value=tmpdir)) 

467 self.mock_object(self.fake_driver, '_execute', mock.Mock()) 

468 self.mock_object(privsep_os, 'find') 

469 self.mock_object(common, '_mount_gluster_vol', mock.Mock()) 

470 self.mock_object(common, '_umount_gluster_vol', mock.Mock()) 

471 self.mock_object(shutil, 'rmtree', mock.Mock()) 

472 

473 self._layout._wipe_gluster_vol(gmgr1) 

474 

475 tempfile.mkdtemp.assert_called_once_with() 

476 common._mount_gluster_vol.assert_called_once_with( 

477 self.fake_driver._execute, gmgr1.export, 

478 tmpdir) 

479 privsep_os.find.assert_called_once_with( 

480 tmpdir, dirs_to_ignore=dirs_to_ignore, delete=True) 

481 common._umount_gluster_vol.assert_called_once_with( 

482 tmpdir) 

483 kwargs = {'ignore_errors': True} 

484 shutil.rmtree.assert_called_once_with(tmpdir, 

485 **kwargs) 

486 

487 def test_wipe_gluster_vol_mount_fail(self): 

488 tmpdir = '/tmp/tmpKGHKJ' 

489 gmgr = common.GlusterManager 

490 gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) 

491 self._layout.glusterfs_versions = { 

492 self.glusterfs_server1: ('3', '6')} 

493 self.mock_object(tempfile, 'mkdtemp', 

494 mock.Mock(return_value=tmpdir)) 

495 self.mock_object(self.fake_driver, '_execute', mock.Mock()) 

496 self.mock_object(common, '_mount_gluster_vol', 

497 mock.Mock(side_effect=exception.GlusterfsException)) 

498 self.mock_object(common, '_umount_gluster_vol', mock.Mock()) 

499 self.mock_object(shutil, 'rmtree', mock.Mock()) 

500 

501 self.assertRaises(exception.GlusterfsException, 

502 self._layout._wipe_gluster_vol, 

503 gmgr1) 

504 

505 tempfile.mkdtemp.assert_called_once_with() 

506 common._mount_gluster_vol.assert_called_once_with( 

507 self.fake_driver._execute, gmgr1.export, 

508 tmpdir) 

509 self.assertFalse(self.fake_driver._execute.called) 

510 self.assertFalse(common._umount_gluster_vol.called) 

511 kwargs = {'ignore_errors': True} 

512 shutil.rmtree.assert_called_once_with(tmpdir, 

513 **kwargs) 

514 

515 def test_wipe_gluster_vol_error_wiping_gluster_vol(self): 

516 tmpdir = '/tmp/tmpKGHKJ' 

517 gmgr = common.GlusterManager 

518 gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) 

519 self._layout.glusterfs_versions = { 

520 self.glusterfs_server1: ('3', '6')} 

521 self.mock_object(tempfile, 'mkdtemp', 

522 mock.Mock(return_value=tmpdir)) 

523 self.mock_object( 

524 privsep_os, 'find', 

525 mock.Mock(side_effect=exception.ProcessExecutionError)) 

526 self.mock_object(common, '_mount_gluster_vol', mock.Mock()) 

527 self.mock_object(common, '_umount_gluster_vol', mock.Mock()) 

528 self.mock_object(shutil, 'rmtree', mock.Mock()) 

529 

530 self.assertRaises(exception.GlusterfsException, 

531 self._layout._wipe_gluster_vol, 

532 gmgr1) 

533 

534 tempfile.mkdtemp.assert_called_once_with() 

535 common._mount_gluster_vol.assert_called_once_with( 

536 self.fake_driver._execute, gmgr1.export, 

537 tmpdir) 

538 common._umount_gluster_vol.assert_called_once_with(tmpdir) 

539 kwargs = {'ignore_errors': True} 

540 shutil.rmtree.assert_called_once_with(tmpdir, 

541 **kwargs) 

542 

543 def test_create_share(self): 

544 self._layout._pop_gluster_vol = mock.Mock( 

545 return_value=self.glusterfs_target1) 

546 gmgr1 = common.GlusterManager(self.glusterfs_target1) 

547 gmgr1.set_vol_option = mock.Mock() 

548 self.mock_object(self._layout, '_glustermanager', 

549 mock.Mock(return_value=gmgr1)) 

550 self.mock_object(self.fake_driver, '_setup_via_manager', 

551 mock.Mock(return_value='host1:/gv1')) 

552 

553 share = new_share() 

554 exp_locn = self._layout.create_share(self._context, share) 

555 

556 self._layout._pop_gluster_vol.assert_called_once_with(share['size']) 

557 self.fake_driver._setup_via_manager.assert_called_once_with( 

558 {'manager': gmgr1, 'share': share}) 

559 self._layout.private_storage.update.assert_called_once_with( 

560 share['id'], {'volume': self.glusterfs_target1}) 

561 gmgr1.set_vol_option.assert_called_once_with( 

562 'user.manila-share', share['id']) 

563 self.assertEqual('host1:/gv1', exp_locn) 

564 

565 def test_create_share_error(self): 

566 self._layout._pop_gluster_vol = mock.Mock( 

567 side_effect=exception.GlusterfsException) 

568 

569 share = new_share() 

570 self.assertRaises(exception.GlusterfsException, 

571 self._layout.create_share, self._context, share) 

572 

573 self._layout._pop_gluster_vol.assert_called_once_with( 

574 share['size']) 

575 

576 @ddt.data(None, '', 'Eeyore') 

577 def test_delete_share(self, clone_of): 

578 self._layout._push_gluster_vol = mock.Mock() 

579 self._layout._wipe_gluster_vol = mock.Mock() 

580 gmgr = common.GlusterManager 

581 gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) 

582 gmgr1.set_vol_option = mock.Mock() 

583 gmgr1.get_vol_option = mock.Mock(return_value=clone_of) 

584 new_vol_addr = self.glusterfs_target1 

585 self.mock_object(self._layout, '_glustermanager', 

586 mock.Mock(return_value=gmgr1)) 

587 self._layout.gluster_used_vols = set([self.glusterfs_target1]) 

588 

589 self._layout.delete_share(self._context, self.share1) 

590 

591 gmgr1.get_vol_option.assert_called_once_with( 

592 'user.manila-cloned-from') 

593 self._layout._wipe_gluster_vol.assert_called_once_with(gmgr1) 

594 self.assertIn(new_vol_addr, self._layout.gluster_used_vols) 

595 self._layout._push_gluster_vol.assert_called_once_with( 

596 self.glusterfs_target1) 

597 self._layout.private_storage.delete.assert_called_once_with( 

598 self.share1['id']) 

599 gmgr1.set_vol_option.assert_has_calls([ 

600 mock.call('user.manila-share', 'NONE'), 

601 mock.call('nfs.disable', 'on') 

602 ]) 

603 

604 def test_delete_share_clone(self): 

605 self._layout._push_gluster_vol = mock.Mock() 

606 self._layout._wipe_gluster_vol = mock.Mock() 

607 gmgr = common.GlusterManager 

608 gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) 

609 gmgr1.gluster_call = mock.Mock() 

610 gmgr1.get_vol_option = mock.Mock(return_value=FAKE_UUID1) 

611 self.mock_object(self._layout, '_glustermanager', 

612 mock.Mock(return_value=gmgr1)) 

613 self._layout.gluster_used_vols = set([self.glusterfs_target1]) 

614 

615 self._layout.delete_share(self._context, self.share1) 

616 

617 gmgr1.get_vol_option.assert_called_once_with( 

618 'user.manila-cloned-from') 

619 self.assertFalse(self._layout._wipe_gluster_vol.called) 

620 self._layout._push_gluster_vol.assert_called_once_with( 

621 self.glusterfs_target1) 

622 self._layout.private_storage.delete.assert_called_once_with( 

623 self.share1['id']) 

624 gmgr1.gluster_call.assert_called_once_with( 

625 'volume', 'delete', 'gv1') 

626 

627 def test_delete_share_error(self): 

628 self._layout._wipe_gluster_vol = mock.Mock() 

629 self._layout._wipe_gluster_vol.side_effect = ( 

630 exception.GlusterfsException) 

631 self._layout._push_gluster_vol = mock.Mock() 

632 gmgr = common.GlusterManager 

633 gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) 

634 gmgr1.get_vol_option = mock.Mock(return_value=None) 

635 self.mock_object(self._layout, '_glustermanager', 

636 mock.Mock(return_value=gmgr1)) 

637 self._layout.gluster_used_vols = set([self.glusterfs_target1]) 

638 

639 self.assertRaises(exception.GlusterfsException, 

640 self._layout.delete_share, self._context, 

641 self.share1) 

642 

643 self._layout._wipe_gluster_vol.assert_called_once_with(gmgr1) 

644 self.assertFalse(self._layout._push_gluster_vol.called) 

645 

646 def test_delete_share_missing_record(self): 

647 self.mock_object(self._layout, '_share_manager', 

648 mock.Mock(return_value=None)) 

649 

650 self._layout.delete_share(self._context, self.share1) 

651 

652 self._layout._share_manager.assert_called_once_with(self.share1) 

653 

654 def test_create_snapshot(self): 

655 self._layout.gluster_nosnap_vols_dict = {} 

656 self._layout.glusterfs_versions = {self.glusterfs_server1: ('3', '6')} 

657 gmgr = common.GlusterManager 

658 gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) 

659 self._layout.gluster_used_vols = set([self.glusterfs_target1]) 

660 self.mock_object(gmgr1, 'gluster_call', 

661 mock.Mock( 

662 side_effect=(glusterXMLOut(ret=0, errno=0),))) 

663 self.mock_object(self._layout, '_glustermanager', 

664 mock.Mock(return_value=gmgr1)) 

665 

666 snapshot = { 

667 'id': 'fake_snap_id', 

668 'share_id': self.share1['id'], 

669 'share': self.share1 

670 } 

671 ret = self._layout.create_snapshot(self._context, snapshot) 

672 

673 self.assertIsNone(ret) 

674 args = ('--xml', 'snapshot', 'create', 'manila-fake_snap_id', 

675 gmgr1.volume) 

676 gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) 

677 

678 @ddt.data({'side_effect': (glusterXMLOut(ret=-1, errno=2),), 

679 '_exception': exception.GlusterfsException}, 

680 {'side_effect': (('', ''),), 

681 '_exception': exception.GlusterfsException}) 

682 @ddt.unpack 

683 def test_create_snapshot_error(self, side_effect, _exception): 

684 self._layout.gluster_nosnap_vols_dict = {} 

685 self._layout.glusterfs_versions = {self.glusterfs_server1: ('3', '6')} 

686 gmgr = common.GlusterManager 

687 gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) 

688 self._layout.gluster_used_vols = set([self.glusterfs_target1]) 

689 self.mock_object(gmgr1, 'gluster_call', 

690 mock.Mock(side_effect=side_effect)) 

691 self.mock_object(self._layout, '_glustermanager', 

692 mock.Mock(return_value=gmgr1)) 

693 

694 snapshot = { 

695 'id': 'fake_snap_id', 

696 'share_id': self.share1['id'], 

697 'share': self.share1 

698 } 

699 self.assertRaises(_exception, self._layout.create_snapshot, 

700 self._context, snapshot) 

701 

702 args = ('--xml', 'snapshot', 'create', 'manila-fake_snap_id', 

703 gmgr1.volume) 

704 gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) 

705 

706 @ddt.data({"vers_minor": '6', "exctype": exception.GlusterfsException}, 

707 {"vers_minor": '7', 

708 "exctype": exception.ShareSnapshotNotSupported}) 

709 @ddt.unpack 

710 def test_create_snapshot_no_snap(self, vers_minor, exctype): 

711 self._layout.gluster_nosnap_vols_dict = {} 

712 self._layout.glusterfs_versions = { 

713 self.glusterfs_server1: ('3', vers_minor)} 

714 gmgr = common.GlusterManager 

715 gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) 

716 self._layout.gluster_used_vols = set([self.glusterfs_target1]) 

717 self.mock_object(gmgr1, 'gluster_call', 

718 mock.Mock( 

719 side_effect=(glusterXMLOut(ret=-1, errno=0),))) 

720 self.mock_object(self._layout, '_glustermanager', 

721 mock.Mock(return_value=gmgr1)) 

722 

723 snapshot = { 

724 'id': 'fake_snap_id', 

725 'share_id': self.share1['id'], 

726 'share': self.share1 

727 } 

728 self.assertRaises(exctype, self._layout.create_snapshot, self._context, 

729 snapshot) 

730 

731 args = ('--xml', 'snapshot', 'create', 'manila-fake_snap_id', 

732 gmgr1.volume) 

733 gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) 

734 

735 @ddt.data({"vers_minor": '6', "exctype": exception.GlusterfsException}, 

736 {"vers_minor": '7', 

737 "exctype": exception.ShareSnapshotNotSupported}) 

738 @ddt.unpack 

739 def test_create_snapshot_no_snap_cached(self, vers_minor, exctype): 

740 self._layout.gluster_nosnap_vols_dict = { 

741 self.glusterfs_target1: 'fake error'} 

742 self._layout.glusterfs_versions = { 

743 self.glusterfs_server1: ('3', vers_minor)} 

744 self._layout.gluster_used_vols = set([self.glusterfs_target1]) 

745 gmgr = common.GlusterManager 

746 gmgr1 = gmgr(self.glusterfs_target1, self._execute, None, None) 

747 self.mock_object(self._layout, '_share_manager', 

748 mock.Mock(return_value=gmgr1)) 

749 

750 snapshot = { 

751 'id': 'fake_snap_id', 

752 'share_id': self.share1['id'], 

753 'share': self.share1 

754 } 

755 self.assertRaises(exctype, self._layout.create_snapshot, self._context, 

756 snapshot) 

757 

758 def test_find_actual_backend_snapshot_name(self): 

759 gmgr = common.GlusterManager 

760 gmgr1 = gmgr(self.share1['export_location'], self._execute, None, None) 

761 self.mock_object(gmgr1, 'gluster_call', 

762 mock.Mock(return_value=('fake_snap_id_xyz', ''))) 

763 

764 snapshot = { 

765 'id': 'fake_snap_id', 

766 'share_id': self.share1['id'], 

767 'share': self.share1 

768 } 

769 ret = self._layout._find_actual_backend_snapshot_name(gmgr1, snapshot) 

770 

771 args = ('snapshot', 'list', gmgr1.volume, '--mode=script') 

772 gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) 

773 self.assertEqual('fake_snap_id_xyz', ret) 

774 

775 @ddt.data('this is too bad', 'fake_snap_id_xyx\nfake_snap_id_pqr') 

776 def test_find_actual_backend_snapshot_name_bad_snap_list(self, snaplist): 

777 gmgr = common.GlusterManager 

778 gmgr1 = gmgr(self.share1['export_location'], self._execute, None, None) 

779 self.mock_object(gmgr1, 'gluster_call', 

780 mock.Mock(return_value=(snaplist, ''))) 

781 

782 snapshot = { 

783 'id': 'fake_snap_id', 

784 'share_id': self.share1['id'], 

785 'share': self.share1 

786 } 

787 self.assertRaises(exception.GlusterfsException, 

788 self._layout._find_actual_backend_snapshot_name, 

789 gmgr1, snapshot) 

790 

791 args = ('snapshot', 'list', gmgr1.volume, '--mode=script') 

792 gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) 

793 

794 @ddt.data({'glusterfs_target': 'root@host1:/gv1', 

795 'glusterfs_server': 'root@host1'}, 

796 {'glusterfs_target': 'host1:/gv1', 

797 'glusterfs_server': 'host1'}) 

798 @ddt.unpack 

799 def test_create_share_from_snapshot(self, glusterfs_target, 

800 glusterfs_server): 

801 share = new_share() 

802 snapshot = { 

803 'id': 'fake_snap_id', 

804 'share_instance': new_share(export_location=glusterfs_target), 

805 'share_id': 'fake_share_id', 

806 } 

807 volume = ''.join(['manila-', share['id']]) 

808 new_vol_addr = ':/'.join([glusterfs_server, volume]) 

809 gmgr = common.GlusterManager 

810 old_gmgr = gmgr(glusterfs_target, self._execute, None, None) 

811 new_gmgr = gmgr(new_vol_addr, self._execute, None, None) 

812 self._layout.gluster_used_vols = set([glusterfs_target]) 

813 self._layout.glusterfs_versions = {glusterfs_server: ('3', '7')} 

814 self.mock_object(old_gmgr, 'gluster_call', 

815 mock.Mock(side_effect=[ 

816 ('', ''), ('', ''), ('', ''), ('', '')])) 

817 self.mock_object(new_gmgr, 'gluster_call', 

818 mock.Mock(side_effect=[('', ''), ('', '')])) 

819 self.mock_object(new_gmgr, 'get_vol_option', 

820 mock.Mock()) 

821 new_gmgr.get_vol_option.return_value = ( 

822 'glusterfs-server-1,client') 

823 self.mock_object(self._layout, '_find_actual_backend_snapshot_name', 

824 mock.Mock(return_value='fake_snap_id_xyz')) 

825 self.mock_object(self._layout, '_share_manager', 

826 mock.Mock(return_value=old_gmgr)) 

827 self.mock_object(self._layout, '_glustermanager', 

828 mock.Mock(return_value=new_gmgr)) 

829 self.mock_object(self.fake_driver, '_setup_via_manager', 

830 mock.Mock(return_value='host1:/gv1')) 

831 

832 ret = self._layout.create_share_from_snapshot( 

833 self._context, share, snapshot, None) 

834 

835 (self._layout._find_actual_backend_snapshot_name. 

836 assert_called_once_with(old_gmgr, snapshot)) 

837 args = (('snapshot', 'activate', 'fake_snap_id_xyz', 

838 'force', '--mode=script'), 

839 ('snapshot', 'clone', volume, 'fake_snap_id_xyz'), 

840 ('volume', 'start', volume)) 

841 old_gmgr.gluster_call.assert_has_calls( 

842 [mock.call(*a, log=mock.ANY) for a in args]) 

843 args = (('volume', 'set', volume, 'user.manila-share', share['id']), 

844 ('volume', 'set', volume, 'user.manila-cloned-from', 

845 snapshot['share_id'])) 

846 new_gmgr.gluster_call.assert_has_calls( 

847 [mock.call(*a, log=mock.ANY) for a in args], any_order=True) 

848 self._layout._share_manager.assert_called_once_with( 

849 snapshot['share_instance']) 

850 self._layout._glustermanager.assert_called_once_with( 

851 gmgr.parse(new_vol_addr)) 

852 self._layout.driver._setup_via_manager.assert_called_once_with( 

853 {'manager': new_gmgr, 'share': share}, 

854 {'manager': old_gmgr, 'share': snapshot['share_instance']}) 

855 self._layout.private_storage.update.assert_called_once_with( 

856 share['id'], {'volume': new_vol_addr}) 

857 self.assertIn( 

858 new_vol_addr, 

859 self._layout.gluster_used_vols) 

860 self.assertEqual(['host1:/gv1'], ret) 

861 

862 def test_create_share_from_snapshot_error_unsupported_gluster_version( 

863 self): 

864 glusterfs_target = 'root@host1:/gv1' 

865 glusterfs_server = 'root@host1' 

866 share = new_share() 

867 volume = ''.join(['manila-', share['id']]) 

868 new_vol_addr = ':/'.join([glusterfs_server, volume]) 

869 gmgr = common.GlusterManager 

870 old_gmgr = gmgr(glusterfs_target, self._execute, None, None) 

871 new_gmgr = gmgr(new_vol_addr, self._execute, None, None) 

872 self._layout.gluster_used_vols_dict = {glusterfs_target: old_gmgr} 

873 self._layout.glusterfs_versions = {glusterfs_server: ('3', '6')} 

874 self.mock_object( 

875 old_gmgr, 'gluster_call', 

876 mock.Mock(side_effect=[('', ''), ('', '')])) 

877 self.mock_object(new_gmgr, 'get_vol_option', 

878 mock.Mock()) 

879 new_gmgr.get_vol_option.return_value = ( 

880 'glusterfs-server-1,client') 

881 self.mock_object(self._layout, '_find_actual_backend_snapshot_name', 

882 mock.Mock(return_value='fake_snap_id_xyz')) 

883 self.mock_object(self._layout, '_share_manager', 

884 mock.Mock(return_value=old_gmgr)) 

885 self.mock_object(self._layout, '_glustermanager', 

886 mock.Mock(return_value=new_gmgr)) 

887 

888 snapshot = { 

889 'id': 'fake_snap_id', 

890 'share_instance': new_share(export_location=glusterfs_target) 

891 } 

892 self.assertRaises(exception.GlusterfsException, 

893 self._layout.create_share_from_snapshot, 

894 self._context, share, snapshot) 

895 

896 self.assertFalse( 

897 self._layout._find_actual_backend_snapshot_name.called) 

898 self.assertFalse(old_gmgr.gluster_call.called) 

899 self._layout._share_manager.assert_called_once_with( 

900 snapshot['share_instance']) 

901 self.assertFalse(self._layout._glustermanager.called) 

902 self.assertFalse(new_gmgr.get_vol_option.called) 

903 self.assertFalse(new_gmgr.gluster_call.called) 

904 self.assertNotIn(new_vol_addr, 

905 self._layout.glusterfs_versions.keys()) 

906 

907 def test_delete_snapshot(self): 

908 self._layout.gluster_nosnap_vols_dict = {} 

909 gmgr = common.GlusterManager 

910 gmgr1 = gmgr(self.share1['export_location'], self._execute, None, None) 

911 self._layout.gluster_used_vols = set([self.glusterfs_target1]) 

912 self.mock_object(self._layout, '_find_actual_backend_snapshot_name', 

913 mock.Mock(return_value='fake_snap_id_xyz')) 

914 self.mock_object( 

915 gmgr1, 'gluster_call', 

916 mock.Mock(return_value=glusterXMLOut(ret=0, errno=0))) 

917 self.mock_object(self._layout, '_glustermanager', 

918 mock.Mock(return_value=gmgr1)) 

919 snapshot = { 

920 'id': 'fake_snap_id', 

921 'share_id': self.share1['id'], 

922 'share': self.share1 

923 } 

924 ret = self._layout.delete_snapshot(self._context, snapshot) 

925 

926 self.assertIsNone(ret) 

927 args = ('--xml', 'snapshot', 'delete', 'fake_snap_id_xyz', 

928 '--mode=script') 

929 gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) 

930 (self._layout._find_actual_backend_snapshot_name. 

931 assert_called_once_with(gmgr1, snapshot)) 

932 

933 @ddt.data({'side_effect': (glusterXMLOut(ret=-1, errno=0),), 

934 '_exception': exception.GlusterfsException}, 

935 {'side_effect': (('', ''),), 

936 '_exception': exception.GlusterfsException}) 

937 @ddt.unpack 

938 def test_delete_snapshot_error(self, side_effect, _exception): 

939 self._layout.gluster_nosnap_vols_dict = {} 

940 gmgr = common.GlusterManager 

941 gmgr1 = gmgr(self.share1['export_location'], self._execute, None, None) 

942 self._layout.gluster_used_vols = set([self.glusterfs_target1]) 

943 self.mock_object(self._layout, '_find_actual_backend_snapshot_name', 

944 mock.Mock(return_value='fake_snap_id_xyz')) 

945 args = ('--xml', 'snapshot', 'delete', 'fake_snap_id_xyz', 

946 '--mode=script') 

947 self.mock_object( 

948 gmgr1, 'gluster_call', 

949 mock.Mock(side_effect=side_effect)) 

950 self.mock_object(self._layout, '_glustermanager', 

951 mock.Mock(return_value=gmgr1)) 

952 

953 snapshot = { 

954 'id': 'fake_snap_id', 

955 'share_id': self.share1['id'], 

956 'share': self.share1 

957 } 

958 self.assertRaises(_exception, self._layout.delete_snapshot, 

959 self._context, snapshot) 

960 

961 gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY) 

962 (self._layout._find_actual_backend_snapshot_name. 

963 assert_called_once_with(gmgr1, snapshot)) 

964 

965 @ddt.data( 

966 ('manage_existing', ('share', 'driver_options'), {}), 

967 ('unmanage', ('share',), {}), 

968 ('extend_share', ('share', 'new_size'), {'share_server': None}), 

969 ('shrink_share', ('share', 'new_size'), {'share_server': None})) 

970 def test_nonimplemented_methods(self, method_invocation): 

971 method, args, kwargs = method_invocation 

972 self.assertRaises(NotImplementedError, getattr(self._layout, method), 

973 *args, **kwargs)