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

450 statements  

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

1# Copyright 2012 NetApp 

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 the LVM driver module.""" 

16 

17import os 

18from unittest import mock 

19 

20import ddt 

21from oslo_concurrency import processutils 

22from oslo_config import cfg 

23from oslo_utils import timeutils 

24 

25from manila.common import constants as const 

26from manila import context 

27from manila import exception 

28from manila.privsep import common as privsep_common 

29from manila.privsep import filesystem 

30from manila.privsep import lvm as privsep_lvm 

31from manila.privsep import os as os_routines 

32from manila.share import configuration 

33from manila.share.drivers import lvm 

34from manila import test 

35from manila.tests.db import fakes as db_fakes 

36from manila.tests import fake_utils 

37from manila.tests.share.drivers import test_generic 

38 

39 

40CONF = cfg.CONF 

41 

42 

43def fake_share(**kwargs): 

44 share = { 

45 'id': 'fakeid', 

46 'name': 'fakename', 

47 'size': 1, 

48 'share_proto': 'NFS', 

49 'export_location': '127.0.0.1:/mnt/nfs/volume-00002', 

50 } 

51 share.update(kwargs) 

52 return db_fakes.FakeModel(share) 

53 

54 

55def fake_snapshot(**kwargs): 

56 snapshot = { 

57 'id': 'fakesnapshotid', 

58 'share_name': 'fakename', 

59 'share_id': 'fakeid', 

60 'name': 'fakesnapshotname', 

61 'share_proto': 'NFS', 

62 'export_location': '127.0.0.1:/mnt/nfs/volume-00002', 

63 'share': { 

64 'id': 'fakeid', 

65 'name': 'fakename', 

66 'size': 1, 

67 'share_proto': 'NFS', 

68 }, 

69 } 

70 snapshot.update(kwargs) 

71 return db_fakes.FakeModel(snapshot) 

72 

73 

74def fake_access(**kwargs): 

75 access = { 

76 'id': 'fakeaccid', 

77 'access_type': 'ip', 

78 'access_to': '10.0.0.2', 

79 'access_level': 'rw', 

80 'state': 'active', 

81 } 

82 access.update(kwargs) 

83 return db_fakes.FakeModel(access) 

84 

85 

86@ddt.ddt 

87class LVMShareDriverTestCase(test.TestCase): 

88 """Tests LVMShareDriver.""" 

89 

90 def setUp(self): 

91 super(LVMShareDriverTestCase, self).setUp() 

92 fake_utils.stub_out_utils_execute(self) 

93 self._context = context.get_admin_context() 

94 

95 CONF.set_default('lvm_share_volume_group', 'fakevg') 

96 CONF.set_default('lvm_share_export_ips', ['10.0.0.1', '10.0.0.2']) 

97 CONF.set_default('driver_handles_share_servers', False) 

98 CONF.set_default('reserved_share_percentage', 50) 

99 CONF.set_default('reserved_share_from_snapshot_percentage', 30) 

100 CONF.set_default('reserved_share_extend_percentage', 30) 

101 

102 self._helper_cifs = mock.Mock() 

103 self._helper_nfs = mock.Mock() 

104 self.fake_conf = configuration.Configuration(None) 

105 self._db = mock.Mock() 

106 self._os = lvm.os = mock.Mock() 

107 self._os.path.join = os.path.join 

108 self._driver = lvm.LVMShareDriver(self._db, 

109 configuration=self.fake_conf) 

110 self._driver._helpers = { 

111 'CIFS': self._helper_cifs, 

112 'NFS': self._helper_nfs, 

113 } 

114 

115 self.share = fake_share() 

116 self.access = fake_access() 

117 self.snapshot = fake_snapshot() 

118 self.server = { 

119 'public_addresses': self.fake_conf.lvm_share_export_ips, 

120 'instance_id': 'LVM', 

121 'lock_name': 'manila_lvm', 

122 } 

123 

124 # Used only to test compatibility with share manager 

125 self.share_server = "fake_share_server" 

126 

127 def tearDown(self): 

128 super(LVMShareDriverTestCase, self).tearDown() 

129 fake_utils.fake_execute_set_repliers([]) 

130 fake_utils.fake_execute_clear_log() 

131 

132 def test_do_setup(self): 

133 CONF.set_default('lvm_share_helpers', ['NFS=fakenfs']) 

134 lvm.importutils = mock.Mock() 

135 lvm.importutils.import_class.return_value = self._helper_nfs 

136 self._driver.do_setup(self._context) 

137 lvm.importutils.import_class.assert_has_calls([ 

138 mock.call('fakenfs') 

139 ]) 

140 

141 def test_check_for_setup_error(self): 

142 out, err = '\n fake1\n fakevg\n fake2\n', '' 

143 self.mock_object(privsep_lvm, 'list_vgs_get_name', 

144 mock.Mock(return_value=(out, err))) 

145 

146 self._driver.check_for_setup_error() 

147 

148 privsep_lvm.list_vgs_get_name.assert_called_once() 

149 

150 def test_check_for_setup_error_no_vg(self): 

151 out = '\n fake0\n fake1\n fake2\n' 

152 err = '' 

153 self.mock_object(privsep_lvm, 'list_vgs_get_name', 

154 mock.Mock(return_value=(out, err))) 

155 self.assertRaises(exception.InvalidParameterValue, 

156 self._driver.check_for_setup_error) 

157 

158 def test_check_for_setup_error_no_export_ips(self): 

159 out = '\n fake1\n fakevg\n fake2\n' 

160 err = '' 

161 self.mock_object(privsep_lvm, 'list_vgs_get_name', 

162 mock.Mock(return_value=(out, err))) 

163 

164 CONF.set_default('lvm_share_export_ips', None) 

165 self.assertRaises(exception.InvalidParameterValue, 

166 self._driver.check_for_setup_error) 

167 

168 def test_local_path_normal(self): 

169 share = fake_share(name='fake_sharename') 

170 CONF.set_default('lvm_share_volume_group', 'fake_vg') 

171 ret = self._driver._get_local_path(share) 

172 self.assertEqual('/dev/mapper/fake_vg-fake_sharename', ret) 

173 

174 def test_local_path_escapes(self): 

175 share = fake_share(name='fake-sharename') 

176 CONF.set_default('lvm_share_volume_group', 'fake-vg') 

177 ret = self._driver._get_local_path(share) 

178 self.assertEqual('/dev/mapper/fake--vg-fake--sharename', ret) 

179 

180 def test_create_share(self): 

181 CONF.set_default('lvm_share_mirrors', 0) 

182 self._driver._mount_device = mock.Mock() 

183 lv_create_mock = privsep_lvm.lvcreate = mock.Mock() 

184 lv_create_args = [ 

185 self.share['size'], self.share['name'], 

186 CONF.lvm_share_volume_group, 0, 0] 

187 self.mock_object(privsep_common, 'execute_with_retries') 

188 self.mock_object(filesystem, 'make_filesystem') 

189 self.mock_object(self._driver, '_get_mount_point_name', 

190 mock.Mock(return_value=self.share['name'])) 

191 

192 ret = self._driver.create_share(self._context, self.share, 

193 self.share_server) 

194 

195 self._driver._mount_device.assert_called_with( 

196 self.share, '/dev/mapper/fakevg-fakename') 

197 privsep_common.execute_with_retries.assert_called_once_with( 

198 lv_create_mock, lv_create_args, CONF.num_shell_tries) 

199 filesystem.make_filesystem.assert_called_once_with( 

200 'ext4', '/dev/mapper/fakevg-fakename') 

201 self.assertEqual(self._helper_nfs.create_exports.return_value, ret) 

202 

203 def test_create_share_from_snapshot(self): 

204 CONF.set_default('lvm_share_mirrors', 0) 

205 self._driver._mount_device = mock.Mock() 

206 snapshot_instance = { 

207 'snapshot_id': 'fakesnapshotid', 

208 'name': 'fakename' 

209 } 

210 mount_share = '/dev/mapper/fakevg-fakename' 

211 mount_snapshot = '/dev/mapper/fakevg-fakename' 

212 self._helper_nfs.create_export.return_value = 'fakelocation' 

213 lv_create_mock = privsep_lvm.lvcreate = mock.Mock() 

214 lv_create_args = [ 

215 self.share['size'], self.share['name'], 

216 CONF.lvm_share_volume_group, 0, 0] 

217 

218 self.mock_object(privsep_common, 'execute_with_retries') 

219 self.mock_object(os_routines, 'is_data_definition_direct_io_supported', 

220 mock.Mock(return_value=True)) 

221 self.mock_object(os_routines, 'data_definition') 

222 self.mock_object(os_routines, 'mount') 

223 self.mock_object(os_routines, 'chmod') 

224 self.mock_object(filesystem, 'make_filesystem') 

225 self.mock_object(filesystem, 'e2fsck') 

226 self.mock_object(filesystem, 'tune2fs') 

227 self.mock_object(self._driver, '_get_mount_point_name', 

228 mock.Mock(return_value=self.share['name'])) 

229 

230 self._driver.create_share_from_snapshot(self._context, 

231 self.share, 

232 snapshot_instance, 

233 self.share_server) 

234 

235 self._driver._mount_device.assert_called_with(self.share, 

236 mount_snapshot) 

237 privsep_common.execute_with_retries.assert_called_once_with( 

238 lv_create_mock, lv_create_args, 3) 

239 filesystem.make_filesystem.assert_called_once_with( 

240 'ext4', '/dev/mapper/fakevg-fakename') 

241 filesystem.e2fsck.assert_called_once_with( 

242 mount_share) 

243 filesystem.tune2fs.assert_called_once_with( 

244 mount_share) 

245 (os_routines.is_data_definition_direct_io_supported 

246 .assert_called_once_with( 

247 mount_snapshot, mount_share)) 

248 os_routines.data_definition.assert_called_once_with( 

249 mount_snapshot, mount_share, (self.share['size'] * 1024), 

250 use_direct_io=True) 

251 

252 def test_create_share_mirrors(self): 

253 share = fake_share(size='2048') 

254 CONF.set_default('lvm_share_mirrors', 2) 

255 lv_create_mock = privsep_lvm.lvcreate = mock.Mock() 

256 lv_create_args = [ 

257 '2048', self.share['name'], 

258 CONF.lvm_share_volume_group, 2, '2'] 

259 

260 self._driver._mount_device = mock.Mock() 

261 self.mock_object(privsep_common, 'execute_with_retries') 

262 self.mock_object(filesystem, 'make_filesystem') 

263 self.mock_object(self._driver, '_get_mount_point_name', 

264 mock.Mock(return_value=self.share['name'])) 

265 

266 ret = self._driver.create_share(self._context, share, 

267 self.share_server) 

268 

269 self._driver._mount_device.assert_called_with( 

270 share, '/dev/mapper/fakevg-fakename') 

271 privsep_common.execute_with_retries.assert_called_once_with( 

272 lv_create_mock, lv_create_args, 3) 

273 filesystem.make_filesystem.assert_called_once_with( 

274 'ext4', '/dev/mapper/fakevg-fakename') 

275 self.assertEqual(self._helper_nfs.create_exports.return_value, ret) 

276 

277 def test_deallocate_container(self): 

278 mock_lvremove = privsep_lvm.lvremove = mock.Mock() 

279 self.mock_object(privsep_common, 'execute_with_retries') 

280 

281 self._driver._deallocate_container(self.share['name']) 

282 

283 privsep_common.execute_with_retries.assert_called_once_with( 

284 mock_lvremove, [CONF.lvm_share_volume_group, self.share['name']], 3 

285 ) 

286 

287 def test_deallocate_container_error(self): 

288 def _fake_exec(*args, **kwargs): 

289 raise exception.ProcessExecutionError(stderr="error") 

290 

291 self.mock_object(privsep_common, 'execute_with_retries', _fake_exec) 

292 self.assertRaises(exception.ProcessExecutionError, 

293 self._driver._deallocate_container, 

294 self.share['name']) 

295 

296 @ddt.data( 

297 'Logical volume "fake/fake-volume" not found\n', 

298 'Failed to find logical volume "fake/fake-volume"\n') 

299 def test_deallocate_container_not_found_error(self, error_msg): 

300 def _fake_exec(*args, **kwargs): 

301 raise exception.ProcessExecutionError(stderr=error_msg) 

302 

303 self.mock_object(privsep_common, 'execute_with_retries', _fake_exec) 

304 self._driver._deallocate_container(self.share['name']) 

305 

306 @mock.patch.object(lvm.LVMShareDriver, '_update_share_stats', mock.Mock()) 

307 def test_get_share_stats(self): 

308 with mock.patch.object(self._driver, '_stats', mock.Mock) as stats: 

309 self.assertEqual(stats, self._driver.get_share_stats()) 

310 self.assertFalse(self._driver._update_share_stats.called) 

311 

312 @mock.patch.object(lvm.LVMShareDriver, '_update_share_stats', mock.Mock()) 

313 def test_get_share_stats_refresh(self): 

314 with mock.patch.object(self._driver, '_stats', mock.Mock) as stats: 

315 self.assertEqual(stats, 

316 self._driver.get_share_stats(refresh=True)) 

317 self._driver._update_share_stats.assert_called_once_with() 

318 

319 def test__unmount_device_not_mounted(self): 

320 mount_path = self._get_mount_path(self.share) 

321 error_msg = ( 

322 "umount: /opt/stack/data/manila/mnt/share-fake-share: not " 

323 "mounted.\n") 

324 umount_exception = exception.ProcessExecutionError(stderr=error_msg) 

325 self.mock_object( 

326 os_routines, 'umount', mock.Mock(side_effect=umount_exception)) 

327 self.mock_object(os_routines, 'rmdir') 

328 self._os.path.exists.return_value = True 

329 

330 self._driver._unmount_device(self.share, raise_if_missing=False) 

331 

332 self._os.path.exists.assert_called_with(mount_path) 

333 os_routines.umount.assert_called_once_with(mount_path) 

334 

335 def test__unmount_device_is_busy_error(self): 

336 error_msg = 'device is busy' 

337 umount_exception = exception.ProcessExecutionError(stderr=error_msg) 

338 self.mock_object( 

339 os_routines, 'umount', mock.Mock(side_effect=umount_exception)) 

340 self._os.path.exists.return_value = True 

341 mount_path = self._get_mount_path(self.share) 

342 

343 self.assertRaises(exception.ShareBusyException, 

344 self._driver._unmount_device, 

345 self.share) 

346 os_routines.umount.assert_called_once_with(mount_path) 

347 

348 def test__unmount_device_error(self): 

349 error_msg = 'fake error' 

350 mount_path = self._get_mount_path(self.share) 

351 umount_exception = exception.ProcessExecutionError(stderr=error_msg) 

352 self.mock_object( 

353 os_routines, 'umount', mock.Mock(side_effect=umount_exception)) 

354 self._os.path.exists.return_value = True 

355 

356 self.assertRaises(processutils.ProcessExecutionError, 

357 self._driver._unmount_device, 

358 self.share) 

359 self._os.path.exists.assert_called_with(mount_path) 

360 os_routines.umount.assert_called_once_with(mount_path) 

361 

362 def test__unmount_device_rmdir_error(self): 

363 error_msg = 'fake error' 

364 mount_path = self._get_mount_path(self.share) 

365 umount_exception = exception.ProcessExecutionError(stderr=error_msg) 

366 self.mock_object(os_routines, 'umount') 

367 self.mock_object(os_routines, 'rmdir', 

368 mock.Mock(side_effect=umount_exception)) 

369 self._os.path.exists.return_value = True 

370 

371 self.assertRaises(exception.ShareBackendException, 

372 self._driver._unmount_device, 

373 self.share) 

374 self._os.path.exists.assert_called_with(mount_path) 

375 os_routines.umount.assert_called_once_with(mount_path) 

376 os_routines.rmdir.assert_called_once_with(mount_path) 

377 

378 def test_create_snapshot(self): 

379 mock_lv_create = privsep_lvm.lvcreate = mock.Mock() 

380 orig_lv_name = "%s/%s" % (CONF.lvm_share_volume_group, 

381 self.snapshot['share_name']) 

382 device_path = '/dev/mapper/fakevg-%s' % self.snapshot['name'] 

383 lv_create_args = [ 

384 self.snapshot['share']['size'], self.snapshot['share']['name'], 

385 orig_lv_name] 

386 

387 self.mock_object(privsep_common, 'execute_with_retries') 

388 self.mock_object(filesystem, 'e2fsck') 

389 self.mock_object(filesystem, 'tune2fs') 

390 self.mock_object(os_routines, 'mount') 

391 self.mock_object(os_routines, 'chmod') 

392 

393 self._driver.create_snapshot(self._context, self.snapshot, 

394 self.share_server) 

395 mount_path = self._get_mount_path(self.snapshot) 

396 expected_exec = [ 

397 "mkdir -p " + mount_path, 

398 ] 

399 self.assertEqual(expected_exec, fake_utils.fake_execute_get_log()) 

400 privsep_common.execute_with_retries( 

401 mock_lv_create, lv_create_args, CONF.num_shell_tries) 

402 filesystem.e2fsck.assert_called_once_with(device_path) 

403 filesystem.tune2fs.assert_called_once_with(device_path) 

404 os_routines.mount.assert_called_once_with( 

405 "/dev/mapper/fakevg-fakesnapshotname", mount_path) 

406 os_routines.chmod.assert_called_once_with( 

407 '777', mount_path) 

408 

409 def test_ensure_share(self): 

410 device_name = '/dev/mapper/fakevg-fakename' 

411 self.mock_object(self._driver, '_get_mount_point_name', 

412 mock.Mock(return_value=self.share['name'])) 

413 with mock.patch.object(self._driver, 

414 '_mount_device', 

415 mock.Mock(return_value='fake_location')): 

416 self._driver.ensure_share(self._context, self.share, 

417 self.share_server) 

418 self._driver._mount_device.assert_called_with(self.share, 

419 device_name) 

420 self._helper_nfs.create_exports.assert_called_once_with( 

421 self.server, self.share['name'], recreate=True) 

422 

423 def test_delete_share(self): 

424 self.mock_object(self._driver, '_get_mount_point_name', 

425 mock.Mock(return_value=self.share['name'])) 

426 mount_path = self._get_mount_path(self.share) 

427 self._helper_nfs.remove_export(mount_path, self.share['name']) 

428 self._driver._delete_share(self._context, self.share) 

429 

430 def test_delete_snapshot(self): 

431 mount_path = self._get_mount_path(self.snapshot) 

432 self.mock_object(os_routines, 'umount') 

433 self.mock_object(os_routines, 'rmdir') 

434 self.mock_object(privsep_common, 'execute_with_retries') 

435 self.mock_object(self._driver, '_deallocate_container') 

436 

437 self._driver.delete_snapshot(self._context, self.snapshot, 

438 self.share_server) 

439 

440 os_routines.umount.assert_called_once_with(mount_path) 

441 os_routines.rmdir.assert_called_once_with(mount_path) 

442 self._driver._deallocate_container.assert_called_once_with( 

443 self.snapshot['name']) 

444 

445 def test_delete_share_invalid_share(self): 

446 self.mock_object(self._driver, '_unmount_device') 

447 self.mock_object(self._driver, '_deallocate_container') 

448 self._driver._get_helper = mock.Mock( 

449 side_effect=exception.InvalidShare(reason='fake')) 

450 self.mock_object(self._driver, '_get_mount_point_name', 

451 mock.Mock(return_value=self.share['name'])) 

452 

453 self._driver.delete_share(self._context, self.share, self.share_server) 

454 

455 self._driver._unmount_device.assert_called_once_with( 

456 self.share, raise_if_missing=False, retry_busy_device=True) 

457 self._driver._deallocate_container.assert_called_once_with( 

458 self.share['name']) 

459 

460 def test_delete_share_process_execution_error(self): 

461 self.mock_object( 

462 self._helper_nfs, 

463 'remove_export', 

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

465 self.mock_object(self._driver, '_get_mount_point_name', 

466 mock.Mock(return_value=self.share['name'])) 

467 

468 self._driver._delete_share(self._context, self.share) 

469 self._helper_nfs.remove_exports.assert_called_once_with( 

470 self.server, 

471 self.share['name']) 

472 

473 @ddt.data(const.ACCESS_LEVEL_RW, const.ACCESS_LEVEL_RO) 

474 def test_update_access(self, access_level): 

475 access_rules = [test_generic.get_fake_access_rule( 

476 '1.1.1.1', access_level), ] 

477 add_rules = [test_generic.get_fake_access_rule( 

478 '2.2.2.2', access_level), ] 

479 delete_rules = [test_generic.get_fake_access_rule( 

480 '3.3.3.3', access_level), ] 

481 self.mock_object(self._driver, '_get_mount_point_name', 

482 mock.Mock(return_value=self.share['name'])) 

483 self._driver.update_access(self._context, self.share, access_rules, 

484 add_rules=add_rules, 

485 delete_rules=delete_rules, 

486 update_rules=None, 

487 share_server=self.server) 

488 (self._driver._helpers[self.share['share_proto']]. 

489 update_access.assert_called_once_with( 

490 self.server, self.share['name'], 

491 access_rules, add_rules=add_rules, delete_rules=delete_rules)) 

492 

493 @ddt.data((['1001::1001/129'], False), 

494 (['1.1.1.256'], False), 

495 (['1001::1001'], [6]), 

496 ('1.1.1.0', [4]), 

497 (['1001::1001', '1.1.1.0'], [6, 4]), 

498 (['1001::1001/129', '1.1.1.0'], False)) 

499 @ddt.unpack 

500 def test_get_configured_ip_versions(self, configured_ips, 

501 configured_ip_version): 

502 CONF.set_default('lvm_share_export_ips', configured_ips) 

503 if configured_ip_version: 

504 self.assertEqual(configured_ip_version, 

505 self._driver.get_configured_ip_versions()) 

506 else: 

507 self.assertRaises(exception.InvalidInput, 

508 self._driver.get_configured_ip_versions) 

509 

510 def test_mount_device(self): 

511 mount_path = self._get_mount_path(self.share) 

512 self.mock_object(os_routines, 'mount') 

513 self.mock_object(os_routines, 'chmod') 

514 expected_exec = [ 

515 "mkdir -p %s" % (mount_path,), 

516 ] 

517 device_name = 'fakedevice' 

518 

519 ret = self._driver._mount_device(self.share, device_name) 

520 

521 self.assertEqual(expected_exec, fake_utils.fake_execute_get_log()) 

522 os_routines.mount.assert_called_once_with(device_name, mount_path) 

523 os_routines.chmod.assert_called_once_with('777', mount_path) 

524 self.assertEqual(mount_path, ret) 

525 

526 def test_mount_device_already(self): 

527 def exec_runner(*args, **kwargs): 

528 if 'mount' in args and '-l' not in args: 528 ↛ 529line 528 didn't jump to line 529 because the condition on line 528 was never true

529 raise exception.ProcessExecutionError() 

530 else: 

531 return 'fakedevice', '' 

532 

533 self.mock_object(self._driver, '_execute', exec_runner) 

534 self.mock_object(os_routines, 'mount') 

535 self.mock_object(os_routines, 'chmod') 

536 mount_path = self._get_mount_path(self.share) 

537 

538 ret = self._driver._mount_device(self.share, 'fakedevice') 

539 self.assertEqual(mount_path, ret) 

540 

541 def test_mount_device_error(self): 

542 self.mock_object(self._driver, '_execute') 

543 self.mock_object( 

544 os_routines, 'mount', 

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

546 self.mock_object( 

547 os_routines, 'list_mounts', 

548 mock.Mock(return_value=('fake', ''))) 

549 

550 self.assertRaises(exception.ProcessExecutionError, 

551 self._driver._mount_device, self.share, 'fakedevice') 

552 

553 def test_get_helper(self): 

554 share_cifs = fake_share(share_proto='CIFS') 

555 share_nfs = fake_share(share_proto='NFS') 

556 share_fake = fake_share(share_proto='FAKE') 

557 self.assertEqual(self._driver._get_helper(share_cifs), 

558 self._helper_cifs) 

559 self.assertEqual(self._driver._get_helper(share_nfs), 

560 self._helper_nfs) 

561 self.assertRaises(exception.InvalidShare, self._driver._get_helper, 

562 share_fake) 

563 

564 def _get_mount_path(self, share): 

565 return os.path.join(CONF.lvm_share_export_root, share['name']) 

566 

567 @ddt.data(True, False) 

568 def test__unmount_device_with_retry_busy_device(self, retry_busy_device): 

569 execute_sideeffects = [ 

570 exception.ProcessExecutionError(stderr='device is busy'), 

571 exception.ProcessExecutionError(stderr='target is busy'), 

572 None, None 

573 ] if retry_busy_device else [None, None] 

574 mount_path = self._get_mount_path(self.share) 

575 self._os.path.exists.return_value = True 

576 self.mock_object(os_routines, 'umount', mock.Mock( 

577 side_effect=execute_sideeffects)) 

578 self.mock_object(os_routines, 'rmdir') 

579 

580 self._driver._unmount_device(self.share, 

581 retry_busy_device=retry_busy_device) 

582 

583 num_of_times_umount_is_called = 3 if retry_busy_device else 1 

584 

585 self._os.path.exists.assert_called_with(mount_path) 

586 os_routines.umount.assert_has_calls([ 

587 mock.call(mount_path)] * num_of_times_umount_is_called) 

588 os_routines.rmdir.assert_called_once_with(mount_path) 

589 

590 def test_extend_share(self): 

591 local_path = self._driver._get_local_path(self.share) 

592 self.mock_object(self._driver, '_extend_container') 

593 self.mock_object(self._driver, '_execute') 

594 self._driver.extend_share(self.share, 3) 

595 self._driver._extend_container.assert_called_once_with(self.share, 

596 local_path, 3) 

597 

598 def test_ssh_exec_as_root(self): 

599 command = ['fake_command'] 

600 self.mock_object(self._driver, '_execute') 

601 self._driver._ssh_exec_as_root('fake_server', command) 

602 self._driver._execute.assert_called_once_with('fake_command', 

603 check_exit_code=True) 

604 

605 def test_ssh_exec_as_root_with_sudo(self): 

606 command = ['sudo', 'fake_command'] 

607 self.mock_object(self._driver, '_execute') 

608 self._driver._ssh_exec_as_root('fake_server', command) 

609 self._driver._execute.assert_called_once_with( 

610 'fake_command', run_as_root=True, check_exit_code=True) 

611 

612 def test_extend_container(self): 

613 mock_lvextend = privsep_lvm.lvextend = mock.Mock() 

614 

615 self.mock_object(privsep_common, 'execute_with_retries') 

616 self._driver._extend_container(self.share, 'device_name', 3) 

617 

618 privsep_common.execute_with_retries.assert_called_once_with( 

619 mock_lvextend, ['device_name', 3], CONF.num_shell_tries) 

620 

621 def test_get_share_server_pools(self): 

622 expected_result = [{ 

623 'pool_name': 'lvm-single-pool', 

624 'total_capacity_gb': 33, 

625 'free_capacity_gb': 22, 

626 'reserved_percentage': 0, 

627 'reserved_snapshot_percentage': 0, 

628 'reserved_share_extend_percentage': 0, 

629 'mount_point_name_support': True, 

630 }, ] 

631 out, err = "VSize 33g VFree 22g", None 

632 self.mock_object( 

633 privsep_lvm, 'get_vgs', mock.Mock(return_value=(out, err))) 

634 

635 self.assertEqual(expected_result, 

636 self._driver.get_share_server_pools()) 

637 

638 @ddt.data(True, False) 

639 def test_copy_volume_error(self, use_direct_io): 

640 src_str = 'src' 

641 dest_str = 'dest' 

642 self.mock_object( 

643 os_routines, 'is_data_definition_direct_io_supported', 

644 mock.Mock(return_value=use_direct_io)) 

645 self.mock_object( 

646 os_routines, 'data_definition', 

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

648 

649 self.assertRaises( 

650 exception.ShareBackendException, 

651 self._driver._copy_volume, src_str, dest_str, 1) 

652 (os_routines.is_data_definition_direct_io_supported 

653 .assert_called_once_with( 

654 src_str, dest_str)) 

655 os_routines.data_definition.assert_called_once_with( 

656 src_str, dest_str, (1 * 1024), use_direct_io=use_direct_io) 

657 

658 @ddt.data((['1.1.1.1'], 4), (['1001::1001'], 6)) 

659 @ddt.unpack 

660 def test_update_share_stats(self, configured_ip, version): 

661 CONF.set_default('lvm_share_export_ips', configured_ip) 

662 self.mock_object(self._driver, 'get_share_server_pools', 

663 mock.Mock(return_value='test-pool')) 

664 

665 self._driver._update_share_stats() 

666 self.assertEqual('LVM', self._driver._stats['share_backend_name']) 

667 self.assertEqual('NFS_CIFS', self._driver._stats['storage_protocol']) 

668 self.assertEqual(50, self._driver._stats['reserved_percentage']) 

669 self.assertTrue(self._driver._stats['snapshot_support']) 

670 self.assertEqual('LVMShareDriver', self._driver._stats['driver_name']) 

671 self.assertEqual('test-pool', self._driver._stats['pools']) 

672 self.assertEqual(version == 4, self._driver._stats['ipv4_support']) 

673 self.assertEqual(version == 6, self._driver._stats['ipv6_support']) 

674 

675 def test_revert_to_snapshot(self): 

676 share_local_path = '/dev/mapper/fakevg-fakename' 

677 snapshot_local_path = '/dev/mapper/fakevg-fakesnapshotname' 

678 mock_update_access = self.mock_object( 

679 self._helper_nfs, 'update_access') 

680 mock__unmount_device = self.mock_object( 

681 self._driver, '_unmount_device') 

682 mock_lvconvert = self.mock_object(privsep_lvm, 'lvconvert') 

683 mock_create_snapshot = self.mock_object( 

684 self._driver, '_create_snapshot') 

685 mock_mount_device = self.mock_object( 

686 self._driver, '_mount_device') 

687 mock_get_local_path = self.mock_object( 

688 self._driver, '_get_local_path', 

689 mock.Mock(side_effect=[share_local_path, snapshot_local_path])) 

690 self.mock_object(self._driver, '_get_mount_point_name', 

691 mock.Mock(return_value=self.snapshot['name'])) 

692 snapshot_parent_share = self.snapshot['share'] 

693 

694 self._driver.revert_to_snapshot(self._context, self.snapshot, 

695 [], [], self.share_server) 

696 self.assertEqual(4, mock_update_access.call_count) 

697 mock__unmount_device.assert_has_calls( 

698 [mock.call(self.snapshot), mock.call(self.snapshot['share'])]) 

699 mock_lvconvert.assert_called_once_with( 

700 CONF.lvm_share_volume_group, self.snapshot['name']) 

701 mock_create_snapshot.assert_called_once_with( 

702 self._context, self.snapshot) 

703 mock_mount_device.assert_has_calls( 

704 [mock.call(snapshot_parent_share, share_local_path), 

705 mock.call(self.snapshot, snapshot_local_path)] 

706 ) 

707 mock_get_local_path.assert_has_calls( 

708 [mock.call(snapshot_parent_share), 

709 mock.call(self.snapshot)]) 

710 

711 def test_snapshot_update_access(self): 

712 access_rules = [{ 

713 'access_type': 'ip', 

714 'access_to': '1.1.1.1', 

715 'access_level': 'ro', 

716 }] 

717 

718 add_rules = [{ 

719 'access_type': 'ip', 

720 'access_to': '2.2.2.2', 

721 'access_level': 'ro', 

722 }] 

723 

724 delete_rules = [{ 

725 'access_type': 'ip', 

726 'access_to': '3.3.3.3', 

727 'access_level': 'ro', 

728 }] 

729 

730 self._driver.snapshot_update_access(self._context, self.snapshot, 

731 access_rules, add_rules, 

732 delete_rules) 

733 

734 (self._driver._helpers[self.snapshot['share']['share_proto']]. 

735 update_access.assert_called_once_with( 

736 self.server, self.snapshot['name'], 

737 access_rules, add_rules=add_rules, delete_rules=delete_rules)) 

738 

739 @mock.patch.object(timeutils, 'utcnow', mock.Mock( 

740 return_value='fake_date')) 

741 def test_update_share_usage_size(self): 

742 mount_path = self._get_mount_path(self.share) 

743 self._os.path.exists.return_value = True 

744 self.mock_object( 

745 self._driver, 

746 '_execute', 

747 mock.Mock(return_value=( 

748 "Mounted on Used " 

749 + mount_path + " 1G", None))) 

750 

751 update_shares = self._driver.update_share_usage_size( 

752 self._context, [self.share, ]) 

753 self._os.path.exists.assert_called_with(mount_path) 

754 self.assertEqual( 

755 [{'id': 'fakeid', 'used_size': '1', 

756 'gathered_at': 'fake_date'}], 

757 update_shares) 

758 self._driver._execute.assert_called_once_with( 

759 'df', '-l', '--output=target,used', 

760 '--block-size=g') 

761 

762 @mock.patch.object(timeutils, 'utcnow', mock.Mock( 

763 return_value='fake_date')) 

764 def test_update_share_usage_size_multiple_share(self): 

765 share1 = fake_share(id='fakeid_get_fail', name='get_fail') 

766 share2 = fake_share(id='fakeid_success', name='get_success') 

767 share3 = fake_share(id='fakeid_not_exist', name='get_not_exist') 

768 

769 mount_path2 = self._get_mount_path(share2) 

770 mount_path3 = self._get_mount_path(share3) 

771 self._os.path.exists.side_effect = [True, True, False] 

772 self.mock_object( 

773 self._driver, 

774 '_execute', 

775 mock.Mock(return_value=( 

776 "Mounted on Used " 

777 + mount_path2 + " 1G", None))) 

778 

779 update_shares = self._driver.update_share_usage_size( 

780 self._context, [share1, share2, share3]) 

781 self._os.path.exists.assert_called_with(mount_path3) 

782 self.assertEqual( 

783 [{'gathered_at': 'fake_date', 

784 'id': 'fakeid_success', 'used_size': '1'}], 

785 update_shares) 

786 self._driver._execute.assert_called_with( 

787 'df', '-l', '--output=target,used', 

788 '--block-size=g') 

789 

790 def test_update_share_usage_size_fail(self): 

791 def _fake_exec(*args, **kwargs): 

792 raise exception.ProcessExecutionError(stderr="error") 

793 

794 self.mock_object(self._driver, '_execute', _fake_exec) 

795 self.assertRaises(exception.ProcessExecutionError, 

796 self._driver.update_share_usage_size, 

797 self._context, 

798 [self.share]) 

799 

800 def test_get_backend_info(self): 

801 backend_info = self._driver.get_backend_info(self._context) 

802 

803 self.assertEqual( 

804 {'export_ips': ','.join(self.server['public_addresses']), 

805 'db_version': mock.ANY}, 

806 backend_info) 

807 

808 def test_get_mount_point_name_with_mount_point_name(self): 

809 share = {'mount_point_name': 'fake_mp_name', 'name': 'fakename'} 

810 result = self._driver._get_mount_point_name(share) 

811 self.assertEqual(result, 'fake_mp_name') 

812 

813 def test_get_mount_point_name_without_mount_point_name(self): 

814 share = {'name': 'fakename'} 

815 result = self._driver._get_mount_point_name(share) 

816 self.assertEqual(result, 'fakename') 

817 

818 def test_get_mount_point_name_with_empty_mount_point_name(self): 

819 share = {'mount_point_name': '', 'name': 'fakename'} 

820 result = self._driver._get_mount_point_name(share) 

821 self.assertEqual(result, 'fakename') 

822 

823 def test_get_mount_point_name_with_none_mount_point_name(self): 

824 share = {'mount_point_name': None, 'name': 'fakename'} 

825 result = self._driver._get_mount_point_name(share) 

826 self.assertEqual(result, 'fakename') 

827 

828 def test_get_mount_point_name_without_name(self): 

829 share = {'mount_point_name': 'fake_mp_name'} 

830 result = self._driver._get_mount_point_name(share) 

831 self.assertEqual(result, 'fake_mp_name')