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
« 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.
16""" GlusterFS volume mapped share layout testcases.
17"""
19import re
20import shutil
21import tempfile
22from unittest import mock
24import ddt
25from oslo_config import cfg
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
38CONF = cfg.CONF
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
52def glusterXMLOut(**kwargs):
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>"""
61 return template % kwargs, ''
64FAKE_UUID1 = '11111111-1111-1111-1111-111111111111'
65FAKE_UUID2 = '22222222-2222-2222-2222-222222222222'
68@ddt.ddt
69class GlusterfsVolumeMappedLayoutTestCase(test.TestCase):
70 """Tests GlusterfsVolumeMappedLayout."""
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()
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'])
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)
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)
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')
122 self.fake_private_storage = mock.Mock()
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)
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))
144 ret = self._layout._glustermanager(self.glusterfs_target1,
145 **test_kwargs)
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)
154 def test_compile_volume_pattern(self):
155 volume_pattern = r'manila-share-\d+-(?P<size>\d+)G$'
157 ret = self._layout._compile_volume_pattern()
159 self.assertEqual(re.compile(volume_pattern), ret)
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
192 ret = self._layout._fetch_gluster_volumes()
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)
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
223 ret = self._layout._fetch_gluster_volumes(filter_used=False)
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)
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}}
249 ret = self._layout._fetch_gluster_volumes()
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)
256 def test_fetch_gluster_volumes_error(self):
257 test_args = ('volume', 'list')
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()
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')
270 self.assertRaises(exception.GlusterfsException,
271 self._layout._fetch_gluster_volumes)
273 self.gmgr1.gluster_call.assert_called_once_with(*test_args,
274 log=mock.ANY)
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')
288 self._layout.do_setup(self._context)
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()
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))
302 self.assertRaises(exception.GlusterfsException,
303 self._layout.do_setup, self._context)
305 self.gmgr1.get_gluster_version.assert_called_once_with()
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
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))
318 self.assertRaises(exc, self._layout.do_setup, self._context)
320 self.gmgr1.get_gluster_version.assert_called_once_with()
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={}))
331 self.assertRaises(exception.GlusterfsException,
332 self._layout.do_setup, self._context)
334 self._layout._fetch_gluster_volumes.assert_called_once_with(
335 filter_used=False)
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'))
343 ret = self._layout._share_manager(self.share1)
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)
350 def test_share_manager_no_privdata(self):
351 self.mock_object(self._layout.private_storage,
352 'get', mock.Mock(return_value=None))
354 ret = self._layout._share_manager(self.share1)
356 self._layout.private_storage.get.assert_called_once_with(
357 self.share1['id'], 'volume')
358 self.assertIsNone(ret)
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))
368 self._layout.ensure_share(self._context, share)
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'])
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()
410 result = self._layout._pop_gluster_vol(size=size)
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)
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()
427 self.assertRaises(exception.GlusterfsException,
428 self._layout._pop_gluster_vol, size=size)
430 self._layout._fetch_gluster_volumes.assert_called_once_with()
431 self.assertFalse(
432 self.fake_driver._setup_via_manager.called)
434 def test_push_gluster_vol(self):
435 self._layout.gluster_used_vols = set([
436 self.glusterfs_target1, self.glusterfs_target2])
438 self._layout._push_gluster_vol(self.glusterfs_target2)
440 self.assertEqual(1, len(self._layout.gluster_used_vols))
441 self.assertNotIn(self.glusterfs_target2,
442 self._layout.gluster_used_vols)
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 = {}
448 self.assertRaises(exception.GlusterfsException,
449 self._layout._push_gluster_vol,
450 self.glusterfs_target2)
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)}
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())
473 self._layout._wipe_gluster_vol(gmgr1)
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)
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())
501 self.assertRaises(exception.GlusterfsException,
502 self._layout._wipe_gluster_vol,
503 gmgr1)
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)
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())
530 self.assertRaises(exception.GlusterfsException,
531 self._layout._wipe_gluster_vol,
532 gmgr1)
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)
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'))
553 share = new_share()
554 exp_locn = self._layout.create_share(self._context, share)
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)
565 def test_create_share_error(self):
566 self._layout._pop_gluster_vol = mock.Mock(
567 side_effect=exception.GlusterfsException)
569 share = new_share()
570 self.assertRaises(exception.GlusterfsException,
571 self._layout.create_share, self._context, share)
573 self._layout._pop_gluster_vol.assert_called_once_with(
574 share['size'])
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])
589 self._layout.delete_share(self._context, self.share1)
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 ])
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])
615 self._layout.delete_share(self._context, self.share1)
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')
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])
639 self.assertRaises(exception.GlusterfsException,
640 self._layout.delete_share, self._context,
641 self.share1)
643 self._layout._wipe_gluster_vol.assert_called_once_with(gmgr1)
644 self.assertFalse(self._layout._push_gluster_vol.called)
646 def test_delete_share_missing_record(self):
647 self.mock_object(self._layout, '_share_manager',
648 mock.Mock(return_value=None))
650 self._layout.delete_share(self._context, self.share1)
652 self._layout._share_manager.assert_called_once_with(self.share1)
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))
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)
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)
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))
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)
702 args = ('--xml', 'snapshot', 'create', 'manila-fake_snap_id',
703 gmgr1.volume)
704 gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY)
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))
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)
731 args = ('--xml', 'snapshot', 'create', 'manila-fake_snap_id',
732 gmgr1.volume)
733 gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY)
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))
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)
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', '')))
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)
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)
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, '')))
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)
791 args = ('snapshot', 'list', gmgr1.volume, '--mode=script')
792 gmgr1.gluster_call.assert_called_once_with(*args, log=mock.ANY)
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'))
832 ret = self._layout.create_share_from_snapshot(
833 self._context, share, snapshot, None)
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)
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))
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)
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())
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)
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))
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))
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)
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))
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)