From e1ff87378a8a7b9c00eb7cf75a24c5a442d92217 Mon Sep 17 00:00:00 2001 From: Tomoki Sekiyama Date: Tue, 31 Mar 2015 18:30:14 -0400 Subject: [PATCH] Support host-attach of volumes This patch adds the host_name optional argument to attach API in order to enable users of cinderclient to attach volumes not only to the instances but also to the hosts. The API interface is implemented in https://review.openstack.org/#/c/34125/ . Change-Id: I0ff8ef19d1e3073fee1b9ec0f60609c37dfab9ab Implements: blueprint host-attach --- cinderclient/tests/unit/v1/fakes.py | 6 ++--- cinderclient/tests/unit/v1/test_volumes.py | 5 +++++ cinderclient/tests/unit/v2/fakes.py | 6 ++--- cinderclient/tests/unit/v2/test_volumes.py | 5 +++++ cinderclient/v1/volumes.py | 26 +++++++++++++--------- cinderclient/v2/volumes.py | 25 ++++++++++++--------- 6 files changed, 47 insertions(+), 26 deletions(-) diff --git a/cinderclient/tests/unit/v1/fakes.py b/cinderclient/tests/unit/v1/fakes.py index e391dc9..35043d2 100644 --- a/cinderclient/tests/unit/v1/fakes.py +++ b/cinderclient/tests/unit/v1/fakes.py @@ -330,9 +330,9 @@ class FakeHTTPClient(base_client.HTTPClient): assert len(list(body)) == 1 action = list(body)[0] if action == 'os-attach': - assert sorted(list(body[action])) == ['instance_uuid', - 'mode', - 'mountpoint'] + keys = sorted(list(body[action])) + assert (keys == ['instance_uuid', 'mode', 'mountpoint'] or + keys == ['host_name', 'mode', 'mountpoint']) elif action == 'os-detach': assert body[action] is None elif action == 'os-reserve': diff --git a/cinderclient/tests/unit/v1/test_volumes.py b/cinderclient/tests/unit/v1/test_volumes.py index 75c81bd..8906a4e 100644 --- a/cinderclient/tests/unit/v1/test_volumes.py +++ b/cinderclient/tests/unit/v1/test_volumes.py @@ -38,6 +38,11 @@ class VolumesTest(utils.TestCase): cs.volumes.attach(v, 1, '/dev/vdc', mode='rw') cs.assert_called('POST', '/volumes/1234/action') + def test_attach_to_host(self): + v = cs.volumes.get('1234') + cs.volumes.attach(v, None, None, host_name='test', mode='rw') + cs.assert_called('POST', '/volumes/1234/action') + def test_detach(self): v = cs.volumes.get('1234') cs.volumes.detach(v) diff --git a/cinderclient/tests/unit/v2/fakes.py b/cinderclient/tests/unit/v2/fakes.py index 82cdc9c..a9e4041 100644 --- a/cinderclient/tests/unit/v2/fakes.py +++ b/cinderclient/tests/unit/v2/fakes.py @@ -411,9 +411,9 @@ class FakeHTTPClient(base_client.HTTPClient): assert len(list(body)) == 1 action = list(body)[0] if action == 'os-attach': - assert sorted(list(body[action])) == ['instance_uuid', - 'mode', - 'mountpoint'] + keys = sorted(list(body[action])) + assert (keys == ['instance_uuid', 'mode', 'mountpoint'] or + keys == ['host_name', 'mode', 'mountpoint']) elif action == 'os-detach': assert body[action] is None elif action == 'os-reserve': diff --git a/cinderclient/tests/unit/v2/test_volumes.py b/cinderclient/tests/unit/v2/test_volumes.py index d94b471..8ddb59e 100644 --- a/cinderclient/tests/unit/v2/test_volumes.py +++ b/cinderclient/tests/unit/v2/test_volumes.py @@ -104,6 +104,11 @@ class VolumesTest(utils.TestCase): cs.volumes.attach(v, 1, '/dev/vdc', mode='ro') cs.assert_called('POST', '/volumes/1234/action') + def test_attach_to_host(self): + v = cs.volumes.get('1234') + cs.volumes.attach(v, None, None, host_name='test', mode='rw') + cs.assert_called('POST', '/volumes/1234/action') + def test_detach(self): v = cs.volumes.get('1234') cs.volumes.detach(v) diff --git a/cinderclient/v1/volumes.py b/cinderclient/v1/volumes.py index 3ef3181..22cf5aa 100644 --- a/cinderclient/v1/volumes.py +++ b/cinderclient/v1/volumes.py @@ -40,14 +40,17 @@ class Volume(base.Resource): """Update the display_name or display_description for this volume.""" self.manager.update(self, **kwargs) - def attach(self, instance_uuid, mountpoint, mode='rw'): + def attach(self, instance_uuid, mountpoint, mode='rw', + host_name=None): """Set attachment metadata. :param instance_uuid: uuid of the attaching instance. - :param mountpoint: mountpoint on the attaching instance. + :param mountpoint: mountpoint on the attaching instance or host. :param mode: the access mode + :param host_name: name of the attaching host. """ - return self.manager.attach(self, instance_uuid, mountpoint, mode) + return self.manager.attach(self, instance_uuid, mountpoint, mode, + host_name) def detach(self): """Clear attachment metadata.""" @@ -255,21 +258,24 @@ class VolumeManager(base.ManagerWithFind): url = '/volumes/%s/action' % base.getid(volume) return self.api.client.post(url, body=body) - def attach(self, volume, instance_uuid, mountpoint, mode='rw'): + def attach(self, volume, instance_uuid, mountpoint, mode='rw', + host_name=None): """ Set attachment metadata. :param volume: The :class:`Volume` (or its ID) you would like to attach. - :param instance_uuid: uuid of the attaching instance. + :param instance_uuid: uuid of the attaching instance or host. :param mountpoint: mountpoint on the attaching instance. :param mode: the access mode. + :param host_name: name of the attaching host. """ - return self._action('os-attach', - volume, - {'instance_uuid': instance_uuid, - 'mountpoint': mountpoint, - 'mode': mode}) + body = {'mountpoint': mountpoint, 'mode': mode} + if instance_uuid is not None: + body.update({'instance_uuid': instance_uuid}) + if host_name is not None: + body.update({'host_name': host_name}) + return self._action('os-attach', volume, body) def detach(self, volume): """ diff --git a/cinderclient/v2/volumes.py b/cinderclient/v2/volumes.py index 28e8129..e2e5368 100644 --- a/cinderclient/v2/volumes.py +++ b/cinderclient/v2/volumes.py @@ -45,14 +45,16 @@ class Volume(base.Resource): """Update the name or description for this volume.""" self.manager.update(self, **kwargs) - def attach(self, instance_uuid, mountpoint, mode='rw'): + def attach(self, instance_uuid, mountpoint, mode='rw', host_name=None): """Set attachment metadata. :param instance_uuid: uuid of the attaching instance. - :param mountpoint: mountpoint on the attaching instance. + :param mountpoint: mountpoint on the attaching instance or host. :param mode: the access mode. + :param host_name: name of the attaching host. """ - return self.manager.attach(self, instance_uuid, mountpoint, mode) + return self.manager.attach(self, instance_uuid, mountpoint, mode, + host_name) def detach(self): """Clear attachment metadata.""" @@ -374,20 +376,23 @@ class VolumeManager(base.ManagerWithFind): url = '/volumes/%s/action' % base.getid(volume) return self.api.client.post(url, body=body) - def attach(self, volume, instance_uuid, mountpoint, mode='rw'): + def attach(self, volume, instance_uuid, mountpoint, mode='rw', + host_name=None): """Set attachment metadata. :param volume: The :class:`Volume` (or its ID) you would like to attach. :param instance_uuid: uuid of the attaching instance. - :param mountpoint: mountpoint on the attaching instance. + :param mountpoint: mountpoint on the attaching instance or host. :param mode: the access mode. + :param host_name: name of the attaching host. """ - return self._action('os-attach', - volume, - {'instance_uuid': instance_uuid, - 'mountpoint': mountpoint, - 'mode': mode}) + body = {'mountpoint': mountpoint, 'mode': mode} + if instance_uuid is not None: + body.update({'instance_uuid': instance_uuid}) + if host_name is not None: + body.update({'host_name': host_name}) + return self._action('os-attach', volume, body) def detach(self, volume): """Clear attachment metadata.