summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/compute/v2/keypair.py27
-rw-r--r--openstackclient/compute/v2/service.py18
-rw-r--r--openstackclient/network/v2/network.py2
-rw-r--r--openstackclient/network/v2/subnet.py8
-rw-r--r--openstackclient/tests/compute/v2/fakes.py19
-rw-r--r--openstackclient/tests/compute/v2/test_keypair.py62
-rw-r--r--openstackclient/tests/compute/v2/test_service.py55
-rw-r--r--openstackclient/tests/volume/v2/test_backup.py8
-rw-r--r--openstackclient/volume/v1/backup.py6
-rw-r--r--openstackclient/volume/v2/backup.py11
-rw-r--r--openstackclient/volume/v2/volume.py3
11 files changed, 188 insertions, 31 deletions
diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py
index 13c0a99c..3725a3a8 100644
--- a/openstackclient/compute/v2/keypair.py
+++ b/openstackclient/compute/v2/keypair.py
@@ -16,6 +16,7 @@
"""Keypair action implementations"""
import io
+import logging
import os
import sys
@@ -27,6 +28,9 @@ import six
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
+
class CreateKeypair(command.ShowOne):
"""Create new public key"""
@@ -78,20 +82,37 @@ class CreateKeypair(command.ShowOne):
class DeleteKeypair(command.Command):
- """Delete public key"""
+ """Delete public key(s)"""
def get_parser(self, prog_name):
parser = super(DeleteKeypair, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar='<key>',
- help=_("Public key to delete (name only)")
+ nargs='+',
+ help=_("Public key(s) to delete (name only)")
)
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- compute_client.keypairs.delete(parsed_args.name)
+ result = 0
+ for n in parsed_args.name:
+ try:
+ data = utils.find_resource(
+ compute_client.keypairs, n)
+ compute_client.keypairs.delete(data.name)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete public key with name "
+ "'%(name)s': %(e)s")
+ % {'name': n, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.name)
+ msg = (_("%(result)s of %(total)s public keys failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListKeypair(command.Lister):
diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py
index 1441114b..53624f55 100644
--- a/openstackclient/compute/v2/service.py
+++ b/openstackclient/compute/v2/service.py
@@ -36,14 +36,28 @@ class DeleteService(command.Command):
parser.add_argument(
"service",
metavar="<service>",
- help=_("Compute service to delete (ID only)")
+ nargs='+',
+ help=_("Compute service(s) to delete (ID only)")
)
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
+ result = 0
+ for s in parsed_args.service:
+ try:
+ compute_client.services.delete(s)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete compute service with "
+ "ID '%(service)s': %(e)s")
+ % {'service': s, 'e': e})
- compute_client.services.delete(parsed_args.service)
+ if result > 0:
+ total = len(parsed_args.service)
+ msg = (_("%(result)s of %(total)s compute services failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
class ListService(command.Lister):
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index 41735500..31dfc798 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -108,7 +108,7 @@ def _add_additional_network_options(parser):
'vlan', 'vxlan'],
help=_("The physical mechanism by which the virtual network "
"is implemented. The supported options are: "
- "flat, geneve, gre, local, vlan, vxlan"))
+ "flat, geneve, gre, local, vlan, vxlan."))
parser.add_argument(
'--provider-physical-network',
metavar='<provider-physical-network>',
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index 752923f7..b076d82e 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -235,7 +235,7 @@ class CreateSubnet(command.ShowOne):
"'auto': Gateway address should automatically be chosen "
"from within the subnet itself, 'none': This subnet will "
"not use a gateway, e.g.: --gateway 192.168.9.1, "
- "--gateway auto, --gateway none (default is 'auto')")
+ "--gateway auto, --gateway none (default is 'auto').")
)
parser.add_argument(
'--ip-version',
@@ -244,7 +244,7 @@ class CreateSubnet(command.ShowOne):
choices=[4, 6],
help=_("IP version (default is 4). Note that when subnet pool is "
"specified, IP version is determined from the subnet pool "
- "and this option is ignored")
+ "and this option is ignored.")
)
parser.add_argument(
'--ipv6-ra-mode',
@@ -334,7 +334,7 @@ class ListSubnet(command.Lister):
choices=[4, 6],
metavar='<ip-version>',
dest='ip_version',
- help=_("List only subnets of given IP version in output"
+ help=_("List only subnets of given IP version in output."
"Allowed values for IP version are 4 and 6."),
)
return parser
@@ -395,7 +395,7 @@ class SetSubnet(command.Command):
help=_("Specify a gateway for the subnet. The options are: "
"<ip-address>: Specific IP address to use as the gateway, "
"'none': This subnet will not use a gateway, "
- "e.g.: --gateway 192.168.9.1, --gateway none")
+ "e.g.: --gateway 192.168.9.1, --gateway none.")
)
_get_common_parse_arguments(parser)
return parser
diff --git a/openstackclient/tests/compute/v2/fakes.py b/openstackclient/tests/compute/v2/fakes.py
index b7f17fbc..882d8480 100644
--- a/openstackclient/tests/compute/v2/fakes.py
+++ b/openstackclient/tests/compute/v2/fakes.py
@@ -826,6 +826,25 @@ class FakeKeypair(object):
return keypairs
+ @staticmethod
+ def get_keypairs(keypairs=None, count=2):
+ """Get an iterable MagicMock object with a list of faked keypairs.
+
+ If keypairs list is provided, then initialize the Mock object with the
+ list. Otherwise create one.
+
+ :param List keypairs:
+ A list of FakeResource objects faking keypairs
+ :param int count:
+ The number of keypairs to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ keypairs
+ """
+ if keypairs is None:
+ keypairs = FakeKeypair.create_keypairs(count)
+ return mock.MagicMock(side_effect=keypairs)
+
class FakeAvailabilityZone(object):
"""Fake one or more compute availability zones (AZs)."""
diff --git a/openstackclient/tests/compute/v2/test_keypair.py b/openstackclient/tests/compute/v2/test_keypair.py
index a50a5323..25949e31 100644
--- a/openstackclient/tests/compute/v2/test_keypair.py
+++ b/openstackclient/tests/compute/v2/test_keypair.py
@@ -14,6 +14,10 @@
#
import mock
+from mock import call
+
+from osc_lib import exceptions
+from osc_lib import utils
from openstackclient.compute.v2 import keypair
from openstackclient.tests.compute.v2 import fakes as compute_fakes
@@ -114,22 +118,23 @@ class TestKeypairCreate(TestKeypair):
class TestKeypairDelete(TestKeypair):
- keypair = compute_fakes.FakeKeypair.create_one_keypair()
+ keypairs = compute_fakes.FakeKeypair.create_keypairs(count=2)
def setUp(self):
super(TestKeypairDelete, self).setUp()
- self.keypairs_mock.get.return_value = self.keypair
+ self.keypairs_mock.get = compute_fakes.FakeKeypair.get_keypairs(
+ self.keypairs)
self.keypairs_mock.delete.return_value = None
self.cmd = keypair.DeleteKeypair(self.app, None)
def test_keypair_delete(self):
arglist = [
- self.keypair.name
+ self.keypairs[0].name
]
verifylist = [
- ('name', self.keypair.name),
+ ('name', [self.keypairs[0].name]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -137,7 +142,54 @@ class TestKeypairDelete(TestKeypair):
ret = self.cmd.take_action(parsed_args)
self.assertIsNone(ret)
- self.keypairs_mock.delete.assert_called_with(self.keypair.name)
+ self.keypairs_mock.delete.assert_called_with(self.keypairs[0].name)
+
+ def test_delete_multiple_keypairs(self):
+ arglist = []
+ for k in self.keypairs:
+ arglist.append(k.name)
+ verifylist = [
+ ('name', arglist),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for k in self.keypairs:
+ calls.append(call(k.name))
+ self.keypairs_mock.delete.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_delete_multiple_keypairs_with_exception(self):
+ arglist = [
+ self.keypairs[0].name,
+ 'unexist_keypair',
+ ]
+ verifylist = [
+ ('name', arglist),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [self.keypairs[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 public keys failed to delete.',
+ str(e))
+
+ find_mock.assert_any_call(
+ self.keypairs_mock, self.keypairs[0].name)
+ find_mock.assert_any_call(self.keypairs_mock, 'unexist_keypair')
+
+ self.assertEqual(2, find_mock.call_count)
+ self.keypairs_mock.delete.assert_called_once_with(
+ self.keypairs[0].name
+ )
class TestKeypairList(TestKeypair):
diff --git a/openstackclient/tests/compute/v2/test_service.py b/openstackclient/tests/compute/v2/test_service.py
index e41d633a..1599f466 100644
--- a/openstackclient/tests/compute/v2/test_service.py
+++ b/openstackclient/tests/compute/v2/test_service.py
@@ -14,6 +14,7 @@
#
import mock
+from mock import call
from osc_lib import exceptions
@@ -33,32 +34,74 @@ class TestService(compute_fakes.TestComputev2):
class TestServiceDelete(TestService):
+ services = compute_fakes.FakeService.create_services(count=2)
+
def setUp(self):
super(TestServiceDelete, self).setUp()
- self.service = compute_fakes.FakeService.create_one_service()
-
self.service_mock.delete.return_value = None
# Get the command object to test
self.cmd = service.DeleteService(self.app, None)
- def test_service_delete_no_options(self):
+ def test_service_delete(self):
arglist = [
- self.service.binary,
+ self.services[0].binary,
]
verifylist = [
- ('service', self.service.binary),
+ ('service', [self.services[0].binary]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.service_mock.delete.assert_called_with(
- self.service.binary,
+ self.services[0].binary,
)
self.assertIsNone(result)
+ def test_multi_services_delete(self):
+ arglist = []
+ for s in self.services:
+ arglist.append(s.binary)
+ verifylist = [
+ ('service', arglist),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for s in self.services:
+ calls.append(call(s.binary))
+ self.service_mock.delete.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_multi_services_delete_with_exception(self):
+ arglist = [
+ self.services[0].binary,
+ 'unexist_service',
+ ]
+ verifylist = [
+ ('service', arglist)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ delete_mock_result = [None, exceptions.CommandError]
+ self.service_mock.delete = (
+ mock.MagicMock(side_effect=delete_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual(
+ '1 of 2 compute services failed to delete.', str(e))
+
+ self.service_mock.delete.assert_any_call(self.services[0].binary)
+ self.service_mock.delete.assert_any_call('unexist_service')
+
class TestServiceList(TestService):
diff --git a/openstackclient/tests/volume/v2/test_backup.py b/openstackclient/tests/volume/v2/test_backup.py
index 8a151a91..ba0f1c18 100644
--- a/openstackclient/tests/volume/v2/test_backup.py
+++ b/openstackclient/tests/volume/v2/test_backup.py
@@ -72,12 +72,14 @@ class TestBackupCreate(TestBackup):
"--name", self.new_backup.name,
"--description", self.new_backup.description,
"--container", self.new_backup.container,
+ "--force",
self.new_backup.volume_id,
]
verifylist = [
("name", self.new_backup.name),
("description", self.new_backup.description),
("container", self.new_backup.container),
+ ("force", True),
("volume", self.new_backup.volume_id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -88,7 +90,8 @@ class TestBackupCreate(TestBackup):
self.new_backup.volume_id,
container=self.new_backup.container,
name=self.new_backup.name,
- description=self.new_backup.description
+ description=self.new_backup.description,
+ force=True,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -112,7 +115,8 @@ class TestBackupCreate(TestBackup):
self.new_backup.volume_id,
container=self.new_backup.container,
name=None,
- description=self.new_backup.description
+ description=self.new_backup.description,
+ force=False,
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
diff --git a/openstackclient/volume/v1/backup.py b/openstackclient/volume/v1/backup.py
index 212c74ef..5f34a2c5 100644
--- a/openstackclient/volume/v1/backup.py
+++ b/openstackclient/volume/v1/backup.py
@@ -76,7 +76,7 @@ class DeleteBackup(command.Command):
'backups',
metavar='<backup>',
nargs="+",
- help=_('Backup(s) to delete (ID only)'),
+ help=_('Backup(s) to delete (name or ID)'),
)
return parser
@@ -150,7 +150,7 @@ class RestoreBackup(command.Command):
parser.add_argument(
'backup',
metavar='<backup>',
- help=_('Backup to restore (ID only)')
+ help=_('Backup to restore (name or ID)')
)
parser.add_argument(
'volume',
@@ -177,7 +177,7 @@ class ShowBackup(command.ShowOne):
parser.add_argument(
'backup',
metavar='<backup>',
- help=_('Backup to display (ID only)')
+ help=_('Backup to display (name or ID)')
)
return parser
diff --git a/openstackclient/volume/v2/backup.py b/openstackclient/volume/v2/backup.py
index 6a44e30c..519913a9 100644
--- a/openstackclient/volume/v2/backup.py
+++ b/openstackclient/volume/v2/backup.py
@@ -48,6 +48,12 @@ class CreateBackup(command.ShowOne):
metavar="<container>",
help=_("Optional backup container name")
)
+ parser.add_argument(
+ '--force',
+ action='store_true',
+ default=False,
+ help=_("Allow to back up an in-use volume")
+ )
return parser
def take_action(self, parsed_args):
@@ -58,7 +64,8 @@ class CreateBackup(command.ShowOne):
volume_id,
container=parsed_args.container,
name=parsed_args.name,
- description=parsed_args.description
+ description=parsed_args.description,
+ force=parsed_args.force,
)
backup._info.pop("links", None)
return zip(*sorted(six.iteritems(backup._info)))
@@ -147,7 +154,7 @@ class RestoreBackup(command.ShowOne):
parser.add_argument(
"backup",
metavar="<backup>",
- help=_("Backup to restore (ID only)")
+ help=_("Backup to restore (name or ID)")
)
parser.add_argument(
"volume",
diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py
index e54395fa..be2388fb 100644
--- a/openstackclient/volume/v2/volume.py
+++ b/openstackclient/volume/v2/volume.py
@@ -457,6 +457,3 @@ class UnsetVolume(command.Command):
if parsed_args.image_property:
volume_client.volumes.delete_image_metadata(
volume.id, parsed_args.image_property)
-
- if (not parsed_args.image_property and not parsed_args.property):
- LOG.error(_("No changes requested"))