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
« 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."""
17import os
18from unittest import mock
20import ddt
21from oslo_concurrency import processutils
22from oslo_config import cfg
23from oslo_utils import timeutils
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
40CONF = cfg.CONF
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)
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)
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)
86@ddt.ddt
87class LVMShareDriverTestCase(test.TestCase):
88 """Tests LVMShareDriver."""
90 def setUp(self):
91 super(LVMShareDriverTestCase, self).setUp()
92 fake_utils.stub_out_utils_execute(self)
93 self._context = context.get_admin_context()
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)
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 }
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 }
124 # Used only to test compatibility with share manager
125 self.share_server = "fake_share_server"
127 def tearDown(self):
128 super(LVMShareDriverTestCase, self).tearDown()
129 fake_utils.fake_execute_set_repliers([])
130 fake_utils.fake_execute_clear_log()
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 ])
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)))
146 self._driver.check_for_setup_error()
148 privsep_lvm.list_vgs_get_name.assert_called_once()
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)
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)))
164 CONF.set_default('lvm_share_export_ips', None)
165 self.assertRaises(exception.InvalidParameterValue,
166 self._driver.check_for_setup_error)
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)
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)
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']))
192 ret = self._driver.create_share(self._context, self.share,
193 self.share_server)
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)
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]
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']))
230 self._driver.create_share_from_snapshot(self._context,
231 self.share,
232 snapshot_instance,
233 self.share_server)
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)
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']
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']))
266 ret = self._driver.create_share(self._context, share,
267 self.share_server)
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)
277 def test_deallocate_container(self):
278 mock_lvremove = privsep_lvm.lvremove = mock.Mock()
279 self.mock_object(privsep_common, 'execute_with_retries')
281 self._driver._deallocate_container(self.share['name'])
283 privsep_common.execute_with_retries.assert_called_once_with(
284 mock_lvremove, [CONF.lvm_share_volume_group, self.share['name']], 3
285 )
287 def test_deallocate_container_error(self):
288 def _fake_exec(*args, **kwargs):
289 raise exception.ProcessExecutionError(stderr="error")
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'])
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)
303 self.mock_object(privsep_common, 'execute_with_retries', _fake_exec)
304 self._driver._deallocate_container(self.share['name'])
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)
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()
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
330 self._driver._unmount_device(self.share, raise_if_missing=False)
332 self._os.path.exists.assert_called_with(mount_path)
333 os_routines.umount.assert_called_once_with(mount_path)
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)
343 self.assertRaises(exception.ShareBusyException,
344 self._driver._unmount_device,
345 self.share)
346 os_routines.umount.assert_called_once_with(mount_path)
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
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)
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
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)
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]
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')
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)
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)
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)
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')
437 self._driver.delete_snapshot(self._context, self.snapshot,
438 self.share_server)
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'])
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']))
453 self._driver.delete_share(self._context, self.share, self.share_server)
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'])
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']))
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'])
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))
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)
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'
519 ret = self._driver._mount_device(self.share, device_name)
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)
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', ''
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)
538 ret = self._driver._mount_device(self.share, 'fakedevice')
539 self.assertEqual(mount_path, ret)
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', '')))
550 self.assertRaises(exception.ProcessExecutionError,
551 self._driver._mount_device, self.share, 'fakedevice')
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)
564 def _get_mount_path(self, share):
565 return os.path.join(CONF.lvm_share_export_root, share['name'])
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')
580 self._driver._unmount_device(self.share,
581 retry_busy_device=retry_busy_device)
583 num_of_times_umount_is_called = 3 if retry_busy_device else 1
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)
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)
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)
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)
612 def test_extend_container(self):
613 mock_lvextend = privsep_lvm.lvextend = mock.Mock()
615 self.mock_object(privsep_common, 'execute_with_retries')
616 self._driver._extend_container(self.share, 'device_name', 3)
618 privsep_common.execute_with_retries.assert_called_once_with(
619 mock_lvextend, ['device_name', 3], CONF.num_shell_tries)
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)))
635 self.assertEqual(expected_result,
636 self._driver.get_share_server_pools())
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))
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)
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'))
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'])
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']
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)])
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 }]
718 add_rules = [{
719 'access_type': 'ip',
720 'access_to': '2.2.2.2',
721 'access_level': 'ro',
722 }]
724 delete_rules = [{
725 'access_type': 'ip',
726 'access_to': '3.3.3.3',
727 'access_level': 'ro',
728 }]
730 self._driver.snapshot_update_access(self._context, self.snapshot,
731 access_rules, add_rules,
732 delete_rules)
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))
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)))
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')
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')
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)))
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')
790 def test_update_share_usage_size_fail(self):
791 def _fake_exec(*args, **kwargs):
792 raise exception.ProcessExecutionError(stderr="error")
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])
800 def test_get_backend_info(self):
801 backend_info = self._driver.get_backend_info(self._context)
803 self.assertEqual(
804 {'export_ips': ','.join(self.server['public_addresses']),
805 'db_version': mock.ANY},
806 backend_info)
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')
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')
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')
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')
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')