diff --git a/cinder/scheduler/filter_scheduler.py b/cinder/scheduler/filter_scheduler.py index 5f19f0a459b..5db592c6c88 100644 --- a/cinder/scheduler/filter_scheduler.py +++ b/cinder/scheduler/filter_scheduler.py @@ -397,7 +397,10 @@ class FilterScheduler(driver.Scheduler): new_weighed_hosts = [] for host1 in weighed_hosts: for host2 in temp_weighed_hosts: - if host1.obj.host == host2.obj.host: + # Should schedule creation of CG on backend level, + # not pool level. + if (utils.extract_host(host1.obj.host) == + utils.extract_host(host2.obj.host)): new_weighed_hosts.append(host1) weighed_hosts = new_weighed_hosts if not weighed_hosts: @@ -410,6 +413,16 @@ class FilterScheduler(driver.Scheduler): def _schedule(self, context, request_spec, filter_properties=None): weighed_hosts = self._get_weighted_candidates(context, request_spec, filter_properties) + # When we get the weighed_hosts, we clear those hosts whose backend + # is not same as consistencygroup's backend. + CG_backend = request_spec.get('CG_backend') + if weighed_hosts and CG_backend: + # Get host name including host@backend#pool info from + # weighed_hosts. + for host in weighed_hosts[::-1]: + backend = utils.extract_host(host.obj.host) + if backend != CG_backend: + weighed_hosts.remove(host) if not weighed_hosts: LOG.warning(_LW('No weighed hosts found for volume ' 'with properties: %s'), diff --git a/cinder/tests/unit/scheduler/test_filter_scheduler.py b/cinder/tests/unit/scheduler/test_filter_scheduler.py index 455bef4b57c..c1e3cd3df92 100644 --- a/cinder/tests/unit/scheduler/test_filter_scheduler.py +++ b/cinder/tests/unit/scheduler/test_filter_scheduler.py @@ -192,6 +192,37 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): self.assertIsNotNone(weighed_host.obj) self.assertTrue(_mock_service_get_all_by_topic.called) + @mock.patch('cinder.db.service_get_all_by_topic') + def test_create_volume_clear_host_different_with_cg(self, + _mock_service_get_all): + # Ensure we clear those hosts whose backend is not same as + # consistencygroup's backend. + sched = fakes.FakeFilterScheduler() + sched.host_manager = fakes.FakeHostManager() + fakes.mock_host_manager_db_calls(_mock_service_get_all) + fake_context = context.RequestContext('user', 'project') + request_spec = {'volume_properties': {'project_id': 1, + 'size': 1}, + 'volume_type': {'name': 'LVM_iSCSI'}, + 'CG_backend': 'host@lvmdriver'} + weighed_host = sched._schedule(fake_context, request_spec, {}) + self.assertIsNone(weighed_host) + + @mock.patch('cinder.db.service_get_all_by_topic') + def test_create_volume_host_same_as_cg(self, _mock_service_get_all): + # Ensure we don't clear the host whose backend is same as + # consistencygroup's backend. + sched = fakes.FakeFilterScheduler() + sched.host_manager = fakes.FakeHostManager() + fakes.mock_host_manager_db_calls(_mock_service_get_all) + fake_context = context.RequestContext('user', 'project') + request_spec = {'volume_properties': {'project_id': 1, + 'size': 1}, + 'volume_type': {'name': 'LVM_iSCSI'}, + 'CG_backend': 'host1'} + weighed_host = sched._schedule(fake_context, request_spec, {}) + self.assertEqual('host1#lvm1', weighed_host.obj.host) + def test_max_attempts(self): self.flags(scheduler_max_attempts=4) diff --git a/cinder/tests/unit/volume/flows/test_create_volume_flow.py b/cinder/tests/unit/volume/flows/test_create_volume_flow.py index df820918ea0..04086c72eab 100644 --- a/cinder/tests/unit/volume/flows/test_create_volume_flow.py +++ b/cinder/tests/unit/volume/flows/test_create_volume_flow.py @@ -50,14 +50,16 @@ class CreateVolumeFlowTestCase(test.TestCase): # called to avoid div by zero errors. self.counter = float(0) + @mock.patch('cinder.volume.utils.extract_host') @mock.patch('time.time', side_effect=time_inc) @mock.patch('cinder.objects.ConsistencyGroup.get_by_id') - def test_cast_create_volume(self, consistencygroup_get_by_id, mock_time): + def test_cast_create_volume(self, consistencygroup_get_by_id, mock_time, + mock_extract_host): props = {} - consistencygroup_obj = \ - fake_consistencygroup.fake_consistencyobject_obj( - self.ctxt, consistencygroup_id=1, host=None) - consistencygroup_get_by_id.return_value = consistencygroup_obj + cg_obj = (fake_consistencygroup. + fake_consistencyobject_obj(self.ctxt, consistencygroup_id=1, + host='host@backend#pool')) + consistencygroup_get_by_id.return_value = cg_obj spec = {'volume_id': None, 'source_volid': None, 'snapshot_id': None, @@ -90,6 +92,7 @@ class CreateVolumeFlowTestCase(test.TestCase): task._cast_create_volume(self.ctxt, spec, props) consistencygroup_get_by_id.assert_called_once_with(self.ctxt, 5) + mock_extract_host.assert_called_once_with('host@backend#pool') @mock.patch('cinder.volume.volume_types.is_encrypted') @mock.patch('cinder.volume.flows.api.create_volume.' diff --git a/cinder/volume/flows/api/create_volume.py b/cinder/volume/flows/api/create_volume.py index 6156a225b0f..8e1f98ce43b 100644 --- a/cinder/volume/flows/api/create_volume.py +++ b/cinder/volume/flows/api/create_volume.py @@ -27,6 +27,7 @@ from cinder import policy from cinder import quota from cinder import utils from cinder.volume.flows import common +from cinder.volume import utils as vol_utils from cinder.volume import volume_types LOG = logging.getLogger(__name__) @@ -697,8 +698,14 @@ class VolumeCastTask(flow_utils.CinderTask): cgsnapshot_id = request_spec['cgsnapshot_id'] if cgroup_id: + # If cgroup_id existed, we should cast volume to the scheduler + # to choose a proper pool whose backend is same as CG's backend. cgroup = objects.ConsistencyGroup.get_by_id(context, cgroup_id) - host = cgroup.host + # FIXME(wanghao): CG_backend got added before request_spec was + # converted to versioned objects. We should make sure that this + # will be handled by object version translations once we add + # RequestSpec object. + request_spec['CG_backend'] = vol_utils.extract_host(cgroup.host) elif snapshot_id and CONF.snapshot_same_host: # NOTE(Rongze Zhu): A simple solution for bug 1008866. #