From aff65749b75f859162ff958b10b2cd0979fb82f0 Mon Sep 17 00:00:00 2001 From: Gorka Eguileor Date: Wed, 18 May 2016 16:55:31 +0200 Subject: [PATCH] Support A/A in attach/detach operations This patch allows attaching and detaching volumes in HA Active-Active configurations. To allow the attach and detach of volumes in Cinder using the cluster topic queue in the Message Broker following RPC methods in cinder/volume/rpcapi has been changed: - attach_volume - detach_volume - initialize_connection - terminate_connection - remove_export When the cluster field is not set for a volume all RPC calls will behave as before, sending it to the host topic queue. Specs: https://review.openstack.org/327283 Implements: blueprint cinder-volume-active-active-support Change-Id: I6ec689ea8abb10832a47b20f82bf2e710c62deae --- .../unit/api/contrib/test_admin_actions.py | 2 +- cinder/tests/unit/test_volume_rpcapi.py | 64 +++++++++++++++---- cinder/volume/rpcapi.py | 14 ++-- ...ter_job_distribution-f916dd2e4cce6c1b.yaml | 2 + 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/cinder/tests/unit/api/contrib/test_admin_actions.py b/cinder/tests/unit/api/contrib/test_admin_actions.py index 5ac464a237e..2253179948b 100644 --- a/cinder/tests/unit/api/contrib/test_admin_actions.py +++ b/cinder/tests/unit/api/contrib/test_admin_actions.py @@ -773,7 +773,7 @@ class AdminActionsAttachDetachTest(BaseAdminTest): attachment = self.volume_api.attach(self.ctx, volume, fake.INSTANCE_ID, None, mountpoint, 'rw') # volume is attached - volume = objects.Volume.get_by_id(self.ctx, volume.id) + volume.refresh() self.assertEqual('in-use', volume.status) self.assertEqual(fake.INSTANCE_ID, attachment['instance_uuid']) self.assertEqual(mountpoint, attachment['mountpoint']) diff --git a/cinder/tests/unit/test_volume_rpcapi.py b/cinder/tests/unit/test_volume_rpcapi.py index da26867dd98..bf553c90bc7 100644 --- a/cinder/tests/unit/test_volume_rpcapi.py +++ b/cinder/tests/unit/test_volume_rpcapi.py @@ -16,8 +16,8 @@ Unit Tests for cinder.volume.rpcapi """ import copy -import mock +import mock from oslo_config import cfg from oslo_serialization import jsonutils @@ -188,7 +188,8 @@ class VolumeRpcAPITestCase(test.TestCase): elif 'group' in kwargs: host = kwargs['group']['host'] elif 'volume' in kwargs: - host = kwargs['volume']['host'] + vol = kwargs['volume'] + host = vol.service_topic_queue elif 'snapshot' in kwargs: host = 'fake_host' elif 'cgsnapshot' in kwargs: @@ -239,7 +240,7 @@ class VolumeRpcAPITestCase(test.TestCase): elif isinstance(value, objects.Volume): expected_volume = expected_msg[kwarg].obj_to_primitive() volume = value.obj_to_primitive() - self.assertEqual(expected_volume, volume) + self.assertDictEqual(expected_volume, volume) elif isinstance(value, objects.Backup): expected_backup = expected_msg[kwarg].obj_to_primitive() backup = value.obj_to_primitive() @@ -372,7 +373,7 @@ class VolumeRpcAPITestCase(test.TestCase): def test_create_snapshot(self): self._test_volume_api('create_snapshot', rpc_method='cast', - volume=self.fake_volume, + volume=self.fake_volume_obj, snapshot=self.fake_snapshot, version='3.0') @@ -395,7 +396,7 @@ class VolumeRpcAPITestCase(test.TestCase): def test_attach_volume_to_instance(self): self._test_volume_api('attach_volume', rpc_method='call', - volume=self.fake_volume, + volume=self.fake_volume_obj, instance_uuid='fake_uuid', host_name=None, mountpoint='fake_mountpoint', @@ -405,7 +406,22 @@ class VolumeRpcAPITestCase(test.TestCase): def test_attach_volume_to_host(self): self._test_volume_api('attach_volume', rpc_method='call', - volume=self.fake_volume, + volume=self.fake_volume_obj, + instance_uuid=None, + host_name='fake_host', + mountpoint='fake_mountpoint', + mode='rw', + version='3.0') + + def _set_cluster(self): + self.fake_volume_obj.cluster_name = 'my_cluster' + self.fake_volume_obj.obj_reset_changes(['cluster_name']) + + def test_attach_volume_to_cluster(self): + self._set_cluster() + self._test_volume_api('attach_volume', + rpc_method='call', + volume=self.fake_volume_obj, instance_uuid=None, host_name='fake_host', mountpoint='fake_mountpoint', @@ -415,14 +431,22 @@ class VolumeRpcAPITestCase(test.TestCase): def test_detach_volume(self): self._test_volume_api('detach_volume', rpc_method='call', - volume=self.fake_volume, + volume=self.fake_volume_obj, + attachment_id='fake_uuid', + version="3.0") + + def test_detach_volume_cluster(self): + self._set_cluster() + self._test_volume_api('detach_volume', + rpc_method='call', + volume=self.fake_volume_obj, attachment_id='fake_uuid', version="3.0") def test_copy_volume_to_image(self): self._test_volume_api('copy_volume_to_image', rpc_method='cast', - volume=self.fake_volume, + volume=self.fake_volume_obj, image_meta={'id': 'fake_image_id', 'container_format': 'fake_type', 'disk_format': 'fake_type'}, @@ -435,10 +459,28 @@ class VolumeRpcAPITestCase(test.TestCase): connector='fake_connector', version='3.0') + @mock.patch('oslo_messaging.RPCClient.can_send_version', return_value=True) + def test_initialize_connection_cluster(self, mock_can_send_version): + self._set_cluster() + self._test_volume_api('initialize_connection', + rpc_method='call', + volume=self.fake_volume_obj, + connector='fake_connector', + version='3.0') + def test_terminate_connection(self): self._test_volume_api('terminate_connection', rpc_method='call', - volume=self.fake_volume, + volume=self.fake_volume_obj, + connector='fake_connector', + force=False, + version='3.0') + + def test_terminate_connection_cluster(self): + self._set_cluster() + self._test_volume_api('terminate_connection', + rpc_method='call', + volume=self.fake_volume_obj, connector='fake_connector', force=False, version='3.0') @@ -446,7 +488,7 @@ class VolumeRpcAPITestCase(test.TestCase): def test_accept_transfer(self): self._test_volume_api('accept_transfer', rpc_method='call', - volume=self.fake_volume, + volume=self.fake_volume_obj, new_user='e5565fd0-06c8-11e3-' '8ffd-0800200c9b77', new_project='e4465fd0-06c8-11e3' @@ -566,7 +608,7 @@ class VolumeRpcAPITestCase(test.TestCase): def test_remove_export(self): self._test_volume_api('remove_export', rpc_method='cast', - volume=self.fake_volume, + volume=self.fake_volume_obj, version='3.0') @mock.patch('oslo_messaging.RPCClient.can_send_version', diff --git a/cinder/volume/rpcapi.py b/cinder/volume/rpcapi.py index fbf8ad25a67..1af6bed9c40 100644 --- a/cinder/volume/rpcapi.py +++ b/cinder/volume/rpcapi.py @@ -188,17 +188,17 @@ class VolumeAPI(rpc.RPCAPI): def attach_volume(self, ctxt, volume, instance_uuid, host_name, mountpoint, mode): - cctxt = self._get_cctxt(volume['host']) + cctxt = self._get_cctxt(volume.service_topic_queue) return cctxt.call(ctxt, 'attach_volume', - volume_id=volume['id'], + volume_id=volume.id, instance_uuid=instance_uuid, host_name=host_name, mountpoint=mountpoint, mode=mode) def detach_volume(self, ctxt, volume, attachment_id): - cctxt = self._get_cctxt(volume['host']) - return cctxt.call(ctxt, 'detach_volume', volume_id=volume['id'], + cctxt = self._get_cctxt(volume.service_topic_queue) + return cctxt.call(ctxt, 'detach_volume', volume_id=volume.id, attachment_id=attachment_id) def copy_volume_to_image(self, ctxt, volume, image_meta): @@ -207,17 +207,17 @@ class VolumeAPI(rpc.RPCAPI): image_meta=image_meta) def initialize_connection(self, ctxt, volume, connector): - cctxt = self._get_cctxt(volume['host']) + cctxt = self._get_cctxt(volume.service_topic_queue) return cctxt.call(ctxt, 'initialize_connection', connector=connector, volume=volume) def terminate_connection(self, ctxt, volume, connector, force=False): - cctxt = self._get_cctxt(volume['host']) + cctxt = self._get_cctxt(volume.service_topic_queue) return cctxt.call(ctxt, 'terminate_connection', volume_id=volume['id'], connector=connector, force=force) def remove_export(self, ctxt, volume): - cctxt = self._get_cctxt(volume['host']) + cctxt = self._get_cctxt(volume.service_topic_queue) cctxt.cast(ctxt, 'remove_export', volume_id=volume['id']) def publish_service_capabilities(self, ctxt): diff --git a/releasenotes/notes/cluster_job_distribution-f916dd2e4cce6c1b.yaml b/releasenotes/notes/cluster_job_distribution-f916dd2e4cce6c1b.yaml index 89515166194..f27078d8ae7 100644 --- a/releasenotes/notes/cluster_job_distribution-f916dd2e4cce6c1b.yaml +++ b/releasenotes/notes/cluster_job_distribution-f916dd2e4cce6c1b.yaml @@ -18,3 +18,5 @@ features: detail, show, enable/disable). Index and detail accept filtering by `name`, `binary`, `disabled`, `num_hosts`, `num_down_hosts`, and up/down status (`is_up`) as URL parameters. Also added their respective policies." + - "HA A-A: Attach and detach operations are now cluster aware and make full + use of clustered cinder-volume services."