summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHuanxuan Ao <huanxuan.ao@easystack.cn>2016-10-01 18:38:29 +0800
committerHuanxuan Ao <huanxuan.ao@easystack.cn>2016-10-18 11:48:30 +0800
commitd7c8bb88e4a117d8ab6a53c3a7d14cc7a4105eda (patch)
tree0d57ee569161f51704811caf70b5985ea3a75c8d
parent5e3ec1b42faf7dc49722b58829b6c2cf5c15da79 (diff)
downloadpython-openstackclient-d7c8bb88e4a117d8ab6a53c3a7d14cc7a4105eda.tar.gz
Add "volume migrate" command
Add "volume migrate" command in volume v1 and v2 to support migrating volume to a new host Change-Id: Ie4e6037171a31a872006a13f9fd1e15eaa627c26 Implements: bp cinder-command-support
-rw-r--r--doc/source/command-objects/volume.rst42
-rw-r--r--doc/source/commands.rst4
-rw-r--r--openstackclient/tests/unit/volume/v1/test_volume.py62
-rw-r--r--openstackclient/tests/unit/volume/v2/test_volume.py90
-rw-r--r--openstackclient/volume/v1/volume.py31
-rw-r--r--openstackclient/volume/v2/volume.py47
-rw-r--r--releasenotes/notes/volume-migrate-command-52cf6edd62fe17a7.yaml4
-rw-r--r--setup.cfg2
8 files changed, 280 insertions, 2 deletions
diff --git a/doc/source/command-objects/volume.rst b/doc/source/command-objects/volume.rst
index 8f123361..703a5c76 100644
--- a/doc/source/command-objects/volume.rst
+++ b/doc/source/command-objects/volume.rst
@@ -197,6 +197,48 @@ List volumes
*Volume version 2 only*
+volume migrate
+--------------
+
+Migrate volume to a new host
+
+.. program:: volume migrate
+.. code:: bash
+
+ os volume migrate
+ --host <host>
+ [--force-host-copy]
+ [--lock-volume | --unlock-volume]
+ <volume>
+
+.. option:: --host <host>
+
+ Destination host (takes the form: host@backend-name#pool) (required)
+
+.. option:: --force-host-copy
+
+ Enable generic host-based force-migration,
+ which bypasses driver optimizations
+
+.. option:: --lock-volume
+
+ If specified, the volume state will be locked and will not allow
+ a migration to be aborted (possibly by another operation)
+
+ *Volume version 2 only*
+
+.. option:: --unlock-volume
+
+ If specified, the volume state will not be locked and the a
+ migration can be aborted (default) (possibly by another operation)
+
+ *Volume version 2 only*
+
+.. _volume_migrate-volume:
+.. describe:: <volume>
+
+ Volume to migrate (name or ID)
+
volume set
----------
diff --git a/doc/source/commands.rst b/doc/source/commands.rst
index de473a06..072767a4 100644
--- a/doc/source/commands.rst
+++ b/doc/source/commands.rst
@@ -230,8 +230,8 @@ Those actions with an opposite action are noted in parens if applicable.
* ``issue`` (``revoke``) - issue a token
* ``list`` - display summary information about multiple objects
* ``lock`` (``unlock``) - lock one or more servers so that non-admin user won't be able to execute actions
-* ``migrate`` - move a server to a different host; ``--live`` performs a
- live migration if possible
+* ``migrate`` - move a server or a volume to a different host; ``--live`` performs a
+ live server migration if possible
* ``pause`` (``unpause``) - stop one or more servers and leave them in memory
* ``reboot`` - forcibly reboot a server
* ``rebuild`` - rebuild a server using (most of) the same arguments as in the original create
diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py
index 73c00844..8e79ecbb 100644
--- a/openstackclient/tests/unit/volume/v1/test_volume.py
+++ b/openstackclient/tests/unit/volume/v1/test_volume.py
@@ -739,6 +739,68 @@ class TestVolumeList(TestVolume):
self.cmd, arglist, verifylist)
+class TestVolumeMigrate(TestVolume):
+
+ _volume = volume_fakes.FakeVolume.create_one_volume()
+
+ def setUp(self):
+ super(TestVolumeMigrate, self).setUp()
+
+ self.volumes_mock.get.return_value = self._volume
+ self.volumes_mock.migrate_volume.return_value = None
+ # Get the command object to test
+ self.cmd = volume.MigrateVolume(self.app, None)
+
+ def test_volume_migrate(self):
+ arglist = [
+ "--host", "host@backend-name#pool",
+ self._volume.id,
+ ]
+ verifylist = [
+ ("force_host_copy", False),
+ ("host", "host@backend-name#pool"),
+ ("volume", self._volume.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.volumes_mock.get.assert_called_once_with(self._volume.id)
+ self.volumes_mock.migrate_volume.assert_called_once_with(
+ self._volume.id, "host@backend-name#pool", False)
+ self.assertIsNone(result)
+
+ def test_volume_migrate_with_option(self):
+ arglist = [
+ "--force-host-copy",
+ "--host", "host@backend-name#pool",
+ self._volume.id,
+ ]
+ verifylist = [
+ ("force_host_copy", True),
+ ("host", "host@backend-name#pool"),
+ ("volume", self._volume.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.volumes_mock.get.assert_called_once_with(self._volume.id)
+ self.volumes_mock.migrate_volume.assert_called_once_with(
+ self._volume.id, "host@backend-name#pool", True)
+ self.assertIsNone(result)
+
+ def test_volume_migrate_without_host(self):
+ arglist = [
+ self._volume.id,
+ ]
+ verifylist = [
+ ("force_host_copy", False),
+ ("volume", self._volume.id),
+ ]
+
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+
class TestVolumeSet(TestVolume):
_volume = volume_fakes.FakeVolume.create_one_volume()
diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py
index f4a7c142..0a436e61 100644
--- a/openstackclient/tests/unit/volume/v2/test_volume.py
+++ b/openstackclient/tests/unit/volume/v2/test_volume.py
@@ -996,6 +996,96 @@ class TestVolumeList(TestVolume):
self.cmd, arglist, verifylist)
+class TestVolumeMigrate(TestVolume):
+
+ _volume = volume_fakes.FakeVolume.create_one_volume()
+
+ def setUp(self):
+ super(TestVolumeMigrate, self).setUp()
+
+ self.volumes_mock.get.return_value = self._volume
+ self.volumes_mock.migrate_volume.return_value = None
+ # Get the command object to test
+ self.cmd = volume.MigrateVolume(self.app, None)
+
+ def test_volume_migrate(self):
+ arglist = [
+ "--host", "host@backend-name#pool",
+ self._volume.id,
+ ]
+ verifylist = [
+ ("force_host_copy", False),
+ ("lock_volume", False),
+ ("unlock_volume", False),
+ ("host", "host@backend-name#pool"),
+ ("volume", self._volume.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.volumes_mock.get.assert_called_once_with(self._volume.id)
+ self.volumes_mock.migrate_volume.assert_called_once_with(
+ self._volume.id, "host@backend-name#pool", False, False)
+ self.assertIsNone(result)
+
+ def test_volume_migrate_with_option(self):
+ arglist = [
+ "--force-host-copy",
+ "--lock-volume",
+ "--host", "host@backend-name#pool",
+ self._volume.id,
+ ]
+ verifylist = [
+ ("force_host_copy", True),
+ ("lock_volume", True),
+ ("unlock_volume", False),
+ ("host", "host@backend-name#pool"),
+ ("volume", self._volume.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.volumes_mock.get.assert_called_once_with(self._volume.id)
+ self.volumes_mock.migrate_volume.assert_called_once_with(
+ self._volume.id, "host@backend-name#pool", True, True)
+ self.assertIsNone(result)
+
+ def test_volume_migrate_with_unlock_volume(self):
+ arglist = [
+ "--unlock-volume",
+ "--host", "host@backend-name#pool",
+ self._volume.id,
+ ]
+ verifylist = [
+ ("force_host_copy", False),
+ ("lock_volume", False),
+ ("unlock_volume", True),
+ ("host", "host@backend-name#pool"),
+ ("volume", self._volume.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.volumes_mock.get.assert_called_once_with(self._volume.id)
+ self.volumes_mock.migrate_volume.assert_called_once_with(
+ self._volume.id, "host@backend-name#pool", False, False)
+ self.assertIsNone(result)
+
+ def test_volume_migrate_without_host(self):
+ arglist = [
+ self._volume.id,
+ ]
+ verifylist = [
+ ("force_host_copy", False),
+ ("lock_volume", False),
+ ("unlock_volume", False),
+ ("volume", self._volume.id),
+ ]
+
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+
class TestVolumeSet(TestVolume):
def setUp(self):
diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py
index cafe8ce6..63bcfbb8 100644
--- a/openstackclient/volume/v1/volume.py
+++ b/openstackclient/volume/v1/volume.py
@@ -344,6 +344,37 @@ class ListVolume(command.Lister):
) for s in data))
+class MigrateVolume(command.Command):
+ """Migrate volume to a new host"""
+
+ def get_parser(self, prog_name):
+ parser = super(MigrateVolume, self).get_parser(prog_name)
+ parser.add_argument(
+ 'volume',
+ metavar="<volume>",
+ help=_("Volume to migrate (name or ID)")
+ )
+ parser.add_argument(
+ '--host',
+ metavar="<host>",
+ required=True,
+ help=_("Destination host (takes the form: host@backend-name#pool)")
+ )
+ parser.add_argument(
+ '--force-host-copy',
+ action="store_true",
+ help=_("Enable generic host-based force-migration, "
+ "which bypasses driver optimizations")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
+ volume_client.volumes.migrate_volume(volume.id, parsed_args.host,
+ parsed_args.force_host_copy,)
+
+
class SetVolume(command.Command):
"""Set volume properties"""
diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py
index cb409711..0e4071fb 100644
--- a/openstackclient/volume/v2/volume.py
+++ b/openstackclient/volume/v2/volume.py
@@ -409,6 +409,53 @@ class ListVolume(command.Lister):
) for s in data))
+class MigrateVolume(command.Command):
+ """Migrate volume to a new host"""
+
+ def get_parser(self, prog_name):
+ parser = super(MigrateVolume, self).get_parser(prog_name)
+ parser.add_argument(
+ 'volume',
+ metavar="<volume>",
+ help=_("Volume to migrate (name or ID)")
+ )
+ parser.add_argument(
+ '--host',
+ metavar="<host>",
+ required=True,
+ help=_("Destination host (takes the form: host@backend-name#pool)")
+ )
+ parser.add_argument(
+ '--force-host-copy',
+ action="store_true",
+ help=_("Enable generic host-based force-migration, "
+ "which bypasses driver optimizations")
+ )
+ lock_group = parser.add_mutually_exclusive_group()
+ lock_group.add_argument(
+ '--lock-volume',
+ action="store_true",
+ help=_("If specified, the volume state will be locked "
+ "and will not allow a migration to be aborted "
+ "(possibly by another operation)")
+ )
+ lock_group.add_argument(
+ '--unlock-volume',
+ action="store_true",
+ help=_("If specified, the volume state will not be "
+ "locked and the a migration can be aborted "
+ "(default) (possibly by another operation)")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
+ volume_client.volumes.migrate_volume(volume.id, parsed_args.host,
+ parsed_args.force_host_copy,
+ parsed_args.lock_volume,)
+
+
class SetVolume(command.Command):
"""Set volume properties"""
diff --git a/releasenotes/notes/volume-migrate-command-52cf6edd62fe17a7.yaml b/releasenotes/notes/volume-migrate-command-52cf6edd62fe17a7.yaml
new file mode 100644
index 00000000..634f0082
--- /dev/null
+++ b/releasenotes/notes/volume-migrate-command-52cf6edd62fe17a7.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - Add ``volume migrate`` command.
+ [Blueprint `cinder-command-support <https://blueprints.launchpad.net/python-openstackclient/+spec/cinder-command-support>`_]
diff --git a/setup.cfg b/setup.cfg
index a4abec1b..c9443739 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -463,6 +463,7 @@ openstack.volume.v1 =
volume_create = openstackclient.volume.v1.volume:CreateVolume
volume_delete = openstackclient.volume.v1.volume:DeleteVolume
volume_list = openstackclient.volume.v1.volume:ListVolume
+ volume_migrate = openstackclient.volume.v1.volume:MigrateVolume
volume_set = openstackclient.volume.v1.volume:SetVolume
volume_show = openstackclient.volume.v1.volume:ShowVolume
volume_unset = openstackclient.volume.v1.volume:UnsetVolume
@@ -517,6 +518,7 @@ openstack.volume.v2 =
volume_create = openstackclient.volume.v2.volume:CreateVolume
volume_delete = openstackclient.volume.v2.volume:DeleteVolume
volume_list = openstackclient.volume.v2.volume:ListVolume
+ volume_migrate = openstackclient.volume.v2.volume:MigrateVolume
volume_set = openstackclient.volume.v2.volume:SetVolume
volume_show = openstackclient.volume.v2.volume:ShowVolume
volume_unset = openstackclient.volume.v2.volume:UnsetVolume