Merge "[Share groups] Add scheduler filter ConsistentSnapshotFilter"

This commit is contained in:
Jenkins 2017-06-03 03:25:09 +00:00 committed by Gerrit Code Review
commit acecb8b5b9
23 changed files with 271 additions and 54 deletions

View File

@ -232,6 +232,7 @@ elif [[ "$DRIVER" == "dummy" ]]; then
iniset $TEMPEST_CONFIG share build_timeout 180 iniset $TEMPEST_CONFIG share build_timeout 180
iniset $TEMPEST_CONFIG share share_creation_retry_number 0 iniset $TEMPEST_CONFIG share share_creation_retry_number 0
iniset $TEMPEST_CONFIG share capability_storage_protocol 'NFS_CIFS' iniset $TEMPEST_CONFIG share capability_storage_protocol 'NFS_CIFS'
iniset $TEMPEST_CONFIG share capability_sg_consistent_snapshot_support 'pool'
iniset $TEMPEST_CONFIG share enable_protocols 'nfs,cifs' iniset $TEMPEST_CONFIG share enable_protocols 'nfs,cifs'
iniset $TEMPEST_CONFIG share suppress_errors_in_cleanup False iniset $TEMPEST_CONFIG share suppress_errors_in_cleanup False
iniset $TEMPEST_CONFIG share multitenancy_enabled True iniset $TEMPEST_CONFIG share multitenancy_enabled True

View File

@ -15,9 +15,9 @@
# under the License. # under the License.
""" """
The FilterScheduler is for creating shares. The FilterScheduler is for scheduling of share and share group creation.
You can customize this scheduler by specifying your own share Filters and You can customize this scheduler by specifying your own share/share group
Weighing Functions. filters and weighing functions.
""" """
from oslo_config import cfg from oslo_config import cfg
@ -392,7 +392,9 @@ class FilterScheduler(base.Scheduler):
} }
hosts = self.host_manager.get_filtered_hosts( hosts = self.host_manager.get_filtered_hosts(
all_hosts, filter_properties) all_hosts,
filter_properties,
CONF.scheduler_default_share_group_filters)
if not hosts: if not hosts:
return [] return []

View File

@ -0,0 +1,31 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from manila.scheduler.filters import base_host
class ConsistentSnapshotFilter(base_host.BaseHostFilter):
"""Filters hosts based on possibility to create consistent SG snapshots."""
def host_passes(self, host_state, filter_properties):
"""Return True if host will work with desired share group."""
cs_group_spec = filter_properties['share_group_type'].get(
'group_specs', {}).get('consistent_snapshot_support')
# NOTE(vpoomaryov): if 'consistent_snapshot_support' group spec
# is not set, then we assume that share group owner do not care about
# it, which means any host should pass this filter.
if cs_group_spec is None:
return True
return cs_group_spec == host_state.sg_consistent_snapshot_support

View File

@ -56,7 +56,14 @@ host_manager_opts = [
'CapacityWeigher', 'CapacityWeigher',
'GoodnessWeigher', 'GoodnessWeigher',
], ],
help='Which weigher class names to use for weighing hosts.') help='Which weigher class names to use for weighing hosts.'),
cfg.ListOpt(
'scheduler_default_share_group_filters',
default=[
'ConsistentSnapshotFilter',
],
help='Which filter class names to use for filtering hosts '
'creating share group when not specified in the request.'),
] ]
CONF = cfg.CONF CONF = cfg.CONF
@ -140,6 +147,9 @@ class HostState(object):
self.pools = {} self.pools = {}
self.updated = None self.updated = None
# Share Group capabilities
self.sg_consistent_snapshot_support = None
def update_capabilities(self, capabilities=None, service=None): def update_capabilities(self, capabilities=None, service=None):
# Read-only capability dicts # Read-only capability dicts
@ -317,6 +327,10 @@ class HostState(object):
if not pool_cap.get('replication_domain'): if not pool_cap.get('replication_domain'):
pool_cap['replication_domain'] = self.replication_domain pool_cap['replication_domain'] = self.replication_domain
if 'sg_consistent_snapshot_support' not in pool_cap:
pool_cap['sg_consistent_snapshot_support'] = (
self.sg_consistent_snapshot_support)
def update_backend(self, capability): def update_backend(self, capability):
self.share_backend_name = capability.get('share_backend_name') self.share_backend_name = capability.get('share_backend_name')
self.vendor_name = capability.get('vendor_name') self.vendor_name = capability.get('vendor_name')
@ -334,6 +348,8 @@ class HostState(object):
self.updated = capability['timestamp'] self.updated = capability['timestamp']
self.replication_type = capability.get('replication_type') self.replication_type = capability.get('replication_type')
self.replication_domain = capability.get('replication_domain') self.replication_domain = capability.get('replication_domain')
self.sg_consistent_snapshot_support = capability.get(
'share_group_stats', {}).get('consistent_snapshot_support')
def consume_from_share(self, share): def consume_from_share(self, share):
"""Incrementally update host state from an share.""" """Incrementally update host state from an share."""
@ -419,6 +435,8 @@ class PoolState(HostState):
'replication_type', self.replication_type) 'replication_type', self.replication_type)
self.replication_domain = capability.get( self.replication_domain = capability.get(
'replication_domain') 'replication_domain')
self.sg_consistent_snapshot_support = capability.get(
'sg_consistent_snapshot_support')
def update_pools(self, capability): def update_pools(self, capability):
# Do nothing, since we don't have pools within pool, yet # Do nothing, since we don't have pools within pool, yet

View File

@ -53,6 +53,8 @@ def generate_stats(host_state, properties):
'pools': host_state.pools, 'pools': host_state.pools,
'max_over_subscription_ratio': 'max_over_subscription_ratio':
host_state.max_over_subscription_ratio, host_state.max_over_subscription_ratio,
'sg_consistent_snapshot_support': (
host_state.sg_consistent_snapshot_support),
} }
host_caps = host_state.capabilities host_caps = host_state.capabilities
@ -60,15 +62,20 @@ def generate_stats(host_state, properties):
share_type = properties.get('share_type', {}) share_type = properties.get('share_type', {})
extra_specs = share_type.get('extra_specs', {}) extra_specs = share_type.get('extra_specs', {})
share_group_type = properties.get('share_group_type', {})
group_specs = share_group_type.get('group_specs', {})
request_spec = properties.get('request_spec', {}) request_spec = properties.get('request_spec', {})
share_stats = request_spec.get('resource_properties', {}) share_stats = request_spec.get('resource_properties', {})
stats = { stats = {
'host_stats': host_stats, 'host_stats': host_stats,
'host_caps': host_caps, 'host_caps': host_caps,
'share_type': share_type,
'extra_specs': extra_specs, 'extra_specs': extra_specs,
'share_stats': share_stats, 'share_stats': share_stats,
'share_type': share_type, 'share_group_type': share_group_type,
'group_specs': group_specs,
} }
return stats return stats

View File

@ -1112,7 +1112,6 @@ class ShareDriver(object):
create_share_from_snapshot_support=( create_share_from_snapshot_support=(
self.creating_shares_from_snapshots_is_supported), self.creating_shares_from_snapshots_is_supported),
revert_to_snapshot_support=False, revert_to_snapshot_support=False,
share_group_snapshot_support=self.snapshots_are_supported,
mount_snapshot_support=False, mount_snapshot_support=False,
replication_domain=self.replication_domain, replication_domain=self.replication_domain,
filter_function=self.get_filter_function(), filter_function=self.get_filter_function(),
@ -1120,6 +1119,13 @@ class ShareDriver(object):
) )
if isinstance(data, dict): if isinstance(data, dict):
common.update(data) common.update(data)
sg_stats = data.get('share_group_stats', {}) if data else {}
common['share_group_stats'] = {
'consistent_snapshot_support': sg_stats.get(
'consistent_snapshot_support'),
}
self._stats = common self._stats = common
def get_share_server_pools(self, share_server): def get_share_server_pools(self, share_server):
@ -1357,7 +1363,7 @@ class ShareDriver(object):
snap_dict['id']) snap_dict['id'])
snapshot_members = snap_dict.get('share_group_snapshot_members', []) snapshot_members = snap_dict.get('share_group_snapshot_members', [])
if not self._stats.get('share_group_snapshot_support'): if not self._stats.get('snapshot_support'):
raise exception.ShareGroupSnapshotNotSupported( raise exception.ShareGroupSnapshotNotSupported(
share_group=snap_dict['share_group_id']) share_group=snap_dict['share_group_id'])
elif not snapshot_members: elif not snapshot_members:

View File

@ -3522,7 +3522,10 @@ class ShareManager(manager.SchedulerDependentManager):
self.db.share_group_update( self.db.share_group_update(
context, context,
share_group_ref['id'], share_group_ref['id'],
{'status': constants.STATUS_ERROR}) {'status': constants.STATUS_ERROR,
'consistent_snapshot_support': self.driver._stats[
'share_group_stats'].get(
'consistent_snapshot_support')})
for share in shares: for share in shares:
self.db.share_instance_update( self.db.share_instance_update(
context, share['id'], context, share['id'],
@ -3533,10 +3536,13 @@ class ShareManager(manager.SchedulerDependentManager):
for share in shares: for share in shares:
self.db.share_instance_update( self.db.share_instance_update(
context, share['id'], {'status': constants.STATUS_AVAILABLE}) context, share['id'], {'status': constants.STATUS_AVAILABLE})
self.db.share_group_update(context, self.db.share_group_update(
context,
share_group_ref['id'], share_group_ref['id'],
{'status': status, {'status': status,
'created_at': now}) 'created_at': now,
'consistent_snapshot_support': self.driver._stats[
'share_group_stats'].get('consistent_snapshot_support')})
LOG.info("Share group %s: created successfully", share_group_id) LOG.info("Share group %s: created successfully", share_group_id)
# TODO(ameade): Add notification for create.end # TODO(ameade): Add notification for create.end

View File

@ -217,6 +217,7 @@ class HostManagerTestCase(test.TestCase):
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None,
}, },
}, { }, {
'name': 'host2@back1#BBB', 'name': 'host2@back1#BBB',
@ -244,6 +245,7 @@ class HostManagerTestCase(test.TestCase):
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None,
}, },
}, { }, {
'name': 'host2@back2#CCC', 'name': 'host2@back2#CCC',
@ -271,6 +273,7 @@ class HostManagerTestCase(test.TestCase):
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None,
}, },
}, },
] ]
@ -320,6 +323,7 @@ class HostManagerTestCase(test.TestCase):
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None,
}, },
}, { }, {
'name': 'host2@BBB#pool2', 'name': 'host2@BBB#pool2',
@ -348,6 +352,7 @@ class HostManagerTestCase(test.TestCase):
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None,
}, },
}, { }, {
'name': 'host3@CCC#pool3', 'name': 'host3@CCC#pool3',
@ -376,6 +381,7 @@ class HostManagerTestCase(test.TestCase):
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None,
}, },
}, { }, {
'name': 'host4@DDD#pool4a', 'name': 'host4@DDD#pool4a',
@ -404,6 +410,7 @@ class HostManagerTestCase(test.TestCase):
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None,
}, },
}, { }, {
'name': 'host4@DDD#pool4b', 'name': 'host4@DDD#pool4b',
@ -432,6 +439,7 @@ class HostManagerTestCase(test.TestCase):
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None,
}, },
}, },
] ]
@ -493,6 +501,7 @@ class HostManagerTestCase(test.TestCase):
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None,
}, },
}, { }, {
'name': 'host2@back1#BBB', 'name': 'host2@back1#BBB',
@ -520,6 +529,7 @@ class HostManagerTestCase(test.TestCase):
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None,
}, },
}, },
] ]
@ -575,6 +585,7 @@ class HostManagerTestCase(test.TestCase):
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
'replication_domain': None, 'replication_domain': None,
'sg_consistent_snapshot_support': None,
}, },
}, },
] ]

View File

@ -126,7 +126,7 @@ class EMCShareFrameworkTestCase(test.TestCase):
data['snapshot_support'] = True data['snapshot_support'] = True
data['create_share_from_snapshot_support'] = True data['create_share_from_snapshot_support'] = True
data['revert_to_snapshot_support'] = False data['revert_to_snapshot_support'] = False
data['share_group_snapshot_support'] = True data['share_group_stats'] = {'consistent_snapshot_support': None}
data['mount_snapshot_support'] = False data['mount_snapshot_support'] = False
data['replication_domain'] = None data['replication_domain'] = None
data['filter_function'] = None data['filter_function'] = None

View File

@ -380,13 +380,15 @@ class DummyDriver(driver.ShareDriver):
"storage_protocol": "NFS_CIFS", "storage_protocol": "NFS_CIFS",
"reserved_percentage": "reserved_percentage":
self.configuration.reserved_share_percentage, self.configuration.reserved_share_percentage,
"consistency_group_support": "pool",
"snapshot_support": True, "snapshot_support": True,
"create_share_from_snapshot_support": True, "create_share_from_snapshot_support": True,
"revert_to_snapshot_support": True, "revert_to_snapshot_support": True,
"mount_snapshot_support": True, "mount_snapshot_support": True,
"driver_name": "Dummy", "driver_name": "Dummy",
"pools": self._get_pools_info(), "pools": self._get_pools_info(),
"share_group_stats": {
"consistent_snapshot_support": "pool",
}
} }
if self.configuration.replication_domain: if self.configuration.replication_domain:
data["replication_type"] = "readable" data["replication_type"] = "readable"

View File

@ -258,8 +258,10 @@ class GlusterfsNativeShareDriverTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'share_group_snapshot_support': True,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'share_group_stats': {
'consistent_snapshot_support': None,
},
'replication_domain': None, 'replication_domain': None,
'filter_function': None, 'filter_function': None,
'goodness_function': None, 'goodness_function': None,

View File

@ -735,8 +735,10 @@ class HPE3ParDriverTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'share_group_snapshot_support': True,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'share_group_stats': {
'consistent_snapshot_support': None,
},
'storage_protocol': 'NFS_CIFS', 'storage_protocol': 'NFS_CIFS',
'thin_provisioning': True, 'thin_provisioning': True,
'total_capacity_gb': 0, 'total_capacity_gb': 0,
@ -813,8 +815,10 @@ class HPE3ParDriverTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'share_group_snapshot_support': True,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'share_group_stats': {
'consistent_snapshot_support': None,
},
'replication_domain': None, 'replication_domain': None,
'filter_function': None, 'filter_function': None,
'goodness_function': None, 'goodness_function': None,
@ -853,8 +857,10 @@ class HPE3ParDriverTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'share_group_snapshot_support': True,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'share_group_stats': {
'consistent_snapshot_support': None,
},
'replication_domain': None, 'replication_domain': None,
'filter_function': None, 'filter_function': None,
'goodness_function': None, 'goodness_function': None,

View File

@ -2425,12 +2425,12 @@ class HuaweiShareDriverTestCase(test.TestCase):
"snapshot_support": snapshot_support, "snapshot_support": snapshot_support,
"create_share_from_snapshot_support": snapshot_support, "create_share_from_snapshot_support": snapshot_support,
"revert_to_snapshot_support": False, "revert_to_snapshot_support": False,
"share_group_snapshot_support": True,
"mount_snapshot_support": False, "mount_snapshot_support": False,
"replication_domain": None, "replication_domain": None,
"filter_function": None, "filter_function": None,
"goodness_function": None, "goodness_function": None,
"pools": [], "pools": [],
"share_group_stats": {"consistent_snapshot_support": None},
} }
if replication_support: if replication_support:

View File

@ -352,10 +352,10 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
'replication_domain': replication_domain, 'replication_domain': replication_domain,
'reserved_percentage': 0, 'reserved_percentage': 0,
'share_backend_name': self.driver.backend_name, 'share_backend_name': self.driver.backend_name,
'share_group_stats': {'consistent_snapshot_support': None},
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'share_group_snapshot_support': True,
'mount_snapshot_support': False, 'mount_snapshot_support': False,
'storage_protocol': 'NFS', 'storage_protocol': 'NFS',
'total_capacity_gb': 'unknown', 'total_capacity_gb': 'unknown',

View File

@ -690,9 +690,6 @@ class ShareDriverTestCase(test.TestCase):
self.assertEqual( self.assertEqual(
snapshots_are_supported, snapshots_are_supported,
child_class_instance._stats["snapshot_support"]) child_class_instance._stats["snapshot_support"])
self.assertEqual(
snapshots_are_supported,
child_class_instance._stats["share_group_snapshot_support"])
self.assertTrue(child_class_instance.configuration.safe_get.called) self.assertTrue(child_class_instance.configuration.safe_get.called)
def test_create_share_group_from_share_group_snapshot(self): def test_create_share_group_from_share_group_snapshot(self):
@ -843,7 +840,7 @@ class ShareDriverTestCase(test.TestCase):
'name': None 'name': None
} }
share_driver = self._instantiate_share_driver(None, False) share_driver = self._instantiate_share_driver(None, False)
share_driver._stats['share_group_snapshot_support'] = True share_driver._stats['snapshot_support'] = True
mock_create_snap = self.mock_object( mock_create_snap = self.mock_object(
share_driver, 'create_snapshot', share_driver, 'create_snapshot',
mock.Mock(side_effect=lambda *args, **kwargs: { mock.Mock(side_effect=lambda *args, **kwargs: {
@ -917,7 +914,7 @@ class ShareDriverTestCase(test.TestCase):
expected_exception = exception.ManilaException expected_exception = exception.ManilaException
share_driver = self._instantiate_share_driver(None, False) share_driver = self._instantiate_share_driver(None, False)
share_driver._stats['share_group_snapshot_support'] = True share_driver._stats['snapshot_support'] = True
mock_create_snap = self.mock_object( mock_create_snap = self.mock_object(
share_driver, 'create_snapshot', share_driver, 'create_snapshot',
mock.Mock(side_effect=[None, expected_exception])) mock.Mock(side_effect=[None, expected_exception]))
@ -985,7 +982,7 @@ class ShareDriverTestCase(test.TestCase):
'name': None 'name': None
} }
share_driver = self._instantiate_share_driver(None, False) share_driver = self._instantiate_share_driver(None, False)
share_driver._stats['share_group_snapshot_support'] = False share_driver._stats['snapshot_support'] = False
self.assertRaises( self.assertRaises(
exception.ShareGroupSnapshotNotSupported, exception.ShareGroupSnapshotNotSupported,
@ -1006,7 +1003,7 @@ class ShareDriverTestCase(test.TestCase):
'name': None 'name': None
} }
share_driver = self._instantiate_share_driver(None, False) share_driver = self._instantiate_share_driver(None, False)
share_driver._stats['share_group_snapshot_support'] = True share_driver._stats['snapshot_support'] = True
share_group_snapshot_update, member_update_list = ( share_group_snapshot_update, member_update_list = (
share_driver.create_share_group_snapshot( share_driver.create_share_group_snapshot(

View File

@ -89,6 +89,9 @@ class ShareManagerTestCase(test.TestCase):
"manila.share.manager.ShareManager") "manila.share.manager.ShareManager")
self.mock_object(self.share_manager.driver, 'do_setup') self.mock_object(self.share_manager.driver, 'do_setup')
self.mock_object(self.share_manager.driver, 'check_for_setup_error') self.mock_object(self.share_manager.driver, 'check_for_setup_error')
self.share_manager.driver._stats = {
'share_group_stats': {'consistent_snapshot_support': None},
}
self.context = context.get_admin_context() self.context = context.get_admin_context()
self.share_manager.driver.initialized = True self.share_manager.driver.initialized = True
mock.patch.object( mock.patch.object(
@ -3226,10 +3229,13 @@ class ShareManagerTestCase(test.TestCase):
self.share_manager.create_share_group(self.context, "fake_id") self.share_manager.create_share_group(self.context, "fake_id")
self.share_manager.db.share_group_update.\ self.share_manager.db.share_group_update.assert_called_once_with(
assert_called_once_with(mock.ANY, 'fake_id', mock.ANY, 'fake_id', {
{'status': constants.STATUS_AVAILABLE, 'status': constants.STATUS_AVAILABLE,
'created_at': mock.ANY}) 'created_at': mock.ANY,
'consistent_snapshot_support': None,
}
)
def test_create_cg_with_share_network_driver_not_handles_servers(self): def test_create_cg_with_share_network_driver_not_handles_servers(self):
manager.CONF.set_default('driver_handles_share_servers', False) manager.CONF.set_default('driver_handles_share_servers', False)
@ -3281,8 +3287,12 @@ class ShareManagerTestCase(test.TestCase):
self.share_manager.create_share_group(self.context, "fake_id") self.share_manager.create_share_group(self.context, "fake_id")
self.share_manager.db.share_group_update.assert_called_once_with( self.share_manager.db.share_group_update.assert_called_once_with(
mock.ANY, 'fake_id', mock.ANY, 'fake_id', {
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) 'status': constants.STATUS_AVAILABLE,
'created_at': mock.ANY,
'consistent_snapshot_support': None,
}
)
def test_create_share_group_with_update(self): def test_create_share_group_with_update(self):
fake_group = {'id': 'fake_id'} fake_group = {'id': 'fake_id'}
@ -3298,10 +3308,13 @@ class ShareManagerTestCase(test.TestCase):
self.share_manager.db.share_group_update.\ self.share_manager.db.share_group_update.\
assert_any_call(mock.ANY, 'fake_id', {'foo': 'bar'}) assert_any_call(mock.ANY, 'fake_id', {'foo': 'bar'})
self.share_manager.db.share_group_update.\ self.share_manager.db.share_group_update.assert_any_call(
assert_any_call(mock.ANY, 'fake_id', mock.ANY, 'fake_id', {
{'status': constants.STATUS_AVAILABLE, 'status': constants.STATUS_AVAILABLE,
'created_at': mock.ANY}) 'created_at': mock.ANY,
'consistent_snapshot_support': None,
}
)
def test_create_share_group_with_error(self): def test_create_share_group_with_error(self):
fake_group = {'id': 'fake_id'} fake_group = {'id': 'fake_id'}
@ -3318,7 +3331,11 @@ class ShareManagerTestCase(test.TestCase):
self.context, "fake_id") self.context, "fake_id")
self.share_manager.db.share_group_update.assert_called_once_with( self.share_manager.db.share_group_update.assert_called_once_with(
mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR}) mock.ANY, 'fake_id', {
'status': constants.STATUS_ERROR,
'consistent_snapshot_support': None,
}
)
def test_create_share_group_from_sg_snapshot(self): def test_create_share_group_from_sg_snapshot(self):
fake_group = { fake_group = {
@ -3348,7 +3365,9 @@ class ShareManagerTestCase(test.TestCase):
self.share_manager.db.share_group_update.assert_called_once_with( self.share_manager.db.share_group_update.assert_called_once_with(
mock.ANY, 'fake_id', mock.ANY, 'fake_id',
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) {'status': constants.STATUS_AVAILABLE,
'created_at': mock.ANY,
'consistent_snapshot_support': None})
self.share_manager.db.share_server_get(mock.ANY, 'fake_ss_id') self.share_manager.db.share_server_get(mock.ANY, 'fake_ss_id')
mock_create_sg_from_sg_snap.assert_called_once_with( mock_create_sg_from_sg_snap.assert_called_once_with(
mock.ANY, fake_group, fake_snap, share_server=fake_ss) mock.ANY, fake_group, fake_snap, share_server=fake_ss)
@ -3415,7 +3434,9 @@ class ShareManagerTestCase(test.TestCase):
self.share_manager.db.share_group_update.assert_called_once_with( self.share_manager.db.share_group_update.assert_called_once_with(
mock.ANY, 'fake_id', mock.ANY, 'fake_id',
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) {'status': constants.STATUS_AVAILABLE,
'created_at': mock.ANY,
'consistent_snapshot_support': None})
def test_create_share_group_from_share_group_snapshot_with_update(self): def test_create_share_group_from_share_group_snapshot_with_update(self):
fake_group = { fake_group = {
@ -3439,8 +3460,12 @@ class ShareManagerTestCase(test.TestCase):
self.share_manager.db.share_group_update.assert_any_call( self.share_manager.db.share_group_update.assert_any_call(
mock.ANY, 'fake_id', {'foo': 'bar'}) mock.ANY, 'fake_id', {'foo': 'bar'})
self.share_manager.db.share_group_update.assert_any_call( self.share_manager.db.share_group_update.assert_any_call(
mock.ANY, 'fake_id', mock.ANY, 'fake_id', {
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) 'status': constants.STATUS_AVAILABLE,
'created_at': mock.ANY,
'consistent_snapshot_support': None,
}
)
def test_create_share_group_from_sg_snapshot_with_share_update(self): def test_create_share_group_from_sg_snapshot_with_share_update(self):
fake_share = {'id': 'fake_share_id'} fake_share = {'id': 'fake_share_id'}
@ -3474,8 +3499,12 @@ class ShareManagerTestCase(test.TestCase):
self.share_manager.db.share_export_locations_update.assert_any_call( self.share_manager.db.share_export_locations_update.assert_any_call(
mock.ANY, 'fake_share_id', fake_export_locations) mock.ANY, 'fake_share_id', fake_export_locations)
self.share_manager.db.share_group_update.assert_any_call( self.share_manager.db.share_group_update.assert_any_call(
mock.ANY, 'fake_id', mock.ANY, 'fake_id', {
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) 'status': constants.STATUS_AVAILABLE,
'created_at': mock.ANY,
'consistent_snapshot_support': None,
}
)
def test_create_share_group_from_sg_snapshot_with_error(self): def test_create_share_group_from_sg_snapshot_with_error(self):
fake_group = { fake_group = {
@ -3502,7 +3531,11 @@ class ShareManagerTestCase(test.TestCase):
self.context, "fake_id") self.context, "fake_id")
self.share_manager.db.share_group_update.assert_called_once_with( self.share_manager.db.share_group_update.assert_called_once_with(
mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR}) mock.ANY, 'fake_id', {
'status': constants.STATUS_ERROR,
'consistent_snapshot_support': None,
}
)
def test_create_share_group_from_sg_snapshot_with_share_error(self): def test_create_share_group_from_sg_snapshot_with_share_error(self):
fake_share = {'id': 'fake_share_id'} fake_share = {'id': 'fake_share_id'}
@ -3532,7 +3565,11 @@ class ShareManagerTestCase(test.TestCase):
self.share_manager.db.share_instance_update.assert_any_call( self.share_manager.db.share_instance_update.assert_any_call(
mock.ANY, 'fake_share_id', {'status': constants.STATUS_ERROR}) mock.ANY, 'fake_share_id', {'status': constants.STATUS_ERROR})
self.share_manager.db.share_group_update.assert_called_once_with( self.share_manager.db.share_group_update.assert_called_once_with(
mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR}) mock.ANY, 'fake_id', {
'status': constants.STATUS_ERROR,
'consistent_snapshot_support': None,
}
)
def test_delete_share_group(self): def test_delete_share_group(self):
fake_group = {'id': 'fake_id'} fake_group = {'id': 'fake_id'}

View File

@ -112,6 +112,11 @@ ShareGroup = [
"capability called 'revert_to_snapshot_support' " "capability called 'revert_to_snapshot_support' "
"and will be used for setting up custom share type. " "and will be used for setting up custom share type. "
"Defaults to the value of run_revert_to_snapshot_tests."), "Defaults to the value of run_revert_to_snapshot_tests."),
cfg.StrOpt("capability_sg_consistent_snapshot_support",
choices=["host", "pool", None],
help="Backend capability to create consistent snapshots of "
"share group members. Will be used with creation "
"of new share group types as group spec."),
cfg.StrOpt("share_network_id", cfg.StrOpt("share_network_id",
default="", default="",
help="Some backend drivers requires share network " help="Some backend drivers requires share network "

View File

@ -136,13 +136,16 @@ class ShareGroupTypesTest(base.BaseSharesAdminTest):
self.assertDictMatch(group_specs, sg_type['group_specs']) self.assertDictMatch(group_specs, sg_type['group_specs'])
group_specs = {'key1': 'value3', 'key2': 'value2'} group_specs = {'key1': 'value1', 'key2': 'value2'}
self.shares_v2_client.update_share_group_type_spec( self.shares_v2_client.update_share_group_type_spec(
sg_type['id'], 'key1', 'value3') sg_type['id'], 'key1', 'value3')
sg_type = self.shares_v2_client.get_share_group_type(sg_type['id']) sg_type = self.shares_v2_client.get_share_group_type(sg_type['id'])
self.assertDictMatch(group_specs, sg_type['group_specs']) self.assertIn('key1', sg_type['group_specs'])
self.assertIn('key2', sg_type['group_specs'])
self.assertEqual('value3', sg_type['group_specs']['key1'])
self.assertEqual(group_specs['key2'], sg_type['group_specs']['key2'])
@tc.attr(base.TAG_POSITIVE, base.TAG_API) @tc.attr(base.TAG_POSITIVE, base.TAG_API)
def test_update_all_share_group_type_specs_min(self): def test_update_all_share_group_type_specs_min(self):
@ -164,7 +167,9 @@ class ShareGroupTypesTest(base.BaseSharesAdminTest):
sg_type['id'], group_specs) sg_type['id'], group_specs)
sg_type = self.shares_v2_client.get_share_group_type(sg_type['id']) sg_type = self.shares_v2_client.get_share_group_type(sg_type['id'])
self.assertDictMatch(group_specs, sg_type['group_specs']) for k, v in group_specs.items():
self.assertIn(k, sg_type['group_specs'])
self.assertEqual(v, sg_type['group_specs'][k])
@tc.attr(base.TAG_POSITIVE, base.TAG_API) @tc.attr(base.TAG_POSITIVE, base.TAG_API)
def test_delete_single_share_group_type_spec_min(self): def test_delete_single_share_group_type_spec_min(self):

View File

@ -48,7 +48,7 @@ class ShareGroupsTest(base.BaseSharesAdminTest):
cleanup_in_class=True, cleanup_in_class=True,
version=constants.MIN_SHARE_GROUP_MICROVERSION) version=constants.MIN_SHARE_GROUP_MICROVERSION)
@tc.attr(base.TAG_POSITIVE, base.TAG_API) @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
def test_create_share_group_with_single_share_type_min(self): def test_create_share_group_with_single_share_type_min(self):
share_group = self.create_share_group( share_group = self.create_share_group(
share_group_type_id=self.sg_type['id'], share_group_type_id=self.sg_type['id'],
@ -81,7 +81,7 @@ class ShareGroupsTest(base.BaseSharesAdminTest):
'Expected %s, got %s' % ( 'Expected %s, got %s' % (
share_group['id'], expected_share_types, actual_share_types)) share_group['id'], expected_share_types, actual_share_types))
@tc.attr(base.TAG_POSITIVE, base.TAG_API) @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
def test_create_share_group_with_multiple_share_types_min(self): def test_create_share_group_with_multiple_share_types_min(self):
share_group = self.create_share_group( share_group = self.create_share_group(
share_group_type_id=self.sg_type['id'], share_group_type_id=self.sg_type['id'],
@ -114,7 +114,7 @@ class ShareGroupsTest(base.BaseSharesAdminTest):
'Expected %s, got %s' % ( 'Expected %s, got %s' % (
share_group['id'], expected_share_types, actual_share_types)) share_group['id'], expected_share_types, actual_share_types))
@tc.attr(base.TAG_POSITIVE, base.TAG_API) @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
def test_default_share_group_type_applied(self): def test_default_share_group_type_applied(self):
default_type = self.shares_v2_client.get_default_share_group_type() default_type = self.shares_v2_client.get_default_share_group_type()
default_share_types = default_type['share_types'] default_share_types = default_type['share_types']
@ -142,7 +142,7 @@ class ShareGroupsTest(base.BaseSharesAdminTest):
@testtools.skipUnless( @testtools.skipUnless(
CONF.share.multitenancy_enabled, "Only for multitenancy.") CONF.share.multitenancy_enabled, "Only for multitenancy.")
@tc.attr(base.TAG_POSITIVE, base.TAG_API) @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
def test_create_sg_from_snapshot_verify_share_server_information_min(self): def test_create_sg_from_snapshot_verify_share_server_information_min(self):
# Create a share group # Create a share group
orig_sg = self.create_share_group( orig_sg = self.create_share_group(
@ -173,3 +173,19 @@ class ShareGroupsTest(base.BaseSharesAdminTest):
orig_sg['share_network_id'], new_sg['share_network_id']) orig_sg['share_network_id'], new_sg['share_network_id'])
self.assertEqual( self.assertEqual(
orig_sg['share_server_id'], new_sg['share_server_id']) orig_sg['share_server_id'], new_sg['share_server_id'])
@tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
def test_create_sg_with_sg_type_but_without_any_group_specs(self):
# Create share group type not specifying any group specs
sg_type = self.create_share_group_type(
name=data_utils.rand_name("tempest-manila"),
share_types=[self.share_type['id']],
group_specs={},
cleanup_in_class=False)
# Create share group, it should be created always, because we do not
# restrict choice anyhow.
self.create_share_group(
share_type_ids=[self.share_type['id']],
share_group_type_id=sg_type['id'],
cleanup_in_class=False)

View File

@ -0,0 +1,56 @@
# Copyright 2017 Mirantis Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest import config
from tempest.lib.common.utils import data_utils
import testtools
from testtools import testcase as tc
from manila_tempest_tests.common import constants
from manila_tempest_tests import share_exceptions
from manila_tempest_tests.tests.api import base
CONF = config.CONF
@testtools.skipUnless(
CONF.share.run_share_group_tests, 'Share Group tests disabled.')
@base.skip_if_microversion_lt(constants.MIN_SHARE_GROUP_MICROVERSION)
class ShareGroupsNegativeTest(base.BaseSharesAdminTest):
@tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
def test_create_share_group_with_wrong_consistent_snapshot_spec(self):
# Create valid share type for share group type
name = data_utils.rand_name("tempest-manila")
extra_specs = self.add_extra_specs_to_dict()
st = self.create_share_type(name, extra_specs=extra_specs)
share_type = st['share_type'] if 'share_type' in st else st
# Create share group type with wrong value for
# 'consistent_snapshot_support' capability, we always expect
# NoValidHostFound using this SG type.
sg_type = self.create_share_group_type(
name=name,
share_types=[share_type['id']],
group_specs={"consistent_snapshot_support": "fake"},
cleanup_in_class=False)
# Try create share group
self.assertRaises(
share_exceptions.ShareGroupBuildErrorException,
self.create_share_group,
share_type_ids=[share_type['id']],
share_group_type_id=sg_type['id'],
cleanup_in_class=False)

View File

@ -595,6 +595,11 @@ class BaseSharesTest(test.BaseTestCase):
group_specs=None, client=None, group_specs=None, client=None,
cleanup_in_class=True, **kwargs): cleanup_in_class=True, **kwargs):
client = client or cls.shares_v2_client client = client or cls.shares_v2_client
if group_specs is None:
group_specs = {
'consistent_snapshot_support': (
CONF.share.capability_sg_consistent_snapshot_support),
}
share_group_type = client.create_share_group_type( share_group_type = client.create_share_group_type(
name=name, name=name,
share_types=share_types, share_types=share_types,

View File

@ -45,10 +45,14 @@ manila.scheduler.filters =
JsonFilter = manila.scheduler.filters.json:JsonFilter JsonFilter = manila.scheduler.filters.json:JsonFilter
RetryFilter = manila.scheduler.filters.retry:RetryFilter RetryFilter = manila.scheduler.filters.retry:RetryFilter
ShareReplicationFilter = manila.scheduler.filters.share_replication:ShareReplicationFilter ShareReplicationFilter = manila.scheduler.filters.share_replication:ShareReplicationFilter
# Share Group filters
ConsistentSnapshotFilter = manila.scheduler.filters.share_group_filters.consistent_snapshot:ConsistentSnapshotFilter
manila.scheduler.weighers = manila.scheduler.weighers =
CapacityWeigher = manila.scheduler.weighers.capacity:CapacityWeigher CapacityWeigher = manila.scheduler.weighers.capacity:CapacityWeigher
GoodnessWeigher = manila.scheduler.weighers.goodness:GoodnessWeigher GoodnessWeigher = manila.scheduler.weighers.goodness:GoodnessWeigher
PoolWeigher = manila.scheduler.weighers.pool:PoolWeigher PoolWeigher = manila.scheduler.weighers.pool:PoolWeigher
# These are for backwards compat with Havana notification_driver configuration values # These are for backwards compat with Havana notification_driver configuration values
oslo_messaging.notify.drivers = oslo_messaging.notify.drivers =
manila.openstack.common.notifier.log_notifier = oslo_messaging.notify._impl_log:LogDriver manila.openstack.common.notifier.log_notifier = oslo_messaging.notify._impl_log:LogDriver