Implement "volume transfer request delete" command
Add "volume transfer request delete" command in volume v1 and v2. Also add the unit tests, docs, release note and functional tests Change-Id: Ic3d375bc8df3312fac53c1800d75f48376b8c91c Implements: bp cinder-command-support Co-Authored-By: Sheel Rana <ranasheel2000@gmail.com>
This commit is contained in:
parent
6f4acc45c6
commit
d2273ecea5
@ -25,6 +25,22 @@ Create volume transfer request
|
||||
|
||||
Volume to transfer (name or ID)
|
||||
|
||||
volume transfer request delete
|
||||
------------------------------
|
||||
|
||||
Delete volume transfer request(s)
|
||||
|
||||
.. program:: volume transfer request delete
|
||||
.. code:: bash
|
||||
|
||||
os volume transfer request delete
|
||||
<transfer-request> [<transfer-request> ...]
|
||||
|
||||
.. _volume_transfer_request_delete-transfer-request:
|
||||
.. describe:: <transfer-request>
|
||||
|
||||
Volume transfer request(s) to delete (name or ID)
|
||||
|
||||
volume transfer request list
|
||||
----------------------------
|
||||
|
||||
|
@ -0,0 +1,53 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
from openstackclient.tests.functional.volume.v1 import common
|
||||
|
||||
|
||||
class TransferRequestTests(common.BaseVolumeTests):
|
||||
"""Functional tests for transfer request. """
|
||||
|
||||
NAME = uuid.uuid4().hex
|
||||
VOLUME_NAME = uuid.uuid4().hex
|
||||
HEADERS = ['Name']
|
||||
FIELDS = ['name']
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TransferRequestTests, cls).setUpClass()
|
||||
opts = cls.get_opts(['display_name'])
|
||||
raw_output = cls.openstack(
|
||||
'volume create --size 1 ' + cls.VOLUME_NAME + opts)
|
||||
cls.assertOutput(cls.VOLUME_NAME + '\n', raw_output)
|
||||
|
||||
opts = cls.get_opts(cls.FIELDS)
|
||||
raw_output = cls.openstack(
|
||||
'volume transfer request create ' +
|
||||
cls.VOLUME_NAME +
|
||||
' --name ' + cls.NAME + opts)
|
||||
cls.assertOutput(cls.NAME + '\n', raw_output)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
raw_output_transfer = cls.openstack(
|
||||
'volume transfer request delete ' + cls.NAME)
|
||||
raw_output_volume = cls.openstack(
|
||||
'volume delete ' + cls.VOLUME_NAME)
|
||||
cls.assertOutput('', raw_output_transfer)
|
||||
cls.assertOutput('', raw_output_volume)
|
||||
|
||||
def test_volume_transfer_request_list(self):
|
||||
opts = self.get_opts(self.HEADERS)
|
||||
raw_output = self.openstack('volume transfer request list' + opts)
|
||||
self.assertIn(self.NAME, raw_output)
|
@ -0,0 +1,53 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
from openstackclient.tests.functional.volume.v2 import common
|
||||
|
||||
|
||||
class TransferRequestTests(common.BaseVolumeTests):
|
||||
"""Functional tests for transfer request. """
|
||||
|
||||
NAME = uuid.uuid4().hex
|
||||
VOLUME_NAME = uuid.uuid4().hex
|
||||
HEADERS = ['Name']
|
||||
FIELDS = ['name']
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TransferRequestTests, cls).setUpClass()
|
||||
opts = cls.get_opts(cls.FIELDS)
|
||||
|
||||
raw_output = cls.openstack(
|
||||
'volume create --size 1 ' + cls.VOLUME_NAME + opts)
|
||||
cls.assertOutput(cls.VOLUME_NAME + '\n', raw_output)
|
||||
|
||||
raw_output = cls.openstack(
|
||||
'volume transfer request create ' +
|
||||
cls.VOLUME_NAME +
|
||||
' --name ' + cls.NAME + opts)
|
||||
cls.assertOutput(cls.NAME + '\n', raw_output)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
raw_output_transfer = cls.openstack(
|
||||
'volume transfer request delete ' + cls.NAME)
|
||||
raw_output_volume = cls.openstack(
|
||||
'volume delete ' + cls.VOLUME_NAME)
|
||||
cls.assertOutput('', raw_output_transfer)
|
||||
cls.assertOutput('', raw_output_volume)
|
||||
|
||||
def test_volume_transfer_request_list(self):
|
||||
opts = self.get_opts(self.HEADERS)
|
||||
raw_output = self.openstack('volume transfer request list' + opts)
|
||||
self.assertIn(self.NAME, raw_output)
|
@ -166,6 +166,43 @@ class FakeTransfer(object):
|
||||
|
||||
return transfer
|
||||
|
||||
@staticmethod
|
||||
def create_transfers(attrs=None, count=2):
|
||||
"""Create multiple fake transfers.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes of transfer
|
||||
:param Integer count:
|
||||
The number of transfers to be faked
|
||||
:return:
|
||||
A list of FakeResource objects
|
||||
"""
|
||||
transfers = []
|
||||
for n in range(0, count):
|
||||
transfers.append(FakeTransfer.create_one_transfer(attrs))
|
||||
|
||||
return transfers
|
||||
|
||||
@staticmethod
|
||||
def get_transfers(transfers=None, count=2):
|
||||
"""Get an iterable MagicMock object with a list of faked transfers.
|
||||
|
||||
If transfers list is provided, then initialize the Mock object with the
|
||||
list. Otherwise create one.
|
||||
|
||||
:param List transfers:
|
||||
A list of FakeResource objects faking transfers
|
||||
:param Integer count:
|
||||
The number of transfers to be faked
|
||||
:return
|
||||
An iterable Mock object with side_effect set to a list of faked
|
||||
transfers
|
||||
"""
|
||||
if transfers is None:
|
||||
transfers = FakeTransfer.create_transfers(count)
|
||||
|
||||
return mock.MagicMock(side_effect=transfers)
|
||||
|
||||
|
||||
class FakeService(object):
|
||||
"""Fake one or more Services."""
|
||||
|
@ -12,6 +12,12 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import mock
|
||||
from mock import call
|
||||
|
||||
from osc_lib import exceptions
|
||||
from osc_lib import utils
|
||||
|
||||
from openstackclient.tests.unit.volume.v1 import fakes as transfer_fakes
|
||||
from openstackclient.volume.v1 import volume_transfer_request
|
||||
|
||||
@ -97,6 +103,84 @@ class TestTransferCreate(TestTransfer):
|
||||
self.assertEqual(self.data, data)
|
||||
|
||||
|
||||
class TestTransferDelete(TestTransfer):
|
||||
|
||||
volume_transfers = transfer_fakes.FakeTransfer.create_transfers(count=2)
|
||||
|
||||
def setUp(self):
|
||||
super(TestTransferDelete, self).setUp()
|
||||
|
||||
self.transfer_mock.get = (
|
||||
transfer_fakes.FakeTransfer.get_transfers(self.volume_transfers))
|
||||
self.transfer_mock.delete.return_value = None
|
||||
|
||||
# Get the command object to mock
|
||||
self.cmd = volume_transfer_request.DeleteTransferRequest(
|
||||
self.app, None)
|
||||
|
||||
def test_transfer_delete(self):
|
||||
arglist = [
|
||||
self.volume_transfers[0].id
|
||||
]
|
||||
verifylist = [
|
||||
("transfer_request", [self.volume_transfers[0].id])
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.transfer_mock.delete.assert_called_with(
|
||||
self.volume_transfers[0].id)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_delete_multiple_transfers(self):
|
||||
arglist = []
|
||||
for v in self.volume_transfers:
|
||||
arglist.append(v.id)
|
||||
verifylist = [
|
||||
('transfer_request', arglist),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
calls = []
|
||||
for v in self.volume_transfers:
|
||||
calls.append(call(v.id))
|
||||
self.transfer_mock.delete.assert_has_calls(calls)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_delete_multiple_transfers_with_exception(self):
|
||||
arglist = [
|
||||
self.volume_transfers[0].id,
|
||||
'unexist_transfer',
|
||||
]
|
||||
verifylist = [
|
||||
('transfer_request', arglist),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
find_mock_result = [self.volume_transfers[0], exceptions.CommandError]
|
||||
with mock.patch.object(utils, 'find_resource',
|
||||
side_effect=find_mock_result) as find_mock:
|
||||
try:
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.fail('CommandError should be raised.')
|
||||
except exceptions.CommandError as e:
|
||||
self.assertEqual('1 of 2 volume transfer requests failed '
|
||||
'to delete.', str(e))
|
||||
|
||||
find_mock.assert_any_call(
|
||||
self.transfer_mock, self.volume_transfers[0].id)
|
||||
find_mock.assert_any_call(self.transfer_mock, 'unexist_transfer')
|
||||
|
||||
self.assertEqual(2, find_mock.call_count)
|
||||
self.transfer_mock.delete.assert_called_once_with(
|
||||
self.volume_transfers[0].id,
|
||||
)
|
||||
|
||||
|
||||
class TestTransferList(TestTransfer):
|
||||
|
||||
# The Transfers to be listed
|
||||
|
@ -59,6 +59,43 @@ class FakeTransfer(object):
|
||||
|
||||
return transfer
|
||||
|
||||
@staticmethod
|
||||
def create_transfers(attrs=None, count=2):
|
||||
"""Create multiple fake transfers.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes of transfer
|
||||
:param Integer count:
|
||||
The number of transfers to be faked
|
||||
:return:
|
||||
A list of FakeResource objects
|
||||
"""
|
||||
transfers = []
|
||||
for n in range(0, count):
|
||||
transfers.append(FakeTransfer.create_one_transfer(attrs))
|
||||
|
||||
return transfers
|
||||
|
||||
@staticmethod
|
||||
def get_transfers(transfers=None, count=2):
|
||||
"""Get an iterable MagicMock object with a list of faked transfers.
|
||||
|
||||
If transfers list is provided, then initialize the Mock object with the
|
||||
list. Otherwise create one.
|
||||
|
||||
:param List transfers:
|
||||
A list of FakeResource objects faking transfers
|
||||
:param Integer count:
|
||||
The number of transfers to be faked
|
||||
:return
|
||||
An iterable Mock object with side_effect set to a list of faked
|
||||
transfers
|
||||
"""
|
||||
if transfers is None:
|
||||
transfers = FakeTransfer.create_transfers(count)
|
||||
|
||||
return mock.MagicMock(side_effect=transfers)
|
||||
|
||||
|
||||
class FakeTypeAccess(object):
|
||||
"""Fake one or more volume type access."""
|
||||
|
@ -12,6 +12,12 @@
|
||||
# under the License.
|
||||
#
|
||||
|
||||
import mock
|
||||
from mock import call
|
||||
|
||||
from osc_lib import exceptions
|
||||
from osc_lib import utils
|
||||
|
||||
from openstackclient.tests.unit.volume.v2 import fakes as transfer_fakes
|
||||
from openstackclient.volume.v2 import volume_transfer_request
|
||||
|
||||
@ -97,6 +103,84 @@ class TestTransferCreate(TestTransfer):
|
||||
self.assertEqual(self.data, data)
|
||||
|
||||
|
||||
class TestTransferDelete(TestTransfer):
|
||||
|
||||
volume_transfers = transfer_fakes.FakeTransfer.create_transfers(count=2)
|
||||
|
||||
def setUp(self):
|
||||
super(TestTransferDelete, self).setUp()
|
||||
|
||||
self.transfer_mock.get = (
|
||||
transfer_fakes.FakeTransfer.get_transfers(self.volume_transfers))
|
||||
self.transfer_mock.delete.return_value = None
|
||||
|
||||
# Get the command object to mock
|
||||
self.cmd = volume_transfer_request.DeleteTransferRequest(
|
||||
self.app, None)
|
||||
|
||||
def test_transfer_delete(self):
|
||||
arglist = [
|
||||
self.volume_transfers[0].id
|
||||
]
|
||||
verifylist = [
|
||||
("transfer_request", [self.volume_transfers[0].id])
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.transfer_mock.delete.assert_called_with(
|
||||
self.volume_transfers[0].id)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_delete_multiple_transfers(self):
|
||||
arglist = []
|
||||
for v in self.volume_transfers:
|
||||
arglist.append(v.id)
|
||||
verifylist = [
|
||||
('transfer_request', arglist),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
calls = []
|
||||
for v in self.volume_transfers:
|
||||
calls.append(call(v.id))
|
||||
self.transfer_mock.delete.assert_has_calls(calls)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_delete_multiple_transfers_with_exception(self):
|
||||
arglist = [
|
||||
self.volume_transfers[0].id,
|
||||
'unexist_transfer',
|
||||
]
|
||||
verifylist = [
|
||||
('transfer_request', arglist),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
find_mock_result = [self.volume_transfers[0], exceptions.CommandError]
|
||||
with mock.patch.object(utils, 'find_resource',
|
||||
side_effect=find_mock_result) as find_mock:
|
||||
try:
|
||||
self.cmd.take_action(parsed_args)
|
||||
self.fail('CommandError should be raised.')
|
||||
except exceptions.CommandError as e:
|
||||
self.assertEqual('1 of 2 volume transfer requests failed '
|
||||
'to delete.', str(e))
|
||||
|
||||
find_mock.assert_any_call(
|
||||
self.transfer_mock, self.volume_transfers[0].id)
|
||||
find_mock.assert_any_call(self.transfer_mock, 'unexist_transfer')
|
||||
|
||||
self.assertEqual(2, find_mock.call_count)
|
||||
self.transfer_mock.delete.assert_called_once_with(
|
||||
self.volume_transfers[0].id,
|
||||
)
|
||||
|
||||
|
||||
class TestTransferList(TestTransfer):
|
||||
|
||||
# The Transfers to be listed
|
||||
|
@ -14,13 +14,19 @@
|
||||
|
||||
"""Volume v1 transfer action implementations"""
|
||||
|
||||
import logging
|
||||
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions
|
||||
from osc_lib import utils
|
||||
import six
|
||||
|
||||
from openstackclient.i18n import _
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CreateTransferRequest(command.ShowOne):
|
||||
"""Create volume transfer request."""
|
||||
|
||||
@ -50,6 +56,41 @@ class CreateTransferRequest(command.ShowOne):
|
||||
return zip(*sorted(six.iteritems(volume_transfer_request._info)))
|
||||
|
||||
|
||||
class DeleteTransferRequest(command.Command):
|
||||
"""Delete volume transfer request(s)."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteTransferRequest, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'transfer_request',
|
||||
metavar="<transfer-request>",
|
||||
nargs="+",
|
||||
help=_('Volume transfer request(s) to delete (name or ID)'),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
volume_client = self.app.client_manager.volume
|
||||
result = 0
|
||||
|
||||
for t in parsed_args.transfer_request:
|
||||
try:
|
||||
transfer_request_id = utils.find_resource(
|
||||
volume_client.transfers, t).id
|
||||
volume_client.transfers.delete(transfer_request_id)
|
||||
except Exception as e:
|
||||
result += 1
|
||||
LOG.error(_("Failed to delete volume transfer request "
|
||||
"with name or ID '%(transfer)s': %(e)s")
|
||||
% {'transfer': t, 'e': e})
|
||||
|
||||
if result > 0:
|
||||
total = len(parsed_args.transfer_request)
|
||||
msg = (_("%(result)s of %(total)s volume transfer requests failed"
|
||||
" to delete.") % {'result': result, 'total': total})
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
|
||||
class ListTransferRequests(command.Lister):
|
||||
"""Lists all volume transfer requests."""
|
||||
|
||||
|
@ -14,13 +14,19 @@
|
||||
|
||||
"""Volume v2 transfer action implementations"""
|
||||
|
||||
import logging
|
||||
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions
|
||||
from osc_lib import utils
|
||||
import six
|
||||
|
||||
from openstackclient.i18n import _
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CreateTransferRequest(command.ShowOne):
|
||||
"""Create volume transfer request."""
|
||||
|
||||
@ -50,6 +56,41 @@ class CreateTransferRequest(command.ShowOne):
|
||||
return zip(*sorted(six.iteritems(volume_transfer_request._info)))
|
||||
|
||||
|
||||
class DeleteTransferRequest(command.Command):
|
||||
"""Delete volume transfer request(s)."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteTransferRequest, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'transfer_request',
|
||||
metavar="<transfer-request>",
|
||||
nargs="+",
|
||||
help=_('Volume transfer request(s) to delete (name or ID)'),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
volume_client = self.app.client_manager.volume
|
||||
result = 0
|
||||
|
||||
for t in parsed_args.transfer_request:
|
||||
try:
|
||||
transfer_request_id = utils.find_resource(
|
||||
volume_client.transfers, t).id
|
||||
volume_client.transfers.delete(transfer_request_id)
|
||||
except Exception as e:
|
||||
result += 1
|
||||
LOG.error(_("Failed to delete volume transfer request "
|
||||
"with name or ID '%(transfer)s': %(e)s")
|
||||
% {'transfer': t, 'e': e})
|
||||
|
||||
if result > 0:
|
||||
total = len(parsed_args.transfer_request)
|
||||
msg = (_("%(result)s of %(total)s volume transfer requests failed"
|
||||
" to delete.") % {'result': result, 'total': total})
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
|
||||
class ListTransferRequests(command.Lister):
|
||||
"""Lists all volume transfer requests."""
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- Add ``volume transfer request create`` command in volume v1 and v2
|
||||
- Add ``volume transfer request create`` and ``volume transfer request delete``
|
||||
commands in volume v1 and v2.
|
||||
[Blueprint `cinder-command-support <https://blueprints.launchpad.net/python-openstackclient/+spec/cinder-command-support>`_]
|
||||
|
@ -484,6 +484,7 @@ openstack.volume.v1 =
|
||||
volume_service_set = openstackclient.volume.v1.service:SetService
|
||||
|
||||
volume_transfer_request_create = openstackclient.volume.v1.volume_transfer_request:CreateTransferRequest
|
||||
volume_transfer_request_delete = openstackclient.volume.v1.volume_transfer_request:DeleteTransferRequest
|
||||
volume_transfer_request_list = openstackclient.volume.v1.volume_transfer_request:ListTransferRequests
|
||||
|
||||
openstack.volume.v2 =
|
||||
@ -533,6 +534,7 @@ openstack.volume.v2 =
|
||||
volume_service_set = openstackclient.volume.v2.service:SetService
|
||||
|
||||
volume_transfer_request_create = openstackclient.volume.v2.volume_transfer_request:CreateTransferRequest
|
||||
volume_transfer_request_delete = openstackclient.volume.v2.volume_transfer_request:DeleteTransferRequest
|
||||
volume_transfer_request_list = openstackclient.volume.v2.volume_transfer_request:ListTransferRequests
|
||||
|
||||
[build_sphinx]
|
||||
|
Loading…
x
Reference in New Issue
Block a user