diff --git a/cinder/db/sqlalchemy/api.py b/cinder/db/sqlalchemy/api.py index c869f1fdad1..3f12f65c1bc 100644 --- a/cinder/db/sqlalchemy/api.py +++ b/cinder/db/sqlalchemy/api.py @@ -6129,15 +6129,18 @@ def group_create(context, values, group_snapshot_id=None, values.pop('group_type_id', None) values.pop('availability_zone', None) values.pop('host', None) + values.pop('cluster_name', None) # NOTE(xyang): Save volume_type_ids to update later. volume_type_ids = values.pop('volume_type_ids', []) sel = session.query(group_model.group_type_id, group_model.availability_zone, group_model.host, + group_model.cluster_name, *(bindparam(k, v) for k, v in values.items()) ).filter(*conditions) - names = ['group_type_id', 'availability_zone', 'host'] + names = ['group_type_id', 'availability_zone', 'host', + 'cluster_name'] names.extend(values.keys()) insert_stmt = group_model.__table__.insert().from_select( names, sel) diff --git a/cinder/group/api.py b/cinder/group/api.py index 5b184c1bd0a..04f20fdd46a 100644 --- a/cinder/group/api.py +++ b/cinder/group/api.py @@ -97,6 +97,15 @@ class API(base.Base): return availability_zone + def _update_volumes_host(self, context, group): + volumes = objects.VolumeList.get_all_by_generic_group(context, + group.id) + for vol in volumes: + # Update the host field for the volume. + vol.host = group.host + vol.cluster_name = group.cluster_name + vol.save() + def create(self, context, name, description, group_type, volume_types, availability_zone=None): context.authorize(group_policy.CREATE_POLICY) @@ -247,8 +256,9 @@ class API(base.Base): kwargs = {'group_id': group.id, 'volume_properties': objects.VolumeProperties(size=size)} - if not group.host or not self.scheduler_rpcapi.validate_host_capacity( - context, group.host, objects.RequestSpec(**kwargs)): + host = group.resource_backend + if not host or not self.scheduler_rpcapi.validate_host_capacity( + context, host, objects.RequestSpec(**kwargs)): msg = _("No valid host to create group %s.") % group.id LOG.error(msg) raise exception.InvalidGroup(reason=msg) @@ -335,12 +345,7 @@ class API(base.Base): {'group': group.id, 'group_snap': group_snapshot.id}) - volumes = objects.VolumeList.get_all_by_generic_group(context, - group.id) - for vol in volumes: - # Update the host field for the volume. - vol.host = group.host - vol.save() + self._update_volumes_host(context, group) self.volume_rpcapi.create_group_from_src( context, group, group_snapshot) @@ -418,12 +423,7 @@ class API(base.Base): {'group': group.id, 'source_group': source_group.id}) - volumes = objects.VolumeList.get_all_by_generic_group(context, - group.id) - for vol in volumes: - # Update the host field for the volume. - vol.host = group.host - vol.save() + self._update_volumes_host(context, group) self.volume_rpcapi.create_group_from_src(context, group, None, source_group) diff --git a/cinder/tests/unit/group/test_groups_api.py b/cinder/tests/unit/group/test_groups_api.py index a5aac7d9023..227cf4f1b86 100644 --- a/cinder/tests/unit/group/test_groups_api.py +++ b/cinder/tests/unit/group/test_groups_api.py @@ -500,6 +500,7 @@ class GroupAPITestCase(test.TestCase): vol1.destroy() grp_snap.destroy() + @mock.patch('cinder.group.api.API._update_volumes_host') @mock.patch('cinder.objects.VolumeType.get_by_name_or_id') @mock.patch('cinder.db.group_volume_type_mapping_create') @mock.patch('cinder.volume.api.API.create') @@ -512,7 +513,8 @@ class GroupAPITestCase(test.TestCase): mock_snap_get_all, mock_group_snap_get, mock_volume_api_create, mock_mapping_create, - mock_get_volume_type): + mock_get_volume_type, + mock_update_volumes_host): vol_type = fake_volume.fake_volume_type_obj( self.ctxt, id=fake.VOLUME_TYPE_ID, @@ -566,12 +568,17 @@ class GroupAPITestCase(test.TestCase): mock_rpc_create_group_from_src.assert_called_once_with( self.ctxt, grp, grp_snap) + mock_update_volumes_host.assert_called_once_with( + self.ctxt, grp + ) + vol2.destroy() grp.destroy() snap.destroy() vol1.destroy() grp_snap.destroy() + @mock.patch('cinder.group.api.API._update_volumes_host') @mock.patch('cinder.objects.VolumeType.get_by_name_or_id') @mock.patch('cinder.db.group_volume_type_mapping_create') @mock.patch('cinder.volume.api.API.create') @@ -583,7 +590,8 @@ class GroupAPITestCase(test.TestCase): mock_group_get, mock_volume_api_create, mock_mapping_create, - mock_get_volume_type): + mock_get_volume_type, + mock_update_volumes_host): vol_type = fake_volume.fake_volume_type_obj( self.ctxt, id=fake.VOLUME_TYPE_ID, @@ -631,6 +639,10 @@ class GroupAPITestCase(test.TestCase): mock_rpc_create_group_from_src.assert_called_once_with( self.ctxt, grp2, None, grp) + mock_update_volumes_host.assert_called_once_with( + self.ctxt, grp2 + ) + vol2.destroy() grp2.destroy() vol.destroy() @@ -781,6 +793,42 @@ class GroupAPITestCase(test.TestCase): self.ctxt, 'group', 'desc', group_snapshot_id=None, source_group_id=group.id) + @mock.patch('cinder.objects.volume.Volume.host', + new_callable=mock.PropertyMock) + @mock.patch('cinder.objects.volume.Volume.cluster_name', + new_callable=mock.PropertyMock) + @mock.patch('cinder.objects.VolumeList.get_all_by_generic_group') + def test_update_volumes_host(self, mock_volume_get_all, mock_cluster_name, + mock_host): + vol_type = utils.create_volume_type(self.ctxt, name='test_vol_type') + grp = utils.create_group(self.ctxt, group_type_id=fake.GROUP_TYPE_ID, + volume_type_ids=[vol_type['id']], + availability_zone='nova', + status=fields.GroupStatus.CREATING, + cluster_name='fake_cluster') + + vol1 = utils.create_volume( + self.ctxt, + availability_zone=grp.availability_zone, + volume_type_id=fake.VOLUME_TYPE_ID, + group_id=grp.id) + + mock_volume = mock.Mock() + mock_volume_get_all.return_value = [mock_volume] + group_api = cinder.group.api.API() + group_api._update_volumes_host(None, grp) + + mock_cluster_name.assert_called() + mock_host.assert_called() + + self.assertEqual(grp.host, mock_volume.host) + self.assertEqual(grp.cluster_name, mock_volume.cluster_name) + mock_volume.save.assert_called_once_with() + + vol1.destroy() + + grp.destroy() + def test_delete_group_frozen(self): service = utils.create_service(self.ctxt, {'frozen': True}) group = utils.create_group(self.ctxt, host=service.host,