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:
Huanxuan Ao 2016-08-08 12:09:19 +08:00
parent 6f4acc45c6
commit d2273ecea5
11 changed files with 450 additions and 1 deletions

View File

@ -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
----------------------------

View File

@ -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)

View File

@ -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)

View File

@ -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."""

View File

@ -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

View File

@ -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."""

View File

@ -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

View File

@ -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."""

View File

@ -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."""

View File

@ -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>`_]

View File

@ -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]