Skip to content

Commit b37ad99

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Add "volume migrate" command"
2 parents 8b2f256 + d7c8bb8 commit b37ad99

8 files changed

Lines changed: 280 additions & 2 deletions

File tree

doc/source/command-objects/volume.rst

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,48 @@ List volumes
197197
198198
*Volume version 2 only*
199199
200+
volume migrate
201+
--------------
202+
203+
Migrate volume to a new host
204+
205+
.. program:: volume migrate
206+
.. code:: bash
207+
208+
os volume migrate
209+
--host <host>
210+
[--force-host-copy]
211+
[--lock-volume | --unlock-volume]
212+
<volume>
213+
214+
.. option:: --host <host>
215+
216+
Destination host (takes the form: host@backend-name#pool) (required)
217+
218+
.. option:: --force-host-copy
219+
220+
Enable generic host-based force-migration,
221+
which bypasses driver optimizations
222+
223+
.. option:: --lock-volume
224+
225+
If specified, the volume state will be locked and will not allow
226+
a migration to be aborted (possibly by another operation)
227+
228+
*Volume version 2 only*
229+
230+
.. option:: --unlock-volume
231+
232+
If specified, the volume state will not be locked and the a
233+
migration can be aborted (default) (possibly by another operation)
234+
235+
*Volume version 2 only*
236+
237+
.. _volume_migrate-volume:
238+
.. describe:: <volume>
239+
240+
Volume to migrate (name or ID)
241+
200242
volume set
201243
----------
202244

doc/source/commands.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,8 @@ Those actions with an opposite action are noted in parens if applicable.
232232
* ``issue`` (``revoke``) - issue a token
233233
* ``list`` - display summary information about multiple objects
234234
* ``lock`` (``unlock``) - lock one or more servers so that non-admin user won't be able to execute actions
235-
* ``migrate`` - move a server to a different host; ``--live`` performs a
236-
live migration if possible
235+
* ``migrate`` - move a server or a volume to a different host; ``--live`` performs a
236+
live server migration if possible
237237
* ``pause`` (``unpause``) - stop one or more servers and leave them in memory
238238
* ``reboot`` - forcibly reboot a server
239239
* ``rebuild`` - rebuild a server using (most of) the same arguments as in the original create

openstackclient/tests/unit/volume/v1/test_volume.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,68 @@ def test_volume_list_negative_limit(self):
739739
self.cmd, arglist, verifylist)
740740

741741

742+
class TestVolumeMigrate(TestVolume):
743+
744+
_volume = volume_fakes.FakeVolume.create_one_volume()
745+
746+
def setUp(self):
747+
super(TestVolumeMigrate, self).setUp()
748+
749+
self.volumes_mock.get.return_value = self._volume
750+
self.volumes_mock.migrate_volume.return_value = None
751+
# Get the command object to test
752+
self.cmd = volume.MigrateVolume(self.app, None)
753+
754+
def test_volume_migrate(self):
755+
arglist = [
756+
"--host", "host@backend-name#pool",
757+
self._volume.id,
758+
]
759+
verifylist = [
760+
("force_host_copy", False),
761+
("host", "host@backend-name#pool"),
762+
("volume", self._volume.id),
763+
]
764+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
765+
766+
result = self.cmd.take_action(parsed_args)
767+
self.volumes_mock.get.assert_called_once_with(self._volume.id)
768+
self.volumes_mock.migrate_volume.assert_called_once_with(
769+
self._volume.id, "host@backend-name#pool", False)
770+
self.assertIsNone(result)
771+
772+
def test_volume_migrate_with_option(self):
773+
arglist = [
774+
"--force-host-copy",
775+
"--host", "host@backend-name#pool",
776+
self._volume.id,
777+
]
778+
verifylist = [
779+
("force_host_copy", True),
780+
("host", "host@backend-name#pool"),
781+
("volume", self._volume.id),
782+
]
783+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
784+
785+
result = self.cmd.take_action(parsed_args)
786+
self.volumes_mock.get.assert_called_once_with(self._volume.id)
787+
self.volumes_mock.migrate_volume.assert_called_once_with(
788+
self._volume.id, "host@backend-name#pool", True)
789+
self.assertIsNone(result)
790+
791+
def test_volume_migrate_without_host(self):
792+
arglist = [
793+
self._volume.id,
794+
]
795+
verifylist = [
796+
("force_host_copy", False),
797+
("volume", self._volume.id),
798+
]
799+
800+
self.assertRaises(tests_utils.ParserException, self.check_parser,
801+
self.cmd, arglist, verifylist)
802+
803+
742804
class TestVolumeSet(TestVolume):
743805

744806
_volume = volume_fakes.FakeVolume.create_one_volume()

openstackclient/tests/unit/volume/v2/test_volume.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,96 @@ def test_volume_list_negative_limit(self):
996996
self.cmd, arglist, verifylist)
997997

998998

999+
class TestVolumeMigrate(TestVolume):
1000+
1001+
_volume = volume_fakes.FakeVolume.create_one_volume()
1002+
1003+
def setUp(self):
1004+
super(TestVolumeMigrate, self).setUp()
1005+
1006+
self.volumes_mock.get.return_value = self._volume
1007+
self.volumes_mock.migrate_volume.return_value = None
1008+
# Get the command object to test
1009+
self.cmd = volume.MigrateVolume(self.app, None)
1010+
1011+
def test_volume_migrate(self):
1012+
arglist = [
1013+
"--host", "host@backend-name#pool",
1014+
self._volume.id,
1015+
]
1016+
verifylist = [
1017+
("force_host_copy", False),
1018+
("lock_volume", False),
1019+
("unlock_volume", False),
1020+
("host", "host@backend-name#pool"),
1021+
("volume", self._volume.id),
1022+
]
1023+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1024+
1025+
result = self.cmd.take_action(parsed_args)
1026+
self.volumes_mock.get.assert_called_once_with(self._volume.id)
1027+
self.volumes_mock.migrate_volume.assert_called_once_with(
1028+
self._volume.id, "host@backend-name#pool", False, False)
1029+
self.assertIsNone(result)
1030+
1031+
def test_volume_migrate_with_option(self):
1032+
arglist = [
1033+
"--force-host-copy",
1034+
"--lock-volume",
1035+
"--host", "host@backend-name#pool",
1036+
self._volume.id,
1037+
]
1038+
verifylist = [
1039+
("force_host_copy", True),
1040+
("lock_volume", True),
1041+
("unlock_volume", False),
1042+
("host", "host@backend-name#pool"),
1043+
("volume", self._volume.id),
1044+
]
1045+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1046+
1047+
result = self.cmd.take_action(parsed_args)
1048+
self.volumes_mock.get.assert_called_once_with(self._volume.id)
1049+
self.volumes_mock.migrate_volume.assert_called_once_with(
1050+
self._volume.id, "host@backend-name#pool", True, True)
1051+
self.assertIsNone(result)
1052+
1053+
def test_volume_migrate_with_unlock_volume(self):
1054+
arglist = [
1055+
"--unlock-volume",
1056+
"--host", "host@backend-name#pool",
1057+
self._volume.id,
1058+
]
1059+
verifylist = [
1060+
("force_host_copy", False),
1061+
("lock_volume", False),
1062+
("unlock_volume", True),
1063+
("host", "host@backend-name#pool"),
1064+
("volume", self._volume.id),
1065+
]
1066+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
1067+
1068+
result = self.cmd.take_action(parsed_args)
1069+
self.volumes_mock.get.assert_called_once_with(self._volume.id)
1070+
self.volumes_mock.migrate_volume.assert_called_once_with(
1071+
self._volume.id, "host@backend-name#pool", False, False)
1072+
self.assertIsNone(result)
1073+
1074+
def test_volume_migrate_without_host(self):
1075+
arglist = [
1076+
self._volume.id,
1077+
]
1078+
verifylist = [
1079+
("force_host_copy", False),
1080+
("lock_volume", False),
1081+
("unlock_volume", False),
1082+
("volume", self._volume.id),
1083+
]
1084+
1085+
self.assertRaises(tests_utils.ParserException, self.check_parser,
1086+
self.cmd, arglist, verifylist)
1087+
1088+
9991089
class TestVolumeSet(TestVolume):
10001090

10011091
def setUp(self):

openstackclient/volume/v1/volume.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,37 @@ def _format_attach(attachments):
344344
) for s in data))
345345

346346

347+
class MigrateVolume(command.Command):
348+
"""Migrate volume to a new host"""
349+
350+
def get_parser(self, prog_name):
351+
parser = super(MigrateVolume, self).get_parser(prog_name)
352+
parser.add_argument(
353+
'volume',
354+
metavar="<volume>",
355+
help=_("Volume to migrate (name or ID)")
356+
)
357+
parser.add_argument(
358+
'--host',
359+
metavar="<host>",
360+
required=True,
361+
help=_("Destination host (takes the form: host@backend-name#pool)")
362+
)
363+
parser.add_argument(
364+
'--force-host-copy',
365+
action="store_true",
366+
help=_("Enable generic host-based force-migration, "
367+
"which bypasses driver optimizations")
368+
)
369+
return parser
370+
371+
def take_action(self, parsed_args):
372+
volume_client = self.app.client_manager.volume
373+
volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
374+
volume_client.volumes.migrate_volume(volume.id, parsed_args.host,
375+
parsed_args.force_host_copy,)
376+
377+
347378
class SetVolume(command.Command):
348379
"""Set volume properties"""
349380

openstackclient/volume/v2/volume.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,53 @@ def _format_attach(attachments):
409409
) for s in data))
410410

411411

412+
class MigrateVolume(command.Command):
413+
"""Migrate volume to a new host"""
414+
415+
def get_parser(self, prog_name):
416+
parser = super(MigrateVolume, self).get_parser(prog_name)
417+
parser.add_argument(
418+
'volume',
419+
metavar="<volume>",
420+
help=_("Volume to migrate (name or ID)")
421+
)
422+
parser.add_argument(
423+
'--host',
424+
metavar="<host>",
425+
required=True,
426+
help=_("Destination host (takes the form: host@backend-name#pool)")
427+
)
428+
parser.add_argument(
429+
'--force-host-copy',
430+
action="store_true",
431+
help=_("Enable generic host-based force-migration, "
432+
"which bypasses driver optimizations")
433+
)
434+
lock_group = parser.add_mutually_exclusive_group()
435+
lock_group.add_argument(
436+
'--lock-volume',
437+
action="store_true",
438+
help=_("If specified, the volume state will be locked "
439+
"and will not allow a migration to be aborted "
440+
"(possibly by another operation)")
441+
)
442+
lock_group.add_argument(
443+
'--unlock-volume',
444+
action="store_true",
445+
help=_("If specified, the volume state will not be "
446+
"locked and the a migration can be aborted "
447+
"(default) (possibly by another operation)")
448+
)
449+
return parser
450+
451+
def take_action(self, parsed_args):
452+
volume_client = self.app.client_manager.volume
453+
volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
454+
volume_client.volumes.migrate_volume(volume.id, parsed_args.host,
455+
parsed_args.force_host_copy,
456+
parsed_args.lock_volume,)
457+
458+
412459
class SetVolume(command.Command):
413460
"""Set volume properties"""
414461

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
features:
3+
- Add ``volume migrate`` command.
4+
[Blueprint `cinder-command-support <https://blueprints.launchpad.net/python-openstackclient/+spec/cinder-command-support>`_]

setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ openstack.volume.v1 =
463463
volume_create = openstackclient.volume.v1.volume:CreateVolume
464464
volume_delete = openstackclient.volume.v1.volume:DeleteVolume
465465
volume_list = openstackclient.volume.v1.volume:ListVolume
466+
volume_migrate = openstackclient.volume.v1.volume:MigrateVolume
466467
volume_set = openstackclient.volume.v1.volume:SetVolume
467468
volume_show = openstackclient.volume.v1.volume:ShowVolume
468469
volume_unset = openstackclient.volume.v1.volume:UnsetVolume
@@ -517,6 +518,7 @@ openstack.volume.v2 =
517518
volume_create = openstackclient.volume.v2.volume:CreateVolume
518519
volume_delete = openstackclient.volume.v2.volume:DeleteVolume
519520
volume_list = openstackclient.volume.v2.volume:ListVolume
521+
volume_migrate = openstackclient.volume.v2.volume:MigrateVolume
520522
volume_set = openstackclient.volume.v2.volume:SetVolume
521523
volume_show = openstackclient.volume.v2.volume:ShowVolume
522524
volume_unset = openstackclient.volume.v2.volume:UnsetVolume

0 commit comments

Comments
 (0)