From 7eccd403c711026b8cd596326baa8337114b6c9c Mon Sep 17 00:00:00 2001 From: Tobias Urdin Date: Sun, 19 May 2024 22:17:06 +0200 Subject: [PATCH 001/245] Show Created At column for volume backups The ``volume backup list`` command does not show the created_at column for backups which doesn't really help the end-user since a critical part of a backup is knowing when it was taken, and fast if there is a rush doing recovery by restoring a volume from a backup. Change-Id: I8d8a7f36c468c9faa2c2c47bfa9ba9e1ca5b9858 --- .../notes/volume-backup-created-at-list-b49ec893ae1f6b0d.yaml | 4 ++++ openstackclient/tests/unit/volume/v2/test_volume_backup.py | 3 +++ openstackclient/volume/v2/volume_backup.py | 2 ++ 3 files changed, 9 insertions(+) create mode 100644 openstackclient/releasenotes/notes/volume-backup-created-at-list-b49ec893ae1f6b0d.yaml diff --git a/openstackclient/releasenotes/notes/volume-backup-created-at-list-b49ec893ae1f6b0d.yaml b/openstackclient/releasenotes/notes/volume-backup-created-at-list-b49ec893ae1f6b0d.yaml new file mode 100644 index 000000000..974d88ed9 --- /dev/null +++ b/openstackclient/releasenotes/notes/volume-backup-created-at-list-b49ec893ae1f6b0d.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Listing volume backups now shows the created_at column. diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index aa0c050cd..94f07a861 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -359,6 +359,7 @@ class TestBackupList(TestBackup): 'Status', 'Size', 'Incremental', + 'Created At', ) columns_long = columns + ( 'Availability Zone', @@ -376,6 +377,7 @@ class TestBackupList(TestBackup): b.status, b.size, b.is_incremental, + b.created_at, ) ) data_long = [] @@ -388,6 +390,7 @@ class TestBackupList(TestBackup): b.status, b.size, b.is_incremental, + b.created_at, b.availability_zone, volume_backup.VolumeIdColumn(b.volume_id), b.container, diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index 7eed05a1d..4e58e54b2 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -305,6 +305,7 @@ def take_action(self, parsed_args): 'status', 'size', 'is_incremental', + 'created_at', ) column_headers = ( 'ID', @@ -313,6 +314,7 @@ def take_action(self, parsed_args): 'Status', 'Size', 'Incremental', + 'Created At', ) if parsed_args.long: columns += ('availability_zone', 'volume_id', 'container') From 49c42c73a29f6381e6dbf94fb1a0f96329c6a1f4 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Tue, 14 May 2024 17:39:56 +0530 Subject: [PATCH 002/245] Add DeleteVolumeSnapshot class to v3 This is done to support the snapshot unmanage command. Change-Id: I174ad08c7c03fe7a9206d40645a6916f104212c6 --- .../unit/volume/v3/test_volume_snapshot.py | 113 ++++++++++++++++++ openstackclient/volume/v3/volume_snapshot.py | 76 ++++++++++++ setup.cfg | 2 +- 3 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 openstackclient/tests/unit/volume/v3/test_volume_snapshot.py create mode 100644 openstackclient/volume/v3/volume_snapshot.py diff --git a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py new file mode 100644 index 000000000..3d5bc528e --- /dev/null +++ b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py @@ -0,0 +1,113 @@ +# +# 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. + +from unittest import mock + +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes +from openstackclient.volume.v3 import volume_snapshot + + +class TestVolumeSnapshot(volume_fakes.TestVolume): + def setUp(self): + super().setUp() + + self.snapshots_mock = self.volume_client.volume_snapshots + self.snapshots_mock.reset_mock() + + +class TestVolumeSnapshotDelete(TestVolumeSnapshot): + snapshots = volume_fakes.create_snapshots(count=2) + + def setUp(self): + super().setUp() + + self.snapshots_mock.get = volume_fakes.get_snapshots(self.snapshots) + self.snapshots_mock.delete.return_value = None + + # Get the command object to mock + self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None) + + def test_snapshot_delete(self): + arglist = [self.snapshots[0].id] + verifylist = [("snapshots", [self.snapshots[0].id])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.snapshots_mock.delete.assert_called_with( + self.snapshots[0].id, False + ) + self.assertIsNone(result) + + def test_snapshot_delete_with_force(self): + arglist = ['--force', self.snapshots[0].id] + verifylist = [('force', True), ("snapshots", [self.snapshots[0].id])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.snapshots_mock.delete.assert_called_with( + self.snapshots[0].id, True + ) + self.assertIsNone(result) + + def test_delete_multiple_snapshots(self): + arglist = [] + for s in self.snapshots: + arglist.append(s.id) + verifylist = [ + ('snapshots', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for s in self.snapshots: + calls.append(mock.call(s.id, False)) + self.snapshots_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_delete_multiple_snapshots_with_exception(self): + arglist = [ + self.snapshots[0].id, + 'unexist_snapshot', + ] + verifylist = [ + ('snapshots', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.snapshots[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 snapshots failed to delete.', str(e)) + + find_mock.assert_any_call( + self.snapshots_mock, self.snapshots[0].id + ) + find_mock.assert_any_call(self.snapshots_mock, 'unexist_snapshot') + + self.assertEqual(2, find_mock.call_count) + self.snapshots_mock.delete.assert_called_once_with( + self.snapshots[0].id, False + ) diff --git a/openstackclient/volume/v3/volume_snapshot.py b/openstackclient/volume/v3/volume_snapshot.py new file mode 100644 index 000000000..7b60e0f0d --- /dev/null +++ b/openstackclient/volume/v3/volume_snapshot.py @@ -0,0 +1,76 @@ +# +# 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. +# + +"""Volume v3 snapshot action implementations""" + +import logging + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.i18n import _ + +LOG = logging.getLogger(__name__) + + +class DeleteVolumeSnapshot(command.Command): + _description = _("Delete volume snapshot(s)") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "snapshots", + metavar="", + nargs="+", + help=_("Snapshot(s) to delete (name or ID)"), + ) + parser.add_argument( + '--force', + action='store_true', + help=_( + "Attempt forced removal of snapshot(s), " + "regardless of state (defaults to False)" + ), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + result = 0 + + for i in parsed_args.snapshots: + try: + snapshot_id = utils.find_resource( + volume_client.volume_snapshots, i + ).id + volume_client.volume_snapshots.delete( + snapshot_id, parsed_args.force + ) + except Exception as e: + result += 1 + LOG.error( + _( + "Failed to delete snapshot with " + "name or ID '%(snapshot)s': %(e)s" + ) + % {'snapshot': i, 'e': e} + ) + + if result > 0: + total = len(parsed_args.snapshots) + msg = _( + "%(result)s of %(total)s snapshots failed " "to delete." + ) % {'result': result, 'total': total} + raise exceptions.CommandError(msg) diff --git a/setup.cfg b/setup.cfg index 093bfe7a1..0991a1eb3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -825,7 +825,7 @@ openstack.volume.v3 = block_storage_resource_filter_show = openstackclient.volume.v3.block_storage_resource_filter:ShowBlockStorageResourceFilter volume_snapshot_create = openstackclient.volume.v2.volume_snapshot:CreateVolumeSnapshot - volume_snapshot_delete = openstackclient.volume.v2.volume_snapshot:DeleteVolumeSnapshot + volume_snapshot_delete = openstackclient.volume.v3.volume_snapshot:DeleteVolumeSnapshot volume_snapshot_list = openstackclient.volume.v2.volume_snapshot:ListVolumeSnapshot volume_snapshot_set = openstackclient.volume.v2.volume_snapshot:SetVolumeSnapshot volume_snapshot_show = openstackclient.volume.v2.volume_snapshot:ShowVolumeSnapshot From 4e94c415ed57c0d6366a7e49259e79fd6505ed6a Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Tue, 12 Sep 2023 16:08:35 +0530 Subject: [PATCH 003/245] Add volume snapshot unmanage support This patch adds support for unmanaging a snapshot with ``openstack volume snapshot delete --remote`` command. Change-Id: I3caf3471a007fcb988835d495727bbc5c66f42f8 --- .../unit/volume/v3/test_volume_snapshot.py | 50 ++++++++++++++++++- openstackclient/volume/v3/volume_snapshot.py | 27 ++++++++-- ...hot-unmanage-command-d4c0c8fd8b638d48.yaml | 5 ++ 3 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/add-snapshot-unmanage-command-d4c0c8fd8b638d48.yaml diff --git a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py index 3d5bc528e..e07679fa8 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py @@ -17,16 +17,19 @@ from osc_lib import utils from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes +from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes_v3 from openstackclient.volume.v3 import volume_snapshot -class TestVolumeSnapshot(volume_fakes.TestVolume): +class TestVolumeSnapshot(volume_fakes_v3.TestVolume): def setUp(self): super().setUp() self.snapshots_mock = self.volume_client.volume_snapshots self.snapshots_mock.reset_mock() + self.volume_sdk_client.unmanage_snapshot.return_value = None + class TestVolumeSnapshotDelete(TestVolumeSnapshot): snapshots = volume_fakes.create_snapshots(count=2) @@ -111,3 +114,48 @@ def test_delete_multiple_snapshots_with_exception(self): self.snapshots_mock.delete.assert_called_once_with( self.snapshots[0].id, False ) + + def test_snapshot_delete_remote(self): + arglist = ['--remote', self.snapshots[0].id] + verifylist = [('remote', True), ("snapshots", [self.snapshots[0].id])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.unmanage_snapshot.assert_called_with( + self.snapshots[0].id + ) + self.assertIsNone(result) + + def test_snapshot_delete_with_remote_force(self): + arglist = ['--remote', '--force', self.snapshots[0].id] + verifylist = [ + ('remote', True), + ('force', True), + ("snapshots", [self.snapshots[0].id]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + "The --force option is not supported with the --remote " + "parameter.", + str(exc), + ) + + def test_delete_multiple_snapshots_remote(self): + arglist = ['--remote'] + for s in self.snapshots: + arglist.append(s.id) + verifylist = [('remote', True), ('snapshots', arglist[1:])] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for s in self.snapshots: + calls.append(mock.call(s.id)) + self.volume_sdk_client.unmanage_snapshot.assert_has_calls(calls) + self.assertIsNone(result) diff --git a/openstackclient/volume/v3/volume_snapshot.py b/openstackclient/volume/v3/volume_snapshot.py index 7b60e0f0d..1e528e860 100644 --- a/openstackclient/volume/v3/volume_snapshot.py +++ b/openstackclient/volume/v3/volume_snapshot.py @@ -44,20 +44,41 @@ def get_parser(self, prog_name): "regardless of state (defaults to False)" ), ) + parser.add_argument( + '--remote', + action='store_true', + help=_( + 'Unmanage the snapshot, removing it from the Block Storage ' + 'service management but not from the backend.' + ), + ) return parser def take_action(self, parsed_args): volume_client = self.app.client_manager.volume + volume_client_sdk = self.app.client_manager.sdk_connection.volume + result = 0 + if parsed_args.remote: + if parsed_args.force: + msg = _( + "The --force option is not supported with the " + "--remote parameter." + ) + raise exceptions.CommandError(msg) + for i in parsed_args.snapshots: try: snapshot_id = utils.find_resource( volume_client.volume_snapshots, i ).id - volume_client.volume_snapshots.delete( - snapshot_id, parsed_args.force - ) + if parsed_args.remote: + volume_client_sdk.unmanage_snapshot(snapshot_id) + else: + volume_client.volume_snapshots.delete( + snapshot_id, parsed_args.force + ) except Exception as e: result += 1 LOG.error( diff --git a/releasenotes/notes/add-snapshot-unmanage-command-d4c0c8fd8b638d48.yaml b/releasenotes/notes/add-snapshot-unmanage-command-d4c0c8fd8b638d48.yaml new file mode 100644 index 000000000..a0abd5e58 --- /dev/null +++ b/releasenotes/notes/add-snapshot-unmanage-command-d4c0c8fd8b638d48.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added support for unmanaging snapshots with the + ``openstack snapshot delete --remote`` command. From 75696f8f3f3524f7e804b82e63280387215a23ec Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Sat, 22 Jun 2024 19:20:08 -0400 Subject: [PATCH 004/245] Add tests for 'default security group rule create' A previous change fixed a bug with the custom SG rule flag, but did not add any tests to verify all the combinations of default and custom SG flags. Let's add them now. Change-Id: Id74552346cfa975e3dd5eb45b8f621b365e88463 Related-bug: #2054629 --- .../v2/test_default_security_group_rule.py | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py b/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py index f1e9e28cc..9c9241a29 100644 --- a/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py +++ b/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py @@ -223,7 +223,9 @@ def test_create_default_rule(self): self.assertEqual(self.expected_columns, columns) self.assertEqual(self.expected_data, data) - def test_create_protocol_any(self): + def _test_create_protocol_any_helper( + self, for_default_sg=False, for_custom_sg=False + ): self._setup_default_security_group_rule( { 'protocol': None, @@ -236,9 +238,15 @@ def test_create_protocol_any(self): '--remote-ip', self._default_sg_rule.remote_ip_prefix, ] + if for_default_sg: + arglist.append('--for-default-sg') + if for_custom_sg: + arglist.append('--for-custom-sg') verifylist = [ ('protocol', 'any'), ('remote_ip', self._default_sg_rule.remote_ip_prefix), + ('for_default_sg', for_default_sg), + ('for_custom_sg', for_custom_sg), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -250,13 +258,27 @@ def test_create_protocol_any(self): 'ethertype': self._default_sg_rule.ether_type, 'protocol': self._default_sg_rule.protocol, 'remote_ip_prefix': self._default_sg_rule.remote_ip_prefix, - 'used_in_default_sg': False, - 'used_in_non_default_sg': False, + 'used_in_default_sg': for_default_sg, + 'used_in_non_default_sg': for_custom_sg, } ) self.assertEqual(self.expected_columns, columns) self.assertEqual(self.expected_data, data) + def test_create_protocol_any_not_for_default_sg(self): + self._test_create_protocol_any_helper() + + def test_create_protocol_any_for_default_sg(self): + self._test_create_protocol_any_helper(for_default_sg=True) + + def test_create_protocol_any_for_custom_sg(self): + self._test_create_protocol_any_helper(for_custom_sg=True) + + def test_create_protocol_any_for_default_and_custom_sg(self): + self._test_create_protocol_any_helper( + for_default_sg=True, for_custom_sg=True + ) + def test_create_remote_address_group(self): self._setup_default_security_group_rule( { From f52e888dabaa75c7dde0faf7e121474ee3699f31 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Fri, 28 Jun 2024 20:30:56 +0530 Subject: [PATCH 005/245] Followup: Reduce LOC in volume v3 service This is a followup of[1] in which we are reducing LOC by removing redundant definition of columns. [1] https://review.opendev.org/c/openstack/python-openstackclient/+/922865 Change-Id: I2992d3ab678e751726906926b680e4aa9ad1d502 --- openstackclient/volume/v3/service.py | 29 ++++++++++------------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/openstackclient/volume/v3/service.py b/openstackclient/volume/v3/service.py index 900aeab2d..5f01b5456 100644 --- a/openstackclient/volume/v3/service.py +++ b/openstackclient/volume/v3/service.py @@ -25,30 +25,21 @@ class ListService(service_v2.ListService): def take_action(self, parsed_args): service_client = self.app.client_manager.volume - if parsed_args.long: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - "Disabled Reason", - ] - else: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - ] + columns = [ + "Binary", + "Host", + "Zone", + "Status", + "State", + "Updated At", + ] if service_client.api_version >= api_versions.APIVersion('3.7'): columns.append("Cluster") if service_client.api_version >= api_versions.APIVersion('3.49'): columns.append("Backend State") + if parsed_args.long: + columns.append("Disabled Reason") data = service_client.services.list( parsed_args.host, parsed_args.service From ece30e8f703f918e391e934ecd2b201f211bfbfe Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 18 Jul 2024 11:48:43 +0100 Subject: [PATCH 006/245] compute: Allow adding, removing multiple SGs We also ensure we call neutron rather than the deprecated nova proxy API in the event that neutron is available. Change-Id: I8315ea164fd3fa6c1d759f16677bfd6c24c4ef63 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 124 ++++++++++++++---- .../tests/functional/common/test_help.py | 2 +- .../tests/unit/compute/v2/test_server.py | 14 +- ...iple-security-groups-2c0b2d599124c9c9.yaml | 5 + 4 files changed, 108 insertions(+), 37 deletions(-) create mode 100644 releasenotes/notes/add-remove-multiple-security-groups-2c0b2d599124c9c9.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 78ab16d72..694a1f728 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -670,7 +670,7 @@ def take_action(self, parsed_args): class AddServerSecurityGroup(command.Command): - _description = _("Add security group to server") + _description = _("Add security group(s) to server") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -680,9 +680,13 @@ def get_parser(self, prog_name): help=_('Server (name or ID)'), ) parser.add_argument( - 'group', - metavar='', - help=_('Security group to add (name or ID)'), + 'security_groups', + metavar='', + nargs='+', + help=_( + 'Security group(s) to add to the server (name or ID) ' + '(repeat option to add multiple groups)' + ), ) return parser @@ -694,14 +698,43 @@ def take_action(self, parsed_args): ) if self.app.client_manager.is_network_endpoint_enabled(): # the server handles both names and IDs for neutron SGs, so just - # pass things through - security_group = parsed_args.group + # pass things through if using neutron + security_groups = parsed_args.security_groups else: - # however, if using nova-network then it needs a name, not an ID - security_group = compute_v2.find_security_group( - compute_client, parsed_args.group - )['name'] - compute_client.add_security_group_to_server(server, security_group) + # however, if using nova-network then it needs names, not IDs + security_groups = [] + for security_group in parsed_args.security_groups: + security_groups.append( + compute_v2.find_security_group( + compute_client, security_group + )['name'] + ) + + errors = 0 + for security_group in security_groups: + try: + compute_client.add_security_group_to_server( + server, security_group + ) + except sdk_exceptions.HttpException as e: + errors += 1 + LOG.error( + _( + "Failed to add security group with name or ID " + "'%(security_group)s' to server '%(server)s': %(e)s" + ), + { + 'security_group': security_group, + 'server': server.id, + 'e': e, + }, + ) + + if errors > 0: + msg = _( + "%(errors)d of %(total)d security groups were not added." + ) % {'errors': errors, 'total': len(security_groups)} + raise exceptions.CommandError(msg) class AddServerVolume(command.ShowOne): @@ -1328,6 +1361,7 @@ def get_parser(self, prog_name): metavar='', action='append', default=[], + dest='security_groups', help=_( 'Security group to assign to this server (name or ID) ' '(repeat option to set multiple groups)' @@ -1948,21 +1982,22 @@ def _match_image(image_api, wanted_properties): # 'auto' to maintain legacy behavior if a nic wasn't specified. networks = 'auto' - # Check security group exist and convert ID to name + # Check security group(s) exist and convert ID to name security_groups = [] if self.app.client_manager.is_network_endpoint_enabled(): network_client = self.app.client_manager.network - for each_sg in parsed_args.security_group: + for security_group in parsed_args.security_groups: sg = network_client.find_security_group( - each_sg, ignore_missing=False + security_group, ignore_missing=False ) # Use security group ID to avoid multiple security group have # same name in neutron networking backend security_groups.append({'name': sg.id}) - else: - # Handle nova-network case - for each_sg in parsed_args.security_group: - sg = compute_v2.find_security_group(compute_client, each_sg) + else: # nova-network + for security_group in parsed_args.security_groups: + sg = compute_v2.find_security_group( + compute_client, security_group + ) security_groups.append({'name': sg['name']}) hints = {} @@ -4016,9 +4051,13 @@ def get_parser(self, prog_name): help=_('Server (name or ID)'), ) parser.add_argument( - 'group', - metavar='', - help=_('Security group to remove (name or ID)'), + 'security_groups', + metavar='', + nargs='+', + help=_( + 'Security group(s) to remove from server (name or ID) ' + '(repeat option to remove multiple groups)' + ), ) return parser @@ -4031,15 +4070,42 @@ def take_action(self, parsed_args): if self.app.client_manager.is_network_endpoint_enabled(): # the server handles both names and IDs for neutron SGs, so just # pass things through - security_group = parsed_args.group + security_groups = parsed_args.security_groups else: - # however, if using nova-network then it needs a name, not an ID - security_group = compute_v2.find_security_group( - compute_client, parsed_args.group - )['name'] - compute_client.remove_security_group_from_server( - server, security_group - ) + # however, if using nova-network then it needs names, not IDs + security_groups = [] + for security_group in parsed_args.security_groups: + security_groups.append( + compute_v2.find_security_group( + compute_client, security_group + )['name'] + ) + + errors = 0 + for security_group in security_groups: + try: + compute_client.remove_security_group_from_server( + server, security_group + ) + except sdk_exceptions.HttpException as e: + errors += 1 + LOG.error( + _( + "Failed to remove security group with name or ID " + "'%(security_group)s' from server '%(server)s': %(e)s" + ), + { + 'security_group': security_group, + 'server': server.id, + 'e': e, + }, + ) + + if errors > 0: + msg = _( + "%(errors)d of %(total)d security groups were not removed." + ) % {'errors': errors, 'total': len(security_groups)} + raise exceptions.CommandError(msg) class RemoveServerVolume(command.Command): diff --git a/openstackclient/tests/functional/common/test_help.py b/openstackclient/tests/functional/common/test_help.py index f1aea8ee9..8f31c25eb 100644 --- a/openstackclient/tests/functional/common/test_help.py +++ b/openstackclient/tests/functional/common/test_help.py @@ -21,7 +21,7 @@ class HelpTests(base.TestCase): """Functional tests for openstackclient help output.""" SERVER_COMMANDS = [ - ('server add security group', 'Add security group to server'), + ('server add security group', 'Add security group(s) to server'), ('server add volume', 'Add volume to server'), ('server backup create', 'Create a server backup image'), ('server create', 'Create a new server'), diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index c96e0fada..8d62aacd3 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -1166,7 +1166,7 @@ def test_server_add_security_group__nova_network(self): arglist = [self.server.id, 'fake_sg'] verifylist = [ ('server', self.server.id), - ('group', 'fake_sg'), + ('security_groups', ['fake_sg']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1197,7 +1197,7 @@ def test_server_add_security_group(self): arglist = [self.server.id, 'fake_sg'] verifylist = [ ('server', self.server.id), - ('group', 'fake_sg'), + ('security_groups', ['fake_sg']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1429,7 +1429,7 @@ def test_server_create_with_options(self): ('flavor', self.flavor.id), ('key_name', 'keyname'), ('properties', {'Beta': 'b'}), - ('security_group', [security_group.id]), + ('security_groups', [security_group.id]), ('hints', {'a': ['b', 'c']}), ('server_group', server_group.id), ('config_drive', True), @@ -1499,7 +1499,7 @@ def test_server_create_with_not_exist_security_group(self): ('image', self.image.id), ('flavor', self.flavor.id), ('key_name', 'keyname'), - ('security_group', ['not_exist_sg']), + ('security_groups', ['not_exist_sg']), ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1525,7 +1525,7 @@ def test_server_create_with_security_group_in_nova_network(self): verifylist = [ ('image', self.image.id), ('flavor', self.flavor.id), - ('security_group', [sg_name]), + ('security_groups', [sg_name]), ('server_name', self.server.name), ] @@ -7416,7 +7416,7 @@ def test_server_remove_security_group__nova_network(self): arglist = [self.server.id, 'fake_sg'] verifylist = [ ('server', self.server.id), - ('group', 'fake_sg'), + ('security_groups', ['fake_sg']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -7447,7 +7447,7 @@ def test_server_remove_security_group(self): arglist = [self.server.id, 'fake_sg'] verifylist = [ ('server', self.server.id), - ('group', 'fake_sg'), + ('security_groups', ['fake_sg']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/releasenotes/notes/add-remove-multiple-security-groups-2c0b2d599124c9c9.yaml b/releasenotes/notes/add-remove-multiple-security-groups-2c0b2d599124c9c9.yaml new file mode 100644 index 000000000..1938f7121 --- /dev/null +++ b/releasenotes/notes/add-remove-multiple-security-groups-2c0b2d599124c9c9.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + The ``server add security group`` and ``server remove security group`` + commands now accept multiple security groups. From 0d89f01448ee2d77d82a9e86e18a0fd909ceb9fe Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 10 May 2024 14:26:39 +0100 Subject: [PATCH 007/245] Remove TestServer base class Simplify this somewhat. Change-Id: I4ece72344e0e671c2f190fa9200e91d72333e96d Signed-off-by: Stephen Finucane --- .../tests/unit/compute/v2/test_server.py | 220 +++++++++--------- 1 file changed, 115 insertions(+), 105 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index a116ff3c3..aa85e3d9a 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -78,30 +78,6 @@ def setup_sdk_servers_mock(self, count): return servers - def setup_sdk_volumes_mock(self, count): - volumes = volume_fakes.create_sdk_volumes(count=count) - - # This is the return value for volume_client.find_volume() - self.volume_sdk_client.find_volume.side_effect = volumes - - return volumes - - def run_method_with_sdk_servers(self, method_name, server_count): - servers = self.setup_sdk_servers_mock(count=server_count) - - arglist = [s.id for s in servers] - verifylist = [ - ('server', arglist), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - calls = [mock.call(s.id) for s in servers] - method = getattr(self.compute_sdk_client, method_name) - method.assert_has_calls(calls) - self.assertIsNone(result) - class TestServerAddFixedIP(TestServer): def setUp(self): @@ -730,12 +706,15 @@ class TestServerVolume(TestServer): def setUp(self): super().setUp() - self.servers = self.setup_sdk_servers_mock(count=1) - self.volumes = self.setup_sdk_volumes_mock(count=1) + self.server = compute_fakes.create_one_sdk_server() + self.compute_sdk_client.find_server.return_value = self.server + + self.volume = volume_fakes.create_one_sdk_volume() + self.volume_sdk_client.find_volume.return_value = self.volume attrs = { - 'server_id': self.servers[0].id, - 'volume_id': self.volumes[0].id, + 'server_id': self.server.id, + 'volume_id': self.volume.id, } self.volume_attachment = compute_fakes.create_one_volume_attachment( attrs=attrs @@ -758,12 +737,12 @@ def test_server_add_volume(self): arglist = [ '--device', '/dev/sdb', - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('device', '/dev/sdb'), ] @@ -782,7 +761,7 @@ def test_server_add_volume(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) self.compute_sdk_client.create_volume_attachment.assert_called_once_with( - self.servers[0], volumeId=self.volumes[0].id, device='/dev/sdb' + self.server, volumeId=self.volume.id, device='/dev/sdb' ) def test_server_add_volume_with_tag(self): @@ -793,12 +772,12 @@ def test_server_add_volume_with_tag(self): '/dev/sdb', '--tag', 'foo', - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('device', '/dev/sdb'), ('tag', 'foo'), ] @@ -819,8 +798,8 @@ def test_server_add_volume_with_tag(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) self.compute_sdk_client.create_volume_attachment.assert_called_once_with( - self.servers[0], - volumeId=self.volumes[0].id, + self.server, + volumeId=self.volume.id, device='/dev/sdb', tag='foo', ) @@ -829,14 +808,14 @@ def test_server_add_volume_with_tag_pre_v249(self): self.set_compute_api_version('2.48') arglist = [ - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, '--tag', 'foo', ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('tag', 'foo'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -856,13 +835,13 @@ def test_server_add_volume_with_enable_delete_on_termination(self): '--enable-delete-on-termination', '--device', '/dev/sdb', - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('device', '/dev/sdb'), ('enable_delete_on_termination', True), ] @@ -889,8 +868,8 @@ def test_server_add_volume_with_enable_delete_on_termination(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) self.compute_sdk_client.create_volume_attachment.assert_called_once_with( - self.servers[0], - volumeId=self.volumes[0].id, + self.server, + volumeId=self.volume.id, device='/dev/sdb', delete_on_termination=True, ) @@ -904,13 +883,13 @@ def test_server_add_volume_with_disable_delete_on_termination(self): '--disable-delete-on-termination', '--device', '/dev/sdb', - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('device', '/dev/sdb'), ('disable_delete_on_termination', True), ] @@ -938,8 +917,8 @@ def test_server_add_volume_with_disable_delete_on_termination(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) self.compute_sdk_client.create_volume_attachment.assert_called_once_with( - self.servers[0], - volumeId=self.volumes[0].id, + self.server, + volumeId=self.volume.id, device='/dev/sdb', delete_on_termination=False, ) @@ -950,13 +929,13 @@ def test_server_add_volume_with_enable_delete_on_termination_pre_v279( self.set_compute_api_version('2.78') arglist = [ - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, '--enable-delete-on-termination', ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('enable_delete_on_termination', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -974,13 +953,13 @@ def test_server_add_volume_with_disable_delete_on_termination_pre_v279( self.set_compute_api_version('2.78') arglist = [ - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, '--disable-delete-on-termination', ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('disable_delete_on_termination', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1002,13 +981,13 @@ def test_server_add_volume_with_disable_and_enable_delete_on_termination( '--disable-delete-on-termination', '--device', '/dev/sdb', - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ('device', '/dev/sdb'), ('enable_delete_on_termination', True), ('disable_delete_on_termination', True), @@ -1036,13 +1015,13 @@ def setUp(self): def test_server_remove_volume(self): arglist = [ - self.servers[0].id, - self.volumes[0].id, + self.server.id, + self.volume.id, ] verifylist = [ - ('server', self.servers[0].id), - ('volume', self.volumes[0].id), + ('server', self.server.id), + ('volume', self.volume.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1051,8 +1030,8 @@ def test_server_remove_volume(self): self.assertIsNone(result) self.compute_sdk_client.delete_volume_attachment.assert_called_once_with( - self.volumes[0], - self.servers[0], + self.volume, + self.server, ignore_missing=False, ) @@ -5476,14 +5455,28 @@ def test_server_list_v269_with_partial_constructs(self): self.assertEqual(expected_row, partial_server) -class TestServerLock(TestServer): - def setUp(self): - super().setUp() +class TestServerAction(compute_fakes.TestComputev2): + def run_method_with_sdk_servers(self, method_name, server_count): + servers = compute_fakes.create_sdk_servers(count=server_count) + self.compute_sdk_client.find_server.side_effect = servers - self.server = compute_fakes.create_one_sdk_server() + arglist = [s.id for s in servers] + verifylist = [ + ('server', arglist), + ] - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.lock_server.return_value = None + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [mock.call(s.id) for s in servers] + method = getattr(self.compute_sdk_client, method_name) + method.assert_has_calls(calls) + self.assertIsNone(result) + + +class TestServerLock(TestServerAction): + def setUp(self): + super().setUp() # Get the command object to test self.cmd = server.LockServer(self.app, None) @@ -5497,6 +5490,10 @@ def test_server_lock_multi_servers(self): def test_server_lock_with_reason(self): self.set_compute_api_version('2.73') + self.server = compute_fakes.create_one_sdk_server() + self.compute_sdk_client.find_server.return_value = self.server + self.compute_sdk_client.lock_server.return_value = None + arglist = [ self.server.id, '--reason', @@ -5506,8 +5503,10 @@ def test_server_lock_with_reason(self): ('server', [self.server.id]), ('reason', 'blah'), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) + self.compute_sdk_client.find_server.assert_called_with( self.server.id, ignore_missing=False, @@ -5520,30 +5519,38 @@ def test_server_lock_with_reason(self): def test_server_lock_with_reason_multi_servers(self): self.set_compute_api_version('2.73') - server2 = compute_fakes.create_one_sdk_server() + server_a = compute_fakes.create_one_sdk_server() + server_b = compute_fakes.create_one_sdk_server() + + self.compute_sdk_client.find_server.side_effect = [server_a, server_b] + self.compute_sdk_client.lock_server.return_value = None arglist = [ - self.server.id, - server2.id, + server_a.id, + server_b.id, '--reason', 'choo..choo', ] verifylist = [ - ('server', [self.server.id, server2.id]), + ('server', [server_a.id, server_b.id]), ('reason', 'choo..choo'), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) + self.assertEqual(2, self.compute_sdk_client.find_server.call_count) - self.compute_sdk_client.lock_server.assert_called_with( - self.server.id, - locked_reason="choo..choo", + self.compute_sdk_client.lock_server.assert_has_calls( + [ + mock.call(server_a.id, locked_reason="choo..choo"), + mock.call(server_b.id, locked_reason="choo..choo"), + ] ) - self.assertEqual(2, self.compute_sdk_client.lock_server.call_count) def test_server_lock_with_reason_pre_v273(self): self.set_compute_api_version('2.72') server = compute_fakes.create_one_sdk_server() + arglist = [ server.id, '--reason', @@ -5553,6 +5560,7 @@ def test_server_lock_with_reason_pre_v273(self): ('server', [server.id]), ('reason', "blah"), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) ex = self.assertRaises( exceptions.CommandError, @@ -6089,7 +6097,7 @@ def test_server_reboot_with_wait_fails( ) -class TestServerPause(TestServer): +class TestServerPause(TestServerAction): def setUp(self): super().setUp() @@ -7869,7 +7877,7 @@ def test_migration_revert(self): self.assertIsNone(result) -class TestServerRestore(TestServer): +class TestServerRestore(TestServerAction): def setUp(self): super().setUp() @@ -7883,7 +7891,7 @@ def test_server_restore_multi_servers(self): self.run_method_with_sdk_servers('restore_server', 3) -class TestServerResume(TestServer): +class TestServerResume(TestServerAction): def setUp(self): super().setUp() @@ -8752,7 +8760,7 @@ def test_server_ssh_deprecated_opts(self, mock_exec): ) -class TestServerStart(TestServer): +class TestServerStart(TestServerAction): def setUp(self): super().setUp() @@ -8766,28 +8774,29 @@ def test_server_start_multi_servers(self): self.run_method_with_sdk_servers('start_server', 3) def test_server_start_with_all_projects(self): - servers = self.setup_sdk_servers_mock(count=1) + server = compute_fakes.create_one_sdk_server() + self.compute_sdk_client.find_server.return_value = server arglist = [ - servers[0].id, + server.id, '--all-projects', ] verifylist = [ - ('server', [servers[0].id]), + ('server', [server.id]), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.compute_sdk_client.find_server.assert_called_once_with( - servers[0].id, + server.id, ignore_missing=False, details=False, all_projects=True, ) -class TestServerStop(TestServer): +class TestServerStop(TestServerAction): def setUp(self): super().setUp() @@ -8801,28 +8810,29 @@ def test_server_stop_multi_servers(self): self.run_method_with_sdk_servers('stop_server', 3) def test_server_start_with_all_projects(self): - servers = self.setup_sdk_servers_mock(count=1) + server = compute_fakes.create_one_sdk_server() + self.compute_sdk_client.find_server.return_value = server arglist = [ - servers[0].id, + server.id, '--all-projects', ] verifylist = [ - ('server', [servers[0].id]), + ('server', [server.id]), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.compute_sdk_client.find_server.assert_called_once_with( - servers[0].id, + server.id, ignore_missing=False, details=False, all_projects=True, ) -class TestServerSuspend(TestServer): +class TestServerSuspend(TestServerAction): def setUp(self): super().setUp() @@ -8836,7 +8846,7 @@ def test_server_suspend_multi_servers(self): self.run_method_with_sdk_servers('suspend_server', 3) -class TestServerUnlock(TestServer): +class TestServerUnlock(TestServerAction): def setUp(self): super().setUp() @@ -8850,7 +8860,7 @@ def test_server_unlock_multi_servers(self): self.run_method_with_sdk_servers('unlock_server', 3) -class TestServerUnpause(TestServer): +class TestServerUnpause(TestServerAction): def setUp(self): super().setUp() From f68000d77226534ff61300fbca89f605bf5fac5a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 18 Jul 2024 13:39:00 +0100 Subject: [PATCH 008/245] tests: Remove aliasing from extensions test This isn't necessary and makes migration of the compute client a little more difficult. Change-Id: Ib07e60171c43838935ea30f4916bf68d54cdc944 Signed-off-by: Stephen Finucane --- .../tests/unit/common/test_extension.py | 55 ++++++++----------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/openstackclient/tests/unit/common/test_extension.py b/openstackclient/tests/unit/common/test_extension.py index 765903b3b..b22365899 100644 --- a/openstackclient/tests/unit/common/test_extension.py +++ b/openstackclient/tests/unit/common/test_extension.py @@ -14,7 +14,6 @@ from openstackclient.common import extension from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes from openstackclient.tests.unit.network.v2 import fakes as network_fakes from openstackclient.tests.unit import utils @@ -22,23 +21,13 @@ from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes -class TestExtension(network_fakes.FakeClientMixin, utils.TestCommand): - def setUp(self): - super().setUp() - - identity_client = identity_fakes.FakeIdentityv2Client( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, - ) - self.app.client_manager.identity = identity_client - self.identity_extensions_mock = identity_client.extensions - self.identity_extensions_mock.reset_mock() - - sdk_connection = self.app.client_manager.sdk_connection - self.compute_extensions_mock = sdk_connection.compute.extensions - self.compute_extensions_mock.reset_mock() - self.volume_extensions_mock = sdk_connection.volume.extensions - self.volume_extensions_mock.reset_mock() +class TestExtension( + network_fakes.FakeClientMixin, + compute_fakes.FakeClientMixin, + volume_fakes.FakeClientMixin, + identity_fakes.FakeClientMixin, + utils.TestCommand, +): ... class TestExtensionList(TestExtension): @@ -60,11 +49,15 @@ class TestExtensionList(TestExtension): def setUp(self): super().setUp() - self.identity_extensions_mock.list.return_value = [ + self.identity_client.extensions.list.return_value = [ self.identity_extension ] - self.compute_extensions_mock.return_value = [self.compute_extension] - self.volume_extensions_mock.return_value = [self.volume_extension] + self.compute_sdk_client.extensions.return_value = [ + self.compute_extension + ] + self.volume_sdk_client.extensions.return_value = [ + self.volume_extension + ] self.network_client.extensions.return_value = [self.network_extension] # Get the command object to test @@ -112,9 +105,9 @@ def test_extension_list_no_options(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist) - self.identity_extensions_mock.list.assert_called_with() - self.compute_extensions_mock.assert_called_with() - self.volume_extensions_mock.assert_called_with() + self.identity_client.extensions.list.assert_called_with() + self.compute_sdk_client.extensions.assert_called_with() + self.volume_sdk_client.extensions.assert_called_with() self.network_client.extensions.assert_called_with() def test_extension_list_long(self): @@ -159,9 +152,9 @@ def test_extension_list_long(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist, True) - self.identity_extensions_mock.list.assert_called_with() - self.compute_extensions_mock.assert_called_with() - self.volume_extensions_mock.assert_called_with() + self.identity_client.extensions.list.assert_called_with() + self.compute_sdk_client.extensions.assert_called_with() + self.volume_sdk_client.extensions.assert_called_with() self.network_client.extensions.assert_called_with() def test_extension_list_identity(self): @@ -179,7 +172,7 @@ def test_extension_list_identity(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist) - self.identity_extensions_mock.list.assert_called_with() + self.identity_client.extensions.list.assert_called_with() def test_extension_list_network(self): arglist = [ @@ -237,7 +230,7 @@ def test_extension_list_compute(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist) - self.compute_extensions_mock.assert_called_with() + self.compute_sdk_client.extensions.assert_called_with() def test_extension_list_compute_and_network(self): arglist = [ @@ -261,7 +254,7 @@ def test_extension_list_compute_and_network(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist) - self.compute_extensions_mock.assert_called_with() + self.compute_sdk_client.extensions.assert_called_with() self.network_client.extensions.assert_called_with() def test_extension_list_volume(self): @@ -279,7 +272,7 @@ def test_extension_list_volume(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist) - self.volume_extensions_mock.assert_called_with() + self.volume_sdk_client.extensions.assert_called_with() class TestExtensionShow(TestExtension): From f885a474507a97a3075fe0ebd50ebe3a78cf5497 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 15 Jul 2024 10:28:25 +0100 Subject: [PATCH 009/245] Add Python 3.12 classifier We are testing this in CI and can support it going forward now. The osc-tox-py310-tips job is removed in a favour of a new osc-tox-py312-tips job. Change-Id: I87a726cb65223cba9975d0505c13fe42dcd7fa14 Signed-off-by: Stephen Finucane --- .zuul.yaml | 8 ++++---- setup.cfg | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 4915f9f85..9c0f0b22c 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -38,8 +38,8 @@ zuul_work_dir: src/opendev.org/openstack/python-openstackclient - job: - name: osc-tox-py310-tips - parent: openstack-tox-py310 + name: osc-tox-py312-tips + parent: openstack-tox-py312 description: | Run unit tests for OpenStackClient with master branch of important libs. @@ -202,11 +202,11 @@ check: jobs: - osc-tox-py38-tips - - osc-tox-py310-tips + - osc-tox-py312-tips gate: jobs: - osc-tox-py38-tips - - osc-tox-py310-tips + - osc-tox-py312-tips - project: templates: diff --git a/setup.cfg b/setup.cfg index d824afb15..1308c0698 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,6 +19,7 @@ classifier = Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 [files] packages = From 7a92bc4abc4587c270fd86fdc44f231fdab7ab96 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 8 Jul 2024 17:15:51 +0100 Subject: [PATCH 010/245] Drop support for Python 3.8 This goes EOL in October. We don't want to be supporting it past then. Drop it now. Change-Id: I6166678a35092760718d4fcd0357e7195bbe8be0 Signed-off-by: Stephen Finucane --- .zuul.yaml | 8 ++++---- releasenotes/notes/drop-python-38-9dcbd2b2b51f24f2.yaml | 4 ++++ setup.cfg | 3 +-- 3 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/drop-python-38-9dcbd2b2b51f24f2.yaml diff --git a/.zuul.yaml b/.zuul.yaml index 9c0f0b22c..ac4d63a04 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -18,8 +18,8 @@ zuul_work_dir: src/opendev.org/openstack/python-openstackclient - job: - name: osc-tox-py38-tips - parent: openstack-tox-py38 + name: osc-tox-py39-tips + parent: openstack-tox-py39 description: | Run unit tests for OpenStackClient with master branch of important libs. @@ -201,11 +201,11 @@ name: osc-tox-unit-tips check: jobs: - - osc-tox-py38-tips + - osc-tox-py39-tips - osc-tox-py312-tips gate: jobs: - - osc-tox-py38-tips + - osc-tox-py39-tips - osc-tox-py312-tips - project: diff --git a/releasenotes/notes/drop-python-38-9dcbd2b2b51f24f2.yaml b/releasenotes/notes/drop-python-38-9dcbd2b2b51f24f2.yaml new file mode 100644 index 000000000..6d61b9e7a --- /dev/null +++ b/releasenotes/notes/drop-python-38-9dcbd2b2b51f24f2.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + Support for Python 3.8 has been dropped. diff --git a/setup.cfg b/setup.cfg index 1308c0698..0266a4359 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,7 +6,7 @@ description_file = author = OpenStack author_email = openstack-discuss@lists.openstack.org home_page = https://docs.openstack.org/python-openstackclient/latest/ -python_requires = >=3.8 +python_requires = >=3.9 classifier = Environment :: OpenStack Intended Audience :: Information Technology @@ -15,7 +15,6 @@ classifier = Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 3 - Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 From aa88db9448c7f840ff619de989ff6f4564f47aae Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 24 Jul 2024 16:20:21 +0100 Subject: [PATCH 011/245] Add callback on plugin load failure I have seen a few cases where import errors (distutils - I am looking at you) result in an extension not being available, but there is no indication why this is the case. We do configure logging, but this happens too late (as part of the 'cliff.app.App.run' call to execute a command, which calls osc-lib's 'configure_logging' but which happens long after we've tried to import our plugins) to be of any use. Instead, make use of a callback to make it more obvious. Change-Id: Id68b06161e445b79fe43f463e06cda3c4771ef02 Signed-off-by: Stephen Finucane --- openstackclient/common/clientmanager.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 8caf94a5d..6ace1f170 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -158,10 +158,22 @@ def is_volume_endpoint_enabled(self, volume_client): # Plugin Support +def _on_load_failure_callback( + manager: stevedore.ExtensionManager, + ep: importlib.metadata.EntryPoint, + err: Exception, +) -> None: + sys.stderr.write( + f"WARNING: Failed to import plugin {ep.group}:{ep.name}: {err}.\n" + ) + + def get_plugin_modules(group): """Find plugin entry points""" mod_list = [] - mgr = stevedore.ExtensionManager(group) + mgr = stevedore.ExtensionManager( + group, on_load_failure_callback=_on_load_failure_callback + ) for ep in mgr: LOG.debug('Found plugin %s', ep.name) @@ -180,9 +192,8 @@ def get_plugin_modules(group): module = importlib.import_module(module_name) except Exception as err: sys.stderr.write( - "WARNING: Failed to import plugin {}: {}.\n".format( - ep.name, err - ) + f"WARNING: Failed to import plugin {ep.group}:{ep.name}: " + f"{err}.\n" ) continue From 1d4aabab2e7b5d5de5ee313556c147a96f74d426 Mon Sep 17 00:00:00 2001 From: Tim Bishop Date: Mon, 22 Jul 2024 16:55:34 +0100 Subject: [PATCH 012/245] Respect --skip-resource when deleting. When running project cleanup and using the --skip-resource flag, this flag is only used during the --dry-run stage. This change ensures it is also used when deleting the resources which fixes the bug mentioned below. Closes-Bug: #2074109 Change-Id: I8e50fd14dcde069b932cc8ec4bb4fc809f7d2eb7 --- openstackclient/common/project_cleanup.py | 5 ++- .../tests/unit/common/test_project_cleanup.py | 35 ++++++++++++++++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/openstackclient/common/project_cleanup.py b/openstackclient/common/project_cleanup.py index 2ac156cba..0c143fad3 100644 --- a/openstackclient/common/project_cleanup.py +++ b/openstackclient/common/project_cleanup.py @@ -150,5 +150,8 @@ def take_action(self, parsed_args): self.log.warning(_('Deleting resources')) project_connect.project_cleanup( - dry_run=False, status_queue=status_queue, filters=filters + dry_run=False, + status_queue=status_queue, + filters=filters, + skip_resources=parsed_args.skip_resource, ) diff --git a/openstackclient/tests/unit/common/test_project_cleanup.py b/openstackclient/tests/unit/common/test_project_cleanup.py index 019d6cf15..50d4e6487 100644 --- a/openstackclient/tests/unit/common/test_project_cleanup.py +++ b/openstackclient/tests/unit/common/test_project_cleanup.py @@ -83,7 +83,12 @@ def test_project_cleanup_with_filters(self): filters=filters, skip_resources=None, ), - mock.call(dry_run=False, status_queue=mock.ANY, filters=filters), + mock.call( + dry_run=False, + status_queue=mock.ANY, + filters=filters, + skip_resources=None, + ), ] self.project_cleanup_mock.assert_has_calls(calls) @@ -114,7 +119,12 @@ def test_project_cleanup_with_auto_approve(self): filters={}, skip_resources=None, ), - mock.call(dry_run=False, status_queue=mock.ANY, filters={}), + mock.call( + dry_run=False, + status_queue=mock.ANY, + filters={}, + skip_resources=None, + ), ] self.project_cleanup_mock.assert_has_calls(calls) @@ -144,7 +154,12 @@ def test_project_cleanup_with_project(self): filters={}, skip_resources=None, ), - mock.call(dry_run=False, status_queue=mock.ANY, filters={}), + mock.call( + dry_run=False, + status_queue=mock.ANY, + filters={}, + skip_resources=None, + ), ] self.project_cleanup_mock.assert_has_calls(calls) @@ -230,7 +245,12 @@ def test_project_cleanup_with_auth_project(self): filters={}, skip_resources=None, ), - mock.call(dry_run=False, status_queue=mock.ANY, filters={}), + mock.call( + dry_run=False, + status_queue=mock.ANY, + filters={}, + skip_resources=None, + ), ] self.project_cleanup_mock.assert_has_calls(calls) @@ -260,7 +280,12 @@ def test_project_cleanup_with_skip_resource(self): filters={}, skip_resources=[skip_resource], ), - mock.call(dry_run=False, status_queue=mock.ANY, filters={}), + mock.call( + dry_run=False, + status_queue=mock.ANY, + filters={}, + skip_resources=[skip_resource], + ), ] self.project_cleanup_mock.assert_has_calls(calls) From 5238394e6a7a40ddf4505009d36076ef9076e0a2 Mon Sep 17 00:00:00 2001 From: Joel Capitao Date: Thu, 1 Aug 2024 09:35:22 +0200 Subject: [PATCH 013/245] Bump requests minimal version It's a follow-up of [1]. JSONDecodeError exception was implemented in requests since 2.27.0 [2]. [1] https://review.opendev.org/c/openstack/python-openstackclient/+/924927 [2] https://github.com/psf/requests/commit/db575eeedcfdb03bf31285afd3033e301df8b685 Change-Id: I8a6a8ac064cb0703097ed686b1fcd2983181fcfa --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3c494d981..c31de42a7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,5 +12,5 @@ osc-lib>=2.3.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 python-keystoneclient>=3.22.0 # Apache-2.0 python-cinderclient>=3.3.0 # Apache-2.0 -requests>=2.14.2 # Apache-2.0 +requests>=2.27.0 # Apache-2.0 stevedore>=2.0.1 # Apache-2.0 From 6693f555bcc412bc84226bb71d093e77a2ab240c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 7 Aug 2024 12:44:37 +0100 Subject: [PATCH 014/245] quota: Allow 'quota set' to function without volume service Unlike cinderclient, SDK attempts to connect to a service as soon as you create a client. A keystoneauth1.exceptions.catalog.EndpointNotFound exception can be raised if this service does not exist in the service catalog. Avoid this for the quota and limits commands by first checking if the service is enabled. In the process, we rework the 'is_volume_endpoint_enabled' helper we are using to check for the existence of the service to *not* require a volume client, since this was causing a chicken and egg issue for us (and was also pretty much unnecessary). Change-Id: I56e68f00ea221d689eb7f668e9e5ffa7d1a20184 Signed-off-by: Stephen Finucane Closes-bug: #2076229 --- openstackclient/common/clientmanager.py | 33 ++++------------ openstackclient/common/limits.py | 7 ++-- openstackclient/common/quota.py | 51 ++++++++++++++----------- openstackclient/tests/unit/fakes.py | 2 +- 4 files changed, 39 insertions(+), 54 deletions(-) diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 8caf94a5d..c117ae1f7 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -116,7 +116,6 @@ def _fallback_load_auth_plugin(self, e): def is_network_endpoint_enabled(self): """Check if the network endpoint is enabled""" - # NOTE(dtroyer): is_service_available() can also return None if # there is no Service Catalog, callers here are # not expecting that so fold None into True to @@ -125,34 +124,16 @@ def is_network_endpoint_enabled(self): def is_compute_endpoint_enabled(self): """Check if Compute endpoint is enabled""" - return self.is_service_available('compute') is not False - def is_volume_endpoint_enabled(self, volume_client): + # TODO(stephenfin): Drop volume_client argument in OSC 8.0 or later. + def is_volume_endpoint_enabled(self, volume_client=None): """Check if volume endpoint is enabled""" - # NOTE(jcross): Cinder did some interesting things with their service - # name so we need to figure out which version to look - # for when calling is_service_available() - endpoint_data = volume_client.get_endpoint_data() - # Not sure how endpoint data stores the api version for v2 API, - # for v3 it is a tuple (3, 0) - if endpoint_data.api_version and isinstance( - endpoint_data.api_version, tuple - ): - volume_version = endpoint_data.api_version[0] - else: - # Setting volume_version as 2 here if it doesn't satisfy the - # conditions for version 3 - volume_version = 2 - if ( - self.is_service_available("volumev%s" % volume_version) - is not False - ): - return True - elif self.is_service_available('volume') is not False: - return True - else: - return False + return ( + self.is_service_available('volume') is not False + or self.is_service_available('volumev3') is not False + or self.is_service_available('volumev2') is not False + ) # Plugin Support diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py index 043bf5321..cb2f331f6 100644 --- a/openstackclient/common/limits.py +++ b/openstackclient/common/limits.py @@ -101,9 +101,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute - volume_client = self.app.client_manager.sdk_connection.volume - project_id = None if parsed_args.project is not None: identity_client = self.app.client_manager.identity @@ -125,11 +122,13 @@ def take_action(self, parsed_args): volume_limits = None if self.app.client_manager.is_compute_endpoint_enabled(): + compute_client = self.app.client_manager.sdk_connection.compute compute_limits = compute_client.get_limits( reserved=parsed_args.is_reserved, tenant_id=project_id ) - if self.app.client_manager.is_volume_endpoint_enabled(volume_client): + if self.app.client_manager.is_volume_endpoint_enabled(): + volume_client = self.app.client_manager.sdk_connection.volume volume_limits = volume_client.get_limits( project_id=project_id, ) diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index c408e97ba..ec1f91a6c 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -565,33 +565,42 @@ def take_action(self, parsed_args): msg = _('--force cannot be used with --class or --default') raise exceptions.CommandError(msg) - compute_client = self.app.client_manager.sdk_connection.compute - volume_client = self.app.client_manager.sdk_connection.volume - compute_kwargs = {} - for k, v in COMPUTE_QUOTAS.items(): - value = getattr(parsed_args, k, None) - if value is not None: - compute_kwargs[k] = value + volume_kwargs = {} + network_kwargs = {} - if compute_kwargs and parsed_args.force is True: - compute_kwargs['force'] = parsed_args.force + if self.app.client_manager.is_compute_endpoint_enabled(): + compute_client = self.app.client_manager.sdk_connection.compute - volume_kwargs = {} - for k, v in VOLUME_QUOTAS.items(): - value = getattr(parsed_args, k, None) - if value is not None: - if parsed_args.volume_type and k in IMPACT_VOLUME_TYPE_QUOTAS: - k = k + '_%s' % parsed_args.volume_type - volume_kwargs[k] = value + for k, v in COMPUTE_QUOTAS.items(): + value = getattr(parsed_args, k, None) + if value is not None: + compute_kwargs[k] = value + + if compute_kwargs and parsed_args.force is True: + compute_kwargs['force'] = parsed_args.force + + if self.app.client_manager.is_volume_endpoint_enabled(): + volume_client = self.app.client_manager.sdk_connection.volume + + for k, v in VOLUME_QUOTAS.items(): + value = getattr(parsed_args, k, None) + if value is not None: + if ( + parsed_args.volume_type + and k in IMPACT_VOLUME_TYPE_QUOTAS + ): + k = k + '_%s' % parsed_args.volume_type + volume_kwargs[k] = value - network_kwargs = {} if self.app.client_manager.is_network_endpoint_enabled(): + network_client = self.app.client_manager.network + for k, v in NETWORK_QUOTAS.items(): value = getattr(parsed_args, k, None) if value is not None: network_kwargs[k] = value - else: + elif self.app.client_manager.is_compute_endpoint_enabled(): for k, v in NOVA_NETWORK_QUOTAS.items(): value = getattr(parsed_args, k, None) if value is not None: @@ -632,11 +641,7 @@ def take_action(self, parsed_args): compute_client.update_quota_set(project, **compute_kwargs) if volume_kwargs: volume_client.update_quota_set(project, **volume_kwargs) - if ( - network_kwargs - and self.app.client_manager.is_network_endpoint_enabled() - ): - network_client = self.app.client_manager.network + if network_kwargs: network_client.update_quota(project, **network_kwargs) diff --git a/openstackclient/tests/unit/fakes.py b/openstackclient/tests/unit/fakes.py index cc42b1750..0d378ff8d 100644 --- a/openstackclient/tests/unit/fakes.py +++ b/openstackclient/tests/unit/fakes.py @@ -154,7 +154,7 @@ def is_network_endpoint_enabled(self): def is_compute_endpoint_enabled(self): return self.compute_endpoint_enabled - def is_volume_endpoint_enabled(self, client): + def is_volume_endpoint_enabled(self, client=None): return self.volume_endpoint_enabled From b8459c57c89456812417fcc52944ec9b6dce4a04 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 7 Aug 2024 13:00:52 +0100 Subject: [PATCH 015/245] compute: Only pass admin_password on rebuild if set In SDK change I3bc3150877c6c00aa9ec4355104308d7755aa1d4, we modified the 'rebuild_server' proxy method to use a sentinel 'unset' value so that we could distinguish between fields that were not being changed and those that were being changed to 'null'. However, we are currently passing the 'admin_password' field to the 'rebuild_server' SDK proxy command regardless of whether it is set or not. Resolve this conflict. Change-Id: If7b7585aadd43cdc6d2a9358f14223e43dc21a73 Signed-off-by: Stephen Finucane Closes-bug: #2076232 --- openstackclient/compute/v2/server.py | 7 +- .../tests/unit/compute/v2/test_server.py | 75 +++++-------------- 2 files changed, 22 insertions(+), 60 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 78ab16d72..661453b65 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -3587,6 +3587,9 @@ def _show_progress(progress): if parsed_args.name is not None: kwargs['name'] = parsed_args.name + if parsed_args.password is not None: + kwargs['admin_password'] = parsed_args.password + if parsed_args.preserve_ephemeral is not None: kwargs['preserve_ephemeral'] = parsed_args.preserve_ephemeral @@ -3725,9 +3728,7 @@ def _show_progress(progress): msg = _("The server status is not ACTIVE, SHUTOFF or ERROR.") raise exceptions.CommandError(msg) - server = compute_client.rebuild_server( - server, image, admin_password=parsed_args.password, **kwargs - ) + server = compute_client.rebuild_server(server, image, **kwargs) if parsed_args.wait: if utils.wait_for_status( diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index a116ff3c3..434d2e61f 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -6146,7 +6146,7 @@ def test_rebuild_with_image_name(self): ) self.image_client.get_image.assert_called_with(self.image.id) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, image, admin_password=None + self.server, image ) def test_rebuild_with_current_image(self): @@ -6167,7 +6167,7 @@ def test_rebuild_with_current_image(self): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, self.image, admin_password=None + self.server, self.image ) def test_rebuild_with_volume_backed_server_no_image(self): @@ -6211,7 +6211,7 @@ def test_rebuild_with_name(self): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, self.image, admin_password=None, name=name + self.server, self.image, name=name ) def test_rebuild_with_preserve_ephemeral(self): @@ -6235,10 +6235,7 @@ def test_rebuild_with_preserve_ephemeral(self): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - preserve_ephemeral=True, + self.server, self.image, preserve_ephemeral=True ) def test_rebuild_with_no_preserve_ephemeral(self): @@ -6263,10 +6260,7 @@ def test_rebuild_with_no_preserve_ephemeral(self): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - preserve_ephemeral=False, + self.server, self.image, preserve_ephemeral=False ) def test_rebuild_with_password(self): @@ -6308,10 +6302,7 @@ def test_rebuild_with_description(self): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - description=description, + self.server, self.image, description=description ) def test_rebuild_with_description_pre_v219(self): @@ -6348,9 +6339,7 @@ def test_rebuild_with_wait_ok(self, mock_wait_for_status): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, + self.server, self.image ) mock_wait_for_status.assert_called_once_with( @@ -6382,9 +6371,7 @@ def test_rebuild_with_wait_fails(self, mock_wait_for_status): self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_called_once_with(self.image.id) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, + self.server, self.image ) mock_wait_for_status.assert_called_once_with( @@ -6417,9 +6404,7 @@ def test_rebuild_with_wait_shutoff_status(self, mock_wait_for_status): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, + self.server, self.image ) mock_wait_for_status.assert_called_once_with( @@ -6452,9 +6437,7 @@ def test_rebuild_with_wait_error_status(self, mock_wait_for_status): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, + self.server, self.image ) mock_wait_for_status.assert_called_once_with( @@ -6510,10 +6493,7 @@ def test_rebuild_with_property(self): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - metadata=expected_properties, + self.server, self.image, metadata=expected_properties ) def test_rebuild_with_keypair_name(self): @@ -6541,10 +6521,7 @@ def test_rebuild_with_keypair_name(self): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - key_name=self.server.key_name, + self.server, self.image, key_name=self.server.key_name ) def test_rebuild_with_keypair_name_pre_v254(self): @@ -6589,10 +6566,7 @@ def test_rebuild_with_no_keypair_name(self): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - key_name=None, + self.server, self.image, key_name=None ) def test_rebuild_with_keypair_name_and_unset(self): @@ -6649,7 +6623,6 @@ def test_rebuild_with_user_data(self): self.compute_sdk_client.rebuild_server.assert_called_once_with( self.server, self.image, - admin_password=None, user_data=base64.b64encode(user_data).decode('utf-8'), ) @@ -6695,10 +6668,7 @@ def test_rebuild_with_no_user_data(self): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - user_data=None, + self.server, self.image, user_data=None ) def test_rebuild_with_no_user_data_pre_v254(self): @@ -6759,10 +6729,7 @@ def test_rebuild_with_trusted_image_cert(self): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - trusted_image_certificates=['foo', 'bar'], + self.server, self.image, trusted_image_certificates=['foo', 'bar'] ) def test_rebuild_with_trusted_image_cert_pre_v263(self): @@ -6808,10 +6775,7 @@ def test_rebuild_with_no_trusted_image_cert(self): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - trusted_image_certificates=None, + self.server, self.image, trusted_image_certificates=None ) def test_rebuild_with_no_trusted_image_cert_pre_v263(self): @@ -6855,10 +6819,7 @@ def test_rebuild_with_hostname(self): [mock.call(self.image.id), mock.call(self.image.id)] ) self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, - self.image, - admin_password=None, - hostname='new-hostname', + self.server, self.image, hostname='new-hostname' ) def test_rebuild_with_hostname_pre_v290(self): @@ -6920,7 +6881,7 @@ def test_rebuild_with_reimage_boot_volume(self): ) self.image_client.get_image.assert_not_called() self.compute_sdk_client.rebuild_server.assert_called_once_with( - self.server, self.new_image, admin_password=None + self.server, self.new_image ) def test_rebuild_with_no_reimage_boot_volume(self): From 04ebe0853d1f59b8a18fcb8c8afffbc23dd0efc4 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 7 Aug 2024 13:17:05 +0100 Subject: [PATCH 016/245] compute: Add 'uuid' column to aggregate list Change-Id: I15d4a2d5980c1ba3e00f7d1bd09f11d0f42564e1 Signed-off-by: Stephen Finucane Closes-bug: #2073542 --- openstackclient/compute/v2/aggregate.py | 52 ++++---- .../tests/unit/compute/v2/test_aggregate.py | 112 +++++++++++------- ...ate-list-uuid-column-808a0d051006a5ef.yaml | 5 + 3 files changed, 104 insertions(+), 65 deletions(-) create mode 100644 releasenotes/notes/aggregate-list-uuid-column-808a0d051006a5ef.yaml diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index 967eec02c..4cbce3c81 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -192,40 +192,46 @@ def take_action(self, parsed_args): aggregates = list(compute_client.aggregates()) + if sdk_utils.supports_microversion(compute_client, '2.41'): + column_headers = ("ID", "UUID") + columns = ("id", "uuid") + else: + column_headers = ("ID",) + columns = ("id",) + + column_headers += ( + "Name", + "Availability Zone", + ) + columns += ( + "name", + "availability_zone", + ) + if parsed_args.long: # Remove availability_zone from metadata because Nova doesn't for aggregate in aggregates: if 'availability_zone' in aggregate.metadata: aggregate.metadata.pop('availability_zone') - # This is the easiest way to change column headers - column_headers = ( - "ID", - "Name", - "Availability Zone", + + column_headers += ( "Properties", "Hosts", ) - columns = ( - "ID", - "Name", - "Availability Zone", - "Metadata", - "Hosts", - ) - else: - column_headers = columns = ( - "ID", - "Name", - "Availability Zone", + columns += ( + "metadata", + "hosts", ) - data = ( - utils.get_item_properties( - s, columns, formatters=_aggregate_formatters - ) - for s in aggregates + return ( + column_headers, + ( + utils.get_item_properties( + s, columns, formatters=_aggregate_formatters + ) + for s in aggregates + ), ) - return (column_headers, data) class RemoveAggregateHost(command.ShowOne): diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index 6cfe5bc79..933344e8c 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -227,44 +227,6 @@ def test_delete_multiple_agggregates_with_exception(self): class TestAggregateList(TestAggregate): - list_columns = ( - "ID", - "Name", - "Availability Zone", - ) - - list_columns_long = ( - "ID", - "Name", - "Availability Zone", - "Properties", - "Hosts", - ) - - list_data = ( - ( - TestAggregate.fake_ag.id, - TestAggregate.fake_ag.name, - TestAggregate.fake_ag.availability_zone, - ), - ) - - list_data_long = ( - ( - TestAggregate.fake_ag.id, - TestAggregate.fake_ag.name, - TestAggregate.fake_ag.availability_zone, - format_columns.DictColumn( - { - key: value - for key, value in TestAggregate.fake_ag.metadata.items() - if key != 'availability_zone' - } - ), - format_columns.ListColumn(TestAggregate.fake_ag.hosts), - ), - ) - def setUp(self): super().setUp() @@ -272,13 +234,54 @@ def setUp(self): self.cmd = aggregate.ListAggregate(self.app, None) def test_aggregate_list(self): + self.set_compute_api_version('2.41') + + parsed_args = self.check_parser(self.cmd, [], []) + columns, data = self.cmd.take_action(parsed_args) + + expected_columns = ( + "ID", + "UUID", + "Name", + "Availability Zone", + ) + expected_data = ( + ( + self.fake_ag.id, + self.fake_ag.uuid, + self.fake_ag.name, + self.fake_ag.availability_zone, + ), + ) + + self.assertEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) + + def test_aggregate_list_pre_v241(self): + self.set_compute_api_version('2.40') + parsed_args = self.check_parser(self.cmd, [], []) columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.list_columns, columns) - self.assertCountEqual(self.list_data, tuple(data)) + expected_columns = ( + "ID", + "Name", + "Availability Zone", + ) + expected_data = ( + ( + self.fake_ag.id, + self.fake_ag.name, + self.fake_ag.availability_zone, + ), + ) + + self.assertEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) def test_aggregate_list_with_long(self): + self.set_compute_api_version('2.41') + arglist = [ '--long', ] @@ -288,8 +291,33 @@ def test_aggregate_list_with_long(self): parsed_args = self.check_parser(self.cmd, arglist, vertifylist) columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.list_columns_long, columns) - self.assertCountEqual(self.list_data_long, tuple(data)) + expected_columns = ( + "ID", + "UUID", + "Name", + "Availability Zone", + "Properties", + "Hosts", + ) + expected_data = ( + ( + self.fake_ag.id, + self.fake_ag.uuid, + self.fake_ag.name, + self.fake_ag.availability_zone, + format_columns.DictColumn( + { + key: value + for key, value in self.fake_ag.metadata.items() + if key != 'availability_zone' + } + ), + format_columns.ListColumn(self.fake_ag.hosts), + ), + ) + + self.assertEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) class TestAggregateRemoveHost(TestAggregate): diff --git a/releasenotes/notes/aggregate-list-uuid-column-808a0d051006a5ef.yaml b/releasenotes/notes/aggregate-list-uuid-column-808a0d051006a5ef.yaml new file mode 100644 index 000000000..49e9e557d --- /dev/null +++ b/releasenotes/notes/aggregate-list-uuid-column-808a0d051006a5ef.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + The ``aggregate list`` command will now include the UUIDs of the aggregates + when the cloud supports it. From 4a2fd82b07ada8b5aded1f9507e7b566366127d8 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 7 Aug 2024 13:38:52 +0100 Subject: [PATCH 017/245] compute: Make 'hypervisor show' a bit faster In the event that a user provides a hypervisor name rather than an ID to the 'hypervisor show' command, passing 'details=True' (the default) to 'find_hypervisor' will ensure we get the detailed response we need. However, this comes at the cost of retrieving reams of additional irrelevant data for all the other hypervisors. Rather than doing this, use a summary view and then a second call to fetch only the hypervisor we care about. Change-Id: I92b53802e41a962c6f916c3a111dc2de7c12d0fc Signed-off-by: Stephen Finucane Closes-bug: #2072965 --- openstackclient/compute/v2/hypervisor.py | 8 +++-- .../tests/unit/compute/v2/test_hypervisor.py | 32 +++++++++++++++++-- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py index fffeb3521..8a82a30d9 100644 --- a/openstackclient/compute/v2/hypervisor.py +++ b/openstackclient/compute/v2/hypervisor.py @@ -165,9 +165,11 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): compute_client = self.app.client_manager.sdk_connection.compute - hypervisor = compute_client.find_hypervisor( - parsed_args.hypervisor, ignore_missing=False - ).copy() + + hypervisor_id = compute_client.find_hypervisor( + parsed_args.hypervisor, ignore_missing=False, details=False + ).id + hypervisor = compute_client.get_hypervisor(hypervisor_id).copy() # Some of the properties in the hypervisor object need to be processed # before they get reported to the user. We spend this section diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor.py b/openstackclient/tests/unit/compute/v2/test_hypervisor.py index 2c226f004..2c83fe4c9 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor.py @@ -296,13 +296,11 @@ def setUp(self): } ) - # Return value of compute_client.find_hypervisor self.compute_sdk_client.find_hypervisor.return_value = self.hypervisor + self.compute_sdk_client.get_hypervisor.return_value = self.hypervisor - # Return value of compute_client.aggregates() self.compute_sdk_client.aggregates.return_value = [] - # Return value of compute_client.get_hypervisor_uptime() uptime_info = { 'status': self.hypervisor.status, 'state': self.hypervisor.state, @@ -429,6 +427,13 @@ def test_hypervisor_show(self): self.assertEqual(self.columns_v288, columns) self.assertCountEqual(self.data_v288, data) + self.compute_sdk_client.find_hypervisor.assert_called_once_with( + self.hypervisor.name, ignore_missing=False, details=False + ) + self.compute_sdk_client.get_hypervisor.assert_called_once_with( + self.hypervisor.id + ) + def test_hypervisor_show_pre_v288(self): self.set_compute_api_version('2.87') @@ -448,6 +453,13 @@ def test_hypervisor_show_pre_v288(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) + self.compute_sdk_client.find_hypervisor.assert_called_once_with( + self.hypervisor.name, ignore_missing=False, details=False + ) + self.compute_sdk_client.get_hypervisor.assert_called_once_with( + self.hypervisor.id + ) + def test_hypervisor_show_pre_v228(self): self.set_compute_api_version('2.27') @@ -472,6 +484,13 @@ def test_hypervisor_show_pre_v228(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) + self.compute_sdk_client.find_hypervisor.assert_called_once_with( + self.hypervisor.name, ignore_missing=False, details=False + ) + self.compute_sdk_client.get_hypervisor.assert_called_once_with( + self.hypervisor.id + ) + def test_hypervisor_show_uptime_not_implemented(self): self.set_compute_api_version('2.87') @@ -543,3 +562,10 @@ def test_hypervisor_show_uptime_not_implemented(self): self.assertEqual(expected_columns, columns) self.assertCountEqual(expected_data, data) + + self.compute_sdk_client.find_hypervisor.assert_called_once_with( + self.hypervisor.name, ignore_missing=False, details=False + ) + self.compute_sdk_client.get_hypervisor.assert_called_once_with( + self.hypervisor.id + ) From 0e731cd22fffbcaf77143bbc259027514e73597a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 7 Aug 2024 17:00:23 +0100 Subject: [PATCH 018/245] identity: Use previous naming for 'application credential show' fields We changed these in change Iba3fee2672d32266623c6f367beaabe84bd3d24e but the '-c/--column' option provided by cliff currently requires an explicit match on column names. Change them back for now. We can revert this when cliff is a little bit cleverer. Change-Id: I6b4f1b793dc383856bfdf9a01514381be3cd2bf1 Signed-off-by: Stephen Finucane Related-bug: #2076212 --- .../identity/v3/application_credential.py | 34 +++++++++---------- .../v3/test_application_credential.py | 14 ++++---- .../v3/test_application_credential.py | 34 +++++++++---------- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py index 035be2d21..7bc2b8a19 100644 --- a/openstackclient/identity/v3/application_credential.py +++ b/openstackclient/identity/v3/application_credential.py @@ -186,15 +186,15 @@ def take_action(self, parsed_args): application_credential['roles'] = msg columns = ( - 'ID', - 'Name', - 'Description', - 'Project ID', - 'Roles', - 'Unrestricted', - 'Access Rules', - 'Expires At', - 'Secret', + 'id', + 'name', + 'description', + 'project_id', + 'roles', + 'unrestricted', + 'access_rules', + 'expires_at', + 'secret', ) return ( columns, @@ -337,14 +337,14 @@ def take_action(self, parsed_args): app_cred['roles'] = msg columns = ( - 'ID', - 'Name', - 'Description', - 'Project ID', - 'Roles', - 'Unrestricted', - 'Access Rules', - 'Expires At', + 'id', + 'name', + 'description', + 'project_id', + 'roles', + 'unrestricted', + 'access_rules', + 'expires_at', ) return ( columns, diff --git a/openstackclient/tests/functional/identity/v3/test_application_credential.py b/openstackclient/tests/functional/identity/v3/test_application_credential.py index 9c8b0462f..22f2b90bb 100644 --- a/openstackclient/tests/functional/identity/v3/test_application_credential.py +++ b/openstackclient/tests/functional/identity/v3/test_application_credential.py @@ -21,13 +21,13 @@ class ApplicationCredentialTests(common.IdentityTests): APPLICATION_CREDENTIAL_FIELDS = [ - 'ID', - 'Name', - 'Project ID', - 'Description', - 'Roles', - 'Expires At', - 'Unrestricted', + 'id', + 'name', + 'project_id', + 'description', + 'roles', + 'expires_at', + 'unrestricted', ] APPLICATION_CREDENTIAL_LIST_HEADERS = [ 'ID', diff --git a/openstackclient/tests/unit/identity/v3/test_application_credential.py b/openstackclient/tests/unit/identity/v3/test_application_credential.py index 277204dd4..94631d95a 100644 --- a/openstackclient/tests/unit/identity/v3/test_application_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py @@ -31,15 +31,15 @@ class TestApplicationCredentialCreate(identity_fakes.TestIdentityv3): columns = ( - 'ID', - 'Name', - 'Description', - 'Project ID', - 'Roles', - 'Unrestricted', - 'Access Rules', - 'Expires At', - 'Secret', + 'id', + 'name', + 'description', + 'project_id', + 'roles', + 'unrestricted', + 'access_rules', + 'expires_at', + 'secret', ) def setUp(self): @@ -413,14 +413,14 @@ def test_application_credential_show(self): ) collist = ( - 'ID', - 'Name', - 'Description', - 'Project ID', - 'Roles', - 'Unrestricted', - 'Access Rules', - 'Expires At', + 'id', + 'name', + 'description', + 'project_id', + 'roles', + 'unrestricted', + 'access_rules', + 'expires_at', ) self.assertEqual(collist, columns) datalist = ( From 25780e80ab7ca3b0ea2c22e384f4ca792b3a61b2 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 7 Aug 2024 12:06:33 +0100 Subject: [PATCH 019/245] identity: Use previous naming for 'service show' fields We changed these in change I37d07a6c5cdc98680b8d65d596521cad2b049500 but the '-c/--column' option provided by cliff currently requires an explicit match on column names. Change them back for now. We can revert this when cliff is a little bit smarter. Change-Id: I9180922e9da5c22ae3d8878946d1bf1ec4b8c6e1 Signed-off-by: Stephen Finucane Closes-bug: #2076212 --- openstackclient/identity/v3/service.py | 10 +++++----- .../tests/functional/identity/v3/common.py | 4 ++-- .../tests/functional/identity/v3/test_limit.py | 4 ++-- .../functional/identity/v3/test_registered_limit.py | 2 +- .../tests/functional/identity/v3/test_service.py | 6 +++--- .../tests/unit/identity/v3/test_service.py | 12 ++++++------ 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index b7d4befc9..f04fa979d 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -37,11 +37,11 @@ def _format_service(service): 'description', ) column_headers = ( - 'ID', - 'Name', - 'Type', - 'Enabled', - 'Description', + 'id', + 'name', + 'type', + 'enabled', + 'description', ) return ( diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py index cc7adcc46..e3c32e35b 100644 --- a/openstackclient/tests/functional/identity/v3/common.py +++ b/openstackclient/tests/functional/identity/v3/common.py @@ -48,7 +48,7 @@ class IdentityTests(base.TestCase): 'parent_id', ] ROLE_FIELDS = ['id', 'name', 'domain_id', 'description'] - SERVICE_FIELDS = ['ID', 'Enabled', 'Name', 'Type', 'Description'] + SERVICE_FIELDS = ['id', 'enabled', 'name', 'type', 'description'] REGION_FIELDS = ['description', 'enabled', 'parent_region', 'region'] ENDPOINT_FIELDS = [ 'id', @@ -376,7 +376,7 @@ def _create_dummy_service(self, add_clean_up=True): if add_clean_up: service = self.parse_show_as_object(raw_output) self.addCleanup( - self.openstack, 'service delete %s' % service['ID'] + self.openstack, 'service delete %s' % service['id'] ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.SERVICE_FIELDS) diff --git a/openstackclient/tests/functional/identity/v3/test_limit.py b/openstackclient/tests/functional/identity/v3/test_limit.py index d096762d2..32c65107d 100644 --- a/openstackclient/tests/functional/identity/v3/test_limit.py +++ b/openstackclient/tests/functional/identity/v3/test_limit.py @@ -32,7 +32,7 @@ def test_limit_create_with_service_name(self): raw_output = self.openstack('service show %s' % service_id) items = self.parse_show(raw_output) - service_name = self._extract_value_from_items('Name', items) + service_name = self._extract_value_from_items('name', items) project_name = self._create_dummy_project() raw_output = self.openstack('project show %s' % project_name) @@ -73,7 +73,7 @@ def test_limit_create_with_project_name(self): raw_output = self.openstack('service show %s' % service_id) items = self.parse_show(raw_output) - service_name = self._extract_value_from_items('Name', items) + service_name = self._extract_value_from_items('name', items) project_name = self._create_dummy_project() diff --git a/openstackclient/tests/functional/identity/v3/test_registered_limit.py b/openstackclient/tests/functional/identity/v3/test_registered_limit.py index ba7f8cbd4..6e3c30aeb 100644 --- a/openstackclient/tests/functional/identity/v3/test_registered_limit.py +++ b/openstackclient/tests/functional/identity/v3/test_registered_limit.py @@ -29,7 +29,7 @@ def test_registered_limit_create_with_service_id(self): 'service show' ' %(service_name)s' % {'service_name': service_name} ) service_items = self.parse_show(raw_output) - service_id = self._extract_value_from_items('ID', service_items) + service_id = self._extract_value_from_items('id', service_items) raw_output = self.openstack( 'registered limit create' diff --git a/openstackclient/tests/functional/identity/v3/test_service.py b/openstackclient/tests/functional/identity/v3/test_service.py index 76009bf48..98fa349d1 100644 --- a/openstackclient/tests/functional/identity/v3/test_service.py +++ b/openstackclient/tests/functional/identity/v3/test_service.py @@ -61,9 +61,9 @@ def test_service_set(self): raw_output = self.openstack('service show %s' % new_service_name) # assert service details service = self.parse_show_as_object(raw_output) - self.assertEqual(new_service_type, service['Type']) - self.assertEqual(new_service_name, service['Name']) - self.assertEqual(new_service_description, service['Description']) + self.assertEqual(new_service_type, service['type']) + self.assertEqual(new_service_name, service['name']) + self.assertEqual(new_service_description, service['description']) def test_service_show(self): service_name = self._create_dummy_service() diff --git a/openstackclient/tests/unit/identity/v3/test_service.py b/openstackclient/tests/unit/identity/v3/test_service.py index d44712124..4c02d9891 100644 --- a/openstackclient/tests/unit/identity/v3/test_service.py +++ b/openstackclient/tests/unit/identity/v3/test_service.py @@ -24,11 +24,11 @@ class TestServiceCreate(identity_fakes.TestIdentityv3): columns = ( - 'ID', - 'Name', - 'Type', - 'Enabled', - 'Description', + 'id', + 'name', + 'type', + 'enabled', + 'description', ) def setUp(self): @@ -455,7 +455,7 @@ def test_service_show(self): self.service.name, ignore_missing=False ) - collist = ('ID', 'Name', 'Type', 'Enabled', 'Description') + collist = ('id', 'name', 'type', 'enabled', 'description') self.assertEqual(collist, columns) datalist = ( self.service.id, From 869b07ededc5b8bee5669ebd877c3fd3c543a464 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 29 Aug 2024 10:57:20 +0100 Subject: [PATCH 020/245] pre-commit: Bump versions We also drop the default language setting: everything is Python 3 nowadays. Change-Id: I9dc9573a86c93416d1bbbc782dac76ecdda6effd Signed-off-by: Stephen Finucane --- .pre-commit-config.yaml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6154c51ea..0088cd060 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,4 @@ --- -default_language_version: - # force all unspecified python hooks to run python3 - python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 @@ -18,23 +15,23 @@ repos: files: .*\.(yaml|yml)$ args: ['--unsafe'] - repo: https://github.com/asottile/pyupgrade - rev: v3.15.2 + rev: v3.17.0 hooks: - id: pyupgrade args: ['--py38-plus'] - repo: https://github.com/psf/black - rev: 24.4.0 + rev: 24.8.0 hooks: - id: black args: ['-S', '-l', '79'] - repo: https://github.com/PyCQA/bandit - rev: 1.7.8 + rev: 1.7.9 hooks: - id: bandit args: ['-x', 'tests'] - repo: https://opendev.org/openstack/hacking - rev: 6.1.0 + rev: 7.0.0 hooks: - id: hacking additional_dependencies: [] - exclude: '^(doc|releasenotes|tools)/.*$' + exclude: '^(doc|releasenotes)/.*$' From 2ba90581d5d68c565347b6c3aa16faf4b6478f2d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 29 Aug 2024 10:53:30 +0100 Subject: [PATCH 021/245] pre-commit: Migrate from flake8 to ruff Well, mostly. We still keep our own flake8 hooks and the hacking hooks enabled. Everything else can be handled by ruff. Doing this enables a couple of hacking checks that were previously unaddressed. It also highlights a few cases that flake8 missed. Both are addressed. Change-Id: If81c7055e9ef692425da2789bae18a96d04b104f Signed-off-by: Stephen Finucane --- .pre-commit-config.yaml | 5 +++++ openstackclient/common/quota.py | 2 +- openstackclient/compute/v2/flavor.py | 2 +- openstackclient/compute/v2/server_migration.py | 13 +++++++++++-- openstackclient/image/v2/image.py | 14 +++++++------- openstackclient/image/v2/metadef_objects.py | 10 ++++++---- openstackclient/volume/v2/volume_type.py | 8 ++++---- openstackclient/volume/v3/volume.py | 2 +- openstackclient/volume/v3/volume_group.py | 10 +++------- openstackclient/volume/v3/volume_type.py | 12 ++++++------ tox.ini | 8 +++----- 11 files changed, 48 insertions(+), 38 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0088cd060..3ff6e7335 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,6 +24,11 @@ repos: hooks: - id: black args: ['-S', '-l', '79'] + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.6.2 + hooks: + - id: ruff + args: ['--fix'] - repo: https://github.com/PyCQA/bandit rev: 1.7.9 hooks: diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index c408e97ba..6d8d0c6fc 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -176,7 +176,7 @@ def get_network_quotas( default=False, ): def _network_quota_to_dict(network_quota, detail=False): - if type(network_quota) is not dict: + if not isinstance(network_quota, dict): dict_quota = network_quota.to_dict() else: dict_quota = network_quota diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index e4df62885..f94b3162e 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -577,7 +577,7 @@ def take_action(self, parsed_args): parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) except sdk_exceptions.ResourceNotFound as e: - raise exceptions.CommandError(_(e.message)) + raise exceptions.CommandError(e.message) result = 0 if parsed_args.properties: diff --git a/openstackclient/compute/v2/server_migration.py b/openstackclient/compute/v2/server_migration.py index 18299156a..75073a0c3 100644 --- a/openstackclient/compute/v2/server_migration.py +++ b/openstackclient/compute/v2/server_migration.py @@ -253,8 +253,17 @@ def _get_migration_by_uuid(compute_client, server_id, migration_uuid): if migration.uuid == migration_uuid: return migration else: - msg = _('In-progress live migration %s is not found for server %s.') - raise exceptions.CommandError(msg % (migration_uuid, server_id)) + msg = _( + 'In-progress live migration %(migration)s is not found for ' + 'server %(server)s' + ) + raise exceptions.CommandError( + msg + % { + 'migration': migration_uuid, + 'server': server_id, + } + ) class ShowMigration(command.ShowOne): diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 46d374e9e..5390a704c 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -1614,8 +1614,6 @@ def get_parser(self, prog_name): metavar='', help=_('Image to initiate import process for (name or ID)'), ) - # TODO(stephenfin): Uncomment help text when we have this command - # implemented parser.add_argument( '--method', metavar='', @@ -1630,8 +1628,6 @@ def get_parser(self, prog_name): help=_( "Import method used for image import process. " "Not all deployments will support all methods. " - # "Valid values can be retrieved with the 'image import " - # "methods' command. " "The 'glance-direct' method (default) requires images be " "first staged using the 'image-stage' command." ), @@ -1734,11 +1730,15 @@ def take_action(self, parsed_args): if parsed_args.import_method not in import_methods: msg = _( - "The '%s' import method is not supported by this deployment. " - "Supported: %s" + "The '%(method)s' import method is not supported by this " + "deployment. Supported: %(supported)s" ) raise exceptions.CommandError( - msg % (parsed_args.import_method, ', '.join(import_methods)), + msg + % { + 'method': parsed_args.import_method, + 'supported': ', '.join(import_methods), + }, ) if parsed_args.import_method == 'web-download': diff --git a/openstackclient/image/v2/metadef_objects.py b/openstackclient/image/v2/metadef_objects.py index 2dc33968a..04c0fa55f 100644 --- a/openstackclient/image/v2/metadef_objects.py +++ b/openstackclient/image/v2/metadef_objects.py @@ -260,10 +260,12 @@ def take_action(self, parsed_args): prop['name'] = parsed_args.property except KeyError: - msg = _('Property %s not found in object %s.') % ( - parsed_args.property, - parsed_args.object, - ) + msg = _( + 'Property %(property)s not found in object %(object)s.' + ) % { + 'property': parsed_args.property, + 'object': parsed_args.object, + } raise exceptions.CommandError(msg) return zip(*sorted(prop.items())) diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index f4540f4e8..be57f4b50 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -171,7 +171,7 @@ def get_parser(self, prog_name): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(this is an alias for '--property replication_enabled= True') " "(requires driver support)" ), ) @@ -181,7 +181,7 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(this is an alias for '--property RESKEY:availability_zones:') " "(repeat option to set multiple availability zones)" ), ) @@ -535,7 +535,7 @@ def get_parser(self, prog_name): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(this is an alias for '--property replication_enabled= True') " "(requires driver support)" ), ) @@ -545,7 +545,7 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(this is an alias for '--property RESKEY:availability_zones:') " "(repeat option to set multiple availability zones)" ), ) diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 3bb3cdaed..2a3f13d95 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -154,7 +154,7 @@ def get_parser(self, prog_name): "Cinder cluster on which the existing volume resides; " "takes the form: cluster@backend-name#pool. This is only " "used along with the --remote-source option. " - "(supported by --os-volume-api-version 3.16 or above)", + "(supported by --os-volume-api-version 3.16 or above)" ), ) return parser diff --git a/openstackclient/volume/v3/volume_group.py b/openstackclient/volume/v3/volume_group.py index ba9368e01..201e7a6d7 100644 --- a/openstackclient/volume/v3/volume_group.py +++ b/openstackclient/volume/v3/volume_group.py @@ -288,7 +288,7 @@ def get_parser(self, prog_name): default=False, help=_( 'Delete the volume group even if it contains volumes. ' - 'This will delete any remaining volumes in the group.', + 'This will delete any remaining volumes in the group.' ), ) return parser @@ -582,18 +582,14 @@ def get_parser(self, prog_name): action='store_true', dest='allow_attached_volume', default=False, - help=_( - 'Allow group with attached volumes to be failed over.', - ), + help=_('Allow group with attached volumes to be failed over.'), ) parser.add_argument( '--disallow-attached-volume', action='store_false', dest='allow_attached_volume', default=False, - help=_( - 'Disallow group with attached volumes to be failed over.', - ), + help=_('Disallow group with attached volumes to be failed over.'), ) parser.add_argument( '--secondary-backend-id', diff --git a/openstackclient/volume/v3/volume_type.py b/openstackclient/volume/v3/volume_type.py index 96d7553d4..03c3fe245 100644 --- a/openstackclient/volume/v3/volume_type.py +++ b/openstackclient/volume/v3/volume_type.py @@ -172,7 +172,7 @@ def get_parser(self, prog_name): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(this is an alias for '--property replication_enabled= True') " "(requires driver support)" ), ) @@ -182,7 +182,7 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(this is an alias for '--property RESKEY:availability_zones:') " "(repeat option to set multiple availability zones)" ), ) @@ -448,7 +448,7 @@ def get_parser(self, prog_name): default=False, help=_( "List only volume types with replication enabled " - "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(this is an alias for '--property replication_enabled= True') " "(supported by --os-volume-api-version 3.52 or above)" ), ) @@ -458,7 +458,7 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "List only volume types with this availability configured " - "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(this is an alias for '--property RESKEY:availability_zones:') " "(repeat option to filter on multiple availability zones)" ), ) @@ -617,7 +617,7 @@ def get_parser(self, prog_name): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(this is an alias for '--property replication_enabled= True') " "(requires driver support)" ), ) @@ -627,7 +627,7 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(this is an alias for '--property RESKEY:availability_zones:') " "(repeat option to set multiple availability zones)" ), ) diff --git a/tox.ini b/tox.ini index e7c84c511..7b3d951d2 100644 --- a/tox.ini +++ b/tox.ini @@ -113,11 +113,9 @@ commands = [flake8] show-source = true exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,releasenotes -# E203 Black will put spaces after colons in list comprehensions -# E501 Black takes care of line length for us -# E704 Black will occasionally put multiple statements on one line +# We only enable the hacking (H) checks +select = H # H301 Black will put commas after imports that can't fit on one line -# W503 and W504 are disabled since they're not very useful -ignore = E203, E501, E701, H301, W503, W504 +ignore = H301 import-order-style = pep8 application_import_names = openstackclient From 17e6545fd44c53efbc85612fbc318c3a145a03a8 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 16 Feb 2024 15:06:35 +0000 Subject: [PATCH 022/245] trivial: Remove unnecessary trailing comma Change-Id: I62402145c17f96626ec6e29598e32dee03a6038e Signed-off-by: Stephen Finucane --- openstackclient/network/v2/floating_ip.py | 2 +- openstackclient/network/v2/floating_ip_port_forwarding.py | 4 ++-- openstackclient/network/v2/subnet_pool.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index 301ae716d..a94e58b62 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -448,7 +448,7 @@ def get_parser(self, prog_name): '--port', metavar='', help=_("Associate the floating IP with port (name or ID)"), - ), + ) parser.add_argument( '--fixed-ip-address', metavar='', diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py index e4a9aa511..9151f0498 100644 --- a/openstackclient/network/v2/floating_ip_port_forwarding.py +++ b/openstackclient/network/v2/floating_ip_port_forwarding.py @@ -144,7 +144,7 @@ def get_parser(self, prog_name): "The protocol used in the floating IP " "port forwarding, for instance: TCP, UDP" ), - ), + ) parser.add_argument( '--description', metavar='', @@ -404,7 +404,7 @@ def get_parser(self, prog_name): metavar='', choices=['tcp', 'udp'], help=_("The IP protocol used in the floating IP port forwarding"), - ), + ) parser.add_argument( '--description', metavar='', diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index c6d92a6b4..78505bf2d 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -201,7 +201,7 @@ def get_parser(self, prog_name): "as the number of IP addresses that can be allocated " "from the subnet pool" ), - ), + ) _tag.add_tag_option_to_parser_for_create(parser, _('subnet pool')) return parser @@ -433,7 +433,7 @@ def get_parser(self, prog_name): "as the number of IP addresses that can be allocated " "from the subnet pool" ), - ), + ) _tag.add_tag_option_to_parser_for_set(parser, _('subnet pool')) return parser From 519fa7aabc10d4c2383ba6fe7801358078ce46fc Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 29 Aug 2024 10:55:32 +0100 Subject: [PATCH 023/245] pre-commit: Migrate from black to ruff format Change-Id: I28ca7d31d30272002799f3e2832105dc67c60538 Signed-off-by: Stephen Finucane --- .pre-commit-config.yaml | 6 +----- openstackclient/api/api.py | 1 + openstackclient/api/image_v2.py | 2 +- openstackclient/api/object_store_v1.py | 12 ++++++------ openstackclient/identity/v2_0/project.py | 2 +- openstackclient/identity/v2_0/role_assignment.py | 2 +- openstackclient/identity/v3/identity_provider.py | 2 +- openstackclient/identity/v3/project.py | 2 +- openstackclient/network/v2/l3_conntrack_helper.py | 2 +- openstackclient/network/v2/ndp_proxy.py | 1 + openstackclient/network/v2/network_trunk.py | 9 +++++---- openstackclient/network/v2/router.py | 11 +++++++---- openstackclient/tests/unit/api/test_compute_v2.py | 5 ----- openstackclient/tests/unit/compute/v2/test_host.py | 1 - openstackclient/tests/unit/compute/v2/test_server.py | 4 +--- openstackclient/tests/unit/identity/v3/test_limit.py | 4 ++-- .../v2/test_metadef_resource_type_association.py | 8 ++------ .../network/v2/test_default_security_group_rule.py | 6 +++--- .../network/v2/test_floating_ip_port_forwarding.py | 8 ++++---- .../unit/network/v2/test_local_ip_association.py | 4 ++-- .../tests/unit/network/v2/test_network_qos_rule.py | 8 ++++---- .../tests/unit/network/v2/test_subnet_pool.py | 2 +- openstackclient/tests/unit/object/v1/test_object.py | 2 +- openstackclient/volume/client.py | 2 +- openstackclient/volume/v3/service.py | 1 - pyproject.toml | 6 ++++++ 26 files changed, 54 insertions(+), 59 deletions(-) create mode 100644 pyproject.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3ff6e7335..838177bd9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,16 +19,12 @@ repos: hooks: - id: pyupgrade args: ['--py38-plus'] - - repo: https://github.com/psf/black - rev: 24.8.0 - hooks: - - id: black - args: ['-S', '-l', '79'] - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.6.2 hooks: - id: ruff args: ['--fix'] + - id: ruff-format - repo: https://github.com/PyCQA/bandit rev: 1.7.9 hooks: diff --git a/openstackclient/api/api.py b/openstackclient/api/api.py index c8def49e3..838383f02 100644 --- a/openstackclient/api/api.py +++ b/openstackclient/api/api.py @@ -12,6 +12,7 @@ # """Base API Library""" + from keystoneauth1 import exceptions as ks_exceptions from keystoneauth1 import session as ks_session from osc_lib import exceptions diff --git a/openstackclient/api/image_v2.py b/openstackclient/api/image_v2.py index d01631895..9b0e9b1f8 100644 --- a/openstackclient/api/image_v2.py +++ b/openstackclient/api/image_v2.py @@ -33,7 +33,7 @@ def image_list( private=False, community=False, shared=False, - **filter + **filter, ): """Get available images diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py index bc49ac850..e7896b1e4 100644 --- a/openstackclient/api/object_store_v1.py +++ b/openstackclient/api/object_store_v1.py @@ -89,7 +89,7 @@ def container_list( marker=None, end_marker=None, prefix=None, - **params + **params, ): """Get containers in an account @@ -116,7 +116,7 @@ def container_list( marker=marker, end_marker=end_marker, prefix=prefix, - **params + **params, ) while listing: marker = listing[-1]['name'] @@ -125,7 +125,7 @@ def container_list( marker=marker, end_marker=end_marker, prefix=prefix, - **params + **params, ) if listing: data.extend(listing) @@ -306,7 +306,7 @@ def object_list( end_marker=None, delimiter=None, prefix=None, - **params + **params, ): """List objects in a container @@ -341,7 +341,7 @@ def object_list( end_marker=end_marker, prefix=prefix, delimiter=delimiter, - **params + **params, ) while listing: if delimiter: @@ -355,7 +355,7 @@ def object_list( end_marker=end_marker, prefix=prefix, delimiter=delimiter, - **params + **params, ) if listing: data.extend(listing) diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py index 160755523..8920e0e16 100644 --- a/openstackclient/identity/v2_0/project.py +++ b/openstackclient/identity/v2_0/project.py @@ -87,7 +87,7 @@ def take_action(self, parsed_args): parsed_args.name, description=parsed_args.description, enabled=enabled, - **kwargs + **kwargs, ) except ks_exc.Conflict: if parsed_args.or_show: diff --git a/openstackclient/identity/v2_0/role_assignment.py b/openstackclient/identity/v2_0/role_assignment.py index 6610bab94..54d01b4e5 100644 --- a/openstackclient/identity/v2_0/role_assignment.py +++ b/openstackclient/identity/v2_0/role_assignment.py @@ -11,7 +11,7 @@ # under the License. # -"""Identity v2 Assignment action implementations """ +"""Identity v2 Assignment action implementations""" from osc_lib.command import command from osc_lib import exceptions diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py index 5e0c8f7c9..dad716569 100644 --- a/openstackclient/identity/v3/identity_provider.py +++ b/openstackclient/identity/v3/identity_provider.py @@ -133,7 +133,7 @@ def take_action(self, parsed_args): description=parsed_args.description, domain_id=domain_id, enabled=parsed_args.enabled, - **kwargs + **kwargs, ) idp._info.pop('links', None) diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index d66fd6121..aa6f62949 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -125,7 +125,7 @@ def take_action(self, parsed_args): description=parsed_args.description, enabled=enabled, options=options, - **kwargs + **kwargs, ) except ks_exc.Conflict: if parsed_args.or_show: diff --git a/openstackclient/network/v2/l3_conntrack_helper.py b/openstackclient/network/v2/l3_conntrack_helper.py index 598ced997..f200090b7 100644 --- a/openstackclient/network/v2/l3_conntrack_helper.py +++ b/openstackclient/network/v2/l3_conntrack_helper.py @@ -245,7 +245,7 @@ def take_action(self, parsed_args): client.update_conntrack_helper( parsed_args.conntrack_helper_id, attrs.pop('router_id'), - **attrs + **attrs, ) diff --git a/openstackclient/network/v2/ndp_proxy.py b/openstackclient/network/v2/ndp_proxy.py index 3b6caab66..4c1871172 100644 --- a/openstackclient/network/v2/ndp_proxy.py +++ b/openstackclient/network/v2/ndp_proxy.py @@ -14,6 +14,7 @@ # under the License. """Router NDP proxy action implementations""" + import logging from osc_lib.command import command diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index 2b00d03ec..a2b0fbf1f 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -15,6 +15,7 @@ # """Network trunk and subports action implementations""" + import logging from cliff import columns as cliff_columns @@ -67,8 +68,8 @@ def get_parser(self, prog_name): required_keys=['port'], help=_( "Subport to add. Subport is of form " - "\'port=,segmentation-type=," - "segmentation-id=\' (--subport) option " + "'port=,segmentation-type=," + "segmentation-id=' (--subport) option " "can be repeated" ), ) @@ -198,8 +199,8 @@ def get_parser(self, prog_name): required_keys=['port'], help=_( "Subport to add. Subport is of form " - "\'port=,segmentation-type=" - ",segmentation-id=\' (--subport) option " + "'port=,segmentation-type=" + ",segmentation-id=' (--subport) option " "can be repeated" ), ) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 8527426ee..5193abcb7 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -164,10 +164,13 @@ def _get_external_gateway_attrs(client_manager, parsed_args): 'subnet_id' in ip_spec and ip_net_id not in external_gateways ): - msg = _( - 'Subnet %s does not belong to any of the networks ' - 'provided for --external-gateway.' - ) % (ip_spec['subnet_id']) + msg = ( + _( + 'Subnet %s does not belong to any of the networks ' + 'provided for --external-gateway.' + ) + % (ip_spec['subnet_id']) + ) raise exceptions.CommandError(msg) for gw_info in external_gateways[ip_net_id]: if 'external_fixed_ips' not in gw_info: diff --git a/openstackclient/tests/unit/api/test_compute_v2.py b/openstackclient/tests/unit/api/test_compute_v2.py index 01ebf7b92..cae8db9a7 100644 --- a/openstackclient/tests/unit/api/test_compute_v2.py +++ b/openstackclient/tests/unit/api/test_compute_v2.py @@ -26,7 +26,6 @@ class TestSecurityGroup(utils.TestCase): - def setUp(self): super().setUp() @@ -227,7 +226,6 @@ def test_delete_security_group(self): class TestSecurityGroupRule(utils.TestCase): - def setUp(self): super().setUp() @@ -290,7 +288,6 @@ def test_delete_security_group_rule(self): class TestNetwork(utils.TestCase): - def setUp(self): super().setUp() @@ -457,7 +454,6 @@ def test_delete_network(self): class TestFloatingIP(utils.TestCase): - def setUp(self): super().setUp() @@ -547,7 +543,6 @@ def test_delete_floating_ip(self): class TestFloatingIPPool(utils.TestCase): - def setUp(self): super().setUp() diff --git a/openstackclient/tests/unit/compute/v2/test_host.py b/openstackclient/tests/unit/compute/v2/test_host.py index afda8e958..1de802311 100644 --- a/openstackclient/tests/unit/compute/v2/test_host.py +++ b/openstackclient/tests/unit/compute/v2/test_host.py @@ -59,7 +59,6 @@ def _generate_fake_host(): class TestHostList(compute_fakes.TestComputev2): - def setUp(self): super().setUp() diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 434d2e61f..47aa9df57 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -7363,9 +7363,7 @@ def setUp(self): self.server = compute_fakes.create_one_sdk_server() self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.remove_security_group_from_server.return_value = ( - None - ) + self.compute_sdk_client.remove_security_group_from_server.return_value = None # Get the command object to test self.cmd = server.RemoveServerSecurityGroup(self.app, None) diff --git a/openstackclient/tests/unit/identity/v3/test_limit.py b/openstackclient/tests/unit/identity/v3/test_limit.py index ef0fa4b1e..a1d180b4b 100644 --- a/openstackclient/tests/unit/identity/v3/test_limit.py +++ b/openstackclient/tests/unit/identity/v3/test_limit.py @@ -90,7 +90,7 @@ def test_limit_create_without_options(self): self.service, identity_fakes.limit_resource_name, resource_limit, - **kwargs + **kwargs, ) collist = ( @@ -154,7 +154,7 @@ def test_limit_create_with_options(self): self.service, identity_fakes.limit_resource_name, resource_limit, - **kwargs + **kwargs, ) collist = ( diff --git a/openstackclient/tests/unit/image/v2/test_metadef_resource_type_association.py b/openstackclient/tests/unit/image/v2/test_metadef_resource_type_association.py index ac1d65008..d7c1ee686 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_resource_type_association.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_resource_type_association.py @@ -42,9 +42,7 @@ class TestMetadefResourceTypeAssociationCreate( def setUp(self): super().setUp() - self.image_client.create_metadef_resource_type_association.return_value = ( - self.resource_type_association - ) + self.image_client.create_metadef_resource_type_association.return_value = self.resource_type_association self.cmd = metadef_resource_type_association.CreateMetadefResourceTypeAssociation( self.app, None ) @@ -74,9 +72,7 @@ class TestMetadefResourceTypeAssociationDelete( def setUp(self): super().setUp() - self.image_client.delete_metadef_resource_type_association.return_value = ( - self.resource_type_association - ) + self.image_client.delete_metadef_resource_type_association.return_value = self.resource_type_association self.cmd = metadef_resource_type_association.DeleteMetadefResourceTypeAssociation( self.app, None ) diff --git a/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py b/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py index 9c9241a29..9b461269c 100644 --- a/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py +++ b/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py @@ -79,7 +79,7 @@ def _setup_default_security_group_rule(self, attrs=None): default_security_group_rule_attrs.update(attrs) self._default_sg_rule = sdk_fakes.generate_fake_resource( _default_security_group_rule.DefaultSecurityGroupRule, - **default_security_group_rule_attrs + **default_security_group_rule_attrs, ) self.sdk_client.create_default_security_group_rule.return_value = ( @@ -956,11 +956,11 @@ class TestListDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): # The security group rule to be listed. _default_sg_rule_tcp = sdk_fakes.generate_fake_resource( _default_security_group_rule.DefaultSecurityGroupRule, - **{'protocol': 'tcp', 'port_range_max': 80, 'port_range_min': 80} + **{'protocol': 'tcp', 'port_range_max': 80, 'port_range_min': 80}, ) _default_sg_rule_icmp = sdk_fakes.generate_fake_resource( _default_security_group_rule.DefaultSecurityGroupRule, - **{'protocol': 'icmp', 'remote_ip_prefix': '10.0.2.0/24'} + **{'protocol': 'icmp', 'remote_ip_prefix': '10.0.2.0/24'}, ) _default_sg_rules = [ _default_sg_rule_tcp, diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py index 6c0e163ce..2e4c93f8a 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py @@ -153,7 +153,7 @@ def test_create_all_options_with_range(self): 'internal_port_id': self.new_port_forwarding_with_ranges.internal_port_id, # noqa: E501 'protocol': self.new_port_forwarding_with_ranges.protocol, 'description': self.new_port_forwarding_with_ranges.description, # noqa: E501 - } + }, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -334,7 +334,7 @@ def test_create_all_options(self): 'internal_port_id': self.new_port_forwarding.internal_port_id, 'protocol': self.new_port_forwarding.protocol, 'description': self.new_port_forwarding.description, - } + }, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -587,7 +587,7 @@ def test_set_nothing(self): self.network_client.update_floating_ip_port_forwarding.assert_called_with( self._port_forwarding.floatingip_id, self._port_forwarding.id, - **attrs + **attrs, ) self.assertIsNone(result) @@ -648,7 +648,7 @@ def run_and_validate(arglist, verifylist, attrs): self.network_client.update_floating_ip_port_forwarding.assert_called_with( self._port_forwarding.floatingip_id, self._port_forwarding.id, - **attrs + **attrs, ) self.assertIsNone(result) diff --git a/openstackclient/tests/unit/network/v2/test_local_ip_association.py b/openstackclient/tests/unit/network/v2/test_local_ip_association.py index 35a104543..2c1c0ff31 100644 --- a/openstackclient/tests/unit/network/v2/test_local_ip_association.py +++ b/openstackclient/tests/unit/network/v2/test_local_ip_association.py @@ -86,7 +86,7 @@ def test_create_no_options(self): self.new_local_ip_association.local_ip_id, **{ 'fixed_port_id': self.new_local_ip_association.fixed_port_id, - } + }, ) self.assertEqual(set(self.columns), set(columns)) self.assertEqual(set(self.data), set(data)) @@ -111,7 +111,7 @@ def test_create_all_options(self): **{ 'fixed_port_id': self.new_local_ip_association.fixed_port_id, 'fixed_ip': self.new_local_ip_association.fixed_ip, - } + }, ) self.assertEqual(set(self.columns), set(columns)) self.assertEqual(set(self.data), set(data)) diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py index 1f6fe093e..132902fb3 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py @@ -137,7 +137,7 @@ def test_create_default_options(self): **{ 'min_kbps': self.new_rule.min_kbps, 'direction': self.new_rule.direction, - } + }, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -244,7 +244,7 @@ def test_create_default_options(self): **{ 'min_kpps': self.new_rule.min_kpps, 'direction': self.new_rule.direction, - } + }, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -473,7 +473,7 @@ def test_create_default_options(self): **{ 'max_kbps': self.new_rule.max_kbps, 'direction': self.new_rule.direction, - } + }, ) self.assertEqual(self.columns, columns) self.assertEqual(expected_data, data) @@ -507,7 +507,7 @@ def test_create_all_options(self): 'max_kbps': self.new_rule.max_kbps, 'max_burst_kbps': self.new_rule.max_burst_kbits, 'direction': self.new_rule.direction, - } + }, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py index 214bfdce5..40aa1e990 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py @@ -1006,7 +1006,7 @@ def test_set_with_default_quota(self): self._subnet_pool, **{ 'default_quota': 20, - } + }, ) self.assertIsNone(result) diff --git a/openstackclient/tests/unit/object/v1/test_object.py b/openstackclient/tests/unit/object/v1/test_object.py index 665a71d14..f1777f963 100644 --- a/openstackclient/tests/unit/object/v1/test_object.py +++ b/openstackclient/tests/unit/object/v1/test_object.py @@ -352,7 +352,7 @@ def test_object_show(self, c_mock): c_mock.assert_called_with( container=object_fakes.container_name, object=object_fakes.object_name_1, - **kwargs + **kwargs, ) collist = ('bytes', 'content_type', 'hash', 'last_modified', 'name') diff --git a/openstackclient/volume/client.py b/openstackclient/volume/client.py index 4615184e7..b07913086 100644 --- a/openstackclient/volume/client.py +++ b/openstackclient/volume/client.py @@ -93,7 +93,7 @@ def make_client(instance): region_name=instance.region_name, endpoint_override=endpoint_override, api_version=version, - **kwargs + **kwargs, ) return client diff --git a/openstackclient/volume/v3/service.py b/openstackclient/volume/v3/service.py index 5f01b5456..fe4db4c11 100644 --- a/openstackclient/volume/v3/service.py +++ b/openstackclient/volume/v3/service.py @@ -21,7 +21,6 @@ class ListService(service_v2.ListService): - def take_action(self, parsed_args): service_client = self.app.client_manager.volume diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..3ab847ac0 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[tool.ruff] +line-length = 79 + +[tool.ruff.format] +quote-style = "preserve" +docstring-code-format = true From b0c08ae29a566d5abea940e50c8174d571624ee2 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Wed, 11 Sep 2024 16:07:20 +0000 Subject: [PATCH 024/245] Update master for stable/2024.2 Add file to the reno documentation build to show release notes for stable/2024.2. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/2024.2. Sem-Ver: feature Change-Id: I5b2c70118df024dfc9236c7e1fa39554ffccf5e0 --- releasenotes/source/2024.2.rst | 6 ++++++ releasenotes/source/index.rst | 1 + 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/2024.2.rst diff --git a/releasenotes/source/2024.2.rst b/releasenotes/source/2024.2.rst new file mode 100644 index 000000000..aaebcbc8c --- /dev/null +++ b/releasenotes/source/2024.2.rst @@ -0,0 +1,6 @@ +=========================== +2024.2 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2024.2 diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index e25b52dec..991d079e6 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ OpenStackClient Release Notes :maxdepth: 1 unreleased + 2024.2 2024.1 2023.2 2023.1 From ffa683ab4edf44219b687af54e2ace5177dbc2f3 Mon Sep 17 00:00:00 2001 From: Johannes Kulik Date: Tue, 13 Aug 2024 12:14:31 +0200 Subject: [PATCH 025/245] compute: Fix --host in server list for new openstacksdk With `openstacksdk` 3.2.0 the `host` attribute of an Instance got added to the `Server` class [0]. With that change, listing servers with the `host` attribute leads to a query-filter for `compute_host` as expected, but `openstacksdk` will also filter for the `host` attribute locally after the results are returned. Since `compute_host` being `OS-EXT-SRV-ATTR:host` is not the same as `host, this means no results are returned. Since we want to keep the old behaviour of filtering by `compute_host` i.e. the service host name, we need to switch to filter for `compute_host`. This is already supported in older versions of `openstacksdk`. [0] https://github.com/openstack/openstacksdk/commit/0f311ff3e2e57bf3659cef77e98551b6c0c7e3c9 Change-Id: I0cd32c5b7d6d4d21194f3efdcfb9b205dea6a91e Closes-bug: #2074200 --- openstackclient/compute/v2/server.py | 2 +- openstackclient/tests/unit/compute/v2/test_server.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 661453b65..2820ffc27 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2648,7 +2648,7 @@ def take_action(self, parsed_args): 'status': parsed_args.status, 'flavor': flavor_id, 'image': image_id, - 'host': parsed_args.host, + 'compute_host': parsed_args.host, 'project_id': project_id, 'all_projects': parsed_args.all_projects, 'user_id': user_id, diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 434d2e61f..951a8d0e3 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4581,7 +4581,7 @@ def setUp(self): 'status': None, 'flavor': None, 'image': None, - 'host': None, + 'compute_host': None, 'project_id': None, 'all_projects': False, 'user_id': None, From 2bf123f315ecba055b6ce7909c7547da02768281 Mon Sep 17 00:00:00 2001 From: Mohammed Al-Dokimi Date: Mon, 2 Sep 2024 16:55:48 +0200 Subject: [PATCH 026/245] Removed the emit_duplicated_warning() funtion. Since this function is called once, I moved its implementaion to where its called. Story: 2010344 Change-Id: Iaf06def1a06ffbb605ee42569e6f87b409a72772 --- openstackclient/compute/v2/server.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 661453b65..82f5fa376 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1520,17 +1520,6 @@ def _show_progress(progress): if not image and parsed_args.image_properties: - def emit_duplicated_warning(img): - img_uuid_list = [str(image.id) for image in img] - LOG.warning( - 'Multiple matching images: %(img_uuid_list)s\n' - 'Using image: %(chosen_one)s', - { - 'img_uuid_list': img_uuid_list, - 'chosen_one': img_uuid_list[0], - }, - ) - def _match_image(image_api, wanted_properties): image_list = image_api.images() images_matched = [] @@ -1568,7 +1557,15 @@ def _match_image(image_api, wanted_properties): images = _match_image(image_client, parsed_args.image_properties) if len(images) > 1: - emit_duplicated_warning(images, parsed_args.image_properties) + img_uuid_list = [str(image.id) for image in images] + LOG.warning( + 'Multiple matching images: %(img_uuid_list)s\n' + 'Using image: %(chosen_one)s', + { + 'img_uuid_list': img_uuid_list, + 'chosen_one': img_uuid_list[0], + }, + ) if images: image = images[0] else: From fc6852cd94973c45b7e8c6453178e6ab46472d5e Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 12 Sep 2024 18:00:48 +0100 Subject: [PATCH 027/245] pre-commit: Migrate bandit to ruff The name of the errors change and we need to move things around a little, but it's otherwise a straight swap. Change-Id: I0a19765ebeaa14c0534faa1542165b76ed2bf4e2 Signed-off-by: Stephen Finucane --- .pre-commit-config.yaml | 5 ----- openstackclient/common/clientmanager.py | 2 +- openstackclient/common/module.py | 4 ++-- openstackclient/compute/v2/server.py | 18 +++++++++--------- openstackclient/compute/v2/usage.py | 4 ++-- openstackclient/identity/v2_0/user.py | 4 ++-- openstackclient/volume/v1/volume.py | 4 ++-- openstackclient/volume/v1/volume_backup.py | 4 ++-- openstackclient/volume/v1/volume_snapshot.py | 4 ++-- openstackclient/volume/v2/volume.py | 4 ++-- openstackclient/volume/v2/volume_backup.py | 4 ++-- openstackclient/volume/v2/volume_snapshot.py | 4 ++-- openstackclient/volume/v3/volume.py | 4 ++-- openstackclient/volume/v3/volume_backup.py | 4 ++-- pyproject.toml | 6 ++++++ 15 files changed, 38 insertions(+), 37 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 838177bd9..738f7d192 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,11 +25,6 @@ repos: - id: ruff args: ['--fix'] - id: ruff-format - - repo: https://github.com/PyCQA/bandit - rev: 1.7.9 - hooks: - - id: bandit - args: ['-x', 'tests'] - repo: https://opendev.org/openstack/hacking rev: 7.0.0 hooks: diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 2e990985b..d1b470d9e 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -101,7 +101,7 @@ def _fallback_load_auth_plugin(self, e): # expect, delete fake token and endpoint, then try to # load auth plugin again with user specified options. # We know it looks ugly, but it's necessary. - if self._cli_options.config['auth']['token'] == 'x': + if self._cli_options.config['auth']['token'] == 'x': # noqa: S105 # restore original auth_type self._cli_options.config['auth_type'] = self._original_auth_type del self._cli_options.config['auth']['token'] diff --git a/openstackclient/common/module.py b/openstackclient/common/module.py index e9102561e..d4a5b955a 100644 --- a/openstackclient/common/module.py +++ b/openstackclient/common/module.py @@ -111,8 +111,8 @@ def take_action(self, parsed_args): data[k] = mods[k].version.__version__ else: data[k] = mods[k].__version__ - except Exception: + except Exception: # noqa: S110 # Catch all exceptions, just skip it - pass # nosec: B110 + pass return zip(*sorted(data.items())) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 661453b65..293e010a7 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2915,10 +2915,10 @@ def take_action(self, parsed_args): for image_id in image_ids: try: images[image_id] = image_client.get_image(image_id) - except Exception: + except Exception: # noqa: S110 # retrieving image names is not crucial, so we swallow # any exceptions - pass # nosec: B110 + pass else: try: # some deployments can have *loads* of images so we only @@ -2936,10 +2936,10 @@ def take_action(self, parsed_args): ) for i in images_list: images[i.id] = i - except Exception: + except Exception: # noqa: S110 # retrieving image names is not crucial, so we swallow any # exceptions - pass # nosec: B110 + pass # create a dict that maps flavor_id to flavor object, which is used # to display the "Flavor Name" column. Note that 'flavor.id' is not @@ -2955,19 +2955,19 @@ def take_action(self, parsed_args): flavors[f_id] = compute_client.find_flavor( f_id, ignore_missing=False ) - except Exception: + except Exception: # noqa: S110 # retrieving flavor names is not crucial, so we swallow # any exceptions - pass # nosec: B110 + pass else: try: flavors_list = compute_client.flavors(is_public=None) for i in flavors_list: flavors[i.id] = i - except Exception: + except Exception: # noqa: S110 # retrieving flavor names is not crucial, so we swallow any # exceptions - pass # nosec: B110 + pass # Populate image_name, image_id, flavor_name and flavor_id attributes # of server objects so that we can display those columns. @@ -4872,7 +4872,7 @@ def take_action(self, parsed_args): LOG.debug(f"ssh command: {cmd}") # we intentionally pass through user-provided arguments and run this in # the user's shell - os.system(cmd) # nosec: B605 + os.system(cmd) # noqa: S605 class StartServer(command.Command): diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index 84118b9d9..2b3fa6a3f 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -180,9 +180,9 @@ def _format_project(project): try: for p in self.app.client_manager.identity.projects.list(): project_cache[p.id] = p - except Exception: + except Exception: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass if parsed_args.formatter == 'table' and len(usage_list) > 0: self.app.stdout.write( diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py index 745a48d11..3625adc7c 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -250,9 +250,9 @@ def take_action(self, parsed_args): try: for p in identity_client.tenants.list(): project_cache[p.id] = p - except Exception: + except Exception: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass formatters['tenantId'] = functools.partial( ProjectColumn, project_cache=project_cache ) diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index c60625ee9..3ec995961 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -422,9 +422,9 @@ def take_action(self, parsed_args): compute_client = self.app.client_manager.sdk_connection.compute for s in compute_client.servers(): server_cache[s.id] = s - except Exception: + except Exception: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass AttachmentsColumnWithCache = functools.partial( AttachmentsColumn, server_cache=server_cache ) diff --git a/openstackclient/volume/v1/volume_backup.py b/openstackclient/volume/v1/volume_backup.py index 99d583809..77aa81dc5 100644 --- a/openstackclient/volume/v1/volume_backup.py +++ b/openstackclient/volume/v1/volume_backup.py @@ -215,9 +215,9 @@ def take_action(self, parsed_args): try: for s in volume_client.volumes.list(): volume_cache[s.id] = s - except Exception: + except Exception: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass VolumeIdColumnWithCache = functools.partial( VolumeIdColumn, volume_cache=volume_cache ) diff --git a/openstackclient/volume/v1/volume_snapshot.py b/openstackclient/volume/v1/volume_snapshot.py index 18d8c3d62..4953db129 100644 --- a/openstackclient/volume/v1/volume_snapshot.py +++ b/openstackclient/volume/v1/volume_snapshot.py @@ -242,9 +242,9 @@ def take_action(self, parsed_args): try: for s in volume_client.volumes.list(): volume_cache[s.id] = s - except Exception: + except Exception: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass VolumeIdColumnWithCache = functools.partial( VolumeIdColumn, volume_cache=volume_cache ) diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index b5a4a1ffc..179da1960 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -511,9 +511,9 @@ def take_action(self, parsed_args): compute_client = self.app.client_manager.sdk_connection.compute for s in compute_client.servers(): server_cache[s.id] = s - except sdk_exceptions.SDKException: + except sdk_exceptions.SDKException: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass AttachmentsColumnWithCache = functools.partial( AttachmentsColumn, server_cache=server_cache ) diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index e3ac5930d..fd59c0cef 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -267,9 +267,9 @@ def take_action(self, parsed_args): try: for s in volume_client.volumes(): volume_cache[s.id] = s - except Exception: + except Exception: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass _VolumeIdColumn = functools.partial( VolumeIdColumn, volume_cache=volume_cache diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index 3398f502a..797fdc853 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -287,9 +287,9 @@ def take_action(self, parsed_args): try: for s in volume_client.volumes.list(): volume_cache[s.id] = s - except Exception: + except Exception: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass _VolumeIdColumn = functools.partial( VolumeIdColumn, volume_cache=volume_cache ) diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 2a3f13d95..6f705ad6f 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -526,9 +526,9 @@ def take_action(self, parsed_args): compute_client = self.app.client_manager.sdk_connection.compute for s in compute_client.servers(): server_cache[s.id] = s - except sdk_exceptions.SDKException: + except sdk_exceptions.SDKException: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass AttachmentsColumnWithCache = functools.partial( AttachmentsColumn, server_cache=server_cache ) diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py index 7052f608a..471b4cee1 100644 --- a/openstackclient/volume/v3/volume_backup.py +++ b/openstackclient/volume/v3/volume_backup.py @@ -323,9 +323,9 @@ def take_action(self, parsed_args): try: for s in volume_client.volumes(): volume_cache[s.id] = s - except Exception: + except Exception: # noqa: S110 # Just forget it if there's any trouble - pass # nosec: B110 + pass _VolumeIdColumn = functools.partial( VolumeIdColumn, volume_cache=volume_cache diff --git a/pyproject.toml b/pyproject.toml index 3ab847ac0..6b13ff246 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,3 +4,9 @@ line-length = 79 [tool.ruff.format] quote-style = "preserve" docstring-code-format = true + +[tool.ruff.lint] +select = ["E4", "E7", "E9", "F", "S"] + +[tool.ruff.lint.per-file-ignores] +"openstackclient/tests/*" = ["S"] From f98006ca9d15ea2a8816887bed26d5d0e28180dc Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 12 Sep 2024 17:57:14 +0100 Subject: [PATCH 028/245] pre-commit: Migrate pyupgrade to ruff Change-Id: Ic50d2a5e0bc9dcdfe29f382607135cab510cd396 Signed-off-by: Stephen Finucane --- .pre-commit-config.yaml | 7 +- examples/common.py | 6 +- examples/object_api.py | 2 +- examples/osc-lib.py | 4 +- openstackclient/api/api.py | 2 +- openstackclient/api/object_store_v1.py | 20 +- openstackclient/common/quota.py | 6 +- openstackclient/compute/v2/server.py | 18 +- openstackclient/compute/v2/server_backup.py | 2 +- openstackclient/compute/v2/server_image.py | 2 +- openstackclient/compute/v2/usage.py | 2 +- openstackclient/identity/common.py | 2 +- openstackclient/image/v2/image.py | 12 +- openstackclient/network/common.py | 15 +- openstackclient/network/v2/network_agent.py | 8 +- .../network/v2/network_qos_rule.py | 7 +- openstackclient/network/v2/port.py | 6 +- openstackclient/shell.py | 5 +- .../tests/functional/common/test_help.py | 10 +- .../tests/functional/common/test_module.py | 2 +- .../tests/functional/common/test_quota.py | 6 +- .../tests/functional/compute/v2/common.py | 4 +- .../functional/compute/v2/test_hypervisor.py | 6 +- .../functional/compute/v2/test_keypair.py | 4 +- .../functional/compute/v2/test_server.py | 2 +- .../tests/functional/identity/v2/common.py | 79 +++----- .../functional/identity/v2/test_catalog.py | 2 +- .../identity/v2/test_ec2_credentials.py | 4 +- .../functional/identity/v2/test_endpoint.py | 4 +- .../functional/identity/v2/test_project.py | 16 +- .../tests/functional/identity/v2/test_role.py | 48 ++--- .../functional/identity/v2/test_service.py | 4 +- .../functional/identity/v2/test_token.py | 2 +- .../tests/functional/identity/v2/test_user.py | 16 +- .../tests/functional/identity/v3/common.py | 179 +++++++----------- .../v3/test_application_credential.py | 62 ++---- .../functional/identity/v3/test_catalog.py | 2 +- .../functional/identity/v3/test_domain.py | 20 +- .../functional/identity/v3/test_endpoint.py | 42 ++-- .../functional/identity/v3/test_group.py | 125 ++++-------- .../tests/functional/identity/v3/test_idp.py | 6 +- .../functional/identity/v3/test_limit.py | 64 +++---- .../functional/identity/v3/test_project.py | 43 ++--- .../functional/identity/v3/test_region.py | 13 +- .../identity/v3/test_registered_limit.py | 63 +++--- .../tests/functional/identity/v3/test_role.py | 90 +++------ .../identity/v3/test_role_assignment.py | 113 ++++------- .../functional/identity/v3/test_service.py | 20 +- .../identity/v3/test_service_provider.py | 6 +- .../tests/functional/identity/v3/test_user.py | 51 ++--- .../tests/functional/image/v2/test_image.py | 4 +- .../tests/functional/network/v2/common.py | 4 +- .../v2/test_default_security_group_rule.py | 11 +- .../network/v2/test_l3_conntrack_helper.py | 58 +++--- .../functional/network/v2/test_network.py | 21 +- .../network/v2/test_network_agent.py | 35 ++-- .../network/v2/test_network_flavor.py | 4 +- .../network/v2/test_network_ndp_proxy.py | 56 ++---- .../network/v2/test_network_qos_rule.py | 111 +++++------ .../network/v2/test_network_qos_rule_type.py | 2 +- .../network/v2/test_network_segment_range.py | 4 +- .../network/v2/test_network_trunk.py | 40 ++-- .../tests/functional/network/v2/test_port.py | 62 +++--- .../functional/network/v2/test_router.py | 32 ++-- .../tests/functional/object/v1/test_object.py | 11 +- .../tests/functional/volume/base.py | 22 +-- .../functional/volume/v1/test_volume_type.py | 22 +-- .../volume/v2/test_volume_backup.py | 2 +- .../functional/volume/v2/test_volume_type.py | 28 ++- .../functional/volume/v3/test_volume_type.py | 28 ++- .../tests/unit/api/test_object_store_v1.py | 10 +- .../tests/unit/identity/v3/test_group.py | 14 +- .../unit/image/v2/test_metadef_objects.py | 3 +- openstackclient/tests/unit/integ/base.py | 2 +- .../unit/network/v2/test_network_qos_rule.py | 17 +- .../unit/network/v2/test_network_trunk.py | 66 +++---- .../tests/unit/network/v2/test_port.py | 34 ++-- openstackclient/tests/unit/utils.py | 4 +- pyproject.toml | 2 +- 79 files changed, 753 insertions(+), 1190 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 738f7d192..786c26378 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,16 +14,11 @@ repos: - id: check-yaml files: .*\.(yaml|yml)$ args: ['--unsafe'] - - repo: https://github.com/asottile/pyupgrade - rev: v3.17.0 - hooks: - - id: pyupgrade - args: ['--py38-plus'] - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.6.2 hooks: - id: ruff - args: ['--fix'] + args: ['--fix', '--unsafe-fixes'] - id: ruff-format - repo: https://opendev.org/openstack/hacking rev: 7.0.0 diff --git a/examples/common.py b/examples/common.py index d97bfb55c..650139ec2 100755 --- a/examples/common.py +++ b/examples/common.py @@ -241,9 +241,9 @@ def run(opts): # Do some basic testing here sys.stdout.write("Default run command\n") - sys.stdout.write("Verbose level: %s\n" % opts.verbose_level) - sys.stdout.write("Debug: %s\n" % opts.debug) - sys.stdout.write("dump_stack_trace: %s\n" % dump_stack_trace) + sys.stdout.write(f"Verbose level: {opts.verbose_level}\n") + sys.stdout.write(f"Debug: {opts.debug}\n") + sys.stdout.write(f"dump_stack_trace: {dump_stack_trace}\n") def setup(): diff --git a/examples/object_api.py b/examples/object_api.py index 577fc052f..4ff245d4c 100755 --- a/examples/object_api.py +++ b/examples/object_api.py @@ -101,7 +101,7 @@ def run(opts): o_list = obj_api.object_list(c_list[0]['name']) print("\nObject") for o in o_list: - print("%s" % o) + print(f"{o}") if __name__ == "__main__": diff --git a/examples/osc-lib.py b/examples/osc-lib.py index cc04bc70f..6bd0347ac 100755 --- a/examples/osc-lib.py +++ b/examples/osc-lib.py @@ -94,13 +94,13 @@ def run(opts): o_list = client_manager.object_store.object_list(c_list[0]['name']) print("\nObject") for o in o_list: - print("%s" % o) + print(f"{o}") # Look at the compute flavors flavor_list = client_manager.compute.flavors.list() print("\nFlavors:") for f in flavor_list: - print("%s" % f) + print(f"{f}") if __name__ == "__main__": diff --git a/openstackclient/api/api.py b/openstackclient/api/api.py index 838383f02..5f78b0ee5 100644 --- a/openstackclient/api/api.py +++ b/openstackclient/api/api.py @@ -307,7 +307,7 @@ def find( except ks_exceptions.NotFound: kwargs = {attr: value} try: - ret = self.find_one("/%s/detail" % (path), **kwargs) + ret = self.find_one(f"/{path}/detail", **kwargs) except ks_exceptions.NotFound: msg = _("%s not found") % value raise exceptions.NotFound(msg) diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py index e7896b1e4..fd941e483 100644 --- a/openstackclient/api/object_store_v1.py +++ b/openstackclient/api/object_store_v1.py @@ -256,10 +256,7 @@ def object_create( # object's name in the container. object_name_str = name if name else object - full_url = "{}/{}".format( - urllib.parse.quote(container), - urllib.parse.quote(object_name_str), - ) + full_url = f"{urllib.parse.quote(container)}/{urllib.parse.quote(object_name_str)}" with open(object, 'rb') as f: response = self.create( full_url, @@ -293,8 +290,7 @@ def object_delete( return self.delete( - "%s/%s" - % (urllib.parse.quote(container), urllib.parse.quote(object)) + f"{urllib.parse.quote(container)}/{urllib.parse.quote(object)}" ) def object_list( @@ -395,8 +391,7 @@ def object_save( response = self._request( 'GET', - "%s/%s" - % (urllib.parse.quote(container), urllib.parse.quote(object)), + f"{urllib.parse.quote(container)}/{urllib.parse.quote(object)}", stream=True, ) if response.status_code == 200: @@ -431,8 +426,7 @@ def object_set( headers = self._set_properties(properties, 'X-Object-Meta-%s') if headers: self.create( - "%s/%s" - % (urllib.parse.quote(container), urllib.parse.quote(object)), + f"{urllib.parse.quote(container)}/{urllib.parse.quote(object)}", headers=headers, ) @@ -455,8 +449,7 @@ def object_unset( headers = self._unset_properties(properties, 'X-Remove-Object-Meta-%s') if headers: self.create( - "%s/%s" - % (urllib.parse.quote(container), urllib.parse.quote(object)), + f"{urllib.parse.quote(container)}/{urllib.parse.quote(object)}", headers=headers, ) @@ -480,8 +473,7 @@ def object_show( response = self._request( 'HEAD', - "%s/%s" - % (urllib.parse.quote(container), urllib.parse.quote(object)), + f"{urllib.parse.quote(container)}/{urllib.parse.quote(object)}", ) data = { diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index b3b04e0b3..4356cae66 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -507,8 +507,8 @@ def get_parser(self, prog_name): ) for k, v, h in self._build_options_list(): parser.add_argument( - '--%s' % v, - metavar='<%s>' % v, + f'--{v}', + metavar=f'<{v}>', dest=k, type=int, help=h, @@ -590,7 +590,7 @@ def take_action(self, parsed_args): parsed_args.volume_type and k in IMPACT_VOLUME_TYPE_QUOTAS ): - k = k + '_%s' % parsed_args.volume_type + k = k + f'_{parsed_args.volume_type}' volume_kwargs[k] = value if self.app.client_manager.is_network_endpoint_enabled(): diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 293e010a7..9cf200f15 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1504,7 +1504,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() compute_client = self.app.client_manager.sdk_connection.compute @@ -2189,7 +2189,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() compute_client = self.app.client_manager.sdk_connection.compute @@ -3198,7 +3198,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() compute_client = self.app.client_manager.sdk_connection.compute @@ -3350,7 +3350,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() compute_client = self.app.client_manager.sdk_connection.compute @@ -3555,7 +3555,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() compute_client = self.app.client_manager.sdk_connection.compute @@ -3816,7 +3816,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() compute_client = self.app.client_manager.sdk_connection.compute @@ -4188,7 +4188,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() compute_client = self.app.client_manager.sdk_connection.compute @@ -4584,7 +4584,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() compute_client = self.app.client_manager.sdk_connection.compute @@ -5181,7 +5181,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() compute_client = self.app.client_manager.sdk_connection.compute diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py index 59bd5540e..96b325b10 100644 --- a/openstackclient/compute/v2/server_backup.py +++ b/openstackclient/compute/v2/server_backup.py @@ -68,7 +68,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stderr.write('\rProgress: %s' % progress) + self.app.stderr.write(f'\rProgress: {progress}') self.app.stderr.flush() compute_client = self.app.client_manager.sdk_connection.compute diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py index 12b45b285..e49a5d36c 100644 --- a/openstackclient/compute/v2/server_image.py +++ b/openstackclient/compute/v2/server_image.py @@ -69,7 +69,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): def _show_progress(progress): if progress: - self.app.stdout.write('\rProgress: %s' % progress) + self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() compute_client = self.app.client_manager.sdk_connection.compute diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index 2b3fa6a3f..27f9335bb 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -60,7 +60,7 @@ def human_readable(self): class FloatColumn(cliff_columns.FormattableColumn): def human_readable(self): - return float("%.2f" % self._value) + return float(f"{self._value:.2f}") def _formatters(project_cache): diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index 4e5f083de..50ad2fed6 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -34,7 +34,7 @@ def find_service_in_list(service_list, service_id): if service.id == service_id: return service raise exceptions.CommandError( - "No service with a type, name or ID of '%s' exists." % service_id + f"No service with a type, name or ID of '{service_id}' exists." ) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 5390a704c..094c507b0 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -421,8 +421,8 @@ def get_parser(self, prog_name): identity_common.add_project_domain_option_to_parser(parser) for deadopt in self.deadopts: parser.add_argument( - "--%s" % deadopt, - metavar="<%s>" % deadopt, + f"--{deadopt}", + metavar=f"<{deadopt}>", dest=deadopt.replace('-', '_'), help=argparse.SUPPRESS, ) @@ -488,7 +488,7 @@ def _take_action_image(self, parsed_args): fp = open(parsed_args.filename, 'rb') except FileNotFoundError: raise exceptions.CommandError( - '%r is not a valid file' % parsed_args.filename, + f'{parsed_args.filename!r} is not a valid file', ) else: fp = get_data_from_stdin() @@ -1209,8 +1209,8 @@ def get_parser(self, prog_name): identity_common.add_project_domain_option_to_parser(parser) for deadopt in self.deadopts: parser.add_argument( - "--%s" % deadopt, - metavar="<%s>" % deadopt, + f"--{deadopt}", + metavar=f"<{deadopt}>", dest=f"dead_{deadopt.replace('-', '_')}", help=argparse.SUPPRESS, ) @@ -1575,7 +1575,7 @@ def take_action(self, parsed_args): fp = open(parsed_args.filename, 'rb') except FileNotFoundError: raise exceptions.CommandError( - '%r is not a valid file' % parsed_args.filename, + f'{parsed_args.filename!r} is not a valid file', ) else: fp = get_data_from_stdin() diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index 4c01f90ef..3d5237b1b 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -120,13 +120,14 @@ def enhance_help_nova_network(self, _help): @staticmethod def split_help(network_help, compute_help): return ( - "*%(network_qualifier)s:*\n %(network_help)s\n\n" - "*%(compute_qualifier)s:*\n %(compute_help)s" - % dict( - network_qualifier=_("Network version 2"), - network_help=network_help, - compute_qualifier=_("Compute version 2"), - compute_help=compute_help, + "*{network_qualifier}:*\n {network_help}\n\n" + "*{compute_qualifier}:*\n {compute_help}".format( + **dict( + network_qualifier=_("Network version 2"), + network_help=network_help, + compute_qualifier=_("Compute version 2"), + compute_help=compute_help, + ) ) ) diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py index d31bd3f56..bccde7ef3 100644 --- a/openstackclient/network/v2/network_agent.py +++ b/openstackclient/network/v2/network_agent.py @@ -89,9 +89,7 @@ def take_action(self, parsed_args): try: client.add_dhcp_agent_to_network(agent, network) except Exception: - msg = 'Failed to add {} to {}'.format( - network.name, agent.agent_type - ) + msg = f'Failed to add {network.name} to {agent.agent_type}' exceptions.CommandError(msg) @@ -321,9 +319,7 @@ def take_action(self, parsed_args): try: client.remove_dhcp_agent_from_network(agent, network) except Exception: - msg = 'Failed to remove {} to {}'.format( - network.name, agent.agent_type - ) + msg = f'Failed to remove {network.name} to {agent.agent_type}' exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py index 70f4d2843..0b5e06fe5 100644 --- a/openstackclient/network/v2/network_qos_rule.py +++ b/openstackclient/network/v2/network_qos_rule.py @@ -159,10 +159,7 @@ def _get_item_properties(item, fields): def _rule_action_call(client, action, rule_type): rule_type = rule_type.replace('-', '_') - func_name = '{action}_qos_{rule_type}_rule'.format( - action=action, - rule_type=rule_type, - ) + func_name = f'{action}_qos_{rule_type}_rule' return getattr(client, func_name) @@ -311,7 +308,7 @@ def take_action(self, parsed_args): ) rule_type = _find_rule_type(qos, rule_id) if not rule_type: - raise Exception('Rule %s not found' % rule_id) + raise Exception(f'Rule {rule_id} not found') _rule_action_call(network_client, ACTION_DELETE, rule_type)( rule_id, qos.id ) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index b1e60df4e..84aef4775 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -274,13 +274,13 @@ def _prepare_filter_fixed_ips(client_manager, parsed_args): _subnet = client.find_subnet( subnet_name_id, ignore_missing=False ) - ips.append('subnet_id=%s' % _subnet.id) + ips.append(f'subnet_id={_subnet.id}') if 'ip-address' in ip_spec: - ips.append('ip_address=%s' % ip_spec['ip-address']) + ips.append('ip_address={}'.format(ip_spec['ip-address'])) if 'ip-substring' in ip_spec: - ips.append('ip_address_substr=%s' % ip_spec['ip-substring']) + ips.append('ip_address_substr={}'.format(ip_spec['ip-substring'])) return ips diff --git a/openstackclient/shell.py b/openstackclient/shell.py index 5c64cb7cb..df60d3c07 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -96,8 +96,9 @@ def _load_plugins(self): key=lambda s: list(map(int, s.split('.'))), ) self.log.warning( - "%s version %s is not in supported versions: %s" - % (api, version_opt, ', '.join(sorted_versions)) + "{} version {} is not in supported versions: {}".format( + api, version_opt, ', '.join(sorted_versions) + ) ) # Command groups deal only with major versions diff --git a/openstackclient/tests/functional/common/test_help.py b/openstackclient/tests/functional/common/test_help.py index f1aea8ee9..4ff365b6d 100644 --- a/openstackclient/tests/functional/common/test_help.py +++ b/openstackclient/tests/functional/common/test_help.py @@ -60,15 +60,9 @@ def test_server_commands_main_help(self): """Check server commands in main help message.""" raw_output = self.openstack('help') for command, description in self.SERVER_COMMANDS: - msg = 'Command: {} not found in help output:\n{}'.format( - command, - raw_output, - ) + msg = f'Command: {command} not found in help output:\n{raw_output}' self.assertIn(command, raw_output, msg) - msg = 'Description: {} not found in help output:\n{}'.format( - description, - raw_output, - ) + msg = f'Description: {description} not found in help output:\n{raw_output}' self.assertIn(description, raw_output, msg) def test_server_only_help(self): diff --git a/openstackclient/tests/functional/common/test_module.py b/openstackclient/tests/functional/common/test_module.py index 150668349..41486d1ff 100644 --- a/openstackclient/tests/functional/common/test_module.py +++ b/openstackclient/tests/functional/common/test_module.py @@ -59,7 +59,7 @@ def test_command_list_with_group(self): input_groups = ['volume', 'network', 'image', 'identity', 'compute.v2'] for each_input in input_groups: cmd_output = self.openstack( - 'command list --group %s' % each_input, + f'command list --group {each_input}', parse_output=True, ) group_names = [each.get('Command Group') for each in cmd_output] diff --git a/openstackclient/tests/functional/common/test_quota.py b/openstackclient/tests/functional/common/test_quota.py index 8aa93a929..677db5036 100644 --- a/openstackclient/tests/functional/common/test_quota.py +++ b/openstackclient/tests/functional/common/test_quota.py @@ -165,8 +165,7 @@ def test_quota_set_network(self): # That will ensure we have at least two networks in the system. for _ in range(2): self.openstack( - 'network create --project %s %s' - % (self.PROJECT_NAME, uuid.uuid4().hex) + f'network create --project {self.PROJECT_NAME} {uuid.uuid4().hex}' ) self.assertRaises( @@ -211,8 +210,7 @@ def test_quota_set_network_with_force(self): # That will ensure we have at least two networks in the system. for _ in range(2): self.openstack( - 'network create --project %s %s' - % (self.PROJECT_NAME, uuid.uuid4().hex) + f'network create --project {self.PROJECT_NAME} {uuid.uuid4().hex}' ) self.openstack('quota set --networks 1 --force ' + self.PROJECT_NAME) diff --git a/openstackclient/tests/functional/compute/v2/common.py b/openstackclient/tests/functional/compute/v2/common.py index ef59d458a..4ada4304c 100644 --- a/openstackclient/tests/functional/compute/v2/common.py +++ b/openstackclient/tests/functional/compute/v2/common.py @@ -132,9 +132,7 @@ def wait_for_status( print(f'Server {name} now has status {status}') break print( - 'Server {}: Waiting for {}, current status: {}'.format( - name, expected_status, status - ) + f'Server {name}: Waiting for {expected_status}, current status: {status}' ) self.assertNotIn(status, failures) time.sleep(interval) diff --git a/openstackclient/tests/functional/compute/v2/test_hypervisor.py b/openstackclient/tests/functional/compute/v2/test_hypervisor.py index 7e90b743c..0ed4e904b 100644 --- a/openstackclient/tests/functional/compute/v2/test_hypervisor.py +++ b/openstackclient/tests/functional/compute/v2/test_hypervisor.py @@ -37,8 +37,8 @@ def test_hypervisor_list(self): for i in ids1: cmd_output = json.loads( self.openstack( - "hypervisor show %s -f json " - " --os-compute-api-version 2.1" % (i) + f"hypervisor show {i} -f json " + " --os-compute-api-version 2.1" ) ) self.assertIsNotNone(cmd_output) @@ -47,6 +47,6 @@ def test_hypervisor_list(self): # Show test - latest microversion for i in ids2: cmd_output = json.loads( - self.openstack("hypervisor show %s -f json" % (i)) + self.openstack(f"hypervisor show {i} -f json") ) self.assertIsNotNone(cmd_output) diff --git a/openstackclient/tests/functional/compute/v2/test_keypair.py b/openstackclient/tests/functional/compute/v2/test_keypair.py index ebc78abda..4b178cb72 100644 --- a/openstackclient/tests/functional/compute/v2/test_keypair.py +++ b/openstackclient/tests/functional/compute/v2/test_keypair.py @@ -96,7 +96,7 @@ def test_keypair_create_public_key(self): f.flush() raw_output = self.openstack( - 'keypair create --public-key %s tmpkey' % f.name, + f'keypair create --public-key {f.name} tmpkey', ) self.addCleanup( self.openstack, @@ -113,7 +113,7 @@ def test_keypair_create_private_key(self): """ with tempfile.NamedTemporaryFile(mode='w+') as f: cmd_output = self.openstack( - 'keypair create --private-key %s tmpkey' % f.name, + f'keypair create --private-key {f.name} tmpkey', parse_output=True, ) self.addCleanup(self.openstack, 'keypair delete tmpkey') diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py index d6638d2f7..19531da47 100644 --- a/openstackclient/tests/functional/compute/v2/test_server.py +++ b/openstackclient/tests/functional/compute/v2/test_server.py @@ -111,7 +111,7 @@ def test_server_list_with_marker_and_deleted(self): ) except exceptions.CommandFailed as e: self.assertIn( - 'marker [%s] not found' % (name2), e.stderr.decode('utf-8') + f'marker [{name2}] not found', e.stderr.decode('utf-8') ) def test_server_list_with_changes_before(self): diff --git a/openstackclient/tests/functional/identity/v2/common.py b/openstackclient/tests/functional/identity/v2/common.py index a78f3b031..dd2e27193 100644 --- a/openstackclient/tests/functional/identity/v2/common.py +++ b/openstackclient/tests/functional/identity/v2/common.py @@ -67,13 +67,9 @@ def setUpClass(cls): cls.openstack( '--os-identity-api-version 2 ' 'project create ' - '--description %(description)s ' + f'--description {cls.project_description} ' '--enable ' - '%(name)s' - % { - 'description': cls.project_description, - 'name': cls.project_name, - } + f'{cls.project_name}' ) except tempest_exceptions.CommandFailed: # Good chance this is due to Identity v2 admin not being enabled @@ -87,7 +83,7 @@ def tearDownClass(cls): try: cls.openstack( '--os-identity-api-version 2 ' - 'project delete %s' % cls.project_name + f'project delete {cls.project_name}' ) finally: super().tearDownClass() @@ -111,14 +107,13 @@ def _create_dummy_project(self, add_clean_up=True): project_description = data_utils.rand_name('description') raw_output = self.openstack( 'project create ' - '--description %(description)s ' - '--enable %(name)s' - % {'description': project_description, 'name': project_name} + f'--description {project_description} ' + f'--enable {project_name}' ) project = self.parse_show_as_object(raw_output) if add_clean_up: self.addCleanup( - self.openstack, 'project delete %s' % project['id'] + self.openstack, 'project delete {}'.format(project['id']) ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.PROJECT_FIELDS) @@ -130,22 +125,18 @@ def _create_dummy_user(self, add_clean_up=True): email = data_utils.rand_name() + '@example.com' raw_output = self.openstack( 'user create ' - '--project %(project)s ' - '--password %(password)s ' - '--email %(email)s ' + f'--project {self.project_name} ' + f'--password {password} ' + f'--email {email} ' '--enable ' - '%(name)s' - % { - 'project': self.project_name, - 'email': email, - 'password': password, - 'name': username, - } + f'{username}' ) if add_clean_up: self.addCleanup( self.openstack, - 'user delete %s' % self.parse_show_as_object(raw_output)['id'], + 'user delete {}'.format( + self.parse_show_as_object(raw_output)['id'] + ), ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.USER_FIELDS) @@ -153,10 +144,12 @@ def _create_dummy_user(self, add_clean_up=True): def _create_dummy_role(self, add_clean_up=True): role_name = data_utils.rand_name('TestRole') - raw_output = self.openstack('role create %s' % role_name) + raw_output = self.openstack(f'role create {role_name}') role = self.parse_show_as_object(raw_output) if add_clean_up: - self.addCleanup(self.openstack, 'role delete %s' % role['id']) + self.addCleanup( + self.openstack, 'role delete {}'.format(role['id']) + ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) self.assertEqual(role_name, role['name']) @@ -168,7 +161,7 @@ def _create_dummy_ec2_credentials(self, add_clean_up=True): access_key = ec2_credentials['access'] if add_clean_up: self.addCleanup( - self.openstack, 'ec2 credentials delete %s' % access_key + self.openstack, f'ec2 credentials delete {access_key}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.EC2_CREDENTIALS_FIELDS) @@ -178,7 +171,9 @@ def _create_dummy_token(self, add_clean_up=True): raw_output = self.openstack('token issue') token = self.parse_show_as_object(raw_output) if add_clean_up: - self.addCleanup(self.openstack, 'token revoke %s' % token['id']) + self.addCleanup( + self.openstack, 'token revoke {}'.format(token['id']) + ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.TOKEN_FIELDS) return token['id'] @@ -189,19 +184,14 @@ def _create_dummy_service(self, add_clean_up=True): type_name = data_utils.rand_name('TestType') raw_output = self.openstack( 'service create ' - '--name %(name)s ' - '--description %(description)s ' - '%(type)s' - % { - 'name': service_name, - 'description': description, - 'type': type_name, - } + f'--name {service_name} ' + f'--description {description} ' + f'{type_name}' ) if add_clean_up: service = self.parse_show_as_object(raw_output) self.addCleanup( - self.openstack, 'service delete %s' % service['id'] + self.openstack, 'service delete {}'.format(service['id']) ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.SERVICE_FIELDS) @@ -215,23 +205,16 @@ def _create_dummy_endpoint(self, add_clean_up=True): internal_url = data_utils.rand_url() raw_output = self.openstack( 'endpoint create ' - '--publicurl %(publicurl)s ' - '--adminurl %(adminurl)s ' - '--internalurl %(internalurl)s ' - '--region %(region)s ' - '%(service)s' - % { - 'publicurl': public_url, - 'adminurl': admin_url, - 'internalurl': internal_url, - 'region': region_id, - 'service': service_name, - } + f'--publicurl {public_url} ' + f'--adminurl {admin_url} ' + f'--internalurl {internal_url} ' + f'--region {region_id} ' + f'{service_name}' ) endpoint = self.parse_show_as_object(raw_output) if add_clean_up: self.addCleanup( - self.openstack, 'endpoint delete %s' % endpoint['id'] + self.openstack, 'endpoint delete {}'.format(endpoint['id']) ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.ENDPOINT_FIELDS) diff --git a/openstackclient/tests/functional/identity/v2/test_catalog.py b/openstackclient/tests/functional/identity/v2/test_catalog.py index 1cef314f7..67c6c3694 100644 --- a/openstackclient/tests/functional/identity/v2/test_catalog.py +++ b/openstackclient/tests/functional/identity/v2/test_catalog.py @@ -35,7 +35,7 @@ def test_catalog_show(self): | type | identity | +-----------+-------------------------------------------+ """ - raw_output = self.openstack('catalog show %s' % 'identity') + raw_output = self.openstack('catalog show {}'.format('identity')) items = self.parse_show(raw_output) # items may have multiple endpoint urls with empty key self.assert_show_fields(items, ['endpoints', 'name', 'type', '']) diff --git a/openstackclient/tests/functional/identity/v2/test_ec2_credentials.py b/openstackclient/tests/functional/identity/v2/test_ec2_credentials.py index 6d25bdae2..b72dc4014 100644 --- a/openstackclient/tests/functional/identity/v2/test_ec2_credentials.py +++ b/openstackclient/tests/functional/identity/v2/test_ec2_credentials.py @@ -20,7 +20,7 @@ def test_ec2_credentials_create(self): def test_ec2_credentials_delete(self): access_key = self._create_dummy_ec2_credentials(add_clean_up=False) raw_output = self.openstack( - 'ec2 credentials delete %s' % access_key, + f'ec2 credentials delete {access_key}', ) self.assertEqual(0, len(raw_output)) @@ -41,7 +41,7 @@ def test_ec2_credentials_list(self): def test_ec2_credentials_show(self): access_key = self._create_dummy_ec2_credentials() show_output = self.openstack( - 'ec2 credentials show %s' % access_key, + f'ec2 credentials show {access_key}', ) items = self.parse_show(show_output) self.assert_show_fields(items, self.EC2_CREDENTIALS_FIELDS) diff --git a/openstackclient/tests/functional/identity/v2/test_endpoint.py b/openstackclient/tests/functional/identity/v2/test_endpoint.py index bafbfdaed..8a0077b7f 100644 --- a/openstackclient/tests/functional/identity/v2/test_endpoint.py +++ b/openstackclient/tests/functional/identity/v2/test_endpoint.py @@ -19,7 +19,7 @@ def test_endpoint_create(self): def test_endpoint_delete(self): endpoint_id = self._create_dummy_endpoint(add_clean_up=False) - raw_output = self.openstack('endpoint delete %s' % endpoint_id) + raw_output = self.openstack(f'endpoint delete {endpoint_id}') self.assertEqual(0, len(raw_output)) def test_endpoint_multi_delete(self): @@ -39,6 +39,6 @@ def test_endpoint_list(self): def test_endpoint_show(self): endpoint_id = self._create_dummy_endpoint() - raw_output = self.openstack('endpoint show %s' % endpoint_id) + raw_output = self.openstack(f'endpoint show {endpoint_id}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.ENDPOINT_FIELDS) diff --git a/openstackclient/tests/functional/identity/v2/test_project.py b/openstackclient/tests/functional/identity/v2/test_project.py index 1ad0edaa3..2dfc95edb 100644 --- a/openstackclient/tests/functional/identity/v2/test_project.py +++ b/openstackclient/tests/functional/identity/v2/test_project.py @@ -21,13 +21,13 @@ def test_project_create(self): description = data_utils.rand_name('description') raw_output = self.openstack( 'project create ' - '--description %(description)s ' + f'--description {description} ' '--enable ' '--property k1=v1 ' '--property k2=v2 ' - '%(name)s' % {'description': description, 'name': project_name} + f'{project_name}' ) - self.addCleanup(self.openstack, 'project delete %s' % project_name) + self.addCleanup(self.openstack, f'project delete {project_name}') items = self.parse_show(raw_output) show_fields = list(self.PROJECT_FIELDS) show_fields.extend(['k1', 'k2']) @@ -38,7 +38,7 @@ def test_project_create(self): def test_project_delete(self): project_name = self._create_dummy_project(add_clean_up=False) - raw_output = self.openstack('project delete %s' % project_name) + raw_output = self.openstack(f'project delete {project_name}') self.assertEqual(0, len(raw_output)) def test_project_list(self): @@ -51,14 +51,14 @@ def test_project_set(self): new_project_name = data_utils.rand_name('NewTestProject') raw_output = self.openstack( 'project set ' - '--name %(new_name)s ' + f'--name {new_project_name} ' '--disable ' '--property k0=v0 ' - '%(name)s' % {'new_name': new_project_name, 'name': project_name} + f'{project_name}' ) self.assertEqual(0, len(raw_output)) # check project details - raw_output = self.openstack('project show %s' % new_project_name) + raw_output = self.openstack(f'project show {new_project_name}') items = self.parse_show(raw_output) fields = list(self.PROJECT_FIELDS) fields.extend(['properties']) @@ -70,7 +70,7 @@ def test_project_set(self): def test_project_show(self): project_name = self._create_dummy_project() - raw_output = self.openstack('project show %s' % project_name) + raw_output = self.openstack(f'project show {project_name}') items = self.parse_show(raw_output) fields = list(self.PROJECT_FIELDS) fields.extend(['properties']) diff --git a/openstackclient/tests/functional/identity/v2/test_role.py b/openstackclient/tests/functional/identity/v2/test_role.py index fb22f0181..ec6134012 100644 --- a/openstackclient/tests/functional/identity/v2/test_role.py +++ b/openstackclient/tests/functional/identity/v2/test_role.py @@ -19,7 +19,7 @@ def test_role_create(self): def test_role_delete(self): role_name = self._create_dummy_role(add_clean_up=False) - raw_output = self.openstack('role delete %s' % role_name) + raw_output = self.openstack(f'role delete {role_name}') self.assertEqual(0, len(raw_output)) def test_role_list(self): @@ -30,7 +30,7 @@ def test_role_list(self): def test_role_show(self): role_name = self._create_dummy_role() - raw_output = self.openstack('role show %s' % role_name) + raw_output = self.openstack(f'role show {role_name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) @@ -39,26 +39,16 @@ def test_role_add(self): username = self._create_dummy_user() raw_output = self.openstack( 'role add ' - '--project %(project)s ' - '--user %(user)s ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--user {username} ' + f'{role_name}' ) self.addCleanup( self.openstack, 'role remove ' - '--project %(project)s ' - '--user %(user)s ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - }, + f'--project {self.project_name} ' + f'--user {username} ' + f'{role_name}', ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) @@ -68,25 +58,15 @@ def test_role_remove(self): username = self._create_dummy_user() add_raw_output = self.openstack( 'role add ' - '--project %(project)s ' - '--user %(user)s ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--user {username} ' + f'{role_name}' ) del_raw_output = self.openstack( 'role remove ' - '--project %(project)s ' - '--user %(user)s ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--user {username} ' + f'{role_name}' ) items = self.parse_show(add_raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) diff --git a/openstackclient/tests/functional/identity/v2/test_service.py b/openstackclient/tests/functional/identity/v2/test_service.py index 7afa967d4..5f1611caf 100644 --- a/openstackclient/tests/functional/identity/v2/test_service.py +++ b/openstackclient/tests/functional/identity/v2/test_service.py @@ -19,7 +19,7 @@ def test_service_create(self): def test_service_delete(self): service_name = self._create_dummy_service(add_clean_up=False) - raw_output = self.openstack('service delete %s' % service_name) + raw_output = self.openstack(f'service delete {service_name}') self.assertEqual(0, len(raw_output)) def test_service_multi_delete(self): @@ -38,6 +38,6 @@ def test_service_list(self): def test_service_show(self): service_name = self._create_dummy_service() - raw_output = self.openstack('service show %s' % service_name) + raw_output = self.openstack(f'service show {service_name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.SERVICE_FIELDS) diff --git a/openstackclient/tests/functional/identity/v2/test_token.py b/openstackclient/tests/functional/identity/v2/test_token.py index d2ef78e38..51be24319 100644 --- a/openstackclient/tests/functional/identity/v2/test_token.py +++ b/openstackclient/tests/functional/identity/v2/test_token.py @@ -19,5 +19,5 @@ def test_token_issue(self): def test_token_revoke(self): token_id = self._create_dummy_token(add_clean_up=False) - raw_output = self.openstack('token revoke %s' % token_id) + raw_output = self.openstack(f'token revoke {token_id}') self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v2/test_user.py b/openstackclient/tests/functional/identity/v2/test_user.py index d30e3c456..e0d2c956a 100644 --- a/openstackclient/tests/functional/identity/v2/test_user.py +++ b/openstackclient/tests/functional/identity/v2/test_user.py @@ -22,7 +22,7 @@ def test_user_create(self): def test_user_delete(self): username = self._create_dummy_user(add_clean_up=False) - raw_output = self.openstack('user delete %s' % username) + raw_output = self.openstack(f'user delete {username}') self.assertEqual(0, len(raw_output)) def test_user_list(self): @@ -32,26 +32,24 @@ def test_user_list(self): def test_user_set(self): username = self._create_dummy_user() - raw_output = self.openstack('user show %s' % username) + raw_output = self.openstack(f'user show {username}') user = self.parse_show_as_object(raw_output) new_username = data_utils.rand_name('NewTestUser') new_email = data_utils.rand_name() + '@example.com' raw_output = self.openstack( - 'user set ' - '--email %(email)s ' - '--name %(new_name)s ' - '%(id)s' - % {'email': new_email, 'new_name': new_username, 'id': user['id']} + 'user set ' '--email {email} ' '--name {new_name} ' '{id}'.format( + email=new_email, new_name=new_username, id=user['id'] + ) ) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack('user show %s' % new_username) + raw_output = self.openstack(f'user show {new_username}') new_user = self.parse_show_as_object(raw_output) self.assertEqual(user['id'], new_user['id']) self.assertEqual(new_email, new_user['email']) def test_user_show(self): username = self._create_dummy_user() - raw_output = self.openstack('user show %s' % username) + raw_output = self.openstack(f'user show {username}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.USER_FIELDS) diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py index e3c32e35b..e44f291e5 100644 --- a/openstackclient/tests/functional/identity/v3/common.py +++ b/openstackclient/tests/functional/identity/v3/common.py @@ -156,10 +156,9 @@ def setUpClass(cls): cls.openstack( '--os-identity-api-version 3 ' 'domain create ' - '--description %(description)s ' + f'--description {cls.domain_description} ' '--enable ' - '%(name)s' - % {'description': cls.domain_description, 'name': cls.domain_name} + f'{cls.domain_name}' ) # create dummy project @@ -168,15 +167,10 @@ def setUpClass(cls): cls.openstack( '--os-identity-api-version 3 ' 'project create ' - '--domain %(domain)s ' - '--description %(description)s ' + f'--domain {cls.domain_name} ' + f'--description {cls.project_description} ' '--enable ' - '%(name)s' - % { - 'domain': cls.domain_name, - 'description': cls.project_description, - 'name': cls.project_name, - } + f'{cls.project_name}' ) @classmethod @@ -185,16 +179,16 @@ def tearDownClass(cls): # delete dummy project cls.openstack( '--os-identity-api-version 3 ' - 'project delete %s' % cls.project_name + f'project delete {cls.project_name}' ) # disable and delete dummy domain cls.openstack( '--os-identity-api-version 3 ' - 'domain set --disable %s' % cls.domain_name + f'domain set --disable {cls.domain_name}' ) cls.openstack( '--os-identity-api-version 3 ' - 'domain delete %s' % cls.domain_name + f'domain delete {cls.domain_name}' ) finally: super().tearDownClass() @@ -220,28 +214,21 @@ def _create_dummy_user(self, add_clean_up=True): description = data_utils.rand_name('description') raw_output = self.openstack( 'user create ' - '--domain %(domain)s ' - '--project %(project)s ' - '--project-domain %(project_domain)s ' - '--password %(password)s ' - '--email %(email)s ' - '--description %(description)s ' + f'--domain {self.domain_name} ' + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--password {password} ' + f'--email {email} ' + f'--description {description} ' '--enable ' - '%(name)s' - % { - 'domain': self.domain_name, - 'project': self.project_name, - 'project_domain': self.domain_name, - 'email': email, - 'password': password, - 'description': description, - 'name': username, - } + f'{username}' ) if add_clean_up: self.addCleanup( self.openstack, - 'user delete %s' % self.parse_show_as_object(raw_output)['id'], + 'user delete {}'.format( + self.parse_show_as_object(raw_output)['id'] + ), ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.USER_FIELDS) @@ -249,10 +236,12 @@ def _create_dummy_user(self, add_clean_up=True): def _create_dummy_role(self, add_clean_up=True): role_name = data_utils.rand_name('TestRole') - raw_output = self.openstack('role create %s' % role_name) + raw_output = self.openstack(f'role create {role_name}') role = self.parse_show_as_object(raw_output) if add_clean_up: - self.addCleanup(self.openstack, 'role delete %s' % role['id']) + self.addCleanup( + self.openstack, 'role delete {}'.format(role['id']) + ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) self.assertEqual(role_name, role['name']) @@ -263,8 +252,8 @@ def _create_dummy_implied_role(self, add_clean_up=True): implied_role_name = self._create_dummy_role(add_clean_up) self.openstack( 'implied role create ' - '--implied-role %(implied_role)s ' - '%(role)s' % {'implied_role': implied_role_name, 'role': role_name} + f'--implied-role {implied_role_name} ' + f'{role_name}' ) return implied_role_name, role_name @@ -274,21 +263,16 @@ def _create_dummy_group(self, add_clean_up=True): description = data_utils.rand_name('description') raw_output = self.openstack( 'group create ' - '--domain %(domain)s ' - '--description %(description)s ' - '%(name)s' - % { - 'domain': self.domain_name, - 'description': description, - 'name': group_name, - } + f'--domain {self.domain_name} ' + f'--description {description} ' + f'{group_name}' ) if add_clean_up: self.addCleanup( self.openstack, 'group delete ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': group_name}, + f'--domain {self.domain_name} ' + f'{group_name}', ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.GROUP_FIELDS) @@ -299,14 +283,13 @@ def _create_dummy_domain(self, add_clean_up=True): domain_description = data_utils.rand_name('description') self.openstack( 'domain create ' - '--description %(description)s ' - '--enable %(name)s' - % {'description': domain_description, 'name': domain_name} + f'--description {domain_description} ' + f'--enable {domain_name}' ) if add_clean_up: - self.addCleanup(self.openstack, 'domain delete %s' % domain_name) + self.addCleanup(self.openstack, f'domain delete {domain_name}') self.addCleanup( - self.openstack, 'domain set --disable %s' % domain_name + self.openstack, f'domain set --disable {domain_name}' ) return domain_name @@ -315,22 +298,16 @@ def _create_dummy_project(self, add_clean_up=True): project_description = data_utils.rand_name('description') self.openstack( 'project create ' - '--domain %(domain)s ' - '--description %(description)s ' - '--enable %(name)s' - % { - 'domain': self.domain_name, - 'description': project_description, - 'name': project_name, - } + f'--domain {self.domain_name} ' + f'--description {project_description} ' + f'--enable {project_name}' ) if add_clean_up: self.addCleanup( self.openstack, 'project delete ' - '--domain %(domain)s ' - '%(name)s' - % {'domain': self.domain_name, 'name': project_name}, + f'--domain {self.domain_name} ' + f'{project_name}', ) return project_name @@ -339,20 +316,15 @@ def _create_dummy_region(self, parent_region=None, add_clean_up=True): description = data_utils.rand_name('description') parent_region_arg = '' if parent_region is not None: - parent_region_arg = '--parent-region %s' % parent_region + parent_region_arg = f'--parent-region {parent_region}' raw_output = self.openstack( 'region create ' - '%(parent_region_arg)s ' - '--description %(description)s ' - '%(id)s' - % { - 'parent_region_arg': parent_region_arg, - 'description': description, - 'id': region_id, - } + f'{parent_region_arg} ' + f'--description {description} ' + f'{region_id}' ) if add_clean_up: - self.addCleanup(self.openstack, 'region delete %s' % region_id) + self.addCleanup(self.openstack, f'region delete {region_id}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.REGION_FIELDS) return region_id @@ -363,20 +335,15 @@ def _create_dummy_service(self, add_clean_up=True): type_name = data_utils.rand_name('TestType') raw_output = self.openstack( 'service create ' - '--name %(name)s ' - '--description %(description)s ' + f'--name {service_name} ' + f'--description {description} ' '--enable ' - '%(type)s' - % { - 'name': service_name, - 'description': description, - 'type': type_name, - } + f'{type_name}' ) if add_clean_up: service = self.parse_show_as_object(raw_output) self.addCleanup( - self.openstack, 'service delete %s' % service['id'] + self.openstack, 'service delete {}'.format(service['id']) ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.SERVICE_FIELDS) @@ -388,22 +355,16 @@ def _create_dummy_endpoint(self, interface='public', add_clean_up=True): endpoint_url = data_utils.rand_url() raw_output = self.openstack( 'endpoint create ' - '--region %(region)s ' + f'--region {region_id} ' '--enable ' - '%(service)s ' - '%(interface)s ' - '%(url)s' - % { - 'region': region_id, - 'service': service_name, - 'interface': interface, - 'url': endpoint_url, - } + f'{service_name} ' + f'{interface} ' + f'{endpoint_url}' ) endpoint = self.parse_show_as_object(raw_output) if add_clean_up: self.addCleanup( - self.openstack, 'endpoint delete %s' % endpoint['id'] + self.openstack, 'endpoint delete {}'.format(endpoint['id']) ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.ENDPOINT_FIELDS) @@ -414,15 +375,14 @@ def _create_dummy_idp(self, add_clean_up=True): description = data_utils.rand_name('description') raw_output = self.openstack( 'identity provider create ' - ' %(name)s ' - '--description %(description)s ' + f' {identity_provider} ' + f'--description {description} ' '--enable ' - % {'name': identity_provider, 'description': description} ) if add_clean_up: self.addCleanup( self.openstack, - 'identity provider delete %s' % identity_provider, + f'identity provider delete {identity_provider}', ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.IDENTITY_PROVIDER_FIELDS) @@ -433,16 +393,15 @@ def _create_dummy_sp(self, add_clean_up=True): description = data_utils.rand_name('description') raw_output = self.openstack( 'service provider create ' - ' %(name)s ' - '--description %(description)s ' + f' {service_provider} ' + f'--description {description} ' '--auth-url https://sp.example.com:35357 ' '--service-provider-url https://sp.example.com:5000 ' '--enable ' - % {'name': service_provider, 'description': description} ) if add_clean_up: self.addCleanup( - self.openstack, 'service provider delete %s' % service_provider + self.openstack, f'service provider delete {service_provider}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.SERVICE_PROVIDER_FIELDS) @@ -458,9 +417,9 @@ def _create_dummy_registered_limit(self, add_clean_up=True): } raw_output = self.openstack( 'registered limit create' - ' --service %(service_name)s' - ' --default-limit %(default_limit)s' - ' %(resource_name)s' % params, + ' --service {service_name}' + ' --default-limit {default_limit}' + ' {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -469,7 +428,7 @@ def _create_dummy_registered_limit(self, add_clean_up=True): if add_clean_up: self.addCleanup( self.openstack, - 'registered limit delete %s' % registered_limit_id, + f'registered limit delete {registered_limit_id}', cloud=SYSTEM_CLOUD, ) @@ -486,7 +445,7 @@ def _create_dummy_limit(self, add_clean_up=True): registered_limit_id = self._create_dummy_registered_limit() raw_output = self.openstack( - 'registered limit show %s' % registered_limit_id, + f'registered limit show {registered_limit_id}', cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -495,7 +454,7 @@ def _create_dummy_limit(self, add_clean_up=True): resource_limit = 15 project_name = self._create_dummy_project() - raw_output = self.openstack('project show %s' % project_name) + raw_output = self.openstack(f'project show {project_name}') items = self.parse_show(raw_output) project_id = self._extract_value_from_items('id', items) @@ -508,10 +467,10 @@ def _create_dummy_limit(self, add_clean_up=True): raw_output = self.openstack( 'limit create' - ' --project %(project_id)s' - ' --service %(service_id)s' - ' --resource-limit %(resource_limit)s' - ' %(resource_name)s' % params, + ' --project {project_id}' + ' --service {service_id}' + ' --resource-limit {resource_limit}' + ' {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -520,7 +479,7 @@ def _create_dummy_limit(self, add_clean_up=True): if add_clean_up: self.addCleanup( self.openstack, - 'limit delete %s' % limit_id, + f'limit delete {limit_id}', cloud=SYSTEM_CLOUD, ) diff --git a/openstackclient/tests/functional/identity/v3/test_application_credential.py b/openstackclient/tests/functional/identity/v3/test_application_credential.py index 22f2b90bb..441259278 100644 --- a/openstackclient/tests/functional/identity/v3/test_application_credential.py +++ b/openstackclient/tests/functional/identity/v3/test_application_credential.py @@ -85,34 +85,20 @@ def _create_role_assignments(self): for role in role1, role2: self.openstack( 'role add' - ' --user %(user)s' - ' --user-domain %(user_domain)s' - ' --project %(project)s' - ' --project-domain %(project_domain)s' - ' %(role)s' - % { - 'user': user, - 'user_domain': user_domain, - 'project': project, - 'project_domain': project_domain, - 'role': role, - } + f' --user {user}' + f' --user-domain {user_domain}' + f' --project {project}' + f' --project-domain {project_domain}' + f' {role}' ) self.addCleanup( self.openstack, 'role remove' - ' --user %(user)s' - ' --user-domain %(user_domain)s' - ' --project %(project)s' - ' --project-domain %(project_domain)s' - ' %(role)s' - % { - 'user': user, - 'user_domain': user_domain, - 'project': project, - 'project_domain': project_domain, - 'role': role, - }, + f' --user {user}' + f' --user-domain {user_domain}' + f' --project {project}' + f' --project-domain {project_domain}' + f' {role}', ) return role1, role2 @@ -125,21 +111,13 @@ def test_application_credential_create_with_options(self): ).strftime('%Y-%m-%dT%H:%M:%S%z') role1, role2 = self._create_role_assignments() raw_output = self.openstack( - 'application credential create %(name)s' - ' --secret %(secret)s' - ' --description %(description)s' - ' --expiration %(tomorrow)s' - ' --role %(role1)s' - ' --role %(role2)s' + f'application credential create {name}' + f' --secret {secret}' + f' --description {description}' + f' --expiration {tomorrow}' + f' --role {role1}' + f' --role {role2}' ' --unrestricted' - % { - 'name': name, - 'secret': secret, - 'description': description, - 'tomorrow': tomorrow, - 'role1': role1, - 'role2': role2, - } ) self.addCleanup( self.openstack, @@ -151,9 +129,7 @@ def test_application_credential_create_with_options(self): def test_application_credential_delete(self): name = data_utils.rand_name('name') self.openstack(f'application credential create {name}') - raw_output = self.openstack( - 'application credential delete ' '%(name)s' % {'name': name} - ) + raw_output = self.openstack('application credential delete ' f'{name}') self.assertEqual(0, len(raw_output)) def test_application_credential_list(self): @@ -170,8 +146,6 @@ def test_application_credential_show(self): self.openstack, f'application credential delete {name}', ) - raw_output = self.openstack( - 'application credential show ' '%(name)s' % {'name': name} - ) + raw_output = self.openstack('application credential show ' f'{name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.APPLICATION_CREDENTIAL_FIELDS) diff --git a/openstackclient/tests/functional/identity/v3/test_catalog.py b/openstackclient/tests/functional/identity/v3/test_catalog.py index 9b6df2d19..f4089b7eb 100644 --- a/openstackclient/tests/functional/identity/v3/test_catalog.py +++ b/openstackclient/tests/functional/identity/v3/test_catalog.py @@ -38,7 +38,7 @@ def test_catalog_show(self): | type | identity | +-----------+----------------------------------------+ """ - raw_output = self.openstack('catalog show %s' % 'identity') + raw_output = self.openstack('catalog show {}'.format('identity')) items = self.parse_show(raw_output) # items may have multiple endpoint urls with empty key self.assert_show_fields(items, ['endpoints', 'name', 'type', '', 'id']) diff --git a/openstackclient/tests/functional/identity/v3/test_domain.py b/openstackclient/tests/functional/identity/v3/test_domain.py index 08e0e0790..867db91df 100644 --- a/openstackclient/tests/functional/identity/v3/test_domain.py +++ b/openstackclient/tests/functional/identity/v3/test_domain.py @@ -19,12 +19,10 @@ class DomainTests(common.IdentityTests): def test_domain_create(self): domain_name = data_utils.rand_name('TestDomain') - raw_output = self.openstack('domain create %s' % domain_name) + raw_output = self.openstack(f'domain create {domain_name}') # disable domain first before deleting it - self.addCleanup(self.openstack, 'domain delete %s' % domain_name) - self.addCleanup( - self.openstack, 'domain set --disable %s' % domain_name - ) + self.addCleanup(self.openstack, f'domain delete {domain_name}') + self.addCleanup(self.openstack, f'domain set --disable {domain_name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.DOMAIN_FIELDS) @@ -37,18 +35,18 @@ def test_domain_list(self): def test_domain_delete(self): domain_name = self._create_dummy_domain(add_clean_up=False) # cannot delete enabled domain, disable it first - raw_output = self.openstack('domain set --disable %s' % domain_name) + raw_output = self.openstack(f'domain set --disable {domain_name}') self.assertEqual(0, len(raw_output)) - raw_output = self.openstack('domain delete %s' % domain_name) + raw_output = self.openstack(f'domain delete {domain_name}') self.assertEqual(0, len(raw_output)) def test_domain_multi_delete(self): domain_1 = self._create_dummy_domain(add_clean_up=False) domain_2 = self._create_dummy_domain(add_clean_up=False) # cannot delete enabled domain, disable it first - raw_output = self.openstack('domain set --disable %s' % domain_1) + raw_output = self.openstack(f'domain set --disable {domain_1}') self.assertEqual(0, len(raw_output)) - raw_output = self.openstack('domain set --disable %s' % domain_2) + raw_output = self.openstack(f'domain set --disable {domain_2}') self.assertEqual(0, len(raw_output)) raw_output = self.openstack(f'domain delete {domain_1} {domain_2}') self.assertEqual(0, len(raw_output)) @@ -59,11 +57,11 @@ def test_domain_delete_failure(self): self.assertRaises( exceptions.CommandFailed, self.openstack, - 'domain delete %s' % domain_name, + f'domain delete {domain_name}', ) def test_domain_show(self): domain_name = self._create_dummy_domain() - raw_output = self.openstack('domain show %s' % domain_name) + raw_output = self.openstack(f'domain show {domain_name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.DOMAIN_FIELDS) diff --git a/openstackclient/tests/functional/identity/v3/test_endpoint.py b/openstackclient/tests/functional/identity/v3/test_endpoint.py index 1fff6abeb..cafab670a 100644 --- a/openstackclient/tests/functional/identity/v3/test_endpoint.py +++ b/openstackclient/tests/functional/identity/v3/test_endpoint.py @@ -23,7 +23,7 @@ def test_endpoint_create(self): def test_endpoint_delete(self): endpoint_id = self._create_dummy_endpoint(add_clean_up=False) - raw_output = self.openstack('endpoint delete %s' % endpoint_id) + raw_output = self.openstack(f'endpoint delete {endpoint_id}') self.assertEqual(0, len(raw_output)) def test_endpoint_multi_delete(self): @@ -45,20 +45,15 @@ def test_endpoint_list_filter(self): endpoint_id = self._create_dummy_endpoint(add_clean_up=False) project_id = self._create_dummy_project(add_clean_up=False) raw_output = self.openstack( - 'endpoint add project ' - '%(endpoint_id)s ' - '%(project_id)s' - % {'project_id': project_id, 'endpoint_id': endpoint_id} + 'endpoint add project ' f'{endpoint_id} ' f'{project_id}' ) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack( - 'endpoint list --endpoint %s' % endpoint_id - ) + raw_output = self.openstack(f'endpoint list --endpoint {endpoint_id}') self.assertIn(project_id, raw_output) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ENDPOINT_LIST_PROJECT_HEADERS) - raw_output = self.openstack('endpoint list --project %s' % project_id) + raw_output = self.openstack(f'endpoint list --project {project_id}') self.assertIn(endpoint_id, raw_output) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ENDPOINT_LIST_HEADERS) @@ -68,18 +63,17 @@ def test_endpoint_set(self): new_endpoint_url = data_utils.rand_url() raw_output = self.openstack( 'endpoint set ' - '--interface %(interface)s ' - '--url %(url)s ' + '--interface {interface} ' + '--url {url} ' '--disable ' - '%(endpoint_id)s' - % { - 'interface': 'admin', - 'url': new_endpoint_url, - 'endpoint_id': endpoint_id, - } + '{endpoint_id}'.format( + interface='admin', + url=new_endpoint_url, + endpoint_id=endpoint_id, + ) ) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack('endpoint show %s' % endpoint_id) + raw_output = self.openstack(f'endpoint show {endpoint_id}') endpoint = self.parse_show_as_object(raw_output) self.assertEqual('admin', endpoint['interface']) self.assertEqual(new_endpoint_url, endpoint['url']) @@ -87,7 +81,7 @@ def test_endpoint_set(self): def test_endpoint_show(self): endpoint_id = self._create_dummy_endpoint() - raw_output = self.openstack('endpoint show %s' % endpoint_id) + raw_output = self.openstack(f'endpoint show {endpoint_id}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.ENDPOINT_FIELDS) @@ -95,17 +89,11 @@ def test_endpoint_add_remove_project(self): endpoint_id = self._create_dummy_endpoint(add_clean_up=False) project_id = self._create_dummy_project(add_clean_up=False) raw_output = self.openstack( - 'endpoint add project ' - '%(endpoint_id)s ' - '%(project_id)s' - % {'project_id': project_id, 'endpoint_id': endpoint_id} + 'endpoint add project ' f'{endpoint_id} ' f'{project_id}' ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'endpoint remove project ' - '%(endpoint_id)s ' - '%(project_id)s' - % {'project_id': project_id, 'endpoint_id': endpoint_id} + 'endpoint remove project ' f'{endpoint_id} ' f'{project_id}' ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_group.py b/openstackclient/tests/functional/identity/v3/test_group.py index 95d74cb76..5fc1ed39c 100644 --- a/openstackclient/tests/functional/identity/v3/test_group.py +++ b/openstackclient/tests/functional/identity/v3/test_group.py @@ -28,9 +28,7 @@ def test_group_list(self): def test_group_list_with_domain(self): group_name = self._create_dummy_group() - raw_output = self.openstack( - 'group list --domain %s' % self.domain_name - ) + raw_output = self.openstack(f'group list --domain {self.domain_name}') items = self.parse_listing(raw_output) self.assert_table_structure(items, common.BASIC_LIST_HEADERS) self.assertIn(group_name, raw_output) @@ -38,18 +36,14 @@ def test_group_list_with_domain(self): def test_group_delete(self): group_name = self._create_dummy_group(add_clean_up=False) raw_output = self.openstack( - 'group delete ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': group_name} + 'group delete ' f'--domain {self.domain_name} ' f'{group_name}' ) self.assertEqual(0, len(raw_output)) def test_group_show(self): group_name = self._create_dummy_group() raw_output = self.openstack( - 'group show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': group_name} + 'group show ' f'--domain {self.domain_name} ' f'{group_name}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.GROUP_FIELDS) @@ -59,34 +53,22 @@ def test_group_set(self): new_group_name = data_utils.rand_name('NewTestGroup') raw_output = self.openstack( 'group set ' - '--domain %(domain)s ' - '--name %(new_group)s ' - '%(group)s' - % { - 'domain': self.domain_name, - 'new_group': new_group_name, - 'group': group_name, - } + f'--domain {self.domain_name} ' + f'--name {new_group_name} ' + f'{group_name}' ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'group show ' - '--domain %(domain)s ' - '%(group)s' % {'domain': self.domain_name, 'group': new_group_name} + 'group show ' f'--domain {self.domain_name} ' f'{new_group_name}' ) group = self.parse_show_as_object(raw_output) self.assertEqual(new_group_name, group['name']) # reset group name to make sure it will be cleaned up raw_output = self.openstack( 'group set ' - '--domain %(domain)s ' - '--name %(new_group)s ' - '%(group)s' - % { - 'domain': self.domain_name, - 'new_group': group_name, - 'group': new_group_name, - } + f'--domain {self.domain_name} ' + f'--name {group_name} ' + f'{new_group_name}' ) self.assertEqual(0, len(raw_output)) @@ -95,28 +77,16 @@ def test_group_add_user(self): username = self._create_dummy_user() raw_output = self.openstack( 'group add user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - } + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}' ) self.addCleanup( self.openstack, 'group remove user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - }, + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}', ) self.assertOutput('', raw_output) @@ -125,45 +95,26 @@ def test_group_contains_user(self): username = self._create_dummy_user() raw_output = self.openstack( 'group add user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - } + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}' ) self.addCleanup( self.openstack, 'group remove user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - }, + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}', ) self.assertOutput('', raw_output) raw_output = self.openstack( 'group contains user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - } + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}' ) self.assertEqual( - '%(user)s in group %(group)s\n' - % {'user': username, 'group': group_name}, + f'{username} in group {group_name}\n', raw_output, ) @@ -172,27 +123,15 @@ def test_group_remove_user(self): username = self._create_dummy_user() add_raw_output = self.openstack( 'group add user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - } + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}' ) remove_raw_output = self.openstack( 'group remove user ' - '--group-domain %(group_domain)s ' - '--user-domain %(user_domain)s ' - '%(group)s %(user)s' - % { - 'group_domain': self.domain_name, - 'user_domain': self.domain_name, - 'group': group_name, - 'user': username, - } + f'--group-domain {self.domain_name} ' + f'--user-domain {self.domain_name} ' + f'{group_name} {username}' ) self.assertOutput('', add_raw_output) self.assertOutput('', remove_raw_output) diff --git a/openstackclient/tests/functional/identity/v3/test_idp.py b/openstackclient/tests/functional/identity/v3/test_idp.py index 05f3ee84e..11275795b 100644 --- a/openstackclient/tests/functional/identity/v3/test_idp.py +++ b/openstackclient/tests/functional/identity/v3/test_idp.py @@ -24,7 +24,7 @@ def test_idp_create(self): def test_idp_delete(self): identity_provider = self._create_dummy_idp(add_clean_up=False) raw_output = self.openstack( - 'identity provider delete %s' % identity_provider + f'identity provider delete {identity_provider}' ) self.assertEqual(0, len(raw_output)) @@ -39,7 +39,7 @@ def test_idp_multi_delete(self): def test_idp_show(self): identity_provider = self._create_dummy_idp(add_clean_up=True) raw_output = self.openstack( - 'identity provider show %s' % identity_provider + f'identity provider show {identity_provider}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.IDENTITY_PROVIDER_FIELDS) @@ -64,7 +64,7 @@ def test_idp_set(self): ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'identity provider show %s' % identity_provider + f'identity provider show {identity_provider}' ) updated_value = self.parse_show_as_object(raw_output) self.assertIn(new_remoteid, updated_value['remote_ids']) diff --git a/openstackclient/tests/functional/identity/v3/test_limit.py b/openstackclient/tests/functional/identity/v3/test_limit.py index 32c65107d..79213308d 100644 --- a/openstackclient/tests/functional/identity/v3/test_limit.py +++ b/openstackclient/tests/functional/identity/v3/test_limit.py @@ -23,19 +23,19 @@ class LimitTestCase(common.IdentityTests): def test_limit_create_with_service_name(self): registered_limit_id = self._create_dummy_registered_limit() raw_output = self.openstack( - 'registered limit show %s' % registered_limit_id, + f'registered limit show {registered_limit_id}', cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) service_id = self._extract_value_from_items('service_id', items) resource_name = self._extract_value_from_items('resource_name', items) - raw_output = self.openstack('service show %s' % service_id) + raw_output = self.openstack(f'service show {service_id}') items = self.parse_show(raw_output) service_name = self._extract_value_from_items('name', items) project_name = self._create_dummy_project() - raw_output = self.openstack('project show %s' % project_name) + raw_output = self.openstack(f'project show {project_name}') items = self.parse_show(raw_output) project_id = self._extract_value_from_items('id', items) @@ -47,16 +47,16 @@ def test_limit_create_with_service_name(self): } raw_output = self.openstack( 'limit create' - ' --project %(project_id)s' - ' --service %(service_name)s' - ' --resource-limit %(resource_limit)s' - ' %(resource_name)s' % params, + ' --project {project_id}' + ' --service {service_name}' + ' --resource-limit {resource_limit}' + ' {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) limit_id = self._extract_value_from_items('id', items) self.addCleanup( - self.openstack, 'limit delete %s' % limit_id, cloud=SYSTEM_CLOUD + self.openstack, f'limit delete {limit_id}', cloud=SYSTEM_CLOUD ) self.assert_show_fields(items, self.LIMIT_FIELDS) @@ -64,14 +64,14 @@ def test_limit_create_with_service_name(self): def test_limit_create_with_project_name(self): registered_limit_id = self._create_dummy_registered_limit() raw_output = self.openstack( - 'registered limit show %s' % registered_limit_id, + f'registered limit show {registered_limit_id}', cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) service_id = self._extract_value_from_items('service_id', items) resource_name = self._extract_value_from_items('resource_name', items) - raw_output = self.openstack('service show %s' % service_id) + raw_output = self.openstack(f'service show {service_id}') items = self.parse_show(raw_output) service_name = self._extract_value_from_items('name', items) @@ -85,16 +85,16 @@ def test_limit_create_with_project_name(self): } raw_output = self.openstack( 'limit create' - ' --project %(project_name)s' - ' --service %(service_name)s' - ' --resource-limit %(resource_limit)s' - ' %(resource_name)s' % params, + ' --project {project_name}' + ' --service {service_name}' + ' --resource-limit {resource_limit}' + ' {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) limit_id = self._extract_value_from_items('id', items) self.addCleanup( - self.openstack, 'limit delete %s' % limit_id, cloud=SYSTEM_CLOUD + self.openstack, f'limit delete {limit_id}', cloud=SYSTEM_CLOUD ) self.assert_show_fields(items, self.LIMIT_FIELDS) @@ -117,8 +117,8 @@ def test_limit_create_with_options(self): raw_output = self.openstack( 'registered limit set' - ' %(registered_limit_id)s' - ' --region %(region_id)s' % params, + ' {registered_limit_id}' + ' --region {region_id}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -126,7 +126,7 @@ def test_limit_create_with_options(self): resource_name = self._extract_value_from_items('resource_name', items) project_name = self._create_dummy_project() - raw_output = self.openstack('project show %s' % project_name) + raw_output = self.openstack(f'project show {project_name}') items = self.parse_show(raw_output) project_id = self._extract_value_from_items('id', items) description = data_utils.arbitrary_string() @@ -141,18 +141,18 @@ def test_limit_create_with_options(self): } raw_output = self.openstack( 'limit create' - ' --project %(project_id)s' - ' --service %(service_id)s' - ' --resource-limit %(resource_limit)s' - ' --region %(region_id)s' - ' --description %(description)s' - ' %(resource_name)s' % params, + ' --project {project_id}' + ' --service {service_id}' + ' --resource-limit {resource_limit}' + ' --region {region_id}' + ' --description {description}' + ' {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) limit_id = self._extract_value_from_items('id', items) self.addCleanup( - self.openstack, 'limit delete %s' % limit_id, cloud=SYSTEM_CLOUD + self.openstack, f'limit delete {limit_id}', cloud=SYSTEM_CLOUD ) self.assert_show_fields(items, self.LIMIT_FIELDS) @@ -160,7 +160,7 @@ def test_limit_create_with_options(self): def test_limit_show(self): limit_id = self._create_dummy_limit() raw_output = self.openstack( - 'limit show %s' % limit_id, cloud=SYSTEM_CLOUD + f'limit show {limit_id}', cloud=SYSTEM_CLOUD ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.LIMIT_FIELDS) @@ -174,9 +174,9 @@ def test_limit_set_description(self): } raw_output = self.openstack( - 'limit set' - ' --description %(description)s' - ' %(limit_id)s' % params, + 'limit set' ' --description {description}' ' {limit_id}'.format( + **params + ), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -189,8 +189,8 @@ def test_limit_set_resource_limit(self): raw_output = self.openstack( 'limit set' - ' --resource-limit %(resource_limit)s' - ' %(limit_id)s' % params, + ' --resource-limit {resource_limit}' + ' {limit_id}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -205,6 +205,6 @@ def test_limit_list(self): def test_limit_delete(self): limit_id = self._create_dummy_limit(add_clean_up=False) raw_output = self.openstack( - 'limit delete %s' % limit_id, cloud=SYSTEM_CLOUD + f'limit delete {limit_id}', cloud=SYSTEM_CLOUD ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_project.py b/openstackclient/tests/functional/identity/v3/test_project.py index d91ba87a8..9115c0e1a 100644 --- a/openstackclient/tests/functional/identity/v3/test_project.py +++ b/openstackclient/tests/functional/identity/v3/test_project.py @@ -21,23 +21,18 @@ def test_project_create(self): description = data_utils.rand_name('description') raw_output = self.openstack( 'project create ' - '--domain %(domain)s ' - '--description %(description)s ' + f'--domain {self.domain_name} ' + f'--description {description} ' '--enable ' '--property k1=v1 ' '--property k2=v2 ' - '%(name)s' - % { - 'domain': self.domain_name, - 'description': description, - 'name': project_name, - } + f'{project_name}' ) self.addCleanup( self.openstack, 'project delete ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': project_name}, + f'--domain {self.domain_name} ' + f'{project_name}', ) items = self.parse_show(raw_output) show_fields = list(self.PROJECT_FIELDS) @@ -50,9 +45,7 @@ def test_project_create(self): def test_project_delete(self): project_name = self._create_dummy_project(add_clean_up=False) raw_output = self.openstack( - 'project delete ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': project_name} + 'project delete ' f'--domain {self.domain_name} ' f'{project_name}' ) self.assertEqual(0, len(raw_output)) @@ -64,7 +57,7 @@ def test_project_list(self): def test_project_list_with_domain(self): project_name = self._create_dummy_project() raw_output = self.openstack( - 'project list --domain %s' % self.domain_name + f'project list --domain {self.domain_name}' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, common.BASIC_LIST_HEADERS) @@ -76,17 +69,17 @@ def test_project_set(self): new_project_name = data_utils.rand_name('NewTestProject') raw_output = self.openstack( 'project set ' - '--name %(new_name)s ' + f'--name {new_project_name} ' '--disable ' '--property k0=v0 ' - '%(name)s' % {'new_name': new_project_name, 'name': project_name} + f'{project_name}' ) self.assertEqual(0, len(raw_output)) # check project details raw_output = self.openstack( 'project show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': new_project_name} + f'--domain {self.domain_name} ' + f'{new_project_name}' ) items = self.parse_show(raw_output) fields = list(self.PROJECT_FIELDS) @@ -99,17 +92,16 @@ def test_project_set(self): # reset project to make sure it will be cleaned up self.openstack( 'project set ' - '--name %(new_name)s ' + f'--name {project_name} ' '--enable ' - '%(name)s' % {'new_name': project_name, 'name': new_project_name} + f'{new_project_name}' ) def test_project_show(self): raw_output = self.openstack( 'project show ' - '--domain %(domain)s ' - '%(name)s' - % {'domain': self.domain_name, 'name': self.project_name} + f'--domain {self.domain_name} ' + f'{self.project_name}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.PROJECT_FIELDS) @@ -118,9 +110,8 @@ def test_project_show_with_parents_children(self): output = self.openstack( 'project show ' '--parents --children ' - '--domain %(domain)s ' - '%(name)s' - % {'domain': self.domain_name, 'name': self.project_name}, + f'--domain {self.domain_name} ' + f'{self.project_name}', parse_output=True, ) for attr_name in self.PROJECT_FIELDS + ['parents', 'subtree']: diff --git a/openstackclient/tests/functional/identity/v3/test_region.py b/openstackclient/tests/functional/identity/v3/test_region.py index 48ca83781..16133f894 100644 --- a/openstackclient/tests/functional/identity/v3/test_region.py +++ b/openstackclient/tests/functional/identity/v3/test_region.py @@ -23,7 +23,7 @@ def test_region_create_with_parent_region(self): def test_region_delete(self): region_id = self._create_dummy_region(add_clean_up=False) - raw_output = self.openstack('region delete %s' % region_id) + raw_output = self.openstack(f'region delete {region_id}') self.assertEqual(0, len(raw_output)) def test_region_multi_delete(self): @@ -43,27 +43,26 @@ def test_region_set(self): new_parent_region_id = self._create_dummy_region() region_id = self._create_dummy_region(parent_region_id) # check region details - raw_output = self.openstack('region show %s' % region_id) + raw_output = self.openstack(f'region show {region_id}') region = self.parse_show_as_object(raw_output) self.assertEqual(parent_region_id, region['parent_region']) self.assertEqual(region_id, region['region']) # update parent-region raw_output = self.openstack( 'region set ' - '--parent-region %(parent_region)s ' - '%(region)s' - % {'parent_region': new_parent_region_id, 'region': region_id} + f'--parent-region {new_parent_region_id} ' + f'{region_id}' ) self.assertEqual(0, len(raw_output)) # check updated region details - raw_output = self.openstack('region show %s' % region_id) + raw_output = self.openstack(f'region show {region_id}') region = self.parse_show_as_object(raw_output) self.assertEqual(new_parent_region_id, region['parent_region']) self.assertEqual(region_id, region['region']) def test_region_show(self): region_id = self._create_dummy_region() - raw_output = self.openstack('region show %s' % region_id) + raw_output = self.openstack(f'region show {region_id}') region = self.parse_show_as_object(raw_output) self.assertEqual(region_id, region['region']) self.assertEqual('None', region['parent_region']) diff --git a/openstackclient/tests/functional/identity/v3/test_registered_limit.py b/openstackclient/tests/functional/identity/v3/test_registered_limit.py index 6e3c30aeb..f91b47fc4 100644 --- a/openstackclient/tests/functional/identity/v3/test_registered_limit.py +++ b/openstackclient/tests/functional/identity/v3/test_registered_limit.py @@ -25,31 +25,26 @@ def test_registered_limit_create_with_service_name(self): def test_registered_limit_create_with_service_id(self): service_name = self._create_dummy_service() - raw_output = self.openstack( - 'service show' ' %(service_name)s' % {'service_name': service_name} - ) + raw_output = self.openstack('service show' f' {service_name}') service_items = self.parse_show(raw_output) service_id = self._extract_value_from_items('id', service_items) raw_output = self.openstack( 'registered limit create' - ' --service %(service_id)s' - ' --default-limit %(default_limit)s' - ' %(resource_name)s' - % { - 'service_id': service_id, - 'default_limit': 10, - 'resource_name': 'cores', - }, + ' --service {service_id}' + ' --default-limit {default_limit}' + ' {resource_name}'.format( + service_id=service_id, + default_limit=10, + resource_name='cores', + ), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) registered_limit_id = self._extract_value_from_items('id', items) self.addCleanup( self.openstack, - 'registered limit delete' - ' %(registered_limit_id)s' - % {'registered_limit_id': registered_limit_id}, + 'registered limit delete' f' {registered_limit_id}', cloud=SYSTEM_CLOUD, ) @@ -68,19 +63,18 @@ def test_registered_limit_create_with_options(self): raw_output = self.openstack( 'registered limit create' - ' --description \'%(description)s\'' - ' --region %(region_id)s' - ' --service %(service_name)s' - ' --default-limit %(default_limit)s' - ' %(resource_name)s' % params, + ' --description \'{description}\'' + ' --region {region_id}' + ' --service {service_name}' + ' --default-limit {default_limit}' + ' {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) registered_limit_id = self._extract_value_from_items('id', items) self.addCleanup( self.openstack, - 'registered limit delete %(registered_limit_id)s' - % {'registered_limit_id': registered_limit_id}, + f'registered limit delete {registered_limit_id}', cloud=SYSTEM_CLOUD, ) @@ -89,8 +83,7 @@ def test_registered_limit_create_with_options(self): def test_registered_limit_show(self): registered_limit_id = self._create_dummy_registered_limit() raw_output = self.openstack( - 'registered limit show %(registered_limit_id)s' - % {'registered_limit_id': registered_limit_id} + f'registered limit show {registered_limit_id}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.REGISTERED_LIMIT_FIELDS) @@ -105,8 +98,8 @@ def test_registered_limit_set_region_id(self): } raw_output = self.openstack( 'registered limit set' - ' %(registered_limit_id)s' - ' --region %(region_id)s' % params, + ' {registered_limit_id}' + ' --region {region_id}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -120,8 +113,8 @@ def test_registered_limit_set_description(self): } raw_output = self.openstack( 'registered limit set' - ' %(registered_limit_id)s' - ' --description \'%(description)s\'' % params, + ' {registered_limit_id}' + ' --description \'{description}\''.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -136,8 +129,8 @@ def test_registered_limit_set_service(self): } raw_output = self.openstack( 'registered limit set' - ' %(registered_limit_id)s' - ' --service %(service)s' % params, + ' {registered_limit_id}' + ' --service {service}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -151,8 +144,8 @@ def test_registered_limit_set_default_limit(self): } raw_output = self.openstack( 'registered limit set' - ' %(registered_limit_id)s' - ' --default-limit %(default_limit)s' % params, + ' {registered_limit_id}' + ' --default-limit {default_limit}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -167,8 +160,8 @@ def test_registered_limit_set_resource_name(self): } raw_output = self.openstack( 'registered limit set' - ' %(registered_limit_id)s' - ' --resource-name %(resource_name)s' % params, + ' {registered_limit_id}' + ' --resource-name {resource_name}'.format(**params), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) @@ -185,9 +178,7 @@ def test_registered_limit_delete(self): add_clean_up=False ) raw_output = self.openstack( - 'registered limit delete' - ' %(registered_limit_id)s' - % {'registered_limit_id': registered_limit_id}, + 'registered limit delete' f' {registered_limit_id}', cloud=SYSTEM_CLOUD, ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_role.py b/openstackclient/tests/functional/identity/v3/test_role.py index 9a1aa648f..16ddc23a3 100644 --- a/openstackclient/tests/functional/identity/v3/test_role.py +++ b/openstackclient/tests/functional/identity/v3/test_role.py @@ -23,12 +23,10 @@ def test_role_create_with_description(self): role_name = data_utils.rand_name('TestRole') description = data_utils.rand_name('description') raw_output = self.openstack( - 'role create ' - '--description %(description)s ' - '%(name)s' % {'description': description, 'name': role_name} + 'role create ' f'--description {description} ' f'{role_name}' ) role = self.parse_show_as_object(raw_output) - self.addCleanup(self.openstack, 'role delete %s' % role['id']) + self.addCleanup(self.openstack, 'role delete {}'.format(role['id'])) items = self.parse_show(raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) self.assertEqual(description, role['description']) @@ -36,7 +34,7 @@ def test_role_create_with_description(self): def test_role_delete(self): role_name = self._create_dummy_role(add_clean_up=False) - raw_output = self.openstack('role delete %s' % role_name) + raw_output = self.openstack(f'role delete {role_name}') self.assertEqual(0, len(raw_output)) def test_role_list(self): @@ -47,7 +45,7 @@ def test_role_list(self): def test_role_show(self): role_name = self._create_dummy_role() - raw_output = self.openstack('role show %s' % role_name) + raw_output = self.openstack(f'role show {role_name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.ROLE_FIELDS) @@ -58,7 +56,7 @@ def test_role_set(self): f'role set --name {new_role_name} {role_name}' ) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack('role show %s' % new_role_name) + raw_output = self.openstack(f'role show {new_role_name}') role = self.parse_show_as_object(raw_output) self.assertEqual(new_role_name, role['name']) @@ -69,7 +67,7 @@ def test_role_set_description(self): f'role set --description {description} {role_name}' ) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack('role show %s' % role_name) + raw_output = self.openstack(f'role show {role_name}') role = self.parse_show_as_object(raw_output) self.assertEqual(description, role['description']) @@ -78,34 +76,20 @@ def test_role_add(self): username = self._create_dummy_user() raw_output = self.openstack( 'role add ' - '--project %(project)s ' - '--project-domain %(project_domain)s ' - '--user %(user)s ' - '--user-domain %(user_domain)s ' - '%(role)s' - % { - 'project': self.project_name, - 'project_domain': self.domain_name, - 'user': username, - 'user_domain': self.domain_name, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--user {username} ' + f'--user-domain {self.domain_name} ' + f'{role_name}' ) self.addCleanup( self.openstack, 'role remove ' - '--project %(project)s ' - '--project-domain %(project_domain)s ' - '--user %(user)s ' - '--user-domain %(user_domain)s ' - '%(role)s' - % { - 'project': self.project_name, - 'project_domain': self.domain_name, - 'user': username, - 'user_domain': self.domain_name, - 'role': role_name, - }, + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--user {username} ' + f'--user-domain {self.domain_name} ' + f'{role_name}', ) self.assertEqual(0, len(raw_output)) @@ -114,33 +98,19 @@ def test_role_remove(self): username = self._create_dummy_user() add_raw_output = self.openstack( 'role add ' - '--project %(project)s ' - '--project-domain %(project_domain)s ' - '--user %(user)s ' - '--user-domain %(user_domain)s ' - '%(role)s' - % { - 'project': self.project_name, - 'project_domain': self.domain_name, - 'user': username, - 'user_domain': self.domain_name, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--user {username} ' + f'--user-domain {self.domain_name} ' + f'{role_name}' ) remove_raw_output = self.openstack( 'role remove ' - '--project %(project)s ' - '--project-domain %(project_domain)s ' - '--user %(user)s ' - '--user-domain %(user_domain)s ' - '%(role)s' - % { - 'project': self.project_name, - 'project_domain': self.domain_name, - 'user': username, - 'user_domain': self.domain_name, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--user {username} ' + f'--user-domain {self.domain_name} ' + f'{role_name}' ) self.assertEqual(0, len(add_raw_output)) self.assertEqual(0, len(remove_raw_output)) @@ -165,15 +135,15 @@ def test_implied_role_create(self): implied_role_name = self._create_dummy_role() self.openstack( 'implied role create ' - '--implied-role %(implied_role)s ' - '%(role)s' % {'implied_role': implied_role_name, 'role': role_name} + f'--implied-role {implied_role_name} ' + f'{role_name}' ) def test_implied_role_delete(self): implied_role_name, role_name = self._create_dummy_implied_role() raw_output = self.openstack( 'implied role delete ' - '--implied-role %(implied_role)s ' - '%(role)s' % {'implied_role': implied_role_name, 'role': role_name} + f'--implied-role {implied_role_name} ' + f'{role_name}' ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_role_assignment.py b/openstackclient/tests/functional/identity/v3/test_role_assignment.py index a3927e7e1..941563c7b 100644 --- a/openstackclient/tests/functional/identity/v3/test_role_assignment.py +++ b/openstackclient/tests/functional/identity/v3/test_role_assignment.py @@ -25,43 +25,33 @@ def test_role_assignment_list_user_role_system(self): system = 'all' raw_output = self.openstack( 'role add ' - '--user %(user)s ' - '--system %(system)s ' - '%(role)s' - % { - 'user': username, - 'system': system, - 'role': role_name, - } + f'--user {username} ' + f'--system {system} ' + f'{role_name}' ) self.addCleanup( self.openstack, 'role remove ' - '--user %(user)s ' - '--system %(system)s ' - '%(role)s' - % { - 'user': username, - 'system': system, - 'role': role_name, - }, + f'--user {username} ' + f'--system {system} ' + f'{role_name}', ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'role assignment list ' '--user %(user)s ' % {'user': username} + 'role assignment list ' f'--user {username} ' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) raw_output = self.openstack( - 'role assignment list ' '--role %(role)s ' % {'role': role_name} + 'role assignment list ' f'--role {role_name} ' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) raw_output = self.openstack( - 'role assignment list ' '--system %(system)s ' % {'system': system} + 'role assignment list ' f'--system {system} ' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) @@ -72,30 +62,20 @@ def test_role_assignment_list_group(self): system = 'all' raw_output = self.openstack( 'role add ' - '--group %(group)s ' - '--system %(system)s ' - '%(role)s' - % { - 'group': group, - 'system': system, - 'role': role_name, - } + f'--group {group} ' + f'--system {system} ' + f'{role_name}' ) self.addCleanup( self.openstack, 'role remove ' - '--group %(group)s ' - '--system %(system)s ' - '%(role)s' - % { - 'group': group, - 'system': system, - 'role': role_name, - }, + f'--group {group} ' + f'--system {system} ' + f'{role_name}', ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'role assignment list ' '--group %(group)s ' % {'group': group} + 'role assignment list ' f'--group {group} ' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) @@ -105,31 +85,20 @@ def test_role_assignment_list_domain(self): username = self._create_dummy_user() raw_output = self.openstack( 'role add ' - '--domain %(domain)s ' - '--user %(user)s ' - '%(role)s' - % { - 'domain': self.domain_name, - 'user': username, - 'role': role_name, - } + f'--domain {self.domain_name} ' + f'--user {username} ' + f'{role_name}' ) self.addCleanup( self.openstack, 'role remove ' - '--domain %(domain)s ' - '--user %(user)s ' - '%(role)s' - % { - 'domain': self.domain_name, - 'user': username, - 'role': role_name, - }, + f'--domain {self.domain_name} ' + f'--user {username} ' + f'{role_name}', ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'role assignment list ' - '--domain %(domain)s ' % {'domain': self.domain_name} + 'role assignment list ' f'--domain {self.domain_name} ' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) @@ -139,31 +108,20 @@ def test_role_assignment_list_project(self): username = self._create_dummy_user() raw_output = self.openstack( 'role add ' - '--project %(project)s ' - '--user %(user)s ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - } + f'--project {self.project_name} ' + f'--user {username} ' + f'{role_name}' ) self.addCleanup( self.openstack, 'role remove ' - '--project %(project)s ' - '--user %(user)s ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - }, + f'--project {self.project_name} ' + f'--user {username} ' + f'{role_name}', ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'role assignment list ' - '--project %(project)s ' % {'project': self.project_name} + 'role assignment list ' f'--project {self.project_name} ' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) @@ -188,15 +146,10 @@ def test_role_assignment_list_inherited(self): username = self._create_dummy_user() raw_output = self.openstack( 'role add ' - '--project %(project)s ' - '--user %(user)s ' + f'--project {self.project_name} ' + f'--user {username} ' '--inherited ' - '%(role)s' - % { - 'project': self.project_name, - 'user': username, - 'role': role_name, - } + f'{role_name}' ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_service.py b/openstackclient/tests/functional/identity/v3/test_service.py index 98fa349d1..7e102bacb 100644 --- a/openstackclient/tests/functional/identity/v3/test_service.py +++ b/openstackclient/tests/functional/identity/v3/test_service.py @@ -21,7 +21,7 @@ def test_service_create(self): def test_service_delete(self): service_name = self._create_dummy_service(add_clean_up=False) - raw_output = self.openstack('service delete %s' % service_name) + raw_output = self.openstack(f'service delete {service_name}') self.assertEqual(0, len(raw_output)) def test_service_multi_delete(self): @@ -44,21 +44,15 @@ def test_service_set(self): new_service_type = data_utils.rand_name('NewTestType') raw_output = self.openstack( 'service set ' - '--type %(type)s ' - '--name %(name)s ' - '--description %(description)s ' + f'--type {new_service_type} ' + f'--name {new_service_name} ' + f'--description {new_service_description} ' '--disable ' - '%(service)s' - % { - 'type': new_service_type, - 'name': new_service_name, - 'description': new_service_description, - 'service': service_name, - } + f'{service_name}' ) self.assertEqual(0, len(raw_output)) # get service details - raw_output = self.openstack('service show %s' % new_service_name) + raw_output = self.openstack(f'service show {new_service_name}') # assert service details service = self.parse_show_as_object(raw_output) self.assertEqual(new_service_type, service['type']) @@ -67,6 +61,6 @@ def test_service_set(self): def test_service_show(self): service_name = self._create_dummy_service() - raw_output = self.openstack('service show %s' % service_name) + raw_output = self.openstack(f'service show {service_name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.SERVICE_FIELDS) diff --git a/openstackclient/tests/functional/identity/v3/test_service_provider.py b/openstackclient/tests/functional/identity/v3/test_service_provider.py index b7ab14bc4..793223b9d 100644 --- a/openstackclient/tests/functional/identity/v3/test_service_provider.py +++ b/openstackclient/tests/functional/identity/v3/test_service_provider.py @@ -24,7 +24,7 @@ def test_sp_create(self): def test_sp_delete(self): service_provider = self._create_dummy_sp(add_clean_up=False) raw_output = self.openstack( - 'service provider delete %s' % service_provider + f'service provider delete {service_provider}' ) self.assertEqual(0, len(raw_output)) @@ -37,7 +37,7 @@ def test_sp_multi_delete(self): def test_sp_show(self): service_provider = self._create_dummy_sp(add_clean_up=True) raw_output = self.openstack( - 'service provider show %s' % service_provider + f'service provider show {service_provider}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.SERVICE_PROVIDER_FIELDS) @@ -62,7 +62,7 @@ def test_sp_set(self): ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'service provider show %s' % service_provider + f'service provider show {service_provider}' ) updated_value = self.parse_show_as_object(raw_output) self.assertIn(new_description, updated_value['description']) diff --git a/openstackclient/tests/functional/identity/v3/test_user.py b/openstackclient/tests/functional/identity/v3/test_user.py index 917da4a3f..c32524600 100644 --- a/openstackclient/tests/functional/identity/v3/test_user.py +++ b/openstackclient/tests/functional/identity/v3/test_user.py @@ -22,9 +22,7 @@ def test_user_create(self): def test_user_delete(self): username = self._create_dummy_user(add_clean_up=False) raw_output = self.openstack( - 'user delete ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': username} + 'user delete ' f'--domain {self.domain_name} ' f'{username}' ) self.assertEqual(0, len(raw_output)) @@ -36,25 +34,19 @@ def test_user_list(self): def test_user_set(self): username = self._create_dummy_user() raw_output = self.openstack( - 'user show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': username} + 'user show ' f'--domain {self.domain_name} ' f'{username}' ) user = self.parse_show_as_object(raw_output) new_username = data_utils.rand_name('NewTestUser') new_email = data_utils.rand_name() + '@example.com' raw_output = self.openstack( - 'user set ' - '--email %(email)s ' - '--name %(new_name)s ' - '%(id)s' - % {'email': new_email, 'new_name': new_username, 'id': user['id']} + 'user set ' '--email {email} ' '--name {new_name} ' '{id}'.format( + email=new_email, new_name=new_username, id=user['id'] + ) ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'user show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': new_username} + 'user show ' f'--domain {self.domain_name} ' f'{new_username}' ) updated_user = self.parse_show_as_object(raw_output) self.assertEqual(user['id'], updated_user['id']) @@ -65,36 +57,29 @@ def test_user_set_default_project_id(self): project_name = self._create_dummy_project() # get original user details raw_output = self.openstack( - 'user show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': username} + 'user show ' f'--domain {self.domain_name} ' f'{username}' ) user = self.parse_show_as_object(raw_output) # update user raw_output = self.openstack( 'user set ' - '--project %(project)s ' - '--project-domain %(project_domain)s ' - '%(id)s' - % { - 'project': project_name, - 'project_domain': self.domain_name, - 'id': user['id'], - } + '--project {project} ' + '--project-domain {project_domain} ' + '{id}'.format( + project=project_name, + project_domain=self.domain_name, + id=user['id'], + ) ) self.assertEqual(0, len(raw_output)) # get updated user details raw_output = self.openstack( - 'user show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': username} + 'user show ' f'--domain {self.domain_name} ' f'{username}' ) updated_user = self.parse_show_as_object(raw_output) # get project details raw_output = self.openstack( - 'project show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': project_name} + 'project show ' f'--domain {self.domain_name} ' f'{project_name}' ) project = self.parse_show_as_object(raw_output) # check updated user details @@ -104,9 +89,7 @@ def test_user_set_default_project_id(self): def test_user_show(self): username = self._create_dummy_user() raw_output = self.openstack( - 'user show ' - '--domain %(domain)s ' - '%(name)s' % {'domain': self.domain_name, 'name': username} + 'user show ' f'--domain {self.domain_name} ' f'{username}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.USER_FIELDS) diff --git a/openstackclient/tests/functional/image/v2/test_image.py b/openstackclient/tests/functional/image/v2/test_image.py index 93251ea53..4d51737c3 100644 --- a/openstackclient/tests/functional/image/v2/test_image.py +++ b/openstackclient/tests/functional/image/v2/test_image.py @@ -30,9 +30,7 @@ def setUp(self): self.image_tag = 'my_tag' self.image_tag1 = 'random' output = self.openstack( - 'image create --tag {tag} {name}'.format( - tag=self.image_tag, name=self.name - ), + f'image create --tag {self.image_tag} {self.name}', parse_output=True, ) self.image_id = output["id"] diff --git a/openstackclient/tests/functional/network/v2/common.py b/openstackclient/tests/functional/network/v2/common.py index f7c7212a1..4cd14ebc7 100644 --- a/openstackclient/tests/functional/network/v2/common.py +++ b/openstackclient/tests/functional/network/v2/common.py @@ -80,9 +80,7 @@ def test_tag_operation(self): def _list_tag_check(self, project_id, expected): cmd_output = self.openstack( - '{} list --long --project {}'.format( - self.base_command, project_id - ), + f'{self.base_command} list --long --project {project_id}', parse_output=True, ) for name, tags in expected: diff --git a/openstackclient/tests/functional/network/v2/test_default_security_group_rule.py b/openstackclient/tests/functional/network/v2/test_default_security_group_rule.py index d6ce95c1a..1481c9a41 100644 --- a/openstackclient/tests/functional/network/v2/test_default_security_group_rule.py +++ b/openstackclient/tests/functional/network/v2/test_default_security_group_rule.py @@ -30,14 +30,9 @@ def setUp(self): # Create the default security group rule. cmd_output = self.openstack( 'default security group rule create ' - '--protocol %(protocol)s ' - '--dst-port %(port)s:%(port)s ' - '--%(direction)s --ethertype IPv4 ' - % { - 'protocol': self.protocol, - 'port': self.port, - 'direction': self.direction, - }, + f'--protocol {self.protocol} ' + f'--dst-port {self.port}:{self.port} ' + f'--{self.direction} --ethertype IPv4 ', parse_output=True, ) self.addCleanup( diff --git a/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py b/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py index 510ec8b2d..95ebb49ce 100644 --- a/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py +++ b/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py @@ -41,15 +41,14 @@ def _create_helpers(self, router_id, helpers): created_helpers = [] for helper in helpers: output = self.openstack( - 'network l3 conntrack helper create %(router)s ' - '--helper %(helper)s --protocol %(protocol)s ' - '--port %(port)s ' - % { - 'router': router_id, - 'helper': helper['helper'], - 'protocol': helper['protocol'], - 'port': helper['port'], - }, + 'network l3 conntrack helper create {router} ' + '--helper {helper} --protocol {protocol} ' + '--port {port} '.format( + router=router_id, + helper=helper['helper'], + protocol=helper['protocol'], + port=helper['port'], + ), parse_output=True, ) self.assertEqual(helper['helper'], output['helper']) @@ -70,8 +69,8 @@ def test_l3_conntrack_helper_create_and_delete(self): ct_ids = " ".join([ct['id'] for ct in created_helpers]) raw_output = self.openstack( - '--debug network l3 conntrack helper delete %(router)s ' - '%(ct_ids)s' % {'router': router_id, 'ct_ids': ct_ids} + f'--debug network l3 conntrack helper delete {router_id} ' + f'{ct_ids}' ) self.assertOutput('', raw_output) @@ -87,7 +86,7 @@ def test_l3_conntrack_helper_list(self): router_id = self._create_router() self._create_helpers(router_id, helpers) output = self.openstack( - 'network l3 conntrack helper list %s ' % router_id, + f'network l3 conntrack helper list {router_id} ', parse_output=True, ) for ct in output: @@ -100,12 +99,11 @@ def test_l3_conntrack_helper_set_and_show(self): router_id = self._create_router() created_helper = self._create_helpers(router_id, [helper])[0] output = self.openstack( - 'network l3 conntrack helper show %(router_id)s %(ct_id)s ' - '-f json' - % { - 'router_id': router_id, - 'ct_id': created_helper['id'], - }, + 'network l3 conntrack helper show {router_id} {ct_id} ' + '-f json'.format( + router_id=router_id, + ct_id=created_helper['id'], + ), parse_output=True, ) self.assertEqual(helper['helper'], output['helper']) @@ -113,23 +111,21 @@ def test_l3_conntrack_helper_set_and_show(self): self.assertEqual(helper['port'], output['port']) raw_output = self.openstack( - 'network l3 conntrack helper set %(router_id)s %(ct_id)s ' - '--port %(port)s ' - % { - 'router_id': router_id, - 'ct_id': created_helper['id'], - 'port': helper['port'] + 1, - } + 'network l3 conntrack helper set {router_id} {ct_id} ' + '--port {port} '.format( + router_id=router_id, + ct_id=created_helper['id'], + port=helper['port'] + 1, + ) ) self.assertOutput('', raw_output) output = self.openstack( - 'network l3 conntrack helper show %(router_id)s %(ct_id)s ' - '-f json' - % { - 'router_id': router_id, - 'ct_id': created_helper['id'], - }, + 'network l3 conntrack helper show {router_id} {ct_id} ' + '-f json'.format( + router_id=router_id, + ct_id=created_helper['id'], + ), parse_output=True, ) self.assertEqual(helper['port'] + 1, output['port']) diff --git a/openstackclient/tests/functional/network/v2/test_network.py b/openstackclient/tests/functional/network/v2/test_network.py index 8dbe883b6..a38d88bda 100644 --- a/openstackclient/tests/functional/network/v2/test_network.py +++ b/openstackclient/tests/functional/network/v2/test_network.py @@ -206,7 +206,7 @@ def test_network_list(self): 'network create ' + network_options + name1, parse_output=True, ) - self.addCleanup(self.openstack, 'network delete %s' % name1) + self.addCleanup(self.openstack, f'network delete {name1}') self.assertIsNotNone(cmd_output["id"]) if self.haz_network: self.assertEqual( @@ -342,33 +342,30 @@ def test_network_dhcp_agent(self): name1 = uuid.uuid4().hex cmd_output = self.openstack( - 'network create --description aaaa %s' % name1, + f'network create --description aaaa {name1}', parse_output=True, ) - self.addCleanup(self.openstack, 'network delete %s' % name1) + self.addCleanup(self.openstack, f'network delete {name1}') # Get network ID network_id = cmd_output['id'] # Add Agent to Network self.openstack( - 'network agent add network --dhcp {} {}'.format( - agent_id, network_id - ) + f'network agent add network --dhcp {agent_id} {network_id}' ) # Test network list --agent cmd_output = self.openstack( - 'network list --agent %s' % agent_id, + f'network list --agent {agent_id}', parse_output=True, ) # Cleanup # Remove Agent from Network self.openstack( - 'network agent remove network --dhcp %s %s' - % (agent_id, network_id) + f'network agent remove network --dhcp {agent_id} {network_id}' ) # Assert @@ -388,10 +385,10 @@ def test_network_set(self): '--no-share ' '--internal ' '--no-default ' - '--enable-port-security %s' % name, + f'--enable-port-security {name}', parse_output=True, ) - self.addCleanup(self.openstack, 'network delete %s' % name) + self.addCleanup(self.openstack, f'network delete {name}') self.assertIsNotNone(cmd_output["id"]) self.assertEqual( 'aaaa', @@ -416,7 +413,7 @@ def test_network_set(self): '--disable ' '--share ' '--external ' - '--disable-port-security %s' % name + f'--disable-port-security {name}' ) self.assertOutput('', raw_output) diff --git a/openstackclient/tests/functional/network/v2/test_network_agent.py b/openstackclient/tests/functional/network/v2/test_network_agent.py index bbe168aee..013201a40 100644 --- a/openstackclient/tests/functional/network/v2/test_network_agent.py +++ b/openstackclient/tests/functional/network/v2/test_network_agent.py @@ -42,7 +42,7 @@ def test_network_agent_list_show_set(self): # agent show cmd_output = self.openstack( - 'network agent show %s' % agent_ids[0], + f'network agent show {agent_ids[0]}', parse_output=True, ) self.assertEqual( @@ -57,12 +57,12 @@ def test_network_agent_list_show_set(self): # agent set raw_output = self.openstack( - 'network agent set --disable %s' % agent_ids[0] + f'network agent set --disable {agent_ids[0]}' ) self.assertOutput('', raw_output) cmd_output = self.openstack( - 'network agent show %s' % agent_ids[0], + f'network agent show {agent_ids[0]}', parse_output=True, ) self.assertEqual( @@ -71,12 +71,12 @@ def test_network_agent_list_show_set(self): ) raw_output = self.openstack( - 'network agent set --enable %s' % agent_ids[0] + f'network agent set --enable {agent_ids[0]}' ) self.assertOutput('', raw_output) cmd_output = self.openstack( - 'network agent show %s' % agent_ids[0], + f'network agent show {agent_ids[0]}', parse_output=True, ) self.assertEqual( @@ -112,33 +112,30 @@ def test_network_dhcp_agent_list(self): name1 = uuid.uuid4().hex cmd_output = self.openstack( - 'network create --description aaaa %s' % name1, + f'network create --description aaaa {name1}', parse_output=True, ) - self.addCleanup(self.openstack, 'network delete %s' % name1) + self.addCleanup(self.openstack, f'network delete {name1}') # Get network ID network_id = cmd_output['id'] # Add Agent to Network self.openstack( - 'network agent add network --dhcp {} {}'.format( - agent_id, network_id - ) + f'network agent add network --dhcp {agent_id} {network_id}' ) # Test network agent list --network cmd_output = self.openstack( - 'network agent list --network %s' % network_id, + f'network agent list --network {network_id}', parse_output=True, ) # Cleanup # Remove Agent from Network self.openstack( - 'network agent remove network --dhcp %s %s' - % (agent_id, network_id) + f'network agent remove network --dhcp {agent_id} {network_id}' ) # Assert @@ -153,11 +150,11 @@ def test_network_agent_list_routers(self): name = uuid.uuid4().hex cmd_output = self.openstack( - 'router create %s' % name, + f'router create {name}', parse_output=True, ) - self.addCleanup(self.openstack, 'router delete %s' % name) + self.addCleanup(self.openstack, f'router delete {name}') # Get router ID router_id = cmd_output['id'] # Get l3 agent id @@ -175,7 +172,7 @@ def test_network_agent_list_routers(self): # Test router list --agent cmd_output = self.openstack( - 'network agent list --router %s' % router_id, + f'network agent list --router {router_id}', parse_output=True, ) @@ -184,12 +181,10 @@ def test_network_agent_list_routers(self): # Remove router from agent self.openstack( - 'network agent remove router --l3 {} {}'.format( - agent_id, router_id - ) + f'network agent remove router --l3 {agent_id} {router_id}' ) cmd_output = self.openstack( - 'network agent list --router %s' % router_id, + f'network agent list --router {router_id}', parse_output=True, ) agent_ids = [x['ID'] for x in cmd_output] diff --git a/openstackclient/tests/functional/network/v2/test_network_flavor.py b/openstackclient/tests/functional/network/v2/test_network_flavor.py index e04d10e80..2b8867037 100644 --- a/openstackclient/tests/functional/network/v2/test_network_flavor.py +++ b/openstackclient/tests/functional/network/v2/test_network_flavor.py @@ -37,10 +37,10 @@ def test_network_flavor_add_remove_profile(self): ) service_profile_id = cmd_output2.get('id') - self.addCleanup(self.openstack, 'network flavor delete %s' % flavor_id) + self.addCleanup(self.openstack, f'network flavor delete {flavor_id}') self.addCleanup( self.openstack, - 'network flavor profile delete %s' % service_profile_id, + f'network flavor profile delete {service_profile_id}', ) # Add flavor to service profile self.openstack( diff --git a/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py b/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py index d22f29747..11d121a3e 100644 --- a/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py +++ b/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py @@ -31,20 +31,15 @@ def setUp(self): self.created_ndp_proxies = [] json_output = self.openstack( - 'address scope create --ip-version 6 ' - '%(address_s_name)s' % {'address_s_name': self.ADDR_SCOPE_NAME}, + 'address scope create --ip-version 6 ' f'{self.ADDR_SCOPE_NAME}', parse_output=True, ) self.assertIsNotNone(json_output['id']) self.ADDRESS_SCOPE_ID = json_output['id'] json_output = self.openstack( - 'subnet pool create %(subnet_p_name)s ' - '--address-scope %(address_scope)s ' - '--pool-prefix 2001:db8::/96 --default-prefix-length 112' - % { - 'subnet_p_name': self.SUBNET_P_NAME, - 'address_scope': self.ADDRESS_SCOPE_ID, - }, + f'subnet pool create {self.SUBNET_P_NAME} ' + f'--address-scope {self.ADDRESS_SCOPE_ID} ' + '--pool-prefix 2001:db8::/96 --default-prefix-length 112', parse_output=True, ) self.assertIsNotNone(json_output['id']) @@ -57,12 +52,7 @@ def setUp(self): self.EXT_NET_ID = json_output['id'] json_output = self.openstack( 'subnet create --ip-version 6 --subnet-pool ' - '%(subnet_pool)s --network %(net_id)s %(sub_name)s' - % { - 'subnet_pool': self.SUBNET_POOL_ID, - 'net_id': self.EXT_NET_ID, - 'sub_name': self.EXT_SUB_NAME, - }, + f'{self.SUBNET_POOL_ID} --network {self.EXT_NET_ID} {self.EXT_SUB_NAME}', parse_output=True, ) self.assertIsNotNone(json_output['id']) @@ -74,8 +64,7 @@ def setUp(self): self.assertIsNotNone(json_output['id']) self.ROT_ID = json_output['id'] output = self.openstack( - 'router set %(router_id)s --external-gateway %(net_id)s' - % {'router_id': self.ROT_ID, 'net_id': self.EXT_NET_ID} + f'router set {self.ROT_ID} --external-gateway {self.EXT_NET_ID}' ) self.assertEqual('', output) output = self.openstack('router set --enable-ndp-proxy ' + self.ROT_ID) @@ -93,23 +82,14 @@ def setUp(self): self.INT_NET_ID = json_output['id'] json_output = self.openstack( 'subnet create --ip-version 6 --subnet-pool ' - '%(subnet_pool)s --network %(net_id)s %(sub_name)s' - % { - 'subnet_pool': self.SUBNET_POOL_ID, - 'net_id': self.INT_NET_ID, - 'sub_name': self.INT_SUB_NAME, - }, + f'{self.SUBNET_POOL_ID} --network {self.INT_NET_ID} {self.INT_SUB_NAME}', parse_output=True, ) self.assertIsNotNone(json_output['id']) self.INT_SUB_ID = json_output['id'] json_output = self.openstack( - 'port create --network %(net_id)s ' - '%(port_name)s' - % { - 'net_id': self.INT_NET_ID, - 'port_name': self.INT_PORT_NAME, - }, + f'port create --network {self.INT_NET_ID} ' + f'{self.INT_PORT_NAME}', parse_output=True, ) self.assertIsNotNone(json_output['id']) @@ -161,14 +141,13 @@ def tearDown(self): def _create_ndp_proxies(self, ndp_proxies): for ndp_proxy in ndp_proxies: output = self.openstack( - 'router ndp proxy create %(router)s --name %(name)s ' - '--port %(port)s --ip-address %(address)s' - % { - 'router': ndp_proxy['router_id'], - 'name': ndp_proxy['name'], - 'port': ndp_proxy['port_id'], - 'address': ndp_proxy['address'], - }, + 'router ndp proxy create {router} --name {name} ' + '--port {port} --ip-address {address}'.format( + router=ndp_proxy['router_id'], + name=ndp_proxy['name'], + port=ndp_proxy['port_id'], + address=ndp_proxy['address'], + ), parse_output=True, ) self.assertEqual(ndp_proxy['router_id'], output['router_id']) @@ -214,8 +193,7 @@ def test_ndp_proxy_set_and_show(self): self._create_ndp_proxies([ndp_proxies]) ndp_proxy_id = self.created_ndp_proxies[0]['id'] output = self.openstack( - 'router ndp proxy set --description %s %s' - % (description, ndp_proxy_id) + f'router ndp proxy set --description {description} {ndp_proxy_id}' ) self.assertEqual('', output) json_output = self.openstack( diff --git a/openstackclient/tests/functional/network/v2/test_network_qos_rule.py b/openstackclient/tests/functional/network/v2/test_network_qos_rule.py index 8db484e2e..28c38e5a4 100644 --- a/openstackclient/tests/functional/network/v2/test_network_qos_rule.py +++ b/openstackclient/tests/functional/network/v2/test_network_qos_rule.py @@ -32,40 +32,39 @@ class NetworkQosRuleTestsMinimumBandwidth(NetworkQosTests): def setUp(self): super().setUp() - self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex + self.QOS_POLICY_NAME = f'qos_policy_{uuid.uuid4().hex}' - self.openstack('network qos policy create %s' % self.QOS_POLICY_NAME) + self.openstack(f'network qos policy create {self.QOS_POLICY_NAME}') self.addCleanup( self.openstack, - 'network qos policy delete %s' % self.QOS_POLICY_NAME, + f'network qos policy delete {self.QOS_POLICY_NAME}', ) cmd_output = self.openstack( 'network qos rule create ' '--type minimum-bandwidth ' '--min-kbps 2800 ' - '--egress %s' % self.QOS_POLICY_NAME, + f'--egress {self.QOS_POLICY_NAME}', parse_output=True, ) self.RULE_ID = cmd_output['id'] self.addCleanup( self.openstack, - 'network qos rule delete %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule delete {self.QOS_POLICY_NAME} {self.RULE_ID}', ) self.assertTrue(self.RULE_ID) def test_qos_rule_create_delete(self): # This is to check the output of qos rule delete policy_name = uuid.uuid4().hex - self.openstack('network qos policy create %s' % policy_name) + self.openstack(f'network qos policy create {policy_name}') self.addCleanup( - self.openstack, 'network qos policy delete %s' % policy_name + self.openstack, f'network qos policy delete {policy_name}' ) rule = self.openstack( 'network qos rule create ' '--type minimum-bandwidth ' '--min-kbps 2800 ' - '--egress %s' % policy_name, + f'--egress {policy_name}', parse_output=True, ) raw_output = self.openstack( @@ -75,27 +74,24 @@ def test_qos_rule_create_delete(self): def test_qos_rule_list(self): cmd_output = self.openstack( - 'network qos rule list %s' % self.QOS_POLICY_NAME, + f'network qos rule list {self.QOS_POLICY_NAME}', parse_output=True, ) self.assertIn(self.RULE_ID, [rule['ID'] for rule in cmd_output]) def test_qos_rule_show(self): cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(self.RULE_ID, cmd_output['id']) def test_qos_rule_set(self): self.openstack( - 'network qos rule set --min-kbps 7500 %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID) + f'network qos rule set --min-kbps 7500 {self.QOS_POLICY_NAME} {self.RULE_ID}' ) cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(7500, cmd_output['min_kbps']) @@ -107,40 +103,39 @@ class NetworkQosRuleTestsMinimumPacketRate(NetworkQosTests): def setUp(self): super().setUp() - self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex + self.QOS_POLICY_NAME = f'qos_policy_{uuid.uuid4().hex}' - self.openstack('network qos policy create %s' % self.QOS_POLICY_NAME) + self.openstack(f'network qos policy create {self.QOS_POLICY_NAME}') self.addCleanup( self.openstack, - 'network qos policy delete %s' % self.QOS_POLICY_NAME, + f'network qos policy delete {self.QOS_POLICY_NAME}', ) cmd_output = self.openstack( 'network qos rule create ' '--type minimum-packet-rate ' '--min-kpps 2800 ' - '--egress %s' % self.QOS_POLICY_NAME, + f'--egress {self.QOS_POLICY_NAME}', parse_output=True, ) self.RULE_ID = cmd_output['id'] self.addCleanup( self.openstack, - 'network qos rule delete %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule delete {self.QOS_POLICY_NAME} {self.RULE_ID}', ) self.assertTrue(self.RULE_ID) def test_qos_rule_create_delete(self): # This is to check the output of qos rule delete policy_name = uuid.uuid4().hex - self.openstack('network qos policy create %s' % policy_name) + self.openstack(f'network qos policy create {policy_name}') self.addCleanup( - self.openstack, 'network qos policy delete %s' % policy_name + self.openstack, f'network qos policy delete {policy_name}' ) rule = self.openstack( 'network qos rule create ' '--type minimum-packet-rate ' '--min-kpps 2800 ' - '--egress %s' % policy_name, + f'--egress {policy_name}', parse_output=True, ) raw_output = self.openstack( @@ -150,27 +145,24 @@ def test_qos_rule_create_delete(self): def test_qos_rule_list(self): cmd_output = self.openstack( - 'network qos rule list %s' % self.QOS_POLICY_NAME, + f'network qos rule list {self.QOS_POLICY_NAME}', parse_output=True, ) self.assertIn(self.RULE_ID, [rule['ID'] for rule in cmd_output]) def test_qos_rule_show(self): cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(self.RULE_ID, cmd_output['id']) def test_qos_rule_set(self): self.openstack( - 'network qos rule set --min-kpps 7500 --any %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID) + f'network qos rule set --min-kpps 7500 --any {self.QOS_POLICY_NAME} {self.RULE_ID}' ) cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(7500, cmd_output['min_kpps']) @@ -183,37 +175,36 @@ class NetworkQosRuleTestsDSCPMarking(NetworkQosTests): def setUp(self): super().setUp() - self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex - self.openstack('network qos policy create %s' % self.QOS_POLICY_NAME) + self.QOS_POLICY_NAME = f'qos_policy_{uuid.uuid4().hex}' + self.openstack(f'network qos policy create {self.QOS_POLICY_NAME}') self.addCleanup( self.openstack, - 'network qos policy delete %s' % self.QOS_POLICY_NAME, + f'network qos policy delete {self.QOS_POLICY_NAME}', ) cmd_output = self.openstack( 'network qos rule create ' '--type dscp-marking ' - '--dscp-mark 8 %s' % self.QOS_POLICY_NAME, + f'--dscp-mark 8 {self.QOS_POLICY_NAME}', parse_output=True, ) self.RULE_ID = cmd_output['id'] self.addCleanup( self.openstack, - 'network qos rule delete %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule delete {self.QOS_POLICY_NAME} {self.RULE_ID}', ) self.assertTrue(self.RULE_ID) def test_qos_rule_create_delete(self): # This is to check the output of qos rule delete policy_name = uuid.uuid4().hex - self.openstack('network qos policy create %s' % policy_name) + self.openstack(f'network qos policy create {policy_name}') self.addCleanup( - self.openstack, 'network qos policy delete %s' % policy_name + self.openstack, f'network qos policy delete {policy_name}' ) rule = self.openstack( 'network qos rule create ' '--type dscp-marking ' - '--dscp-mark 8 %s' % policy_name, + f'--dscp-mark 8 {policy_name}', parse_output=True, ) raw_output = self.openstack( @@ -223,27 +214,24 @@ def test_qos_rule_create_delete(self): def test_qos_rule_list(self): cmd_output = self.openstack( - 'network qos rule list %s' % self.QOS_POLICY_NAME, + f'network qos rule list {self.QOS_POLICY_NAME}', parse_output=True, ) self.assertIn(self.RULE_ID, [rule['ID'] for rule in cmd_output]) def test_qos_rule_show(self): cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(self.RULE_ID, cmd_output['id']) def test_qos_rule_set(self): self.openstack( - 'network qos rule set --dscp-mark 32 %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID) + f'network qos rule set --dscp-mark 32 {self.QOS_POLICY_NAME} {self.RULE_ID}' ) cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(32, cmd_output['dscp_mark']) @@ -255,40 +243,39 @@ class NetworkQosRuleTestsBandwidthLimit(NetworkQosTests): def setUp(self): super().setUp() - self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex - self.openstack('network qos policy create %s' % self.QOS_POLICY_NAME) + self.QOS_POLICY_NAME = f'qos_policy_{uuid.uuid4().hex}' + self.openstack(f'network qos policy create {self.QOS_POLICY_NAME}') self.addCleanup( self.openstack, - 'network qos policy delete %s' % self.QOS_POLICY_NAME, + f'network qos policy delete {self.QOS_POLICY_NAME}', ) cmd_output = self.openstack( 'network qos rule create ' '--type bandwidth-limit ' '--max-kbps 10000 ' - '--egress %s' % self.QOS_POLICY_NAME, + f'--egress {self.QOS_POLICY_NAME}', parse_output=True, ) self.RULE_ID = cmd_output['id'] self.addCleanup( self.openstack, - 'network qos rule delete %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule delete {self.QOS_POLICY_NAME} {self.RULE_ID}', ) self.assertTrue(self.RULE_ID) def test_qos_rule_create_delete(self): # This is to check the output of qos rule delete policy_name = uuid.uuid4().hex - self.openstack('network qos policy create %s' % policy_name) + self.openstack(f'network qos policy create {policy_name}') self.addCleanup( - self.openstack, 'network qos policy delete %s' % policy_name + self.openstack, f'network qos policy delete {policy_name}' ) rule = self.openstack( 'network qos rule create ' '--type bandwidth-limit ' '--max-kbps 10000 ' '--max-burst-kbits 1400 ' - '--egress %s' % policy_name, + f'--egress {policy_name}', parse_output=True, ) raw_output = self.openstack( @@ -298,15 +285,14 @@ def test_qos_rule_create_delete(self): def test_qos_rule_list(self): cmd_output = self.openstack( - 'network qos rule list %s' % self.QOS_POLICY_NAME, + f'network qos rule list {self.QOS_POLICY_NAME}', parse_output=True, ) self.assertIn(self.RULE_ID, [rule['ID'] for rule in cmd_output]) def test_qos_rule_show(self): cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(self.RULE_ID, cmd_output['id']) @@ -315,11 +301,10 @@ def test_qos_rule_set(self): self.openstack( 'network qos rule set --max-kbps 15000 ' '--max-burst-kbits 1800 ' - '--ingress %s %s' % (self.QOS_POLICY_NAME, self.RULE_ID) + f'--ingress {self.QOS_POLICY_NAME} {self.RULE_ID}' ) cmd_output = self.openstack( - 'network qos rule show %s %s' - % (self.QOS_POLICY_NAME, self.RULE_ID), + f'network qos rule show {self.QOS_POLICY_NAME} {self.RULE_ID}', parse_output=True, ) self.assertEqual(15000, cmd_output['max_kbps']) diff --git a/openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py b/openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py index 77ce35716..745191b8c 100644 --- a/openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py +++ b/openstackclient/tests/functional/network/v2/test_network_qos_rule_type.py @@ -67,7 +67,7 @@ def test_qos_rule_type_list_all_rules(self): def test_qos_rule_type_details(self): for rule_type in self.AVAILABLE_RULE_TYPES: cmd_output = self.openstack( - 'network qos rule type show %s -f json' % rule_type, + f'network qos rule type show {rule_type} -f json', parse_output=True, ) self.assertEqual(rule_type, cmd_output['rule_type_name']) diff --git a/openstackclient/tests/functional/network/v2/test_network_segment_range.py b/openstackclient/tests/functional/network/v2/test_network_segment_range.py index 604ee3cfa..3f07948b5 100644 --- a/openstackclient/tests/functional/network/v2/test_network_segment_range.py +++ b/openstackclient/tests/functional/network/v2/test_network_segment_range.py @@ -128,8 +128,8 @@ def test_network_segment_range_set_show(self): new_minimum = 2020 new_maximum = 2029 cmd_output = self.openstack( - 'network segment range set --minimum {min} --maximum {max} ' - '{name}'.format(min=new_minimum, max=new_maximum, name=name) + f'network segment range set --minimum {new_minimum} --maximum {new_maximum} ' + f'{name}' ) self.assertOutput('', cmd_output) diff --git a/openstackclient/tests/functional/network/v2/test_network_trunk.py b/openstackclient/tests/functional/network/v2/test_network_trunk.py index 7c504286d..0232943b3 100644 --- a/openstackclient/tests/functional/network/v2/test_network_trunk.py +++ b/openstackclient/tests/functional/network/v2/test_network_trunk.py @@ -32,33 +32,27 @@ def setUp(self): self.parent_port_name = uuid.uuid4().hex self.sub_port_name = uuid.uuid4().hex - self.openstack('network create %s' % network_name) - self.addCleanup(self.openstack, 'network delete %s' % network_name) + self.openstack(f'network create {network_name}') + self.addCleanup(self.openstack, f'network delete {network_name}') self.openstack( - 'subnet create %s ' - '--network %s --subnet-range 10.0.0.0/24' - % (subnet_name, network_name) + f'subnet create {subnet_name} ' + f'--network {network_name} --subnet-range 10.0.0.0/24' ) self.openstack( - 'port create %s --network %s' - % (self.parent_port_name, network_name) - ) - self.addCleanup( - self.openstack, 'port delete %s' % self.parent_port_name + f'port create {self.parent_port_name} --network {network_name}' ) + self.addCleanup(self.openstack, f'port delete {self.parent_port_name}') json_out = self.openstack( - 'port create %s --network %s -f json' - % (self.sub_port_name, network_name) + f'port create {self.sub_port_name} --network {network_name} -f json' ) self.sub_port_id = json.loads(json_out)['id'] - self.addCleanup(self.openstack, 'port delete %s' % self.sub_port_name) + self.addCleanup(self.openstack, f'port delete {self.sub_port_name}') def test_network_trunk_create_delete(self): trunk_name = uuid.uuid4().hex self.openstack( - 'network trunk create %s --parent-port %s -f json ' - % (trunk_name, self.parent_port_name) + f'network trunk create {trunk_name} --parent-port {self.parent_port_name} -f json ' ) raw_output = self.openstack('network trunk delete ' + trunk_name) self.assertEqual('', raw_output) @@ -67,8 +61,7 @@ def test_network_trunk_list(self): trunk_name = uuid.uuid4().hex json_output = json.loads( self.openstack( - 'network trunk create %s --parent-port %s -f json ' - % (trunk_name, self.parent_port_name) + f'network trunk create {trunk_name} --parent-port {self.parent_port_name} -f json ' ) ) self.addCleanup(self.openstack, 'network trunk delete ' + trunk_name) @@ -81,8 +74,7 @@ def test_network_trunk_set_unset(self): trunk_name = uuid.uuid4().hex json_output = json.loads( self.openstack( - 'network trunk create %s --parent-port %s -f json ' - % (trunk_name, self.parent_port_name) + f'network trunk create {trunk_name} --parent-port {self.parent_port_name} -f json ' ) ) self.addCleanup(self.openstack, 'network trunk delete ' + trunk_name) @@ -98,8 +90,7 @@ def test_network_trunk_set_unset(self): # Add subport to trunk self.openstack( 'network trunk set ' - + '--subport port=%s,segmentation-type=vlan,segmentation-id=42 ' - % (self.sub_port_name) + + f'--subport port={self.sub_port_name},segmentation-type=vlan,segmentation-id=42 ' + trunk_name ) json_output = json.loads( @@ -132,10 +123,9 @@ def test_network_trunk_list_subports(self): trunk_name = uuid.uuid4().hex json_output = json.loads( self.openstack( - 'network trunk create %s --parent-port %s ' - '--subport port=%s,segmentation-type=vlan,segmentation-id=42 ' + f'network trunk create {trunk_name} --parent-port {self.parent_port_name} ' + f'--subport port={self.sub_port_name},segmentation-type=vlan,segmentation-id=42 ' '-f json ' - % (trunk_name, self.parent_port_name, self.sub_port_name) ) ) self.addCleanup(self.openstack, 'network trunk delete ' + trunk_name) @@ -143,7 +133,7 @@ def test_network_trunk_list_subports(self): json_output = json.loads( self.openstack( - 'network subport list --trunk %s -f json' % trunk_name + f'network subport list --trunk {trunk_name} -f json' ) ) self.assertEqual( diff --git a/openstackclient/tests/functional/network/v2/test_port.py b/openstackclient/tests/functional/network/v2/test_port.py index 0ba377341..24fbe0ea1 100644 --- a/openstackclient/tests/functional/network/v2/test_port.py +++ b/openstackclient/tests/functional/network/v2/test_port.py @@ -31,14 +31,14 @@ def setUpClass(cls): cls.NETWORK_NAME = uuid.uuid4().hex # Create a network for the port tests - cls.openstack('network create %s' % cls.NETWORK_NAME) + cls.openstack(f'network create {cls.NETWORK_NAME}') @classmethod def tearDownClass(cls): try: if cls.haz_network: raw_output = cls.openstack( - 'network delete %s' % cls.NETWORK_NAME + f'network delete {cls.NETWORK_NAME}' ) cls.assertOutput('', raw_output) finally: @@ -56,9 +56,7 @@ def test_port_delete(self): self.assertEqual(self.NAME, json_output.get('name')) json_output = self.openstack( - 'port create --network {} {}x'.format( - self.NETWORK_NAME, self.NAME - ), + f'port create --network {self.NETWORK_NAME} {self.NAME}x', parse_output=True, ) id2 = json_output.get('id') @@ -80,20 +78,18 @@ def test_port_list(self): self.assertIsNotNone(id1) mac1 = json_output.get('mac_address') self.assertIsNotNone(mac1) - self.addCleanup(self.openstack, 'port delete %s' % id1) + self.addCleanup(self.openstack, f'port delete {id1}') self.assertEqual(self.NAME, json_output.get('name')) json_output = self.openstack( - 'port create --network {} {}x'.format( - self.NETWORK_NAME, self.NAME - ), + f'port create --network {self.NETWORK_NAME} {self.NAME}x', parse_output=True, ) id2 = json_output.get('id') self.assertIsNotNone(id2) mac2 = json_output.get('mac_address') self.assertIsNotNone(mac2) - self.addCleanup(self.openstack, 'port delete %s' % id2) + self.addCleanup(self.openstack, f'port delete {id2}') self.assertEqual(self.NAME + 'x', json_output.get('name')) # Test list @@ -120,7 +116,7 @@ def test_port_list(self): # Test list --mac-address json_output = self.openstack( - 'port list --mac-address %s' % mac2, + f'port list --mac-address {mac2}', parse_output=True, ) item_map = { @@ -147,22 +143,22 @@ def test_port_set(self): name = uuid.uuid4().hex json_output = self.openstack( 'port create ' - '--network %s ' + f'--network {self.NETWORK_NAME} ' '--description xyzpdq ' - '--disable %s' % (self.NETWORK_NAME, name), + f'--disable {name}', parse_output=True, ) id1 = json_output.get('id') - self.addCleanup(self.openstack, 'port delete %s' % id1) + self.addCleanup(self.openstack, f'port delete {id1}') self.assertEqual(name, json_output.get('name')) self.assertEqual('xyzpdq', json_output.get('description')) self.assertEqual(False, json_output.get('admin_state_up')) - raw_output = self.openstack('port set --enable %s' % name) + raw_output = self.openstack(f'port set --enable {name}') self.assertOutput('', raw_output) json_output = self.openstack( - 'port show %s' % name, + f'port show {name}', parse_output=True, ) sg_id = json_output.get('security_group_ids')[0] @@ -178,7 +174,7 @@ def test_port_set(self): self.assertOutput('', raw_output) json_output = self.openstack( - 'port show %s' % name, + f'port show {name}', parse_output=True, ) self.assertEqual([], json_output.get('security_group_ids')) @@ -186,19 +182,19 @@ def test_port_set(self): def test_port_admin_set(self): """Test create, set (as admin), show, delete""" json_output = self.openstack( - 'port create ' '--network %s %s' % (self.NETWORK_NAME, self.NAME), + 'port create ' f'--network {self.NETWORK_NAME} {self.NAME}', parse_output=True, ) id_ = json_output.get('id') - self.addCleanup(self.openstack, 'port delete %s' % id_) + self.addCleanup(self.openstack, f'port delete {id_}') raw_output = self.openstack( '--os-username admin ' - 'port set --mac-address 11:22:33:44:55:66 %s' % self.NAME + f'port set --mac-address 11:22:33:44:55:66 {self.NAME}' ) self.assertOutput('', raw_output) json_output = self.openstack( - 'port show %s' % self.NAME, + f'port show {self.NAME}', parse_output=True, ) self.assertEqual(json_output.get('mac_address'), '11:22:33:44:55:66') @@ -207,39 +203,39 @@ def test_port_set_sg(self): """Test create, set, show, delete""" sg_name1 = uuid.uuid4().hex json_output = self.openstack( - 'security group create %s' % sg_name1, + f'security group create {sg_name1}', parse_output=True, ) sg_id1 = json_output.get('id') - self.addCleanup(self.openstack, 'security group delete %s' % sg_id1) + self.addCleanup(self.openstack, f'security group delete {sg_id1}') sg_name2 = uuid.uuid4().hex json_output = self.openstack( - 'security group create %s' % sg_name2, + f'security group create {sg_name2}', parse_output=True, ) sg_id2 = json_output.get('id') - self.addCleanup(self.openstack, 'security group delete %s' % sg_id2) + self.addCleanup(self.openstack, f'security group delete {sg_id2}') name = uuid.uuid4().hex json_output = self.openstack( 'port create ' - '--network %s ' - '--security-group %s %s' % (self.NETWORK_NAME, sg_name1, name), + f'--network {self.NETWORK_NAME} ' + f'--security-group {sg_name1} {name}', parse_output=True, ) id1 = json_output.get('id') - self.addCleanup(self.openstack, 'port delete %s' % id1) + self.addCleanup(self.openstack, f'port delete {id1}') self.assertEqual(name, json_output.get('name')) self.assertEqual([sg_id1], json_output.get('security_group_ids')) raw_output = self.openstack( - 'port set ' '--security-group %s %s' % (sg_name2, name) + 'port set ' f'--security-group {sg_name2} {name}' ) self.assertOutput('', raw_output) json_output = self.openstack( - 'port show %s' % name, + f'port show {name}', parse_output=True, ) self.assertEqual(name, json_output.get('name')) @@ -256,15 +252,13 @@ def test_port_set_sg(self): self.assertOutput('', raw_output) json_output = self.openstack( - 'port show %s' % name, + f'port show {name}', parse_output=True, ) self.assertEqual([sg_id2], json_output.get('security_group_ids')) def _create_resource_for_tag_test(self, name, args): return self.openstack( - '{} create --network {} {} {}'.format( - self.base_command, self.NETWORK_NAME, args, name - ), + f'{self.base_command} create --network {self.NETWORK_NAME} {args} {name}', parse_output=True, ) diff --git a/openstackclient/tests/functional/network/v2/test_router.py b/openstackclient/tests/functional/network/v2/test_router.py index bd4261ac7..89d065235 100644 --- a/openstackclient/tests/functional/network/v2/test_router.py +++ b/openstackclient/tests/functional/network/v2/test_router.py @@ -275,17 +275,16 @@ def test_router_add_remove_route(self): subnet_name = uuid.uuid4().hex router_name = uuid.uuid4().hex - self.openstack('network create %s' % network_name) - self.addCleanup(self.openstack, 'network delete %s' % network_name) + self.openstack(f'network create {network_name}') + self.addCleanup(self.openstack, f'network delete {network_name}') self.openstack( - 'subnet create %s ' - '--network %s --subnet-range 10.0.0.0/24' - % (subnet_name, network_name) + f'subnet create {subnet_name} ' + f'--network {network_name} --subnet-range 10.0.0.0/24' ) - self.openstack('router create %s' % router_name) - self.addCleanup(self.openstack, 'router delete %s' % router_name) + self.openstack(f'router create {router_name}') + self.addCleanup(self.openstack, f'router delete {router_name}') self.openstack(f'router add subnet {router_name} {subnet_name}') self.addCleanup( @@ -295,24 +294,20 @@ def test_router_add_remove_route(self): out1 = ( self.openstack( - 'router add route %s ' - '--route destination=10.0.10.0/24,gateway=10.0.0.10' - % router_name, + f'router add route {router_name} ' + '--route destination=10.0.10.0/24,gateway=10.0.0.10', parse_output=True, ), ) self.assertEqual(1, len(out1[0]['routes'])) - self.addCleanup( - self.openstack, 'router set %s --no-route' % router_name - ) + self.addCleanup(self.openstack, f'router set {router_name} --no-route') out2 = ( self.openstack( - 'router add route %s ' + f'router add route {router_name} ' '--route destination=10.0.10.0/24,gateway=10.0.0.10 ' - '--route destination=10.0.11.0/24,gateway=10.0.0.11' - % router_name, + '--route destination=10.0.11.0/24,gateway=10.0.0.11', parse_output=True, ), ) @@ -320,10 +315,9 @@ def test_router_add_remove_route(self): out3 = ( self.openstack( - 'router remove route %s ' + f'router remove route {router_name} ' '--route destination=10.0.11.0/24,gateway=10.0.0.11 ' - '--route destination=10.0.12.0/24,gateway=10.0.0.12' - % router_name, + '--route destination=10.0.12.0/24,gateway=10.0.0.12', parse_output=True, ), ) diff --git a/openstackclient/tests/functional/object/v1/test_object.py b/openstackclient/tests/functional/object/v1/test_object.py index 73044de9d..68ca20433 100644 --- a/openstackclient/tests/functional/object/v1/test_object.py +++ b/openstackclient/tests/functional/object/v1/test_object.py @@ -59,7 +59,7 @@ def _test_object(self, object_file): items = self.parse_listing(raw_output) self.assert_show_fields(items, OBJECT_FIELDS) - raw_output = self.openstack('object list %s' % self.CONTAINER_NAME) + raw_output = self.openstack(f'object list {self.CONTAINER_NAME}') items = self.parse_listing(raw_output) self.assert_table_structure(items, BASIC_LIST_HEADERS) @@ -69,15 +69,12 @@ def _test_object(self, object_file): tmp_file = 'tmp.txt' self.addCleanup(os.remove, tmp_file) self.openstack( - 'object save %s %s --file %s' - % (self.CONTAINER_NAME, object_file, tmp_file) + f'object save {self.CONTAINER_NAME} {object_file} --file {tmp_file}' ) # TODO(stevemar): Assert returned fields raw_output = self.openstack( - 'object save {} {} --file -'.format( - self.CONTAINER_NAME, object_file - ) + f'object save {self.CONTAINER_NAME} {object_file} --file -' ) self.assertEqual(raw_output, 'test content') @@ -91,6 +88,6 @@ def _test_object(self, object_file): self.openstack(f'object create {self.CONTAINER_NAME} {object_file}') raw_output = self.openstack( - 'container delete -r %s' % self.CONTAINER_NAME + f'container delete -r {self.CONTAINER_NAME}' ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/volume/base.py b/openstackclient/tests/functional/volume/base.py index ab05642eb..d1d4ffe00 100644 --- a/openstackclient/tests/functional/volume/base.py +++ b/openstackclient/tests/functional/volume/base.py @@ -40,21 +40,15 @@ def wait_for_status( current_status = output['status'] if current_status == desired_status: print( - '{} {} now has status {}'.format( - check_type, check_name, current_status - ) + f'{check_type} {check_name} now has status {current_status}' ) return print( - 'Checking {} {} Waiting for {} current status: {}'.format( - check_type, check_name, desired_status, current_status - ) + f'Checking {check_type} {check_name} Waiting for {desired_status} current status: {current_status}' ) if current_status in failures: raise Exception( - 'Current status {} of {} {} is one of failures {}'.format( - current_status, check_type, check_name, failures - ) + f'Current status {current_status} of {check_type} {check_name} is one of failures {failures}' ) time.sleep(interval) total_sleep += interval @@ -72,15 +66,9 @@ def wait_for_delete( if check_name not in names: print(f'{check_type} {check_name} is now deleted') return - print( - 'Checking {} {} Waiting for deleted'.format( - check_type, check_name - ) - ) + print(f'Checking {check_type} {check_name} Waiting for deleted') time.sleep(interval) total_sleep += interval raise Exception( - 'Timeout: {} {} was not deleted in {} seconds'.format( - check_type, check_name, wait - ) + f'Timeout: {check_type} {check_name} was not deleted in {wait} seconds' ) diff --git a/openstackclient/tests/functional/volume/v1/test_volume_type.py b/openstackclient/tests/functional/volume/v1/test_volume_type.py index 97c8b66c1..b48dec642 100644 --- a/openstackclient/tests/functional/volume/v1/test_volume_type.py +++ b/openstackclient/tests/functional/volume/v1/test_volume_type.py @@ -32,7 +32,7 @@ def test_volume_type_create_list(self): self.assertEqual(name, cmd_output['name']) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual(self.NAME, cmd_output['name']) @@ -57,19 +57,19 @@ def test_volume_type_set_unset_properties(self): self.assertEqual(name, cmd_output['name']) raw_output = self.openstack( - 'volume type set --property a=b --property c=d %s' % name + f'volume type set --property a=b --property c=d {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) - raw_output = self.openstack('volume type unset --property a %s' % name) + raw_output = self.openstack(f'volume type unset --property a {name}') self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'c': 'd'}, cmd_output['properties']) @@ -84,21 +84,21 @@ def test_volume_type_set_unset_multiple_properties(self): self.assertEqual(name, cmd_output['name']) raw_output = self.openstack( - 'volume type set --property a=b --property c=d %s' % name + f'volume type set --property a=b --property c=d {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) raw_output = self.openstack( - 'volume type unset --property a --property c %s' % name + f'volume type unset --property a --property c {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({}, cmd_output['properties']) @@ -106,9 +106,9 @@ def test_volume_type_set_unset_multiple_properties(self): def test_multi_delete(self): vol_type1 = uuid.uuid4().hex vol_type2 = uuid.uuid4().hex - self.openstack('volume type create %s' % vol_type1) + self.openstack(f'volume type create {vol_type1}') time.sleep(5) - self.openstack('volume type create %s' % vol_type2) + self.openstack(f'volume type create {vol_type2}') time.sleep(5) cmd = f'volume type delete {vol_type1} {vol_type2}' raw_output = self.openstack(cmd) diff --git a/openstackclient/tests/functional/volume/v2/test_volume_backup.py b/openstackclient/tests/functional/volume/v2/test_volume_backup.py index 6bd37f4dc..7ace71901 100644 --- a/openstackclient/tests/functional/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/functional/volume/v2/test_volume_backup.py @@ -56,4 +56,4 @@ def test_volume_backup_restore(self): self.wait_for_status( "volume", backup_restored['volume_id'], "available" ) - self.addCleanup(self.openstack, 'volume delete %s' % vol_id) + self.addCleanup(self.openstack, f'volume delete {vol_id}') diff --git a/openstackclient/tests/functional/volume/v2/test_volume_type.py b/openstackclient/tests/functional/volume/v2/test_volume_type.py index bdbced00f..80bf85a5b 100644 --- a/openstackclient/tests/functional/volume/v2/test_volume_type.py +++ b/openstackclient/tests/functional/volume/v2/test_volume_type.py @@ -32,7 +32,7 @@ def test_volume_type_create_list(self): self.assertEqual(name, cmd_output['name']) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual(name, cmd_output['name']) @@ -57,19 +57,19 @@ def test_volume_type_set_unset_properties(self): self.assertEqual(name, cmd_output['name']) raw_output = self.openstack( - 'volume type set --property a=b --property c=d %s' % name + f'volume type set --property a=b --property c=d {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) - raw_output = self.openstack('volume type unset --property a %s' % name) + raw_output = self.openstack(f'volume type unset --property a {name}') self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'c': 'd'}, cmd_output['properties']) @@ -84,21 +84,21 @@ def test_volume_type_set_unset_multiple_properties(self): self.assertEqual(name, cmd_output['name']) raw_output = self.openstack( - 'volume type set --property a=b --property c=d %s' % name + f'volume type set --property a=b --property c=d {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) raw_output = self.openstack( - 'volume type unset --property a --property c %s' % name + f'volume type unset --property a --property c {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({}, cmd_output['properties']) @@ -112,22 +112,20 @@ def test_volume_type_set_unset_project(self): self.addCleanup(self.openstack, 'volume type delete ' + name) self.assertEqual(name, cmd_output['name']) - raw_output = self.openstack( - 'volume type set --project admin %s' % name - ) + raw_output = self.openstack(f'volume type set --project admin {name}') self.assertEqual("", raw_output) raw_output = self.openstack( - 'volume type unset --project admin %s' % name + f'volume type unset --project admin {name}' ) self.assertEqual("", raw_output) def test_multi_delete(self): vol_type1 = uuid.uuid4().hex vol_type2 = uuid.uuid4().hex - self.openstack('volume type create %s' % vol_type1) + self.openstack(f'volume type create {vol_type1}') time.sleep(5) - self.openstack('volume type create %s' % vol_type2) + self.openstack(f'volume type create {vol_type2}') time.sleep(5) cmd = f'volume type delete {vol_type1} {vol_type2}' raw_output = self.openstack(cmd) diff --git a/openstackclient/tests/functional/volume/v3/test_volume_type.py b/openstackclient/tests/functional/volume/v3/test_volume_type.py index 78f9e4063..421b3224f 100644 --- a/openstackclient/tests/functional/volume/v3/test_volume_type.py +++ b/openstackclient/tests/functional/volume/v3/test_volume_type.py @@ -32,7 +32,7 @@ def test_volume_type_create_list(self): self.assertEqual(name, cmd_output['name']) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual(name, cmd_output['name']) @@ -57,19 +57,19 @@ def test_volume_type_set_unset_properties(self): self.assertEqual(name, cmd_output['name']) raw_output = self.openstack( - 'volume type set --property a=b --property c=d %s' % name + f'volume type set --property a=b --property c=d {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) - raw_output = self.openstack('volume type unset --property a %s' % name) + raw_output = self.openstack(f'volume type unset --property a {name}') self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'c': 'd'}, cmd_output['properties']) @@ -84,21 +84,21 @@ def test_volume_type_set_unset_multiple_properties(self): self.assertEqual(name, cmd_output['name']) raw_output = self.openstack( - 'volume type set --property a=b --property c=d %s' % name + f'volume type set --property a=b --property c=d {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) raw_output = self.openstack( - 'volume type unset --property a --property c %s' % name + f'volume type unset --property a --property c {name}' ) self.assertEqual("", raw_output) cmd_output = self.openstack( - 'volume type show %s' % name, + f'volume type show {name}', parse_output=True, ) self.assertEqual({}, cmd_output['properties']) @@ -112,22 +112,20 @@ def test_volume_type_set_unset_project(self): self.addCleanup(self.openstack, 'volume type delete ' + name) self.assertEqual(name, cmd_output['name']) - raw_output = self.openstack( - 'volume type set --project admin %s' % name - ) + raw_output = self.openstack(f'volume type set --project admin {name}') self.assertEqual("", raw_output) raw_output = self.openstack( - 'volume type unset --project admin %s' % name + f'volume type unset --project admin {name}' ) self.assertEqual("", raw_output) def test_multi_delete(self): vol_type1 = uuid.uuid4().hex vol_type2 = uuid.uuid4().hex - self.openstack('volume type create %s' % vol_type1) + self.openstack(f'volume type create {vol_type1}') time.sleep(5) - self.openstack('volume type create %s' % vol_type2) + self.openstack(f'volume type create {vol_type2}') time.sleep(5) cmd = f'volume type delete {vol_type1} {vol_type2}' raw_output = self.openstack(cmd) diff --git a/openstackclient/tests/unit/api/test_object_store_v1.py b/openstackclient/tests/unit/api/test_object_store_v1.py index 39bc26a61..3ff22f509 100644 --- a/openstackclient/tests/unit/api/test_object_store_v1.py +++ b/openstackclient/tests/unit/api/test_object_store_v1.py @@ -135,16 +135,18 @@ def test_container_list_full_listing(self): self.requests_mock.register_uri( 'GET', FAKE_URL - + '?marker=%s&limit=1&format=json' - % LIST_CONTAINER_RESP[0]['name'], + + '?marker={}&limit=1&format=json'.format( + LIST_CONTAINER_RESP[0]['name'] + ), json=[LIST_CONTAINER_RESP[1]], status_code=200, ) self.requests_mock.register_uri( 'GET', FAKE_URL - + '?marker=%s&limit=1&format=json' - % LIST_CONTAINER_RESP[1]['name'], + + '?marker={}&limit=1&format=json'.format( + LIST_CONTAINER_RESP[1]['name'] + ), json=[], status_code=200, ) diff --git a/openstackclient/tests/unit/identity/v3/test_group.py b/openstackclient/tests/unit/identity/v3/test_group.py index 04e5a9cf9..bd983c618 100644 --- a/openstackclient/tests/unit/identity/v3/test_group.py +++ b/openstackclient/tests/unit/identity/v3/test_group.py @@ -109,12 +109,9 @@ def test_group_add_user_with_error(self, mock_error): self.cmd.take_action(parsed_args) self.fail('CommandError should be raised.') except exceptions.CommandError as e: - msg = "1 of 2 users not added to group %s." % self._group.name + msg = f"1 of 2 users not added to group {self._group.name}." self.assertEqual(msg, str(e)) - msg = ("%(user)s not added to group %(group)s: ") % { - 'user': self.users[0].name, - 'group': self._group.name, - } + msg = f"{self.users[0].name} not added to group {self._group.name}: " mock_error.assert_called_once_with(msg) @@ -561,12 +558,9 @@ def test_group_remove_user_with_error(self, mock_error): self.cmd.take_action(parsed_args) self.fail('CommandError should be raised.') except exceptions.CommandError as e: - msg = "1 of 2 users not removed from group %s." % self._group.id + msg = f"1 of 2 users not removed from group {self._group.id}." self.assertEqual(msg, str(e)) - msg = ("%(user)s not removed from group %(group)s: ") % { - 'user': self.users[0].id, - 'group': self._group.id, - } + msg = f"{self.users[0].id} not removed from group {self._group.id}: " mock_error.assert_called_once_with(msg) diff --git a/openstackclient/tests/unit/image/v2/test_metadef_objects.py b/openstackclient/tests/unit/image/v2/test_metadef_objects.py index 323d8ea15..4996060d9 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_objects.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_objects.py @@ -256,7 +256,6 @@ def test_neg_object_property_show(self): parsed_args, ) self.assertIn( - 'Property %s not found in object %s.' - % (parsed_args.property, parsed_args.object), + f'Property {parsed_args.property} not found in object {parsed_args.object}.', str(exc), ) diff --git a/openstackclient/tests/unit/integ/base.py b/openstackclient/tests/unit/integ/base.py index cecf122b2..e61945a77 100644 --- a/openstackclient/tests/unit/integ/base.py +++ b/openstackclient/tests/unit/integ/base.py @@ -18,7 +18,7 @@ HOST = "192.168.5.41" -URL_BASE = "http://%s/identity" % HOST +URL_BASE = f"http://{HOST}/identity" V2_AUTH_URL = URL_BASE + "/v2.0/" V2_VERSION_RESP = { diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py index 132902fb3..39e636873 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py @@ -903,9 +903,9 @@ def test_set_wrong_options(self): self.cmd.take_action(parsed_args) except exceptions.CommandError as e: msg = ( - 'Failed to set Network QoS rule ID "%(rule)s": Rule type ' + f'Failed to set Network QoS rule ID "{self.new_rule.id}": Rule type ' '"minimum-bandwidth" only requires arguments: direction, ' - 'min_kbps' % {'rule': self.new_rule.id} + 'min_kbps' ) self.assertEqual(msg, str(e)) @@ -1007,9 +1007,9 @@ def test_set_wrong_options(self): self.cmd.take_action(parsed_args) except exceptions.CommandError as e: msg = ( - 'Failed to set Network QoS rule ID "%(rule)s": Rule type ' + f'Failed to set Network QoS rule ID "{self.new_rule.id}": Rule type ' '"minimum-packet-rate" only requires arguments: direction, ' - 'min_kpps' % {'rule': self.new_rule.id} + 'min_kpps' ) self.assertEqual(msg, str(e)) @@ -1111,9 +1111,8 @@ def test_set_wrong_options(self): self.cmd.take_action(parsed_args) except exceptions.CommandError as e: msg = ( - 'Failed to set Network QoS rule ID "%(rule)s": Rule type ' + f'Failed to set Network QoS rule ID "{self.new_rule.id}": Rule type ' '"dscp-marking" only requires arguments: dscp_mark' - % {'rule': self.new_rule.id} ) self.assertEqual(msg, str(e)) @@ -1249,7 +1248,7 @@ def _set_direction(self, direction): self.addCleanup(self._reset_direction, self.new_rule.direction) arglist = [ - '--%s' % direction, + f'--{direction}', self.new_rule.qos_policy_id, self.new_rule.id, ] @@ -1288,9 +1287,9 @@ def test_set_wrong_options(self): self.cmd.take_action(parsed_args) except exceptions.CommandError as e: msg = ( - 'Failed to set Network QoS rule ID "%(rule)s": Rule type ' + f'Failed to set Network QoS rule ID "{self.new_rule.id}": Rule type ' '"bandwidth-limit" only requires arguments: direction, ' - 'max_burst_kbps, max_kbps' % {'rule': self.new_rule.id} + 'max_burst_kbps, max_kbps' ) self.assertEqual(msg, str(e)) diff --git a/openstackclient/tests/unit/network/v2/test_network_trunk.py b/openstackclient/tests/unit/network/v2/test_network_trunk.py index 153d7f995..064c44f37 100644 --- a/openstackclient/tests/unit/network/v2/test_network_trunk.py +++ b/openstackclient/tests/unit/network/v2/test_network_trunk.py @@ -143,13 +143,12 @@ def test_create_full_options(self): "--parent-port", self.new_trunk.port_id, "--subport", - 'port=%(port)s,segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' - % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type'], - 'port': subport['port_id'], - }, + 'port={port},segmentation-type={seg_type},' + 'segmentation-id={seg_id}'.format( + seg_id=subport['segmentation_id'], + seg_type=subport['segmentation_type'], + port=subport['port_id'], + ), self.new_trunk.name, ] verifylist = [ @@ -194,12 +193,11 @@ def test_create_trunk_with_subport_invalid_segmentation_id_fail(self): "--parent-port", self.new_trunk.port_id, "--subport", - "port=%(port)s,segmentation-type=%(seg_type)s," - "segmentation-id=boom" - % { - 'seg_type': subport['segmentation_type'], - 'port': subport['port_id'], - }, + "port={port},segmentation-type={seg_type}," + "segmentation-id=boom".format( + seg_type=subport['segmentation_type'], + port=subport['port_id'], + ), self.new_trunk.name, ] verifylist = [ @@ -265,12 +263,10 @@ def test_create_network_trunk_subports_without_required_key_fail(self): '--parent-port', self.new_trunk.port_id, '--subport', - 'segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' - % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type'], - }, + 'segmentation-type={seg_type},' 'segmentation-id={seg_id}'.format( + seg_id=subport['segmentation_id'], + seg_type=subport['segmentation_type'], + ), self.new_trunk.name, ] verifylist = [ @@ -585,7 +581,7 @@ def setUp(self): def _test_set_network_trunk_attr(self, attr, value): arglist = [ - '--%s' % attr, + f'--{attr}', value, self._trunk[attr], ] @@ -674,13 +670,12 @@ def test_set_network_trunk_subports(self): subport = self._trunk['sub_ports'][0] arglist = [ '--subport', - 'port=%(port)s,segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' - % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type'], - 'port': subport['port_id'], - }, + 'port={port},segmentation-type={seg_type},' + 'segmentation-id={seg_id}'.format( + seg_id=subport['segmentation_id'], + seg_type=subport['segmentation_type'], + port=subport['port_id'], + ), self._trunk['name'], ] verifylist = [ @@ -732,12 +727,10 @@ def test_set_network_trunk_subports_without_required_key_fail(self): subport = self._trunk['sub_ports'][0] arglist = [ '--subport', - 'segmentation-type=%(seg_type)s,' - 'segmentation-id=%(seg_id)s' - % { - 'seg_id': subport['segmentation_id'], - 'seg_type': subport['segmentation_type'], - }, + 'segmentation-type={seg_type},' 'segmentation-id={seg_id}'.format( + seg_id=subport['segmentation_id'], + seg_type=subport['segmentation_type'], + ), self._trunk['name'], ] verifylist = [ @@ -776,7 +769,8 @@ def test_set_trunk_attrs_with_exception(self): with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) self.assertEqual( - "Failed to set trunk '%s': " % self._trunk['name'], str(e) + "Failed to set trunk '{}': ".format(self._trunk['name']), + str(e), ) attrs = {'name': 'reallylongname'} self.network_client.update_trunk.assert_called_once_with( @@ -805,7 +799,9 @@ def test_set_trunk_add_subport_with_exception(self): with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) self.assertEqual( - "Failed to add subports to trunk '%s': " % self._trunk['name'], + "Failed to add subports to trunk '{}': ".format( + self._trunk['name'] + ), str(e), ) self.network_client.update_trunk.assert_called_once_with(self._trunk) diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 7d1963651..d7592e99b 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -179,7 +179,7 @@ def test_create_full_options(self): '--mac-address', 'aa:aa:aa:aa:aa:aa', '--fixed-ip', - 'subnet=%s,ip-address=10.0.0.2' % self.fake_subnet.id, + f'subnet={self.fake_subnet.id},ip-address=10.0.0.2', '--description', self._port.description, '--device', @@ -866,7 +866,7 @@ def _test_create_with_numa_affinity_policy(self, policy=None): 'test-port', ] if policy: - arglist += ['--numa-policy-%s' % policy] + arglist += [f'--numa-policy-{policy}'] numa_affinity_policy = None if not policy else policy verifylist = [ @@ -877,7 +877,7 @@ def _test_create_with_numa_affinity_policy(self, policy=None): ('name', 'test-port'), ] if policy: - verifylist.append(('numa_policy_%s' % policy, True)) + verifylist.append((f'numa_policy_{policy}', True)) parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1418,7 +1418,7 @@ def test_port_list_fixed_ip_opt_ip_address(self): ip_address = self._ports[0].fixed_ips[0]['ip_address'] arglist = [ '--fixed-ip', - "ip-address=%s" % ip_address, + f"ip-address={ip_address}", ] verifylist = [('fixed_ip', [{'ip-address': ip_address}])] @@ -1428,7 +1428,7 @@ def test_port_list_fixed_ip_opt_ip_address(self): self.network_client.ports.assert_called_once_with( **{ - 'fixed_ips': ['ip_address=%s' % ip_address], + 'fixed_ips': [f'ip_address={ip_address}'], 'fields': LIST_FIELDS_TO_RETRIEVE, } ) @@ -1439,7 +1439,7 @@ def test_port_list_fixed_ip_opt_ip_address_substr(self): ip_address_ss = self._ports[0].fixed_ips[0]['ip_address'][:-1] arglist = [ '--fixed-ip', - "ip-substring=%s" % ip_address_ss, + f"ip-substring={ip_address_ss}", ] verifylist = [('fixed_ip', [{'ip-substring': ip_address_ss}])] @@ -1449,7 +1449,7 @@ def test_port_list_fixed_ip_opt_ip_address_substr(self): self.network_client.ports.assert_called_once_with( **{ - 'fixed_ips': ['ip_address_substr=%s' % ip_address_ss], + 'fixed_ips': [f'ip_address_substr={ip_address_ss}'], 'fields': LIST_FIELDS_TO_RETRIEVE, } ) @@ -1460,7 +1460,7 @@ def test_port_list_fixed_ip_opt_subnet_id(self): subnet_id = self._ports[0].fixed_ips[0]['subnet_id'] arglist = [ '--fixed-ip', - "subnet=%s" % subnet_id, + f"subnet={subnet_id}", ] verifylist = [('fixed_ip', [{'subnet': subnet_id}])] @@ -1475,7 +1475,7 @@ def test_port_list_fixed_ip_opt_subnet_id(self): self.network_client.ports.assert_called_once_with( **{ - 'fixed_ips': ['subnet_id=%s' % subnet_id], + 'fixed_ips': [f'subnet_id={subnet_id}'], 'fields': LIST_FIELDS_TO_RETRIEVE, } ) @@ -1505,8 +1505,8 @@ def test_port_list_fixed_ip_opts(self): self.network_client.ports.assert_called_once_with( **{ 'fixed_ips': [ - 'subnet_id=%s' % subnet_id, - 'ip_address=%s' % ip_address, + f'subnet_id={subnet_id}', + f'ip_address={ip_address}', ], 'fields': LIST_FIELDS_TO_RETRIEVE, } @@ -1519,9 +1519,9 @@ def test_port_list_fixed_ips(self): ip_address = self._ports[0].fixed_ips[0]['ip_address'] arglist = [ '--fixed-ip', - "subnet=%s" % subnet_id, + f"subnet={subnet_id}", '--fixed-ip', - "ip-address=%s" % ip_address, + f"ip-address={ip_address}", ] verifylist = [ ('fixed_ip', [{'subnet': subnet_id}, {'ip-address': ip_address}]) @@ -1542,8 +1542,8 @@ def test_port_list_fixed_ips(self): self.network_client.ports.assert_called_once_with( **{ 'fixed_ips': [ - 'subnet_id=%s' % subnet_id, - 'ip_address=%s' % ip_address, + f'subnet_id={subnet_id}', + f'ip_address={ip_address}', ], 'fields': LIST_FIELDS_TO_RETRIEVE, } @@ -2360,11 +2360,11 @@ def test_set_with_no_tag(self): def _test_create_with_numa_affinity_policy(self, policy): arglist = [ - '--numa-policy-%s' % policy, + f'--numa-policy-{policy}', self._port.id, ] verifylist = [ - ('numa_policy_%s' % policy, True), + (f'numa_policy_{policy}', True), ( 'port', self._port.id, diff --git a/openstackclient/tests/unit/utils.py b/openstackclient/tests/unit/utils.py index b9f38f950..2a39ca483 100644 --- a/openstackclient/tests/unit/utils.py +++ b/openstackclient/tests/unit/utils.py @@ -62,7 +62,7 @@ def assertNotCalled(self, m, msg=None): if m.called: if not msg: - msg = 'method %s should not have been called' % m + msg = f'method {m} should not have been called' self.fail(msg) @@ -90,7 +90,7 @@ def check_parser(self, cmd, args, verify_args): argparse.ArgumentError, ): raise ParserException( - "Argument parse failed: %s" % stderr.getvalue() + f"Argument parse failed: {stderr.getvalue()}" ) for av in verify_args: attr, value = av diff --git a/pyproject.toml b/pyproject.toml index 6b13ff246..9a30d5bc5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ quote-style = "preserve" docstring-code-format = true [tool.ruff.lint] -select = ["E4", "E7", "E9", "F", "S"] +select = ["E4", "E7", "E9", "F", "S", "U"] [tool.ruff.lint.per-file-ignores] "openstackclient/tests/*" = ["S"] From b0936c5b3031a0abe50dcf004666e2f58909aa82 Mon Sep 17 00:00:00 2001 From: Nate Johnston Date: Fri, 15 Nov 2019 10:13:13 -0500 Subject: [PATCH 029/245] Handle NotFoundException when listing floating IPs Asking for floating IPs on an undercloud results in a NotFoundException. Make openstackclient handle it gracefully. No test is added for this because it would need to be a scenario test - deploying a cloud without the foalting IP extension loaded. I don't think this edge case is worth an entire new job just to exercise it. Change-Id: I73b544853376d98ab0dbb14e32fefc43c1a8a179 Story: 2006863 --- openstackclient/network/v2/floating_ip.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index a94e58b62..86e517ff0 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -12,6 +12,7 @@ """IP Floating action implementations""" +from openstack import exceptions as sdk_exceptions from osc_lib import utils from osc_lib.utils import tags as _tag @@ -390,7 +391,10 @@ def take_action_network(self, client, parsed_args): _tag.get_tag_filtering_args(parsed_args, query) - data = client.ips(**query) + try: + data = list(client.ips(**query)) + except sdk_exceptions.NotFoundException: + data = [] return ( headers, From 8932282952f85320f364d2bdc5efd0d213019299 Mon Sep 17 00:00:00 2001 From: "Dr. Jens Harbott" Date: Fri, 13 Sep 2024 12:39:52 +0200 Subject: [PATCH 030/245] evacuate: Fix password parameter name for SDK The parameter is called admin_password on the SDK side. Change-Id: I0cd86675a884e6c2cbd3a861b8e111f961f0f336 --- openstackclient/compute/v2/server.py | 2 +- openstackclient/tests/unit/compute/v2/test_server.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 2820ffc27..27063b90f 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -3840,7 +3840,7 @@ def _show_progress(progress): kwargs = { 'host': parsed_args.host, - 'password': parsed_args.password, + 'admin_password': parsed_args.password, } if not sdk_utils.supports_microversion(compute_client, '2.14'): diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 951a8d0e3..06c06e5f4 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -6974,7 +6974,7 @@ def test_evacuate(self): evac_args = { 'host': None, 'on_shared_storage': False, - 'password': None, + 'admin_password': None, } self._test_evacuate(args, verify_args, evac_args) @@ -6991,7 +6991,7 @@ def test_evacuate_with_password(self): evac_args = { 'host': None, 'on_shared_storage': False, - 'password': 'password', + 'admin_password': 'password', } self._test_evacuate(args, verify_args, evac_args) @@ -7008,7 +7008,7 @@ def test_evacuate_with_host(self): ('server', self.server.id), ('host', 'target-host'), ] - evac_args = {'host': host, 'password': None} + evac_args = {'host': host, 'admin_password': None} self._test_evacuate(args, verify_args, evac_args) @@ -7041,7 +7041,7 @@ def test_evacuate_without_share_storage(self): evac_args = { 'host': None, 'on_shared_storage': True, - 'password': None, + 'admin_password': None, } self._test_evacuate(args, verify_args, evac_args) @@ -7072,7 +7072,7 @@ def test_evacuate_with_wait_ok(self, mock_wait_for_status): evac_args = { 'host': None, 'on_shared_storage': False, - 'password': None, + 'admin_password': None, } self._test_evacuate(args, verify_args, evac_args) mock_wait_for_status.assert_called_once_with( From bbe04238a8e472067bc4198ec8e4a4b26da9df51 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 3 Jul 2024 21:26:49 +0100 Subject: [PATCH 031/245] tests: Add functional test for access rules Change-Id: I0131eab2e5395ed530c05a2e9c91b348a7a34c13 Signed-off-by: Stephen Finucane --- .../identity/v3/test_access_rule.py | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 openstackclient/tests/functional/identity/v3/test_access_rule.py diff --git a/openstackclient/tests/functional/identity/v3/test_access_rule.py b/openstackclient/tests/functional/identity/v3/test_access_rule.py new file mode 100644 index 000000000..14717273d --- /dev/null +++ b/openstackclient/tests/functional/identity/v3/test_access_rule.py @@ -0,0 +1,86 @@ +# 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 ast +import json + +from tempest.lib.common.utils import data_utils + +from openstackclient.tests.functional.identity.v3 import common + + +class AccessRuleTests(common.IdentityTests): + ACCESS_RULE_FIELDS = [ + 'id', + 'service', + 'method', + 'path', + ] + ACCESS_RULE_LIST_HEADERS = [ + 'ID', + 'Service', + 'Method', + 'Path', + ] + + def setUp(self): + super().setUp() + + application_credential_name = data_utils.rand_name('name') + access_rules = json.dumps( + [ + { + 'method': 'GET', + 'path': '/v2.1/servers', + 'service': 'compute', + }, + { + 'method': 'GET', + 'path': '/v2.0/networks', + 'service': 'networking', + }, + ] + ) + raw_output = self.openstack( + f"application credential create {application_credential_name} " + f"--access-rules '{access_rules}'" + ) + # we immediately delete the application credential since it will leave + # the access rules around + self.openstack( + f'application credential delete {application_credential_name}' + ) + + items = self.parse_show_as_object(raw_output) + self.access_rule_ids = [ + x['id'] for x in ast.literal_eval(items['access_rules']) + ] + self.addCleanup( + self.openstack, + 'access rule delete ' + + ' '.join([x for x in self.access_rule_ids]), + ) + + def test_access_rule(self): + # list + + raw_output = self.openstack('access rule list') + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ACCESS_RULE_LIST_HEADERS) + + # show + + raw_output = self.openstack( + f'access rule show {self.access_rule_ids[0]}' + ) + items = self.parse_show(raw_output) + self.assert_show_fields(items, self.ACCESS_RULE_FIELDS) From 415f68016ca783b5e44aa6b152c7136463467ab0 Mon Sep 17 00:00:00 2001 From: Antonia Gaete Date: Wed, 12 Jun 2024 17:52:08 +0000 Subject: [PATCH 032/245] identity: Migrate 'access rule' commands to SDK Change-Id: Id5740cc61474650f22f9efe8d148c8c666c3b91e --- openstackclient/identity/v3/access_rule.py | 40 ++++-- .../identity/v3/test_access_rule.py | 8 +- .../unit/identity/v3/test_access_rule.py | 129 +++++++++--------- ...e-access-rule-to-sdk-923682b4c71fea8a.yaml | 8 ++ 4 files changed, 103 insertions(+), 82 deletions(-) create mode 100644 releasenotes/notes/migrate-access-rule-to-sdk-923682b4c71fea8a.yaml diff --git a/openstackclient/identity/v3/access_rule.py b/openstackclient/identity/v3/access_rule.py index 655ad14b1..40bb4cc11 100644 --- a/openstackclient/identity/v3/access_rule.py +++ b/openstackclient/identity/v3/access_rule.py @@ -42,15 +42,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) errors = 0 for ac in parsed_args.access_rule: try: - access_rule = common.get_resource_by_id( - identity_client.access_rules, ac - ) - identity_client.access_rules.delete(access_rule.id) + access_rule = identity_client.get_access_rule(user_id, ac) + identity_client.delete_access_rule(user_id, access_rule.id) except Exception as e: errors += 1 LOG.error( @@ -83,16 +83,17 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity if parsed_args.user: user_id = common.find_user( identity_client, parsed_args.user, parsed_args.user_domain ).id else: - user_id = None + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) columns = ('ID', 'Service', 'Method', 'Path') - data = identity_client.access_rules.list(user=user_id) + data = identity_client.access_rules(user=user_id) return ( columns, ( @@ -119,11 +120,22 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - access_rule = common.get_resource_by_id( - identity_client.access_rules, parsed_args.access_rule - ) + identity_client = self.app.client_manager.sdk_connection.identity + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) - access_rule._info.pop('links', None) + access_rule = identity_client.get_access_rule( + user_id, parsed_args.access_rule + ) - return zip(*sorted(access_rule._info.items())) + columns = ('ID', 'Method', 'Path', 'Service') + return ( + columns, + ( + utils.get_item_properties( + access_rule, + columns, + formatters={}, + ) + ), + ) diff --git a/openstackclient/tests/functional/identity/v3/test_access_rule.py b/openstackclient/tests/functional/identity/v3/test_access_rule.py index 14717273d..c8436c834 100644 --- a/openstackclient/tests/functional/identity/v3/test_access_rule.py +++ b/openstackclient/tests/functional/identity/v3/test_access_rule.py @@ -20,10 +20,10 @@ class AccessRuleTests(common.IdentityTests): ACCESS_RULE_FIELDS = [ - 'id', - 'service', - 'method', - 'path', + 'ID', + 'Service', + 'Method', + 'Path', ] ACCESS_RULE_LIST_HEADERS = [ 'ID', diff --git a/openstackclient/tests/unit/identity/v3/test_access_rule.py b/openstackclient/tests/unit/identity/v3/test_access_rule.py index 5367c1987..068dbb5d8 100644 --- a/openstackclient/tests/unit/identity/v3/test_access_rule.py +++ b/openstackclient/tests/unit/identity/v3/test_access_rule.py @@ -13,72 +13,66 @@ # under the License. # -import copy +from unittest.mock import call -from keystoneclient import exceptions as identity_exc +from openstack import exceptions as sdk_exceptions +from openstack.identity.v3 import access_rule as _access_rule +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.identity.v3 import access_rule -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestAccessRule(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - identity_manager = self.identity_client - self.access_rules_mock = identity_manager.access_rules - self.access_rules_mock.reset_mock() - self.roles_mock = identity_manager.roles - self.roles_mock.reset_mock() +class TestAccessRuleDelete(identity_fakes.TestIdentityv3): + access_rule = sdk_fakes.generate_fake_resource(_access_rule.AccessRule) - -class TestAccessRuleDelete(TestAccessRule): def setUp(self): super().setUp() - # This is the return value for utils.find_resource() - self.access_rules_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ACCESS_RULE), - loaded=True, + self.identity_sdk_client.get_access_rule.return_value = ( + self.access_rule ) - self.access_rules_mock.delete.return_value = None + self.identity_sdk_client.delete_access_rule.return_value = None # Get the command object to test self.cmd = access_rule.DeleteAccessRule(self.app, None) def test_access_rule_delete(self): - arglist = [ - identity_fakes.access_rule_id, - ] - verifylist = [('access_rule', [identity_fakes.access_rule_id])] + arglist = [self.access_rule.id] + verifylist = [('access_rule', [self.access_rule.id])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) + result = self.cmd.take_action(parsed_args) - self.access_rules_mock.delete.assert_called_with( - identity_fakes.access_rule_id, + self.identity_sdk_client.delete_access_rule.assert_called_with( + user_id, + self.access_rule.id, ) self.assertIsNone(result) def test_delete_multi_access_rules_with_exception(self): - # mock returns for common.get_resource_by_id - mock_get = self.access_rules_mock.get - mock_get.side_effect = [ - mock_get.return_value, - identity_exc.NotFound, + self.identity_sdk_client.get_access_rule.side_effect = [ + self.access_rule, + sdk_exceptions.NotFoundException, ] + arglist = [ - identity_fakes.access_rule_id, + self.access_rule.id, 'nonexistent_access_rule', ] verifylist = [ ('access_rule', arglist), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) + try: self.cmd.take_action(parsed_args) self.fail('CommandError should be raised.') @@ -87,26 +81,27 @@ def test_delete_multi_access_rules_with_exception(self): '1 of 2 access rules failed to' ' delete.', str(e) ) - mock_get.assert_any_call(identity_fakes.access_rule_id) - mock_get.assert_any_call('nonexistent_access_rule') + calls = [] + for a in arglist: + calls.append(call(user_id, a)) + + self.identity_sdk_client.get_access_rule.assert_has_calls(calls) - self.assertEqual(2, mock_get.call_count) - self.access_rules_mock.delete.assert_called_once_with( - identity_fakes.access_rule_id + self.assertEqual( + 2, self.identity_sdk_client.get_access_rule.call_count + ) + self.identity_sdk_client.delete_access_rule.assert_called_once_with( + user_id, self.access_rule.id ) -class TestAccessRuleList(TestAccessRule): +class TestAccessRuleList(identity_fakes.TestIdentityv3): + access_rule = sdk_fakes.generate_fake_resource(_access_rule.AccessRule) + def setUp(self): super().setUp() - self.access_rules_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ACCESS_RULE), - loaded=True, - ), - ] + self.identity_sdk_client.access_rules.return_value = [self.access_rule] # Get the command object to test self.cmd = access_rule.ListAccessRule(self.app, None) @@ -116,31 +111,34 @@ def test_access_rule_list(self): verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) + columns, data = self.cmd.take_action(parsed_args) - self.access_rules_mock.list.assert_called_with(user=None) + self.identity_sdk_client.access_rules.assert_called_with(user=user_id) collist = ('ID', 'Service', 'Method', 'Path') self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.access_rule_id, - identity_fakes.access_rule_service, - identity_fakes.access_rule_method, - identity_fakes.access_rule_path, + self.access_rule.id, + self.access_rule.service, + self.access_rule.method, + self.access_rule.path, ), ) self.assertEqual(datalist, tuple(data)) -class TestAccessRuleShow(TestAccessRule): +class TestAccessRuleShow(identity_fakes.TestIdentityv3): + access_rule = sdk_fakes.generate_fake_resource(_access_rule.AccessRule) + def setUp(self): super().setUp() - self.access_rules_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ACCESS_RULE), - loaded=True, + self.identity_sdk_client.get_access_rule.return_value = ( + self.access_rule ) # Get the command object to test @@ -148,25 +146,28 @@ def setUp(self): def test_access_rule_show(self): arglist = [ - identity_fakes.access_rule_id, + self.access_rule.id, ] verifylist = [ - ('access_rule', identity_fakes.access_rule_id), + ('access_rule', self.access_rule.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) + columns, data = self.cmd.take_action(parsed_args) - self.access_rules_mock.get.assert_called_with( - identity_fakes.access_rule_id + self.identity_sdk_client.get_access_rule.assert_called_with( + user_id, self.access_rule.id ) - collist = ('id', 'method', 'path', 'service') + collist = ('ID', 'Method', 'Path', 'Service') self.assertEqual(collist, columns) datalist = ( - identity_fakes.access_rule_id, - identity_fakes.access_rule_method, - identity_fakes.access_rule_path, - identity_fakes.access_rule_service, + self.access_rule.id, + self.access_rule.method, + self.access_rule.path, + self.access_rule.service, ) self.assertEqual(datalist, data) diff --git a/releasenotes/notes/migrate-access-rule-to-sdk-923682b4c71fea8a.yaml b/releasenotes/notes/migrate-access-rule-to-sdk-923682b4c71fea8a.yaml new file mode 100644 index 000000000..9f6deb8d4 --- /dev/null +++ b/releasenotes/notes/migrate-access-rule-to-sdk-923682b4c71fea8a.yaml @@ -0,0 +1,8 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``access rule list`` + - ``access rule delete`` + - ``access rule show`` From 9c223696a0e3b45bcd8bcb0a8f81e3354ccea9c6 Mon Sep 17 00:00:00 2001 From: zhangoic Date: Mon, 13 Mar 2017 18:47:19 +0800 Subject: [PATCH 033/245] Add status filtering options to port list The patch adds "--status" options to list command. Change-Id: I710437f67e9432b2b6389986bc922eac4a60c934 Partial-bug: #1672680 --- openstackclient/network/v2/port.py | 15 ++++++++++++ .../tests/unit/network/v2/test_port.py | 23 +++++++++++++++++++ ...t-list-status-option-f51da0aed0528a5d.yaml | 5 ++++ 3 files changed, 43 insertions(+) create mode 100644 releasenotes/notes/add-port-list-status-option-f51da0aed0528a5d.yaml diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index b1e60df4e..957967df2 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -792,6 +792,19 @@ def get_parser(self, prog_name): metavar='', help=_("List only ports associated with this security group"), ) + # the API sadly reports these in upper case and while it would be + # wonderful to plaster over this ugliness client-side, there are + # already users in the wild doing this in upper case that we need to + # support + parser.add_argument( + '--status', + metavar='', + choices=('ACTIVE', 'BUILD', 'DOWN', 'ERROR'), + help=_( + "List ports according to their status " + "('ACTIVE', 'BUILD', 'DOWN', 'ERROR')" + ), + ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--fixed-ip', @@ -859,6 +872,8 @@ def take_action(self, parsed_args): filters['network_id'] = network.id if parsed_args.mac_address: filters['mac_address'] = parsed_args.mac_address + if parsed_args.status: + filters['status'] = parsed_args.status if parsed_args.project: project_id = identity_common.find_project( identity_client, diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 7d1963651..18ae6d2f3 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -1712,6 +1712,29 @@ def test_port_list_security_group(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) + def test_port_list_status(self): + arglist = [ + '--status', + 'ACTIVE', + ] + verifylist = [ + ('status', 'ACTIVE'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = { + 'status': 'ACTIVE', + 'fields': LIST_FIELDS_TO_RETRIEVE, + } + + self.network_client.ports.assert_called_once_with(**filters) + self.assertEqual(self.columns, columns) + self.assertEqual( + self.data, + list(data), + ) + class TestSetPort(TestPort): _port = network_fakes.create_one_port({'tags': ['green', 'red']}) diff --git a/releasenotes/notes/add-port-list-status-option-f51da0aed0528a5d.yaml b/releasenotes/notes/add-port-list-status-option-f51da0aed0528a5d.yaml new file mode 100644 index 000000000..762ea6dca --- /dev/null +++ b/releasenotes/notes/add-port-list-status-option-f51da0aed0528a5d.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``--status`` option to ``port list`` command. + [Bug `1672680 `_] From 033793aa0e96e3b8c8e729ff8fa67a9a37029e55 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 16 Sep 2024 12:43:04 +0100 Subject: [PATCH 034/245] identity: Don't pass unset options when creating user In change I06f3848812bce60c65909f1311f36b70eba427d4, we migrated the 'user *' commands from keystoneclient to SDK. One side effect of this is that we are no longer able to rely on keystoneclient's 'filter_none' helper method that filters out parameters that are set to None. As such, we now need to do this ourselves. Eventually, it would be nice if SDK provided such functionality itself. The same change also introduced a bug where the '--domain' argument was being used to lookup a project rather than the '--project-domain' argument. This is also corrected. Change-Id: I1204ca611a74d134c879467d6c2b73f16e043213 Signed-off-by: Stephen Finucane Closes-bug: #2080600 --- openstackclient/identity/v3/user.py | 50 +++++++--- .../tests/unit/identity/v3/test_user.py | 94 +------------------ 2 files changed, 39 insertions(+), 105 deletions(-) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 38b9a0ab0..545b1cda7 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -249,26 +249,44 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity + kwargs = {} + domain_id = None if parsed_args.domain: domain_id = identity_client.find_domain( - name_or_id=parsed_args.domain, + parsed_args.domain, ignore_missing=False, ).id + kwargs['domain_id'] = domain_id - project_id = None if parsed_args.project: - project_id = identity_client.find_project( - name_or_id=parsed_args.project, + project_domain_id = None + if parsed_args.project_domain: + project_domain_id = identity_client.find_domain( + parsed_args.project_domain, + ignore_missing=False, + ).id + kwargs['default_project_id'] = identity_client.find_project( + parsed_args.project, ignore_missing=False, - domain_id=domain_id, + domain_id=project_domain_id, ).id + if parsed_args.description: + kwargs['description'] = parsed_args.description + + if parsed_args.email: + kwargs['email'] = parsed_args.email + is_enabled = True if parsed_args.disable: is_enabled = False - if parsed_args.password_prompt: - parsed_args.password = utils.get_password(self.app.stdin) + + password = None + if parsed_args.password: + password = parsed_args.password + elif parsed_args.password_prompt: + password = utils.get_password(self.app.stdin) if not parsed_args.password: LOG.warning( @@ -278,24 +296,26 @@ def take_action(self, parsed_args): ) ) options = _get_options_for_user(identity_client, parsed_args) + if options: + kwargs['options'] = options try: user = identity_client.create_user( - default_project_id=project_id, - description=parsed_args.description, - domain_id=domain_id, - email=parsed_args.email, is_enabled=is_enabled, name=parsed_args.name, - password=parsed_args.password, - options=options, + password=password, + **kwargs, ) except sdk_exc.ConflictException: if parsed_args.or_show: + kwargs = {} + if domain_id: + kwargs['domain_id'] = domain_id + user = identity_client.find_user( - name_or_id=parsed_args.name, - domain_id=domain_id, + parsed_args.name, ignore_missing=False, + **kwargs, ) LOG.info(_('Returning existing user %s'), user.name) else: diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index 19c1a6dcc..7f4c2497e 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -91,11 +91,6 @@ def test_user_create_no_options(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, - 'options': {}, 'is_enabled': True, 'password': None, } @@ -127,11 +122,6 @@ def test_user_create_password(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, - 'options': {}, 'is_enabled': True, 'password': 'secret', } @@ -165,11 +155,6 @@ def test_user_create_password_prompt(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, - 'options': {}, 'is_enabled': True, 'password': 'abc123', } @@ -200,12 +185,8 @@ def test_user_create_email(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, 'email': 'barney@example.com', 'is_enabled': True, - 'options': {}, 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -236,11 +217,7 @@ def test_user_create_project(self): kwargs = { 'name': self.user.name, 'default_project_id': self.project.id, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, - 'options': {}, 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -284,14 +261,13 @@ def test_user_create_project_domain(self): kwargs = { 'name': self.user.name, 'default_project_id': self.project.id, - 'description': None, - 'domain_id': None, - 'email': None, - 'options': {}, 'is_enabled': True, 'password': None, } - self.identity_sdk_client.create_user.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_once_with(**kwargs) + self.identity_sdk_client.find_domain.assert_called_once_with( + self.project.domain_id, ignore_missing=False + ) self.assertEqual(self.columns, columns) datalist = ( @@ -328,11 +304,7 @@ def test_user_create_domain(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, 'domain_id': self.domain.id, - 'email': None, - 'options': {}, 'is_enabled': True, 'password': None, } @@ -361,11 +333,6 @@ def test_user_create_enable(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, - 'options': {}, 'is_enabled': True, 'password': None, } @@ -394,11 +361,6 @@ def test_user_create_disable(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, - 'options': {}, 'is_enabled': False, 'password': None, } @@ -428,10 +390,6 @@ def test_user_create_ignore_lockout_failure_attempts(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'ignore_lockout_failure_attempts': True}, 'password': None, @@ -462,10 +420,6 @@ def test_user_create_no_ignore_lockout_failure_attempts(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'ignore_lockout_failure_attempts': False}, 'password': None, @@ -496,10 +450,6 @@ def test_user_create_ignore_password_expiry(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'ignore_password_expiry': True}, 'password': None, @@ -530,10 +480,6 @@ def test_user_create_no_ignore_password_expiry(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'ignore_password_expiry': False}, 'password': None, @@ -564,10 +510,6 @@ def test_user_create_ignore_change_password_upon_first_use(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'ignore_change_password_upon_first_use': True}, 'password': None, @@ -598,10 +540,6 @@ def test_user_create_no_ignore_change_password_upon_first_use(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'ignore_change_password_upon_first_use': False}, 'password': None, @@ -632,10 +570,6 @@ def test_user_create_enables_lock_password(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'lock_password': True}, 'password': None, @@ -666,10 +600,6 @@ def test_user_create_disables_lock_password(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'lock_password': False}, 'password': None, @@ -700,10 +630,6 @@ def test_user_create_enable_multi_factor_auth(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'multi_factor_auth_enabled': True}, 'password': None, @@ -734,10 +660,6 @@ def test_user_create_disable_multi_factor_auth(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': {'multi_factor_auth_enabled': False}, 'password': None, @@ -774,10 +696,6 @@ def test_user_create_option_with_multi_factor_auth_rule(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': { 'multi_factor_auth_rules': [["password", "totp"], ["password"]] @@ -815,10 +733,6 @@ def test_user_create_with_multiple_options(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project_id': None, - 'description': None, - 'domain_id': None, - 'email': None, 'is_enabled': True, 'options': { 'ignore_password_expiry': True, From 58d1b06fdcda8e147ee0de77b9f1c835b97030fd Mon Sep 17 00:00:00 2001 From: Sylvain Bauza Date: Wed, 18 Sep 2024 08:46:44 +0200 Subject: [PATCH 035/245] evacuate SDK actually uses admin_pass param Change I0cd86675a884e6c2cbd3a861b8e111f961f0f336 was incorrect, the SDK param name is admin_pass. Change-Id: Ibe22c3d7d7ba0f1a5178475143e35fee5cac2ca2 --- openstackclient/compute/v2/server.py | 2 +- openstackclient/tests/unit/compute/v2/test_server.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index dbd5dab5d..f7b860df8 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -3872,7 +3872,7 @@ def _show_progress(progress): kwargs = { 'host': parsed_args.host, - 'admin_password': parsed_args.password, + 'admin_pass': parsed_args.password, } if not sdk_utils.supports_microversion(compute_client, '2.14'): diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 44d89a483..a07e33333 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -6982,7 +6982,7 @@ def test_evacuate(self): evac_args = { 'host': None, 'on_shared_storage': False, - 'admin_password': None, + 'admin_pass': None, } self._test_evacuate(args, verify_args, evac_args) @@ -6999,7 +6999,7 @@ def test_evacuate_with_password(self): evac_args = { 'host': None, 'on_shared_storage': False, - 'admin_password': 'password', + 'admin_pass': 'password', } self._test_evacuate(args, verify_args, evac_args) @@ -7016,7 +7016,7 @@ def test_evacuate_with_host(self): ('server', self.server.id), ('host', 'target-host'), ] - evac_args = {'host': host, 'admin_password': None} + evac_args = {'host': host, 'admin_pass': None} self._test_evacuate(args, verify_args, evac_args) @@ -7049,7 +7049,7 @@ def test_evacuate_without_share_storage(self): evac_args = { 'host': None, 'on_shared_storage': True, - 'admin_password': None, + 'admin_pass': None, } self._test_evacuate(args, verify_args, evac_args) @@ -7080,7 +7080,7 @@ def test_evacuate_with_wait_ok(self, mock_wait_for_status): evac_args = { 'host': None, 'on_shared_storage': False, - 'admin_password': None, + 'admin_pass': None, } self._test_evacuate(args, verify_args, evac_args) mock_wait_for_status.assert_called_once_with( From 52d56b3fd94fa66edb2a24250f3a937831cb108f Mon Sep 17 00:00:00 2001 From: Alfredo Moralejo Date: Mon, 30 Sep 2024 12:08:47 +0200 Subject: [PATCH 036/245] identity: in `service set` command, don't pass the enable option when it is None Currently, it is passing None value which is not accepted by keystone parameters validation: BadRequestException: 400: Client Error for url: ... Invalid input for field 'enabled': None is not of type 'boolean' Failed validating 'type' in schema['properties']['enabled']: {'enum': [True, False, None], 'type': 'boolean'} On instance['enabled']: None Closes-Bug: #2083021 Change-Id: Ia8772560deb54e71672102157659d4eb22e6ad59 --- openstackclient/identity/v3/service.py | 3 ++- openstackclient/tests/unit/identity/v3/test_service.py | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index f04fa979d..41faa2551 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -225,7 +225,8 @@ def take_action(self, parsed_args): kwargs['name'] = parsed_args.name if parsed_args.description: kwargs['description'] = parsed_args.description - kwargs['is_enabled'] = parsed_args.is_enabled + if parsed_args.is_enabled is not None: + kwargs['is_enabled'] = parsed_args.is_enabled identity_client.update_service(service.id, **kwargs) diff --git a/openstackclient/tests/unit/identity/v3/test_service.py b/openstackclient/tests/unit/identity/v3/test_service.py index 4c02d9891..d6dd0287b 100644 --- a/openstackclient/tests/unit/identity/v3/test_service.py +++ b/openstackclient/tests/unit/identity/v3/test_service.py @@ -311,7 +311,6 @@ def test_service_set_type(self): # Set expected values kwargs = { 'type': self.service.type, - 'is_enabled': None, } self.identity_sdk_client.update_service.assert_called_with( self.service.id, **kwargs @@ -338,7 +337,6 @@ def test_service_set_name(self): # Set expected values kwargs = { 'name': self.service.name, - 'is_enabled': None, } self.identity_sdk_client.update_service.assert_called_with( self.service.id, **kwargs @@ -365,7 +363,6 @@ def test_service_set_description(self): # Set expected values kwargs = { 'description': self.service.description, - 'is_enabled': None, } self.identity_sdk_client.update_service.assert_called_with( self.service.id, **kwargs From c8326b55259378c7b6a57b7a708008ab3d57bea1 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Wed, 2 Oct 2024 00:16:40 +0900 Subject: [PATCH 037/245] Always resolve domain id The --user-domain option and the --project-domain option may take id or name. In case name is given it should be translated to id. Closes-Bug: 2083390 Change-Id: Idf3f113a74452daabc80660574030cb9b24b1a15 --- openstackclient/identity/common.py | 3 +++ .../identity/v3/role_assignment.py | 27 ++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index 50ad2fed6..b8a5b885f 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -204,6 +204,7 @@ def find_group(identity_client, name_or_id, domain_name_or_id=None): identity_client.groups, name_or_id, groups.Group ) else: + domain_id = find_domain(identity_client, domain_id).id return _find_identity_resource( identity_client.groups, name_or_id, @@ -219,6 +220,7 @@ def find_project(identity_client, name_or_id, domain_name_or_id=None): identity_client.projects, name_or_id, projects.Project ) else: + domain_id = find_domain(identity_client, domain_id).id return _find_identity_resource( identity_client.projects, name_or_id, @@ -234,6 +236,7 @@ def find_user(identity_client, name_or_id, domain_name_or_id=None): identity_client.users, name_or_id, users.User ) else: + domain_id = find_domain(identity_client, domain_id).id return _find_identity_resource( identity_client.users, name_or_id, users.User, domain_id=domain_id ) diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py index 975329793..7d4dfcdb0 100644 --- a/openstackclient/identity/v3/role_assignment.py +++ b/openstackclient/identity/v3/role_assignment.py @@ -146,12 +146,19 @@ def take_action(self, parsed_args): domain_id=role_domain_id, ) + user_domain_id = None + if parsed_args.user_domain: + project_domain_id = _find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.user_domain, + ) + user_id = None if parsed_args.user: user_id = _find_sdk_id( identity_client.find_user, name_or_id=parsed_args.user, - domain_id=parsed_args.user_domain, + domain_id=user_domain_id, ) elif parsed_args.authuser: if auth_ref: @@ -171,6 +178,13 @@ def take_action(self, parsed_args): name_or_id=parsed_args.domain, ) + project_domain_id = None + if parsed_args.project_domain: + project_domain_id = _find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.project_domain, + ) + project_id = None if parsed_args.project: project_id = _find_sdk_id( @@ -178,7 +192,7 @@ def take_action(self, parsed_args): name_or_id=common._get_token_resource( identity_client, 'project', parsed_args.project ), - domain_id=parsed_args.project_domain, + domain_id=project_domain_id, ) elif parsed_args.authproject: if auth_ref: @@ -187,12 +201,19 @@ def take_action(self, parsed_args): name_or_id=auth_ref.project_id, ) + group_domain_id = None + if parsed_args.group_domain: + group_domain_id = _find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.group_domain, + ) + group_id = None if parsed_args.group: group_id = _find_sdk_id( identity_client.find_group, name_or_id=parsed_args.group, - domain_id=parsed_args.group_domain, + domain_id=group_domain_id, ) include_names = True if parsed_args.names else None From b6b18489b0b1b200c40e060f6c830103070d90f5 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Mon, 7 Oct 2024 13:21:22 +0000 Subject: [PATCH 038/245] Fix volume backup show by name When we show a volume backup by name, it calls the get_backup method in SDK which is only used for getting a backup by ID. This patch modifies the approach to call find_backup method which first tries the find by ID and then find by name logic eventually returning the backup details. Story: 2011234 Task: 51127 Change-Id: I926d8de9810fcf2e5335bbe35aaab15e1e36a5cb --- openstackclient/tests/unit/volume/v2/test_volume_backup.py | 4 ++-- openstackclient/tests/unit/volume/v3/test_volume_backup.py | 4 ++-- openstackclient/volume/v2/volume_backup.py | 2 +- openstackclient/volume/v3/volume_backup.py | 2 +- .../notes/fix-show-backup-by-name-0759c55396be77a3.yaml | 5 +++++ 5 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/fix-show-backup-by-name-0759c55396be77a3.yaml diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index 483ca24de..b63aa0341 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -544,7 +544,7 @@ class TestBackupShow(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volume_sdk_client.get_backup.return_value = self.backup + self.volume_sdk_client.find_backup.return_value = self.backup # Get the command object to test self.cmd = volume_backup.ShowVolumeBackup(self.app, None) @@ -554,7 +554,7 @@ def test_backup_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_sdk_client.get_backup.assert_called_with(self.backup.id) + self.volume_sdk_client.find_backup.assert_called_with(self.backup.id) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v3/test_volume_backup.py b/openstackclient/tests/unit/volume/v3/test_volume_backup.py index d4e7e7924..2a6ae17b8 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_backup.py @@ -876,7 +876,7 @@ class TestBackupShow(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volume_sdk_client.get_backup.return_value = self.backup + self.volume_sdk_client.find_backup.return_value = self.backup # Get the command object to test self.cmd = volume_backup.ShowVolumeBackup(self.app, None) @@ -886,7 +886,7 @@ def test_backup_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_sdk_client.get_backup.assert_called_with(self.backup.id) + self.volume_sdk_client.find_backup.assert_called_with(self.backup.id) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index fd59c0cef..5295733ae 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -439,7 +439,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume - backup = volume_client.get_backup(parsed_args.backup) + backup = volume_client.find_backup(parsed_args.backup) columns = ( "availability_zone", "container", diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py index 471b4cee1..3d2e743ec 100644 --- a/openstackclient/volume/v3/volume_backup.py +++ b/openstackclient/volume/v3/volume_backup.py @@ -643,7 +643,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume - backup = volume_client.get_backup(parsed_args.backup) + backup = volume_client.find_backup(parsed_args.backup) columns = ( "availability_zone", "container", diff --git a/releasenotes/notes/fix-show-backup-by-name-0759c55396be77a3.yaml b/releasenotes/notes/fix-show-backup-by-name-0759c55396be77a3.yaml new file mode 100644 index 000000000..7ed9d72cc --- /dev/null +++ b/releasenotes/notes/fix-show-backup-by-name-0759c55396be77a3.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixed the ``openstack volume backup show`` command + to show a backup by name. From 7c6b47b451f30d1d3965358c515baae87955d7dc Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 15 Oct 2024 17:41:50 +0100 Subject: [PATCH 039/245] clientmanager: Check for 'block-storage' service type This is a fun one driven by two separate changes. We recently started checking whether the volume service was available before setting quotas in order to allow us to use quota set for other services [1]. This merged a number of weeks ago and was included in 7.1.0. More recently, we modified DevStack to stop publishing a service catalog entry with a service type of 'volumev3', preferring instead to use the correct 'block-storage' service type. Taken separately, neither of these changes would have caused issues. Together, they mean our lookups for the volume service now fail and we can't set volume quotas. Fix things by checking for the block-storage service type also. A future change will raise a warning (later an error) if the volume service is not found and you're attempting to set a quota since this is clearly a mistake. Change-Id: Ibbeef52225e18757cd28d0fbfb14c1ca06975b60 Signed-off-by: Stephen Finucane Closes-bug: #2084580 --- openstackclient/common/clientmanager.py | 7 ++++++- releasenotes/notes/bug-2084580-cb1e8c47501e730c.yaml | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/bug-2084580-cb1e8c47501e730c.yaml diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index d1b470d9e..42e2b24fe 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -129,10 +129,15 @@ def is_compute_endpoint_enabled(self): # TODO(stephenfin): Drop volume_client argument in OSC 8.0 or later. def is_volume_endpoint_enabled(self, volume_client=None): """Check if volume endpoint is enabled""" + # We check against the service type and all aliases defined by the + # Service Types Authority + # https://service-types.openstack.org/service-types.json return ( - self.is_service_available('volume') is not False + self.is_service_available('block-storage') is not False + or self.is_service_available('volume') is not False or self.is_service_available('volumev3') is not False or self.is_service_available('volumev2') is not False + or self.is_service_available('block-store') is not False ) diff --git a/releasenotes/notes/bug-2084580-cb1e8c47501e730c.yaml b/releasenotes/notes/bug-2084580-cb1e8c47501e730c.yaml new file mode 100644 index 000000000..5ac30b373 --- /dev/null +++ b/releasenotes/notes/bug-2084580-cb1e8c47501e730c.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + The ``quota set`` and ``limits show`` commands will now check for the + ``block-storage`` and ``block-store`` service types along with ``volume``, + ``volumev2`` and ``volumev3``. + + [Bug `2084580 `_] From db115c09a229cc955f96f26bc4c9e5d7ec679469 Mon Sep 17 00:00:00 2001 From: Yosef Salmalian Date: Mon, 14 Oct 2024 16:29:17 +0330 Subject: [PATCH 040/245] remove project from network flavor profile Removing project from network_flavor_profile as neutron api does not uses project. Closes-Bug: 2046496 Change-Id: I77b0544cf8629fb0a5b9914361a007d28b2b5662 --- .../network/v2/network_flavor_profile.py | 18 +--------- .../network/v2/test_network_flavor_profile.py | 35 ------------------- 2 files changed, 1 insertion(+), 52 deletions(-) diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py index 1fb5a8011..d9a4b5d2e 100644 --- a/openstackclient/network/v2/network_flavor_profile.py +++ b/openstackclient/network/v2/network_flavor_profile.py @@ -18,7 +18,6 @@ from osc_lib import utils from openstackclient.i18n import _ -from openstackclient.identity import common as identity_common from openstackclient.network import common LOG = logging.getLogger(__name__) @@ -29,7 +28,7 @@ def _get_columns(item): 'is_enabled': 'enabled', } - hidden_columns = ['location', 'name', 'tenant_id'] + hidden_columns = ['location', 'name', 'tenant_id', 'project_id'] return utils.get_osc_show_columns_for_sdk_resource( item, column_map, hidden_columns ) @@ -47,14 +46,6 @@ def _get_attrs(client_manager, parsed_args): attrs['enabled'] = True if parsed_args.disable: attrs['enabled'] = False - if 'project' in parsed_args and parsed_args.project is not None: - identity_client = client_manager.identity - project_id = identity_common.find_project( - identity_client, - parsed_args.project, - parsed_args.project_domain, - ).id - attrs['project_id'] = project_id return attrs @@ -68,12 +59,6 @@ class CreateNetworkFlavorProfile( def get_parser(self, prog_name): parser = super().get_parser(prog_name) - parser.add_argument( - '--project', - metavar="", - help=_("Owner's project (name or ID)"), - ) - identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--description', metavar="", @@ -212,7 +197,6 @@ def get_parser(self, prog_name): metavar="", help=_("Flavor profile to update (ID only)"), ) - identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--description', metavar="", diff --git a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py index 44cc4f493..d94926c4c 100644 --- a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py +++ b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py @@ -23,14 +23,11 @@ class TestFlavorProfile(network_fakes.TestNetworkV2): def setUp(self): super().setUp() - # Get the ProjectManager Mock - self.projects_mock = self.identity_client.projects # Get the DomainManager Mock self.domains_mock = self.identity_client.domains class TestCreateFlavorProfile(TestFlavorProfile): - project = identity_fakes_v3.FakeProject.create_one_project() domain = identity_fakes_v3.FakeDomain.create_one_domain() new_flavor_profile = network_fakes.create_one_service_profile() @@ -40,7 +37,6 @@ class TestCreateFlavorProfile(TestFlavorProfile): 'enabled', 'id', 'meta_info', - 'project_id', ) data = ( @@ -49,7 +45,6 @@ class TestCreateFlavorProfile(TestFlavorProfile): new_flavor_profile.is_enabled, new_flavor_profile.id, new_flavor_profile.meta_info, - new_flavor_profile.project_id, ) def setUp(self): @@ -57,7 +52,6 @@ def setUp(self): self.network_client.create_service_profile = mock.Mock( return_value=self.new_flavor_profile ) - self.projects_mock.get.return_value = self.project # Get the command object to test self.cmd = network_flavor_profile.CreateNetworkFlavorProfile( self.app, None @@ -67,10 +61,6 @@ def test_create_all_options(self): arglist = [ "--description", self.new_flavor_profile.description, - "--project", - self.new_flavor_profile.project_id, - '--project-domain', - self.domain.name, "--enable", "--driver", self.new_flavor_profile.driver, @@ -80,8 +70,6 @@ def test_create_all_options(self): verifylist = [ ('description', self.new_flavor_profile.description), - ('project', self.new_flavor_profile.project_id), - ('project_domain', self.domain.name), ('enable', True), ('driver', self.new_flavor_profile.driver), ('metainfo', self.new_flavor_profile.meta_info), @@ -93,7 +81,6 @@ def test_create_all_options(self): self.network_client.create_service_profile.assert_called_once_with( **{ 'description': self.new_flavor_profile.description, - 'project_id': self.project.id, 'enabled': self.new_flavor_profile.is_enabled, 'driver': self.new_flavor_profile.driver, 'metainfo': self.new_flavor_profile.meta_info, @@ -106,10 +93,6 @@ def test_create_with_metainfo(self): arglist = [ "--description", self.new_flavor_profile.description, - "--project", - self.new_flavor_profile.project_id, - '--project-domain', - self.domain.name, "--enable", "--metainfo", self.new_flavor_profile.meta_info, @@ -117,8 +100,6 @@ def test_create_with_metainfo(self): verifylist = [ ('description', self.new_flavor_profile.description), - ('project', self.new_flavor_profile.project_id), - ('project_domain', self.domain.name), ('enable', True), ('metainfo', self.new_flavor_profile.meta_info), ] @@ -129,7 +110,6 @@ def test_create_with_metainfo(self): self.network_client.create_service_profile.assert_called_once_with( **{ 'description': self.new_flavor_profile.description, - 'project_id': self.project.id, 'enabled': self.new_flavor_profile.is_enabled, 'metainfo': self.new_flavor_profile.meta_info, } @@ -141,10 +121,6 @@ def test_create_with_driver(self): arglist = [ "--description", self.new_flavor_profile.description, - "--project", - self.new_flavor_profile.project_id, - '--project-domain', - self.domain.name, "--enable", "--driver", self.new_flavor_profile.driver, @@ -152,8 +128,6 @@ def test_create_with_driver(self): verifylist = [ ('description', self.new_flavor_profile.description), - ('project', self.new_flavor_profile.project_id), - ('project_domain', self.domain.name), ('enable', True), ('driver', self.new_flavor_profile.driver), ] @@ -164,7 +138,6 @@ def test_create_with_driver(self): self.network_client.create_service_profile.assert_called_once_with( **{ 'description': self.new_flavor_profile.description, - 'project_id': self.project.id, 'enabled': self.new_flavor_profile.is_enabled, 'driver': self.new_flavor_profile.driver, } @@ -176,17 +149,11 @@ def test_create_without_driver_and_metainfo(self): arglist = [ "--description", self.new_flavor_profile.description, - "--project", - self.new_flavor_profile.project_id, - '--project-domain', - self.domain.name, "--enable", ] verifylist = [ ('description', self.new_flavor_profile.description), - ('project', self.new_flavor_profile.project_id), - ('project_domain', self.domain.name), ('enable', True), ] @@ -377,7 +344,6 @@ class TestShowFlavorProfile(TestFlavorProfile): 'enabled', 'id', 'meta_info', - 'project_id', ) data = ( network_flavor_profile.description, @@ -385,7 +351,6 @@ class TestShowFlavorProfile(TestFlavorProfile): network_flavor_profile.is_enabled, network_flavor_profile.id, network_flavor_profile.meta_info, - network_flavor_profile.project_id, ) def setUp(self): From 695d025f0028dae2715096b53629f1a00810d270 Mon Sep 17 00:00:00 2001 From: Tobias Urdin Date: Thu, 17 Oct 2024 16:25:13 +0200 Subject: [PATCH 041/245] Show Created At column for volume backups in v3 The change in [1] erroneously only added the created_at column for volume v2 API and not also for the volume v3 API. [1] https://review.opendev.org/c/openstack/python-openstackclient/+/920003 Change-Id: Iae0b4f21cbc31ae7464a79c8f5e01446ca4ff098 --- openstackclient/tests/unit/volume/v3/test_volume_backup.py | 3 +++ openstackclient/volume/v3/volume_backup.py | 2 ++ .../volume-backup-created-at-list-v3-47400b31be5143bc.yaml | 5 +++++ 3 files changed, 10 insertions(+) create mode 100644 releasenotes/notes/volume-backup-created-at-list-v3-47400b31be5143bc.yaml diff --git a/openstackclient/tests/unit/volume/v3/test_volume_backup.py b/openstackclient/tests/unit/volume/v3/test_volume_backup.py index d4e7e7924..864f16241 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_backup.py @@ -333,6 +333,7 @@ class TestBackupList(volume_fakes.TestVolume): 'Status', 'Size', 'Incremental', + 'Created At', ) columns_long = columns + ( 'Availability Zone', @@ -350,6 +351,7 @@ class TestBackupList(volume_fakes.TestVolume): b.status, b.size, b.is_incremental, + b.created_at, ) ) data_long = [] @@ -362,6 +364,7 @@ class TestBackupList(volume_fakes.TestVolume): b.status, b.size, b.is_incremental, + b.created_at, b.availability_zone, volume_backup.VolumeIdColumn(b.volume_id), b.container, diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py index 471b4cee1..45d50b843 100644 --- a/openstackclient/volume/v3/volume_backup.py +++ b/openstackclient/volume/v3/volume_backup.py @@ -305,6 +305,7 @@ def take_action(self, parsed_args): 'status', 'size', 'is_incremental', + 'created_at', ) column_headers = ( 'ID', @@ -313,6 +314,7 @@ def take_action(self, parsed_args): 'Status', 'Size', 'Incremental', + 'Created At', ) if parsed_args.long: columns += ('availability_zone', 'volume_id', 'container') diff --git a/releasenotes/notes/volume-backup-created-at-list-v3-47400b31be5143bc.yaml b/releasenotes/notes/volume-backup-created-at-list-v3-47400b31be5143bc.yaml new file mode 100644 index 000000000..88aa7143f --- /dev/null +++ b/releasenotes/notes/volume-backup-created-at-list-v3-47400b31be5143bc.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Listing volume backups now shows the created_at column when + volume v3 API is used. From 2e491191e56dba338e7604cbc0c45b7658d4ee7a Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Fri, 25 Oct 2024 18:27:48 +0900 Subject: [PATCH 042/245] Fix ignored --user-domain in role assignment list Fix the wrong value assignment which made the --user-domain option ignored. Unit tests are updated to verify usage of domain options to avoid further regressions. Also drop the redundant look up of domain id to avoid unnecessary API call. Closes-Bug: #2085604 Change-Id: I5112b8e831fb26eb6544615277f0d3fe4f15dc5a --- openstackclient/identity/common.py | 56 ++-- .../identity/v3/role_assignment.py | 2 +- .../unit/identity/v3/test_role_assignment.py | 261 ++++++++++++++++++ 3 files changed, 284 insertions(+), 35 deletions(-) diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index b8a5b885f..9f4402f5b 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -184,13 +184,6 @@ def _get_token_resource(client, resource, parsed_name, parsed_domain=None): return parsed_name -def _get_domain_id_if_requested(identity_client, domain_name_or_id): - if not domain_name_or_id: - return None - domain = find_domain(identity_client, domain_name_or_id) - return domain.id - - def find_domain(identity_client, name_or_id): return _find_identity_resource( identity_client.domains, name_or_id, domains.Domain @@ -198,48 +191,43 @@ def find_domain(identity_client, name_or_id): def find_group(identity_client, name_or_id, domain_name_or_id=None): - domain_id = _get_domain_id_if_requested(identity_client, domain_name_or_id) - if not domain_id: + if domain_name_or_id is None: return _find_identity_resource( identity_client.groups, name_or_id, groups.Group ) - else: - domain_id = find_domain(identity_client, domain_id).id - return _find_identity_resource( - identity_client.groups, - name_or_id, - groups.Group, - domain_id=domain_id, - ) + + domain_id = find_domain(identity_client, domain_name_or_id).id + return _find_identity_resource( + identity_client.groups, + name_or_id, + groups.Group, + domain_id=domain_id, + ) def find_project(identity_client, name_or_id, domain_name_or_id=None): - domain_id = _get_domain_id_if_requested(identity_client, domain_name_or_id) - if not domain_id: + if domain_name_or_id is None: return _find_identity_resource( identity_client.projects, name_or_id, projects.Project ) - else: - domain_id = find_domain(identity_client, domain_id).id - return _find_identity_resource( - identity_client.projects, - name_or_id, - projects.Project, - domain_id=domain_id, - ) + domain_id = find_domain(identity_client, domain_name_or_id).id + return _find_identity_resource( + identity_client.projects, + name_or_id, + projects.Project, + domain_id=domain_id, + ) def find_user(identity_client, name_or_id, domain_name_or_id=None): - domain_id = _get_domain_id_if_requested(identity_client, domain_name_or_id) - if not domain_id: + if domain_name_or_id is None: return _find_identity_resource( identity_client.users, name_or_id, users.User ) - else: - domain_id = find_domain(identity_client, domain_id).id - return _find_identity_resource( - identity_client.users, name_or_id, users.User, domain_id=domain_id - ) + domain_id = find_domain(identity_client, domain_name_or_id).id + return _find_identity_resource( + identity_client.users, name_or_id, users.User, domain_id=domain_id + ) def _find_identity_resource( diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py index 7d4dfcdb0..12e1527b6 100644 --- a/openstackclient/identity/v3/role_assignment.py +++ b/openstackclient/identity/v3/role_assignment.py @@ -148,7 +148,7 @@ def take_action(self, parsed_args): user_domain_id = None if parsed_args.user_domain: - project_domain_id = _find_sdk_id( + user_domain_id = _find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.user_domain, ) diff --git a/openstackclient/tests/unit/identity/v3/test_role_assignment.py b/openstackclient/tests/unit/identity/v3/test_role_assignment.py index ec7c7da56..65dad0d77 100644 --- a/openstackclient/tests/unit/identity/v3/test_role_assignment.py +++ b/openstackclient/tests/unit/identity/v3/test_role_assignment.py @@ -165,10 +165,13 @@ def test_role_assignment_list_user(self): arglist = ['--user', self.user.name] verifylist = [ ('user', self.user.name), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -181,6 +184,79 @@ def test_role_assignment_list_user(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.find_user.assert_called_with( + name_or_id=self.user.name, ignore_missing=False, domain_id=None + ) + self.identity_sdk_client.role_assignments.assert_called_with( + role_id=None, + user_id=self.user.id, + group_id=None, + scope_project_id=None, + scope_domain_id=None, + scope_system=None, + effective=None, + include_names=None, + inherited_to=None, + ) + + self.assertEqual(self.columns, columns) + datalist = ( + ( + self.role.id, + self.user.id, + '', + '', + self.domain.id, + '', + False, + ), + ( + self.role.id, + self.user.id, + '', + self.project.id, + '', + '', + False, + ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_user_with_domain(self): + self.identity_sdk_client.role_assignments.return_value = [ + self.assignment_with_domain_id_and_user_id, + self.assignment_with_project_id_and_user_id, + ] + + arglist = ['--user', self.user.name, '--user-domain', self.domain.name] + verifylist = [ + ('user', self.user.name), + ('user_domain', self.domain.name), + ('group', None), + ('group_domain', None), + ('system', None), + ('domain', None), + ('project', None), + ('role', None), + ('effective', None), + ('inherited', False), + ('names', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.identity_sdk_client.find_domain.assert_called_with( + name_or_id=self.domain.name, ignore_missing=False + ) + self.identity_sdk_client.find_user.assert_called_with( + name_or_id=self.user.name, + ignore_missing=False, + domain_id=self.domain.id, + ) self.identity_sdk_client.role_assignments.assert_called_with( role_id=None, user_id=self.user.id, @@ -230,10 +306,13 @@ def test_role_assignment_list_user_not_found(self, find_mock): arglist = ['--user', self.user.id] verifylist = [ ('user', self.user.id), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -290,10 +369,13 @@ def test_role_assignment_list_group(self): arglist = ['--group', self.group.name] verifylist = [ ('user', None), + ('user_domain', None), ('group', self.group.name), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -306,6 +388,85 @@ def test_role_assignment_list_group(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.find_group.assert_called_with( + name_or_id=self.group.name, ignore_missing=False, domain_id=None + ) + self.identity_sdk_client.role_assignments.assert_called_with( + role_id=None, + user_id=None, + group_id=self.group.id, + scope_project_id=None, + scope_domain_id=None, + scope_system=None, + effective=None, + include_names=None, + inherited_to=None, + ) + + self.assertEqual(self.columns, columns) + datalist = ( + ( + self.role.id, + '', + self.group.id, + '', + self.domain.id, + '', + False, + ), + ( + self.role.id, + '', + self.group.id, + self.project.id, + '', + '', + False, + ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_group_with_domain(self): + self.identity_sdk_client.role_assignments.return_value = [ + self.assignment_with_domain_id_and_group_id, + self.assignment_with_project_id_and_group_id, + ] + + arglist = [ + '--group', + self.group.name, + '--group-domain', + self.domain.name, + ] + verifylist = [ + ('user', None), + ('user_domain', None), + ('group', self.group.name), + ('group_domain', self.domain.name), + ('system', None), + ('domain', None), + ('project', None), + ('project_domain', None), + ('role', None), + ('effective', None), + ('inherited', False), + ('names', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.identity_sdk_client.find_domain.assert_called_with( + name_or_id=self.domain.name, ignore_missing=False + ) + self.identity_sdk_client.find_group.assert_called_with( + name_or_id=self.group.name, + ignore_missing=False, + domain_id=self.domain.id, + ) self.identity_sdk_client.role_assignments.assert_called_with( role_id=None, user_id=None, @@ -350,10 +511,13 @@ def test_role_assignment_list_domain(self): arglist = ['--domain', self.domain.name] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', self.domain.name), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -410,10 +574,13 @@ def test_role_assignment_list_project(self): arglist = ['--project', self.project.name] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', self.project.name), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -426,6 +593,85 @@ def test_role_assignment_list_project(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.find_project.assert_called_with( + name_or_id=self.project.name, ignore_missing=False, domain_id=None + ) + self.identity_sdk_client.role_assignments.assert_called_with( + role_id=None, + user_id=None, + group_id=None, + scope_project_id=self.project.id, + scope_domain_id=None, + scope_system=None, + effective=None, + include_names=None, + inherited_to=None, + ) + + self.assertEqual(self.columns, columns) + datalist = ( + ( + self.role.id, + self.user.id, + '', + self.project.id, + '', + '', + False, + ), + ( + self.role.id, + '', + self.group.id, + self.project.id, + '', + '', + False, + ), + ) + self.assertEqual(datalist, tuple(data)) + + def test_role_assignment_list_project_with_domain(self): + self.identity_sdk_client.role_assignments.return_value = [ + self.assignment_with_project_id_and_user_id, + self.assignment_with_project_id_and_group_id, + ] + + arglist = [ + '--project', + self.project.name, + '--project-domain', + self.domain.name, + ] + verifylist = [ + ('user', None), + ('user_domain', None), + ('group', None), + ('group_domain', None), + ('system', None), + ('domain', None), + ('project', self.project.name), + ('project_domain', self.domain.name), + ('role', None), + ('effective', None), + ('inherited', False), + ('names', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.identity_sdk_client.find_domain.assert_called_with( + name_or_id=self.domain.name, ignore_missing=False + ) + self.identity_sdk_client.find_project.assert_called_with( + name_or_id=self.project.name, + ignore_missing=False, + domain_id=self.domain.id, + ) self.identity_sdk_client.role_assignments.assert_called_with( role_id=None, user_id=None, @@ -476,10 +722,13 @@ def test_role_assignment_list_def_creds(self): ] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -529,10 +778,13 @@ def test_role_assignment_list_effective(self): arglist = ['--effective'] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', True), ('inherited', False), @@ -611,10 +863,13 @@ def test_role_assignment_list_inherited(self): arglist = ['--inherited'] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', True), @@ -707,10 +962,13 @@ def test_role_assignment_list_include_names(self): arglist = ['--names'] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', None), ('effective', None), ('inherited', False), @@ -799,10 +1057,13 @@ def test_role_assignment_list_domain_role(self): ] verifylist = [ ('user', None), + ('user_domain', None), ('group', None), + ('group_domain', None), ('system', None), ('domain', None), ('project', None), + ('project_domain', None), ('role', role_2.name), ('effective', None), ('inherited', False), From 47144103cabf11edb108ae80f700ea936bf50b30 Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Mon, 2 Sep 2024 12:37:35 +0200 Subject: [PATCH 043/245] Add "trusted" attribute to the "port" Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/927723 Related-bug: #2060916 Change-Id: I8e3d4ee2208ef6bb6c96ee430d7b550a0720431e --- openstackclient/network/v2/port.py | 29 +++++++ .../tests/unit/network/v2/fakes.py | 1 + .../tests/unit/network/v2/test_port.py | 75 +++++++++++++++++++ ...sted-vif-to-the-port-0a0c76d9da8f3da0.yaml | 7 ++ 4 files changed, 112 insertions(+) create mode 100644 releasenotes/notes/Add-trusted-vif-to-the-port-0a0c76d9da8f3da0.yaml diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index b1e60df4e..9b7a93c3d 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -93,6 +93,7 @@ def _get_columns(item): 'status': 'status', 'tags': 'tags', 'trunk_details': 'trunk_details', + 'trusted': 'trusted', 'updated_at': 'updated_at', } return ( @@ -222,6 +223,10 @@ def _get_attrs(client_manager, parsed_args): and parsed_args.hardware_offload_type ): attrs['hardware_offload_type'] = parsed_args.hardware_offload_type + if parsed_args.not_trusted: + attrs['trusted'] = False + if parsed_args.trusted: + attrs['trusted'] = True return attrs @@ -388,6 +393,25 @@ def _add_updatable_args(parser, create=False): '(repeat option to set multiple hints)' ), ) + port_trusted = parser.add_mutually_exclusive_group() + port_trusted.add_argument( + '--trusted', + action='store_true', + help=_( + "Set port to be trusted. This will be populated into the " + "'binding:profile' dictionary and passed to the services " + "which expect it in this dictionary (for example, Nova)" + ), + ) + port_trusted.add_argument( + '--not-trusted', + action='store_true', + help=_( + "Set port to be not trusted. This will be populated into the " + "'binding:profile' dictionary and passed to the services " + "which expect it in this dictionary (for example, Nova)" + ), + ) # TODO(abhiraut): Use the SDK resource mapped attribute names once the @@ -1122,6 +1146,11 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) attrs['hints'] = expanded_hints + if parsed_args.not_trusted: + attrs['trusted'] = False + if parsed_args.trusted: + attrs['trusted'] = True + attrs.update( self._parse_extra_properties(parsed_args.extra_properties) ) diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index 999e1027c..909bfaf47 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -1676,6 +1676,7 @@ def create_one_port(attrs=None): 'qos_network_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, 'tags': [], + 'trusted': None, 'propagate_uplink_status': False, 'location': 'MUNCHMUNCHMUNCH', } diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 7d1963651..2d40e637a 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -73,6 +73,7 @@ def _get_common_cols_data(fake_port): 'security_group_ids', 'status', 'tags', + 'trusted', 'trunk_details', 'updated_at', ) @@ -114,6 +115,7 @@ def _get_common_cols_data(fake_port): format_columns.ListColumn(fake_port.security_group_ids), fake_port.status, format_columns.ListColumn(fake_port.tags), + fake_port.trusted, fake_port.trunk_details, fake_port.updated_at, ) @@ -1111,6 +1113,50 @@ def test_create_with_hardware_offload_type_switchdev(self): def test_create_with_hardware_offload_type_null(self): self._test_create_with_hardware_offload_type() + def _test_create_with_trusted_field(self, trusted): + arglist = [ + '--network', + self._port.network_id, + 'test-port', + ] + if trusted: + arglist += ['--trusted'] + else: + arglist += ['--not-trusted'] + + verifylist = [ + ( + 'network', + self._port.network_id, + ), + ('name', 'test-port'), + ] + if trusted: + verifylist.append(('trusted', True)) + else: + verifylist.append(('trusted', False)) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + create_args = { + 'admin_state_up': True, + 'network_id': self._port.network_id, + 'name': 'test-port', + } + create_args['trusted'] = trusted + self.network_client.create_port.assert_called_once_with(**create_args) + + self.assertEqual(set(self.columns), set(columns)) + self.assertCountEqual(self.data, data) + + def test_create_with_trusted_true(self): + self._test_create_with_trusted_field(True) + + def test_create_with_trusted_false(self): + self._test_create_with_trusted_field(False) + class TestDeletePort(TestPort): # Ports to delete. @@ -2497,6 +2543,35 @@ def test_set_hints_valid_json(self): ) self.assertIsNone(result) + def _test_set_trusted_field(self, trusted): + arglist = [self._port.id] + if trusted: + arglist += ['--trusted'] + else: + arglist += ['--not-trusted'] + + verifylist = [ + ('port', self._port.id), + ] + if trusted: + verifylist.append(('trusted', True)) + else: + verifylist.append(('trusted', False)) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network_client.update_port.assert_called_once_with( + self._port, **{'trusted': trusted} + ) + self.assertIsNone(result) + + def test_set_trusted_true(self): + self._test_set_trusted_field(True) + + def test_set_trusted_false(self): + self._test_set_trusted_field(False) + class TestShowPort(TestPort): # The port to show. diff --git a/releasenotes/notes/Add-trusted-vif-to-the-port-0a0c76d9da8f3da0.yaml b/releasenotes/notes/Add-trusted-vif-to-the-port-0a0c76d9da8f3da0.yaml new file mode 100644 index 000000000..ffa966954 --- /dev/null +++ b/releasenotes/notes/Add-trusted-vif-to-the-port-0a0c76d9da8f3da0.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Add ``trusted`` attribute to the ``port create`` and ``port set`` commands. + It can be set to ``true`` with ``--trusted`` and to ``false`` with + ``--not-trusted`` CLI arguments passed to the ``port create`` and ``port + set`` commands`` From e5ccf1eb1ca4c2efe946f6dc8967041ee82d4ec5 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 5 Nov 2024 17:07:49 +0000 Subject: [PATCH 044/245] common: Use correct argument for volume limits The sooner we have type hints in SDK, the better /o\ Change-Id: Iaf9596aea02f683c280ae68504a14d43dbd6134a Closes-bug: #2077634 Signed-off-by: Stephen Finucane --- openstackclient/common/limits.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py index cb2f331f6..ac613fd83 100644 --- a/openstackclient/common/limits.py +++ b/openstackclient/common/limits.py @@ -130,7 +130,7 @@ def take_action(self, parsed_args): if self.app.client_manager.is_volume_endpoint_enabled(): volume_client = self.app.client_manager.sdk_connection.volume volume_limits = volume_client.get_limits( - project_id=project_id, + project=project_id, ) if parsed_args.is_absolute: From 8890981491cca87fbbd89f1fb32c428828a56976 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 12 Nov 2024 14:10:47 +0000 Subject: [PATCH 045/245] Skip tips jobs on pre-commit config update We only need to care about the unversioned tips jobs since [1] will take care of the others. [1] https://review.opendev.org/c/openstack/openstack-zuul-jobs/+/933197 Change-Id: I3d569dc496a995eee58fdbcf4a42a187143d1b24 Signed-off-by: Stephen Finucane --- .zuul.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.zuul.yaml b/.zuul.yaml index ac4d63a04..5352e894f 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -6,6 +6,11 @@ Run unit tests for OpenStackClient with master branch of important libs. Takes advantage of the base tox job's install-siblings feature. + irrelevant-files: &common-irrelevant-files + - ^.*\.rst$ + - ^doc/.*$ + - ^releasenotes/.*$ + - ^\.pre-commit-config\.yaml$ required-projects: - openstack/cliff - openstack/keystoneauth From 58e21d8e2bd69ca8a7a6af4c25f9feb9dac29458 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 12 Nov 2024 17:25:12 +0000 Subject: [PATCH 046/245] requirements: Remove unused test deps oslotest is not used anywhere, while requests is already a runtime dep. Change-Id: I852b7d8664cddf22b1314b057c42930580a1f9f4 Signed-off-by: Stephen Finucane --- test-requirements.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index 275c2ff11..c9c1b28cc 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,7 +1,5 @@ coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD -oslotest>=3.2.0 # Apache-2.0 -requests>=2.14.2 # Apache-2.0 requests-mock>=1.2.0 # Apache-2.0 stestr>=1.0.0 # Apache-2.0 testtools>=2.2.0 # MIT From fb1f841d2de9091c4e672a046c0faf529378366c Mon Sep 17 00:00:00 2001 From: Takashi Natsume Date: Sun, 29 Sep 2024 00:03:43 +0900 Subject: [PATCH 047/245] Replace deprecated datetime.utcnow() The datetime.utcnow() is deprecated in Python 3.12. Replace datetime.utcnow() with datetime.now(datetime.timezone.utc).replace(tzinfo=None). Change-Id: Ic20174a9c6cacac05471fa57b105c1f784a73057 Signed-off-by: Takashi Natsume --- openstackclient/compute/v2/usage.py | 4 ++-- .../functional/identity/v3/test_application_credential.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index 27f9335bb..5d91358b9 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -153,7 +153,7 @@ def _format_project(project): ) date_cli_format = "%Y-%m-%d" - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) if parsed_args.start: start = datetime.datetime.strptime( @@ -238,7 +238,7 @@ def take_action(self, parsed_args): identity_client = self.app.client_manager.identity compute_client = self.app.client_manager.sdk_connection.compute date_cli_format = "%Y-%m-%d" - now = datetime.datetime.utcnow() + now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) if parsed_args.start: start = datetime.datetime.strptime( diff --git a/openstackclient/tests/functional/identity/v3/test_application_credential.py b/openstackclient/tests/functional/identity/v3/test_application_credential.py index 441259278..39c735b2b 100644 --- a/openstackclient/tests/functional/identity/v3/test_application_credential.py +++ b/openstackclient/tests/functional/identity/v3/test_application_credential.py @@ -107,7 +107,8 @@ def test_application_credential_create_with_options(self): secret = data_utils.rand_name('secret') description = data_utils.rand_name('description') tomorrow = ( - datetime.datetime.utcnow() + datetime.timedelta(days=1) + datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) + + datetime.timedelta(days=1) ).strftime('%Y-%m-%dT%H:%M:%S%z') role1, role2 = self._create_role_assignments() raw_output = self.openstack( From 9c6df823e24fcb94823ebce9515973386cfed372 Mon Sep 17 00:00:00 2001 From: Antonia Gaete Date: Mon, 8 Jul 2024 18:11:52 +0000 Subject: [PATCH 048/245] identity: Migrate 'credential' commands to SDK Change-Id: I49391fec3d7b6a1b81438a2a311ac7b86173a6a4 --- openstackclient/identity/v3/credential.py | 73 ++++++---- .../tests/unit/identity/v3/test_credential.py | 137 ++++++++++-------- ...te-credential-to-sdk-33a841847fe7d568.yaml | 10 ++ 3 files changed, 129 insertions(+), 91 deletions(-) create mode 100644 releasenotes/notes/migrate-credential-to-sdk-33a841847fe7d568.yaml diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py index 1b0d52517..4784b836d 100644 --- a/openstackclient/identity/v3/credential.py +++ b/openstackclient/identity/v3/credential.py @@ -28,6 +28,23 @@ LOG = logging.getLogger(__name__) +def _format_credential(credential): + columns = ( + 'blob', + 'id', + 'project_id', + 'type', + 'user_id', + ) + return ( + columns, + utils.get_item_properties( + credential, + columns, + ), + ) + + class CreateCredential(command.ShowOne): _description = _("Create new credential") @@ -60,25 +77,24 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - user_id = utils.find_resource( - identity_client.users, parsed_args.user + identity_client = self.app.client_manager.sdk_connection.identity + user_id = identity_client.find_user( + parsed_args.user, ignore_missing=False ).id if parsed_args.project: - project = utils.find_resource( - identity_client.projects, parsed_args.project + project = identity_client.find_project( + parsed_args.project, ignore_missing=False ).id else: project = None - credential = identity_client.credentials.create( + credential = identity_client.create_credential( user=user_id, type=parsed_args.type, blob=parsed_args.data, project=project, ) - credential._info.pop('links') - return zip(*sorted(credential._info.items())) + return _format_credential(credential) class DeleteCredential(command.Command): @@ -95,11 +111,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity result = 0 for i in parsed_args.credential: try: - identity_client.credentials.delete(i) + identity_client.delete_credential(i) except Exception as e: result += 1 LOG.error( @@ -137,14 +153,17 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity kwargs = {} if parsed_args.user: - user_id = common.find_user( - identity_client, - parsed_args.user, - parsed_args.user_domain, + domain_id = None + if parsed_args.user_domain: + domain_id = identity_client.find_domain( + parsed_args.user_domain, ignore_missing=False + ) + user_id = identity_client.find_user( + parsed_args.user, domain_id=domain_id, ignore_missing=False ).id kwargs["user_id"] = user_id @@ -153,7 +172,8 @@ def take_action(self, parsed_args): columns = ('ID', 'Type', 'User ID', 'Blob', 'Project ID') column_headers = ('ID', 'Type', 'User ID', 'Data', 'Project ID') - data = self.app.client_manager.identity.credentials.list(**kwargs) + data = identity_client.credentials(**kwargs) + return ( column_headers, ( @@ -206,20 +226,20 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - user_id = utils.find_resource( - identity_client.users, parsed_args.user + user_id = identity_client.find_user( + parsed_args.user, ignore_missing=False ).id if parsed_args.project: - project = utils.find_resource( - identity_client.projects, parsed_args.project + project = identity_client.find_project( + parsed_args.project, ignore_missing=False ).id else: project = None - identity_client.credentials.update( + identity_client.update_credential( parsed_args.credential, user=user_id, type=parsed_args.type, @@ -241,10 +261,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - credential = utils.find_resource( - identity_client.credentials, parsed_args.credential - ) + identity_client = self.app.client_manager.sdk_connection.identity + credential = identity_client.get_credential(parsed_args.credential) - credential._info.pop('links') - return zip(*sorted(credential._info.items())) + return _format_credential(credential) diff --git a/openstackclient/tests/unit/identity/v3/test_credential.py b/openstackclient/tests/unit/identity/v3/test_credential.py index 794f40b05..2e90d2d78 100644 --- a/openstackclient/tests/unit/identity/v3/test_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_credential.py @@ -10,9 +10,13 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from unittest.mock import call +from openstack import exceptions as sdk_exceptions +from openstack.identity.v3 import credential as _credential +from openstack.identity.v3 import project as _project +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.identity.v3 import credential @@ -20,26 +24,9 @@ from openstackclient.tests.unit import utils -class TestCredential(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - # Get a shortcut to the CredentialManager Mock - self.credentials_mock = self.identity_client.credentials - self.credentials_mock.reset_mock() - - # Get a shortcut to the UserManager Mock - self.users_mock = self.identity_client.users - self.users_mock.reset_mock() - - # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.identity_client.projects - self.projects_mock.reset_mock() - - -class TestCredentialCreate(TestCredential): - user = identity_fakes.FakeUser.create_one_user() - project = identity_fakes.FakeProject.create_one_project() +class TestCredentialCreate(identity_fakes.TestIdentityv3): + user = sdk_fakes.generate_fake_resource(_user.User) + project = sdk_fakes.generate_fake_resource(_project.Project) columns = ( 'blob', 'id', @@ -51,12 +38,17 @@ class TestCredentialCreate(TestCredential): def setUp(self): super().setUp() - self.credential = identity_fakes.FakeCredential.create_one_credential( - attrs={'user_id': self.user.id, 'project_id': self.project.id} + self.credential = sdk_fakes.generate_fake_resource( + resource_type=_credential.Credential, + user_id=self.user.id, + project_id=self.project.id, + type='cert', ) - self.credentials_mock.create.return_value = self.credential - self.users_mock.get.return_value = self.user - self.projects_mock.get.return_value = self.project + self.identity_sdk_client.create_credential.return_value = ( + self.credential + ) + self.identity_sdk_client.find_user.return_value = self.user + self.identity_sdk_client.find_project.return_value = self.project self.data = ( self.credential.blob, self.credential.id, @@ -86,7 +78,9 @@ def test_credential_create_no_options(self): 'blob': self.credential.blob, 'project': None, } - self.credentials_mock.create.assert_called_once_with(**kwargs) + self.identity_sdk_client.create_credential.assert_called_once_with( + **kwargs + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -116,42 +110,48 @@ def test_credential_create_with_options(self): 'blob': self.credential.blob, 'project': self.credential.project_id, } - self.credentials_mock.create.assert_called_once_with(**kwargs) + self.identity_sdk_client.create_credential.assert_called_once_with( + **kwargs + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) -class TestCredentialDelete(TestCredential): - credentials = identity_fakes.FakeCredential.create_credentials(count=2) - +class TestCredentialDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.credentials_mock.delete.return_value = None + self.identity_sdk_client.delete_credential.return_value = None # Get the command object to test self.cmd = credential.DeleteCredential(self.app, None) def test_credential_delete(self): + credential = sdk_fakes.generate_fake_resource( + _credential.Credential, + ) arglist = [ - self.credentials[0].id, + credential.id, ] verifylist = [ - ('credential', [self.credentials[0].id]), + ('credential', [credential.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.credentials_mock.delete.assert_called_with( - self.credentials[0].id, + self.identity_sdk_client.delete_credential.assert_called_with( + credential.id, ) self.assertIsNone(result) def test_credential_multi_delete(self): + credentials = sdk_fakes.generate_fake_resources( + _credential.Credential, count=2 + ) arglist = [] - for c in self.credentials: + for c in credentials: arglist.append(c.id) verifylist = [ ('credential', arglist), @@ -161,25 +161,26 @@ def test_credential_multi_delete(self): result = self.cmd.take_action(parsed_args) calls = [] - for c in self.credentials: + for c in credentials: calls.append(call(c.id)) - self.credentials_mock.delete.assert_has_calls(calls) + self.identity_sdk_client.delete_credential.assert_has_calls(calls) self.assertIsNone(result) def test_credential_multi_delete_with_exception(self): + credential = sdk_fakes.generate_fake_resource( + _credential.Credential, + ) arglist = [ - self.credentials[0].id, + credential.id, 'unexist_credential', ] - verifylist = [ - ('credential', [self.credentials[0].id, 'unexist_credential']) - ] + verifylist = [('credential', [credential.id, 'unexist_credential'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - delete_mock_result = [None, exceptions.CommandError] - self.credentials_mock.delete = mock.Mock( - side_effect=delete_mock_result - ) + self.identity_sdk_client.delete_credential.side_effect = [ + None, + sdk_exceptions.NotFoundException, + ] try: self.cmd.take_action(parsed_args) @@ -187,12 +188,16 @@ def test_credential_multi_delete_with_exception(self): except exceptions.CommandError as e: self.assertEqual('1 of 2 credential failed to delete.', str(e)) - self.credentials_mock.delete.assert_any_call(self.credentials[0].id) - self.credentials_mock.delete.assert_any_call('unexist_credential') + self.identity_sdk_client.delete_credential.assert_any_call( + credential.id + ) + self.identity_sdk_client.delete_credential.assert_any_call( + 'unexist_credential' + ) -class TestCredentialList(TestCredential): - credential = identity_fakes.FakeCredential.create_one_credential() +class TestCredentialList(identity_fakes.TestIdentityv3): + credential = sdk_fakes.generate_fake_resource(_credential.Credential) columns = ('ID', 'Type', 'User ID', 'Data', 'Project ID') data = ( @@ -208,10 +213,10 @@ class TestCredentialList(TestCredential): def setUp(self): super().setUp() - self.user = identity_fakes.FakeUser.create_one_user() - self.users_mock.get.return_value = self.user + self.user = sdk_fakes.generate_fake_resource(_user.User) + self.identity_sdk_client.find_user.return_value = self.user - self.credentials_mock.list.return_value = [self.credential] + self.identity_sdk_client.credentials.return_value = [self.credential] # Get the command object to test self.cmd = credential.ListCredential(self.app, None) @@ -223,7 +228,7 @@ def test_credential_list_no_options(self): columns, data = self.cmd.take_action(parsed_args) - self.credentials_mock.list.assert_called_with() + self.identity_sdk_client.credentials.assert_called_with() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -246,15 +251,17 @@ def test_credential_list_with_options(self): 'user_id': self.user.id, 'type': self.credential.type, } - self.users_mock.get.assert_called_with(self.credential.user_id) - self.credentials_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.find_user.assert_called_with( + self.credential.user_id, domain_id=None, ignore_missing=False + ) + self.identity_sdk_client.credentials.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) -class TestCredentialSet(TestCredential): - credential = identity_fakes.FakeCredential.create_one_credential() +class TestCredentialSet(identity_fakes.TestIdentityv3): + credential = sdk_fakes.generate_fake_resource(_credential.Credential) def setUp(self): super().setUp() @@ -343,7 +350,7 @@ def test_credential_set_valid_with_project(self): self.assertIsNone(result) -class TestCredentialShow(TestCredential): +class TestCredentialShow(identity_fakes.TestIdentityv3): columns = ( 'blob', 'id', @@ -355,8 +362,10 @@ class TestCredentialShow(TestCredential): def setUp(self): super().setUp() - self.credential = identity_fakes.FakeCredential.create_one_credential() - self.credentials_mock.get.return_value = self.credential + self.credential = sdk_fakes.generate_fake_resource( + _credential.Credential + ) + self.identity_sdk_client.get_credential.return_value = self.credential self.data = ( self.credential.blob, self.credential.id, @@ -378,6 +387,8 @@ def test_credential_show(self): columns, data = self.cmd.take_action(parsed_args) - self.credentials_mock.get.assert_called_once_with(self.credential.id) + self.identity_sdk_client.get_credential.assert_called_once_with( + self.credential.id + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/releasenotes/notes/migrate-credential-to-sdk-33a841847fe7d568.yaml b/releasenotes/notes/migrate-credential-to-sdk-33a841847fe7d568.yaml new file mode 100644 index 000000000..e83001273 --- /dev/null +++ b/releasenotes/notes/migrate-credential-to-sdk-33a841847fe7d568.yaml @@ -0,0 +1,10 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``credential create`` + - ``credential delete`` + - ``credential list`` + - ``credential set`` + - ``credential show`` From 8f56d3f5e49f8a15faa4a7b20291793681b29274 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 14 Nov 2024 10:17:59 +0000 Subject: [PATCH 049/245] reno: Update master for unmaintained/2023.1 Update the 2023.1 release notes configuration to build from unmaintained/2023.1. Change-Id: Id29f0413825372df296cc123d63cc092fbba1821 --- releasenotes/source/2023.1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/2023.1.rst b/releasenotes/source/2023.1.rst index d1238479b..2c9a36fae 100644 --- a/releasenotes/source/2023.1.rst +++ b/releasenotes/source/2023.1.rst @@ -3,4 +3,4 @@ =========================== .. release-notes:: - :branch: stable/2023.1 + :branch: unmaintained/2023.1 From 4c8290012d60b1b535594f95dbc5a9e18db721ea Mon Sep 17 00:00:00 2001 From: Antonia Gaete Date: Mon, 30 Sep 2024 17:38:02 +0000 Subject: [PATCH 050/245] identity: Migrate `region` commands to SDK Change-Id: I980693732d794f1ccbfc8d7f06d61b4a9824ef15 --- openstackclient/identity/v3/region.py | 45 ++--- .../tests/functional/identity/v3/common.py | 2 +- .../tests/unit/identity/v3/test_region.py | 170 ++++++++---------- ...igrate-region-to-sdk-fbd27bceaa1db9dc.yaml | 10 ++ 4 files changed, 108 insertions(+), 119 deletions(-) create mode 100644 releasenotes/notes/migrate-region-to-sdk-fbd27bceaa1db9dc.yaml diff --git a/openstackclient/identity/v3/region.py b/openstackclient/identity/v3/region.py index ba8c02cf2..faa2bc414 100644 --- a/openstackclient/identity/v3/region.py +++ b/openstackclient/identity/v3/region.py @@ -25,6 +25,15 @@ LOG = logging.getLogger(__name__) +def _format_region(region): + columns = ('id', 'description', 'parent_region_id') + column_headers = ('region', 'description', 'parent_region') + return ( + column_headers, + utils.get_item_properties(region, columns), + ) + + class CreateRegion(command.ShowOne): _description = _("Create new region") @@ -50,18 +59,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - region = identity_client.regions.create( + region = identity_client.create_region( id=parsed_args.region, - parent_region=parsed_args.parent_region, + parent_region_id=parsed_args.parent_region, description=parsed_args.description, ) - region._info['region'] = region._info.pop('id') - region._info['parent_region'] = region._info.pop('parent_region_id') - region._info.pop('links', None) - return zip(*sorted(region._info.items())) + return _format_region(region) class DeleteRegion(command.Command): @@ -78,11 +84,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity result = 0 for i in parsed_args.region: try: - identity_client.regions.delete(i) + identity_client.delete_region(i) except Exception as e: result += 1 LOG.error( @@ -115,7 +121,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity kwargs = {} if parsed_args.parent_region: @@ -124,7 +130,7 @@ def take_action(self, parsed_args): columns_headers = ('Region', 'Parent Region', 'Description') columns = ('ID', 'Parent Region Id', 'Description') - data = identity_client.regions.list(**kwargs) + data = identity_client.regions(**kwargs) return ( columns_headers, ( @@ -161,15 +167,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity kwargs = {} if parsed_args.description: kwargs['description'] = parsed_args.description if parsed_args.parent_region: - kwargs['parent_region'] = parsed_args.parent_region + kwargs['parent_region_id'] = parsed_args.parent_region - identity_client.regions.update(parsed_args.region, **kwargs) + identity_client.update_region(parsed_args.region, **kwargs) class ShowRegion(command.ShowOne): @@ -185,13 +191,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - region = utils.find_resource( - identity_client.regions, parsed_args.region - ) + region = identity_client.get_region(parsed_args.region) - region._info['region'] = region._info.pop('id') - region._info['parent_region'] = region._info.pop('parent_region_id') - region._info.pop('links', None) - return zip(*sorted(region._info.items())) + return _format_region(region) diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py index e44f291e5..ecabfd256 100644 --- a/openstackclient/tests/functional/identity/v3/common.py +++ b/openstackclient/tests/functional/identity/v3/common.py @@ -49,7 +49,7 @@ class IdentityTests(base.TestCase): ] ROLE_FIELDS = ['id', 'name', 'domain_id', 'description'] SERVICE_FIELDS = ['id', 'enabled', 'name', 'type', 'description'] - REGION_FIELDS = ['description', 'enabled', 'parent_region', 'region'] + REGION_FIELDS = ['description', 'parent_region', 'region'] ENDPOINT_FIELDS = [ 'id', 'region', diff --git a/openstackclient/tests/unit/identity/v3/test_region.py b/openstackclient/tests/unit/identity/v3/test_region.py index 9b66c3ce9..eecb07913 100644 --- a/openstackclient/tests/unit/identity/v3/test_region.py +++ b/openstackclient/tests/unit/identity/v3/test_region.py @@ -11,55 +11,44 @@ # under the License. # -import copy + +from openstack.identity.v3 import region as _region +from openstack.test import fakes as sdk_fakes from openstackclient.identity.v3 import region -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestRegion(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - # Get a shortcut to the RegionManager Mock - self.regions_mock = self.identity_client.regions - self.regions_mock.reset_mock() - - -class TestRegionCreate(TestRegion): +class TestRegionCreate(identity_fakes.TestIdentityv3): + region = sdk_fakes.generate_fake_resource(_region.Region) columns = ( + 'region', 'description', 'parent_region', - 'region', ) datalist = ( - identity_fakes.region_description, - identity_fakes.region_parent_region_id, - identity_fakes.region_id, + region.id, + region.description, + region.parent_region_id, ) def setUp(self): super().setUp() - self.regions_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.REGION), - loaded=True, - ) + self.identity_sdk_client.create_region.return_value = self.region # Get the command object to test self.cmd = region.CreateRegion(self.app, None) def test_region_create_description(self): arglist = [ - identity_fakes.region_id, + self.region.id, '--description', - identity_fakes.region_description, + self.region.description, ] verifylist = [ - ('region', identity_fakes.region_id), - ('description', identity_fakes.region_description), + ('region', self.region.id), + ('description', self.region.description), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -70,21 +59,21 @@ def test_region_create_description(self): # Set expected values kwargs = { - 'description': identity_fakes.region_description, - 'id': identity_fakes.region_id, - 'parent_region': None, + 'description': self.region.description, + 'id': self.region.id, + 'parent_region_id': None, } - self.regions_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_region.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) def test_region_create_no_options(self): arglist = [ - identity_fakes.region_id, + self.region.id, ] verifylist = [ - ('region', identity_fakes.region_id), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -96,23 +85,23 @@ def test_region_create_no_options(self): # Set expected values kwargs = { 'description': None, - 'id': identity_fakes.region_id, - 'parent_region': None, + 'id': self.region.id, + 'parent_region_id': None, } - self.regions_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_region.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) def test_region_create_parent_region_id(self): arglist = [ - identity_fakes.region_id, + self.region.id, '--parent-region', - identity_fakes.region_parent_region_id, + self.region.parent_region_id, ] verifylist = [ - ('region', identity_fakes.region_id), - ('parent_region', identity_fakes.region_parent_region_id), + ('region', self.region.id), + ('parent_region', self.region.parent_region_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -124,41 +113,43 @@ def test_region_create_parent_region_id(self): # Set expected values kwargs = { 'description': None, - 'id': identity_fakes.region_id, - 'parent_region': identity_fakes.region_parent_region_id, + 'id': self.region.id, + 'parent_region_id': self.region.parent_region_id, } - self.regions_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_region.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) -class TestRegionDelete(TestRegion): +class TestRegionDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.regions_mock.delete.return_value = None + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.identity_sdk_client.delete_region.return_value = None # Get the command object to test self.cmd = region.DeleteRegion(self.app, None) def test_region_delete_no_options(self): arglist = [ - identity_fakes.region_id, + self.region.id, ] verifylist = [ - ('region', [identity_fakes.region_id]), + ('region', [self.region.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.regions_mock.delete.assert_called_with( - identity_fakes.region_id, + self.identity_sdk_client.delete_region.assert_called_with( + self.region.id, ) self.assertIsNone(result) -class TestRegionList(TestRegion): +class TestRegionList(identity_fakes.TestIdentityv3): + region = sdk_fakes.generate_fake_resource(_region.Region) columns = ( 'Region', 'Parent Region', @@ -166,22 +157,16 @@ class TestRegionList(TestRegion): ) datalist = ( ( - identity_fakes.region_id, - identity_fakes.region_parent_region_id, - identity_fakes.region_description, + region.id, + region.parent_region_id, + region.description, ), ) def setUp(self): super().setUp() - self.regions_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.REGION), - loaded=True, - ), - ] + self.identity_sdk_client.regions.return_value = [self.region] # Get the command object to test self.cmd = region.ListRegion(self.app, None) @@ -195,7 +180,7 @@ def test_region_list_no_options(self): # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.regions_mock.list.assert_called_with() + self.identity_sdk_client.regions.assert_called_with() self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) @@ -203,10 +188,10 @@ def test_region_list_no_options(self): def test_region_list_parent_region_id(self): arglist = [ '--parent-region', - identity_fakes.region_parent_region_id, + self.region.parent_region_id, ] verifylist = [ - ('parent_region', identity_fakes.region_parent_region_id), + ('parent_region', self.region.parent_region_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -214,41 +199,37 @@ def test_region_list_parent_region_id(self): # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.regions_mock.list.assert_called_with( - parent_region_id=identity_fakes.region_parent_region_id + self.identity_sdk_client.regions.assert_called_with( + parent_region_id=self.region.parent_region_id ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) -class TestRegionSet(TestRegion): +class TestRegionSet(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.regions_mock.update.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.REGION), - loaded=True, - ) + self.region = sdk_fakes.generate_fake_resource(_region.Region) # Get the command object to test self.cmd = region.SetRegion(self.app, None) def test_region_set_no_options(self): arglist = [ - identity_fakes.region_id, + self.region.id, ] verifylist = [ - ('region', identity_fakes.region_id), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = {} - self.regions_mock.update.assert_called_with( - identity_fakes.region_id, **kwargs + self.identity_sdk_client.update_region.assert_called_with( + self.region.id, **kwargs ) self.assertIsNone(result) @@ -256,11 +237,11 @@ def test_region_set_description(self): arglist = [ '--description', 'qwerty', - identity_fakes.region_id, + self.region.id, ] verifylist = [ ('description', 'qwerty'), - ('region', identity_fakes.region_id), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -270,8 +251,8 @@ def test_region_set_description(self): kwargs = { 'description': 'qwerty', } - self.regions_mock.update.assert_called_with( - identity_fakes.region_id, **kwargs + self.identity_sdk_client.update_region.assert_called_with( + self.region.id, **kwargs ) self.assertIsNone(result) @@ -279,11 +260,11 @@ def test_region_set_parent_region_id(self): arglist = [ '--parent-region', 'new_parent', - identity_fakes.region_id, + self.region.id, ] verifylist = [ ('parent_region', 'new_parent'), - ('region', identity_fakes.region_id), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -291,33 +272,30 @@ def test_region_set_parent_region_id(self): # Set expected values kwargs = { - 'parent_region': 'new_parent', + 'parent_region_id': 'new_parent', } - self.regions_mock.update.assert_called_with( - identity_fakes.region_id, **kwargs + self.identity_sdk_client.update_region.assert_called_with( + self.region.id, **kwargs ) self.assertIsNone(result) -class TestRegionShow(TestRegion): +class TestRegionShow(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.regions_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.REGION), - loaded=True, - ) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.identity_sdk_client.get_region.return_value = self.region # Get the command object to test self.cmd = region.ShowRegion(self.app, None) def test_region_show(self): arglist = [ - identity_fakes.region_id, + self.region.id, ] verifylist = [ - ('region', identity_fakes.region_id), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -325,15 +303,15 @@ def test_region_show(self): # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.regions_mock.get.assert_called_with( - identity_fakes.region_id, + self.identity_sdk_client.get_region.assert_called_with( + self.region.id, ) - collist = ('description', 'parent_region', 'region') + collist = ('region', 'description', 'parent_region') self.assertEqual(collist, columns) datalist = ( - identity_fakes.region_description, - identity_fakes.region_parent_region_id, - identity_fakes.region_id, + self.region.id, + self.region.description, + self.region.parent_region_id, ) self.assertEqual(datalist, data) diff --git a/releasenotes/notes/migrate-region-to-sdk-fbd27bceaa1db9dc.yaml b/releasenotes/notes/migrate-region-to-sdk-fbd27bceaa1db9dc.yaml new file mode 100644 index 000000000..31a5e6d3e --- /dev/null +++ b/releasenotes/notes/migrate-region-to-sdk-fbd27bceaa1db9dc.yaml @@ -0,0 +1,10 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``region create`` + - ``region list`` + - ``region delete`` + - ``region set`` + - ``region show`` From 4bdd51cbea41e08f4c78bf071ac4f76d18f42b41 Mon Sep 17 00:00:00 2001 From: Antonia Gaete Date: Tue, 9 Jul 2024 18:53:55 +0000 Subject: [PATCH 051/245] identity: Migrate 'domain' commands to SDK Change-Id: Ide9cb9491334e139482f8cf1ea1874d01da0884f Depends-On: Id1b7b00fe5b96f0cc922716afabcc678193f0f57 --- openstackclient/identity/v3/domain.py | 105 +++++---- .../tests/unit/identity/v3/test_domain.py | 220 +++++++++--------- ...igrate-domain-to-sdk-da6ec38221e79a37.yaml | 10 + 3 files changed, 187 insertions(+), 148 deletions(-) create mode 100644 releasenotes/notes/migrate-domain-to-sdk-da6ec38221e79a37.yaml diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index aaa270830..a65191ec5 100644 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -17,7 +17,7 @@ import logging -from keystoneauth1 import exceptions as ks_exc +from openstack import exceptions as sdk_exceptions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -29,6 +29,31 @@ LOG = logging.getLogger(__name__) +def _format_domain(domain): + columns = ( + 'id', + 'name', + 'is_enabled', + 'description', + 'options', + ) + column_headers = ( + 'id', + 'name', + 'enabled', + 'description', + 'options', + ) + + return ( + column_headers, + utils.get_item_properties( + domain, + columns, + ), + ) + + class CreateDomain(command.ShowOne): _description = _("Create new domain") @@ -47,12 +72,15 @@ def get_parser(self, prog_name): enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', + dest='is_enabled', action='store_true', + default=True, help=_('Enable domain (default)'), ) enable_group.add_argument( '--disable', - action='store_true', + dest='is_enabled', + action='store_false', help=_('Disable domain'), ) parser.add_argument( @@ -64,32 +92,25 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - - enabled = True - if parsed_args.disable: - enabled = False + identity_client = self.app.client_manager.sdk_connection.identity options = common.get_immutable_options(parsed_args) try: - domain = identity_client.domains.create( + domain = identity_client.create_domain( name=parsed_args.name, description=parsed_args.description, options=options, - enabled=enabled, + is_enabled=parsed_args.is_enabled, ) - except ks_exc.Conflict: + except sdk_exceptions.ConflictException: if parsed_args.or_show: - domain = utils.find_resource( - identity_client.domains, parsed_args.name - ) + domain = identity_client.find_domain(parsed_args.name) LOG.info(_('Returning existing domain %s'), domain.name) else: raise - domain._info.pop('links') - return zip(*sorted(domain._info.items())) + return _format_domain(domain) class DeleteDomain(command.Command): @@ -106,12 +127,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity result = 0 for i in parsed_args.domain: try: - domain = utils.find_resource(identity_client.domains, i) - identity_client.domains.delete(domain.id) + domain = identity_client.find_domain(i, ignore_missing=False) + identity_client.delete_domain(domain.id) except Exception as e: result += 1 LOG.error( @@ -143,7 +164,7 @@ def get_parser(self, prog_name): ) parser.add_argument( '--enabled', - dest='enabled', + dest='is_enabled', action='store_true', help=_('The domains that are enabled will be returned'), ) @@ -153,13 +174,17 @@ def take_action(self, parsed_args): kwargs = {} if parsed_args.name: kwargs['name'] = parsed_args.name - if parsed_args.enabled: - kwargs['enabled'] = True + if parsed_args.is_enabled: + kwargs['is_enabled'] = True + + columns = ('id', 'name', 'is_enabled', 'description') + column_headers = ('ID', 'Name', 'Enabled', 'Description') + data = self.app.client_manager.sdk_connection.identity.domains( + **kwargs + ) - columns = ('ID', 'Name', 'Enabled', 'Description') - data = self.app.client_manager.identity.domains.list(**kwargs) return ( - columns, + column_headers, ( utils.get_item_properties( s, @@ -194,38 +219,38 @@ def get_parser(self, prog_name): enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', + dest='is_enabled', action='store_true', + default=None, help=_('Enable domain'), ) enable_group.add_argument( '--disable', - action='store_true', + dest='is_enabled', + action='store_false', + default=None, help=_('Disable domain'), ) common.add_resource_option_to_parser(parser) return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - domain = utils.find_resource( - identity_client.domains, parsed_args.domain - ) + identity_client = self.app.client_manager.sdk_connection.identity + domain = identity_client.find_domain(parsed_args.domain) kwargs = {} if parsed_args.name: kwargs['name'] = parsed_args.name if parsed_args.description: kwargs['description'] = parsed_args.description - if parsed_args.enable: - kwargs['enabled'] = True - if parsed_args.disable: - kwargs['enabled'] = False + if parsed_args.is_enabled is not None: + kwargs['is_enabled'] = parsed_args.is_enabled options = common.get_immutable_options(parsed_args) if options: kwargs['options'] = options - identity_client.domains.update(domain.id, **kwargs) + identity_client.update_domain(domain.id, **kwargs) class ShowDomain(command.ShowOne): @@ -241,13 +266,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - - domain_str = common._get_token_resource( - identity_client, 'domain', parsed_args.domain - ) - - domain = utils.find_resource(identity_client.domains, domain_str) + identity_client = self.app.client_manager.sdk_connection.identity + domain = identity_client.find_domain(parsed_args.domain) - domain._info.pop('links') - return zip(*sorted(domain._info.items())) + return _format_domain(domain) diff --git a/openstackclient/tests/unit/identity/v3/test_domain.py b/openstackclient/tests/unit/identity/v3/test_domain.py index fe3f655e4..079170739 100644 --- a/openstackclient/tests/unit/identity/v3/test_domain.py +++ b/openstackclient/tests/unit/identity/v3/test_domain.py @@ -10,33 +10,32 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack.identity.v3 import domain as _domain +from openstack.test import fakes as sdk_fakes from openstackclient.identity.v3 import domain from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestDomain(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - # Get a shortcut to the DomainManager Mock - self.domains_mock = self.identity_client.domains - self.domains_mock.reset_mock() - - -class TestDomainCreate(TestDomain): - columns = ('description', 'enabled', 'id', 'name', 'tags') +class TestDomainCreate(identity_fakes.TestIdentityv3): + columns = ( + 'id', + 'name', + 'enabled', + 'description', + 'options', + ) def setUp(self): super().setUp() - self.domain = identity_fakes.FakeDomain.create_one_domain() - self.domains_mock.create.return_value = self.domain + self.domain = sdk_fakes.generate_fake_resource(_domain.Domain) + self.identity_sdk_client.create_domain.return_value = self.domain self.datalist = ( - self.domain.description, - True, self.domain.id, self.domain.name, - self.domain.tags, + self.domain.is_enabled, + self.domain.description, + self.domain.options, ) # Get the command object to test @@ -61,9 +60,9 @@ def test_domain_create_no_options(self): 'name': self.domain.name, 'description': None, 'options': {}, - 'enabled': True, + 'is_enabled': True, } - self.domains_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_domain.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -90,9 +89,9 @@ def test_domain_create_description(self): 'name': self.domain.name, 'description': 'new desc', 'options': {}, - 'enabled': True, + 'is_enabled': True, } - self.domains_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_domain.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -103,7 +102,7 @@ def test_domain_create_enable(self): self.domain.name, ] verifylist = [ - ('enable', True), + ('is_enabled', True), ('name', self.domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -118,9 +117,9 @@ def test_domain_create_enable(self): 'name': self.domain.name, 'description': None, 'options': {}, - 'enabled': True, + 'is_enabled': True, } - self.domains_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_domain.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -131,7 +130,7 @@ def test_domain_create_disable(self): self.domain.name, ] verifylist = [ - ('disable', True), + ('is_enabled', False), ('name', self.domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -146,9 +145,9 @@ def test_domain_create_disable(self): 'name': self.domain.name, 'description': None, 'options': {}, - 'enabled': False, + 'is_enabled': False, } - self.domains_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_domain.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -174,9 +173,9 @@ def test_domain_create_with_immutable(self): 'name': self.domain.name, 'description': None, 'options': {'immutable': True}, - 'enabled': True, + 'is_enabled': True, } - self.domains_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_domain.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -202,23 +201,23 @@ def test_domain_create_with_no_immutable(self): 'name': self.domain.name, 'description': None, 'options': {'immutable': False}, - 'enabled': True, + 'is_enabled': True, } - self.domains_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_domain.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) -class TestDomainDelete(TestDomain): - domain = identity_fakes.FakeDomain.create_one_domain() +class TestDomainDelete(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) def setUp(self): super().setUp() # This is the return value for utils.find_resource() - self.domains_mock.get.return_value = self.domain - self.domains_mock.delete.return_value = None + self.identity_sdk_client.find_domain.return_value = self.domain + self.identity_sdk_client.delete_domain.return_value = None # Get the command object to test self.cmd = domain.DeleteDomain(self.app, None) @@ -234,19 +233,35 @@ def test_domain_delete(self): result = self.cmd.take_action(parsed_args) - self.domains_mock.delete.assert_called_with( + self.identity_sdk_client.delete_domain.assert_called_with( self.domain.id, ) self.assertIsNone(result) -class TestDomainList(TestDomain): - domain = identity_fakes.FakeDomain.create_one_domain() +class TestDomainList(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource( + resource_type=_domain.Domain, is_enabled=True + ) + columns = ( + 'ID', + 'Name', + 'Enabled', + 'Description', + ) def setUp(self): super().setUp() - self.domains_mock.list.return_value = [self.domain] + self.identity_sdk_client.domains.return_value = [self.domain] + self.datalist = ( + ( + self.domain.id, + self.domain.name, + self.domain.is_enabled, + self.domain.description, + ), + ) # Get the command object to test self.cmd = domain.ListDomain(self.app, None) @@ -260,19 +275,10 @@ def test_domain_list_no_options(self): # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.domains_mock.list.assert_called_with() + self.identity_sdk_client.domains.assert_called_with() - collist = ('ID', 'Name', 'Enabled', 'Description') - self.assertEqual(collist, columns) - datalist = ( - ( - self.domain.id, - self.domain.name, - True, - self.domain.description, - ), - ) - self.assertEqual(datalist, tuple(data)) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) def test_domain_list_with_option_name(self): arglist = ['--name', self.domain.name] @@ -285,23 +291,14 @@ def test_domain_list_with_option_name(self): columns, data = self.cmd.take_action(parsed_args) kwargs = {'name': self.domain.name} - self.domains_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.domains.assert_called_with(**kwargs) - collist = ('ID', 'Name', 'Enabled', 'Description') - self.assertEqual(collist, columns) - datalist = ( - ( - self.domain.id, - self.domain.name, - True, - self.domain.description, - ), - ) - self.assertEqual(datalist, tuple(data)) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) def test_domain_list_with_option_enabled(self): arglist = ['--enabled'] - verifylist = [('enabled', True)] + verifylist = [('is_enabled', True)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class Lister in cliff, abstract method take_action() @@ -309,31 +306,22 @@ def test_domain_list_with_option_enabled(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - kwargs = {'enabled': True} - self.domains_mock.list.assert_called_with(**kwargs) + kwargs = {'is_enabled': True} + self.identity_sdk_client.domains.assert_called_with(**kwargs) - collist = ('ID', 'Name', 'Enabled', 'Description') - self.assertEqual(collist, columns) - datalist = ( - ( - self.domain.id, - self.domain.name, - True, - self.domain.description, - ), - ) - self.assertEqual(datalist, tuple(data)) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) -class TestDomainSet(TestDomain): - domain = identity_fakes.FakeDomain.create_one_domain() +class TestDomainSet(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) def setUp(self): super().setUp() - self.domains_mock.get.return_value = self.domain + self.identity_sdk_client.find_domain.return_value = self.domain - self.domains_mock.update.return_value = self.domain + self.identity_sdk_client.update_domain.return_value = self.domain # Get the command object to test self.cmd = domain.SetDomain(self.app, None) @@ -350,7 +338,9 @@ def test_domain_set_no_options(self): result = self.cmd.take_action(parsed_args) kwargs = {} - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) def test_domain_set_name(self): @@ -371,7 +361,9 @@ def test_domain_set_name(self): kwargs = { 'name': 'qwerty', } - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) def test_domain_set_description(self): @@ -392,7 +384,9 @@ def test_domain_set_description(self): kwargs = { 'description': 'new desc', } - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) def test_domain_set_enable(self): @@ -401,7 +395,7 @@ def test_domain_set_enable(self): self.domain.id, ] verifylist = [ - ('enable', True), + ('is_enabled', True), ('domain', self.domain.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -410,9 +404,11 @@ def test_domain_set_enable(self): # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, } - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) def test_domain_set_disable(self): @@ -421,7 +417,7 @@ def test_domain_set_disable(self): self.domain.id, ] verifylist = [ - ('disable', True), + ('is_enabled', False), ('domain', self.domain.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -430,9 +426,11 @@ def test_domain_set_disable(self): # Set expected values kwargs = { - 'enabled': False, + 'is_enabled': False, } - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) def test_domain_set_immutable_option(self): @@ -452,7 +450,9 @@ def test_domain_set_immutable_option(self): kwargs = { 'options': {'immutable': True}, } - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) def test_domain_set_no_immutable_option(self): @@ -472,16 +472,34 @@ def test_domain_set_no_immutable_option(self): kwargs = { 'options': {'immutable': False}, } - self.domains_mock.update.assert_called_with(self.domain.id, **kwargs) + self.identity_sdk_client.update_domain.assert_called_with( + self.domain.id, **kwargs + ) self.assertIsNone(result) -class TestDomainShow(TestDomain): +class TestDomainShow(identity_fakes.TestIdentityv3): + columns = ( + 'id', + 'name', + 'enabled', + 'description', + 'options', + ) + def setUp(self): super().setUp() - self.domain = identity_fakes.FakeDomain.create_one_domain() - self.domains_mock.get.return_value = self.domain + self.domain = sdk_fakes.generate_fake_resource(_domain.Domain) + self.identity_sdk_client.find_domain.return_value = self.domain + self.datalist = ( + self.domain.id, + self.domain.name, + self.domain.is_enabled, + self.domain.description, + self.domain.options, + ) + # Get the command object to test self.cmd = domain.ShowDomain(self.app, None) @@ -501,17 +519,9 @@ def test_domain_show(self): # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.domains_mock.get.assert_called_with( + self.identity_sdk_client.find_domain.assert_called_with( self.domain.id, ) - collist = ('description', 'enabled', 'id', 'name', 'tags') - self.assertEqual(collist, columns) - datalist = ( - self.domain.description, - True, - self.domain.id, - self.domain.name, - self.domain.tags, - ) - self.assertEqual(datalist, data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) diff --git a/releasenotes/notes/migrate-domain-to-sdk-da6ec38221e79a37.yaml b/releasenotes/notes/migrate-domain-to-sdk-da6ec38221e79a37.yaml new file mode 100644 index 000000000..ce26909b4 --- /dev/null +++ b/releasenotes/notes/migrate-domain-to-sdk-da6ec38221e79a37.yaml @@ -0,0 +1,10 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``domain create`` + - ``domain delete`` + - ``domain list`` + - ``domain set`` + - ``domain show`` From ecc744a4fdf58c37ba05bcf72a8ae22f9aea4173 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Mon, 25 Nov 2024 00:41:46 +0900 Subject: [PATCH 052/245] Remove ceilometer service overrides devstack no longer installs ceilometer services unlesss ceilometer devstack plugin is enabled, so we can safely remove the options to disable ceilometer services. This allows us to remove references to removed services such as ceilometer-api . Change-Id: I4201878d0deba4490cf2a08bbabec8fe64474385 --- .zuul.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 5352e894f..e688bd26a 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -86,13 +86,6 @@ # NOTE(amotoki): Some neutron features are enabled by devstack plugin neutron: https://opendev.org/openstack/neutron devstack_services: - ceilometer-acentral: false - ceilometer-acompute: false - ceilometer-alarm-evaluator: false - ceilometer-alarm-notifier: false - ceilometer-anotification: false - ceilometer-api: false - ceilometer-collector: false s-account: true s-container: true s-object: true From 5ef5cc9c82799290962b80aa663af0094572c903 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 10 Aug 2023 19:04:36 +0100 Subject: [PATCH 053/245] compute: Add server create --no-security-group option To allow users to create servers with no security groups associated with the ports. Change-Id: I91b1d9dd5c3fbba838640841d98341cd8ccb1b16 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 54 ++++++++++++------- .../tests/unit/compute/v2/test_server.py | 54 +++++++++++++++++++ openstackclient/tests/unit/utils.py | 10 +++- ...ecurity-group-option-627697bddae429b1.yaml | 7 +++ 4 files changed, 103 insertions(+), 22 deletions(-) create mode 100644 releasenotes/notes/server-create-no-security-group-option-627697bddae429b1.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index f7b860df8..7be1bb9fb 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1356,14 +1356,26 @@ def get_parser(self, prog_name): 'This option requires cloud support.' ), ) - parser.add_argument( + secgroups = parser.add_mutually_exclusive_group() + secgroups.add_argument( + '--no-security-group', + dest='security_groups', + action='store_const', + const=[], + help=_( + 'Do not associate a security group with ports attached to ' + 'this server. This does not affect the security groups ' + 'associated with pre-existing ports.' + ), + ) + secgroups.add_argument( '--security-group', metavar='', action='append', - default=[], dest='security_groups', help=_( - 'Security group to assign to this server (name or ID) ' + 'Security group to associate with ports attached to this ' + 'server (name or ID) ' '(repeat option to set multiple groups)' ), ) @@ -1980,22 +1992,24 @@ def _match_image(image_api, wanted_properties): networks = 'auto' # Check security group(s) exist and convert ID to name - security_groups = [] - if self.app.client_manager.is_network_endpoint_enabled(): - network_client = self.app.client_manager.network - for security_group in parsed_args.security_groups: - sg = network_client.find_security_group( - security_group, ignore_missing=False - ) - # Use security group ID to avoid multiple security group have - # same name in neutron networking backend - security_groups.append({'name': sg.id}) - else: # nova-network - for security_group in parsed_args.security_groups: - sg = compute_v2.find_security_group( - compute_client, security_group - ) - security_groups.append({'name': sg['name']}) + security_groups = None + if parsed_args.security_groups is not None: + security_groups = [] + if self.app.client_manager.is_network_endpoint_enabled(): + network_client = self.app.client_manager.network + for security_group in parsed_args.security_groups: + sg = network_client.find_security_group( + security_group, ignore_missing=False + ) + # Use security group ID to avoid multiple security group + # have same name in neutron networking backend + security_groups.append({'name': sg.id}) + else: # nova-network + for security_group in parsed_args.security_groups: + sg = compute_v2.find_security_group( + compute_client, security_group + ) + security_groups.append({'name': sg['name']}) hints = {} for key, values in parsed_args.hints.items(): @@ -2058,7 +2072,7 @@ def _match_image(image_api, wanted_properties): if files: kwargs['personality'] = files - if security_groups: + if security_groups is not None: kwargs['security_groups'] = security_groups if block_device_mapping_v2: diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index a07e33333..20848842e 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -1544,6 +1544,60 @@ def test_server_create_with_security_group_in_nova_network(self): self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) + def test_server_create_with_no_security_group(self): + arglist = [ + '--image', + self.image.id, + '--flavor', + self.flavor.id, + '--no-security-group', + self.server.name, + ] + verifylist = [ + ('image', self.image.id), + ('flavor', self.flavor.id), + ('key_name', None), + ('properties', None), + ('security_groups', []), + ('hints', {}), + ('server_group', None), + ('config_drive', False), + ('password', None), + ('server_name', self.server.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.compute_sdk_client.find_flavor.assert_has_calls( + [mock.call(self.flavor.id, ignore_missing=False)] * 2 + ) + self.network_client.find_security_group.assert_not_called() + self.image_client.find_image.assert_called_once_with( + self.image.id, ignore_missing=False + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + security_groups=[], + networks=[], + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) + def test_server_create_with_network(self): network_net1 = network_fakes.create_one_network() network_net2 = network_fakes.create_one_network() diff --git a/openstackclient/tests/unit/utils.py b/openstackclient/tests/unit/utils.py index 2a39ca483..607047f14 100644 --- a/openstackclient/tests/unit/utils.py +++ b/openstackclient/tests/unit/utils.py @@ -93,8 +93,14 @@ def check_parser(self, cmd, args, verify_args): f"Argument parse failed: {stderr.getvalue()}" ) for av in verify_args: - attr, value = av + attr, expected_value = av if attr: + actual_value = getattr(parsed_args, attr) self.assertIn(attr, parsed_args) - self.assertEqual(value, getattr(parsed_args, attr)) + self.assertEqual( + expected_value, + actual_value, + f'args.{attr}: expected: {expected_value}, got: ' + f'{actual_value}', + ) return parsed_args diff --git a/releasenotes/notes/server-create-no-security-group-option-627697bddae429b1.yaml b/releasenotes/notes/server-create-no-security-group-option-627697bddae429b1.yaml new file mode 100644 index 000000000..b2169d36e --- /dev/null +++ b/releasenotes/notes/server-create-no-security-group-option-627697bddae429b1.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + The ``server create`` command now supports a ``--no-security-group`` + option. When provided, no security groups will be associated with ports + created and attached to the server during server creation. This does not + affect pre-created ports. From c888cf25560fddf236073ede42914da851833396 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 10 Aug 2023 19:32:07 +0100 Subject: [PATCH 054/245] network: Make better use of argparse Change-Id: I7421a0ab957412a8283eee6ae9783dac9d3f6a4a Signed-off-by: Stephen Finucane --- openstackclient/network/v2/port.py | 25 +++++++++---------- .../tests/unit/network/v2/test_port.py | 16 ++++++------ 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 9b2fc34b1..a70788238 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -558,7 +558,7 @@ def get_parser(self, prog_name): '--security-group', metavar='', action='append', - dest='security_group', + dest='security_groups', help=_( "Security group to associate with this port (name or ID) " "(repeat option to set multiple security groups)" @@ -566,8 +566,9 @@ def get_parser(self, prog_name): ) secgroups.add_argument( '--no-security-group', - dest='no_security_group', - action='store_true', + action='store_const', + const=[], + dest='security_groups', help=_("Associate no security groups with this port"), ) parser.add_argument( @@ -633,13 +634,11 @@ def take_action(self, parsed_args): elif parsed_args.no_fixed_ip: attrs['fixed_ips'] = [] - if parsed_args.security_group: + if parsed_args.security_groups is not None: attrs['security_group_ids'] = [ client.find_security_group(sg, ignore_missing=False).id - for sg in parsed_args.security_group + for sg in parsed_args.security_groups ] - elif parsed_args.no_security_group: - attrs['security_group_ids'] = [] if parsed_args.allowed_address_pairs: attrs['allowed_address_pairs'] = _convert_address_pairs( @@ -1006,7 +1005,7 @@ def get_parser(self, prog_name): '--security-group', metavar='', action='append', - dest='security_group', + dest='security_groups', help=_( "Security group to associate with this port (name or ID) " "(repeat option to set multiple security groups)" @@ -1107,7 +1106,7 @@ def take_action(self, parsed_args): if parsed_args.no_security_group: attrs['security_group_ids'] = [] - if parsed_args.security_group: + if parsed_args.security_groups: if 'security_group_ids' not in attrs: # NOTE(dtroyer): Get existing security groups, iterate the # list to force a new list object to be @@ -1118,7 +1117,7 @@ def take_action(self, parsed_args): ] attrs['security_group_ids'].extend( client.find_security_group(sg, ignore_missing=False).id - for sg in parsed_args.security_group + for sg in parsed_args.security_groups ) if parsed_args.no_allowed_address_pair: @@ -1231,7 +1230,7 @@ def get_parser(self, prog_name): '--security-group', metavar='', action='append', - dest='security_group_ids', + dest='security_groups', help=_( "Security group which should be removed this port (name " "or ID) (repeat option to unset multiple security groups)" @@ -1316,9 +1315,9 @@ def take_action(self, parsed_args): msg = _("Port does not contain binding-profile %s") % key raise exceptions.CommandError(msg) attrs['binding:profile'] = tmp_binding_profile - if parsed_args.security_group_ids: + if parsed_args.security_groups: try: - for sg in parsed_args.security_group_ids: + for sg in parsed_args.security_groups: sg_id = client.find_security_group( sg, ignore_missing=False ).id diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index bb9f3a20f..dcfe64b3b 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -332,7 +332,7 @@ def test_create_with_security_group(self): self._port.network_id, ), ('enable', True), - ('security_group', [secgroup.id]), + ('security_groups', [secgroup.id]), ('name', 'test-port'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -405,7 +405,7 @@ def test_create_with_security_groups(self): self._port.network_id, ), ('enable', True), - ('security_group', [sg_1.id, sg_2.id]), + ('security_groups', [sg_1.id, sg_2.id]), ('name', 'test-port'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -434,7 +434,7 @@ def test_create_with_no_security_groups(self): verifylist = [ ('network', self._port.network_id), ('enable', True), - ('no_security_group', True), + ('security_groups', []), ('name', 'test-port'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2067,7 +2067,7 @@ def test_set_port_security_group(self): self._port.name, ] verifylist = [ - ('security_group', [sg.id]), + ('security_groups', [sg.id]), ('port', self._port.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2100,7 +2100,7 @@ def test_set_port_security_group_append(self): _testport.name, ] verifylist = [ - ('security_group', [sg_2.id, sg_3.id]), + ('security_groups', [sg_2.id, sg_3.id]), ('port', _testport.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2149,7 +2149,7 @@ def test_set_port_security_group_replace(self): _testport.name, ] verifylist = [ - ('security_group', [sg2.id]), + ('security_groups', [sg2.id]), ('no_security_group', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2770,7 +2770,7 @@ def test_unset_security_group(self): _fake_port.name, ] verifylist = [ - ('security_group_ids', [_fake_sg2.id]), + ('security_groups', [_fake_sg2.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2797,7 +2797,7 @@ def test_unset_port_security_group_not_existent(self): _fake_port.name, ] verifylist = [ - ('security_group_ids', [_fake_sg2.id]), + ('security_groups', [_fake_sg2.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) From 03933e9a736daa3531e46d851fc7ecdc817a3944 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Mon, 9 Dec 2024 18:58:27 +0530 Subject: [PATCH 055/245] Fix: extend in-use volumes check Currently we have 2 issues with extending volumes checks: 1. We don't specify explicitly that MV 3.42 needs to be passed for in-use volumes 2. Any state of volume (error, attaching, detaching etc) can pass this check by specifying MV 3.42 The fundamentally correct approach to these checks should be: 1. Only allow 'available' and 'in-use' volumes to be extended 2. Check MV 3.42 or greater is specified in case of 'in-use' volumes otherwise fail This approach is implemented in the patch. Change-Id: I45ab9af953f7d060379f48ca429eaea7cfe857cc --- openstackclient/volume/v3/volume.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 6f705ad6f..b5cdac15f 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -768,18 +768,24 @@ def take_action(self, parsed_args): _("New size must be greater than %s GB") % volume.size ) raise exceptions.CommandError(msg) - if ( - volume.status != 'available' - and not volume_client.api_version.matches('3.42') - ): + if volume.status not in ('available', 'in-use'): msg = ( _( "Volume is in %s state, it must be available " - "before size can be extended" + "or in-use before size can be extended." ) % volume.status ) raise exceptions.CommandError(msg) + if ( + volume.status == 'in-use' + and not volume_client.api_version.matches('3.42') + ): + msg = _( + "--os-volume-api-version 3.42 or greater is " + "required to extend in-use volumes." + ) + raise exceptions.CommandError(msg) volume_client.volumes.extend(volume.id, parsed_args.size) except Exception as e: LOG.error(_("Failed to set volume size: %s"), e) From 22b30b99ce0e7b64aed1a13df73ab52a0b33204d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 9 Dec 2024 13:32:15 +0000 Subject: [PATCH 056/245] compute: Workaround bug #2089821 By passing a dict instead of a single value, we force SDK to populate the correct attribute on the object. Change-Id: I9f4c5964dc0546215474c92db567966ffad68a1a Signed-off-by: Stephen Finucane Related-bug: #2089821 --- openstackclient/compute/v2/server.py | 6 ++++-- openstackclient/tests/unit/compute/v2/test_server.py | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index f7b860df8..02f1c3515 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -714,7 +714,8 @@ def take_action(self, parsed_args): for security_group in security_groups: try: compute_client.add_security_group_to_server( - server, security_group + server, + {'name': security_group}, ) except sdk_exceptions.HttpException as e: errors += 1 @@ -4083,7 +4084,8 @@ def take_action(self, parsed_args): for security_group in security_groups: try: compute_client.remove_security_group_from_server( - server, security_group + server, + {'name': security_group}, ) except sdk_exceptions.HttpException as e: errors += 1 diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index a07e33333..7746b82af 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -1165,7 +1165,7 @@ def test_server_add_security_group__nova_network(self): self.server.id, ignore_missing=False ) self.compute_sdk_client.add_security_group_to_server.assert_called_once_with( - self.server, 'fake_sg' + self.server, {'name': 'fake_sg'} ) mock_find_nova_net_sg.assert_called_once_with( self.compute_sdk_client, 'fake_sg' @@ -1186,7 +1186,7 @@ def test_server_add_security_group(self): self.server.id, ignore_missing=False ) self.compute_sdk_client.add_security_group_to_server.assert_called_once_with( - self.server, 'fake_sg' + self.server, {'name': 'fake_sg'} ) self.assertIsNone(result) @@ -7400,7 +7400,7 @@ def test_server_remove_security_group__nova_network(self): self.server.id, ignore_missing=False ) self.compute_sdk_client.remove_security_group_from_server.assert_called_once_with( - self.server, 'fake_sg' + self.server, {'name': 'fake_sg'} ) mock_find_nova_net_sg.assert_called_once_with( self.compute_sdk_client, 'fake_sg' @@ -7421,7 +7421,7 @@ def test_server_remove_security_group(self): self.server.id, ignore_missing=False ) self.compute_sdk_client.remove_security_group_from_server.assert_called_once_with( - self.server, 'fake_sg' + self.server, {'name': 'fake_sg'} ) self.assertIsNone(result) From e736394d1bd3d13fc6939e9b0e954d646c152777 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 3 Dec 2024 15:32:26 +0000 Subject: [PATCH 057/245] tests: Add functional test for adding, removing SGs The fix is in openstacksdk. Let's test it here though. Change-Id: I661e6d66c8196e8c9ca8b9cda3d08e756e3d5877 Signed-off-by: Stephen Finucane Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/936947 Related-bug: #2089821 --- .../functional/compute/v2/test_server.py | 88 ++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py index 19531da47..47b6d7c72 100644 --- a/openstackclient/tests/functional/compute/v2/test_server.py +++ b/openstackclient/tests/functional/compute/v2/test_server.py @@ -1295,7 +1295,7 @@ def test_server_add_remove_port(self): ) if ip_address in cmd_output['addresses']['private']: # Hang out for a bit and try again - print('retrying add port check') + print('retrying remove port check') wait_time += 10 time.sleep(10) else: @@ -1367,6 +1367,92 @@ def test_server_add_fixed_ip(self): addresses = cmd_output['addresses']['private'] self.assertIn(ip_address, addresses) + def test_server_add_remove_security_group(self): + name = uuid.uuid4().hex + cmd_output = self.openstack( + 'server create ' + + '--network private ' + + '--flavor ' + + self.flavor_name + + ' ' + + '--image ' + + self.image_name + + ' ' + + '--wait ' + + name, + parse_output=True, + ) + + self.assertIsNotNone(cmd_output['id']) + self.assertEqual(name, cmd_output['name']) + self.addCleanup(self.openstack, 'server delete --wait ' + name) + + # create security group + security_group_name = uuid.uuid4().hex + + cmd_output = self.openstack( + 'security group list', + parse_output=True, + ) + self.assertNotIn(security_group_name, cmd_output) + + cmd_output = self.openstack( + 'security group create ' + security_group_name, + parse_output=True, + ) + self.assertIsNotNone(cmd_output['id']) + self.addCleanup( + self.openstack, 'security group delete ' + security_group_name + ) + + # add security group to server, assert the name of the security group + # appears + self.openstack( + 'server add security group ' + name + ' ' + security_group_name + ) + + wait_time = 0 + while wait_time < 60: + cmd_output = self.openstack( + 'server show ' + name, + parse_output=True, + ) + if security_group_name not in [ + x['name'] for x in cmd_output['security_groups'] + ]: + # Hang out for a bit and try again + print('retrying add security group check') + wait_time += 10 + time.sleep(10) + else: + break + security_groups = [x['name'] for x in cmd_output['security_groups']] + self.assertIn(security_group_name, security_groups) + + # remove security group, assert the name of the security group doesn't + # appear + self.openstack( + 'server remove security group ' + name + ' ' + security_group_name + ) + + wait_time = 0 + while wait_time < 60: + cmd_output = self.openstack( + 'server show ' + name, + parse_output=True, + ) + if security_group_name not in [ + x['name'] for x in cmd_output['security_groups'] + ]: + # Hang out for a bit and try again + print('retrying remove security group check') + wait_time += 10 + time.sleep(10) + else: + break + security_groups = [x['name'] for x in cmd_output['security_groups']] + self.assertNotIn(security_group_name, security_groups) + def test_server_add_remove_volume(self): volume_wait_for = volume_common.BaseVolumeTests.wait_for_status From 99cef9354b70fe8c5a227dd1b3fa41908c290d0d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 5 Dec 2024 10:56:52 +0000 Subject: [PATCH 058/245] quota: Catch correct exception type for Compute quotas There is a flaw (IMO) in the design of Nova's os-quota-sets API: despite project IDs forming the identifier for an individual resource, we get a HTTP 400 (Bad Request) error if you pass an ID that does not exist, rather than the HTTP 404 (Not Found) we would expect. Correct this, noting why we're doing what we're doing for readers from the future (hi!). Note that HTTP 400 is unfortunately quite broad and means we'll also catch things like invalid requests but the exception may have been translated so we can't rely on a string match. Change-Id: I720502930d50be8ead5f2033d9dbcab5d99a37a9 Signed-off-by: Stephen Finucane Closes-bug: #2091086 --- openstackclient/common/quota.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 4356cae66..638d2e1ce 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -249,9 +249,14 @@ def _list_quota_compute(self, parsed_args, project_ids): for project_id in project_ids: try: project_data = compute_client.get_quota_set(project_id) + # NOTE(stephenfin): Unfortunately, Nova raises a HTTP 400 (Bad + # Request) if the project ID is invalid, even though the project + # ID is actually the resource's identifier which would normally + # lead us to expect a HTTP 404 (Not Found). except ( - sdk_exceptions.NotFoundException, + sdk_exceptions.BadRequestException, sdk_exceptions.ForbiddenException, + sdk_exceptions.NotFoundException, ) as exc: # Project not found, move on to next one LOG.warning(f"Project {project_id} not found: {exc}") @@ -312,8 +317,8 @@ def _list_quota_volume(self, parsed_args, project_ids): try: project_data = volume_client.get_quota_set(project_id) except ( - sdk_exceptions.NotFoundException, sdk_exceptions.ForbiddenException, + sdk_exceptions.NotFoundException, ) as exc: # Project not found, move on to next one LOG.warning(f"Project {project_id} not found: {exc}") From 56baf50655cfe7948ca5e010b9377749066b6bf9 Mon Sep 17 00:00:00 2001 From: Antonia Gaete Date: Thu, 19 Sep 2024 17:53:51 +0000 Subject: [PATCH 059/245] identity: Migrate 'service provider' commands to SDK Change-Id: I7f5fba408b7c350bb0a279f8dd17bd7bae451774 --- .../identity/v3/service_provider.py | 140 ++++--- .../identity/v3/test_service_provider.py | 6 +- .../unit/identity/v3/test_service_provider.py | 368 ++++++++---------- ...vice-provider-to-sdk-74dc48b227f21a05.yaml | 10 + 4 files changed, 265 insertions(+), 259 deletions(-) create mode 100644 releasenotes/notes/migrate-service-provider-to-sdk-74dc48b227f21a05.yaml diff --git a/openstackclient/identity/v3/service_provider.py b/openstackclient/identity/v3/service_provider.py index 79869ee60..1a7b02fad 100644 --- a/openstackclient/identity/v3/service_provider.py +++ b/openstackclient/identity/v3/service_provider.py @@ -25,6 +25,29 @@ LOG = logging.getLogger(__name__) +def _format_service_provider(sp): + column_headers = ( + 'id', + 'enabled', + 'description', + 'auth_url', + 'sp_url', + 'relay_state_prefix', + ) + columns = ( + 'id', + 'is_enabled', + 'description', + 'auth_url', + 'sp_url', + 'relay_state_prefix', + ) + return ( + column_headers, + utils.get_item_properties(sp, columns), + ) + + class CreateServiceProvider(command.ShowOne): _description = _("Create new service provider") @@ -62,14 +85,14 @@ def get_parser(self, prog_name): enable_service_provider = parser.add_mutually_exclusive_group() enable_service_provider.add_argument( '--enable', - dest='enabled', + dest='is_enabled', action='store_true', default=True, help=_('Enable the service provider (default)'), ) enable_service_provider.add_argument( '--disable', - dest='enabled', + dest='is_enabled', action='store_false', help=_('Disable the service provider'), ) @@ -77,17 +100,27 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.identity - sp = service_client.federation.service_providers.create( - id=parsed_args.service_provider_id, - auth_url=parsed_args.auth_url, - description=parsed_args.description, - enabled=parsed_args.enabled, - sp_url=parsed_args.service_provider_url, - ) + service_client = self.app.client_manager.sdk_connection.identity + + kwargs = {} + + kwargs = {'id': parsed_args.service_provider_id} + + if parsed_args.is_enabled is not None: + kwargs['is_enabled'] = parsed_args.is_enabled - sp._info.pop('links', None) - return zip(*sorted(sp._info.items())) + if parsed_args.description: + kwargs['description'] = parsed_args.description + + if parsed_args.auth_url: + kwargs['auth_url'] = parsed_args.auth_url + + if parsed_args.service_provider_url: + kwargs['sp_url'] = parsed_args.service_provider_url + + sp = service_client.create_service_provider(**kwargs) + + return _format_service_provider(sp) class DeleteServiceProvider(command.Command): @@ -104,11 +137,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.identity + service_client = self.app.client_manager.sdk_connection.identity result = 0 for i in parsed_args.service_provider: try: - service_client.federation.service_providers.delete(i) + service_client.delete_service_provider(i) except Exception as e: result += 1 LOG.error( @@ -132,24 +165,32 @@ class ListServiceProvider(command.Lister): _description = _("List service providers") def take_action(self, parsed_args): - service_client = self.app.client_manager.identity - data = service_client.federation.service_providers.list() - - column_headers = ('ID', 'Enabled', 'Description', 'Auth URL') + service_client = self.app.client_manager.sdk_connection.identity + data = service_client.service_providers() + + column_headers = ( + 'ID', + 'Enabled', + 'Description', + 'Auth URL', + 'Service Provider URL', + 'Relay State Prefix', + ) + columns = ( + 'id', + 'is_enabled', + 'description', + 'auth_url', + 'sp_url', + 'relay_state_prefix', + ) return ( column_headers, - ( - utils.get_item_properties( - s, - column_headers, - formatters={}, - ) - for s in data - ), + (utils.get_item_properties(s, columns) for s in data), ) -class SetServiceProvider(command.Command): +class SetServiceProvider(command.ShowOne): _description = _("Set service provider properties") def get_parser(self, prog_name): @@ -181,33 +222,44 @@ def get_parser(self, prog_name): enable_service_provider = parser.add_mutually_exclusive_group() enable_service_provider.add_argument( '--enable', + dest='is_enabled', action='store_true', + default=None, help=_('Enable the service provider'), ) enable_service_provider.add_argument( '--disable', - action='store_true', + dest='is_enabled', + action='store_false', + default=None, help=_('Disable the service provider'), ) return parser def take_action(self, parsed_args): - federation_client = self.app.client_manager.identity.federation + service_client = self.app.client_manager.sdk_connection.identity - enabled = None - if parsed_args.enable is True: - enabled = True - elif parsed_args.disable is True: - enabled = False + kwargs = {} - federation_client.service_providers.update( + if parsed_args.is_enabled is not None: + kwargs['is_enabled'] = parsed_args.is_enabled + + if parsed_args.description: + kwargs['description'] = parsed_args.description + + if parsed_args.auth_url: + kwargs['auth_url'] = parsed_args.auth_url + + if parsed_args.service_provider_url: + kwargs['sp_url'] = parsed_args.service_provider_url + + service_provider = service_client.update_service_provider( parsed_args.service_provider, - enabled=enabled, - description=parsed_args.description, - auth_url=parsed_args.auth_url, - sp_url=parsed_args.service_provider_url, + **kwargs, ) + return _format_service_provider(service_provider) + class ShowServiceProvider(command.ShowOne): _description = _("Display service provider details") @@ -222,12 +274,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.identity - service_provider = utils.find_resource( - service_client.federation.service_providers, + service_client = self.app.client_manager.sdk_connection.identity + service_provider = service_client.find_service_provider( parsed_args.service_provider, - id=parsed_args.service_provider, + ignore_missing=False, ) - service_provider._info.pop('links', None) - return zip(*sorted(service_provider._info.items())) + return _format_service_provider(service_provider) diff --git a/openstackclient/tests/functional/identity/v3/test_service_provider.py b/openstackclient/tests/functional/identity/v3/test_service_provider.py index 793223b9d..2f941d5e8 100644 --- a/openstackclient/tests/functional/identity/v3/test_service_provider.py +++ b/openstackclient/tests/functional/identity/v3/test_service_provider.py @@ -60,9 +60,5 @@ def test_sp_set(self): 'description': new_description, } ) - self.assertEqual(0, len(raw_output)) - raw_output = self.openstack( - f'service provider show {service_provider}' - ) updated_value = self.parse_show_as_object(raw_output) - self.assertIn(new_description, updated_value['description']) + self.assertEqual(new_description, updated_value.get('description')) diff --git a/openstackclient/tests/unit/identity/v3/test_service_provider.py b/openstackclient/tests/unit/identity/v3/test_service_provider.py index 23402d948..185cdc6b0 100644 --- a/openstackclient/tests/unit/identity/v3/test_service_provider.py +++ b/openstackclient/tests/unit/identity/v3/test_service_provider.py @@ -12,92 +12,88 @@ # License for the specific language governing permissions and limitations # under the License. -import copy + +from openstack.identity.v3 import service_provider as _service_provider +from openstack.test import fakes as sdk_fakes from openstackclient.identity.v3 import service_provider -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as service_fakes -class TestServiceProvider(service_fakes.TestFederatedIdentity): - def setUp(self): - super().setUp() - - federation_lib = self.identity_client.federation - self.service_providers_mock = federation_lib.service_providers - self.service_providers_mock.reset_mock() - - -class TestServiceProviderCreate(TestServiceProvider): +class TestServiceProviderCreate(service_fakes.TestFederatedIdentity): columns = ( - 'auth_url', - 'description', - 'enabled', 'id', + 'enabled', + 'description', + 'auth_url', 'sp_url', - ) - datalist = ( - service_fakes.sp_auth_url, - service_fakes.sp_description, - True, - service_fakes.sp_id, - service_fakes.service_provider_url, + 'relay_state_prefix', ) def setUp(self): super().setUp() - copied_sp = copy.deepcopy(service_fakes.SERVICE_PROVIDER) - resource = fakes.FakeResource(None, copied_sp, loaded=True) - self.service_providers_mock.create.return_value = resource + self.service_provider = sdk_fakes.generate_fake_resource( + _service_provider.ServiceProvider + ) + self.identity_sdk_client.create_service_provider.return_value = ( + self.service_provider + ) + self.data = ( + self.service_provider.id, + self.service_provider.is_enabled, + self.service_provider.description, + self.service_provider.auth_url, + self.service_provider.sp_url, + self.service_provider.relay_state_prefix, + ) self.cmd = service_provider.CreateServiceProvider(self.app, None) def test_create_service_provider_required_options_only(self): arglist = [ '--auth-url', - service_fakes.sp_auth_url, + self.service_provider.auth_url, '--service-provider-url', - service_fakes.service_provider_url, - service_fakes.sp_id, + self.service_provider.sp_url, + self.service_provider.id, ] verifylist = [ - ('auth_url', service_fakes.sp_auth_url), - ('service_provider_url', service_fakes.service_provider_url), - ('service_provider_id', service_fakes.sp_id), + ('auth_url', self.service_provider.auth_url), + ('service_provider_url', self.service_provider.sp_url), + ('service_provider_id', self.service_provider.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, - 'description': None, - 'auth_url': service_fakes.sp_auth_url, - 'sp_url': service_fakes.service_provider_url, + 'is_enabled': True, + 'auth_url': self.service_provider.auth_url, + 'sp_url': self.service_provider.sp_url, } - self.service_providers_mock.create.assert_called_with( - id=service_fakes.sp_id, **kwargs + self.identity_sdk_client.create_service_provider.assert_called_with( + id=self.service_provider.id, **kwargs ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + self.assertEqual(self.data, data) def test_create_service_provider_description(self): arglist = [ '--description', - service_fakes.sp_description, + self.service_provider.description, '--auth-url', - service_fakes.sp_auth_url, + self.service_provider.auth_url, '--service-provider-url', - service_fakes.service_provider_url, - service_fakes.sp_id, + self.service_provider.sp_url, + self.service_provider.id, ] verifylist = [ - ('description', service_fakes.sp_description), - ('auth_url', service_fakes.sp_auth_url), - ('service_provider_url', service_fakes.service_provider_url), - ('service_provider_id', service_fakes.sp_id), + ('description', self.service_provider.description), + ('auth_url', self.service_provider.auth_url), + ('service_provider_url', self.service_provider.sp_url), + ('service_provider_id', self.service_provider.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -105,111 +101,85 @@ def test_create_service_provider_description(self): # Set expected values kwargs = { - 'description': service_fakes.sp_description, - 'auth_url': service_fakes.sp_auth_url, - 'sp_url': service_fakes.service_provider_url, - 'enabled': True, + 'description': self.service_provider.description, + 'auth_url': self.service_provider.auth_url, + 'sp_url': self.service_provider.sp_url, + 'is_enabled': self.service_provider.is_enabled, } - self.service_providers_mock.create.assert_called_with( - id=service_fakes.sp_id, **kwargs + self.identity_sdk_client.create_service_provider.assert_called_with( + id=self.service_provider.id, **kwargs ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + self.assertEqual(self.data, data) def test_create_service_provider_disabled(self): - # Prepare FakeResource object - service_provider = copy.deepcopy(service_fakes.SERVICE_PROVIDER) - service_provider['enabled'] = False - service_provider['description'] = None - - resource = fakes.FakeResource(None, service_provider, loaded=True) - self.service_providers_mock.create.return_value = resource - arglist = [ '--auth-url', - service_fakes.sp_auth_url, + self.service_provider.auth_url, '--service-provider-url', - service_fakes.service_provider_url, + self.service_provider.sp_url, '--disable', - service_fakes.sp_id, + self.service_provider.id, ] verifylist = [ - ('auth_url', service_fakes.sp_auth_url), - ('service_provider_url', service_fakes.service_provider_url), - ('service_provider_id', service_fakes.sp_id), + ('auth_url', self.service_provider.auth_url), + ('service_provider_url', self.service_provider.sp_url), + ('service_provider_id', self.service_provider.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'auth_url': service_fakes.sp_auth_url, - 'sp_url': service_fakes.service_provider_url, - 'enabled': False, - 'description': None, + 'auth_url': self.service_provider.auth_url, + 'sp_url': self.service_provider.sp_url, + 'is_enabled': False, } - self.service_providers_mock.create.assert_called_with( - id=service_fakes.sp_id, **kwargs + self.identity_sdk_client.create_service_provider.assert_called_with( + id=self.service_provider.id, **kwargs ) self.assertEqual(self.columns, columns) - datalist = ( - service_fakes.sp_auth_url, - None, - False, - service_fakes.sp_id, - service_fakes.service_provider_url, - ) - self.assertEqual(datalist, data) + self.assertEqual(self.data, data) -class TestServiceProviderDelete(TestServiceProvider): +class TestServiceProviderDelete(service_fakes.TestFederatedIdentity): def setUp(self): super().setUp() - # This is the return value for utils.find_resource() - self.service_providers_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, + self.service_provider = sdk_fakes.generate_fake_resource( + _service_provider.ServiceProvider ) - - self.service_providers_mock.delete.return_value = None + self.identity_sdk_client.delete_service_provider.return_value = None self.cmd = service_provider.DeleteServiceProvider(self.app, None) def test_delete_service_provider(self): arglist = [ - service_fakes.sp_id, + self.service_provider.id, ] verifylist = [ - ('service_provider', [service_fakes.sp_id]), + ('service_provider', [self.service_provider.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.service_providers_mock.delete.assert_called_with( - service_fakes.sp_id, + self.identity_sdk_client.delete_service_provider.assert_called_with( + self.service_provider.id, ) self.assertIsNone(result) -class TestServiceProviderList(TestServiceProvider): +class TestServiceProviderList(service_fakes.TestFederatedIdentity): def setUp(self): super().setUp() - self.service_providers_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, + self.service_provider = sdk_fakes.generate_fake_resource( + _service_provider.ServiceProvider ) - self.service_providers_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, - ), + self.identity_sdk_client.service_providers.return_value = [ + self.service_provider ] # Get the command object to test @@ -225,39 +195,56 @@ def test_service_provider_list_no_options(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.service_providers_mock.list.assert_called_with() + self.identity_sdk_client.service_providers.assert_called_with() - collist = ('ID', 'Enabled', 'Description', 'Auth URL') + collist = ( + 'ID', + 'Enabled', + 'Description', + 'Auth URL', + 'Service Provider URL', + 'Relay State Prefix', + ) self.assertEqual(collist, columns) datalist = ( ( - service_fakes.sp_id, + self.service_provider.id, True, - service_fakes.sp_description, - service_fakes.sp_auth_url, + self.service_provider.description, + self.service_provider.auth_url, + self.service_provider.sp_url, + self.service_provider.relay_state_prefix, ), ) self.assertEqual(tuple(data), datalist) -class TestServiceProviderSet(TestServiceProvider): +class TestServiceProviderSet(service_fakes.TestFederatedIdentity): columns = ( - 'auth_url', - 'description', - 'enabled', 'id', + 'enabled', + 'description', + 'auth_url', 'sp_url', - ) - datalist = ( - service_fakes.sp_auth_url, - service_fakes.sp_description, - False, - service_fakes.sp_id, - service_fakes.service_provider_url, + 'relay_state_prefix', ) def setUp(self): super().setUp() + self.service_provider = sdk_fakes.generate_fake_resource( + _service_provider.ServiceProvider + ) + self.identity_sdk_client.update_service_provider.return_value = ( + self.service_provider + ) + self.data = ( + self.service_provider.id, + self.service_provider.is_enabled, + self.service_provider.description, + self.service_provider.auth_url, + self.service_provider.sp_url, + self.service_provider.relay_state_prefix, + ) self.cmd = service_provider.SetServiceProvider(self.app, None) def test_service_provider_disable(self): @@ -265,144 +252,107 @@ def test_service_provider_disable(self): Set Service Provider's ``enabled`` attribute to False. """ - - def prepare(self): - """Prepare fake return objects before the test is executed""" - updated_sp = copy.deepcopy(service_fakes.SERVICE_PROVIDER) - updated_sp['enabled'] = False - resources = fakes.FakeResource(None, updated_sp, loaded=True) - self.service_providers_mock.update.return_value = resources - - prepare(self) arglist = [ '--disable', - service_fakes.sp_id, + self.service_provider.id, ] verifylist = [ - ('service_provider', service_fakes.sp_id), - ('enable', False), - ('disable', True), + ('service_provider', self.service_provider.id), + ('is_enabled', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) - self.service_providers_mock.update.assert_called_with( - service_fakes.sp_id, - enabled=False, - description=None, - auth_url=None, - sp_url=None, + columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.update_service_provider.assert_called_with( + self.service_provider.id, + is_enabled=False, ) + self.assertEqual(columns, self.columns) + self.assertEqual(data, self.data) def test_service_provider_enable(self): """Enable Service Provider. Set Service Provider's ``enabled`` attribute to True. """ - - def prepare(self): - """Prepare fake return objects before the test is executed""" - resources = fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, - ) - self.service_providers_mock.update.return_value = resources - - prepare(self) arglist = [ '--enable', - service_fakes.sp_id, + self.service_provider.id, ] verifylist = [ - ('service_provider', service_fakes.sp_id), - ('enable', True), - ('disable', False), + ('service_provider', self.service_provider.id), + ('is_enabled', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.cmd.take_action(parsed_args) - self.service_providers_mock.update.assert_called_with( - service_fakes.sp_id, - enabled=True, - description=None, - auth_url=None, - sp_url=None, + columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.update_service_provider.assert_called_with( + self.service_provider.id, + is_enabled=True, ) + self.assertEqual(columns, self.columns) + self.assertEqual(data, self.data) def test_service_provider_no_options(self): - def prepare(self): - """Prepare fake return objects before the test is executed""" - resources = fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, - ) - self.service_providers_mock.get.return_value = resources - - resources = fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, - ) - self.service_providers_mock.update.return_value = resources - - prepare(self) arglist = [ - service_fakes.sp_id, + self.service_provider.id, ] verifylist = [ - ('service_provider', service_fakes.sp_id), + ('service_provider', self.service_provider.id), ('description', None), - ('enable', False), - ('disable', False), + ('is_enabled', None), ('auth_url', None), ('service_provider_url', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(columns, self.columns) + self.assertEqual(data, self.data) -class TestServiceProviderShow(TestServiceProvider): +class TestServiceProviderShow(service_fakes.TestFederatedIdentity): def setUp(self): super().setUp() - ret = fakes.FakeResource( - None, - copy.deepcopy(service_fakes.SERVICE_PROVIDER), - loaded=True, + self.service_provider = sdk_fakes.generate_fake_resource( + _service_provider.ServiceProvider + ) + self.identity_sdk_client.find_service_provider.return_value = ( + self.service_provider + ) + self.data = ( + self.service_provider.id, + self.service_provider.is_enabled, + self.service_provider.description, + self.service_provider.auth_url, + self.service_provider.sp_url, + self.service_provider.relay_state_prefix, ) - self.service_providers_mock.get.side_effect = [ - Exception("Not found"), - ret, - ] - self.service_providers_mock.get.return_value = ret # Get the command object to test self.cmd = service_provider.ShowServiceProvider(self.app, None) def test_service_provider_show(self): arglist = [ - service_fakes.sp_id, + self.service_provider.id, ] verifylist = [ - ('service_provider', service_fakes.sp_id), + ('service_provider', self.service_provider.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.service_providers_mock.get.assert_called_with( - service_fakes.sp_id, id='BETA' + self.identity_sdk_client.find_service_provider.assert_called_with( + self.service_provider.id, + ignore_missing=False, ) - collist = ('auth_url', 'description', 'enabled', 'id', 'sp_url') - self.assertEqual(collist, columns) - datalist = ( - service_fakes.sp_auth_url, - service_fakes.sp_description, - True, - service_fakes.sp_id, - service_fakes.service_provider_url, + collist = ( + 'id', + 'enabled', + 'description', + 'auth_url', + 'sp_url', + 'relay_state_prefix', ) - self.assertEqual(data, datalist) + self.assertEqual(collist, columns) + self.assertEqual(data, self.data) diff --git a/releasenotes/notes/migrate-service-provider-to-sdk-74dc48b227f21a05.yaml b/releasenotes/notes/migrate-service-provider-to-sdk-74dc48b227f21a05.yaml new file mode 100644 index 000000000..d9914cf97 --- /dev/null +++ b/releasenotes/notes/migrate-service-provider-to-sdk-74dc48b227f21a05.yaml @@ -0,0 +1,10 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``service provider create`` + - ``service provider delete`` + - ``service provider set`` + - ``service provider list`` + - ``service provider show`` From 329b351cb80464e9dc4b8a6bcb1a6855c8dc70bb Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 12 Dec 2024 10:39:53 +0000 Subject: [PATCH 060/245] Adopt sdk_fakes for compute.aggregate Drop fakes generation for compute aggregates in favor of sdk_fakes Change-Id: I4965a5fe8fc3d70390ca0268716519b617ca24eb --- .../tests/unit/compute/v2/fakes.py | 58 --------------- .../tests/unit/compute/v2/test_aggregate.py | 71 +++++++++++-------- 2 files changed, 40 insertions(+), 89 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index d021c5d76..792773e63 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -21,7 +21,6 @@ from keystoneauth1 import discover from openstack.compute.v2 import _proxy -from openstack.compute.v2 import aggregate as _aggregate from openstack.compute.v2 import availability_zone as _availability_zone from openstack.compute.v2 import extension as _extension from openstack.compute.v2 import flavor as _flavor @@ -142,63 +141,6 @@ class TestComputev2( ): ... -def create_one_aggregate(attrs=None): - """Create a fake aggregate. - - :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.aggregate.Aggregate object - """ - attrs = attrs or {} - - # Set default attribute - aggregate_info = { - "name": "aggregate-name-" + uuid.uuid4().hex, - "availability_zone": "ag_zone", - "hosts": [], - "id": "aggregate-id-" + uuid.uuid4().hex, - "metadata": { - "availability_zone": "ag_zone", - "key1": "value1", - }, - } - - # Overwrite default attributes. - aggregate_info.update(attrs) - - aggregate = _aggregate.Aggregate(**aggregate_info) - return aggregate - - -def create_aggregates(attrs=None, count=2): - """Create multiple fake aggregates. - - :param dict attrs: A dictionary with all attributes - :param int count: The number of aggregates to fake - :return: A list of fake openstack.compute.v2.aggregate.Aggregate objects - """ - aggregates = [] - for i in range(0, count): - aggregates.append(create_one_aggregate(attrs)) - - return aggregates - - -def get_aggregates(aggregates=None, count=2): - """Get an iterable MagicMock object with a list of faked aggregates. - - If aggregates list is provided, then initialize the Mock object - with the list. Otherwise create one. - - :return: A list of fake openstack.compute.v2.aggregate.Aggregate objects - :param int count: The number of aggregates to fake - :return: An iterable Mock object with side_effect set to a list of faked - aggregates - """ - if aggregates is None: - aggregates = create_aggregates(count) - return mock.Mock(side_effect=aggregates) - - def create_one_agent(attrs=None): """Create a fake agent. diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index 933344e8c..f905d67e3 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -16,7 +16,9 @@ from unittest import mock from unittest.mock import call +from openstack.compute.v2 import aggregate as _aggregate from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions @@ -26,8 +28,6 @@ class TestAggregate(compute_fakes.TestComputev2): - fake_ag = compute_fakes.create_one_aggregate() - columns = ( 'availability_zone', 'created_at', @@ -41,18 +41,25 @@ class TestAggregate(compute_fakes.TestComputev2): 'uuid', ) - data = ( - fake_ag.availability_zone, - fake_ag.created_at, - fake_ag.deleted_at, - format_columns.ListColumn(fake_ag.hosts), - fake_ag.id, - fake_ag.is_deleted, - fake_ag.name, - format_columns.DictColumn(fake_ag.metadata), - fake_ag.updated_at, - fake_ag.uuid, - ) + def setUp(self): + super().setUp() + + self.fake_ag = sdk_fakes.generate_fake_resource( + _aggregate.Aggregate, + metadata={'availability_zone': 'ag_zone', 'key1': 'value1'}, + ) + self.data = ( + self.fake_ag.availability_zone, + self.fake_ag.created_at, + self.fake_ag.deleted_at, + format_columns.ListColumn(self.fake_ag.hosts), + self.fake_ag.id, + self.fake_ag.is_deleted, + self.fake_ag.name, + format_columns.DictColumn(self.fake_ag.metadata), + self.fake_ag.updated_at, + self.fake_ag.uuid, + ) class TestAggregateAddHost(TestAggregate): @@ -155,13 +162,15 @@ def test_aggregate_create_with_property(self): class TestAggregateDelete(TestAggregate): - fake_ags = compute_fakes.create_aggregates(count=2) - def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate = compute_fakes.get_aggregates( - self.fake_ags + self.fake_ags = list( + sdk_fakes.generate_fake_resources(_aggregate.Aggregate, 2) + ) + + self.compute_sdk_client.find_aggregate = mock.Mock( + side_effect=[self.fake_ags[0], self.fake_ags[1]] ) self.cmd = aggregate.DeleteAggregate(self.app, None) @@ -526,25 +535,25 @@ class TestAggregateShow(TestAggregate): 'uuid', ) - data = ( - TestAggregate.fake_ag.availability_zone, - TestAggregate.fake_ag.created_at, - TestAggregate.fake_ag.deleted_at, - format_columns.ListColumn(TestAggregate.fake_ag.hosts), - TestAggregate.fake_ag.id, - TestAggregate.fake_ag.is_deleted, - TestAggregate.fake_ag.name, - format_columns.DictColumn(TestAggregate.fake_ag.metadata), - TestAggregate.fake_ag.updated_at, - TestAggregate.fake_ag.uuid, - ) - def setUp(self): super().setUp() self.compute_sdk_client.find_aggregate.return_value = self.fake_ag self.cmd = aggregate.ShowAggregate(self.app, None) + self.data = ( + self.fake_ag.availability_zone, + self.fake_ag.created_at, + self.fake_ag.deleted_at, + format_columns.ListColumn(self.fake_ag.hosts), + self.fake_ag.id, + self.fake_ag.is_deleted, + self.fake_ag.name, + format_columns.DictColumn(self.fake_ag.metadata), + self.fake_ag.updated_at, + self.fake_ag.uuid, + ) + def test_aggregate_show(self): arglist = [ 'ag1', From d175dea6bcaa4b7239d3652c1383148280f3122d Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Wed, 7 Jun 2023 14:41:21 +0200 Subject: [PATCH 061/245] Adopt sdk_fakes for compute.test_console Change-Id: I6181009a8b2ae7e632214364527ccc6fdd03fff9 --- openstackclient/tests/unit/compute/v2/test_console.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/test_console.py b/openstackclient/tests/unit/compute/v2/test_console.py index a179db5a7..d4c6940b6 100644 --- a/openstackclient/tests/unit/compute/v2/test_console.py +++ b/openstackclient/tests/unit/compute/v2/test_console.py @@ -15,6 +15,9 @@ from unittest import mock +from openstack.compute.v2 import server as _server +from openstack.test import fakes as sdk_fakes + from openstackclient.compute.v2 import console from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes from openstackclient.tests.unit import utils @@ -24,7 +27,7 @@ class TestConsoleLog(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self._server = compute_fakes.create_one_sdk_server() + self._server = sdk_fakes.generate_fake_resource(_server.Server) self.compute_sdk_client.find_server.return_value = self._server self.cmd = console.ShowConsoleLog(self.app, None) @@ -76,11 +79,12 @@ def test_show_lines(self): class TestConsoleUrlShow(compute_fakes.TestComputev2): - _server = compute_fakes.create_one_sdk_server() - def setUp(self): super().setUp() + + self._server = sdk_fakes.generate_fake_resource(_server.Server) self.compute_sdk_client.find_server.return_value = self._server + fake_console_data = { 'url': 'http://localhost', 'protocol': 'fake_protocol', From 42b1698e5c7f1ff322a95e8d4290cc3c34d1c277 Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Wed, 7 Jun 2023 15:43:31 +0200 Subject: [PATCH 062/245] Adopt sdk_fakes for compute.test_keypair Change-Id: Ifb5df852a9ae6eea3fabce13d450b16cb8348315 --- .../tests/unit/compute/v2/fakes.py | 56 -------- .../tests/unit/compute/v2/test_keypair.py | 126 ++++++++---------- 2 files changed, 57 insertions(+), 125 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 792773e63..ce2f6bd64 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -25,7 +25,6 @@ from openstack.compute.v2 import extension as _extension from openstack.compute.v2 import flavor as _flavor from openstack.compute.v2 import hypervisor as _hypervisor -from openstack.compute.v2 import keypair as _keypair from openstack.compute.v2 import limits as _limits from openstack.compute.v2 import migration as _migration from openstack.compute.v2 import server as _server @@ -620,61 +619,6 @@ def create_one_flavor_access(attrs=None): return flavor_access -def create_one_keypair(attrs=None): - """Create a fake keypair - - :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.keypair.Keypair object - """ - attrs = attrs or {} - - # Set default attributes. - keypair_info = { - 'name': 'keypair-name-' + uuid.uuid4().hex, - 'type': 'ssh', - 'fingerprint': 'dummy', - 'public_key': 'dummy', - 'user_id': 'user', - } - - # Overwrite default attributes. - keypair_info.update(attrs) - - return _keypair.Keypair(**keypair_info) - - -def create_keypairs(attrs=None, count=2): - """Create multiple fake keypairs. - - :param dict attrs: A dictionary with all attributes - :param int count: The number of keypairs to fake - :return: A list of fake openstack.compute.v2.keypair.Keypair objects - """ - - keypairs = [] - for i in range(0, count): - keypairs.append(create_one_keypair(attrs)) - - return keypairs - - -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 fake openstack.compute.v2.keypair.Keypair - objects - :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 = create_keypairs(count) - return mock.Mock(side_effect=keypairs) - - def create_one_availability_zone(attrs=None): """Create a fake AZ. diff --git a/openstackclient/tests/unit/compute/v2/test_keypair.py b/openstackclient/tests/unit/compute/v2/test_keypair.py index d6be7d0d6..0ce08b48c 100644 --- a/openstackclient/tests/unit/compute/v2/test_keypair.py +++ b/openstackclient/tests/unit/compute/v2/test_keypair.py @@ -12,17 +12,18 @@ # License for the specific language governing permissions and limitations # under the License. -import copy from unittest import mock from unittest.mock import call import uuid +from openstack.compute.v2 import keypair as _keypair +from openstack.identity.v3 import project as _project +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.compute.v2 import keypair from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -from openstackclient.tests.unit import fakes -from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes from openstackclient.tests.unit import utils as tests_utils @@ -31,20 +32,17 @@ def setUp(self): super().setUp() # Initialize the user mock + self._user = sdk_fakes.generate_fake_resource(_user.User) self.users_mock = self.identity_client.users self.users_mock.reset_mock() - self.users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) + self.users_mock.get.return_value = self._user class TestKeypairCreate(TestKeypair): def setUp(self): super().setUp() - self.keypair = compute_fakes.create_one_keypair() + self.keypair = sdk_fakes.generate_fake_resource(_keypair.Keypair) self.columns = ( 'created_at', @@ -122,7 +120,7 @@ def test_keypair_create_public_key(self): ) as mock_open: mock_open.return_value = mock.MagicMock() m_file = mock_open.return_value.__enter__.return_value - m_file.read.return_value = 'dummy' + m_file.read.return_value = self.keypair.public_key columns, data = self.cmd.take_action(parsed_args) @@ -208,7 +206,7 @@ def test_keypair_create_with_key_type(self): ) as mock_open: mock_open.return_value = mock.MagicMock() m_file = mock_open.return_value.__enter__.return_value - m_file.read.return_value = 'dummy' + m_file.read.return_value = self.keypair.public_key columns, data = self.cmd.take_action(parsed_args) self.compute_sdk_client.create_keypair.assert_called_with( @@ -263,11 +261,11 @@ def test_key_pair_create_with_user(self, mock_generate): arglist = [ '--user', - identity_fakes.user_name, + self._user.name, self.keypair.name, ] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ('name', self.keypair.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -276,7 +274,7 @@ def test_key_pair_create_with_user(self, mock_generate): self.compute_sdk_client.create_keypair.assert_called_with( name=self.keypair.name, - user_id=identity_fakes.user_id, + user_id=self._user.id, public_key=mock_generate.return_value.public_key, ) @@ -288,11 +286,11 @@ def test_key_pair_create_with_user_pre_v210(self): arglist = [ '--user', - identity_fakes.user_name, + self._user.name, self.keypair.name, ] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ('name', self.keypair.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -306,11 +304,13 @@ def test_key_pair_create_with_user_pre_v210(self): class TestKeypairDelete(TestKeypair): - keypairs = compute_fakes.create_keypairs(count=2) - def setUp(self): super().setUp() + self.keypairs = list( + sdk_fakes.generate_fake_resources(_keypair.Keypair, count=2) + ) + self.cmd = keypair.DeleteKeypair(self.app, None) def test_keypair_delete(self): @@ -374,9 +374,9 @@ def test_delete_multiple_keypairs_with_exception(self): def test_keypair_delete_with_user(self): self.set_compute_api_version('2.10') - arglist = ['--user', identity_fakes.user_name, self.keypairs[0].name] + arglist = ['--user', self._user.name, self.keypairs[0].name] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ('name', [self.keypairs[0].name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -386,16 +386,16 @@ def test_keypair_delete_with_user(self): self.assertIsNone(ret) self.compute_sdk_client.delete_keypair.assert_called_with( self.keypairs[0].name, - user_id=identity_fakes.user_id, + user_id=self._user.id, ignore_missing=False, ) def test_keypair_delete_with_user_pre_v210(self): self.set_compute_api_version('2.9') - arglist = ['--user', identity_fakes.user_name, self.keypairs[0].name] + arglist = ['--user', self._user.name, self.keypairs[0].name] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ('name', [self.keypairs[0].name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -409,17 +409,19 @@ def test_keypair_delete_with_user_pre_v210(self): class TestKeypairList(TestKeypair): - # Return value of self.compute_sdk_client.keypairs(). - keypairs = compute_fakes.create_keypairs(count=1) - def setUp(self): super().setUp() - self.compute_sdk_client.keypairs.return_value = self.keypairs + self.keypairs = list( + sdk_fakes.generate_fake_resources(_keypair.Keypair, count=1) + ) + self.compute_sdk_client.keypairs.return_value = iter(self.keypairs) # Get the command object to test self.cmd = keypair.ListKeypair(self.app, None) + self._project = sdk_fakes.generate_fake_resource(_project.Project) + def test_keypair_list_no_options(self): arglist = [] verifylist = [] @@ -475,26 +477,22 @@ def test_keypair_list_with_user(self): users_mock = self.identity_client.users users_mock.reset_mock() - users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) + users_mock.get.return_value = self._user arglist = [ '--user', - identity_fakes.user_name, + self._user.name, ] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - users_mock.get.assert_called_with(identity_fakes.user_name) + users_mock.get.assert_called_with(self._user.name) self.compute_sdk_client.keypairs.assert_called_with( - user_id=identity_fakes.user_id, + user_id=self._user.id, ) self.assertEqual(('Name', 'Fingerprint', 'Type'), columns) @@ -514,10 +512,10 @@ def test_keypair_list_with_user_pre_v210(self): arglist = [ '--user', - identity_fakes.user_name, + self._user.name, ] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -533,32 +531,22 @@ def test_keypair_list_with_project(self): projects_mock = self.identity_client.tenants projects_mock.reset_mock() - projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) + projects_mock.get.return_value = self._project users_mock = self.identity_client.users users_mock.reset_mock() - users_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ), - ] + users_mock.list.return_value = [self._user] - arglist = ['--project', identity_fakes.project_name] - verifylist = [('project', identity_fakes.project_name)] + arglist = ['--project', self._project.name] + verifylist = [('project', self._project.name)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - projects_mock.get.assert_called_with(identity_fakes.project_name) - users_mock.list.assert_called_with(tenant_id=identity_fakes.project_id) + projects_mock.get.assert_called_with(self._project.name) + users_mock.list.assert_called_with(tenant_id=self._project.id) self.compute_sdk_client.keypairs.assert_called_with( - user_id=identity_fakes.user_id, + user_id=self._user.id, ) self.assertEqual(('Name', 'Fingerprint', 'Type'), columns) @@ -576,8 +564,8 @@ def test_keypair_list_with_project(self): def test_keypair_list_with_project_pre_v210(self): self.set_compute_api_version('2.9') - arglist = ['--project', identity_fakes.project_name] - verifylist = [('project', identity_fakes.project_name)] + arglist = ['--project', self._project.name] + verifylist = [('project', self._project.name)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) ex = self.assertRaises( @@ -590,9 +578,9 @@ def test_keypair_list_with_project_pre_v210(self): def test_keypair_list_conflicting_user_options(self): arglist = [ '--user', - identity_fakes.user_name, + self._user.name, '--project', - identity_fakes.project_name, + self._project.name, ] self.assertRaises( @@ -707,7 +695,7 @@ def test_keypair_show_no_options(self): ) def test_keypair_show(self): - self.keypair = compute_fakes.create_one_keypair() + self.keypair = sdk_fakes.generate_fake_resource(_keypair.Keypair) self.compute_sdk_client.find_keypair.return_value = self.keypair self.data = ( @@ -735,7 +723,7 @@ def test_keypair_show(self): self.assertEqual(self.data, data) def test_keypair_show_public(self): - self.keypair = compute_fakes.create_one_keypair() + self.keypair = sdk_fakes.generate_fake_resource(_keypair.Keypair) self.compute_sdk_client.find_keypair.return_value = self.keypair arglist = ['--public-key', self.keypair.name] @@ -751,7 +739,7 @@ def test_keypair_show_public(self): def test_keypair_show_with_user(self): self.set_compute_api_version('2.10') - self.keypair = compute_fakes.create_one_keypair() + self.keypair = sdk_fakes.generate_fake_resource(_keypair.Keypair) self.compute_sdk_client.find_keypair.return_value = self.keypair self.data = ( @@ -767,22 +755,22 @@ def test_keypair_show_with_user(self): arglist = [ '--user', - identity_fakes.user_name, + self._user.name, self.keypair.name, ] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ('name', self.keypair.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.users_mock.get.assert_called_with(identity_fakes.user_name) + self.users_mock.get.assert_called_with(self._user.name) self.compute_sdk_client.find_keypair.assert_called_with( self.keypair.name, ignore_missing=False, - user_id=identity_fakes.user_id, + user_id=self._user.id, ) self.assertEqual(self.columns, columns) @@ -791,14 +779,14 @@ def test_keypair_show_with_user(self): def test_keypair_show_with_user_pre_v210(self): self.set_compute_api_version('2.9') - self.keypair = compute_fakes.create_one_keypair() + self.keypair = sdk_fakes.generate_fake_resource(_keypair.Keypair) arglist = [ '--user', - identity_fakes.user_name, + self._user.name, self.keypair.name, ] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self._user.name), ('name', self.keypair.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) From afc0d3c25287ac98023ae0d97d8b808796bd5372 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 12 Dec 2024 11:01:30 +0000 Subject: [PATCH 063/245] Adopt sdk_fakes for compute.test_service Change-Id: I331283f42914b91bd80dfca354635214fb7ba8a7 --- .../tests/unit/compute/v2/fakes.py | 46 ------- .../tests/unit/compute/v2/test_service.py | 120 ++++++++++++------ 2 files changed, 83 insertions(+), 83 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index ce2f6bd64..5fcd9fa8f 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -32,7 +32,6 @@ from openstack.compute.v2 import server_group as _server_group from openstack.compute.v2 import server_interface as _server_interface from openstack.compute.v2 import server_migration as _server_migration -from openstack.compute.v2 import service as _service from openstack.compute.v2 import usage as _usage from openstack.compute.v2 import volume_attachment as _volume_attachment @@ -485,51 +484,6 @@ def create_one_server_action(attrs=None): return server_action -def create_one_service(attrs=None): - """Create a fake service. - - :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.service.Service object - """ - attrs = attrs or {} - - # Set default attributes. - service_info = { - 'id': 'id-' + uuid.uuid4().hex, - 'host': 'host-' + uuid.uuid4().hex, - 'binary': 'binary-' + uuid.uuid4().hex, - 'status': 'enabled', - 'availability_zone': 'zone-' + uuid.uuid4().hex, - 'state': 'state-' + uuid.uuid4().hex, - 'updated_at': 'time-' + uuid.uuid4().hex, - 'disabled_reason': 'earthquake', - # Introduced in API microversion 2.11 - 'is_forced_down': False, - } - - # Overwrite default attributes. - service_info.update(attrs) - - return _service.Service(**service_info) - - -def create_services(attrs=None, count=2): - """Create multiple fake services. - - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of services to fake - :return: - A list of FakeResource objects faking the services - """ - services = [] - for i in range(0, count): - services.append(create_one_service(attrs)) - - return services - - def create_one_flavor(attrs=None): """Create a fake flavor. diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py index 2997fec79..ce7791d23 100644 --- a/openstackclient/tests/unit/compute/v2/test_service.py +++ b/openstackclient/tests/unit/compute/v2/test_service.py @@ -14,6 +14,8 @@ from unittest import mock +from openstack.compute.v2 import service as _service +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.compute.v2 import service @@ -21,11 +23,13 @@ class TestServiceDelete(compute_fakes.TestComputev2): - services = compute_fakes.create_services(count=2) - def setUp(self): super().setUp() + self.services = list( + sdk_fakes.generate_fake_resources(_service.Service, count=2) + ) + self.compute_sdk_client.delete_service.return_value = None # Get the command object to test @@ -94,35 +98,11 @@ def test_multi_services_delete_with_exception(self): class TestServiceList(compute_fakes.TestComputev2): - service = compute_fakes.create_one_service() - - columns = ( - 'ID', - 'Binary', - 'Host', - 'Zone', - 'Status', - 'State', - 'Updated At', - ) - columns_long = columns + ('Disabled Reason',) - - data = [ - ( - service.id, - service.binary, - service.host, - service.availability_zone, - service.status, - service.state, - service.updated_at, - ) - ] - data_long = [data[0] + (service.disabled_reason,)] - def setUp(self): super().setUp() + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.compute_sdk_client.services.return_value = [self.service] # Get the command object to test @@ -151,8 +131,29 @@ def test_service_list(self): binary=self.service.binary, ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, list(data)) + expected_columns = ( + 'ID', + 'Binary', + 'Host', + 'Zone', + 'Status', + 'State', + 'Updated At', + ) + expected_data = [ + ( + self.service.id, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + ) + ] + + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, list(data)) def test_service_list_with_long_option(self): arglist = [ @@ -179,8 +180,31 @@ def test_service_list_with_long_option(self): binary=self.service.binary, ) - self.assertEqual(self.columns_long, columns) - self.assertEqual(self.data_long, list(data)) + expected_columns = ( + 'ID', + 'Binary', + 'Host', + 'Zone', + 'Status', + 'State', + 'Updated At', + 'Disabled Reason', + ) + expected_data = [ + ( + self.service.id, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + self.service.disabled_reason, + ) + ] + + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, list(data)) def test_service_list_with_long_option_2_11(self): self.set_compute_api_version('2.11') @@ -210,18 +234,40 @@ def test_service_list_with_long_option_2_11(self): ) # In 2.11 there is also a forced_down column. - columns_long = self.columns_long + ('Forced Down',) - data_long = [self.data_long[0] + (self.service.is_forced_down,)] + expected_columns = ( + 'ID', + 'Binary', + 'Host', + 'Zone', + 'Status', + 'State', + 'Updated At', + 'Disabled Reason', + 'Forced Down', + ) + expected_data = [ + ( + self.service.id, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + self.service.disabled_reason, + self.service.is_forced_down, + ) + ] - self.assertEqual(columns_long, columns) - self.assertEqual(data_long, list(data)) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, list(data)) class TestServiceSet(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.service = compute_fakes.create_one_service() + self.service = sdk_fakes.generate_fake_resource(_service.Service) self.compute_sdk_client.enable_service.return_value = self.service self.compute_sdk_client.disable_service.return_value = self.service From 776b7d0c66794147f3b30a0ad74ecb2438b77c0c Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Wed, 7 Jun 2023 15:58:34 +0200 Subject: [PATCH 064/245] Adopt sdk_fakes for compute.test_usage Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/882682 Change-Id: Ia8fd2ccfd1e86749fdeeb49c2d57de64635cbb82 --- .../tests/unit/compute/v2/fakes.py | 57 ------------------- .../tests/unit/compute/v2/test_usage.py | 19 ++++--- 2 files changed, 12 insertions(+), 64 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 5fcd9fa8f..a921591fd 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -32,7 +32,6 @@ from openstack.compute.v2 import server_group as _server_group from openstack.compute.v2 import server_interface as _server_interface from openstack.compute.v2 import server_migration as _server_migration -from openstack.compute.v2 import usage as _usage from openstack.compute.v2 import volume_attachment as _volume_attachment from openstackclient.tests.unit import fakes @@ -810,62 +809,6 @@ def get_networks(networks=None, count=2): return mock.Mock(side_effect=networks) -def create_one_usage(attrs=None): - """Create a fake usage. - - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with tenant_id and other attributes - """ - if attrs is None: - attrs = {} - - # Set default attributes. - usage_info = { - 'project_id': 'usage-tenant-id-' + uuid.uuid4().hex, - 'total_memory_mb_usage': 512.0, - 'total_vcpus_usage': 1.0, - 'total_local_gb_usage': 1.0, - 'server_usages': [ - { - 'ended_at': None, - 'flavor': 'usage-flavor-' + uuid.uuid4().hex, - 'hours': 1.0, - 'local_gb': 1, - 'memory_mb': 512, - 'name': 'usage-name-' + uuid.uuid4().hex, - 'instance_id': uuid.uuid4().hex, - 'state': 'active', - 'uptime': 3600, - 'vcpus': 1, - } - ], - } - - # Overwrite default attributes. - usage_info.update(attrs) - - return _usage.Usage(**usage_info) - - -def create_usages(attrs=None, count=2): - """Create multiple fake services. - - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of services to fake - :return: - A list of FakeResource objects faking the services - """ - usages = [] - for i in range(0, count): - usages.append(create_one_usage(attrs)) - - return usages - - def create_limits(attrs=None): """Create a fake limits object.""" attrs = attrs or {} diff --git a/openstackclient/tests/unit/compute/v2/test_usage.py b/openstackclient/tests/unit/compute/v2/test_usage.py index 2efd25b54..346e2fb0b 100644 --- a/openstackclient/tests/unit/compute/v2/test_usage.py +++ b/openstackclient/tests/unit/compute/v2/test_usage.py @@ -14,9 +14,12 @@ import datetime from unittest import mock +from openstack.compute.v2 import usage as _usage +from openstack.identity.v3 import project as _project +from openstack.test import fakes as sdk_fakes + from openstackclient.compute.v2 import usage as usage_cmds from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes class TestUsage(compute_fakes.TestComputev2): @@ -28,11 +31,11 @@ def setUp(self): class TestUsageList(TestUsage): - project = identity_fakes.FakeProject.create_one_project() + project = sdk_fakes.generate_fake_resource(_project.Project) # Return value of self.usage_mock.list(). - usages = compute_fakes.create_usages( - attrs={'project_id': project.name}, count=1 - ) + usages = [ + sdk_fakes.generate_fake_resource(_usage.Usage, project_id=project.name) + ] columns = ( "Project", @@ -123,9 +126,11 @@ def test_usage_list_with_pagination(self): class TestUsageShow(TestUsage): - project = identity_fakes.FakeProject.create_one_project() + project = sdk_fakes.generate_fake_resource(_project.Project) # Return value of self.usage_mock.list(). - usage = compute_fakes.create_one_usage(attrs={'project_id': project.name}) + usage = sdk_fakes.generate_fake_resource( + _usage.Usage, project_id=project.name + ) columns = ( 'Project', From 38029c698883762fe8ab2d824a689bb516940545 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 12 Dec 2024 11:06:33 +0000 Subject: [PATCH 065/245] Adopt sdk_fakes for compute.test_hypervisor Change-Id: Ibed4390be61c98f8c9e348835493bc714f8b9e87 --- .../tests/unit/compute/v2/fakes.py | 60 ---------- .../tests/unit/compute/v2/test_hypervisor.py | 113 ++++++++++-------- 2 files changed, 60 insertions(+), 113 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index a921591fd..ae8053f9a 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -24,7 +24,6 @@ from openstack.compute.v2 import availability_zone as _availability_zone from openstack.compute.v2 import extension as _extension from openstack.compute.v2 import flavor as _flavor -from openstack.compute.v2 import hypervisor as _hypervisor from openstack.compute.v2 import limits as _limits from openstack.compute.v2 import migration as _migration from openstack.compute.v2 import server as _server @@ -1020,65 +1019,6 @@ def create_volume_attachments(attrs=None, count=2): return volume_attachments -def create_one_hypervisor(attrs=None): - """Create a fake hypervisor. - - :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.hypervisor.Hypervisor object - """ - attrs = attrs or {} - - # Set default attributes. - hypervisor_info = { - 'id': 'hypervisor-id-' + uuid.uuid4().hex, - 'hypervisor_hostname': 'hypervisor-hostname-' + uuid.uuid4().hex, - 'status': 'enabled', - 'host_ip': '192.168.0.10', - 'cpu_info': { - 'aaa': 'aaa', - }, - 'free_disk_gb': 50, - 'hypervisor_version': 2004001, - 'disk_available_least': 50, - 'local_gb': 50, - 'free_ram_mb': 1024, - 'service': { - 'host': 'aaa', - 'disabled_reason': None, - 'id': 1, - }, - 'vcpus_used': 0, - 'hypervisor_type': 'QEMU', - 'local_gb_used': 0, - 'vcpus': 4, - 'memory_mb_used': 512, - 'memory_mb': 1024, - 'current_workload': 0, - 'state': 'up', - 'running_vms': 0, - } - - # Overwrite default attributes. - hypervisor_info.update(attrs) - - hypervisor = _hypervisor.Hypervisor(**hypervisor_info, loaded=True) - return hypervisor - - -def create_hypervisors(attrs=None, count=2): - """Create multiple fake hypervisors. - - :param dict attrs: A dictionary with all attributes - :param int count: The number of hypervisors to fake - :return: A list of fake openstack.compute.v2.hypervisor.Hypervisor objects - """ - hypervisors = [] - for i in range(0, count): - hypervisors.append(create_one_hypervisor(attrs)) - - return hypervisors - - def create_one_server_group(attrs=None): """Create a fake server group diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor.py b/openstackclient/tests/unit/compute/v2/test_hypervisor.py index 2c83fe4c9..da6e7e56d 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor.py @@ -14,7 +14,9 @@ import json +from openstack.compute.v2 import hypervisor as _hypervisor from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions @@ -27,8 +29,12 @@ def setUp(self): super().setUp() # Fake hypervisors to be listed up - self.hypervisors = compute_fakes.create_hypervisors() - self.compute_sdk_client.hypervisors.return_value = self.hypervisors + self.hypervisors = list( + sdk_fakes.generate_fake_resources(_hypervisor.Hypervisor, count=2) + ) + self.compute_sdk_client.hypervisors.return_value = iter( + self.hypervisors + ) self.columns = ( "ID", @@ -125,9 +131,9 @@ def test_hypervisor_list_matching_option_found(self): ( self.hypervisors[0].id, self.hypervisors[0].name, - self.hypervisors[1].hypervisor_type, - self.hypervisors[1].host_ip, - self.hypervisors[1].state, + self.hypervisors[0].hypervisor_type, + self.hypervisors[0].host_ip, + self.hypervisors[0].state, ), ) @@ -290,10 +296,11 @@ def setUp(self): ) # Fake hypervisors to be listed up - self.hypervisor = compute_fakes.create_one_hypervisor( - attrs={ - 'uptime': uptime_string, - } + self.hypervisor = sdk_fakes.generate_fake_resource( + _hypervisor.Hypervisor, + uptime=uptime_string, + service={"id": 1, "host": "aaa"}, + cpu_info={"aaa": "aaa"}, ) self.compute_sdk_client.find_hypervisor.return_value = self.hypervisor @@ -332,18 +339,18 @@ def setUp(self): self.data_v288 = ( [], - format_columns.DictColumn({'aaa': 'aaa'}), - '192.168.0.10', + format_columns.DictColumn(self.hypervisor.cpu_info), + self.hypervisor.host_ip, '01:28:24', self.hypervisor.name, - 'QEMU', - 2004001, + self.hypervisor.hypervisor_type, + self.hypervisor.hypervisor_version, self.hypervisor.id, '0.94, 0.62, 0.50', - 'aaa', - 1, - 'up', - 'enabled', + self.hypervisor.service_details["host"], + self.hypervisor.service_details["id"], + self.hypervisor.state, + self.hypervisor.status, '3 days, 11:15', '1', ) @@ -378,31 +385,31 @@ def setUp(self): ) self.data = ( [], - format_columns.DictColumn({'aaa': 'aaa'}), - 0, - 50, - 50, - 1024, - '192.168.0.10', + format_columns.DictColumn(self.hypervisor.cpu_info), + self.hypervisor.current_workload, + self.hypervisor.disk_available, + self.hypervisor.local_disk_free, + self.hypervisor.memory_free, + self.hypervisor.host_ip, '01:28:24', self.hypervisor.name, - 'QEMU', - 2004001, + self.hypervisor.hypervisor_type, + self.hypervisor.hypervisor_version, self.hypervisor.id, '0.94, 0.62, 0.50', - 50, - 0, - 1024, - 512, - 0, - 'aaa', + self.hypervisor.local_disk_size, + self.hypervisor.local_disk_used, + self.hypervisor.memory_size, + self.hypervisor.memory_used, + self.hypervisor.running_vms, + self.hypervisor.service_details["host"], 1, - 'up', - 'enabled', + self.hypervisor.state, + self.hypervisor.status, '3 days, 11:15', '1', - 4, - 0, + self.hypervisor.vcpus, + self.hypervisor.vcpus_used, ) # Get the command object to test @@ -537,27 +544,27 @@ def test_hypervisor_show_uptime_not_implemented(self): ) expected_data = ( [], - format_columns.DictColumn({'aaa': 'aaa'}), - 0, - 50, - 50, - 1024, - '192.168.0.10', + format_columns.DictColumn(self.hypervisor.cpu_info), + self.hypervisor.current_workload, + self.hypervisor.disk_available, + self.hypervisor.local_disk_free, + self.hypervisor.memory_free, + self.hypervisor.host_ip, self.hypervisor.name, - 'QEMU', - 2004001, + self.hypervisor.hypervisor_type, + self.hypervisor.hypervisor_version, self.hypervisor.id, - 50, - 0, - 1024, - 512, - 0, - 'aaa', + self.hypervisor.local_disk_size, + self.hypervisor.local_disk_used, + self.hypervisor.memory_size, + self.hypervisor.memory_used, + self.hypervisor.running_vms, + self.hypervisor.service_details["host"], 1, - 'up', - 'enabled', - 4, - 0, + self.hypervisor.state, + self.hypervisor.status, + self.hypervisor.vcpus, + self.hypervisor.vcpus_used, ) self.assertEqual(expected_columns, columns) From 4cef5f5549299258cf2285b087529ad98fe5cf24 Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Fri, 9 Jun 2023 17:50:33 +0200 Subject: [PATCH 066/245] Adopt sdk_fakes for compute.test_server_group Change-Id: I1c97a7b2e28233a3b345a81b62c01e74a0aec914 --- .../tests/unit/compute/v2/fakes.py | 27 --- .../tests/unit/compute/v2/test_server.py | 6 +- .../unit/compute/v2/test_server_group.py | 204 +++++++++--------- 3 files changed, 104 insertions(+), 133 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index ae8053f9a..62758c816 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -28,7 +28,6 @@ from openstack.compute.v2 import migration as _migration from openstack.compute.v2 import server as _server from openstack.compute.v2 import server_action as _server_action -from openstack.compute.v2 import server_group as _server_group from openstack.compute.v2 import server_interface as _server_interface from openstack.compute.v2 import server_migration as _server_migration from openstack.compute.v2 import volume_attachment as _volume_attachment @@ -1019,32 +1018,6 @@ def create_volume_attachments(attrs=None, count=2): return volume_attachments -def create_one_server_group(attrs=None): - """Create a fake server group - - :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.server_group.ServerGroup object - """ - if attrs is None: - attrs = {} - - # Set default attributes. - server_group_info = { - 'id': 'server-group-id-' + uuid.uuid4().hex, - 'member_ids': '', - 'metadata': {}, - 'name': 'server-group-name-' + uuid.uuid4().hex, - 'project_id': 'server-group-project-id-' + uuid.uuid4().hex, - 'user_id': 'server-group-user-id-' + uuid.uuid4().hex, - } - - # Overwrite default attributes. - server_group_info.update(attrs) - - server_group = _server_group.ServerGroup(**server_group_info) - return server_group - - def create_one_server_interface(attrs=None): """Create a fake SDK ServerInterface. diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 0870e7354..789daadf4 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -21,7 +21,9 @@ import uuid import iso8601 +from openstack.compute.v2 import server_group as _server_group from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib import utils as common_utils @@ -1375,7 +1377,9 @@ def test_server_create_minimal(self): self.assertEqual(self.datalist(), data) def test_server_create_with_options(self): - server_group = compute_fakes.create_one_server_group() + server_group = sdk_fakes.generate_fake_resource( + _server_group.ServerGroup + ) self.compute_sdk_client.find_server_group.return_value = server_group security_group = network_fakes.create_one_security_group() diff --git a/openstackclient/tests/unit/compute/v2/test_server_group.py b/openstackclient/tests/unit/compute/v2/test_server_group.py index 2bef02796..2e3449323 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_group.py +++ b/openstackclient/tests/unit/compute/v2/test_server_group.py @@ -13,6 +13,8 @@ # under the License. # +from openstack.compute.v2 import server_group as _server_group +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions @@ -22,27 +24,31 @@ class TestServerGroup(compute_fakes.TestComputev2): - fake_server_group = compute_fakes.create_one_server_group() - - columns = ( - 'id', - 'members', - 'name', - 'policy', - 'project_id', - 'rules', - 'user_id', - ) - - data = ( - fake_server_group.id, - format_columns.ListColumn(fake_server_group.member_ids), - fake_server_group.name, - fake_server_group.policy, - fake_server_group.project_id, - format_columns.DictColumn(fake_server_group.rules), - fake_server_group.user_id, - ) + def setUp(self): + super().setUp() + + self.fake_server_group = sdk_fakes.generate_fake_resource( + _server_group.ServerGroup + ) + + self.columns = ( + 'id', + 'members', + 'name', + 'policy', + 'project_id', + 'rules', + 'user_id', + ) + self.data = ( + self.fake_server_group.id, + format_columns.ListColumn(self.fake_server_group.member_ids), + self.fake_server_group.name, + self.fake_server_group.policy, + self.fake_server_group.project_id, + format_columns.DictColumn(self.fake_server_group.rules), + self.fake_server_group.user_id, + ) class TestServerGroupCreate(TestServerGroup): @@ -262,82 +268,6 @@ def test_server_group_multiple_delete_with_exception(self): class TestServerGroupList(TestServerGroup): - list_columns = ( - 'ID', - 'Name', - 'Policies', - ) - - list_columns_long = ( - 'ID', - 'Name', - 'Policies', - 'Members', - 'Project Id', - 'User Id', - ) - - list_columns_v264 = ( - 'ID', - 'Name', - 'Policy', - ) - - list_columns_v264_long = ( - 'ID', - 'Name', - 'Policy', - 'Members', - 'Project Id', - 'User Id', - ) - - list_data = ( - ( - TestServerGroup.fake_server_group.id, - TestServerGroup.fake_server_group.name, - format_columns.ListColumn( - TestServerGroup.fake_server_group.policies - ), - ), - ) - - list_data_long = ( - ( - TestServerGroup.fake_server_group.id, - TestServerGroup.fake_server_group.name, - format_columns.ListColumn( - TestServerGroup.fake_server_group.policies - ), - format_columns.ListColumn( - TestServerGroup.fake_server_group.member_ids - ), - TestServerGroup.fake_server_group.project_id, - TestServerGroup.fake_server_group.user_id, - ), - ) - - list_data_v264 = ( - ( - TestServerGroup.fake_server_group.id, - TestServerGroup.fake_server_group.name, - TestServerGroup.fake_server_group.policy, - ), - ) - - list_data_v264_long = ( - ( - TestServerGroup.fake_server_group.id, - TestServerGroup.fake_server_group.name, - TestServerGroup.fake_server_group.policy, - format_columns.ListColumn( - TestServerGroup.fake_server_group.member_ids - ), - TestServerGroup.fake_server_group.project_id, - TestServerGroup.fake_server_group.user_id, - ), - ) - def setUp(self): super().setUp() @@ -359,8 +289,21 @@ def test_server_group_list(self): self.compute_sdk_client.server_groups.assert_called_once_with() - self.assertCountEqual(self.list_columns, columns) - self.assertCountEqual(self.list_data, tuple(data)) + expected_columns = ( + 'ID', + 'Name', + 'Policies', + ) + expected_data = ( + ( + self.fake_server_group.id, + self.fake_server_group.name, + format_columns.ListColumn(self.fake_server_group.policies), + ), + ) + + self.assertCountEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) def test_server_group_list_with_all_projects_and_long(self): arglist = [ @@ -379,8 +322,27 @@ def test_server_group_list_with_all_projects_and_long(self): all_projects=True ) - self.assertCountEqual(self.list_columns_long, columns) - self.assertCountEqual(self.list_data_long, tuple(data)) + expected_columns = ( + 'ID', + 'Name', + 'Policies', + 'Members', + 'Project Id', + 'User Id', + ) + expected_data = ( + ( + self.fake_server_group.id, + self.fake_server_group.name, + format_columns.ListColumn(self.fake_server_group.policies), + format_columns.ListColumn(self.fake_server_group.member_ids), + self.fake_server_group.project_id, + self.fake_server_group.user_id, + ), + ) + + self.assertCountEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) def test_server_group_list_with_limit(self): arglist = [ @@ -428,8 +390,21 @@ def test_server_group_list_v264(self): columns, data = self.cmd.take_action(parsed_args) self.compute_sdk_client.server_groups.assert_called_once_with() - self.assertCountEqual(self.list_columns_v264, columns) - self.assertCountEqual(self.list_data_v264, tuple(data)) + expected_columns = ( + 'ID', + 'Name', + 'Policy', + ) + expected_data = ( + ( + self.fake_server_group.id, + self.fake_server_group.name, + self.fake_server_group.policy, + ), + ) + + self.assertCountEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) def test_server_group_list_with_all_projects_and_long_v264(self): self.set_compute_api_version('2.64') @@ -448,8 +423,27 @@ def test_server_group_list_with_all_projects_and_long_v264(self): all_projects=True ) - self.assertCountEqual(self.list_columns_v264_long, columns) - self.assertCountEqual(self.list_data_v264_long, tuple(data)) + expected_columns = ( + 'ID', + 'Name', + 'Policy', + 'Members', + 'Project Id', + 'User Id', + ) + expected_data = ( + ( + self.fake_server_group.id, + self.fake_server_group.name, + self.fake_server_group.policy, + format_columns.ListColumn(self.fake_server_group.member_ids), + self.fake_server_group.project_id, + self.fake_server_group.user_id, + ), + ) + + self.assertCountEqual(expected_columns, columns) + self.assertCountEqual(expected_data, tuple(data)) class TestServerGroupShow(TestServerGroup): From 32bd5d35626af07458a39da49320b0f3e483f226 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 12 Dec 2024 11:12:54 +0000 Subject: [PATCH 067/245] Adopt sdk_fakes for compute.test_server_volume Change-Id: I5a82ff970ebb3622e6920cdb240a3c1dbc96e27d --- .../tests/unit/compute/v2/test_server_volume.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/test_server_volume.py b/openstackclient/tests/unit/compute/v2/test_server_volume.py index e59853228..d42da9557 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_volume.py +++ b/openstackclient/tests/unit/compute/v2/test_server_volume.py @@ -10,19 +10,26 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack.block_storage.v3 import volume as _volume +from openstack.compute.v2 import server as _server +from openstack.compute.v2 import volume_attachment as _volume_attachment +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.compute.v2 import server_volume from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes class TestServerVolumeList(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() - self.volume_attachments = compute_fakes.create_volume_attachments() + self.server = sdk_fakes.generate_fake_resource(_server.Server) + self.volume_attachments = list( + sdk_fakes.generate_fake_resources( + _volume_attachment.VolumeAttachment, count=2 + ) + ) self.compute_sdk_client.find_server.return_value = self.server self.compute_sdk_client.volume_attachments.return_value = ( @@ -217,10 +224,10 @@ class TestServerVolumeUpdate(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = sdk_fakes.generate_fake_resource(_server.Server) self.compute_sdk_client.find_server.return_value = self.server - self.volume = volume_fakes.create_one_sdk_volume() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) self.volume_sdk_client.find_volume.return_value = self.volume # Get the command object to test From 38407c6a78b6676ade148bbf9760d33152af7133 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 16 Dec 2024 18:16:48 +0000 Subject: [PATCH 068/245] tests: Stop setting attributes on class For some reason we were setting a property mock on the FakeClientManager class. In multiple places, no less. This has a nasty habit of causing side-effects in other tests, depending on the order that tests run in. Resolve this simply setting the attribute as we'd expect. Change-Id: I8bf9055e3f5b885dd5a7a6d751b774934da4a7d7 Signed-off-by: Stephen Finucane --- .../tests/unit/identity/v2_0/test_catalog.py | 9 +++---- .../tests/unit/identity/v2_0/test_role.py | 3 +-- .../identity/v2_0/test_role_assignment.py | 3 ++- .../tests/unit/identity/v2_0/test_token.py | 26 +++++-------------- .../tests/unit/identity/v3/test_catalog.py | 7 ++--- .../tests/unit/identity/v3/test_project.py | 4 +-- .../unit/identity/v3/test_role_assignment.py | 3 ++- .../tests/unit/identity/v3/test_token.py | 25 ++++-------------- .../tests/unit/identity/v3/test_trust.py | 3 ++- 9 files changed, 24 insertions(+), 59 deletions(-) diff --git a/openstackclient/tests/unit/identity/v2_0/test_catalog.py b/openstackclient/tests/unit/identity/v2_0/test_catalog.py index 63b3d475d..ef8b1cf9a 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_catalog.py +++ b/openstackclient/tests/unit/identity/v2_0/test_catalog.py @@ -53,8 +53,7 @@ def test_catalog_list(self): identity_fakes.TOKEN, fake_service=self.service_catalog, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -99,8 +98,7 @@ def test_catalog_list_with_endpoint_url(self): identity_fakes.TOKEN, fake_service=service_catalog, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -136,8 +134,7 @@ def test_catalog_show(self): identity_fakes.UNSCOPED_TOKEN, fake_service=self.service_catalog, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [ 'compute', diff --git a/openstackclient/tests/unit/identity/v2_0/test_role.py b/openstackclient/tests/unit/identity/v2_0/test_role.py index 830653b0f..117d2f001 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_role.py +++ b/openstackclient/tests/unit/identity/v2_0/test_role.py @@ -58,8 +58,7 @@ def setUp(self): identity_fakes.TOKEN, fake_service=self.fake_service, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref class TestRoleAdd(TestRole): diff --git a/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py b/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py index a06c270f0..741b3ee19 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py +++ b/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py @@ -169,7 +169,8 @@ def test_role_assignment_list_project_and_user(self): self.assertEqual(datalist, tuple(data)) def test_role_assignment_list_def_creds(self): - auth_ref = self.app.client_manager.auth_ref = mock.Mock() + self.app.client_manager.auth_ref = mock.Mock() + auth_ref = self.app.client_manager.auth_ref auth_ref.project_id.return_value = identity_fakes.project_id auth_ref.user_id.return_value = identity_fakes.user_id diff --git a/openstackclient/tests/unit/identity/v2_0/test_token.py b/openstackclient/tests/unit/identity/v2_0/test_token.py index 1e90104d9..56a5a2c45 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_token.py +++ b/openstackclient/tests/unit/identity/v2_0/test_token.py @@ -11,29 +11,17 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# - -from unittest import mock from openstackclient.identity.v2_0 import token from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes -class TestToken(identity_fakes.TestIdentityv2): - fake_user = identity_fakes.FakeUser.create_one_user() - fake_project = identity_fakes.FakeProject.create_one_project() - +class TestTokenIssue(identity_fakes.TestIdentityv2): def setUp(self): super().setUp() - # Get a shortcut to the Auth Ref Mock - self.ar_mock = mock.PropertyMock() - type(self.app.client_manager).auth_ref = self.ar_mock - - -class TestTokenIssue(TestToken): - def setUp(self): - super().setUp() + self.fake_user = identity_fakes.FakeUser.create_one_user() + self.fake_project = identity_fakes.FakeProject.create_one_project() self.cmd = token.IssueToken(self.app, None) @@ -41,8 +29,7 @@ def test_token_issue(self): auth_ref = identity_fakes.fake_auth_ref( identity_fakes.TOKEN, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -67,8 +54,7 @@ def test_token_issue_with_unscoped_token(self): auth_ref = identity_fakes.fake_auth_ref( identity_fakes.UNSCOPED_TOKEN, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -91,7 +77,7 @@ def test_token_issue_with_unscoped_token(self): self.assertEqual(datalist, data) -class TestTokenRevoke(TestToken): +class TestTokenRevoke(identity_fakes.TestIdentityv2): TOKEN = 'fob' def setUp(self): diff --git a/openstackclient/tests/unit/identity/v3/test_catalog.py b/openstackclient/tests/unit/identity/v3/test_catalog.py index ed2db3b4d..df292b168 100644 --- a/openstackclient/tests/unit/identity/v3/test_catalog.py +++ b/openstackclient/tests/unit/identity/v3/test_catalog.py @@ -9,7 +9,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# from unittest import mock @@ -72,8 +71,7 @@ def test_catalog_list(self): identity_fakes.TOKEN_WITH_PROJECT_ID, fake_service=self.fake_service, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -110,8 +108,7 @@ def test_catalog_show(self): identity_fakes.TOKEN_WITH_PROJECT_ID, fake_service=self.fake_service, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [ 'compute', diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py index 9b7fc8cea..9085498e3 100644 --- a/openstackclient/tests/unit/identity/v3/test_project.py +++ b/openstackclient/tests/unit/identity/v3/test_project.py @@ -11,7 +11,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# from unittest import mock from unittest.mock import call @@ -914,8 +913,7 @@ def test_project_list_my_projects(self): auth_ref = identity_fakes.fake_auth_ref( identity_fakes.TOKEN_WITH_PROJECT_ID, ) - ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [ '--my-projects', diff --git a/openstackclient/tests/unit/identity/v3/test_role_assignment.py b/openstackclient/tests/unit/identity/v3/test_role_assignment.py index 65dad0d77..6fc66469c 100644 --- a/openstackclient/tests/unit/identity/v3/test_role_assignment.py +++ b/openstackclient/tests/unit/identity/v3/test_role_assignment.py @@ -708,7 +708,8 @@ def test_role_assignment_list_project_with_domain(self): self.assertEqual(datalist, tuple(data)) def test_role_assignment_list_def_creds(self): - auth_ref = self.app.client_manager.auth_ref = mock.Mock() + self.app.client_manager.auth_ref = mock.Mock() + auth_ref = self.app.client_manager.auth_ref auth_ref.project_id.return_value = self.project.id auth_ref.user_id.return_value = self.user.id diff --git a/openstackclient/tests/unit/identity/v3/test_token.py b/openstackclient/tests/unit/identity/v3/test_token.py index 96375ea35..f8d09b72c 100644 --- a/openstackclient/tests/unit/identity/v3/test_token.py +++ b/openstackclient/tests/unit/identity/v3/test_token.py @@ -11,24 +11,12 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# - -from unittest import mock from openstackclient.identity.v3 import token from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestToken(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - # Get a shortcut to the Auth Ref Mock - self.ar_mock = mock.PropertyMock() - type(self.app.client_manager).auth_ref = self.ar_mock - - -class TestTokenIssue(TestToken): +class TestTokenIssue(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() @@ -38,8 +26,7 @@ def test_token_issue_with_project_id(self): auth_ref = identity_fakes.fake_auth_ref( identity_fakes.TOKEN_WITH_PROJECT_ID, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -64,8 +51,7 @@ def test_token_issue_with_domain_id(self): auth_ref = identity_fakes.fake_auth_ref( identity_fakes.TOKEN_WITH_DOMAIN_ID, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -90,8 +76,7 @@ def test_token_issue_with_unscoped(self): auth_ref = identity_fakes.fake_auth_ref( identity_fakes.UNSCOPED_TOKEN, ) - self.ar_mock = mock.PropertyMock(return_value=auth_ref) - type(self.app.client_manager).auth_ref = self.ar_mock + self.app.client_manager.auth_ref = auth_ref arglist = [] verifylist = [] @@ -114,7 +99,7 @@ def test_token_issue_with_unscoped(self): self.assertEqual(datalist, data) -class TestTokenRevoke(TestToken): +class TestTokenRevoke(identity_fakes.TestIdentityv3): TOKEN = 'fob' def setUp(self): diff --git a/openstackclient/tests/unit/identity/v3/test_trust.py b/openstackclient/tests/unit/identity/v3/test_trust.py index fb7ac9b0c..d3a2fa371 100644 --- a/openstackclient/tests/unit/identity/v3/test_trust.py +++ b/openstackclient/tests/unit/identity/v3/test_trust.py @@ -236,7 +236,8 @@ def test_trust_list_no_options(self): self.assertEqual(datalist, tuple(data)) def test_trust_list_auth_user(self): - auth_ref = self.app.client_manager.auth_ref = mock.Mock() + self.app.client_manager.auth_ref = mock.Mock() + auth_ref = self.app.client_manager.auth_ref auth_ref.user_id.return_value = identity_fakes.user_id arglist = ['--auth-user'] From 769bf87d0a2a305d9ce53c91931f1c7016848053 Mon Sep 17 00:00:00 2001 From: Antonia Gaete Date: Mon, 7 Oct 2024 18:37:37 +0000 Subject: [PATCH 069/245] identity: Migrate 'trust' commands to SDK Change-Id: Idb1fda3428ccf3022ee03c8fb7e42c7121683181 --- openstackclient/identity/v3/trust.py | 189 +++++++----- .../tests/unit/identity/v3/test_trust.py | 289 ++++++++---------- ...migrate-trust-to-sdk-9397c9cfddcb636a.yaml | 9 + 3 files changed, 258 insertions(+), 229 deletions(-) create mode 100644 releasenotes/notes/migrate-trust-to-sdk-9397c9cfddcb636a.yaml diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py index 33fee4a90..447a57f2b 100644 --- a/openstackclient/identity/v3/trust.py +++ b/openstackclient/identity/v3/trust.py @@ -14,9 +14,10 @@ """Identity v3 Trust action implementations""" import datetime +import itertools import logging -from keystoneclient import exceptions as identity_exc +from openstack import exceptions as sdk_exceptions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -28,6 +29,25 @@ LOG = logging.getLogger(__name__) +def _format_trust(trust): + columns = ( + 'expires_at', + 'id', + 'is_impersonation', + 'project_id', + 'redelegated_trust_id', + 'redelegation_count', + 'remaining_uses', + 'roles', + 'trustee_user_id', + 'trustor_user_id', + ) + return ( + columns, + utils.get_item_properties(trust, columns), + ) + + class CreateTrust(command.ShowOne): _description = _("Create new trust") @@ -52,6 +72,7 @@ def get_parser(self, prog_name): parser.add_argument( '--role', metavar='', + dest='roles', action='append', default=[], help=_( @@ -62,7 +83,7 @@ def get_parser(self, prog_name): ) parser.add_argument( '--impersonate', - dest='impersonate', + dest='is_impersonation', action='store_true', default=False, help=_( @@ -92,58 +113,60 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity + + kwargs = {} # NOTE(stevemar): Find the two users, project and roles that # are necessary for making a trust usable, the API dictates that # trustee, project and role are optional, but that makes the trust # pointless, and trusts are immutable, so let's enforce it at the # client level. - trustor_id = common.find_user( - identity_client, parsed_args.trustor, parsed_args.trustor_domain - ).id - trustee_id = common.find_user( - identity_client, parsed_args.trustee, parsed_args.trustee_domain - ).id - project_id = common.find_project( - identity_client, parsed_args.project, parsed_args.project_domain - ).id + try: + trustor_id = identity_client.find_user( + parsed_args.trustor, parsed_args.trustor_domain + ).id + kwargs['trustor_id'] = trustor_id + except sdk_exceptions.ForbiddenException: + kwargs['trustor_id'] = parsed_args.trustor + + try: + trustee_id = identity_client.find_user( + parsed_args.trustee, parsed_args.trustee_domain + ).id + kwargs['trustee_id'] = trustee_id + except sdk_exceptions.ForbiddenException: + kwargs['trustee_id'] = parsed_args.trustee + + try: + project_id = identity_client.find_project( + parsed_args.project, parsed_args.project_domain + ).id + kwargs['project_id'] = project_id + except sdk_exceptions.ForbiddenException: + kwargs['project_id'] = parsed_args.project role_ids = [] - for role in parsed_args.role: + for role in parsed_args.roles: try: - role_id = utils.find_resource( - identity_client.roles, - role, - ).id - except identity_exc.Forbidden: + role_id = identity_client.find_role(role).id + except sdk_exceptions.ForbiddenException: role_id = role role_ids.append(role_id) + kwargs['roles'] = role_ids - expires_at = None if parsed_args.expiration: expires_at = datetime.datetime.strptime( parsed_args.expiration, '%Y-%m-%dT%H:%M:%S' ) + kwargs['expires_at'] = expires_at - trust = identity_client.trusts.create( - trustee_id, - trustor_id, - impersonation=parsed_args.impersonate, - project=project_id, - role_ids=role_ids, - expires_at=expires_at, - ) - - trust._info.pop('roles_links', None) - trust._info.pop('links', None) + if parsed_args.is_impersonation: + kwargs['is_impersonation'] = parsed_args.is_impersonation - # Format roles into something sensible - roles = trust._info.pop('roles') - msg = ' '.join(r['name'] for r in roles) - trust._info['roles'] = msg + trust = identity_client.create_trust(**kwargs) - return zip(*sorted(trust._info.items())) + return _format_trust(trust) class DeleteTrust(command.Command): @@ -160,13 +183,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity errors = 0 for trust in parsed_args.trust: try: - trust_obj = utils.find_resource(identity_client.trusts, trust) - identity_client.trusts.delete(trust_obj.id) + trust_obj = identity_client.find_trust( + trust, ignore_missing=False + ) + identity_client.delete_trust(trust_obj.id) except Exception as e: errors += 1 LOG.error( @@ -220,7 +245,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity auth_ref = self.app.client_manager.auth_ref if parsed_args.authuser and any( @@ -243,38 +268,50 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) if parsed_args.authuser: - if auth_ref: - user = common.find_user(identity_client, auth_ref.user_id) - # We need two calls here as we want trusts with - # either the trustor or the trustee set to current user - # using a single call would give us trusts with both - # trustee and trustor set to current user - data1 = identity_client.trusts.list(trustor_user=user) - data2 = identity_client.trusts.list(trustee_user=user) - data = set(data1 + data2) + # We need two calls here as we want trusts with + # either the trustor or the trustee set to current user + # using a single call would give us trusts with both + # trustee and trustor set to current user + data = list( + { + x.id: x + for x in itertools.chain( + identity_client.trusts( + trustor_user_id=auth_ref.user_id + ), + identity_client.trusts( + trustee_user_id=auth_ref.user_id + ), + ) + }.values() + ) else: trustor = None if parsed_args.trustor: - trustor = common.find_user( - identity_client, - parsed_args.trustor, - parsed_args.trustor_domain, - ) + try: + trustor_id = identity_client.find_user( + parsed_args.trustor, parsed_args.trustor_domain + ).id + trustor = trustor_id + except sdk_exceptions.ForbiddenException: + trustor = parsed_args.trustor trustee = None if parsed_args.trustee: - trustee = common.find_user( - identity_client, - parsed_args.trustor, - parsed_args.trustor_domain, - ) - - data = self.app.client_manager.identity.trusts.list( - trustor_user=trustor, - trustee_user=trustee, + try: + trustee_id = identity_client.find_user( + parsed_args.trustee, parsed_args.trustee_domain + ).id + trustee = trustee_id + except sdk_exceptions.ForbiddenException: + trustee = parsed_args.trustee + + data = identity_client.trusts( + trustor_user_id=trustor, + trustee_user_id=trustee, ) - columns = ( + column_headers = ( 'ID', 'Expires At', 'Impersonation', @@ -282,9 +319,17 @@ def take_action(self, parsed_args): 'Trustee User ID', 'Trustor User ID', ) + columns = ( + 'id', + 'expires_at', + 'is_impersonation', + 'project_id', + 'trustee_user_id', + 'trustor_user_id', + ) return ( - columns, + column_headers, ( utils.get_item_properties( s, @@ -309,15 +354,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - trust = utils.find_resource(identity_client.trusts, parsed_args.trust) - - trust._info.pop('roles_links', None) - trust._info.pop('links', None) - - # Format roles into something sensible - roles = trust._info.pop('roles') - msg = ' '.join(r['name'] for r in roles) - trust._info['roles'] = msg + identity_client = self.app.client_manager.sdk_connection.identity + trust = identity_client.find_trust( + parsed_args.trust, ignore_missing=False + ) - return zip(*sorted(trust._info.items())) + return _format_trust(trust) diff --git a/openstackclient/tests/unit/identity/v3/test_trust.py b/openstackclient/tests/unit/identity/v3/test_trust.py index d3a2fa371..07776fa45 100644 --- a/openstackclient/tests/unit/identity/v3/test_trust.py +++ b/openstackclient/tests/unit/identity/v3/test_trust.py @@ -11,58 +11,36 @@ # under the License. # -import copy from unittest import mock from osc_lib import exceptions -from osc_lib import utils + +from openstack import exceptions as sdk_exceptions +from openstack.identity.v3 import project as _project +from openstack.identity.v3 import role as _role +from openstack.identity.v3 import trust as _trust +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes from openstackclient.identity.v3 import trust -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestTrust(identity_fakes.TestIdentityv3): +class TestTrustCreate(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.trusts_mock = self.identity_client.trusts - self.trusts_mock.reset_mock() - self.projects_mock = self.identity_client.projects - self.projects_mock.reset_mock() - self.users_mock = self.identity_client.users - self.users_mock.reset_mock() - self.roles_mock = self.identity_client.roles - self.roles_mock.reset_mock() - + self.trust = sdk_fakes.generate_fake_resource(_trust.Trust) + self.identity_sdk_client.create_trust.return_value = self.trust -class TestTrustCreate(TestTrust): - def setUp(self): - super().setUp() + self.project = sdk_fakes.generate_fake_resource(_project.Project) + self.identity_sdk_client.find_project.return_value = self.project - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) + self.user = sdk_fakes.generate_fake_resource(_user.User) + self.identity_sdk_client.find_user.return_value = self.user - self.users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) - - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, - ) - - self.trusts_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.TRUST), - loaded=True, - ) + self.role = sdk_fakes.generate_fake_resource(_role.Role) + self.identity_sdk_client.find_role.return_value = self.role # Get the command object to test self.cmd = trust.CreateTrust(self.app, None) @@ -70,18 +48,17 @@ def setUp(self): def test_trust_create_basic(self): arglist = [ '--project', - identity_fakes.project_id, + self.project.id, '--role', - identity_fakes.role_id, - identity_fakes.user_id, - identity_fakes.user_id, + self.role.id, + self.user.id, + self.user.id, ] verifylist = [ - ('project', identity_fakes.project_id), - ('impersonate', False), - ('role', [identity_fakes.role_id]), - ('trustor', identity_fakes.user_id), - ('trustee', identity_fakes.user_id), + ('project', self.project.id), + ('roles', [self.role.id]), + ('trustor', self.user.id), + ('trustee', self.user.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -92,76 +69,71 @@ def test_trust_create_basic(self): # Set expected values kwargs = { - 'impersonation': False, - 'project': identity_fakes.project_id, - 'role_ids': [identity_fakes.role_id], - 'expires_at': None, + 'project_id': self.project.id, + 'roles': [self.role.id], } # TrustManager.create(trustee_id, trustor_id, impersonation=, # project=, role_names=, expires_at=) - self.trusts_mock.create.assert_called_with( - identity_fakes.user_id, identity_fakes.user_id, **kwargs + self.identity_sdk_client.create_trust.assert_called_with( + trustor_id=self.user.id, trustee_id=self.user.id, **kwargs ) collist = ( 'expires_at', 'id', - 'impersonation', + 'is_impersonation', 'project_id', + 'redelegated_trust_id', + 'redelegation_count', + 'remaining_uses', 'roles', 'trustee_user_id', 'trustor_user_id', ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.trust_expires, - identity_fakes.trust_id, - identity_fakes.trust_impersonation, - identity_fakes.project_id, - identity_fakes.role_name, - identity_fakes.user_id, - identity_fakes.user_id, + self.trust.expires_at, + self.trust.id, + self.trust.is_impersonation, + self.trust.project_id, + self.trust.redelegated_trust_id, + self.trust.redelegation_count, + self.trust.remaining_uses, + self.trust.roles, + self.trust.trustee_user_id, + self.trust.trustor_user_id, ) self.assertEqual(datalist, data) -class TestTrustDelete(TestTrust): +class TestTrustDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - # This is the return value for utils.find_resource() - self.trusts_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.TRUST), - loaded=True, - ) - self.trusts_mock.delete.return_value = None + self.trust = sdk_fakes.generate_fake_resource(_trust.Trust) + self.identity_sdk_client.delete_trust.return_value = None + self.identity_sdk_client.find_trust.return_value = self.trust # Get the command object to test self.cmd = trust.DeleteTrust(self.app, None) def test_trust_delete(self): arglist = [ - identity_fakes.trust_id, + self.trust.id, ] - verifylist = [('trust', [identity_fakes.trust_id])] + verifylist = [('trust', [self.trust.id])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.trusts_mock.delete.assert_called_with( - identity_fakes.trust_id, + self.identity_sdk_client.delete_trust.assert_called_with( + self.trust.id, ) self.assertIsNone(result) - @mock.patch.object(utils, 'find_resource') - def test_delete_multi_trusts_with_exception(self, find_mock): - find_mock.side_effect = [ - self.trusts_mock.get.return_value, - exceptions.CommandError, - ] + def test_delete_multi_trusts_with_exception(self): arglist = [ - identity_fakes.trust_id, + self.trust.id, 'unexist_trust', ] verifylist = [ @@ -169,32 +141,37 @@ def test_delete_multi_trusts_with_exception(self, find_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.identity_sdk_client.find_trust.side_effect = [ + self.trust, + sdk_exceptions.ResourceNotFound, + ] + try: self.cmd.take_action(parsed_args) self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual('1 of 2 trusts failed to delete.', str(e)) - find_mock.assert_any_call(self.trusts_mock, identity_fakes.trust_id) - find_mock.assert_any_call(self.trusts_mock, 'unexist_trust') - - self.assertEqual(2, find_mock.call_count) - self.trusts_mock.delete.assert_called_once_with( - identity_fakes.trust_id + self.identity_sdk_client.find_trust.assert_has_calls( + [ + mock.call(self.trust.id, ignore_missing=False), + mock.call('unexist_trust', ignore_missing=False), + ] + ) + self.identity_sdk_client.delete_trust.assert_called_once_with( + self.trust.id ) -class TestTrustList(TestTrust): +class TestTrustList(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.trusts_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.TRUST), - loaded=True, - ), - ] + self.trust = sdk_fakes.generate_fake_resource(_trust.Trust) + self.identity_sdk_client.trusts.return_value = [self.trust] + + self.user = sdk_fakes.generate_fake_resource(_user.User) + self.identity_sdk_client.find_user.return_value = self.user # Get the command object to test self.cmd = trust.ListTrust(self.app, None) @@ -209,9 +186,9 @@ def test_trust_list_no_options(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.trusts_mock.list.assert_called_with( - trustor_user=None, - trustee_user=None, + self.identity_sdk_client.trusts.assert_called_with( + trustor_user_id=None, + trustee_user_id=None, ) collist = ( @@ -225,12 +202,12 @@ def test_trust_list_no_options(self): self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.trust_id, - identity_fakes.trust_expires, - identity_fakes.trust_impersonation, - identity_fakes.project_id, - identity_fakes.user_id, - identity_fakes.user_id, + self.trust.id, + self.trust.expires_at, + self.trust.is_impersonation, + self.trust.project_id, + self.trust.trustee_user_id, + self.trust.trustor_user_id, ), ) self.assertEqual(datalist, tuple(data)) @@ -238,7 +215,6 @@ def test_trust_list_no_options(self): def test_trust_list_auth_user(self): self.app.client_manager.auth_ref = mock.Mock() auth_ref = self.app.client_manager.auth_ref - auth_ref.user_id.return_value = identity_fakes.user_id arglist = ['--auth-user'] verifylist = [ @@ -253,11 +229,11 @@ def test_trust_list_auth_user(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.trusts_mock.list.assert_any_call( - trustor_user=self.users_mock.get() - ) - self.trusts_mock.list.assert_any_call( - trustee_user=self.users_mock.get() + self.identity_sdk_client.trusts.assert_has_calls( + [ + mock.call(trustor_user_id=auth_ref.user_id), + mock.call(trustee_user_id=auth_ref.user_id), + ] ) collist = ( @@ -271,21 +247,21 @@ def test_trust_list_auth_user(self): self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.trust_id, - identity_fakes.trust_expires, - identity_fakes.trust_impersonation, - identity_fakes.project_id, - identity_fakes.user_id, - identity_fakes.user_id, + self.trust.id, + self.trust.expires_at, + self.trust.is_impersonation, + self.trust.project_id, + self.trust.trustee_user_id, + self.trust.trustor_user_id, ), ) self.assertEqual(datalist, tuple(data)) def test_trust_list_trustee(self): - arglist = ['--trustee', identity_fakes.user_name] + arglist = ['--trustee', self.user.name] verifylist = [ ('trustor', None), - ('trustee', identity_fakes.user_name), + ('trustee', self.user.name), ('authuser', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -295,9 +271,9 @@ def test_trust_list_trustee(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.trusts_mock.list.assert_any_call( - trustee_user=self.users_mock.get(), - trustor_user=None, + self.identity_sdk_client.trusts.assert_called_with( + trustee_user_id=self.user.id, + trustor_user_id=None, ) collist = ( @@ -311,21 +287,21 @@ def test_trust_list_trustee(self): self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.trust_id, - identity_fakes.trust_expires, - identity_fakes.trust_impersonation, - identity_fakes.project_id, - identity_fakes.user_id, - identity_fakes.user_id, + self.trust.id, + self.trust.expires_at, + self.trust.is_impersonation, + self.trust.project_id, + self.trust.trustee_user_id, + self.trust.trustor_user_id, ), ) self.assertEqual(datalist, tuple(data)) def test_trust_list_trustor(self): - arglist = ['--trustor', identity_fakes.user_name] + arglist = ['--trustor', self.user.name] verifylist = [ ('trustee', None), - ('trustor', identity_fakes.user_name), + ('trustor', self.user.name), ('authuser', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -335,9 +311,9 @@ def test_trust_list_trustor(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.trusts_mock.list.assert_any_call( - trustor_user=self.users_mock.get(), - trustee_user=None, + self.identity_sdk_client.trusts.assert_called_once_with( + trustor_user_id=self.user.id, + trustee_user_id=None, ) collist = ( @@ -351,36 +327,33 @@ def test_trust_list_trustor(self): self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.trust_id, - identity_fakes.trust_expires, - identity_fakes.trust_impersonation, - identity_fakes.project_id, - identity_fakes.user_id, - identity_fakes.user_id, + self.trust.id, + self.trust.expires_at, + self.trust.is_impersonation, + self.trust.project_id, + self.trust.trustee_user_id, + self.trust.trustor_user_id, ), ) self.assertEqual(datalist, tuple(data)) -class TestTrustShow(TestTrust): +class TestTrustShow(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.trusts_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.TRUST), - loaded=True, - ) + self.trust = sdk_fakes.generate_fake_resource(_trust.Trust) + self.identity_sdk_client.find_trust.return_value = self.trust # Get the command object to test self.cmd = trust.ShowTrust(self.app, None) def test_trust_show(self): arglist = [ - identity_fakes.trust_id, + self.trust.id, ] verifylist = [ - ('trust', identity_fakes.trust_id), + ('trust', self.trust.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -389,25 +362,33 @@ def test_trust_show(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.trusts_mock.get.assert_called_with(identity_fakes.trust_id) + self.identity_sdk_client.find_trust.assert_called_with( + self.trust.id, ignore_missing=False + ) collist = ( 'expires_at', 'id', - 'impersonation', + 'is_impersonation', 'project_id', + 'redelegated_trust_id', + 'redelegation_count', + 'remaining_uses', 'roles', 'trustee_user_id', 'trustor_user_id', ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.trust_expires, - identity_fakes.trust_id, - identity_fakes.trust_impersonation, - identity_fakes.project_id, - identity_fakes.role_name, - identity_fakes.user_id, - identity_fakes.user_id, + self.trust.expires_at, + self.trust.id, + self.trust.is_impersonation, + self.trust.project_id, + self.trust.redelegated_trust_id, + self.trust.redelegation_count, + self.trust.remaining_uses, + self.trust.roles, + self.trust.trustee_user_id, + self.trust.trustor_user_id, ) self.assertEqual(datalist, data) diff --git a/releasenotes/notes/migrate-trust-to-sdk-9397c9cfddcb636a.yaml b/releasenotes/notes/migrate-trust-to-sdk-9397c9cfddcb636a.yaml new file mode 100644 index 000000000..e45218f31 --- /dev/null +++ b/releasenotes/notes/migrate-trust-to-sdk-9397c9cfddcb636a.yaml @@ -0,0 +1,9 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``trust create`` + - ``trust list`` + - ``trust delete`` + - ``trust show`` From 03e2fdd1622d79b0199e349be042991f927f0414 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Tue, 8 Oct 2024 09:26:51 +0000 Subject: [PATCH 070/245] Fix: Volume backup restore output Currently the volume backup restore command returns with error even though the restore is initiated. This patch corrects the response received from SDK and processes it in a human readable form. Change-Id: I7f020631fbb39ceef8740775fd82686d90a6c703 Closes-Bug: #2063335 Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/931755 --- .../unit/volume/v2/test_volume_backup.py | 40 ++++++++++++++----- .../unit/volume/v3/test_volume_backup.py | 40 ++++++++++++++----- openstackclient/volume/v2/volume_backup.py | 11 ++++- openstackclient/volume/v3/volume_backup.py | 11 ++++- .../fix-restore-resp-e664a643a723cd2e.yaml | 4 ++ 5 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 releasenotes/notes/fix-restore-resp-e664a643a723cd2e.yaml diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index 483ca24de..6f5c4ab69 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -364,16 +364,28 @@ class TestBackupRestore(volume_fakes.TestVolume): attrs={'volume_id': volume.id}, ) + columns = ( + "id", + "volume_id", + "volume_name", + ) + + data = ( + backup.id, + volume.id, + volume.name, + ) + def setUp(self): super().setUp() self.volume_sdk_client.find_backup.return_value = self.backup self.volume_sdk_client.find_volume.return_value = self.volume - self.volume_sdk_client.restore_backup.return_value = ( - volume_fakes.create_one_volume( - {'id': self.volume['id']}, - ) - ) + self.volume_sdk_client.restore_backup.return_value = { + 'id': self.backup['id'], + 'volume_id': self.volume['id'], + 'volume_name': self.volume['name'], + } # Get the command object to mock self.cmd = volume_backup.RestoreVolumeBackup(self.app, None) @@ -389,13 +401,15 @@ def test_backup_restore(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=None, name=None, ) - self.assertIsNotNone(result) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) def test_backup_restore_with_volume(self): self.volume_sdk_client.find_volume.side_effect = ( @@ -411,13 +425,15 @@ def test_backup_restore_with_volume(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=None, name=self.backup.volume_id, ) - self.assertIsNotNone(result) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) def test_backup_restore_with_volume_force(self): arglist = [ @@ -432,13 +448,15 @@ def test_backup_restore_with_volume_force(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=self.volume.id, name=None, ) - self.assertIsNotNone(result) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) def test_backup_restore_with_volume_existing(self): arglist = [ diff --git a/openstackclient/tests/unit/volume/v3/test_volume_backup.py b/openstackclient/tests/unit/volume/v3/test_volume_backup.py index 864f16241..857c93046 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_backup.py @@ -462,16 +462,28 @@ class TestBackupRestore(volume_fakes.TestVolume): attrs={'volume_id': volume.id}, ) + columns = ( + "id", + "volume_id", + "volume_name", + ) + + data = ( + backup.id, + volume.id, + volume.name, + ) + def setUp(self): super().setUp() self.volume_sdk_client.find_backup.return_value = self.backup self.volume_sdk_client.find_volume.return_value = self.volume - self.volume_sdk_client.restore_backup.return_value = ( - volume_fakes.create_one_volume( - {'id': self.volume['id']}, - ) - ) + self.volume_sdk_client.restore_backup.return_value = { + 'id': self.backup['id'], + 'volume_id': self.volume['id'], + 'volume_name': self.volume['name'], + } # Get the command object to mock self.cmd = volume_backup.RestoreVolumeBackup(self.app, None) @@ -487,13 +499,15 @@ def test_backup_restore(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=None, name=None, ) - self.assertIsNotNone(result) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) def test_backup_restore_with_volume(self): self.volume_sdk_client.find_volume.side_effect = ( @@ -509,13 +523,15 @@ def test_backup_restore_with_volume(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=None, name=self.backup.volume_id, ) - self.assertIsNotNone(result) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) def test_backup_restore_with_volume_force(self): arglist = [ @@ -530,13 +546,15 @@ def test_backup_restore_with_volume_force(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=self.volume.id, name=None, ) - self.assertIsNotNone(result) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) def test_backup_restore_with_volume_existing(self): arglist = [ diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index fd59c0cef..bbcd35180 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -359,6 +359,12 @@ def take_action(self, parsed_args): ignore_missing=False, ) + columns = ( + 'id', + 'volume_id', + 'volume_name', + ) + volume_name = None volume_id = None try: @@ -378,12 +384,15 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg % parsed_args.volume) - return volume_client.restore_backup( + restore = volume_client.restore_backup( backup.id, volume_id=volume_id, name=volume_name, ) + data = utils.get_dict_properties(restore, columns) + return (columns, data) + class SetVolumeBackup(command.Command): _description = _("Set volume backup properties") diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py index 45d50b843..4a7dabd39 100644 --- a/openstackclient/volume/v3/volume_backup.py +++ b/openstackclient/volume/v3/volume_backup.py @@ -412,6 +412,12 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume + columns = ( + 'id', + 'volume_id', + 'volume_name', + ) + backup = volume_client.find_backup( parsed_args.backup, ignore_missing=False, @@ -436,12 +442,15 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg % parsed_args.volume) - return volume_client.restore_backup( + restore = volume_client.restore_backup( backup.id, volume_id=volume_id, name=volume_name, ) + data = utils.get_dict_properties(restore, columns) + return (columns, data) + class SetVolumeBackup(command.Command): _description = _("Set volume backup properties") diff --git a/releasenotes/notes/fix-restore-resp-e664a643a723cd2e.yaml b/releasenotes/notes/fix-restore-resp-e664a643a723cd2e.yaml new file mode 100644 index 000000000..2ee8f216d --- /dev/null +++ b/releasenotes/notes/fix-restore-resp-e664a643a723cd2e.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - | + Fixed the output of ``volume backup restore`` command. From 2a0431e8255671824b9f938de94715f36bed48ba Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Wed, 7 Aug 2024 14:55:28 +0000 Subject: [PATCH 071/245] Add the trunk subports information to the port list command Added the subports information to the port list command, when the "--long" qualifier is specified. Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/926609 Closes-Bug: #2074187 Change-Id: I8ef66c3415279caf0ebea4ba6232ca3696188de9 --- openstackclient/network/v2/port.py | 29 +++++++++++- .../tests/unit/network/v2/fakes.py | 1 + .../tests/unit/network/v2/test_port.py | 44 +++++++++++++++++-- 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index a70788238..478bca840 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -38,6 +38,20 @@ def human_readable(self): return 'UP' if self._value else 'DOWN' +class SubPortColumn(format_columns.ListDictColumn): + def _retrieve_subports(self): + if isinstance(self._value, dict): + self._value = self._value['sub_ports'] + + def human_readable(self): + self._retrieve_subports() + return super().human_readable() + + def machine_readable(self): + self._retrieve_subports() + return super().machine_readable() + + _formatters = { 'admin_state_up': AdminStateColumn, 'is_admin_state_up': AdminStateColumn, @@ -51,6 +65,7 @@ def human_readable(self): 'fixed_ips': format_columns.ListDictColumn, 'security_group_ids': format_columns.ListColumn, 'tags': format_columns.ListColumn, + 'trunk_details': SubPortColumn, } @@ -868,8 +883,18 @@ def take_action(self, parsed_args): filters = {} if parsed_args.long: - columns += ('security_group_ids', 'device_owner', 'tags') - column_headers += ('Security Groups', 'Device Owner', 'Tags') + columns += ( + 'security_group_ids', + 'device_owner', + 'tags', + 'trunk_details', + ) + column_headers += ( + 'Security Groups', + 'Device Owner', + 'Tags', + 'Trunk subports', + ) if parsed_args.device_owner is not None: filters['device_owner'] = parsed_args.device_owner if parsed_args.device_id is not None: diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index 909bfaf47..4a0f9df77 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -1679,6 +1679,7 @@ def create_one_port(attrs=None): 'trusted': None, 'propagate_uplink_status': False, 'location': 'MUNCHMUNCHMUNCH', + 'trunk_details': {}, } # Overwrite default attributes. diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index dcfe64b3b..9cea37dfc 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -12,6 +12,7 @@ from unittest import mock from unittest.mock import call +import uuid from osc_lib.cli import format_columns from osc_lib import exceptions @@ -24,7 +25,12 @@ LIST_FIELDS_TO_RETRIEVE = ('id', 'name', 'mac_address', 'fixed_ips', 'status') -LIST_FIELDS_TO_RETRIEVE_LONG = ('security_group_ids', 'device_owner', 'tags') +LIST_FIELDS_TO_RETRIEVE_LONG = ( + 'security_group_ids', + 'device_owner', + 'tags', + 'trunk_details', +) class TestPort(network_fakes.TestNetworkV2): @@ -116,7 +122,7 @@ def _get_common_cols_data(fake_port): fake_port.status, format_columns.ListColumn(fake_port.tags), fake_port.trusted, - fake_port.trunk_details, + port.SubPortColumn(fake_port.trunk_details), fake_port.updated_at, ) @@ -1236,7 +1242,37 @@ def test_multi_ports_delete_with_exception(self): class TestListPort(compute_fakes.FakeClientMixin, TestPort): - _ports = network_fakes.create_ports(count=3) + _project = identity_fakes.FakeProject.create_one_project() + _networks = network_fakes.create_networks(count=3) + _sport1 = network_fakes.create_one_port( + attrs={'project_id': _project.id, 'network_id': _networks[1]['id']} + ) + _sport2 = network_fakes.create_one_port( + attrs={'project_id': _project.id, 'network_id': _networks[2]['id']} + ) + _trunk_details = { + 'trunk_id': str(uuid.uuid4()), + 'sub_ports': [ + { + 'segmentation_id': 100, + 'segmentation_type': 'vlan', + 'port_id': _sport1.id, + }, + { + 'segmentation_id': 102, + 'segmentation_type': 'vlan', + 'port_id': _sport2.id, + }, + ], + } + _pport = network_fakes.create_one_port( + attrs={ + 'project_id': _project.id, + 'network_id': _networks[0]['id'], + 'trunk_details': _trunk_details, + } + ) + _ports = (_pport, _sport1, _sport2) columns = ( 'ID', @@ -1255,6 +1291,7 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort): 'Security Groups', 'Device Owner', 'Tags', + 'Trunk subports', ) data = [] @@ -1281,6 +1318,7 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort): format_columns.ListColumn(prt.security_group_ids), prt.device_owner, format_columns.ListColumn(prt.tags), + port.SubPortColumn(prt.trunk_details), ) ) From 8f1382eda3db2952f9d31b50c94388116b9a076f Mon Sep 17 00:00:00 2001 From: ArtofBugs Date: Wed, 5 Jun 2024 15:00:59 -0700 Subject: [PATCH 072/245] Identity: Migrate 'role' commands to SDK Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/929578 Change-Id: I22254604705080095ac852a1e03506b1552a1fd2 --- openstackclient/identity/v3/role.py | 404 ++++-- .../tests/functional/identity/v3/test_role.py | 24 + .../identity/v3/test_role_assignment.py | 8 + .../tests/unit/identity/v3/test_role.py | 1282 +++++++++-------- 4 files changed, 998 insertions(+), 720 deletions(-) diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index c43f38994..a7fb6644b 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -17,7 +17,7 @@ import logging -from keystoneauth1 import exceptions as ks_exc +from openstack import exceptions as sdk_exc from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -29,6 +29,25 @@ LOG = logging.getLogger(__name__) +def _format_role(role): + columns = ( + "id", + "name", + "domain_id", + "description", + ) + column_headers = ( + "id", + "name", + "domain_id", + "description", + ) + return ( + column_headers, + utils.get_item_properties(role, columns), + ) + + def _add_identity_and_resource_options_to_parser(parser): system_or_domain_or_project = parser.add_mutually_exclusive_group() system_or_domain_or_project.add_argument( @@ -63,32 +82,79 @@ def _add_identity_and_resource_options_to_parser(parser): common.add_inherited_option_to_parser(parser) +def _find_sdk_id( + find_command, name_or_id, validate_actor_existence=True, **kwargs +): + try: + resource = find_command( + name_or_id=name_or_id, ignore_missing=False, **kwargs + ) + + # Mimic the behavior of + # openstackclient.identity.common._find_identity_resource() + # and ignore if we don't have permission to find a resource. + except sdk_exc.ForbiddenException: + return name_or_id + except sdk_exc.ResourceNotFound as exc: + if not validate_actor_existence: + return name_or_id + raise exceptions.CommandError from exc + return resource.id + + def _process_identity_and_resource_options( - parsed_args, identity_client_manager, validate_actor_existence=True + parsed_args, identity_client, validate_actor_existence=True ): def _find_user(): - try: - return common.find_user( - identity_client_manager, - parsed_args.user, - parsed_args.user_domain, - ).id - except exceptions.CommandError: - if not validate_actor_existence: - return parsed_args.user - raise + domain_id = ( + _find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.user_domain, + validate_actor_existence=validate_actor_existence, + ) + if parsed_args.user_domain + else None + ) + return _find_sdk_id( + identity_client.find_user, + name_or_id=parsed_args.user, + validate_actor_existence=validate_actor_existence, + domain_id=domain_id, + ) def _find_group(): - try: - return common.find_group( - identity_client_manager, - parsed_args.group, - parsed_args.group_domain, - ).id - except exceptions.CommandError: - if not validate_actor_existence: - return parsed_args.group - raise + domain_id = ( + _find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.group_domain, + validate_actor_existence=validate_actor_existence, + ) + if parsed_args.group_domain + else None + ) + return _find_sdk_id( + identity_client.find_group, + name_or_id=parsed_args.group, + validate_actor_existence=validate_actor_existence, + domain_id=domain_id, + ) + + def _find_project(): + domain_id = ( + _find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.project_domain, + validate_actor_existence=validate_actor_existence, + ) + if parsed_args.project_domain + else None + ) + return _find_sdk_id( + identity_client.find_project, + name_or_id=parsed_args.project, + validate_actor_existence=validate_actor_existence, + domain_id=domain_id, + ) kwargs = {} if parsed_args.user and parsed_args.system: @@ -96,34 +162,35 @@ def _find_group(): kwargs['system'] = parsed_args.system elif parsed_args.user and parsed_args.domain: kwargs['user'] = _find_user() - kwargs['domain'] = common.find_domain( - identity_client_manager, - parsed_args.domain, - ).id + kwargs['domain'] = _find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.domain, + validate_actor_existence=validate_actor_existence, + ) elif parsed_args.user and parsed_args.project: kwargs['user'] = _find_user() - kwargs['project'] = common.find_project( - identity_client_manager, - parsed_args.project, - parsed_args.project_domain, - ).id + kwargs['project'] = _find_project() elif parsed_args.group and parsed_args.system: kwargs['group'] = _find_group() kwargs['system'] = parsed_args.system elif parsed_args.group and parsed_args.domain: kwargs['group'] = _find_group() - kwargs['domain'] = common.find_domain( - identity_client_manager, - parsed_args.domain, - ).id + kwargs['domain'] = _find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.domain, + validate_actor_existence=validate_actor_existence, + ) elif parsed_args.group and parsed_args.project: kwargs['group'] = _find_group() - kwargs['project'] = common.find_project( - identity_client_manager, - parsed_args.project, - parsed_args.project_domain, - ).id - kwargs['os_inherit_extension_inherited'] = parsed_args.inherited + kwargs['project'] = _find_project() + else: + msg = _( + "Role not added, incorrect set of arguments " + "provided. See openstack --help for more details" + ) + raise exceptions.CommandError(msg) + + kwargs['inherited'] = parsed_args.inherited return kwargs @@ -145,7 +212,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity if ( not parsed_args.user @@ -161,18 +228,71 @@ def take_action(self, parsed_args): domain_id = None if parsed_args.role_domain: - domain_id = common.find_domain( - identity_client, parsed_args.role_domain - ).id - role = utils.find_resource( - identity_client.roles, parsed_args.role, domain_id=domain_id + domain_id = _find_sdk_id( + identity_client.find_domain, name_or_id=parsed_args.role_domain + ) + role = _find_sdk_id( + identity_client.find_role, + name_or_id=parsed_args.role, + domain_id=domain_id, ) - kwargs = _process_identity_and_resource_options( - parsed_args, self.app.client_manager.identity + add_kwargs = _process_identity_and_resource_options( + parsed_args, identity_client ) - identity_client.roles.grant(role.id, **kwargs) + if add_kwargs.get("domain"): + if add_kwargs.get("user"): + identity_client.assign_domain_role_to_user( + domain=add_kwargs["domain"], + user=add_kwargs["user"], + role=role, + inherited=add_kwargs["inherited"], + ) + if add_kwargs.get("group"): + identity_client.assign_domain_role_to_group( + domain=add_kwargs["domain"], + group=add_kwargs["group"], + role=role, + inherited=add_kwargs["inherited"], + ) + elif add_kwargs.get("project"): + if add_kwargs.get("user"): + identity_client.assign_project_role_to_user( + project=add_kwargs["project"], + user=add_kwargs["user"], + role=role, + inherited=add_kwargs["inherited"], + ) + if add_kwargs.get("group"): + identity_client.assign_project_role_to_group( + project=add_kwargs["project"], + group=add_kwargs["group"], + role=role, + inherited=add_kwargs["inherited"], + ) + elif add_kwargs.get("system"): + if add_kwargs["inherited"]: + LOG.warning( + _( + "'--inherited' was given, which is not supported " + "when adding a system role. This will be an error " + "in a future release." + ) + ) + # TODO(0weng): This should be an error in a future release + if add_kwargs.get("user"): + identity_client.assign_system_role_to_user( + system=add_kwargs["system"], + user=add_kwargs["user"], + role=role, + ) + if add_kwargs.get("group"): + identity_client.assign_system_role_to_group( + system=add_kwargs["system"], + group=add_kwargs["group"], + role=role, + ) class CreateRole(command.ShowOne): @@ -204,37 +324,35 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - domain_id = None + create_kwargs = {} if parsed_args.domain: - domain_id = common.find_domain( - identity_client, parsed_args.domain - ).id + create_kwargs['domain_id'] = _find_sdk_id( + identity_client.find_domain, name_or_id=parsed_args.domain + ) - options = common.get_immutable_options(parsed_args) + if parsed_args.name: + create_kwargs['name'] = parsed_args.name + if parsed_args.description: + create_kwargs['description'] = parsed_args.description + create_kwargs['options'] = common.get_immutable_options(parsed_args) try: - role = identity_client.roles.create( - name=parsed_args.name, - domain=domain_id, - description=parsed_args.description, - options=options, - ) + role = identity_client.create_role(**create_kwargs) - except ks_exc.Conflict: + except sdk_exc.ConflictException: if parsed_args.or_show: - role = utils.find_resource( - identity_client.roles, - parsed_args.name, - domain_id=domain_id, + role = identity_client.find_role( + name_or_id=parsed_args.name, + domain_id=parsed_args.domain, + ignore_missing=False, ) LOG.info(_('Returning existing role %s'), role.name) else: raise - role._info.pop('links') - return zip(*sorted(role._info.items())) + return _format_role(role) class DeleteRole(command.Command): @@ -245,7 +363,7 @@ def get_parser(self, prog_name): parser.add_argument( 'roles', metavar='', - nargs="+", + nargs='+', help=_('Role(s) to delete (name or ID)'), ) parser.add_argument( @@ -256,20 +374,22 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity domain_id = None if parsed_args.domain: - domain_id = common.find_domain( - identity_client, parsed_args.domain - ).id + domain_id = _find_sdk_id( + identity_client.find_domain, parsed_args.domain + ) errors = 0 for role in parsed_args.roles: try: - role_obj = utils.find_resource( - identity_client.roles, role, domain_id=domain_id + role_id = _find_sdk_id( + identity_client.find_role, + name_or_id=role, + domain_id=domain_id, ) - identity_client.roles.delete(role_obj.id) + identity_client.delete_role(role=role_id, ignore_missing=False) except Exception as e: errors += 1 LOG.error( @@ -302,20 +422,17 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity if parsed_args.domain: - domain = common.find_domain( - identity_client, - parsed_args.domain, + domain = identity_client.find_domain( + name_or_id=parsed_args.domain, ) columns = ('ID', 'Name', 'Domain') - data = identity_client.roles.list(domain_id=domain.id) - for role in data: - role.domain = domain.name + data = identity_client.roles(domain_id=domain.id) else: columns = ('ID', 'Name') - data = identity_client.roles.list() + data = identity_client.roles() return ( columns, @@ -323,7 +440,7 @@ def take_action(self, parsed_args): utils.get_item_properties( s, columns, - formatters={}, + formatters={'Domain': lambda _: domain.name}, ) for s in data ), @@ -348,8 +465,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - + identity_client = self.app.client_manager.sdk_connection.identity if ( not parsed_args.user and not parsed_args.domain @@ -364,19 +480,65 @@ def take_action(self, parsed_args): domain_id = None if parsed_args.role_domain: - domain_id = common.find_domain( - identity_client, parsed_args.role_domain - ).id - role = utils.find_resource( - identity_client.roles, parsed_args.role, domain_id=domain_id + domain_id = _find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.role_domain, + ) + role = _find_sdk_id( + identity_client.find_role, + name_or_id=parsed_args.role, + domain_id=domain_id, ) - kwargs = _process_identity_and_resource_options( + remove_kwargs = _process_identity_and_resource_options( parsed_args, - self.app.client_manager.identity, + identity_client, validate_actor_existence=False, ) - identity_client.roles.revoke(role.id, **kwargs) + + if remove_kwargs.get("domain"): + if remove_kwargs.get("user"): + identity_client.unassign_domain_role_from_user( + domain=remove_kwargs["domain"], + user=remove_kwargs["user"], + role=role, + inherited=remove_kwargs["inherited"], + ) + if remove_kwargs.get("group"): + identity_client.unassign_domain_role_from_group( + domain=remove_kwargs["domain"], + group=remove_kwargs["group"], + role=role, + inherited=remove_kwargs["inherited"], + ) + elif remove_kwargs.get("project"): + if remove_kwargs.get("user"): + identity_client.unassign_project_role_from_user( + project=remove_kwargs["project"], + user=remove_kwargs["user"], + role=role, + inherited=remove_kwargs["inherited"], + ) + if remove_kwargs.get("group"): + identity_client.unassign_project_role_from_group( + project=remove_kwargs["project"], + group=remove_kwargs["group"], + role=role, + inherited=remove_kwargs["inherited"], + ) + elif remove_kwargs.get("system"): + if remove_kwargs.get("user"): + identity_client.unassign_system_role_from_user( + system=remove_kwargs["system"], + user=remove_kwargs["user"], + role=role, + ) + if remove_kwargs.get("group"): + identity_client.unassign_system_role_from_group( + system=remove_kwargs["system"], + group=remove_kwargs["group"], + role=role, + ) class SetRole(command.Command): @@ -408,25 +570,31 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity + + update_kwargs = {} + if parsed_args.description: + update_kwargs["description"] = parsed_args.description + if parsed_args.name: + update_kwargs["name"] = parsed_args.name domain_id = None if parsed_args.domain: - domain_id = common.find_domain( - identity_client, parsed_args.domain - ).id + domain_id = _find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.domain, + ) + update_kwargs["domain_id"] = domain_id - options = common.get_immutable_options(parsed_args) - role = utils.find_resource( - identity_client.roles, parsed_args.role, domain_id=domain_id + update_kwargs["options"] = common.get_immutable_options(parsed_args) + role = _find_sdk_id( + identity_client.find_role, + name_or_id=parsed_args.role, + domain_id=domain_id, ) + update_kwargs["role"] = role - identity_client.roles.update( - role.id, - name=parsed_args.name, - description=parsed_args.description, - options=options, - ) + identity_client.update_role(**update_kwargs) class ShowRole(command.ShowOne): @@ -447,17 +615,19 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity domain_id = None if parsed_args.domain: - domain_id = common.find_domain( - identity_client, parsed_args.domain - ).id + domain_id = _find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.domain, + ) - role = utils.find_resource( - identity_client.roles, parsed_args.role, domain_id=domain_id + role = identity_client.find_role( + name_or_id=parsed_args.role, + domain_id=domain_id, + ignore_missing=False, ) - role._info.pop('links') - return zip(*sorted(role._info.items())) + return _format_role(role) diff --git a/openstackclient/tests/functional/identity/v3/test_role.py b/openstackclient/tests/functional/identity/v3/test_role.py index 16ddc23a3..f4c6832f2 100644 --- a/openstackclient/tests/functional/identity/v3/test_role.py +++ b/openstackclient/tests/functional/identity/v3/test_role.py @@ -93,6 +93,30 @@ def test_role_add(self): ) self.assertEqual(0, len(raw_output)) + def test_role_add_inherited(self): + role_name = self._create_dummy_role() + username = self._create_dummy_user() + raw_output = self.openstack( + 'role add ' + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--user {username} ' + f'--user-domain {self.domain_name} ' + '--inherited ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--project {self.project_name} ' + f'--project-domain {self.domain_name} ' + f'--user {username} ' + f'--user-domain {self.domain_name} ' + '--inherited ' + f'{role_name}', + ) + self.assertEqual(0, len(raw_output)) + def test_role_remove(self): role_name = self._create_dummy_role() username = self._create_dummy_user() diff --git a/openstackclient/tests/functional/identity/v3/test_role_assignment.py b/openstackclient/tests/functional/identity/v3/test_role_assignment.py index 941563c7b..6c53bc277 100644 --- a/openstackclient/tests/functional/identity/v3/test_role_assignment.py +++ b/openstackclient/tests/functional/identity/v3/test_role_assignment.py @@ -151,6 +151,14 @@ def test_role_assignment_list_inherited(self): '--inherited ' f'{role_name}' ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--project {self.project_name} ' + f'--user {username} ' + '--inherited ' + f'{role_name}', + ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack('role assignment list --inherited') diff --git a/openstackclient/tests/unit/identity/v3/test_role.py b/openstackclient/tests/unit/identity/v3/test_role.py index 09b7536e3..e4a27ed11 100644 --- a/openstackclient/tests/unit/identity/v3/test_role.py +++ b/openstackclient/tests/unit/identity/v3/test_role.py @@ -13,110 +13,177 @@ # under the License. # -import copy from unittest import mock from osc_lib import exceptions -from osc_lib import utils -from openstackclient.identity import common +from openstack import exceptions as sdk_exc +from openstack.identity.v3 import domain as _domain +from openstack.identity.v3 import group as _group +from openstack.identity.v3 import project as _project +from openstack.identity.v3 import role as _role +from openstack.identity.v3 import system as _system +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes + from openstackclient.identity.v3 import role -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes +from openstackclient.tests.unit import utils as test_utils + + +class TestRoleInherited(identity_fakes.TestIdentityv3): + def _is_inheritance_testcase(self): + return True -class TestRole(identity_fakes.TestIdentityv3): +class TestFindSDKId(test_utils.TestCase): def setUp(self): super().setUp() + self.user = sdk_fakes.generate_fake_resource(_user.User) + self.identity_sdk_client = mock.Mock() + self.identity_sdk_client.find_user = mock.Mock() - # Get a shortcut to the UserManager Mock - self.users_mock = self.identity_client.users - self.users_mock.reset_mock() + def test_find_sdk_id_validate(self): + self.identity_sdk_client.find_user.side_effect = [self.user] - # Get a shortcut to the UserManager Mock - self.groups_mock = self.identity_client.groups - self.groups_mock.reset_mock() + result = role._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=True, + ) + self.assertEqual(self.user.id, result) - # Get a shortcut to the DomainManager Mock - self.domains_mock = self.identity_client.domains - self.domains_mock.reset_mock() + def test_find_sdk_id_no_validate(self): + self.identity_sdk_client.find_user.side_effect = [self.user] - # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.identity_client.projects - self.projects_mock.reset_mock() + result = role._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=False, + ) + self.assertEqual(self.user.id, result) - # Get a shortcut to the RoleManager Mock - self.roles_mock = self.identity_client.roles - self.roles_mock.reset_mock() + def test_find_sdk_id_not_found_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] - def _is_inheritance_testcase(self): - return False + self.assertRaises( + exceptions.CommandError, + role._find_sdk_id, + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=True, + ) + + def test_find_sdk_id_not_found_no_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] + + result = role._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=False, + ) + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_forbidden_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ForbiddenException, + ] + + result = role._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=True, + ) + + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_forbidden_no_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ForbiddenException, + ] + + result = role._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=False, + ) + + self.assertEqual(self.user.id, result) -class TestRoleInherited(TestRole): +class TestRoleAdd(identity_fakes.TestIdentityv3): def _is_inheritance_testcase(self): - return True + return False + user = sdk_fakes.generate_fake_resource(_user.User) + group = sdk_fakes.generate_fake_resource(_group.Group) + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + project = sdk_fakes.generate_fake_resource(_project.Project) -class TestRoleAdd(TestRole): def setUp(self): super().setUp() - self.users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) + self.identity_sdk_client.find_user.return_value = self.user + self.identity_sdk_client.find_group.return_value = self.group + self.identity_sdk_client.find_domain.return_value = self.domain + self.identity_sdk_client.find_project.return_value = self.project - self.groups_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.GROUP), - loaded=True, + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, ) - - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, + self.identity_sdk_client.find_role.return_value = self.role + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, ) - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, + self.identity_sdk_client.assign_domain_role_to_user.return_value = ( + self.role ) - - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, + self.identity_sdk_client.assign_domain_role_to_group.return_value = ( + self.role ) - self.roles_mock.grant.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, + self.identity_sdk_client.assign_project_role_to_user.return_value = ( + self.role + ) + self.identity_sdk_client.assign_project_role_to_group.return_value = ( + self.role + ) + self.identity_sdk_client.assign_system_role_to_user.return_value = ( + self.role + ) + self.identity_sdk_client.assign_system_role_to_group.return_value = ( + self.role ) # Get the command object to test self.cmd = role.AddRole(self.app, None) - def test_role_add_user_system(self): + @mock.patch.object(role.LOG, 'warning') + def test_role_add_user_system(self, mock_warning): arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--system', 'all', - identity_fakes.role_name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), ('system', 'all'), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -125,32 +192,36 @@ def test_role_add_user_system(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, 'system': 'all', - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'user': self.user.id, + 'role': self.role.id, } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.assign_system_role_to_user.assert_called_with( + **kwargs ) self.assertIsNone(result) + if self._is_inheritance_testcase(): + mock_warning.assert_called_with( + "'--inherited' was given, which is not supported when adding a system role; this will be an error in a future release" + ) + def test_role_add_user_domain(self): arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--domain', - identity_fakes.domain_name, - identity_fakes.role_name, + self.domain.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), - ('domain', identity_fakes.domain_name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -159,32 +230,32 @@ def test_role_add_user_domain(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'domain': self.domain.id, + 'user': self.user.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.assign_domain_role_to_user.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_add_user_project(self): arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--project', - identity_fakes.project_name, - identity_fakes.role_name, + self.project.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.role_name), + ('project', self.project.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -193,33 +264,34 @@ def test_role_add_user_project(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'project': self.project.id, + 'user': self.user.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.assign_project_role_to_user.assert_called_with( + **kwargs ) self.assertIsNone(result) - def test_role_add_group_system(self): + @mock.patch.object(role.LOG, 'warning') + def test_role_add_group_system(self, mock_warning): arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--system', 'all', - identity_fakes.role_name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), + ('group', self.group.name), ('system', 'all'), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -228,32 +300,36 @@ def test_role_add_group_system(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, 'system': 'all', - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'group': self.group.id, + 'role': self.role.id, } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.assign_system_role_to_group.assert_called_with( + **kwargs ) self.assertIsNone(result) + if self._is_inheritance_testcase(): + mock_warning.assert_called_with( + "'--inherited' was given, which is not supported when adding a system role; this will be an error in a future release" + ) + def test_role_add_group_domain(self): arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--domain', - identity_fakes.domain_name, - identity_fakes.role_name, + self.domain.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), - ('domain', identity_fakes.domain_name), + ('group', self.group.name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -262,32 +338,32 @@ def test_role_add_group_domain(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'domain': self.domain.id, + 'group': self.group.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.assign_domain_role_to_group.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_add_group_project(self): arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--project', - identity_fakes.project_name, - identity_fakes.role_name, + self.project.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), + ('group', self.group.name), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.role_name), + ('project', self.project.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -296,39 +372,36 @@ def test_role_add_group_project(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'project': self.project.id, + 'group': self.group.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.assign_project_role_to_group.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_add_domain_role_on_user_project(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ) + self.identity_sdk_client.find_role.return_value = self.role_with_domain + arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--project', - identity_fakes.project_name, + self.project.name, '--role-domain', - identity_fakes.domain_name, - identity_fakes.ROLE_2['name'], + self.domain.name, + self.role_with_domain.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.ROLE_2['name']), + ('project', self.project.name), + ('role', self.role_with_domain.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -337,26 +410,26 @@ def test_role_add_domain_role_on_user_project(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'project': self.project.id, + 'user': self.user.id, + 'role': self.role_with_domain.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.grant(role, user=, group=, domain=, project=) - self.roles_mock.grant.assert_called_with( - identity_fakes.ROLE_2['id'], **kwargs + self.identity_sdk_client.assign_project_role_to_user.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_add_with_error(self): arglist = [ - identity_fakes.role_name, + self.role.name, ] verifylist = [ ('user', None), ('group', None), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -369,31 +442,47 @@ class TestRoleAddInherited(TestRoleAdd, TestRoleInherited): pass -class TestRoleCreate(TestRole): +class TestRoleCreate(identity_fakes.TestIdentityv3): + collist = ('id', 'name', 'domain_id', 'description') + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + def setUp(self): super().setUp() - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, ) - - self.roles_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, + ) + self.role_with_description = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description='role description', ) + self.role_with_immutable_option = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, + options={'immutable': True}, + ) + self.identity_sdk_client.find_domain.return_value = self.domain # Get the command object to test self.cmd = role.CreateRole(self.app, None) def test_role_create_no_options(self): + self.identity_sdk_client.create_role.return_value = self.role + arglist = [ - identity_fakes.role_name, + self.role.name, ] verifylist = [ - ('name', identity_fakes.role_name), + ('name', self.role.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -404,39 +493,34 @@ def test_role_create_no_options(self): # Set expected values kwargs = { - 'domain': None, - 'name': identity_fakes.role_name, - 'description': None, + 'name': self.role.name, 'options': {}, } - # RoleManager.create(name=, domain=) - self.roles_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_role.assert_called_with(**kwargs) - collist = ('domain', 'id', 'name') - self.assertEqual(collist, columns) + self.assertEqual(self.collist, columns) datalist = ( + self.role.id, + self.role.name, + None, None, - identity_fakes.role_id, - identity_fakes.role_name, ) self.assertEqual(datalist, data) def test_role_create_with_domain(self): - self.roles_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, + self.identity_sdk_client.create_role.return_value = ( + self.role_with_domain ) arglist = [ '--domain', - identity_fakes.domain_name, - identity_fakes.ROLE_2['name'], + self.domain.name, + self.role_with_domain.name, ] verifylist = [ - ('domain', identity_fakes.domain_name), - ('name', identity_fakes.ROLE_2['name']), + ('domain', self.domain.name), + ('name', self.role_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -447,38 +531,35 @@ def test_role_create_with_domain(self): # Set expected values kwargs = { - 'domain': identity_fakes.domain_id, - 'name': identity_fakes.ROLE_2['name'], - 'description': None, + 'domain_id': self.domain.id, + 'name': self.role_with_domain.name, 'options': {}, } - # RoleManager.create(name=, domain=) - self.roles_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_role.assert_called_with(**kwargs) - collist = ('domain', 'id', 'name') - self.assertEqual(collist, columns) + self.assertEqual(self.collist, columns) datalist = ( - identity_fakes.domain_id, - identity_fakes.ROLE_2['id'], - identity_fakes.ROLE_2['name'], + self.role_with_domain.id, + self.role_with_domain.name, + self.domain.id, + None, ) self.assertEqual(datalist, data) def test_role_create_with_description(self): - self.roles_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, + self.identity_sdk_client.create_role.return_value = ( + self.role_with_description ) + arglist = [ '--description', - identity_fakes.role_description, - identity_fakes.ROLE_2['name'], + self.role_with_description.description, + self.role_with_description.name, ] verifylist = [ - ('description', identity_fakes.role_description), - ('name', identity_fakes.ROLE_2['name']), + ('description', self.role_with_description.description), + ('name', self.role_with_description.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -489,37 +570,32 @@ def test_role_create_with_description(self): # Set expected values kwargs = { - 'description': identity_fakes.role_description, - 'name': identity_fakes.ROLE_2['name'], - 'domain': None, + 'name': self.role_with_description.name, + 'description': self.role_with_description.description, 'options': {}, } - # RoleManager.create(name=, domain=) - self.roles_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_role.assert_called_with(**kwargs) - collist = ('domain', 'id', 'name') - self.assertEqual(collist, columns) + self.assertEqual(self.collist, columns) datalist = ( - 'd1', - identity_fakes.ROLE_2['id'], - identity_fakes.ROLE_2['name'], + self.role_with_description.id, + self.role_with_description.name, + None, + self.role_with_description.description, ) self.assertEqual(datalist, data) def test_role_create_with_immutable_option(self): - self.roles_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ) + self.identity_sdk_client.create_role.return_value = self.role + arglist = [ '--immutable', - identity_fakes.ROLE_2['name'], + self.role.name, ] verifylist = [ ('immutable', True), - ('name', identity_fakes.ROLE_2['name']), + ('name', self.role.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -531,36 +607,30 @@ def test_role_create_with_immutable_option(self): # Set expected values kwargs = { 'options': {'immutable': True}, - 'description': None, - 'name': identity_fakes.ROLE_2['name'], - 'domain': None, + 'name': self.role.name, } - # RoleManager.create(name=, domain=) - self.roles_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_role.assert_called_with(**kwargs) - collist = ('domain', 'id', 'name') - self.assertEqual(collist, columns) + self.assertEqual(self.collist, columns) datalist = ( - 'd1', - identity_fakes.ROLE_2['id'], - identity_fakes.ROLE_2['name'], + self.role.id, + self.role.name, + None, + None, ) self.assertEqual(datalist, data) def test_role_create_with_no_immutable_option(self): - self.roles_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ) + self.identity_sdk_client.create_role.return_value = self.role + arglist = [ '--no-immutable', - identity_fakes.ROLE_2['name'], + self.role.name, ] verifylist = [ ('no_immutable', True), - ('name', identity_fakes.ROLE_2['name']), + ('name', self.role.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -572,88 +642,94 @@ def test_role_create_with_no_immutable_option(self): # Set expected values kwargs = { 'options': {'immutable': False}, - 'description': None, - 'name': identity_fakes.ROLE_2['name'], - 'domain': None, + 'name': self.role.name, } - # RoleManager.create(name=, domain=) - self.roles_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_role.assert_called_with(**kwargs) - collist = ('domain', 'id', 'name') - self.assertEqual(collist, columns) + self.assertEqual(self.collist, columns) datalist = ( - 'd1', - identity_fakes.ROLE_2['id'], - identity_fakes.ROLE_2['name'], + self.role.id, + self.role.name, + None, + None, ) self.assertEqual(datalist, data) -class TestRoleDelete(TestRole): +class TestRoleDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, - ) - self.roles_mock.delete.return_value = None - # Get the command object to test self.cmd = role.DeleteRole(self.app, None) def test_role_delete_no_options(self): + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, + ) + self.identity_sdk_client.find_role.return_value = self.role + self.identity_sdk_client.delete_role.return_value = None + arglist = [ - identity_fakes.role_name, + self.role.name, ] verifylist = [ - ('roles', [identity_fakes.role_name]), + ('roles', [self.role.name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.roles_mock.delete.assert_called_with( - identity_fakes.role_id, + self.identity_sdk_client.delete_role.assert_called_with( + role=self.role.id, + ignore_missing=False, ) self.assertIsNone(result) def test_role_delete_with_domain(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, + self.domain = sdk_fakes.generate_fake_resource(_domain.Domain) + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, ) - self.roles_mock.delete.return_value = None + self.identity_sdk_client.find_role.return_value = self.role_with_domain + self.identity_sdk_client.delete_role.return_value = None arglist = [ '--domain', - identity_fakes.domain_name, - identity_fakes.ROLE_2['name'], + self.domain.name, + self.role_with_domain.name, ] verifylist = [ - ('roles', [identity_fakes.ROLE_2['name']]), - ('domain', identity_fakes.domain_name), + ('roles', [self.role_with_domain.name]), + ('domain', self.domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.roles_mock.delete.assert_called_with( - identity_fakes.ROLE_2['id'], + self.identity_sdk_client.delete_role.assert_called_with( + role=self.role_with_domain.id, + ignore_missing=False, ) self.assertIsNone(result) - @mock.patch.object(utils, 'find_resource') - def test_delete_multi_roles_with_exception(self, find_mock): - find_mock.side_effect = [ - self.roles_mock.get.return_value, - exceptions.CommandError, + def test_delete_multi_roles_with_exception(self): + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, + ) + self.identity_sdk_client.find_role.side_effect = [ + self.role, + sdk_exc.ResourceNotFound, ] arglist = [ - identity_fakes.role_name, + self.role.id, 'unexist_role', ] verifylist = [ @@ -667,44 +743,62 @@ def test_delete_multi_roles_with_exception(self, find_mock): except exceptions.CommandError as e: self.assertEqual('1 of 2 roles failed to delete.', str(e)) - find_mock.assert_any_call( - self.roles_mock, identity_fakes.role_name, domain_id=None - ) - find_mock.assert_any_call( - self.roles_mock, 'unexist_role', domain_id=None + self.identity_sdk_client.find_role.assert_has_calls( + [ + mock.call( + name_or_id=self.role.id, + ignore_missing=False, + domain_id=None, + ), + mock.call( + name_or_id='unexist_role', + ignore_missing=False, + domain_id=None, + ), + ] ) - self.assertEqual(2, find_mock.call_count) - self.roles_mock.delete.assert_called_once_with(identity_fakes.role_id) + self.assertEqual(2, self.identity_sdk_client.find_role.call_count) + self.identity_sdk_client.delete_role.assert_called_once_with( + role=self.role.id, ignore_missing=False + ) -class TestRoleList(TestRole): +class TestRoleList(identity_fakes.TestIdentityv3): columns = ( 'ID', 'Name', ) - datalist = ( - ( - identity_fakes.role_id, - identity_fakes.role_name, - ), - ) + domain = sdk_fakes.generate_fake_resource(_domain.Domain) def setUp(self): super().setUp() - self.roles_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, - ), + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, + ) + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, + ) + self.identity_sdk_client.roles.return_value = [ + self.role, + self.role_with_domain, ] + self.identity_sdk_client.find_domain.return_value = self.domain - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, + self.datalist = ( + ( + self.role.id, + self.role.name, + ), + ( + self.role_with_domain.id, + self.role_with_domain.name, + ), ) # Get the command object to test @@ -720,25 +814,19 @@ def test_role_list_no_options(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.roles_mock.list.assert_called_with() + self.identity_sdk_client.roles.assert_called_with() self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) def test_role_list_domain_role(self): - self.roles_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ), - ] + self.identity_sdk_client.roles.return_value = [self.role_with_domain] arglist = [ '--domain', - identity_fakes.domain_name, + self.domain.name, ] verifylist = [ - ('domain', identity_fakes.domain_name), + ('domain', self.domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -748,56 +836,51 @@ def test_role_list_domain_role(self): columns, data = self.cmd.take_action(parsed_args) # Set expected values - kwargs = {'domain_id': identity_fakes.domain_id} - # RoleManager.list(user=, group=, domain=, project=, **kwargs) - self.roles_mock.list.assert_called_with(**kwargs) + kwargs = {'domain_id': self.domain.id} + self.identity_sdk_client.roles.assert_called_with(**kwargs) collist = ('ID', 'Name', 'Domain') self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.ROLE_2['id'], - identity_fakes.ROLE_2['name'], - identity_fakes.domain_name, + self.role_with_domain.id, + self.role_with_domain.name, + self.domain.name, ), ) self.assertEqual(datalist, tuple(data)) -class TestRoleRemove(TestRole): - def setUp(self): - super().setUp() +class TestRoleRemove(identity_fakes.TestIdentityv3): + def _is_inheritance_testcase(self): + return False - self.users_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.USER), - loaded=True, - ) + user = sdk_fakes.generate_fake_resource(_user.User) + group = sdk_fakes.generate_fake_resource(_group.Group) + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + project = sdk_fakes.generate_fake_resource(_project.Project) + system = sdk_fakes.generate_fake_resource(_system.System) - self.groups_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.GROUP), - loaded=True, - ) + def setUp(self): + super().setUp() - self.domains_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.DOMAIN), - loaded=True, + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, ) + self.identity_sdk_client.find_role.return_value = self.role + self.identity_sdk_client.find_user.return_value = self.user + self.identity_sdk_client.find_group.return_value = self.group + self.identity_sdk_client.find_domain.return_value = self.domain + self.identity_sdk_client.find_project.return_value = self.project - self.projects_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.PROJECT), - loaded=True, - ) - - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, - ) - self.roles_mock.revoke.return_value = None + self.identity_sdk_client.unassign_domain_role_from_user.return_value = None + self.identity_sdk_client.unassign_domain_role_from_group.return_value = None + self.identity_sdk_client.unassign_project_role_from_user.return_value = None + self.identity_sdk_client.unassign_project_role_from_group.return_value = None + self.identity_sdk_client.unassign_system_role_from_user.return_value = None + self.identity_sdk_client.unassign_system_role_from_group.return_value = None # Get the command object to test self.cmd = role.RemoveRole(self.app, None) @@ -805,20 +888,20 @@ def setUp(self): def test_role_remove_user_system(self): arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--system', 'all', - identity_fakes.role_name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), ('system', 'all'), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -827,40 +910,39 @@ def test_role_remove_user_system(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, + 'user': self.user.id, 'system': 'all', - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'role': self.role.id, } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_system_role_from_user.assert_called_with( + **kwargs ) self.assertIsNone(result) - @mock.patch.object(common, 'find_user') - def test_role_remove_non_existent_user_system(self, find_mock): - # Simulate the user not being in keystone, the client should gracefully + def test_role_remove_non_existent_user_system(self): + # Simulate the user not being in keystone; the client should gracefully # handle this exception and send the request to remove the role since # keystone supports removing role assignments with non-existent actors # (e.g., users or groups). - find_mock.side_effect = exceptions.CommandError - + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] arglist = [ '--user', - identity_fakes.user_id, + self.user.id, '--system', 'all', - identity_fakes.role_name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_id), + ('user', self.user.id), ('group', None), ('system', 'all'), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -869,32 +951,31 @@ def test_role_remove_non_existent_user_system(self, find_mock): # Set expected values kwargs = { - 'user': identity_fakes.user_id, + 'user': self.user.id, 'system': 'all', - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'role': self.role.id, } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_system_role_from_user.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_user_domain(self): arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--domain', - identity_fakes.domain_name, - identity_fakes.role_name, + self.domain.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), - ('domain', identity_fakes.domain_name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -903,39 +984,39 @@ def test_role_remove_user_domain(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'user': self.user.id, + 'domain': self.domain.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_domain_role_from_user.assert_called_with( + **kwargs ) self.assertIsNone(result) - @mock.patch.object(common, 'find_user') - def test_role_remove_non_existent_user_domain(self, find_mock): + def test_role_remove_non_existent_user_domain(self): # Simulate the user not being in keystone, the client the gracefully # handle this exception and send the request to remove the role since # keystone will validate. - find_mock.side_effect = exceptions.CommandError - + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] arglist = [ '--user', - identity_fakes.user_id, + self.user.id, '--domain', - identity_fakes.domain_name, - identity_fakes.role_name, + self.domain.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_id), + ('user', self.user.id), ('group', None), ('system', None), - ('domain', identity_fakes.domain_name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -944,32 +1025,32 @@ def test_role_remove_non_existent_user_domain(self, find_mock): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'user': self.user.id, + 'domain': self.domain.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_domain_role_from_user.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_user_project(self): arglist = [ '--user', - identity_fakes.user_name, + self.user.name, '--project', - identity_fakes.project_name, - identity_fakes.role_name, + self.project.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.name), ('group', None), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.role_name), + ('project', self.project.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -978,39 +1059,40 @@ def test_role_remove_user_project(self): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'user': self.user.id, + 'project': self.project.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_project_role_from_user.assert_called_with( + **kwargs ) self.assertIsNone(result) - @mock.patch.object(common, 'find_user') - def test_role_remove_non_existent_user_project(self, find_mock): + def test_role_remove_non_existent_user_project(self): # Simulate the user not being in keystone, the client the gracefully # handle this exception and send the request to remove the role since # keystone will validate. - find_mock.side_effect = exceptions.CommandError + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] arglist = [ '--user', - identity_fakes.user_id, + self.user.id, '--project', - identity_fakes.project_name, - identity_fakes.role_name, + self.project.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ - ('user', identity_fakes.user_id), + ('user', self.user.id), ('group', None), ('system', None), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.role_name), + ('project', self.project.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1019,34 +1101,34 @@ def test_role_remove_non_existent_user_project(self, find_mock): # Set expected values kwargs = { - 'user': identity_fakes.user_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'user': self.user.id, + 'project': self.project.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_project_role_from_user.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_group_system(self): arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--system', 'all', - identity_fakes.role_name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), + ('group', self.group.name), ('system', 'all'), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), - ('role', identity_fakes.role_name), + ('role', self.role.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1055,39 +1137,39 @@ def test_role_remove_group_system(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, + 'group': self.group.id, 'system': 'all', - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'role': self.role.id, } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_system_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) - @mock.patch.object(common, 'find_group') - def test_role_remove_non_existent_group_system(self, find_mock): + def test_role_remove_non_existent_group_system(self): # Simulate the user not being in keystone, the client the gracefully # handle this exception and send the request to remove the role since # keystone will validate. - find_mock.side_effect = exceptions.CommandError + self.identity_sdk_client.find_group.side_effect = [ + sdk_exc.ResourceNotFound, + ] arglist = [ '--group', - identity_fakes.group_id, + self.group.id, '--system', 'all', - identity_fakes.role_name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_id), + ('group', self.group.id), ('system', 'all'), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1096,33 +1178,32 @@ def test_role_remove_non_existent_group_system(self, find_mock): # Set expected values kwargs = { - 'group': identity_fakes.group_id, + 'group': self.group.id, 'system': 'all', - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'role': self.role.id, } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_system_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_group_domain(self): arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--domain', - identity_fakes.domain_name, - identity_fakes.role_name, + self.domain.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), - ('domain', identity_fakes.domain_name), + ('group', self.group.name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.role_name), - ('role', identity_fakes.role_name), + ('role', self.role.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1131,39 +1212,40 @@ def test_role_remove_group_domain(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'group': self.group.id, + 'domain': self.domain.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_domain_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) - @mock.patch.object(common, 'find_group') - def test_role_remove_non_existent_group_domain(self, find_mock): + def test_role_remove_non_existent_group_domain(self): # Simulate the user not being in keystone, the client the gracefully # handle this exception and send the request to remove the role since # keystone will validate. - find_mock.side_effect = exceptions.CommandError + self.identity_sdk_client.find_group.side_effect = [ + sdk_exc.ResourceNotFound, + ] arglist = [ '--group', - identity_fakes.group_id, + self.group.id, '--domain', - identity_fakes.domain_name, - identity_fakes.role_name, + self.domain.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_id), + ('group', self.group.id), ('system', None), - ('domain', identity_fakes.domain_name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1172,32 +1254,32 @@ def test_role_remove_non_existent_group_domain(self, find_mock): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'group': self.group.id, + 'domain': self.domain.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_domain_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_group_project(self): arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--project', - identity_fakes.project_name, - identity_fakes.role_name, + self.project.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), + ('group', self.group.name), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.role_name), + ('project', self.project.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1206,39 +1288,39 @@ def test_role_remove_group_project(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'group': self.group.id, + 'project': self.project.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_project_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) - @mock.patch.object(common, 'find_group') - def test_role_remove_non_existent_group_project(self, find_mock): + def test_role_remove_non_existent_group_project(self): # Simulate the user not being in keystone, the client the gracefully # handle this exception and send the request to remove the role since # keystone will validate. - find_mock.side_effect = exceptions.CommandError - + self.identity_sdk_client.find_group.side_effect = [ + sdk_exc.ResourceNotFound, + ] arglist = [ '--group', - identity_fakes.group_id, + self.group.id, '--project', - identity_fakes.project_name, - identity_fakes.role_name, + self.project.name, + self.role.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_id), + ('group', self.group.id), ('system', None), ('domain', None), - ('project', identity_fakes.project_name), - ('role', identity_fakes.role_name), + ('project', self.project.name), + ('role', self.role.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1247,37 +1329,38 @@ def test_role_remove_non_existent_group_project(self, find_mock): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'project': identity_fakes.project_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'group': self.group.id, + 'project': self.project.id, + 'role': self.role.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.role_id, **kwargs + self.identity_sdk_client.unassign_project_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_domain_role_on_group_domain(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, ) + self.identity_sdk_client.find_role.return_value = self.role_with_domain arglist = [ '--group', - identity_fakes.group_name, + self.group.name, '--domain', - identity_fakes.domain_name, - identity_fakes.ROLE_2['name'], + self.domain.name, + self.role_with_domain.name, ] if self._is_inheritance_testcase(): arglist.append('--inherited') verifylist = [ ('user', None), - ('group', identity_fakes.group_name), - ('domain', identity_fakes.domain_name), + ('group', self.group.name), + ('domain', self.domain.name), ('project', None), - ('role', identity_fakes.ROLE_2['name']), + ('role', self.role_with_domain.name), ('inherited', self._is_inheritance_testcase()), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1286,26 +1369,26 @@ def test_role_remove_domain_role_on_group_domain(self): # Set expected values kwargs = { - 'group': identity_fakes.group_id, - 'domain': identity_fakes.domain_id, - 'os_inherit_extension_inherited': self._is_inheritance_testcase(), + 'group': self.group.id, + 'domain': self.domain.id, + 'role': self.role_with_domain.id, + 'inherited': self._is_inheritance_testcase(), } - # RoleManager.revoke(role, user=, group=, domain=, project=) - self.roles_mock.revoke.assert_called_with( - identity_fakes.ROLE_2['id'], **kwargs + self.identity_sdk_client.unassign_domain_role_from_group.assert_called_with( + **kwargs ) self.assertIsNone(result) def test_role_remove_with_error(self): arglist = [ - identity_fakes.role_name, + self.role.name, ] verifylist = [ ('user', None), ('group', None), ('domain', None), ('project', None), - ('role', identity_fakes.role_name), + ('role', self.role.name), ('inherited', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1314,29 +1397,37 @@ def test_role_remove_with_error(self): ) -class TestRoleSet(TestRole): +class TestRoleSet(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, + self.domain = sdk_fakes.generate_fake_resource(_domain.Domain) + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, ) - self.roles_mock.update.return_value = None # Get the command object to test self.cmd = role.SetRole(self.app, None) def test_role_set_no_options(self): + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, + ) + self.identity_sdk_client.find_role.return_value = self.role + self.identity_sdk_client.update_role.return_value = self.role + arglist = [ '--name', 'over', - identity_fakes.role_name, + self.role.name, ] verifylist = [ ('name', 'over'), - ('role', identity_fakes.role_name), + ('role', self.role.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1345,32 +1436,32 @@ def test_role_set_no_options(self): # Set expected values kwargs = { 'name': 'over', - 'description': None, + 'role': self.role.id, 'options': {}, } - # RoleManager.update(role, name=) - self.roles_mock.update.assert_called_with( - identity_fakes.role_id, **kwargs - ) + self.identity_sdk_client.update_role.assert_called_with(**kwargs) self.assertIsNone(result) def test_role_set_domain_role(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, + self.domain2 = sdk_fakes.generate_fake_resource(_domain.Domain) + self.identity_sdk_client.find_domain.return_value = self.domain2 + + self.identity_sdk_client.find_role.return_value = self.role_with_domain + self.identity_sdk_client.update_role.return_value = ( + self.role_with_domain ) + arglist = [ '--name', 'over', '--domain', - identity_fakes.domain_name, - identity_fakes.ROLE_2['name'], + self.domain2.name, + self.role_with_domain.name, ] verifylist = [ ('name', 'over'), - ('domain', identity_fakes.domain_name), - ('role', identity_fakes.ROLE_2['name']), + ('domain', self.domain2.name), + ('role', self.role_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1379,32 +1470,27 @@ def test_role_set_domain_role(self): # Set expected values kwargs = { 'name': 'over', - 'description': None, + 'role': self.role_with_domain.id, + 'domain_id': self.domain2.id, 'options': {}, } - # RoleManager.update(role, name=) - self.roles_mock.update.assert_called_with( - identity_fakes.ROLE_2['id'], **kwargs - ) + self.identity_sdk_client.update_role.assert_called_with(**kwargs) self.assertIsNone(result) def test_role_set_description(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ) + self.identity_sdk_client.find_role.return_value = self.role_with_domain + arglist = [ '--name', 'over', '--description', - identity_fakes.role_description, - identity_fakes.ROLE_2['name'], + 'role description', + self.role_with_domain.name, ] verifylist = [ ('name', 'over'), - ('description', identity_fakes.role_description), - ('role', identity_fakes.ROLE_2['name']), + ('description', 'role description'), + ('role', self.role_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1413,31 +1499,26 @@ def test_role_set_description(self): # Set expected values kwargs = { 'name': 'over', - 'description': identity_fakes.role_description, + 'description': 'role description', + 'role': self.role_with_domain.id, 'options': {}, } - # RoleManager.update(role, name=) - self.roles_mock.update.assert_called_with( - identity_fakes.ROLE_2['id'], **kwargs - ) + self.identity_sdk_client.update_role.assert_called_with(**kwargs) self.assertIsNone(result) def test_role_set_with_immutable(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ) + self.identity_sdk_client.find_role.return_value = self.role_with_domain + arglist = [ '--name', 'over', '--immutable', - identity_fakes.ROLE_2['name'], + self.role_with_domain.name, ] verifylist = [ ('name', 'over'), ('immutable', True), - ('role', identity_fakes.ROLE_2['name']), + ('role', self.role_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1446,31 +1527,25 @@ def test_role_set_with_immutable(self): # Set expected values kwargs = { 'name': 'over', - 'description': None, + 'role': self.role_with_domain.id, 'options': {'immutable': True}, } - # RoleManager.update(role, name=) - self.roles_mock.update.assert_called_with( - identity_fakes.ROLE_2['id'], **kwargs - ) + self.identity_sdk_client.update_role.assert_called_with(**kwargs) self.assertIsNone(result) def test_role_set_with_no_immutable(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, - ) + self.identity_sdk_client.find_role.return_value = self.role_with_domain + arglist = [ '--name', 'over', '--no-immutable', - identity_fakes.ROLE_2['name'], + self.role_with_domain.name, ] verifylist = [ ('name', 'over'), ('no_immutable', True), - ('role', identity_fakes.ROLE_2['name']), + ('role', self.role_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1479,35 +1554,37 @@ def test_role_set_with_no_immutable(self): # Set expected values kwargs = { 'name': 'over', - 'description': None, + 'role': self.role_with_domain.id, 'options': {'immutable': False}, } - # RoleManager.update(role, name=) - self.roles_mock.update.assert_called_with( - identity_fakes.ROLE_2['id'], **kwargs - ) + self.identity_sdk_client.update_role.assert_called_with(**kwargs) self.assertIsNone(result) -class TestRoleShow(TestRole): +class TestRoleShow(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + def setUp(self): super().setUp() - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, - ) + self.identity_sdk_client.find_domain.return_value = self.domain # Get the command object to test self.cmd = role.ShowRole(self.app, None) def test_role_show(self): + self.role = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=None, + description=None, + ) + self.identity_sdk_client.find_role.return_value = self.role + arglist = [ - identity_fakes.role_name, + self.role.name, ] verifylist = [ - ('role', identity_fakes.role_name), + ('role', self.role.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1516,34 +1593,38 @@ def test_role_show(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # RoleManager.get(role) - self.roles_mock.get.assert_called_with( - identity_fakes.role_name, + self.identity_sdk_client.find_role.assert_called_with( + name_or_id=self.role.name, + domain_id=None, + ignore_missing=False, ) - collist = ('domain', 'id', 'name') + collist = ('id', 'name', 'domain_id', 'description') self.assertEqual(collist, columns) datalist = ( + self.role.id, + self.role.name, + None, None, - identity_fakes.role_id, - identity_fakes.role_name, ) self.assertEqual(datalist, data) def test_role_show_domain_role(self): - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE_2), - loaded=True, + self.role_with_domain = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=self.domain.id, + description=None, ) + self.identity_sdk_client.find_role.return_value = self.role_with_domain + arglist = [ '--domain', - identity_fakes.domain_name, - identity_fakes.ROLE_2['name'], + self.domain.name, + self.role_with_domain.id, ] verifylist = [ - ('domain', identity_fakes.domain_name), - ('role', identity_fakes.ROLE_2['name']), + ('domain', self.domain.name), + ('role', self.role_with_domain.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1552,23 +1633,18 @@ def test_role_show_domain_role(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # RoleManager.get(role). This is called from utils.find_resource(). - # In fact, the current implementation calls the get(role) first with - # just the name, then with the name+domain_id. So technically we should - # mock this out with a call list, with the first call returning None - # and the second returning the object. However, if we did that we are - # then just testing the current sequencing within the utils method, and - # would become brittle to changes within that method. Hence we just - # check for the first call which is always lookup by name. - self.roles_mock.get.assert_called_with( - identity_fakes.ROLE_2['name'], + self.identity_sdk_client.find_role.assert_called_with( + name_or_id=self.role_with_domain.id, + domain_id=self.domain.id, + ignore_missing=False, ) - collist = ('domain', 'id', 'name') + collist = ('id', 'name', 'domain_id', 'description') self.assertEqual(collist, columns) datalist = ( - identity_fakes.domain_id, - identity_fakes.ROLE_2['id'], - identity_fakes.ROLE_2['name'], + self.role_with_domain.id, + self.role_with_domain.name, + self.domain.id, + None, ) self.assertEqual(datalist, data) From 4f95e0aa1826ae99bf3f7840a0c224348a382337 Mon Sep 17 00:00:00 2001 From: Pavlo Shchelokovskyy Date: Thu, 16 Jan 2025 15:28:21 +0000 Subject: [PATCH 073/245] Show final image state after image create creating the image is a 2step process, first an 'empty' image is created and then the data is uploaded. Currently the output of the 'image create' command is that 'empty' image, in `queued` status etc. A more user friendly approach would be to make a second refresh call to show the user image as it is after data was uploaded. Change-Id: I2f78b113dcc3c941f8cf8dd9b63262971a780a39 --- openstackclient/image/v2/image.py | 3 +++ openstackclient/tests/unit/image/v2/test_image.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 094c507b0..fe237e4e1 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -576,6 +576,9 @@ def _take_action_image(self, parsed_args): if parsed_args.filename: fp.close() + # NOTE(pas-ha): create_image returns the image object as it was created + # before the data was uploaded, need a refresh to show the final state + image = image_client.get_image(image) return _format_image(image) def _take_action_volume(self, parsed_args): diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index e8aafa8cf..69a65a59c 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -55,6 +55,7 @@ def setUp(self): self.new_image = image_fakes.create_one_image() self.image_client.create_image.return_value = self.new_image self.image_client.update_image.return_value = self.new_image + self.image_client.get_image.return_value = self.new_image self.project_mock.get.return_value = self.project @@ -89,6 +90,7 @@ def test_image_reserve_no_options(self, raw_input): container_format=_image.DEFAULT_CONTAINER_FORMAT, disk_format=_image.DEFAULT_DISK_FORMAT, ) + self.image_client.get_image.assert_called_once_with(self.new_image) self.assertEqual(self.expected_columns, columns) self.assertCountEqual(self.expected_data, data) @@ -146,6 +148,7 @@ def test_image_reserve_options(self, raw_input): is_protected=self.new_image.is_protected, visibility=self.new_image.visibility, ) + self.image_client.get_image.assert_called_once_with(self.new_image) self.assertEqual(self.expected_columns, columns) self.assertCountEqual(self.expected_data, data) @@ -243,6 +246,7 @@ def test_image_create_file(self): tags=self.new_image.tags, filename=imagefile.name, ) + self.image_client.get_image.assert_called_once_with(self.new_image) self.assertEqual(self.expected_columns, columns) self.assertCountEqual(self.expected_data, data) @@ -275,6 +279,7 @@ def test_image_create__progress_ignore_with_stdin( data=fake_stdin, validate_checksum=False, ) + self.image_client.get_image.assert_called_once_with(self.new_image) self.assertEqual(self.expected_columns, columns) self.assertCountEqual(self.expected_data, data) @@ -315,6 +320,7 @@ def test_image_create_import(self, raw_input): disk_format=_image.DEFAULT_DISK_FORMAT, use_import=True, ) + self.image_client.get_image.assert_called_once_with(self.new_image) @mock.patch('osc_lib.utils.find_resource') @mock.patch('openstackclient.image.v2.image.get_data_from_stdin') From d2d72192315949929c32a72f693e76fda123a893 Mon Sep 17 00:00:00 2001 From: Fernando Royo Date: Tue, 21 Jan 2025 14:23:07 +0100 Subject: [PATCH 074/245] Get "security_groups" when port list Neutron API is accepting 'security_groups' field in order to return the list of security_groups attached to a port, but openstackclient is parsing the output over a Openstack Port object that has security_group_ids to map. This patch sends to the Neutron API the expected field value and replace the output key to allow the mapping just in case '--long' argument is passed. Closes-Bug: #2095414 Change-Id: I188edc3c620ce29d7b16497ca24fd7d972a06618 --- openstackclient/network/v2/port.py | 29 ++++++++++--------- .../tests/functional/network/v2/test_port.py | 29 ++++++++++++++++++- .../tests/unit/network/v2/test_port.py | 16 +++++----- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 478bca840..cf334bcdd 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -866,35 +866,30 @@ def take_action(self, parsed_args): network_client = self.app.client_manager.network identity_client = self.app.client_manager.identity - columns = ( + columns = [ 'id', 'name', 'mac_address', 'fixed_ips', 'status', - ) - column_headers = ( + ] + column_headers = [ 'ID', 'Name', 'MAC Address', 'Fixed IP Addresses', 'Status', - ) + ] filters = {} if parsed_args.long: - columns += ( - 'security_group_ids', - 'device_owner', - 'tags', - 'trunk_details', + columns.extend( + ['security_groups', 'device_owner', 'tags', 'trunk_details'] ) - column_headers += ( - 'Security Groups', - 'Device Owner', - 'Tags', - 'Trunk subports', + column_headers.extend( + ['Security Groups', 'Device Owner', 'Tags', 'Trunk subports'] ) + if parsed_args.device_owner is not None: filters['device_owner'] = parsed_args.device_owner if parsed_args.device_id is not None: @@ -942,6 +937,12 @@ def take_action(self, parsed_args): data = network_client.ports(fields=columns, **filters) + if parsed_args.long: + columns = [ + 'security_group_ids' if item == 'security_groups' else item + for item in columns + ] + headers, attrs = utils.calculate_header_and_attrs( column_headers, columns, parsed_args ) diff --git a/openstackclient/tests/functional/network/v2/test_port.py b/openstackclient/tests/functional/network/v2/test_port.py index 24fbe0ea1..ef46b1f42 100644 --- a/openstackclient/tests/functional/network/v2/test_port.py +++ b/openstackclient/tests/functional/network/v2/test_port.py @@ -81,10 +81,20 @@ def test_port_list(self): self.addCleanup(self.openstack, f'port delete {id1}') self.assertEqual(self.NAME, json_output.get('name')) + # sg for port2 + sg_name1 = uuid.uuid4().hex json_output = self.openstack( - f'port create --network {self.NETWORK_NAME} {self.NAME}x', + f'security group create {sg_name1}', parse_output=True, ) + sg_id1 = json_output.get('id') + self.addCleanup(self.openstack, f'security group delete {sg_id1}') + json_output = self.openstack( + f'port create --network {self.NETWORK_NAME} ' + f'--security-group {sg_name1} {self.NAME}x', + parse_output=True, + ) + id2 = json_output.get('id') self.assertIsNotNone(id2) mac2 = json_output.get('mac_address') @@ -113,6 +123,12 @@ def test_port_list(self): id_list = [item.get('ID') for item in json_output] self.assertIn(id1, id_list) self.assertIn(id2, id_list) + item_sg_map = { + item.get('ID'): item.get('Security Groups') for item in json_output + } + self.assertIn(id1, item_sg_map.keys()) + self.assertIn(id2, item_sg_map.keys()) + self.assertIn([sg_id1], item_sg_map.values()) # Test list --mac-address json_output = self.openstack( @@ -127,6 +143,17 @@ def test_port_list(self): self.assertNotIn(mac1, item_map.values()) self.assertIn(mac2, item_map.values()) + # Test list --security-group + json_output = self.openstack( + f'port list --security-group {sg_id1}', + parse_output=True, + ) + item_map = { + item.get('ID'): item.get('Security Groups') for item in json_output + } + self.assertNotIn(id1, item_map.keys()) + self.assertIn(id2, item_map.keys()) + # Test list with unknown fields json_output = self.openstack( 'port list -c ID -c Name -c device_id', diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 9cea37dfc..77561f78c 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -24,13 +24,13 @@ from openstackclient.tests.unit import utils as test_utils -LIST_FIELDS_TO_RETRIEVE = ('id', 'name', 'mac_address', 'fixed_ips', 'status') -LIST_FIELDS_TO_RETRIEVE_LONG = ( - 'security_group_ids', +LIST_FIELDS_TO_RETRIEVE = ['id', 'name', 'mac_address', 'fixed_ips', 'status'] +LIST_FIELDS_TO_RETRIEVE_LONG = [ + 'security_groups', 'device_owner', 'tags', 'trunk_details', -) +] class TestPort(network_fakes.TestNetworkV2): @@ -1274,15 +1274,15 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort): ) _ports = (_pport, _sport1, _sport2) - columns = ( + columns = [ 'ID', 'Name', 'MAC Address', 'Fixed IP Addresses', 'Status', - ) + ] - columns_long = ( + columns_long = [ 'ID', 'Name', 'MAC Address', @@ -1292,7 +1292,7 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort): 'Device Owner', 'Tags', 'Trunk subports', - ) + ] data = [] for prt in _ports: From 3de1ac66e02e62a964104a0b9085ae75475cb2dc Mon Sep 17 00:00:00 2001 From: Ivan Anfimov Date: Wed, 22 Jan 2025 08:17:31 +0000 Subject: [PATCH 075/245] Update README for use python3 And small change for `long_description_content_type` missing. Change-Id: I042a319bfc5009ce625565effa7ccf634222be28 --- README.rst | 12 +++++------- setup.cfg | 1 + 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index f526ec6ad..ae1cf8acd 100644 --- a/README.rst +++ b/README.rst @@ -48,12 +48,12 @@ Getting Started OpenStack Client can be installed from PyPI using pip:: - pip install python-openstackclient + python3 -m pip install python-openstackclient There are a few variants on getting help. A list of global options and supported commands is shown with ``--help``:: - openstack --help + openstack --help There is also a ``help`` command that can be used to get help text for a specific command:: @@ -64,11 +64,9 @@ command:: If you want to make changes to the OpenStackClient for testing and contribution, make any changes and then run:: - python setup.py develop - -or:: - - pip install -e . + git clone https://opendev.org/openstack/python-openstackclient + cd python-openstackclient + python3 -m pip install -e . Configuration ============= diff --git a/setup.cfg b/setup.cfg index 0ce65134f..f842dcb7d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,6 +3,7 @@ name = python-openstackclient summary = OpenStack Command-line Client description_file = README.rst +description-content-type = text/x-rst author = OpenStack author_email = openstack-discuss@lists.openstack.org home_page = https://docs.openstack.org/python-openstackclient/latest/ From 060d706bf4219523b2f4a6245a3b6a5a43743453 Mon Sep 17 00:00:00 2001 From: ArtofBugs Date: Tue, 28 Jan 2025 09:10:22 -0800 Subject: [PATCH 076/245] Identity: Deprecate `--region None` in limits/registered limits Change-Id: I37afac1185595216e868202c861d3de719b32073 --- openstackclient/identity/v3/limit.py | 15 +++++++++++++++ .../identity/v3/registered_limit.py | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/openstackclient/identity/v3/limit.py b/openstackclient/identity/v3/limit.py index 59cc1398c..44a3f6356 100644 --- a/openstackclient/identity/v3/limit.py +++ b/openstackclient/identity/v3/limit.py @@ -90,6 +90,13 @@ def take_action(self, parsed_args): region = common_utils.get_resource( identity_client.regions, parsed_args.region ) + else: + self.log.warning( + _( + "Passing 'None' to indicate no region is deprecated. " + "Instead, don't pass --region." + ) + ) limit = identity_client.limits.create( project, @@ -158,6 +165,14 @@ def take_action(self, parsed_args): region = common_utils.get_resource( identity_client.regions, parsed_args.region ) + else: + self.log.warning( + _( + "Passing 'None' to indicate no region is deprecated. " + "Instead, don't pass --region." + ) + ) + project = None if parsed_args.project: project = utils.find_resource( diff --git a/openstackclient/identity/v3/registered_limit.py b/openstackclient/identity/v3/registered_limit.py index f71ef1f5a..336151ddb 100644 --- a/openstackclient/identity/v3/registered_limit.py +++ b/openstackclient/identity/v3/registered_limit.py @@ -81,6 +81,13 @@ def take_action(self, parsed_args): region = common_utils.get_resource( identity_client.regions, parsed_args.region ) + else: + self.log.warning( + _( + "Passing 'None' to indicate no region is deprecated. " + "Instead, don't pass --region." + ) + ) registered_limit = identity_client.registered_limits.create( service, @@ -182,6 +189,13 @@ def take_action(self, parsed_args): region = common_utils.get_resource( identity_client.regions, parsed_args.region ) + else: + self.log.warning( + _( + "Passing 'None' to indicate no region is deprecated. " + "Instead, don't pass --region." + ) + ) registered_limits = identity_client.registered_limits.list( service=service, @@ -280,6 +294,10 @@ def take_action(self, parsed_args): region = common_utils.get_resource( identity_client.regions, parsed_args.region ) + else: + self.log.warning( + _("Passing 'None' to indicate no region is deprecated.") + ) registered_limit = identity_client.registered_limits.update( parsed_args.registered_limit_id, From e761ef8e32969f0235d0de3f64bea6f3e08d2b39 Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Wed, 12 Feb 2025 16:35:36 +0100 Subject: [PATCH 077/245] Temporary ignore "is_vlan_qinq" column in the output We need such temporary workaround until [1] in SDK will be merged, as without that change here py{39,312}-tips jobs are failing on that SDK patch. [1] https://review.opendev.org/c/openstack/openstacksdk/+/939703 Related-bug: #1915151 Change-Id: Id39a6482de54fe78e26fa33c9252253886cf1f3d --- openstackclient/network/v2/network.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index de1910ea7..912e06859 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -60,7 +60,11 @@ def _get_columns_network(item): 'ipv6_address_scope_id': 'ipv6_address_scope', 'tags': 'tags', } - hidden_columns = ['location', 'tenant_id'] + # TODO(slaweq): temporary, until + # https://review.opendev.org/c/openstack/openstacksdk/+/939703 will be + # merged this new column should be hidden from the output (it is just to + # make unit tests in the openstacksdk patch happy) + hidden_columns = ['location', 'tenant_id', 'is_vlan_qinq'] return utils.get_osc_show_columns_for_sdk_resource( item, column_map, hidden_columns ) From 34121473722b1e10ba9b64af9a3517dc2d6e422f Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Tue, 21 Jan 2025 12:25:42 +0100 Subject: [PATCH 078/245] Add "qinq-vlan" and "no-qinq-vlan" params to the "network create" cmd Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/939703 Related-Bug: #1915151 Change-Id: Icacf83c20c3650a9d75f665f020b8818e1b4a585 --- openstackclient/network/v2/network.py | 39 ++++++++++++++++--- .../tests/unit/network/v2/fakes.py | 1 + .../tests/unit/network/v2/test_network.py | 25 ++++++++++++ ..._qinq-to-the-network-3556c094aeedc0de.yaml | 7 ++++ 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/add-vlan_qinq-to-the-network-3556c094aeedc0de.yaml diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 912e06859..7636de0c9 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -14,6 +14,7 @@ from cliff import columns as cliff_columns from osc_lib.cli import format_columns +from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag @@ -60,11 +61,7 @@ def _get_columns_network(item): 'ipv6_address_scope_id': 'ipv6_address_scope', 'tags': 'tags', } - # TODO(slaweq): temporary, until - # https://review.opendev.org/c/openstack/openstacksdk/+/939703 will be - # merged this new column should be hidden from the output (it is just to - # make unit tests in the openstacksdk patch happy) - hidden_columns = ['location', 'tenant_id', 'is_vlan_qinq'] + hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( item, column_map, hidden_columns ) @@ -353,6 +350,25 @@ def update_parser_network(self, parser): ), ) + vlan_qinq_grp = parser.add_mutually_exclusive_group() + vlan_qinq_grp.add_argument( + '--qinq-vlan', + action='store_true', + help=self.enhance_help_neutron( + _("Enable VLAN QinQ (S-Tag ethtype 0x8a88) " "for the network") + ), + ) + vlan_qinq_grp.add_argument( + '--no-qinq-vlan', + action='store_true', + help=self.enhance_help_neutron( + _( + "Disable VLAN QinQ (S-Tag ethtype 0x8a88) " + "for the network" + ) + ), + ) + _add_additional_network_options(parser) _tag.add_tag_option_to_parser_for_create( parser, _('network'), enhance_help=self.enhance_help_neutron @@ -376,6 +392,19 @@ def take_action_network(self, client, parsed_args): attrs['vlan_transparent'] = True if parsed_args.no_transparent_vlan: attrs['vlan_transparent'] = False + + if parsed_args.qinq_vlan: + attrs['vlan_qinq'] = True + if parsed_args.no_qinq_vlan: + attrs['vlan_qinq'] = False + + if attrs.get('vlan_transparent') and attrs.get('vlan_qinq'): + msg = _( + "--transparent-vlan and --qinq-vlan can not be both enabled " + "for the network." + ) + raise exceptions.CommandError(msg) + attrs.update( self._parse_extra_properties(parsed_args.extra_properties) ) diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index 4a0f9df77..d0b1c1ebb 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -1419,6 +1419,7 @@ def create_one_network(attrs=None): 'availability_zone_hints': [], 'is_default': False, 'is_vlan_transparent': True, + 'is_vlan_qinq': False, 'port_security_enabled': True, 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, 'ipv4_address_scope': 'ipv4' + uuid.uuid4().hex, diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index 33fe5be3a..0222be68d 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -63,6 +63,7 @@ class TestCreateNetworkIdentityV3(TestNetwork): 'ipv6_address_scope', 'is_default', 'is_vlan_transparent', + 'is_vlan_qinq', 'mtu', 'name', 'port_security_enabled', @@ -103,6 +104,7 @@ class TestCreateNetworkIdentityV3(TestNetwork): network.RouterExternalColumn(_network.is_router_external), _network.is_shared, _network.is_vlan_transparent, + _network.is_vlan_qinq, _network.status, _network.segments, format_columns.ListColumn(_network.subnet_ids), @@ -190,6 +192,7 @@ def test_create_all_options(self): "--qos-policy", self.qos_policy.id, "--transparent-vlan", + "--no-qinq-vlan", "--enable-port-security", "--dns-domain", "example.org.", @@ -210,6 +213,7 @@ def test_create_all_options(self): ('segmentation_id', '400'), ('qos_policy', self.qos_policy.id), ('transparent_vlan', True), + ('qinq_vlan', False), ('enable_port_security', True), ('name', self._network.name), ('dns_domain', 'example.org.'), @@ -234,6 +238,7 @@ def test_create_all_options(self): 'provider:segmentation_id': '400', 'qos_policy_id': self.qos_policy.id, 'vlan_transparent': True, + 'vlan_qinq': False, 'port_security_enabled': True, 'dns_domain': 'example.org.', } @@ -246,6 +251,7 @@ def test_create_other_options(self): "--enable", "--no-share", "--disable-port-security", + "--qinq-vlan", self._network.name, ] verifylist = [ @@ -253,6 +259,7 @@ def test_create_other_options(self): ('no_share', True), ('name', self._network.name), ('external', False), + ('qinq_vlan', True), ('disable_port_security', True), ] @@ -264,6 +271,7 @@ def test_create_other_options(self): 'admin_state_up': True, 'name': self._network.name, 'shared': False, + 'vlan_qinq': True, 'port_security_enabled': False, } ) @@ -309,6 +317,19 @@ def test_create_with_tags(self): def test_create_with_no_tag(self): self._test_create_with_tag(add_tags=False) + def test_create_with_vlan_qinq_and_transparency_enabled(self): + arglist = [ + "--transparent-vlan", + "--qinq-vlan", + self._network.name, + ] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + class TestCreateNetworkIdentityV2( identity_fakes_v2.FakeClientMixin, @@ -332,6 +353,7 @@ class TestCreateNetworkIdentityV2( 'ipv6_address_scope', 'is_default', 'is_vlan_transparent', + 'is_vlan_qinq', 'mtu', 'name', 'port_security_enabled', @@ -372,6 +394,7 @@ class TestCreateNetworkIdentityV2( network.RouterExternalColumn(_network.is_router_external), _network.is_shared, _network.is_vlan_transparent, + _network.is_vlan_qinq, _network.status, _network.segments, format_columns.ListColumn(_network.subnet_ids), @@ -1149,6 +1172,7 @@ class TestShowNetwork(TestNetwork): 'ipv6_address_scope', 'is_default', 'is_vlan_transparent', + 'is_vlan_qinq', 'mtu', 'name', 'port_security_enabled', @@ -1189,6 +1213,7 @@ class TestShowNetwork(TestNetwork): network.RouterExternalColumn(_network.is_router_external), _network.is_shared, _network.is_vlan_transparent, + _network.is_vlan_qinq, _network.status, _network.segments, format_columns.ListColumn(_network.subnet_ids), diff --git a/releasenotes/notes/add-vlan_qinq-to-the-network-3556c094aeedc0de.yaml b/releasenotes/notes/add-vlan_qinq-to-the-network-3556c094aeedc0de.yaml new file mode 100644 index 000000000..f89575b40 --- /dev/null +++ b/releasenotes/notes/add-vlan_qinq-to-the-network-3556c094aeedc0de.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Add ``qinq-vlan`` and ``no-qinq-vlan`` arguments to the ``network create`` + command. It will enable/disable QinQ feature for the created network. + This new argument is mutually exclusive with the ``transparent-vlan`` - only + one of them can be set to ``True`` for the network. From b50ac8d2a225dd1ab54fbca8deb04370b3aaf065 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Thu, 13 Feb 2025 12:59:08 +0000 Subject: [PATCH 079/245] [Neutron] "uplink-status-propagation" enabled by default Since [1][2], the port flag "uplink-status-propagation" is enabled by default. [1]https://review.opendev.org/c/openstack/neutron-lib/+/744208 [2]https://review.opendev.org/c/openstack/neutron/+/744210 Related-Bug: #1888487 Change-Id: I522707b36c73b3c5bfe0d644bd07774918660b68 --- openstackclient/network/v2/port.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index cf334bcdd..7803b657d 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -537,12 +537,12 @@ def get_parser(self, prog_name): uplink_status_group.add_argument( '--enable-uplink-status-propagation', action='store_true', - help=_("Enable uplink status propagate"), + help=_("Enable uplink status propagation (default)"), ) uplink_status_group.add_argument( '--disable-uplink-status-propagation', action='store_true', - help=_("Disable uplink status propagate (default)"), + help=_("Disable uplink status propagation"), ) parser.add_argument( '--project', From 1f407afe1c3d2f6e205d2e88c7af3fe3820d919f Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Sun, 2 Feb 2025 17:38:23 -0600 Subject: [PATCH 080/245] handle 'router create --flavor' option The '--flavor' option appears in the usage and arglist but is not actually parsed. The '--flavor-id' option is what is silently parsed. Since the goal is to allow the name or the id, this adds the '--flavor' option to being parsed. Closes-Bug: 2091731 Change-Id: Id83facd3825f472e7d864427699bd072d1c08779 Signed-off-by: Doug Goldstein --- openstackclient/network/v2/router.py | 3 + .../tests/unit/network/v2/test_router.py | 64 +++++++++++++++++-- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 5193abcb7..0deae3600 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -237,6 +237,9 @@ def _get_attrs(client_manager, parsed_args): if 'flavor_id' in parsed_args and parsed_args.flavor_id is not None: flavor = n_client.find_flavor(parsed_args.flavor_id) attrs['flavor_id'] = flavor.id + elif 'flavor' in parsed_args and parsed_args.flavor is not None: + flavor = n_client.find_flavor(parsed_args.flavor, ignore_missing=False) + attrs['flavor_id'] = flavor.id for attr in ('enable_default_route_bfd', 'enable_default_route_ecmp'): value = getattr(parsed_args, attr, None) diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index e420f8096..aa6de7d2e 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -384,7 +384,7 @@ def test_create_with_tags(self): def test_create_with_no_tag(self): self._test_create_with_tag(add_tags=False) - def test_create_with_flavor_id_or_name(self): + def test_create_with_flavor_id_id(self): _flavor = network_fakes.create_one_network_flavor() self.network_client.find_flavor = mock.Mock(return_value=_flavor) arglist = [ @@ -392,7 +392,6 @@ def test_create_with_flavor_id_or_name(self): '--flavor-id', _flavor.id, ] - arglist_with_name = [self.new_router.name, '--flavor-id', _flavor.name] verifylist = [ ('name', self.new_router.name), ('enable', True), @@ -412,18 +411,69 @@ def test_create_with_flavor_id_or_name(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) - self.network_client.create_router.reset_mock() - verifylist_w_name = [ + def test_create_with_flavor_id_name(self): + _flavor = network_fakes.create_one_network_flavor() + self.network_client.find_flavor = mock.Mock(return_value=_flavor) + arglist = [self.new_router.name, '--flavor-id', _flavor.name] + verifylist = [ ('name', self.new_router.name), ('enable', True), ('distributed', False), ('ha', False), ('flavor_id', _flavor.name), ] - parsed_args_w_name = self.check_parser( - self.cmd, arglist_with_name, verifylist_w_name + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.network_client.create_router.assert_called_once_with( + **{ + 'admin_state_up': True, + 'name': self.new_router.name, + 'flavor_id': _flavor.id, + } + ) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_create_with_flavor_id(self): + _flavor = network_fakes.create_one_network_flavor() + self.network_client.find_flavor = mock.Mock(return_value=_flavor) + arglist = [ + self.new_router.name, + '--flavor', + _flavor.id, + ] + verifylist = [ + ('name', self.new_router.name), + ('enable', True), + ('distributed', False), + ('ha', False), + ('flavor', _flavor.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.network_client.create_router.assert_called_once_with( + **{ + 'admin_state_up': True, + 'name': self.new_router.name, + 'flavor_id': _flavor.id, + } ) - columns, data = self.cmd.take_action(parsed_args_w_name) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_create_with_flavor_name(self): + _flavor = network_fakes.create_one_network_flavor() + self.network_client.find_flavor = mock.Mock(return_value=_flavor) + arglist = [self.new_router.name, '--flavor', _flavor.name] + verifylist = [ + ('name', self.new_router.name), + ('enable', True), + ('distributed', False), + ('ha', False), + ('flavor', _flavor.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) self.network_client.create_router.assert_called_once_with( **{ 'admin_state_up': True, From 426abbdc681d3f36f186fd685a3f1c0b6058f626 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Thu, 13 Feb 2025 12:48:56 +0000 Subject: [PATCH 081/245] [Neutron] Support ``uplink-status-propagation-updatable`` extension Added ``--enable-uplink-status-propagation`` option and ``--disable-uplink-status-propagation`` option to ``port update`` command. Now the Neutron extension "uplink-status-propagation-updatable" allows to update the related value in a port. That was implemented in the following patches (during 2025.1 Epoxy release): * https://review.opendev.org/c/openstack/neutron-lib/+/927820 * https://review.opendev.org/c/openstack/neutron-lib/+/936234 * https://review.opendev.org/c/openstack/neutron/+/931641 Related-Bug: #1722720 Change-Id: I99cdcf21438d6d85092c995b50cb10b26ae7c059 --- openstackclient/network/v2/port.py | 12 ++++++++ .../tests/unit/network/v2/test_port.py | 29 +++++++++++++++++++ ...ropagation_updatable-d1e155c19247b666.yaml | 5 ++++ 3 files changed, 46 insertions(+) create mode 100644 releasenotes/notes/port_uplink_status_propagation_updatable-d1e155c19247b666.yaml diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 7803b657d..dd202e458 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -1102,6 +1102,18 @@ def get_parser(self, prog_name): "(requires data plane status extension)" ), ) + uplink_status_group = parser.add_mutually_exclusive_group() + uplink_status_group.add_argument( + '--enable-uplink-status-propagation', + action='store_true', + help=_('Enable uplink status propagation'), + ) + uplink_status_group.add_argument( + '--disable-uplink-status-propagation', + action='store_true', + help=_('Disable uplink status propagation'), + ) + _tag.add_tag_option_to_parser_for_set(parser, _('port')) return parser diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 77561f78c..634f291cb 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -2633,6 +2633,35 @@ def test_set_trusted_true(self): def test_set_trusted_false(self): self._test_set_trusted_field(False) + def _test_set_uplink_status_propagation(self, uspropagation): + arglist = [self._port.id] + if uspropagation: + arglist += ['--enable-uplink-status-propagation'] + else: + arglist += ['--disable-uplink-status-propagation'] + + verifylist = [ + ('port', self._port.id), + ] + if uspropagation: + verifylist.append(('enable_uplink_status_propagation', True)) + else: + verifylist.append(('enable_uplink_status_propagation', False)) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network_client.update_port.assert_called_once_with( + self._port, **{'propagate_uplink_status': uspropagation} + ) + self.assertIsNone(result) + + def test_set_uplink_status_propagation_true(self): + self._test_set_uplink_status_propagation(True) + + def test_set_uplink_status_propagation_false(self): + self._test_set_uplink_status_propagation(False) + class TestShowPort(TestPort): # The port to show. diff --git a/releasenotes/notes/port_uplink_status_propagation_updatable-d1e155c19247b666.yaml b/releasenotes/notes/port_uplink_status_propagation_updatable-d1e155c19247b666.yaml new file mode 100644 index 000000000..0e7b77620 --- /dev/null +++ b/releasenotes/notes/port_uplink_status_propagation_updatable-d1e155c19247b666.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``--enable-uplink-status-propagation`` option and + ``--disable-uplink-status-propagation`` option to ``port update`` command. From 0ba77e672756bffc31c1e94eae7baaad556b17e3 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Wed, 19 Feb 2025 21:21:52 +0000 Subject: [PATCH 082/245] [Neutron] Fix the "port show" command for trunk details In [1], the "port list --long" command received a new column, showing the trunk subports related to a parent port. The problem of this patch is that the ``_formatters`` definition, that is shared with the "port show" command too, is changed. The "trunk_details" information presented in both commands differ: * In the "port list" command, only the subports are presented, in order to print a list of dictionaries without showing the ``trunk_id``. * In the "port show" command, it is presented all the trunk information, including the ``trunk_id``. This patch includes functional tests to validate the fix. [1]https://review.opendev.org/c/openstack/python-openstackclient/+/926611 Closes-Bug: #2098950 Change-Id: Ib1107fb3dbb025b39a7c55f90f5fe51ae433a72f --- openstackclient/network/v2/port.py | 5 +- .../tests/functional/network/v2/test_port.py | 79 +++++++++++++++++++ .../tests/unit/network/v2/test_port.py | 2 +- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index cf334bcdd..91573ffbd 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -65,8 +65,9 @@ def machine_readable(self): 'fixed_ips': format_columns.ListDictColumn, 'security_group_ids': format_columns.ListColumn, 'tags': format_columns.ListColumn, - 'trunk_details': SubPortColumn, } +_list_formatters = copy.deepcopy(_formatters) +_list_formatters.update({'trunk_details': SubPortColumn}) def _get_columns(item): @@ -952,7 +953,7 @@ def take_action(self, parsed_args): utils.get_item_properties( s, attrs, - formatters=_formatters, + formatters=_list_formatters, ) for s in data ), diff --git a/openstackclient/tests/functional/network/v2/test_port.py b/openstackclient/tests/functional/network/v2/test_port.py index ef46b1f42..453039550 100644 --- a/openstackclient/tests/functional/network/v2/test_port.py +++ b/openstackclient/tests/functional/network/v2/test_port.py @@ -289,3 +289,82 @@ def _create_resource_for_tag_test(self, name, args): f'{self.base_command} create --network {self.NETWORK_NAME} {args} {name}', parse_output=True, ) + + def _trunk_creation(self): + pport = uuid.uuid4().hex + sport1 = uuid.uuid4().hex + sport2 = uuid.uuid4().hex + trunk = uuid.uuid4().hex + json_output = self.openstack( + 'port create ' f'--network {self.NETWORK_NAME} {pport}', + parse_output=True, + ) + pport_id = json_output.get('id') + json_output = self.openstack( + 'port create ' f'--network {self.NETWORK_NAME} {sport1}', + parse_output=True, + ) + sport1_id = json_output.get('id') + json_output = self.openstack( + 'port create ' f'--network {self.NETWORK_NAME} {sport2}', + parse_output=True, + ) + sport2_id = json_output.get('id') + + self.openstack( + f'network trunk create --parent-port {pport} {trunk}', + ) + self.openstack( + f'network trunk set --subport port={sport1},' + f'segmentation-type=vlan,segmentation-id=100 {trunk}', + ) + self.openstack( + f'network trunk set --subport port={sport2},' + f'segmentation-type=vlan,segmentation-id=101 {trunk}', + ) + + # NOTE(ralonsoh): keep this order to first delete the trunk and then + # the ports. + self.addCleanup(self.openstack, f'port delete {pport_id}') + self.addCleanup(self.openstack, f'port delete {sport1_id}') + self.addCleanup(self.openstack, f'port delete {sport2_id}') + self.addCleanup(self.openstack, f'network trunk delete {trunk}') + + return pport_id, sport1_id, sport2_id + + def check_subports(self, subports, pport_id, sport1_id, sport2_id): + self.assertEqual(2, len(subports)) + for subport in subports: + if subport['port_id'] == sport1_id: + self.assertEqual(100, subport['segmentation_id']) + elif subport['port_id'] == sport2_id: + self.assertEqual(101, subport['segmentation_id']) + else: + self.fail( + f'Port {pport_id} does not have subport ' + f'{subport["port_id"]}' + ) + self.assertEqual('vlan', subport['segmentation_type']) + + def test_port_list_with_trunk(self): + pport_id, sport1_id, sport2_id = self._trunk_creation() + + # List all ports with "--long" flag to retrieve the trunk details + json_output = self.openstack( + 'port list --long', + parse_output=True, + ) + port = next(port for port in json_output if port['ID'] == pport_id) + subports = port['Trunk subports'] + self.check_subports(subports, pport_id, sport1_id, sport2_id) + + def test_port_show_with_trunk(self): + pport_id, sport1_id, sport2_id = self._trunk_creation() + + # List all ports with "--long" flag to retrieve the trunk details + port = self.openstack( + f'port show {pport_id}', + parse_output=True, + ) + subports = port['trunk_details']['sub_ports'] + self.check_subports(subports, pport_id, sport1_id, sport2_id) diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 77561f78c..a934593cf 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -122,7 +122,7 @@ def _get_common_cols_data(fake_port): fake_port.status, format_columns.ListColumn(fake_port.tags), fake_port.trusted, - port.SubPortColumn(fake_port.trunk_details), + fake_port.trunk_details, fake_port.updated_at, ) From 762a3b10d13716e2bc4cef121d5b70ced6958d42 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Wed, 19 Feb 2025 10:59:38 +0000 Subject: [PATCH 083/245] Add four new network agent types to the list command filter Added four new network agent types to the list method filter: * ``ovn-controller`` * ``ovn-controller-gateway`` * ``ovn-metadata`` * ``ovn-agent`` These agents are represented in the OVN network agent classes defined in [1]. The OVN agent names are defined in [2]. [1]https://github.com/openstack/neutron/blob/86f94de99aa08b1b4aadca8e90c6e79487171b8e/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py [2]https://github.com/openstack/neutron/blob/86f94de99aa08b1b4aadca8e90c6e79487171b8e/neutron/common/ovn/constants.py#L91-L94 Closes-Bug: #2097124 Change-Id: I117be7e60f67fdd94677cbaa65a3aff01e57bb5e --- openstackclient/network/v2/network_agent.py | 60 ++++++++----------- .../network-ovn-agents-bdfced3a6d25e7d2.yaml | 6 ++ 2 files changed, 32 insertions(+), 34 deletions(-) create mode 100644 releasenotes/notes/network-ovn-agents-bdfced3a6d25e7d2.yaml diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py index bccde7ef3..516b0f048 100644 --- a/openstackclient/network/v2/network_agent.py +++ b/openstackclient/network/v2/network_agent.py @@ -164,32 +164,37 @@ def take_action(self, parsed_args): # OSC minimum requirements include SDK 1.0. class ListNetworkAgent(command.Lister): _description = _("List network agents") + _supported_agents = { + 'bgp': 'BGP dynamic routing agent', + 'dhcp': 'DHCP agent', + 'open-vswitch': 'Open vSwitch agent', + 'linux-bridge': 'Linux bridge agent', + 'ofa': 'OFA driver agent', + 'l3': 'L3 agent', + 'loadbalancer': 'Loadbalancer agent', + 'metering': 'Metering agent', + 'metadata': 'Metadata agent', + 'macvtap': 'Macvtap agent', + 'nic': 'NIC Switch agent', + 'baremetal': 'Baremetal Node', + 'ovn-controller': 'OVN Controller agent', + 'ovn-controller-gateway': 'OVN Controller Gateway agent', + 'ovn-metadata': 'OVN Metadata agent', + 'ovn-agent': 'OVN Neutron agent', + } def get_parser(self, prog_name): parser = super().get_parser(prog_name) + supported_agents = ','.join(self._supported_agents.keys()) parser.add_argument( '--agent-type', metavar='', - choices=[ - "bgp", - "dhcp", - "open-vswitch", - "linux-bridge", - "ofa", - "l3", - "loadbalancer", - "metering", - "metadata", - "macvtap", - "nic", - "baremetal", - ], + choices=list(self._supported_agents.keys()), help=_( "List only agents with the specified agent type. " - "The supported agent types are: bgp, dhcp, open-vswitch, " - "linux-bridge, ofa, l3, loadbalancer, metering, " - "metadata, macvtap, nic, baremetal." - ), + "The supported agent types are: %(supported_agents)s." + ) + % {'supported_agents': supported_agents}, ) parser.add_argument( '--host', @@ -237,21 +242,6 @@ def take_action(self, parsed_args): 'Binary', ) - key_value = { - 'bgp': 'BGP dynamic routing agent', - 'dhcp': 'DHCP agent', - 'open-vswitch': 'Open vSwitch agent', - 'linux-bridge': 'Linux bridge agent', - 'ofa': 'OFA driver agent', - 'l3': 'L3 agent', - 'loadbalancer': 'Loadbalancer agent', - 'metering': 'Metering agent', - 'metadata': 'Metadata agent', - 'macvtap': 'Macvtap agent', - 'nic': 'NIC Switch agent', - 'baremetal': 'Baremetal Node', - } - filters = {} if parsed_args.network is not None: @@ -269,7 +259,9 @@ def take_action(self, parsed_args): data = client.routers_hosting_l3_agents(router) else: if parsed_args.agent_type is not None: - filters['agent_type'] = key_value[parsed_args.agent_type] + filters['agent_type'] = self._supported_agents[ + parsed_args.agent_type + ] if parsed_args.host is not None: filters['host'] = parsed_args.host diff --git a/releasenotes/notes/network-ovn-agents-bdfced3a6d25e7d2.yaml b/releasenotes/notes/network-ovn-agents-bdfced3a6d25e7d2.yaml new file mode 100644 index 000000000..7d9909fb0 --- /dev/null +++ b/releasenotes/notes/network-ovn-agents-bdfced3a6d25e7d2.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Added four new network agent types to the list method filter: + ``ovn-controller``, ``ovn-controller-gateway``, ``ovn-metadata`` and + ``ovn-agent``. From 1979c20ff0ce76d06c5b5669ef1128ac63b0386a Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Tue, 25 Feb 2025 10:25:09 -0800 Subject: [PATCH 084/245] Fix credential creation openstacksdk's Credential expects user_id and project_id, not user and project. Previously, we would send payloads like {'type': 'ec2', 'blob': '{"access": "s3-user1", "secret": "s3-secret1"}'} which Keystone would reject with 'user_id' is a required property Change-Id: I0544bef7df9247395f0726ea075112d6ac992252 --- openstackclient/identity/v3/credential.py | 4 ++-- openstackclient/tests/unit/identity/v3/test_credential.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py index 4784b836d..33297e9d1 100644 --- a/openstackclient/identity/v3/credential.py +++ b/openstackclient/identity/v3/credential.py @@ -88,10 +88,10 @@ def take_action(self, parsed_args): else: project = None credential = identity_client.create_credential( - user=user_id, + user_id=user_id, type=parsed_args.type, blob=parsed_args.data, - project=project, + project_id=project, ) return _format_credential(credential) diff --git a/openstackclient/tests/unit/identity/v3/test_credential.py b/openstackclient/tests/unit/identity/v3/test_credential.py index 2e90d2d78..f21fd71a0 100644 --- a/openstackclient/tests/unit/identity/v3/test_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_credential.py @@ -73,10 +73,10 @@ def test_credential_create_no_options(self): columns, data = self.cmd.take_action(parsed_args) kwargs = { - 'user': self.credential.user_id, + 'user_id': self.credential.user_id, 'type': self.credential.type, 'blob': self.credential.blob, - 'project': None, + 'project_id': None, } self.identity_sdk_client.create_credential.assert_called_once_with( **kwargs @@ -105,10 +105,10 @@ def test_credential_create_with_options(self): columns, data = self.cmd.take_action(parsed_args) kwargs = { - 'user': self.credential.user_id, + 'user_id': self.credential.user_id, 'type': self.credential.type, 'blob': self.credential.blob, - 'project': self.credential.project_id, + 'project_id': self.credential.project_id, } self.identity_sdk_client.create_credential.assert_called_once_with( **kwargs From f65e4835d3f59d72f6cbafe30252a3230fa5a46a Mon Sep 17 00:00:00 2001 From: Rajesh Tailor Date: Wed, 5 Mar 2025 19:55:59 +0530 Subject: [PATCH 085/245] Fix missing space in help messages This change fixes missing space in help messages to make those consistent and pretty rendering when calling help. Change-Id: I947374821a4dbb5e68651c0e72fc5fd2f938e6a1 --- openstackclient/compute/v2/server.py | 12 ++++++------ openstackclient/volume/v3/volume_backup.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index dcadca1b6..da632927b 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1402,7 +1402,7 @@ def get_parser(self, prog_name): default=[], help=_( 'File(s) to inject into image before boot ' - '(repeat option to set multiple files)' + '(repeat option to set multiple files) ' '(supported by --os-compute-api-version 2.57 or below)' ), ) @@ -3222,7 +3222,7 @@ def get_parser(self, prog_name): action='store_true', default=None, help=_( - 'Allow disk over-commit on the destination host' + 'Allow disk over-commit on the destination host ' '(supported with --os-compute-api-version 2.24 or below)' ), ) @@ -3231,7 +3231,7 @@ def get_parser(self, prog_name): dest='disk_overcommit', action='store_false', help=_( - 'Do not over-commit disk on the destination host (default)' + 'Do not over-commit disk on the destination host (default) ' '(supported with --os-compute-api-version 2.24 or below)' ), ) @@ -3434,7 +3434,7 @@ def get_parser(self, prog_name): '--image', metavar='', help=_( - 'Recreate server from the specified image (name or ID).' + 'Recreate server from the specified image (name or ID). ' 'Defaults to the currently used one.' ), ) @@ -4252,7 +4252,7 @@ def get_parser(self, prog_name): '--revert', action="store_true", help=_( - '**Deprecated** Restore server state before resize' + '**Deprecated** Restore server state before resize. ' "Replaced by the 'openstack server resize revert' and " "'openstack server migration revert' commands" ), @@ -5005,7 +5005,7 @@ def get_parser(self, prog_name): action='store_true', default=boolenv('ALL_PROJECTS'), help=_( - 'Stop server(s) in another project by name (admin only)' + 'Stop server(s) in another project by name (admin only) ' '(can be specified using the ALL_PROJECTS envvar)' ), ) diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py index dce5646a9..25451d356 100644 --- a/openstackclient/volume/v3/volume_backup.py +++ b/openstackclient/volume/v3/volume_backup.py @@ -466,7 +466,7 @@ def get_parser(self, prog_name): '--name', metavar='', help=_( - 'New backup name' + 'New backup name ' '(supported by --os-volume-api-version 3.9 or above)' ), ) From 1458330d3b9cae0961c8cbce95a365f62fda759f Mon Sep 17 00:00:00 2001 From: Vladimir Kozhukalov Date: Mon, 10 Mar 2025 11:33:52 -0500 Subject: [PATCH 086/245] identity: Fix 'trust' commands to work with SDK Closes-Bug: #2102039 Change-Id: I632937e06683cc76e78390a4e6f3de4e3c4f1f87 --- openstackclient/identity/v3/trust.py | 97 ++++++++++++++----- .../tests/unit/identity/v3/test_trust.py | 7 +- 2 files changed, 78 insertions(+), 26 deletions(-) diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py index 447a57f2b..41d01fa7b 100644 --- a/openstackclient/identity/v3/trust.py +++ b/openstackclient/identity/v3/trust.py @@ -123,37 +123,67 @@ def take_action(self, parsed_args): # pointless, and trusts are immutable, so let's enforce it at the # client level. try: - trustor_id = identity_client.find_user( - parsed_args.trustor, parsed_args.trustor_domain - ).id - kwargs['trustor_id'] = trustor_id + if parsed_args.trustor_domain: + trustor_domain_id = identity_client.find_domain( + parsed_args.trustor_domain, ignore_missing=False + ).id + trustor_id = identity_client.find_user( + parsed_args.trustor, + ignore_missing=False, + domain_id=trustor_domain_id, + ).id + else: + trustor_id = identity_client.find_user( + parsed_args.trustor, ignore_missing=False + ).id + kwargs['trustor_user_id'] = trustor_id except sdk_exceptions.ForbiddenException: - kwargs['trustor_id'] = parsed_args.trustor + kwargs['trustor_user_id'] = parsed_args.trustor try: - trustee_id = identity_client.find_user( - parsed_args.trustee, parsed_args.trustee_domain - ).id - kwargs['trustee_id'] = trustee_id + if parsed_args.trustee_domain: + trustee_domain_id = identity_client.find_domain( + parsed_args.trustee_domain, ignore_missing=False + ).id + trustee_id = identity_client.find_user( + parsed_args.trustee, + ignore_missing=False, + domain_id=trustee_domain_id, + ).id + else: + trustee_id = identity_client.find_user( + parsed_args.trustee, ignore_missing=False + ).id + kwargs['trustee_user_id'] = trustee_id except sdk_exceptions.ForbiddenException: - kwargs['trustee_id'] = parsed_args.trustee + kwargs['trustee_user_id'] = parsed_args.trustee try: - project_id = identity_client.find_project( - parsed_args.project, parsed_args.project_domain - ).id + if parsed_args.project_domain: + project_domain_id = identity_client.find_domain( + parsed_args.project_domain, ignore_missing=False + ).id + project_id = identity_client.find_project( + parsed_args.project, + ignore_missing=False, + domain_id=project_domain_id, + ).id + else: + project_id = identity_client.find_project( + parsed_args.project, ignore_missing=False + ).id kwargs['project_id'] = project_id except sdk_exceptions.ForbiddenException: kwargs['project_id'] = parsed_args.project - role_ids = [] + roles = [] for role in parsed_args.roles: try: role_id = identity_client.find_role(role).id except sdk_exceptions.ForbiddenException: role_id = role - role_ids.append(role_id) - kwargs['roles'] = role_ids + roles.append({"id": role_id}) + kwargs['roles'] = roles if parsed_args.expiration: expires_at = datetime.datetime.strptime( @@ -161,8 +191,7 @@ def take_action(self, parsed_args): ) kwargs['expires_at'] = expires_at - if parsed_args.is_impersonation: - kwargs['is_impersonation'] = parsed_args.is_impersonation + kwargs['impersonation'] = bool(parsed_args.is_impersonation) trust = identity_client.create_trust(**kwargs) @@ -289,9 +318,19 @@ def take_action(self, parsed_args): trustor = None if parsed_args.trustor: try: - trustor_id = identity_client.find_user( - parsed_args.trustor, parsed_args.trustor_domain - ).id + if parsed_args.trustor_domain: + trustor_domain_id = identity_client.find_domain( + parsed_args.trustor_domain, ignore_missing=False + ).id + trustor_id = identity_client.find_user( + parsed_args.trustor, + ignore_missing=False, + domain_id=trustor_domain_id, + ).id + else: + trustor_id = identity_client.find_user( + parsed_args.trustor, ignore_missing=False + ).id trustor = trustor_id except sdk_exceptions.ForbiddenException: trustor = parsed_args.trustor @@ -299,9 +338,19 @@ def take_action(self, parsed_args): trustee = None if parsed_args.trustee: try: - trustee_id = identity_client.find_user( - parsed_args.trustee, parsed_args.trustee_domain - ).id + if parsed_args.trustee_domain: + trustee_domain_id = identity_client.find_domain( + parsed_args.trustee_domain, ignore_missing=False + ).id + trustee_id = identity_client.find_user( + parsed_args.trustee, + ignore_missing=False, + domain_id=trustee_domain_id, + ).id + else: + trustee_id = identity_client.find_user( + parsed_args.trustee, ignore_missing=False + ).id trustee = trustee_id except sdk_exceptions.ForbiddenException: trustee = parsed_args.trustee diff --git a/openstackclient/tests/unit/identity/v3/test_trust.py b/openstackclient/tests/unit/identity/v3/test_trust.py index 07776fa45..5c14b7ad9 100644 --- a/openstackclient/tests/unit/identity/v3/test_trust.py +++ b/openstackclient/tests/unit/identity/v3/test_trust.py @@ -70,12 +70,15 @@ def test_trust_create_basic(self): # Set expected values kwargs = { 'project_id': self.project.id, - 'roles': [self.role.id], + 'roles': [{'id': self.role.id}], + 'impersonation': False, } # TrustManager.create(trustee_id, trustor_id, impersonation=, # project=, role_names=, expires_at=) self.identity_sdk_client.create_trust.assert_called_with( - trustor_id=self.user.id, trustee_id=self.user.id, **kwargs + trustor_user_id=self.user.id, + trustee_user_id=self.user.id, + **kwargs, ) collist = ( From f1bd417861aaa584c85db1c97818d81c1309c6af Mon Sep 17 00:00:00 2001 From: Tobias Urdin Date: Wed, 12 Mar 2025 17:03:26 +0100 Subject: [PATCH 087/245] Add device ID and device owner to port unset This adds support to unset the device_id and device_owner property on a port. Change-Id: I43b1ea63e3a119f57162948e128a85f8ba323d41 --- openstackclient/network/v2/port.py | 16 ++++++++ .../tests/unit/network/v2/test_port.py | 40 +++++++++++++++++++ ...-device-id-and-owner-9fce242155c82992.yaml | 5 +++ 3 files changed, 61 insertions(+) create mode 100644 releasenotes/notes/port-unset-device-id-and-owner-9fce242155c82992.yaml diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 3579194bf..01e2e4faa 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -1317,6 +1317,18 @@ def get_parser(self, prog_name): default=False, help=_("Clear hints for the port."), ) + parser.add_argument( + '--device', + action='store_true', + default=False, + help=_("Clear device ID for the port."), + ) + parser.add_argument( + '--device-owner', + action='store_true', + default=False, + help=_("Clear device owner for the port."), + ) _tag.add_tag_option_to_parser_for_unset(parser, _('port')) parser.add_argument( 'port', @@ -1383,6 +1395,10 @@ def take_action(self, parsed_args): attrs['binding:host_id'] = None if parsed_args.hints: attrs['hints'] = None + if parsed_args.device: + attrs['device_id'] = '' + if parsed_args.device_owner: + attrs['device_owner'] = '' attrs.update( self._parse_extra_properties(parsed_args.extra_properties) diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 3f521a42e..d66eb1bf4 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -3015,3 +3015,43 @@ def test_unset_hints(self): **{'hints': None}, ) self.assertIsNone(result) + + def test_unset_device(self): + testport = network_fakes.create_one_port() + self.network_client.find_port = mock.Mock(return_value=testport) + arglist = [ + '--device', + testport.name, + ] + verifylist = [ + ('device', True), + ('port', testport.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.network_client.update_port.assert_called_once_with( + testport, + **{'device_id': ''}, + ) + self.assertIsNone(result) + + def test_unset_device_owner(self): + testport = network_fakes.create_one_port() + self.network_client.find_port = mock.Mock(return_value=testport) + arglist = [ + '--device-owner', + testport.name, + ] + verifylist = [ + ('device_owner', True), + ('port', testport.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.network_client.update_port.assert_called_once_with( + testport, + **{'device_owner': ''}, + ) + self.assertIsNone(result) diff --git a/releasenotes/notes/port-unset-device-id-and-owner-9fce242155c82992.yaml b/releasenotes/notes/port-unset-device-id-and-owner-9fce242155c82992.yaml new file mode 100644 index 000000000..e14baacdd --- /dev/null +++ b/releasenotes/notes/port-unset-device-id-and-owner-9fce242155c82992.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added ``--device`` and ``--device-owner`` parameter to the + ``port unset`` command. From 1efca54465a9a311dee09a96287676b08f9e62b3 Mon Sep 17 00:00:00 2001 From: Pavlo Shchelokovskyy Date: Wed, 29 Jan 2025 14:29:47 +0000 Subject: [PATCH 088/245] Fix image import --disallow-failure flag the flag should store False to 'allow_failure', not True. Also, make the --allow-failure and --disallow-failure flags mutually exclusive. Change-Id: I03699e14d4d69d9f08caab647293732fc211dbad --- openstackclient/image/v2/image.py | 7 ++-- .../tests/unit/image/v2/test_image.py | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index fe237e4e1..95c56f23f 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -1689,7 +1689,8 @@ def get_parser(self, prog_name): "'copy-image' import method)" ), ) - parser.add_argument( + allow_failure_group = parser.add_mutually_exclusive_group() + allow_failure_group.add_argument( '--allow-failure', action='store_true', dest='allow_failure', @@ -1700,9 +1701,9 @@ def get_parser(self, prog_name): 'Only usable with --stores or --all-stores' ), ) - parser.add_argument( + allow_failure_group.add_argument( '--disallow-failure', - action='store_true', + action='store_false', dest='allow_failure', default=True, help=_( diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index 69a65a59c..f563828cb 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -2092,6 +2092,38 @@ def test_import_image__copy_image(self): all_stores_must_succeed=False, ) + def test_import_image__copy_image_disallow_failure(self): + self.image.status = 'active' + arglist = [ + self.image.name, + '--method', + 'copy-image', + '--store', + 'fast', + '--disallow-failure', + ] + verifylist = [ + ('image', self.image.name), + ('import_method', 'copy-image'), + ('stores', ['fast']), + ('allow_failure', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.image_client.import_image.assert_called_once_with( + self.image, + method='copy-image', + uri=None, + remote_region=None, + remote_image_id=None, + remote_service_interface=None, + stores=['fast'], + all_stores=None, + all_stores_must_succeed=True, + ) + def test_import_image__glance_download(self): arglist = [ self.image.name, From 702a37c7ca74e88c80a6450d97eac9584ff4fd3c Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Mon, 17 Mar 2025 09:23:09 -0700 Subject: [PATCH 089/245] Add libpcre3-dev in bindep.txt for pcre.h Doc job is going to run on Ubuntu Noble[1] and we need libpcre3-dev package for pcre.h [1] https://review.opendev.org/c/openstack/openstack-zuul-jobs/+/935459 Change-Id: I0fe73c02b093805d8eb1b15303f92633fad809cb --- bindep.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/bindep.txt b/bindep.txt index 4c90a026f..83ebf2d43 100644 --- a/bindep.txt +++ b/bindep.txt @@ -8,3 +8,4 @@ libffi-dev [compile test platform:dpkg] libssl-dev [compile test platform:dpkg] python3-dev [compile test platform:dpkg] python3-devel [compile test platform:rpm] +libpcre3-dev [test platform:dpkg] From f2df31387bd654ff45a8364d8e09ba549d84fa16 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Tue, 18 Mar 2025 09:01:53 +0000 Subject: [PATCH 090/245] Update master for stable/2025.1 Add file to the reno documentation build to show release notes for stable/2025.1. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/2025.1. Sem-Ver: feature Change-Id: I2789e8605f5e9bae63382ca9e822bbc3e2241f36 --- releasenotes/source/2025.1.rst | 6 ++++++ releasenotes/source/index.rst | 1 + 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/2025.1.rst diff --git a/releasenotes/source/2025.1.rst b/releasenotes/source/2025.1.rst new file mode 100644 index 000000000..3add0e53a --- /dev/null +++ b/releasenotes/source/2025.1.rst @@ -0,0 +1,6 @@ +=========================== +2025.1 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2025.1 diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 991d079e6..a9c057978 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ OpenStackClient Release Notes :maxdepth: 1 unreleased + 2025.1 2024.2 2024.1 2023.2 From 290bc580e6576c7dc4843dca2b7374f6ccd6a8d0 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 19 Feb 2025 20:15:43 +0000 Subject: [PATCH 091/245] Prepare for ruff bump Change-Id: Ia9c402edebc8537d5019d18920b6679b05ea4378 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/agent.py | 2 +- openstackclient/compute/v2/aggregate.py | 7 +-- openstackclient/compute/v2/flavor.py | 16 +++---- openstackclient/compute/v2/keypair.py | 4 +- openstackclient/compute/v2/server.py | 11 ++--- openstackclient/compute/v2/service.py | 12 ++--- openstackclient/compute/v2/usage.py | 6 +-- openstackclient/identity/client.py | 3 +- openstackclient/identity/common.py | 2 +- openstackclient/identity/v2_0/ec2creds.py | 7 +-- openstackclient/identity/v2_0/endpoint.py | 7 +-- openstackclient/identity/v2_0/project.py | 7 +-- openstackclient/identity/v2_0/role.py | 2 +- openstackclient/identity/v2_0/service.py | 9 ++-- openstackclient/identity/v2_0/user.py | 2 +- openstackclient/identity/v3/access_rule.py | 7 +-- openstackclient/identity/v3/consumer.py | 7 +-- openstackclient/identity/v3/credential.py | 13 +++--- openstackclient/identity/v3/domain.py | 2 +- openstackclient/identity/v3/ec2creds.py | 7 +-- openstackclient/identity/v3/endpoint.py | 17 ++++--- openstackclient/identity/v3/endpoint_group.py | 2 +- openstackclient/identity/v3/group.py | 2 +- .../identity/v3/identity_provider.py | 3 +- openstackclient/identity/v3/limit.py | 4 +- openstackclient/identity/v3/mapping.py | 7 +-- openstackclient/identity/v3/policy.py | 4 +- openstackclient/identity/v3/project.py | 7 +-- openstackclient/identity/v3/region.py | 7 +-- .../identity/v3/registered_limit.py | 3 +- openstackclient/identity/v3/role.py | 4 +- openstackclient/identity/v3/service.py | 7 +-- .../identity/v3/service_provider.py | 9 ++-- openstackclient/identity/v3/tag.py | 4 +- openstackclient/identity/v3/token.py | 3 +- openstackclient/identity/v3/trust.py | 2 +- openstackclient/identity/v3/user.py | 2 +- openstackclient/image/v1/image.py | 3 +- openstackclient/image/v2/cache.py | 6 +-- openstackclient/image/v2/image.py | 19 +++----- .../image/v2/metadef_namespaces.py | 7 +-- openstackclient/network/client.py | 4 +- openstackclient/network/common.py | 3 +- openstackclient/network/v2/address_group.py | 11 ++--- openstackclient/network/v2/address_scope.py | 5 +-- openstackclient/network/v2/floating_ip.py | 15 ++----- .../network/v2/floating_ip_port_forwarding.py | 2 +- .../network/v2/l3_conntrack_helper.py | 9 ++-- openstackclient/network/v2/local_ip.py | 21 ++++----- .../network/v2/local_ip_association.py | 6 +-- openstackclient/network/v2/ndp_proxy.py | 4 +- openstackclient/network/v2/network.py | 7 +-- openstackclient/network/v2/network_agent.py | 2 +- .../network/v2/network_flavor_profile.py | 2 +- openstackclient/network/v2/network_meter.py | 4 +- .../network/v2/network_meter_rule.py | 7 +-- .../network/v2/network_qos_policy.py | 2 +- openstackclient/network/v2/network_rbac.py | 2 +- openstackclient/network/v2/network_segment.py | 2 +- .../network/v2/network_segment_range.py | 11 ++--- openstackclient/network/v2/network_trunk.py | 2 +- openstackclient/network/v2/port.py | 5 +-- openstackclient/network/v2/router.py | 4 +- openstackclient/network/v2/security_group.py | 5 +-- .../network/v2/security_group_rule.py | 2 +- openstackclient/network/v2/subnet.py | 4 +- openstackclient/network/v2/subnet_pool.py | 4 +- .../functional/compute/v2/test_server.py | 5 +-- .../compute/v2/test_server_event.py | 2 +- .../tests/functional/identity/v2/test_user.py | 2 +- .../tests/functional/identity/v3/common.py | 11 ++--- .../v3/test_application_credential.py | 20 ++++----- .../functional/identity/v3/test_endpoint.py | 6 +-- .../functional/identity/v3/test_group.py | 6 +-- .../functional/identity/v3/test_limit.py | 8 ++-- .../functional/identity/v3/test_project.py | 19 +++----- .../functional/identity/v3/test_region.py | 4 +- .../identity/v3/test_registered_limit.py | 6 +-- .../tests/functional/identity/v3/test_role.py | 2 +- .../identity/v3/test_role_assignment.py | 44 ++++++------------- .../tests/functional/identity/v3/test_user.py | 16 +++---- .../network/v2/test_l3_conntrack_helper.py | 3 +- .../network/v2/test_network_ndp_proxy.py | 5 +-- .../network/v2/test_network_trunk.py | 2 +- .../tests/functional/network/v2/test_port.py | 10 ++--- .../tests/unit/common/test_command.py | 2 +- .../tests/unit/compute/v2/test_server.py | 4 +- .../tests/unit/identity/v2_0/fakes.py | 2 +- .../unit/identity/v3/test_access_rule.py | 4 +- .../v3/test_application_credential.py | 2 +- .../tests/unit/identity/v3/test_mappings.py | 4 +- .../tests/unit/integ/cli/test_shell.py | 3 +- .../unit/network/v2/test_network_trunk.py | 4 +- .../tests/unit/network/v2/test_port.py | 5 +-- .../tests/unit/object/v1/test_object_all.py | 7 +-- .../unit/volume/v1/test_transfer_request.py | 2 +- .../tests/unit/volume/v1/test_type.py | 2 +- .../tests/unit/volume/v2/test_volume.py | 2 +- .../volume/v2/test_volume_transfer_request.py | 2 +- .../tests/unit/volume/v3/test_volume.py | 2 +- .../unit/volume/v3/test_volume_snapshot.py | 3 +- .../volume/v3/test_volume_transfer_request.py | 2 +- openstackclient/volume/client.py | 4 +- openstackclient/volume/v1/qos_specs.py | 3 +- openstackclient/volume/v1/volume.py | 17 +++---- openstackclient/volume/v1/volume_backup.py | 5 +-- openstackclient/volume/v1/volume_snapshot.py | 15 +++---- openstackclient/volume/v1/volume_type.py | 9 ++-- .../volume/v2/consistency_group.py | 12 ++--- .../volume/v2/consistency_group_snapshot.py | 3 +- openstackclient/volume/v2/qos_specs.py | 3 +- openstackclient/volume/v2/volume.py | 22 +++------- openstackclient/volume/v2/volume_backup.py | 3 +- openstackclient/volume/v2/volume_snapshot.py | 17 ++++--- openstackclient/volume/v2/volume_type.py | 27 ++++-------- .../volume/v3/block_storage_manage.py | 4 +- openstackclient/volume/v3/volume.py | 19 +++----- openstackclient/volume/v3/volume_backup.py | 3 +- openstackclient/volume/v3/volume_snapshot.py | 7 +-- openstackclient/volume/v3/volume_type.py | 27 ++++-------- 120 files changed, 333 insertions(+), 490 deletions(-) diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py index 8f897ca8a..799b828bb 100644 --- a/openstackclient/compute/v2/agent.py +++ b/openstackclient/compute/v2/agent.py @@ -114,7 +114,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.id) - msg = _("%(result)s of %(total)s agents failed " "to delete.") % { + msg = _("%(result)s of %(total)s agents failed to delete.") % { 'result': result, 'total': total, } diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index 4cbce3c81..167b2e82c 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -168,9 +168,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.aggregate) - msg = _( - "%(result)s of %(total)s aggregates failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s aggregates failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index f94b3162e..03092417c 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -190,8 +190,7 @@ def take_action(self, parsed_args): compute_client.flavor_add_tenant_access(flavor.id, project_id) except Exception as e: msg = _( - "Failed to add project %(project)s access to " - "flavor: %(e)s" + "Failed to add project %(project)s access to flavor: %(e)s" ) LOG.error(msg, {'project': parsed_args.project, 'e': e}) if parsed_args.properties: @@ -242,7 +241,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.flavor) - msg = _("%(result)s of %(total)s flavors failed " "to delete.") % { + msg = _("%(result)s of %(total)s flavors failed to delete.") % { 'result': result, 'total': total, } @@ -404,9 +403,7 @@ def get_parser(self, prog_name): parser.add_argument( '--project', metavar='', - help=_( - 'Set flavor access to project (name or ID) ' '(admin only)' - ), + help=_('Set flavor access to project (name or ID) (admin only)'), ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( @@ -483,7 +480,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) @@ -560,8 +557,7 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - 'Remove flavor access from project (name or ID) ' - '(admin only)' + 'Remove flavor access from project (name or ID) (admin only)' ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -612,5 +608,5 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index 8e4389953..377eb903c 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -258,13 +258,13 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error( - _("Failed to delete key with name " "'%(name)s': %(e)s"), + _("Failed to delete 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 keys failed " "to delete.") % { + msg = _("%(result)s of %(total)s keys failed to delete.") % { 'result': result, 'total': total, } diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index dcadca1b6..244c72a45 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1617,8 +1617,7 @@ def _match_image(image_api, wanted_properties): image = images[0] else: msg = _( - 'No images match the property expected by ' - '--image-property' + 'No images match the property expected by --image-property' ) raise exceptions.CommandError(msg) @@ -1902,7 +1901,7 @@ def _match_image(image_api, wanted_properties): # Default to empty list if nothing was specified and let nova # decide the default behavior. - networks: ty.Union[str, ty.List[ty.Dict[str, str]], None] = [] + networks: ty.Union[str, list[dict[str, str]], None] = [] if 'auto' in parsed_args.nics or 'none' in parsed_args.nics: if len(parsed_args.nics) > 1: @@ -2424,8 +2423,7 @@ def get_parser(self, prog_name): parser.add_argument( '--key-name', help=_( - 'Search by keypair name ' - '(admin only before microversion 2.83)' + 'Search by keypair name (admin only before microversion 2.83)' ), ) config_drive_group = parser.add_mutually_exclusive_group() @@ -4475,8 +4473,7 @@ def get_parser(self, prog_name): password_group.add_argument( '--password', help=_( - 'Set the server password. ' - 'This option requires cloud support.' + 'Set the server password. This option requires cloud support.' ), ) password_group.add_argument( diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index b96386e03..b0d77fbad 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -70,7 +70,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.service) msg = _( - "%(result)s of %(total)s compute services failed " "to delete." + "%(result)s of %(total)s compute services failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -153,8 +153,7 @@ def get_parser(self, prog_name): "service", metavar="", help=_( - "Name of service (Binary name), for example " - "``nova-compute``" + "Name of service (Binary name), for example ``nova-compute``" ), ) enabled_group = parser.add_mutually_exclusive_group() @@ -281,9 +280,7 @@ def take_action(self, parsed_args): force_down = False if force_down is not None: if not sdk_utils.supports_microversion(compute_client, '2.11'): - msg = _( - '--os-compute-api-version 2.11 or later is ' 'required' - ) + msg = _('--os-compute-api-version 2.11 or later is required') raise exceptions.CommandError(msg) try: compute_client.update_service_forced_down( @@ -299,7 +296,6 @@ def take_action(self, parsed_args): if result > 0: msg = _( - "Compute service %(service)s of host %(host)s failed to " - "set." + "Compute service %(service)s of host %(host)s failed to set." ) % {"service": parsed_args.service, "host": parsed_args.host} raise exceptions.CommandError(msg) diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index 5d91358b9..b4bfedac9 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -115,8 +115,7 @@ def get_parser(self, prog_name): metavar="", default=None, help=_( - "Usage range start date, ex 2012-01-20" - " (default: 4 weeks ago)" + "Usage range start date, ex 2012-01-20 (default: 4 weeks ago)" ), ) parser.add_argument( @@ -222,8 +221,7 @@ def get_parser(self, prog_name): metavar="", default=None, help=_( - "Usage range start date, ex 2012-01-20" - " (default: 4 weeks ago)" + "Usage range start date, ex 2012-01-20 (default: 4 weeks ago)" ), ) parser.add_argument( diff --git a/openstackclient/identity/client.py b/openstackclient/identity/client.py index 5ed81e580..013c599da 100644 --- a/openstackclient/identity/client.py +++ b/openstackclient/identity/client.py @@ -64,8 +64,7 @@ def build_option_parser(parser): metavar='', default=utils.env('OS_IDENTITY_API_VERSION'), help=_( - 'Identity API version, default=%s ' - '(Env: OS_IDENTITY_API_VERSION)' + 'Identity API version, default=%s (Env: OS_IDENTITY_API_VERSION)' ) % DEFAULT_API_VERSION, ) diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index 9f4402f5b..9a7f11d1a 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -335,7 +335,7 @@ def add_inherited_option_to_parser(parser): action='store_true', default=False, help=_( - 'Specifies if the role grant is inheritable to the sub ' 'projects' + 'Specifies if the role grant is inheritable to the sub projects' ), ) diff --git a/openstackclient/identity/v2_0/ec2creds.py b/openstackclient/identity/v2_0/ec2creds.py index 6970f46dc..25ee8a75d 100644 --- a/openstackclient/identity/v2_0/ec2creds.py +++ b/openstackclient/identity/v2_0/ec2creds.py @@ -128,9 +128,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.access_keys) - msg = _( - "%(result)s of %(total)s EC2 keys failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s EC2 keys failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v2_0/endpoint.py b/openstackclient/identity/v2_0/endpoint.py index ca11164b6..94edc4cbe 100644 --- a/openstackclient/identity/v2_0/endpoint.py +++ b/openstackclient/identity/v2_0/endpoint.py @@ -111,9 +111,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.endpoints) - msg = _( - "%(result)s of %(total)s endpoints failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s endpoints failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py index 8920e0e16..1acaa29e5 100644 --- a/openstackclient/identity/v2_0/project.py +++ b/openstackclient/identity/v2_0/project.py @@ -140,9 +140,10 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.projects) - msg = _( - "%(errors)s of %(total)s projects failed " "to delete." - ) % {'errors': errors, 'total': total} + msg = _("%(errors)s of %(total)s projects failed to delete.") % { + 'errors': errors, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py index 971c61a7f..1faab2e5a 100644 --- a/openstackclient/identity/v2_0/role.py +++ b/openstackclient/identity/v2_0/role.py @@ -143,7 +143,7 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.roles) - msg = _("%(errors)s of %(total)s roles failed " "to delete.") % { + msg = _("%(errors)s of %(total)s roles failed to delete.") % { 'errors': errors, 'total': total, } diff --git a/openstackclient/identity/v2_0/service.py b/openstackclient/identity/v2_0/service.py index 362dc4515..465f002b1 100644 --- a/openstackclient/identity/v2_0/service.py +++ b/openstackclient/identity/v2_0/service.py @@ -100,9 +100,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.services) - msg = _( - "%(result)s of %(total)s services failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s services failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) @@ -164,7 +165,7 @@ def take_action(self, parsed_args): return zip(*sorted(info.items())) msg = _( - "No service catalog with a type, name or ID of '%s' " "exists." + "No service catalog with a type, name or ID of '%s' exists." ) % (parsed_args.service) raise exceptions.CommandError(msg) else: diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py index 3625adc7c..b48c95e27 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -194,7 +194,7 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.users) - msg = _("%(errors)s of %(total)s users failed " "to delete.") % { + msg = _("%(errors)s of %(total)s users failed to delete.") % { 'errors': errors, 'total': total, } diff --git a/openstackclient/identity/v3/access_rule.py b/openstackclient/identity/v3/access_rule.py index 40bb4cc11..367c64901 100644 --- a/openstackclient/identity/v3/access_rule.py +++ b/openstackclient/identity/v3/access_rule.py @@ -54,17 +54,14 @@ def take_action(self, parsed_args): except Exception as e: errors += 1 LOG.error( - _( - "Failed to delete access rule with " - "ID '%(ac)s': %(e)s" - ), + _("Failed to delete access rule with ID '%(ac)s': %(e)s"), {'ac': ac, 'e': e}, ) if errors > 0: total = len(parsed_args.access_rule) msg = _( - "%(errors)s of %(total)s access rules failed " "to delete." + "%(errors)s of %(total)s access rules failed to delete." ) % {'errors': errors, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/consumer.py b/openstackclient/identity/v3/consumer.py index 924b5726d..933f48aa7 100644 --- a/openstackclient/identity/v3/consumer.py +++ b/openstackclient/identity/v3/consumer.py @@ -82,9 +82,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.consumer) - msg = _( - "%(result)s of %(total)s consumers failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s consumers failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py index 33297e9d1..7d9c7c46c 100644 --- a/openstackclient/identity/v3/credential.py +++ b/openstackclient/identity/v3/credential.py @@ -70,8 +70,7 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - 'Project which limits the scope of ' - 'the credential (name or ID)' + 'Project which limits the scope of the credential (name or ID)' ), ) return parser @@ -128,9 +127,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.credential) - msg = _( - "%(result)s of %(total)s credential failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s credential failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) @@ -219,8 +219,7 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - 'Project which limits the scope of ' - 'the credential (name or ID)' + 'Project which limits the scope of the credential (name or ID)' ), ) return parser diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index aaa270830..215f1be8b 100644 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -124,7 +124,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.domain) - msg = _("%(result)s of %(total)s domains failed " "to delete.") % { + msg = _("%(result)s of %(total)s domains failed to delete.") % { 'result': result, 'total': total, } diff --git a/openstackclient/identity/v3/ec2creds.py b/openstackclient/identity/v3/ec2creds.py index a9fbe1ae4..84700f496 100644 --- a/openstackclient/identity/v3/ec2creds.py +++ b/openstackclient/identity/v3/ec2creds.py @@ -156,9 +156,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.access_key) - msg = _( - "%(result)s of %(total)s EC2 keys failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s EC2 keys failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index 6e77bdf87..72dd51570 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -44,15 +44,13 @@ def get_parser(self, prog_name): 'endpoint', metavar='', help=_( - 'Endpoint to associate with ' 'specified project (name or ID)' + 'Endpoint to associate with specified project (name or ID)' ), ) parser.add_argument( 'project', metavar='', - help=_( - 'Project to associate with ' 'specified endpoint name or ID)' - ), + help=_('Project to associate with specified endpoint name or ID)'), ) common.add_project_domain_option_to_parser(parser) return parser @@ -167,9 +165,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.endpoint) - msg = _( - "%(result)s of %(total)s endpoints failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s endpoints failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) @@ -290,14 +289,14 @@ def get_parser(self, prog_name): 'endpoint', metavar='', help=_( - 'Endpoint to dissociate from ' 'specified project (name or ID)' + 'Endpoint to dissociate from specified project (name or ID)' ), ) parser.add_argument( 'project', metavar='', help=_( - 'Project to dissociate from ' 'specified endpoint name or ID)' + 'Project to dissociate from specified endpoint name or ID)' ), ) common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/identity/v3/endpoint_group.py b/openstackclient/identity/v3/endpoint_group.py index b5c2631f0..e4611a167 100644 --- a/openstackclient/identity/v3/endpoint_group.py +++ b/openstackclient/identity/v3/endpoint_group.py @@ -168,7 +168,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.endpointgroup) msg = _( - "%(result)s of %(total)s endpointgroups failed " "to delete." + "%(result)s of %(total)s endpointgroups failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index d9c3b93b8..62a5c2239 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -230,7 +230,7 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.groups) - msg = _("%(errors)s of %(total)s groups failed " "to delete.") % { + msg = _("%(errors)s of %(total)s groups failed to delete.") % { 'errors': errors, 'total': total, } diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py index dad716569..d0c324362 100644 --- a/openstackclient/identity/v3/identity_provider.py +++ b/openstackclient/identity/v3/identity_provider.py @@ -174,8 +174,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.identity_provider) msg = _( - "%(result)s of %(total)s identity providers failed" - " to delete." + "%(result)s of %(total)s identity providers failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/limit.py b/openstackclient/identity/v3/limit.py index 44a3f6356..333078d03 100644 --- a/openstackclient/identity/v3/limit.py +++ b/openstackclient/identity/v3/limit.py @@ -281,13 +281,13 @@ def take_action(self, parsed_args): except Exception as e: errors += 1 LOG.error( - _("Failed to delete limit with ID " "'%(id)s': %(e)s"), + _("Failed to delete limit with ID '%(id)s': %(e)s"), {'id': limit_id, 'e': e}, ) if errors > 0: total = len(parsed_args.limit_id) - msg = _("%(errors)s of %(total)s limits failed to " "delete.") % { + msg = _("%(errors)s of %(total)s limits failed to delete.") % { 'errors': errors, 'total': total, } diff --git a/openstackclient/identity/v3/mapping.py b/openstackclient/identity/v3/mapping.py index ff8f2aa31..8c2d0bf8d 100644 --- a/openstackclient/identity/v3/mapping.py +++ b/openstackclient/identity/v3/mapping.py @@ -161,9 +161,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.mapping) - msg = _( - "%(result)s of %(total)s mappings failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s mappings failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py index 51428e8c9..a7e2a07d9 100644 --- a/openstackclient/identity/v3/policy.py +++ b/openstackclient/identity/v3/policy.py @@ -92,9 +92,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.policy) - msg = _( - "%(result)s of %(total)s policies failed " "to delete." - ) % { + msg = _("%(result)s of %(total)s policies failed to delete.") % { 'result': result, 'total': total, } diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index aa6f62949..250fde4c5 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -190,9 +190,10 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.projects) - msg = _( - "%(errors)s of %(total)s projects failed " "to delete." - ) % {'errors': errors, 'total': total} + msg = _("%(errors)s of %(total)s projects failed to delete.") % { + 'errors': errors, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/region.py b/openstackclient/identity/v3/region.py index faa2bc414..40d381981 100644 --- a/openstackclient/identity/v3/region.py +++ b/openstackclient/identity/v3/region.py @@ -92,16 +92,13 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error( - _( - "Failed to delete region with " - "ID '%(region)s': %(e)s" - ), + _("Failed to delete region with ID '%(region)s': %(e)s"), {'region': i, 'e': e}, ) if result > 0: total = len(parsed_args.region) - msg = _("%(result)s of %(total)s regions failed " "to delete.") % { + msg = _("%(result)s of %(total)s regions failed to delete.") % { 'result': result, 'total': total, } diff --git a/openstackclient/identity/v3/registered_limit.py b/openstackclient/identity/v3/registered_limit.py index 336151ddb..630066f8a 100644 --- a/openstackclient/identity/v3/registered_limit.py +++ b/openstackclient/identity/v3/registered_limit.py @@ -137,8 +137,7 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.registered_limit_id) msg = _( - "%(errors)s of %(total)s registered limits failed to " - "delete." + "%(errors)s of %(total)s registered limits failed to delete." ) % {'errors': errors, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index a7fb6644b..4364931b6 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -402,7 +402,7 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.roles) - msg = _("%(errors)s of %(total)s roles failed " "to delete.") % { + msg = _("%(errors)s of %(total)s roles failed to delete.") % { 'errors': errors, 'total': total, } @@ -449,7 +449,7 @@ def take_action(self, parsed_args): class RemoveRole(command.Command): _description = _( - "Removes a role assignment from system/domain/project : " "user/group" + "Removes a role assignment from system/domain/project : user/group" ) def get_parser(self, prog_name): diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index 41faa2551..dd1bfa473 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -135,9 +135,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.service) - msg = _( - "%(result)s of %(total)s services failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s services failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/service_provider.py b/openstackclient/identity/v3/service_provider.py index 1a7b02fad..5b49cffaf 100644 --- a/openstackclient/identity/v3/service_provider.py +++ b/openstackclient/identity/v3/service_provider.py @@ -77,8 +77,7 @@ def get_parser(self, prog_name): metavar='', required=True, help=_( - 'A service URL where SAML assertions are being sent ' - '(required)' + 'A service URL where SAML assertions are being sent (required)' ), ) @@ -155,8 +154,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.service_provider) msg = _( - "%(result)s of %(total)s service providers failed" - " to delete." + "%(result)s of %(total)s service providers failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -204,8 +202,7 @@ def get_parser(self, prog_name): '--auth-url', metavar='', help=_( - 'New Authentication URL of remote ' - 'federated service provider' + 'New Authentication URL of remote federated service provider' ), ) diff --git a/openstackclient/identity/v3/tag.py b/openstackclient/identity/v3/tag.py index ce1dfe891..0909fd122 100644 --- a/openstackclient/identity/v3/tag.py +++ b/openstackclient/identity/v3/tag.py @@ -83,7 +83,7 @@ def add_tag_option_to_parser_for_create(parser, resource_name): metavar='', default=[], help=_( - 'Tag to be added to the %s ' '(repeat option to set multiple tags)' + 'Tag to be added to the %s (repeat option to set multiple tags)' ) % resource_name, ) @@ -97,7 +97,7 @@ def add_tag_option_to_parser_for_set(parser, resource_name): metavar='', default=[], help=_( - 'Tag to be added to the %s ' '(repeat option to set multiple tags)' + 'Tag to be added to the %s (repeat option to set multiple tags)' ) % resource_name, ) diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py index 8f2dc0940..5500ce1d8 100644 --- a/openstackclient/identity/v3/token.py +++ b/openstackclient/identity/v3/token.py @@ -136,8 +136,7 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - 'Project that consumer wants to access (name or ID)' - ' (required)' + 'Project that consumer wants to access (name or ID) (required)' ), required=True, ) diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py index 41d01fa7b..df8e2f4ea 100644 --- a/openstackclient/identity/v3/trust.py +++ b/openstackclient/identity/v3/trust.py @@ -233,7 +233,7 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.trust) - msg = _("%(errors)s of %(total)s trusts failed " "to delete.") % { + msg = _("%(errors)s of %(total)s trusts failed to delete.") % { 'errors': errors, 'total': total, } diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 545b1cda7..f31d06751 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -377,7 +377,7 @@ def take_action(self, parsed_args): if errors > 0: total = len(parsed_args.users) - msg = _("%(errors)s of %(total)s users failed " "to delete.") % { + msg = _("%(errors)s of %(total)s users failed to delete.") % { 'errors': errors, 'total': total, } diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index ecbb2050b..2391c1639 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -384,8 +384,7 @@ def take_action(self, parsed_args): except Exception as e: result += 1 msg = _( - "Failed to delete image with name or " - "ID '%(image)s': %(e)s" + "Failed to delete image with name or ID '%(image)s': %(e)s" ) LOG.error(msg, {'image': image, 'e': e}) diff --git a/openstackclient/image/v2/cache.py b/openstackclient/image/v2/cache.py index c69b7b592..33a2f4fad 100644 --- a/openstackclient/image/v2/cache.py +++ b/openstackclient/image/v2/cache.py @@ -131,8 +131,7 @@ def take_action(self, parsed_args): except Exception as e: failures += 1 msg = _( - "Failed to queue image with name or " - "ID '%(image)s': %(e)s" + "Failed to queue image with name or ID '%(image)s': %(e)s" ) LOG.error(msg, {'image': image, 'e': e}) @@ -171,8 +170,7 @@ def take_action(self, parsed_args): except Exception as e: failures += 1 msg = _( - "Failed to delete image with name or " - "ID '%(image)s': %(e)s" + "Failed to delete image with name or ID '%(image)s': %(e)s" ) LOG.error(msg, {'image': image, 'e': e}) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index fe237e4e1..c57fa9948 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -359,8 +359,7 @@ def get_parser(self, prog_name): action="store_true", default=False, help=_( - "Show upload progress bar " - "(ignored if passing data via stdin)" + "Show upload progress bar (ignored if passing data via stdin)" ), ) parser.add_argument( @@ -401,8 +400,7 @@ def get_parser(self, prog_name): metavar="", action='append', help=_( - "Set a tag on this image " - "(repeat option to set multiple tags)" + "Set a tag on this image (repeat option to set multiple tags)" ), ) parser.add_argument( @@ -705,8 +703,7 @@ def take_action(self, parsed_args): except Exception as e: result += 1 msg = _( - "Failed to delete image with name or " - "ID '%(image)s': %(e)s" + "Failed to delete image with name or ID '%(image)s': %(e)s" ) LOG.error(msg, {'image': image, 'e': e}) @@ -1153,8 +1150,7 @@ def get_parser(self, prog_name): default=None, action='append', help=_( - "Set a tag on this image " - "(repeat option to set multiple tags)" + "Set a tag on this image (repeat option to set multiple tags)" ), ) parser.add_argument( @@ -1466,7 +1462,7 @@ def take_action(self, parsed_args): image_client.remove_tag(image.id, k) except Exception: LOG.error( - _("tag unset failed, '%s' is a " "nonexistent tag "), k + _("tag unset failed, '%s' is a nonexistent tag "), k ) tagret += 1 @@ -1519,7 +1515,7 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) elif propret > 0: msg = _( - "Failed to unset %(propret)s of %(proptotal)s" " properties." + "Failed to unset %(propret)s of %(proptotal)s properties." ) % {'propret': propret, 'proptotal': proptotal} raise exceptions.CommandError(msg) @@ -1551,8 +1547,7 @@ def get_parser(self, prog_name): action='store_true', default=False, help=_( - 'Show upload progress bar ' - '(ignored if passing data via stdin)' + 'Show upload progress bar (ignored if passing data via stdin)' ), ) parser.add_argument( diff --git a/openstackclient/image/v2/metadef_namespaces.py b/openstackclient/image/v2/metadef_namespaces.py index 8294daa02..f753286c4 100644 --- a/openstackclient/image/v2/metadef_namespaces.py +++ b/openstackclient/image/v2/metadef_namespaces.py @@ -169,9 +169,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.namespace) - msg = _( - "%(result)s of %(total)s namespace failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s namespace failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/network/client.py b/openstackclient/network/client.py index 887be52cb..1970ab7a8 100644 --- a/openstackclient/network/client.py +++ b/openstackclient/network/client.py @@ -50,9 +50,7 @@ def build_option_parser(parser): '--os-network-api-version', metavar='', default=utils.env('OS_NETWORK_API_VERSION'), - help=_( - "Network API version, default=%s " "(Env: OS_NETWORK_API_VERSION)" - ) + help=_("Network API version, default=%s (Env: OS_NETWORK_API_VERSION)") % DEFAULT_API_VERSION, ) return parser diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index 3d5237b1b..c32c98cfb 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -303,8 +303,7 @@ def _get_property_converter(self, _property): if not converter: raise exceptions.CommandError( _( - "Type {property_type} of property {name} " - "is not supported" + "Type {property_type} of property {name} is not supported" ).format( property_type=_property['type'], name=_property['name'] ) diff --git a/openstackclient/network/v2/address_group.py b/openstackclient/network/v2/address_group.py index fee3d143c..893f4673c 100644 --- a/openstackclient/network/v2/address_group.py +++ b/openstackclient/network/v2/address_group.py @@ -76,8 +76,7 @@ def get_parser(self, prog_name): action='append', default=[], help=_( - "IP address or CIDR " - "(repeat option to set multiple addresses)" + "IP address or CIDR (repeat option to set multiple addresses)" ), ) parser.add_argument( @@ -139,7 +138,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.address_group) msg = _( - "%(result)s of %(total)s address groups failed " "to delete." + "%(result)s of %(total)s address groups failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -159,8 +158,7 @@ def get_parser(self, prog_name): '--project', metavar="", help=_( - "List address groups according to their project " - "(name or ID)" + "List address groups according to their project (name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -233,8 +231,7 @@ def get_parser(self, prog_name): action='append', default=[], help=_( - "IP address or CIDR " - "(repeat option to set multiple addresses)" + "IP address or CIDR (repeat option to set multiple addresses)" ), ) return parser diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py index f28215462..a7f396b73 100644 --- a/openstackclient/network/v2/address_scope.py +++ b/openstackclient/network/v2/address_scope.py @@ -144,7 +144,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.address_scope) msg = _( - "%(result)s of %(total)s address scopes failed " "to delete." + "%(result)s of %(total)s address scopes failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -176,8 +176,7 @@ def get_parser(self, prog_name): '--project', metavar="", help=_( - "List address scopes according to their project " - "(name or ID)" + "List address scopes according to their project (name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index 86e517ff0..d190d14b1 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -123,7 +123,7 @@ def update_parser_network(self, parser): '--port', metavar='', help=self.enhance_help_neutron( - _("Port to be associated with the floating IP " "(name or ID)") + _("Port to be associated with the floating IP (name or ID)") ), ) parser.add_argument( @@ -268,10 +268,7 @@ def update_parser_network(self, parser): '--floating-ip-address', metavar='', help=self.enhance_help_neutron( - _( - "List floating IP(s) according to given floating IP " - "address" - ) + _("List floating IP(s) according to given floating IP address") ), ) parser.add_argument( @@ -308,10 +305,7 @@ def update_parser_network(self, parser): '--router', metavar='', help=self.enhance_help_neutron( - _( - "List floating IP(s) according to given router (name or " - "ID)" - ) + _("List floating IP(s) according to given router (name or ID)") ), ) _tag.add_tag_filtering_option_to_parser( @@ -458,8 +452,7 @@ def get_parser(self, prog_name): metavar='', dest='fixed_ip_address', help=_( - "Fixed IP of the port " - "(required only if port has multiple IPs)" + "Fixed IP of the port (required only if port has multiple IPs)" ), ) parser.add_argument( diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py index 9151f0498..115d2a811 100644 --- a/openstackclient/network/v2/floating_ip_port_forwarding.py +++ b/openstackclient/network/v2/floating_ip_port_forwarding.py @@ -243,7 +243,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.port_forwarding_id) msg = _( - "%(result)s of %(total)s Port forwarding failed " "to delete." + "%(result)s of %(total)s Port forwarding failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/l3_conntrack_helper.py b/openstackclient/network/v2/l3_conntrack_helper.py index f200090b7..073b91f7b 100644 --- a/openstackclient/network/v2/l3_conntrack_helper.py +++ b/openstackclient/network/v2/l3_conntrack_helper.py @@ -66,8 +66,7 @@ def get_parser(self, prog_name): required=True, metavar='', help=_( - 'The network protocol for the netfilter conntrack target ' - 'rule' + 'The network protocol for the netfilter conntrack target rule' ), ) parser.add_argument( @@ -158,8 +157,7 @@ def get_parser(self, prog_name): '--protocol', metavar='', help=_( - 'The network protocol for the netfilter conntrack target ' - 'rule' + 'The network protocol for the netfilter conntrack target rule' ), ) parser.add_argument( @@ -226,8 +224,7 @@ def get_parser(self, prog_name): '--protocol', metavar='', help=_( - 'The network protocol for the netfilter conntrack target ' - 'rule' + 'The network protocol for the netfilter conntrack target rule' ), ) parser.add_argument( diff --git a/openstackclient/network/v2/local_ip.py b/openstackclient/network/v2/local_ip.py index 0d92f2c11..cc317fa9e 100644 --- a/openstackclient/network/v2/local_ip.py +++ b/openstackclient/network/v2/local_ip.py @@ -149,9 +149,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.local_ip) - msg = _( - "%(result)s of %(total)s local IPs failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s local IPs failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) @@ -201,31 +202,27 @@ def get_parser(self, prog_name): parser.add_argument( '--project', metavar="", - help=_( - "List Local IPs according to their project " "(name or ID)" - ), + help=_("List Local IPs according to their project (name or ID)"), ) parser.add_argument( '--network', metavar='', - help=_( - "List Local IP(s) according to " "given network (name or ID)" - ), + help=_("List Local IP(s) according to given network (name or ID)"), ) parser.add_argument( '--local-port', metavar='', - help=_("List Local IP(s) according to " "given port (name or ID)"), + help=_("List Local IP(s) according to given port (name or ID)"), ) parser.add_argument( '--local-ip-address', metavar='', - help=_("List Local IP(s) according to " "given Local IP Address"), + help=_("List Local IP(s) according to given Local IP Address"), ) parser.add_argument( '--ip-mode', metavar='', - help=_("List Local IP(s) according to " "given IP mode"), + help=_("List Local IP(s) according to given IP mode"), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/local_ip_association.py b/openstackclient/network/v2/local_ip_association.py index 9f8325be4..816f04882 100644 --- a/openstackclient/network/v2/local_ip_association.py +++ b/openstackclient/network/v2/local_ip_association.py @@ -44,7 +44,7 @@ def get_parser(self, prog_name): 'local_ip', metavar='', help=_( - "Local IP that the port association belongs to " "(Name or ID)" + "Local IP that the port association belongs to (Name or ID)" ), ) parser.add_argument( @@ -90,7 +90,7 @@ def get_parser(self, prog_name): 'local_ip', metavar="", help=_( - "Local IP that the port association belongs to " "(Name or ID)" + "Local IP that the port association belongs to (Name or ID)" ), ) parser.add_argument( @@ -151,7 +151,7 @@ def get_parser(self, prog_name): '--fixed-port', metavar='', help=_( - "Filter the list result by the ID or name of " "the fixed port" + "Filter the list result by the ID or name of the fixed port" ), ) parser.add_argument( diff --git a/openstackclient/network/v2/ndp_proxy.py b/openstackclient/network/v2/ndp_proxy.py index 4c1871172..feda746ed 100644 --- a/openstackclient/network/v2/ndp_proxy.py +++ b/openstackclient/network/v2/ndp_proxy.py @@ -124,13 +124,13 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error( - _("Failed to delete NDP proxy " "'%(ndp_proxy)s': %(e)s"), + _("Failed to delete NDP proxy '%(ndp_proxy)s': %(e)s"), {'ndp_proxy': ndp_proxy, 'e': e}, ) if result > 0: total = len(parsed_args.ndp_proxy) msg = _( - "%(result)s of %(total)s NDP proxies failed " "to delete." + "%(result)s of %(total)s NDP proxies failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 7636de0c9..82e4ce69a 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -355,17 +355,14 @@ def update_parser_network(self, parser): '--qinq-vlan', action='store_true', help=self.enhance_help_neutron( - _("Enable VLAN QinQ (S-Tag ethtype 0x8a88) " "for the network") + _("Enable VLAN QinQ (S-Tag ethtype 0x8a88) for the network") ), ) vlan_qinq_grp.add_argument( '--no-qinq-vlan', action='store_true', help=self.enhance_help_neutron( - _( - "Disable VLAN QinQ (S-Tag ethtype 0x8a88) " - "for the network" - ) + _("Disable VLAN QinQ (S-Tag ethtype 0x8a88) for the network") ), ) diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py index 516b0f048..1555cb654 100644 --- a/openstackclient/network/v2/network_agent.py +++ b/openstackclient/network/v2/network_agent.py @@ -155,7 +155,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.network_agent) msg = _( - "%(result)s of %(total)s network agents failed " "to delete." + "%(result)s of %(total)s network agents failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py index d9a4b5d2e..ab2ba6850 100644 --- a/openstackclient/network/v2/network_flavor_profile.py +++ b/openstackclient/network/v2/network_flavor_profile.py @@ -146,7 +146,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.flavor_profile) msg = _( - "%(result)s of %(total)s flavor_profiles failed " "to delete." + "%(result)s of %(total)s flavor_profiles failed to delete." ) % {"result": result, "total": total} raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py index 5a6a8f2f2..9cb687e15 100644 --- a/openstackclient/network/v2/network_meter.py +++ b/openstackclient/network/v2/network_meter.py @@ -139,12 +139,12 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error( - _("Failed to delete meter with " "ID '%(meter)s': %(e)s"), + _("Failed to delete meter with ID '%(meter)s': %(e)s"), {"meter": meter, "e": e}, ) if result > 0: total = len(parsed_args.meter) - msg = _("%(result)s of %(total)s meters failed " "to delete.") % { + msg = _("%(result)s of %(total)s meters failed to delete.") % { "result": result, "total": total, } diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py index ea1bea269..a3765456c 100644 --- a/openstackclient/network/v2/network_meter_rule.py +++ b/openstackclient/network/v2/network_meter_rule.py @@ -168,17 +168,14 @@ def take_action(self, parsed_args): except Exception as e: result += 1 LOG.error( - _( - "Failed to delete meter rule with " - "ID '%(id)s': %(e)s" - ), + _("Failed to delete meter rule with ID '%(id)s': %(e)s"), {"id": id, "e": e}, ) if result > 0: total = len(parsed_args.meter_rule_id) msg = _( - "%(result)s of %(total)s meter rules failed " "to delete." + "%(result)s of %(total)s meter rules failed to delete." ) % {"result": result, "total": total} raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py index 5023ea988..7f3076f32 100644 --- a/openstackclient/network/v2/network_qos_policy.py +++ b/openstackclient/network/v2/network_qos_policy.py @@ -174,7 +174,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.policy) msg = _( - "%(result)s of %(total)s QoS policies failed " "to delete." + "%(result)s of %(total)s QoS policies failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py index e3e5aff19..2b64885d2 100644 --- a/openstackclient/network/v2/network_rbac.py +++ b/openstackclient/network/v2/network_rbac.py @@ -212,7 +212,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.rbac_policy) msg = _( - "%(result)s of %(total)s RBAC policies failed " "to delete." + "%(result)s of %(total)s RBAC policies failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py index 740952315..4ec2a2724 100644 --- a/openstackclient/network/v2/network_segment.py +++ b/openstackclient/network/v2/network_segment.py @@ -141,7 +141,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.network_segment) msg = _( - "%(result)s of %(total)s network segments failed " "to delete." + "%(result)s of %(total)s network segments failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py index aa0509969..2ee2a2587 100644 --- a/openstackclient/network/v2/network_segment_range.py +++ b/openstackclient/network/v2/network_segment_range.py @@ -105,8 +105,7 @@ def get_parser(self, prog_name): dest="private", action="store_true", help=_( - 'Network segment range is assigned specifically to the ' - 'project' + 'Network segment range is assigned specifically to the project' ), ) shared_group.add_argument( @@ -196,8 +195,7 @@ def take_action(self, parsed_args): and parsed_args.physical_network ): msg = _( - "--physical-network is only allowed with --network-type " - "vlan" + "--physical-network is only allowed with --network-type vlan" ) raise exceptions.CommandError(msg) @@ -323,7 +321,7 @@ def get_parser(self, prog_name): '--unused', action='store_true', help=_( - 'List network segment ranges that have segments ' 'not in use' + 'List network segment ranges that have segments not in use' ), ) available_group = parser.add_mutually_exclusive_group() @@ -457,8 +455,7 @@ def take_action(self, parsed_args): ) except Exception as e: msg = _( - 'Network segment range set not supported by ' - 'Network API: %(e)s' + 'Network segment range set not supported by Network API: %(e)s' ) % {'e': e} raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index a2b0fbf1f..047e3e932 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -128,7 +128,7 @@ def take_action(self, parsed_args): ) if result > 0: total = len(parsed_args.trunk) - msg = _("%(result)s of %(total)s trunks failed " "to delete.") % { + msg = _("%(result)s of %(total)s trunks failed to delete.") % { 'result': result, 'total': total, } diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 3579194bf..43838cd6c 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -368,8 +368,7 @@ def _add_updatable_args(parser, create=False): '--dns-name', metavar='', help=_( - "Set DNS name for this port " - "(requires DNS integration extension)" + "Set DNS name for this port (requires DNS integration extension)" ), ) numa_affinity_policy_group = parser.add_mutually_exclusive_group() @@ -753,7 +752,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.port) - msg = _("%(result)s of %(total)s ports failed " "to delete.") % { + msg = _("%(result)s of %(total)s ports failed to delete.") % { 'result': result, 'total': total, } diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 0deae3600..ee30d87fc 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -671,7 +671,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.router) - msg = _("%(result)s of %(total)s routers failed " "to delete.") % { + msg = _("%(result)s of %(total)s routers failed to delete.") % { 'result': result, 'total': total, } @@ -951,7 +951,7 @@ def get_parser(self, prog_name): '--ha', action='store_true', help=_( - "Set the router as highly available " "(disabled router only)" + "Set the router as highly available (disabled router only)" ), ) routes_ha.add_argument( diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index 556eff2c0..09c888068 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -239,10 +239,7 @@ def update_parser_network(self, parser): '--project', metavar='', help=self.enhance_help_neutron( - _( - "List security groups according to the project (name or " - "ID)" - ) + _("List security groups according to the project (name or ID)") ), ) identity_common.add_project_domain_option_to_parser( diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index 80e5ff085..f9f8a4287 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -608,7 +608,7 @@ def take_action_compute(self, client, parsed_args): if obj is None: msg = ( - _("Could not find security group rule " "with ID '%s'") + _("Could not find security group rule with ID '%s'") % parsed_args.rule ) raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 3d172adb1..5b2aed688 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -400,7 +400,7 @@ def get_parser(self, prog_name): '--network-segment', metavar='', help=_( - "Network segment to associate with this subnet " "(name or ID)" + "Network segment to associate with this subnet (name or ID)" ), ) parser.add_argument( @@ -465,7 +465,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.subnet) - msg = _("%(result)s of %(total)s subnets failed " "to delete.") % { + msg = _("%(result)s of %(total)s subnets failed to delete.") % { 'result': result, 'total': total, } diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index 78505bf2d..fca3bfa66 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -256,7 +256,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.subnet_pool) msg = _( - "%(result)s of %(total)s subnet pools failed " "to delete." + "%(result)s of %(total)s subnet pools failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -290,7 +290,7 @@ def get_parser(self, prog_name): '--default', action='store_true', help=_( - "List subnet pools used as the default external " "subnet pool" + "List subnet pools used as the default external subnet pool" ), ) default_group.add_argument( diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py index 47b6d7c72..6afa2c7c0 100644 --- a/openstackclient/tests/functional/compute/v2/test_server.py +++ b/openstackclient/tests/functional/compute/v2/test_server.py @@ -156,7 +156,7 @@ def test_server_list_with_changes_since(self): server_name3 = cmd_output['name'] cmd_output = self.openstack( - 'server list ' '--changes-since ' + updated_at2, + 'server list --changes-since ' + updated_at2, parse_output=True, ) @@ -852,8 +852,7 @@ def _test_server_boot_with_bdm_image(self, use_legacy): # it to the server at /dev/vdb and delete the volume when the # server is deleted. bdm_arg = ( - f'--block-device-mapping ' - f'vdb={self.image_name}:image:1:true ' + f'--block-device-mapping vdb={self.image_name}:image:1:true ' ) else: # get image ID diff --git a/openstackclient/tests/functional/compute/v2/test_server_event.py b/openstackclient/tests/functional/compute/v2/test_server_event.py index 790aec69d..0457985a0 100644 --- a/openstackclient/tests/functional/compute/v2/test_server_event.py +++ b/openstackclient/tests/functional/compute/v2/test_server_event.py @@ -93,7 +93,7 @@ def test_server_event_list_and_show_deleted_server(self): # And verify we can get the event list after it's deleted # Test 'server event list' for deleting cmd_output = self.openstack( - '--os-compute-api-version 2.21 ' 'server event list ' + server_id, + '--os-compute-api-version 2.21 server event list ' + server_id, parse_output=True, ) request_id = None diff --git a/openstackclient/tests/functional/identity/v2/test_user.py b/openstackclient/tests/functional/identity/v2/test_user.py index e0d2c956a..c92fc8792 100644 --- a/openstackclient/tests/functional/identity/v2/test_user.py +++ b/openstackclient/tests/functional/identity/v2/test_user.py @@ -37,7 +37,7 @@ def test_user_set(self): new_username = data_utils.rand_name('NewTestUser') new_email = data_utils.rand_name() + '@example.com' raw_output = self.openstack( - 'user set ' '--email {email} ' '--name {new_name} ' '{id}'.format( + 'user set --email {email} --name {new_name} {id}'.format( email=new_email, new_name=new_username, id=user['id'] ) ) diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py index ecabfd256..9f21374ff 100644 --- a/openstackclient/tests/functional/identity/v3/common.py +++ b/openstackclient/tests/functional/identity/v3/common.py @@ -187,8 +187,7 @@ def tearDownClass(cls): f'domain set --disable {cls.domain_name}' ) cls.openstack( - '--os-identity-api-version 3 ' - f'domain delete {cls.domain_name}' + f'--os-identity-api-version 3 domain delete {cls.domain_name}' ) finally: super().tearDownClass() @@ -270,9 +269,7 @@ def _create_dummy_group(self, add_clean_up=True): if add_clean_up: self.addCleanup( self.openstack, - 'group delete ' - f'--domain {self.domain_name} ' - f'{group_name}', + f'group delete --domain {self.domain_name} {group_name}', ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.GROUP_FIELDS) @@ -305,9 +302,7 @@ def _create_dummy_project(self, add_clean_up=True): if add_clean_up: self.addCleanup( self.openstack, - 'project delete ' - f'--domain {self.domain_name} ' - f'{project_name}', + f'project delete --domain {self.domain_name} {project_name}', ) return project_name diff --git a/openstackclient/tests/functional/identity/v3/test_application_credential.py b/openstackclient/tests/functional/identity/v3/test_application_credential.py index 39c735b2b..a9c2286d5 100644 --- a/openstackclient/tests/functional/identity/v3/test_application_credential.py +++ b/openstackclient/tests/functional/identity/v3/test_application_credential.py @@ -50,35 +50,35 @@ def test_application_credential_create(self): def _create_role_assignments(self): try: user = self.openstack( - 'configuration show -f value' ' -c auth.username' + 'configuration show -f value -c auth.username' ) except Exception: user = self.openstack( - 'configuration show -f value' ' -c auth.user_id' + 'configuration show -f value -c auth.user_id' ) try: user_domain = self.openstack( - 'configuration show -f value' ' -c auth.user_domain_name' + 'configuration show -f value -c auth.user_domain_name' ) except Exception: user_domain = self.openstack( - 'configuration show -f value' ' -c auth.user_domain_id' + 'configuration show -f value -c auth.user_domain_id' ) try: project = self.openstack( - 'configuration show -f value' ' -c auth.project_name' + 'configuration show -f value -c auth.project_name' ) except Exception: project = self.openstack( - 'configuration show -f value' ' -c auth.project_id' + 'configuration show -f value -c auth.project_id' ) try: project_domain = self.openstack( - 'configuration show -f value' ' -c auth.project_domain_name' + 'configuration show -f value -c auth.project_domain_name' ) except Exception: project_domain = self.openstack( - 'configuration show -f value' ' -c auth.project_domain_id' + 'configuration show -f value -c auth.project_domain_id' ) role1 = self._create_dummy_role() role2 = self._create_dummy_role() @@ -130,7 +130,7 @@ def test_application_credential_create_with_options(self): def test_application_credential_delete(self): name = data_utils.rand_name('name') self.openstack(f'application credential create {name}') - raw_output = self.openstack('application credential delete ' f'{name}') + raw_output = self.openstack(f'application credential delete {name}') self.assertEqual(0, len(raw_output)) def test_application_credential_list(self): @@ -147,6 +147,6 @@ def test_application_credential_show(self): self.openstack, f'application credential delete {name}', ) - raw_output = self.openstack('application credential show ' f'{name}') + raw_output = self.openstack(f'application credential show {name}') items = self.parse_show(raw_output) self.assert_show_fields(items, self.APPLICATION_CREDENTIAL_FIELDS) diff --git a/openstackclient/tests/functional/identity/v3/test_endpoint.py b/openstackclient/tests/functional/identity/v3/test_endpoint.py index cafab670a..0441fcb6e 100644 --- a/openstackclient/tests/functional/identity/v3/test_endpoint.py +++ b/openstackclient/tests/functional/identity/v3/test_endpoint.py @@ -45,7 +45,7 @@ def test_endpoint_list_filter(self): endpoint_id = self._create_dummy_endpoint(add_clean_up=False) project_id = self._create_dummy_project(add_clean_up=False) raw_output = self.openstack( - 'endpoint add project ' f'{endpoint_id} ' f'{project_id}' + f'endpoint add project {endpoint_id} {project_id}' ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack(f'endpoint list --endpoint {endpoint_id}') @@ -89,11 +89,11 @@ def test_endpoint_add_remove_project(self): endpoint_id = self._create_dummy_endpoint(add_clean_up=False) project_id = self._create_dummy_project(add_clean_up=False) raw_output = self.openstack( - 'endpoint add project ' f'{endpoint_id} ' f'{project_id}' + f'endpoint add project {endpoint_id} {project_id}' ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'endpoint remove project ' f'{endpoint_id} ' f'{project_id}' + f'endpoint remove project {endpoint_id} {project_id}' ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_group.py b/openstackclient/tests/functional/identity/v3/test_group.py index 5fc1ed39c..a2e41d813 100644 --- a/openstackclient/tests/functional/identity/v3/test_group.py +++ b/openstackclient/tests/functional/identity/v3/test_group.py @@ -36,14 +36,14 @@ def test_group_list_with_domain(self): def test_group_delete(self): group_name = self._create_dummy_group(add_clean_up=False) raw_output = self.openstack( - 'group delete ' f'--domain {self.domain_name} ' f'{group_name}' + f'group delete --domain {self.domain_name} {group_name}' ) self.assertEqual(0, len(raw_output)) def test_group_show(self): group_name = self._create_dummy_group() raw_output = self.openstack( - 'group show ' f'--domain {self.domain_name} ' f'{group_name}' + f'group show --domain {self.domain_name} {group_name}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.GROUP_FIELDS) @@ -59,7 +59,7 @@ def test_group_set(self): ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'group show ' f'--domain {self.domain_name} ' f'{new_group_name}' + f'group show --domain {self.domain_name} {new_group_name}' ) group = self.parse_show_as_object(raw_output) self.assertEqual(new_group_name, group['name']) diff --git a/openstackclient/tests/functional/identity/v3/test_limit.py b/openstackclient/tests/functional/identity/v3/test_limit.py index 79213308d..8c0bbcd6a 100644 --- a/openstackclient/tests/functional/identity/v3/test_limit.py +++ b/openstackclient/tests/functional/identity/v3/test_limit.py @@ -174,7 +174,7 @@ def test_limit_set_description(self): } raw_output = self.openstack( - 'limit set' ' --description {description}' ' {limit_id}'.format( + 'limit set --description {description} {limit_id}'.format( **params ), cloud=SYSTEM_CLOUD, @@ -188,9 +188,9 @@ def test_limit_set_resource_limit(self): params = {'resource_limit': 5, 'limit_id': limit_id} raw_output = self.openstack( - 'limit set' - ' --resource-limit {resource_limit}' - ' {limit_id}'.format(**params), + 'limit set --resource-limit {resource_limit} {limit_id}'.format( + **params + ), cloud=SYSTEM_CLOUD, ) items = self.parse_show(raw_output) diff --git a/openstackclient/tests/functional/identity/v3/test_project.py b/openstackclient/tests/functional/identity/v3/test_project.py index 9115c0e1a..7a66c1851 100644 --- a/openstackclient/tests/functional/identity/v3/test_project.py +++ b/openstackclient/tests/functional/identity/v3/test_project.py @@ -30,9 +30,7 @@ def test_project_create(self): ) self.addCleanup( self.openstack, - 'project delete ' - f'--domain {self.domain_name} ' - f'{project_name}', + f'project delete --domain {self.domain_name} {project_name}', ) items = self.parse_show(raw_output) show_fields = list(self.PROJECT_FIELDS) @@ -45,7 +43,7 @@ def test_project_create(self): def test_project_delete(self): project_name = self._create_dummy_project(add_clean_up=False) raw_output = self.openstack( - 'project delete ' f'--domain {self.domain_name} ' f'{project_name}' + f'project delete --domain {self.domain_name} {project_name}' ) self.assertEqual(0, len(raw_output)) @@ -77,9 +75,7 @@ def test_project_set(self): self.assertEqual(0, len(raw_output)) # check project details raw_output = self.openstack( - 'project show ' - f'--domain {self.domain_name} ' - f'{new_project_name}' + f'project show --domain {self.domain_name} {new_project_name}' ) items = self.parse_show(raw_output) fields = list(self.PROJECT_FIELDS) @@ -91,17 +87,12 @@ def test_project_set(self): self.assertEqual('v0', project['k0']) # reset project to make sure it will be cleaned up self.openstack( - 'project set ' - f'--name {project_name} ' - '--enable ' - f'{new_project_name}' + f'project set --name {project_name} --enable {new_project_name}' ) def test_project_show(self): raw_output = self.openstack( - 'project show ' - f'--domain {self.domain_name} ' - f'{self.project_name}' + f'project show --domain {self.domain_name} {self.project_name}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.PROJECT_FIELDS) diff --git a/openstackclient/tests/functional/identity/v3/test_region.py b/openstackclient/tests/functional/identity/v3/test_region.py index 16133f894..49471002a 100644 --- a/openstackclient/tests/functional/identity/v3/test_region.py +++ b/openstackclient/tests/functional/identity/v3/test_region.py @@ -49,9 +49,7 @@ def test_region_set(self): self.assertEqual(region_id, region['region']) # update parent-region raw_output = self.openstack( - 'region set ' - f'--parent-region {new_parent_region_id} ' - f'{region_id}' + f'region set --parent-region {new_parent_region_id} {region_id}' ) self.assertEqual(0, len(raw_output)) # check updated region details diff --git a/openstackclient/tests/functional/identity/v3/test_registered_limit.py b/openstackclient/tests/functional/identity/v3/test_registered_limit.py index f91b47fc4..54ee7f4f8 100644 --- a/openstackclient/tests/functional/identity/v3/test_registered_limit.py +++ b/openstackclient/tests/functional/identity/v3/test_registered_limit.py @@ -25,7 +25,7 @@ def test_registered_limit_create_with_service_name(self): def test_registered_limit_create_with_service_id(self): service_name = self._create_dummy_service() - raw_output = self.openstack('service show' f' {service_name}') + raw_output = self.openstack(f'service show {service_name}') service_items = self.parse_show(raw_output) service_id = self._extract_value_from_items('id', service_items) @@ -44,7 +44,7 @@ def test_registered_limit_create_with_service_id(self): registered_limit_id = self._extract_value_from_items('id', items) self.addCleanup( self.openstack, - 'registered limit delete' f' {registered_limit_id}', + f'registered limit delete {registered_limit_id}', cloud=SYSTEM_CLOUD, ) @@ -178,7 +178,7 @@ def test_registered_limit_delete(self): add_clean_up=False ) raw_output = self.openstack( - 'registered limit delete' f' {registered_limit_id}', + f'registered limit delete {registered_limit_id}', cloud=SYSTEM_CLOUD, ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_role.py b/openstackclient/tests/functional/identity/v3/test_role.py index f4c6832f2..3237c0bfb 100644 --- a/openstackclient/tests/functional/identity/v3/test_role.py +++ b/openstackclient/tests/functional/identity/v3/test_role.py @@ -23,7 +23,7 @@ def test_role_create_with_description(self): role_name = data_utils.rand_name('TestRole') description = data_utils.rand_name('description') raw_output = self.openstack( - 'role create ' f'--description {description} ' f'{role_name}' + f'role create --description {description} {role_name}' ) role = self.parse_show_as_object(raw_output) self.addCleanup(self.openstack, 'role delete {}'.format(role['id'])) diff --git a/openstackclient/tests/functional/identity/v3/test_role_assignment.py b/openstackclient/tests/functional/identity/v3/test_role_assignment.py index 6c53bc277..76e33c286 100644 --- a/openstackclient/tests/functional/identity/v3/test_role_assignment.py +++ b/openstackclient/tests/functional/identity/v3/test_role_assignment.py @@ -24,35 +24,25 @@ def test_role_assignment_list_user_role_system(self): username = self._create_dummy_user() system = 'all' raw_output = self.openstack( - 'role add ' - f'--user {username} ' - f'--system {system} ' - f'{role_name}' + f'role add --user {username} --system {system} {role_name}' ) self.addCleanup( self.openstack, - 'role remove ' - f'--user {username} ' - f'--system {system} ' - f'{role_name}', + f'role remove --user {username} --system {system} {role_name}', ) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack( - 'role assignment list ' f'--user {username} ' - ) + raw_output = self.openstack(f'role assignment list --user {username} ') items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) raw_output = self.openstack( - 'role assignment list ' f'--role {role_name} ' + f'role assignment list --role {role_name} ' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) - raw_output = self.openstack( - 'role assignment list ' f'--system {system} ' - ) + raw_output = self.openstack(f'role assignment list --system {system} ') items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) @@ -61,22 +51,14 @@ def test_role_assignment_list_group(self): group = self._create_dummy_group() system = 'all' raw_output = self.openstack( - 'role add ' - f'--group {group} ' - f'--system {system} ' - f'{role_name}' + f'role add --group {group} --system {system} {role_name}' ) self.addCleanup( self.openstack, - 'role remove ' - f'--group {group} ' - f'--system {system} ' - f'{role_name}', + f'role remove --group {group} --system {system} {role_name}', ) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack( - 'role assignment list ' f'--group {group} ' - ) + raw_output = self.openstack(f'role assignment list --group {group} ') items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) @@ -98,7 +80,7 @@ def test_role_assignment_list_domain(self): ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'role assignment list ' f'--domain {self.domain_name} ' + f'role assignment list --domain {self.domain_name} ' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) @@ -121,23 +103,23 @@ def test_role_assignment_list_project(self): ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'role assignment list ' f'--project {self.project_name} ' + f'role assignment list --project {self.project_name} ' ) items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) def test_role_assignment_list_effective(self): - raw_output = self.openstack('role assignment list ' '--effective') + raw_output = self.openstack('role assignment list --effective') items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) def test_role_assignment_list_auth_user(self): - raw_output = self.openstack('role assignment list ' '--auth-user') + raw_output = self.openstack('role assignment list --auth-user') items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) def test_role_assignment_list_auth_project(self): - raw_output = self.openstack('role assignment list ' '--auth-project') + raw_output = self.openstack('role assignment list --auth-project') items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) diff --git a/openstackclient/tests/functional/identity/v3/test_user.py b/openstackclient/tests/functional/identity/v3/test_user.py index c32524600..dd56293e6 100644 --- a/openstackclient/tests/functional/identity/v3/test_user.py +++ b/openstackclient/tests/functional/identity/v3/test_user.py @@ -22,7 +22,7 @@ def test_user_create(self): def test_user_delete(self): username = self._create_dummy_user(add_clean_up=False) raw_output = self.openstack( - 'user delete ' f'--domain {self.domain_name} ' f'{username}' + f'user delete --domain {self.domain_name} {username}' ) self.assertEqual(0, len(raw_output)) @@ -34,19 +34,19 @@ def test_user_list(self): def test_user_set(self): username = self._create_dummy_user() raw_output = self.openstack( - 'user show ' f'--domain {self.domain_name} ' f'{username}' + f'user show --domain {self.domain_name} {username}' ) user = self.parse_show_as_object(raw_output) new_username = data_utils.rand_name('NewTestUser') new_email = data_utils.rand_name() + '@example.com' raw_output = self.openstack( - 'user set ' '--email {email} ' '--name {new_name} ' '{id}'.format( + 'user set --email {email} --name {new_name} {id}'.format( email=new_email, new_name=new_username, id=user['id'] ) ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( - 'user show ' f'--domain {self.domain_name} ' f'{new_username}' + f'user show --domain {self.domain_name} {new_username}' ) updated_user = self.parse_show_as_object(raw_output) self.assertEqual(user['id'], updated_user['id']) @@ -57,7 +57,7 @@ def test_user_set_default_project_id(self): project_name = self._create_dummy_project() # get original user details raw_output = self.openstack( - 'user show ' f'--domain {self.domain_name} ' f'{username}' + f'user show --domain {self.domain_name} {username}' ) user = self.parse_show_as_object(raw_output) # update user @@ -74,12 +74,12 @@ def test_user_set_default_project_id(self): self.assertEqual(0, len(raw_output)) # get updated user details raw_output = self.openstack( - 'user show ' f'--domain {self.domain_name} ' f'{username}' + f'user show --domain {self.domain_name} {username}' ) updated_user = self.parse_show_as_object(raw_output) # get project details raw_output = self.openstack( - 'project show ' f'--domain {self.domain_name} ' f'{project_name}' + f'project show --domain {self.domain_name} {project_name}' ) project = self.parse_show_as_object(raw_output) # check updated user details @@ -89,7 +89,7 @@ def test_user_set_default_project_id(self): def test_user_show(self): username = self._create_dummy_user() raw_output = self.openstack( - 'user show ' f'--domain {self.domain_name} ' f'{username}' + f'user show --domain {self.domain_name} {username}' ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.USER_FIELDS) diff --git a/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py b/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py index 95ebb49ce..60ae6d5ff 100644 --- a/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py +++ b/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py @@ -69,8 +69,7 @@ def test_l3_conntrack_helper_create_and_delete(self): ct_ids = " ".join([ct['id'] for ct in created_helpers]) raw_output = self.openstack( - f'--debug network l3 conntrack helper delete {router_id} ' - f'{ct_ids}' + f'--debug network l3 conntrack helper delete {router_id} {ct_ids}' ) self.assertOutput('', raw_output) diff --git a/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py b/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py index 11d121a3e..120f53d28 100644 --- a/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py +++ b/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py @@ -31,7 +31,7 @@ def setUp(self): self.created_ndp_proxies = [] json_output = self.openstack( - 'address scope create --ip-version 6 ' f'{self.ADDR_SCOPE_NAME}', + f'address scope create --ip-version 6 {self.ADDR_SCOPE_NAME}', parse_output=True, ) self.assertIsNotNone(json_output['id']) @@ -88,8 +88,7 @@ def setUp(self): self.assertIsNotNone(json_output['id']) self.INT_SUB_ID = json_output['id'] json_output = self.openstack( - f'port create --network {self.INT_NET_ID} ' - f'{self.INT_PORT_NAME}', + f'port create --network {self.INT_NET_ID} {self.INT_PORT_NAME}', parse_output=True, ) self.assertIsNotNone(json_output['id']) diff --git a/openstackclient/tests/functional/network/v2/test_network_trunk.py b/openstackclient/tests/functional/network/v2/test_network_trunk.py index 0232943b3..162197359 100644 --- a/openstackclient/tests/functional/network/v2/test_network_trunk.py +++ b/openstackclient/tests/functional/network/v2/test_network_trunk.py @@ -80,7 +80,7 @@ def test_network_trunk_set_unset(self): self.addCleanup(self.openstack, 'network trunk delete ' + trunk_name) self.assertEqual(trunk_name, json_output['name']) - self.openstack('network trunk set ' '--enable ' + trunk_name) + self.openstack('network trunk set --enable ' + trunk_name) json_output = json.loads( self.openstack('network trunk show -f json ' + trunk_name) diff --git a/openstackclient/tests/functional/network/v2/test_port.py b/openstackclient/tests/functional/network/v2/test_port.py index 453039550..5e6468109 100644 --- a/openstackclient/tests/functional/network/v2/test_port.py +++ b/openstackclient/tests/functional/network/v2/test_port.py @@ -209,7 +209,7 @@ def test_port_set(self): def test_port_admin_set(self): """Test create, set (as admin), show, delete""" json_output = self.openstack( - 'port create ' f'--network {self.NETWORK_NAME} {self.NAME}', + f'port create --network {self.NETWORK_NAME} {self.NAME}', parse_output=True, ) id_ = json_output.get('id') @@ -257,7 +257,7 @@ def test_port_set_sg(self): self.assertEqual([sg_id1], json_output.get('security_group_ids')) raw_output = self.openstack( - 'port set ' f'--security-group {sg_name2} {name}' + f'port set --security-group {sg_name2} {name}' ) self.assertOutput('', raw_output) @@ -296,17 +296,17 @@ def _trunk_creation(self): sport2 = uuid.uuid4().hex trunk = uuid.uuid4().hex json_output = self.openstack( - 'port create ' f'--network {self.NETWORK_NAME} {pport}', + f'port create --network {self.NETWORK_NAME} {pport}', parse_output=True, ) pport_id = json_output.get('id') json_output = self.openstack( - 'port create ' f'--network {self.NETWORK_NAME} {sport1}', + f'port create --network {self.NETWORK_NAME} {sport1}', parse_output=True, ) sport1_id = json_output.get('id') json_output = self.openstack( - 'port create ' f'--network {self.NETWORK_NAME} {sport2}', + f'port create --network {self.NETWORK_NAME} {sport2}', parse_output=True, ) sport2_id = json_output.get('id') diff --git a/openstackclient/tests/unit/common/test_command.py b/openstackclient/tests/unit/common/test_command.py index f31c71172..8233f8994 100644 --- a/openstackclient/tests/unit/common/test_command.py +++ b/openstackclient/tests/unit/common/test_command.py @@ -31,7 +31,7 @@ def test_command_has_logger(self): cmd = FakeCommand(mock.Mock(), mock.Mock()) self.assertTrue(hasattr(cmd, 'log')) self.assertEqual( - 'openstackclient.tests.unit.common.test_command.' 'FakeCommand', + 'openstackclient.tests.unit.common.test_command.FakeCommand', cmd.log.name, ) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 789daadf4..2fa3aa7e1 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4967,7 +4967,7 @@ def test_server_list_with_invalid_changes_since(self, mock_parse_isotime): self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual( - 'Invalid changes-since value: Invalid time ' 'value', str(e) + 'Invalid changes-since value: Invalid time value', str(e) ) mock_parse_isotime.assert_called_once_with('Invalid time value') @@ -5454,7 +5454,7 @@ def test_server_list_with_invalid_changes_before(self, mock_parse_isotime): self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual( - 'Invalid changes-before value: Invalid time ' 'value', str(e) + 'Invalid changes-before value: Invalid time value', str(e) ) mock_parse_isotime.assert_called_once_with('Invalid time value') diff --git a/openstackclient/tests/unit/identity/v2_0/fakes.py b/openstackclient/tests/unit/identity/v2_0/fakes.py index e07b02626..c5089d48b 100644 --- a/openstackclient/tests/unit/identity/v2_0/fakes.py +++ b/openstackclient/tests/unit/identity/v2_0/fakes.py @@ -228,7 +228,7 @@ def create_one_extension(attrs=None): extension_info = { 'name': 'name-' + uuid.uuid4().hex, 'namespace': ( - 'http://docs.openstack.org/identity/' 'api/ext/OS-KSCRUD/v1.0' + 'http://docs.openstack.org/identity/api/ext/OS-KSCRUD/v1.0' ), 'description': 'description-' + uuid.uuid4().hex, 'updated': '2013-07-07T12:00:0-00:00', diff --git a/openstackclient/tests/unit/identity/v3/test_access_rule.py b/openstackclient/tests/unit/identity/v3/test_access_rule.py index 068dbb5d8..0fc68dd36 100644 --- a/openstackclient/tests/unit/identity/v3/test_access_rule.py +++ b/openstackclient/tests/unit/identity/v3/test_access_rule.py @@ -77,9 +77,7 @@ def test_delete_multi_access_rules_with_exception(self): self.cmd.take_action(parsed_args) self.fail('CommandError should be raised.') except exceptions.CommandError as e: - self.assertEqual( - '1 of 2 access rules failed to' ' delete.', str(e) - ) + self.assertEqual('1 of 2 access rules failed to delete.', str(e)) calls = [] for a in arglist: diff --git a/openstackclient/tests/unit/identity/v3/test_application_credential.py b/openstackclient/tests/unit/identity/v3/test_application_credential.py index 94631d95a..b53042ed1 100644 --- a/openstackclient/tests/unit/identity/v3/test_application_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py @@ -295,7 +295,7 @@ def test_delete_multi_app_creds_with_exception(self): self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual( - '1 of 2 application credentials failed to' ' delete.', str(e) + '1 of 2 application credentials failed to delete.', str(e) ) calls = [] diff --git a/openstackclient/tests/unit/identity/v3/test_mappings.py b/openstackclient/tests/unit/identity/v3/test_mappings.py index 60a89dd61..5a3fad97a 100644 --- a/openstackclient/tests/unit/identity/v3/test_mappings.py +++ b/openstackclient/tests/unit/identity/v3/test_mappings.py @@ -55,7 +55,7 @@ def test_create_mapping(self): mocker = mock.Mock() mocker.return_value = identity_fakes.MAPPING_RULES with mock.patch( - "openstackclient.identity.v3.mapping." "CreateMapping._read_rules", + "openstackclient.identity.v3.mapping.CreateMapping._read_rules", mocker, ): columns, data = self.cmd.take_action(parsed_args) @@ -170,7 +170,7 @@ def test_set_new_rules(self): mocker = mock.Mock() mocker.return_value = identity_fakes.MAPPING_RULES_2 with mock.patch( - "openstackclient.identity.v3.mapping." "SetMapping._read_rules", + "openstackclient.identity.v3.mapping.SetMapping._read_rules", mocker, ): result = self.cmd.take_action(parsed_args) diff --git a/openstackclient/tests/unit/integ/cli/test_shell.py b/openstackclient/tests/unit/integ/cli/test_shell.py index 0a3a8ad43..6ac17be5f 100644 --- a/openstackclient/tests/unit/integ/cli/test_shell.py +++ b/openstackclient/tests/unit/integ/cli/test_shell.py @@ -361,8 +361,7 @@ def test_shell_args_options(self): _shell = shell.OpenStackShell() _shell.run( - "--os-username zarquon --os-password qaz " - "extension list".split(), + "--os-username zarquon --os-password qaz extension list".split(), ) # Check general calls diff --git a/openstackclient/tests/unit/network/v2/test_network_trunk.py b/openstackclient/tests/unit/network/v2/test_network_trunk.py index 064c44f37..180e80de5 100644 --- a/openstackclient/tests/unit/network/v2/test_network_trunk.py +++ b/openstackclient/tests/unit/network/v2/test_network_trunk.py @@ -263,7 +263,7 @@ def test_create_network_trunk_subports_without_required_key_fail(self): '--parent-port', self.new_trunk.port_id, '--subport', - 'segmentation-type={seg_type},' 'segmentation-id={seg_id}'.format( + 'segmentation-type={seg_type},segmentation-id={seg_id}'.format( seg_id=subport['segmentation_id'], seg_type=subport['segmentation_type'], ), @@ -727,7 +727,7 @@ def test_set_network_trunk_subports_without_required_key_fail(self): subport = self._trunk['sub_ports'][0] arglist = [ '--subport', - 'segmentation-type={seg_type},' 'segmentation-id={seg_id}'.format( + 'segmentation-type={seg_type},segmentation-id={seg_id}'.format( seg_id=subport['segmentation_id'], seg_type=subport['segmentation_type'], ), diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 3f521a42e..7f5271d10 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -807,8 +807,7 @@ def test_create_port_with_extra_dhcp_option(self): extra_dhcp_options = [ { 'opt_name': 'classless-static-route', - 'opt_value': '169.254.169.254/32,22.2.0.2,' - '0.0.0.0/0,22.2.0.1', + 'opt_value': '169.254.169.254/32,22.2.0.2,0.0.0.0/0,22.2.0.1', 'ip_version': '4', }, { @@ -826,7 +825,7 @@ def test_create_port_with_extra_dhcp_option(self): '0.0.0.0/0,22.2.0.1,' 'ip-version=4', '--extra-dhcp-option', - 'name=dns-server,value=240C::6666,' 'ip-version=6', + 'name=dns-server,value=240C::6666,ip-version=6', 'test-port', ] diff --git a/openstackclient/tests/unit/object/v1/test_object_all.py b/openstackclient/tests/unit/object/v1/test_object_all.py index 484ac1b91..968667b68 100644 --- a/openstackclient/tests/unit/object/v1/test_object_all.py +++ b/openstackclient/tests/unit/object/v1/test_object_all.py @@ -252,9 +252,10 @@ def __enter__(self): def __exit__(self, *a): self.context_manager_calls.append('__exit__') - with mock.patch('sys.stdout') as fake_stdout, mock.patch( - 'os.fdopen', return_value=FakeStdout() - ) as fake_fdopen: + with ( + mock.patch('sys.stdout') as fake_stdout, + mock.patch('os.fdopen', return_value=FakeStdout()) as fake_fdopen, + ): fake_stdout.fileno.return_value = 123 self.cmd.take_action(parsed_args) diff --git a/openstackclient/tests/unit/volume/v1/test_transfer_request.py b/openstackclient/tests/unit/volume/v1/test_transfer_request.py index e73a4c3ba..47d925d4c 100644 --- a/openstackclient/tests/unit/volume/v1/test_transfer_request.py +++ b/openstackclient/tests/unit/volume/v1/test_transfer_request.py @@ -239,7 +239,7 @@ def test_delete_multiple_transfers_with_exception(self): self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual( - '1 of 2 volume transfer requests failed ' 'to delete', + '1 of 2 volume transfer requests failed to delete', str(e), ) diff --git a/openstackclient/tests/unit/volume/v1/test_type.py b/openstackclient/tests/unit/volume/v1/test_type.py index daa495995..bc15bcc4d 100644 --- a/openstackclient/tests/unit/volume/v1/test_type.py +++ b/openstackclient/tests/unit/volume/v1/test_type.py @@ -431,7 +431,7 @@ def test_type_set_new_encryption_without_provider(self): self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual( - "Command Failed: One or more of" " the operations failed", + "Command Failed: One or more of the operations failed", str(e), ) self.encryption_types_mock.create.assert_not_called() diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index 1667f38cd..b293ee57d 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -1619,7 +1619,7 @@ def test_volume_set_with_only_retype_policy(self, mock_warning): result = self.cmd.take_action(parsed_args) self.volumes_mock.retype.assert_not_called() mock_warning.assert_called_with( - "'--retype-policy' option will " "not work without '--type' option" + "'--retype-policy' option will not work without '--type' option" ) self.assertIsNone(result) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py b/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py index 1885a1f5e..2677ddc10 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py @@ -241,7 +241,7 @@ def test_delete_multiple_transfers_with_exception(self): self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual( - '1 of 2 volume transfer requests failed ' 'to delete', + '1 of 2 volume transfer requests failed to delete', str(e), ) diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index 076de3ac3..e3dc01665 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -2027,7 +2027,7 @@ def test_volume_set_with_only_retype_policy(self, mock_warning): result = self.cmd.take_action(parsed_args) self.volumes_mock.retype.assert_not_called() mock_warning.assert_called_with( - "'--retype-policy' option will " "not work without '--type' option" + "'--retype-policy' option will not work without '--type' option" ) self.assertIsNone(result) diff --git a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py index e07679fa8..4c990d840 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py @@ -140,8 +140,7 @@ def test_snapshot_delete_with_remote_force(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn( - "The --force option is not supported with the --remote " - "parameter.", + "The --force option is not supported with the --remote parameter.", str(exc), ) diff --git a/openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py b/openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py index 1871bde73..ffe59db65 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py @@ -284,7 +284,7 @@ def test_delete_multiple_transfers_with_exception(self): self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual( - '1 of 2 volume transfer requests failed ' 'to delete', + '1 of 2 volume transfer requests failed to delete', str(e), ) diff --git a/openstackclient/volume/client.py b/openstackclient/volume/client.py index b07913086..d3a3406a3 100644 --- a/openstackclient/volume/client.py +++ b/openstackclient/volume/client.py @@ -105,9 +105,7 @@ def build_option_parser(parser): '--os-volume-api-version', metavar='', default=utils.env('OS_VOLUME_API_VERSION'), - help=_( - 'Volume API version, default=%s ' '(Env: OS_VOLUME_API_VERSION)' - ) + help=_('Volume API version, default=%s (Env: OS_VOLUME_API_VERSION)') % DEFAULT_API_VERSION, ) return parser diff --git a/openstackclient/volume/v1/qos_specs.py b/openstackclient/volume/v1/qos_specs.py index 74938392e..93f7f48c6 100644 --- a/openstackclient/volume/v1/qos_specs.py +++ b/openstackclient/volume/v1/qos_specs.py @@ -152,8 +152,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.qos_specs) msg = _( - "%(result)s of %(total)s QoS specifications failed" - " to delete." + "%(result)s of %(total)s QoS specifications failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index 3ec995961..78619320f 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -242,8 +242,7 @@ def take_action(self, parsed_args): ) else: msg = _( - "Volume status is not available for setting boot " - "state" + "Volume status is not available for setting boot state" ) raise exceptions.CommandError(msg) except Exception as e: @@ -268,10 +267,7 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) except Exception as e: LOG.error( - _( - "Failed to set volume read-only access " - "mode flag: %s" - ), + _("Failed to set volume read-only access mode flag: %s"), e, ) @@ -339,7 +335,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.volumes) - msg = _("%(result)s of %(total)s volumes failed " "to delete.") % { + msg = _("%(result)s of %(total)s volumes failed to delete.") % { 'result': result, 'total': total, } @@ -628,10 +624,7 @@ def take_action(self, parsed_args): ) except Exception as e: LOG.error( - _( - "Failed to set volume read-only access " - "mode flag: %s" - ), + _("Failed to set volume read-only access mode flag: %s"), e, ) result += 1 @@ -655,7 +648,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("One or more of the " "set operations failed") + _("One or more of the set operations failed") ) diff --git a/openstackclient/volume/v1/volume_backup.py b/openstackclient/volume/v1/volume_backup.py index 77aa81dc5..4819609d3 100644 --- a/openstackclient/volume/v1/volume_backup.py +++ b/openstackclient/volume/v1/volume_backup.py @@ -134,7 +134,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.backups) - msg = _("%(result)s of %(total)s backups failed " "to delete.") % { + msg = _("%(result)s of %(total)s backups failed to delete.") % { 'result': result, 'total': total, } @@ -178,8 +178,7 @@ def get_parser(self, prog_name): "--volume", metavar="", help=_( - "Filters results by the volume which they " - "backup (name or ID)" + "Filters results by the volume which they backup (name or ID)" ), ) parser.add_argument( diff --git a/openstackclient/volume/v1/volume_snapshot.py b/openstackclient/volume/v1/volume_snapshot.py index 4953db129..1d9fb86b1 100644 --- a/openstackclient/volume/v1/volume_snapshot.py +++ b/openstackclient/volume/v1/volume_snapshot.py @@ -73,8 +73,7 @@ def get_parser(self, prog_name): '--volume', metavar='', help=_( - 'Volume to snapshot (name or ID) ' - '(default is )' + 'Volume to snapshot (name or ID) (default is )' ), ) parser.add_argument( @@ -88,8 +87,7 @@ def get_parser(self, prog_name): action='store_true', default=False, help=_( - 'Create a snapshot attached to an instance. ' - 'Default is False' + 'Create a snapshot attached to an instance. Default is False' ), ) return parser @@ -153,9 +151,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.snapshots) - msg = _( - "%(result)s of %(total)s snapshots failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s snapshots failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) @@ -366,7 +365,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("One or more of the " "set operations failed") + _("One or more of the set operations failed") ) diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py index fed0a601f..5bd1cfdc9 100644 --- a/openstackclient/volume/v1/volume_type.py +++ b/openstackclient/volume/v1/volume_type.py @@ -224,7 +224,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.volume_types) msg = _( - "%(result)s of %(total)s volume types failed " "to delete." + "%(result)s of %(total)s volume types failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -403,7 +403,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) @@ -481,8 +481,7 @@ def get_parser(self, prog_name): "--encryption-type", action="store_true", help=_( - "Remove the encryption type for this volume type " - "(admin only)" + "Remove the encryption type for this volume type (admin only)" ), ) return parser @@ -516,5 +515,5 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) diff --git a/openstackclient/volume/v2/consistency_group.py b/openstackclient/volume/v2/consistency_group.py index ee17acc53..347724f92 100644 --- a/openstackclient/volume/v2/consistency_group.py +++ b/openstackclient/volume/v2/consistency_group.py @@ -38,10 +38,7 @@ def _find_volumes(parsed_args_volumes, volume_client): except Exception as e: result += 1 LOG.error( - _( - "Failed to find volume with " - "name or ID '%(volume)s':%(e)s" - ) + _("Failed to find volume with name or ID '%(volume)s':%(e)s") % {'volume': volume, 'e': e} ) @@ -76,7 +73,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.volumes) LOG.error( - _("%(result)s of %(total)s volumes failed " "to add.") + _("%(result)s of %(total)s volumes failed to add.") % {'result': result, 'total': total} ) @@ -236,8 +233,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.consistency_groups) msg = _( - "%(result)s of %(total)s consistency groups failed " - "to delete." + "%(result)s of %(total)s consistency groups failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -321,7 +317,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.volumes) LOG.error( - _("%(result)s of %(total)s volumes failed " "to remove.") + _("%(result)s of %(total)s volumes failed to remove.") % {'result': result, 'total': total} ) diff --git a/openstackclient/volume/v2/consistency_group_snapshot.py b/openstackclient/volume/v2/consistency_group_snapshot.py index 220d4f7f0..3c582d6b1 100644 --- a/openstackclient/volume/v2/consistency_group_snapshot.py +++ b/openstackclient/volume/v2/consistency_group_snapshot.py @@ -123,8 +123,7 @@ def get_parser(self, prog_name): '--all-projects', action="store_true", help=_( - 'Show detail for all projects (admin only) ' - '(defaults to False)' + 'Show detail for all projects (admin only) (defaults to False)' ), ) parser.add_argument( diff --git a/openstackclient/volume/v2/qos_specs.py b/openstackclient/volume/v2/qos_specs.py index fa6cec494..72706a740 100644 --- a/openstackclient/volume/v2/qos_specs.py +++ b/openstackclient/volume/v2/qos_specs.py @@ -153,8 +153,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.qos_specs) msg = _( - "%(result)s of %(total)s QoS specifications failed" - " to delete." + "%(result)s of %(total)s QoS specifications failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 179da1960..8bbee4cb4 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -286,8 +286,7 @@ def take_action(self, parsed_args): ) else: msg = _( - "Volume status is not available for setting boot " - "state" + "Volume status is not available for setting boot state" ) raise exceptions.CommandError(msg) except Exception as e: @@ -312,10 +311,7 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) except Exception as e: LOG.error( - _( - "Failed to set volume read-only access " - "mode flag: %s" - ), + _("Failed to set volume read-only access mode flag: %s"), e, ) @@ -356,8 +352,7 @@ def get_parser(self, prog_name): "--purge", action="store_true", help=_( - "Remove any snapshots along with volume(s) " - "(defaults to False)" + "Remove any snapshots along with volume(s) (defaults to False)" ), ) return parser @@ -387,7 +382,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.volumes) - msg = _("%(result)s of %(total)s volumes failed " "to delete.") % { + msg = _("%(result)s of %(total)s volumes failed to delete.") % { 'result': result, 'total': total, } @@ -829,10 +824,7 @@ def take_action(self, parsed_args): ) except Exception as e: LOG.error( - _( - "Failed to set volume read-only access " - "mode flag: %s" - ), + _("Failed to set volume read-only access mode flag: %s"), e, ) result += 1 @@ -885,7 +877,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("One or more of the " "set operations failed") + _("One or more of the set operations failed") ) @@ -977,5 +969,5 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("One or more of the " "unset operations failed") + _("One or more of the unset operations failed") ) diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index 3c7b42eca..5e0c75b6e 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -345,8 +345,7 @@ def get_parser(self, prog_name): "--force", action="store_true", help=_( - "Restore the backup to an existing volume " - "(default to False)" + "Restore the backup to an existing volume (default to False)" ), ) return parser diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index 797fdc853..e8d5484a2 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -74,8 +74,7 @@ def get_parser(self, prog_name): "--volume", metavar="", help=_( - "Volume to snapshot (name or ID) " - "(default is )" + "Volume to snapshot (name or ID) (default is )" ), ) parser.add_argument( @@ -88,8 +87,7 @@ def get_parser(self, prog_name): action="store_true", default=False, help=_( - "Create a snapshot attached to an instance. " - "Default is False" + "Create a snapshot attached to an instance. Default is False" ), ) parser.add_argument( @@ -200,9 +198,10 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.snapshots) - msg = _( - "%(result)s of %(total)s snapshots failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s snapshots failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) @@ -446,14 +445,14 @@ def take_action(self, parsed_args): volume_client.volume_snapshots.update(snapshot.id, **kwargs) except Exception as e: LOG.error( - _("Failed to update snapshot name " "or description: %s"), + _("Failed to update snapshot name or description: %s"), e, ) result += 1 if result > 0: raise exceptions.CommandError( - _("One or more of the " "set operations failed") + _("One or more of the set operations failed") ) diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index be57f4b50..aaf0cb51f 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -272,8 +272,7 @@ def take_action(self, parsed_args): ) except Exception as e: msg = _( - "Failed to add project %(project)s access to " - "type: %(e)s" + "Failed to add project %(project)s access to type: %(e)s" ) LOG.error(msg % {'project': parsed_args.project, 'e': e}) @@ -363,7 +362,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.volume_types) msg = _( - "%(result)s of %(total)s volume types failed " "to delete." + "%(result)s of %(total)s volume types failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -553,8 +552,7 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - 'Set volume type access to project (name or ID) ' - '(admin only)' + 'Set volume type access to project (name or ID) (admin only)' ), ) public_group = parser.add_mutually_exclusive_group() @@ -646,10 +644,7 @@ def take_action(self, parsed_args): volume_client.volume_types.update(volume_type.id, **kwargs) except Exception as e: LOG.error( - _( - "Failed to update volume type name or" - " description: %s" - ), + _("Failed to update volume type name or description: %s"), e, ) result += 1 @@ -690,7 +685,7 @@ def take_action(self, parsed_args): ) except Exception as e: LOG.error( - _("Failed to set volume type access to " "project: %s"), e + _("Failed to set volume type access to project: %s"), e ) result += 1 @@ -714,7 +709,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) @@ -822,8 +817,7 @@ def get_parser(self, prog_name): "--encryption-type", action="store_true", help=_( - "Remove the encryption type for this volume type " - "(admin only)" + "Remove the encryption type for this volume type (admin only)" ), ) return parser @@ -859,10 +853,7 @@ def take_action(self, parsed_args): ) except Exception as e: LOG.error( - _( - "Failed to remove volume type access from " - "project: %s" - ), + _("Failed to remove volume type access from project: %s"), e, ) result += 1 @@ -881,5 +872,5 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) diff --git a/openstackclient/volume/v3/block_storage_manage.py b/openstackclient/volume/v3/block_storage_manage.py index 5b76b53cb..17c699b99 100644 --- a/openstackclient/volume/v3/block_storage_manage.py +++ b/openstackclient/volume/v3/block_storage_manage.py @@ -317,9 +317,7 @@ def take_action(self, parsed_args): # if the user requested e.g. '--detailed false' then they # should simply stop requesting this since the default has # changed - msg = _( - "The --detailed option has been deprecated. " "Unset it." - ) + msg = _("The --detailed option has been deprecated. Unset it.") self.log.warning(msg) columns = [ diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 6f705ad6f..e0c033997 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -309,8 +309,7 @@ def take_action(self, parsed_args): ) else: msg = _( - "Volume status is not available for setting boot " - "state" + "Volume status is not available for setting boot state" ) raise exceptions.CommandError(msg) except Exception as e: @@ -335,10 +334,7 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) except Exception as e: LOG.error( - _( - "Failed to set volume read-only access " - "mode flag: %s" - ), + _("Failed to set volume read-only access mode flag: %s"), e, ) @@ -402,7 +398,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.volumes) - msg = _("%(result)s of %(total)s volumes failed " "to delete.") % { + msg = _("%(result)s of %(total)s volumes failed to delete.") % { 'result': result, 'total': total, } @@ -847,10 +843,7 @@ def take_action(self, parsed_args): ) except Exception as e: LOG.error( - _( - "Failed to set volume read-only access " - "mode flag: %s" - ), + _("Failed to set volume read-only access mode flag: %s"), e, ) result += 1 @@ -903,7 +896,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("One or more of the " "set operations failed") + _("One or more of the set operations failed") ) @@ -995,7 +988,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("One or more of the " "unset operations failed") + _("One or more of the unset operations failed") ) diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py index dce5646a9..ce5911515 100644 --- a/openstackclient/volume/v3/volume_backup.py +++ b/openstackclient/volume/v3/volume_backup.py @@ -403,8 +403,7 @@ def get_parser(self, prog_name): "--force", action="store_true", help=_( - "Restore the backup to an existing volume " - "(default to False)" + "Restore the backup to an existing volume (default to False)" ), ) return parser diff --git a/openstackclient/volume/v3/volume_snapshot.py b/openstackclient/volume/v3/volume_snapshot.py index 1e528e860..bb7f5a660 100644 --- a/openstackclient/volume/v3/volume_snapshot.py +++ b/openstackclient/volume/v3/volume_snapshot.py @@ -91,7 +91,8 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.snapshots) - msg = _( - "%(result)s of %(total)s snapshots failed " "to delete." - ) % {'result': result, 'total': total} + msg = _("%(result)s of %(total)s snapshots failed to delete.") % { + 'result': result, + 'total': total, + } raise exceptions.CommandError(msg) diff --git a/openstackclient/volume/v3/volume_type.py b/openstackclient/volume/v3/volume_type.py index 03c3fe245..1cad79c0f 100644 --- a/openstackclient/volume/v3/volume_type.py +++ b/openstackclient/volume/v3/volume_type.py @@ -273,8 +273,7 @@ def take_action(self, parsed_args): ) except Exception as e: msg = _( - "Failed to add project %(project)s access to " - "type: %(e)s" + "Failed to add project %(project)s access to type: %(e)s" ) LOG.error(msg % {'project': parsed_args.project, 'e': e}) @@ -364,7 +363,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.volume_types) msg = _( - "%(result)s of %(total)s volume types failed " "to delete." + "%(result)s of %(total)s volume types failed to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -635,8 +634,7 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - 'Set volume type access to project (name or ID) ' - '(admin only)' + 'Set volume type access to project (name or ID) (admin only)' ), ) public_group = parser.add_mutually_exclusive_group() @@ -728,10 +726,7 @@ def take_action(self, parsed_args): volume_client.volume_types.update(volume_type.id, **kwargs) except Exception as e: LOG.error( - _( - "Failed to update volume type name or" - " description: %s" - ), + _("Failed to update volume type name or description: %s"), e, ) result += 1 @@ -772,7 +767,7 @@ def take_action(self, parsed_args): ) except Exception as e: LOG.error( - _("Failed to set volume type access to " "project: %s"), e + _("Failed to set volume type access to project: %s"), e ) result += 1 @@ -796,7 +791,7 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) @@ -904,8 +899,7 @@ def get_parser(self, prog_name): "--encryption-type", action="store_true", help=_( - "Remove the encryption type for this volume type " - "(admin only)" + "Remove the encryption type for this volume type (admin only)" ), ) return parser @@ -941,10 +935,7 @@ def take_action(self, parsed_args): ) except Exception as e: LOG.error( - _( - "Failed to remove volume type access from " - "project: %s" - ), + _("Failed to remove volume type access from project: %s"), e, ) result += 1 @@ -963,5 +954,5 @@ def take_action(self, parsed_args): if result > 0: raise exceptions.CommandError( - _("Command Failed: One or more of" " the operations failed") + _("Command Failed: One or more of the operations failed") ) From 49708f6d3fad52685d5632571df5936054ea0e5c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 19 Mar 2025 12:33:49 +0000 Subject: [PATCH 092/245] Remove tags from README The tags framework has been discontinued [1]. [1] https://governance.openstack.org/tc/reference/tags/index.html Change-Id: Ife108e6ae191641b56e872e4616a3f4ec78281e8 Signed-off-by: Stephen Finucane --- README.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.rst b/README.rst index f526ec6ad..1eb8d0bd6 100644 --- a/README.rst +++ b/README.rst @@ -2,11 +2,6 @@ Team and repository tags ======================== -.. image:: https://governance.openstack.org/tc/badges/python-openstackclient.svg - :target: https://governance.openstack.org/tc/reference/tags/index.html - -.. Change things from this point on - =============== OpenStackClient =============== From 7ef588d6952cf4c90f39759e88fdba38007e5975 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 21 Mar 2025 15:03:41 +0000 Subject: [PATCH 093/245] zuul: Make image job non-voting We may need to remove this soon enough, given the new Docker rate limits that we keep bumping into. Change-Id: Id4a9d8df770d107986b20e4a98835ee4e0b6117d Signed-off-by: Stephen Finucane --- .zuul.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.zuul.yaml b/.zuul.yaml index e688bd26a..2654db30a 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -217,7 +217,8 @@ - release-notes-jobs-python3 check: jobs: - - osc-build-image + - osc-build-image: + voting: false - osc-functional-devstack - osc-functional-devstack-tips: # The functional-tips job only tests the latest and shouldn't be run From 2883f3fb957ca79b984f8b9e312ee500f2194aab Mon Sep 17 00:00:00 2001 From: Dmitriy Chubinidze Date: Fri, 21 Mar 2025 08:56:32 +0000 Subject: [PATCH 094/245] Specifying project-domain for project The fix ensures that if a user wants to set a default project, they must also provide the project domain. If it's missing, an explicit error message is shown, making it clear that the project domain is required. Also adding some unit tests by modifying respective calls. Change-Id: Ia6e921a53da55ab1bce85a42c8160872a9d47d64 Closes-Bug: #2102146 --- openstackclient/identity/v3/user.py | 10 ++++++---- .../tests/unit/identity/v3/test_user.py | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 545b1cda7..4c2aa1db0 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -612,10 +612,12 @@ def take_action(self, parsed_args): if parsed_args.description: kwargs['description'] = parsed_args.description if parsed_args.project: - project_domain_id = identity_client.find_domain( - name_or_id=parsed_args.project_domain, - ignore_missing=False, - ).id + project_domain_id = None + if parsed_args.project_domain: + project_domain_id = identity_client.find_domain( + name_or_id=parsed_args.project_domain, + ignore_missing=False, + ).id project_id = identity_client.find_project( name_or_id=parsed_args.project, ignore_missing=False, diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index 7f4c2497e..91e8b45c3 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -1206,6 +1206,17 @@ def test_user_set_project(self): self.identity_sdk_client.update_user.assert_called_with( user=self.user, **kwargs ) + self.identity_sdk_client.find_domain.assert_not_called() + + # Set expected values + kwargs = { + 'ignore_missing': False, + 'domain_id': None, + } + self.identity_sdk_client.find_project.assert_called_once_with( + name_or_id=self.project.id, **kwargs + ) + self.assertIsNone(result) def test_user_set_project_domain(self): @@ -1238,6 +1249,11 @@ def test_user_set_project_domain(self): self.identity_sdk_client.update_user.assert_called_with( user=self.user, **kwargs ) + + self.identity_sdk_client.find_domain.assert_called_once_with( + name_or_id=self.project.domain_id, ignore_missing=False + ) + self.assertIsNone(result) def test_user_set_enable(self): From 2e5a830276902ff4d17baa2f4b53d33b61998401 Mon Sep 17 00:00:00 2001 From: Alfredo Moralejo Date: Mon, 24 Mar 2025 16:34:39 +0100 Subject: [PATCH 095/245] Replace description-content-type by its underscore name Since v78.0.0, setuptools no longer accepts options containing uppercase or dash characters in setup.cfg [1]. [1] https://github.com/pypa/setuptools/blob/main/NEWS.rst#v7800 Closes-Bug: #2104030 Change-Id: Id88b9c73a4cd9511750f38da9393dae3adbc5c1e --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index f842dcb7d..43d9588bf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,7 @@ name = python-openstackclient summary = OpenStack Command-line Client description_file = README.rst -description-content-type = text/x-rst +description_content_type = text/x-rst author = OpenStack author_email = openstack-discuss@lists.openstack.org home_page = https://docs.openstack.org/python-openstackclient/latest/ From 6d27b2f2b6137d809b630233e7c87bdac5ca0218 Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Mon, 17 Mar 2025 16:08:00 +0100 Subject: [PATCH 096/245] Fix networking quota usage show Quotas details returned from the Neutron service are in different format then quota details returned from Nova and Cinder services. This patch fixes helper function to convert data from Neutron to the same format as data from Nova and Cinder is given. Closes-Bug: #2102513 Change-Id: I18649f6c2ee179b64b7e605f4ea07d4b0c7a1635 --- openstackclient/common/quota.py | 57 +++++++++++++------ .../tests/functional/common/test_quota.py | 2 + .../tests/unit/common/test_quota.py | 29 +++++++++- 3 files changed, 69 insertions(+), 19 deletions(-) diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 638d2e1ce..6faae8b77 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -176,25 +176,37 @@ def get_network_quotas( default=False, ): def _network_quota_to_dict(network_quota, detail=False): - if not isinstance(network_quota, dict): - dict_quota = network_quota.to_dict() - else: - dict_quota = network_quota - - result = {} - + dict_quota = network_quota.to_dict(computed=False) + + if not detail: + return dict_quota + + # Neutron returns quota details in dict which is in format like: + # {'resource_name': {'in_use': X, 'limit': Y, 'reserved': Z}, + # 'resource_name_2': {'in_use': X2, 'limit': Y2, 'reserved': Z2}} + # + # but Nova and Cinder returns quota in different format, like: + # {'resource_name': X, + # 'resource_name_2': X2, + # 'usage': { + # 'resource_name': Y, + # 'resource_name_2': Y2 + # }, + # 'reserved': { + # 'resource_name': Z, + # 'resource_name_2': Z2 + # }} + # + # so we need to make conversion to have data in same format from + # all of the services + result = {"usage": {}, "reservation": {}} for key, values in dict_quota.items(): if values is None: continue - - # NOTE(slaweq): Neutron returns values with key "used" but Nova for - # example returns same data with key "in_use" instead. Because of - # that we need to convert Neutron key to the same as is returned - # from Nova to make result more consistent - if isinstance(values, dict) and 'used' in values: - values['in_use'] = values.pop("used") - - result[key] = values + if isinstance(values, dict): + result[key] = values['limit'] + result["reservation"][key] = values['reserved'] + result["usage"][key] = values['used'] return result @@ -756,6 +768,19 @@ def take_action(self, parsed_args): ) info = {} + if parsed_args.usage: + info["reservation"] = compute_quota_info.pop("reservation", {}) + info["reservation"].update( + volume_quota_info.pop("reservation", {}) + ) + info["reservation"].update( + network_quota_info.pop("reservation", {}) + ) + + info["usage"] = compute_quota_info.pop("usage", {}) + info["usage"].update(volume_quota_info.pop("usage", {})) + info["usage"].update(network_quota_info.pop("usage", {})) + info.update(compute_quota_info) info.update(volume_quota_info) info.update(network_quota_info) diff --git a/openstackclient/tests/functional/common/test_quota.py b/openstackclient/tests/functional/common/test_quota.py index 677db5036..a2edc4259 100644 --- a/openstackclient/tests/functional/common/test_quota.py +++ b/openstackclient/tests/functional/common/test_quota.py @@ -250,6 +250,8 @@ def test_quota_show_usage_option(self): row_headers = [str(r) for r in row.keys()] self.assertEqual(sorted(expected_headers), sorted(row_headers)) resources.append(row['Resource']) + for header in expected_headers[1:]: + self.assertIsInstance(row[header], int) # Ensure that returned quota has network quota... self.assertIn("networks", resources) # ...and compute quota diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index e18161d4e..fd18b0619 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -955,6 +955,23 @@ def test_quota_set_with_no_force(self): class TestQuotaShow(TestQuota): + _network_quota_details = { + 'floating_ips': {'limit': 0, 'reserved': 0, 'used': 0}, + 'health_monitors': {'limit': 0, 'reserved': 0, 'used': 0}, + 'l7_policies': {'limit': 0, 'reserved': 0, 'used': 0}, + 'listeners': {'limit': 0, 'reserved': 0, 'used': 0}, + 'load_balancers': {'limit': 0, 'reserved': 0, 'used': 0}, + 'networks': {'limit': 0, 'reserved': 0, 'used': 0}, + 'pools': {'limit': 0, 'reserved': 0, 'used': 0}, + 'ports': {'limit': 0, 'reserved': 0, 'used': 0}, + 'rbac_policies': {'limit': 0, 'reserved': 0, 'used': 0}, + 'routers': {'limit': 0, 'reserved': 0, 'used': 0}, + 'security_group_rules': {'limit': 0, 'reserved': 0, 'used': 0}, + 'security_groups': {'limit': 0, 'reserved': 0, 'used': 0}, + 'subnet_pools': {'limit': 0, 'reserved': 0, 'used': 0}, + 'subnets': {'limit': 0, 'reserved': 0, 'used': 0}, + } + def setUp(self): super().setUp() @@ -980,9 +997,15 @@ def setUp(self): self.default_volume_quotas ) - self.network_client.get_quota.return_value = ( - sdk_fakes.generate_fake_resource(_network_quota_set.Quota) - ) + def get_network_quota_mock(*args, **kwargs): + if kwargs.get("details"): + return sdk_fakes.generate_fake_resource( + _network_quota_set.QuotaDetails, + **self._network_quota_details, + ) + return sdk_fakes.generate_fake_resource(_network_quota_set.Quota) + + self.network_client.get_quota.side_effect = get_network_quota_mock self.default_network_quotas = sdk_fakes.generate_fake_resource( _network_quota_set.QuotaDefault ) From 07515cd1602841851d72dac30c7b12affee27bbe Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Fri, 21 Mar 2025 06:26:36 +0000 Subject: [PATCH 097/245] [Neutron] Add "qos-policy" parameter to router creation command This patch adds the parameter "qos-policy" to the router creation command. Closes-Bug: #2103774 Change-Id: I742b3273c5e9d3ec16e8018beddc8cdace8a57c6 --- openstackclient/network/v2/router.py | 12 ++++ .../functional/network/v2/test_router.py | 38 +++++++++++ .../tests/unit/network/v2/test_router.py | 63 +++++++++++++++++++ ...eate-with-qos-policy-b94967a35351cddd.yaml | 7 +++ 4 files changed, 120 insertions(+) create mode 100644 releasenotes/notes/router-create-with-qos-policy-b94967a35351cddd.yaml diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 0deae3600..da35d367e 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -581,6 +581,11 @@ def get_parser(self, prog_name): help=argparse.SUPPRESS, ) _parser_add_bfd_ecmp_arguments(parser) + parser.add_argument( + '--qos-policy', + metavar='', + help=_('Attach QoS policy to router gateway IPs'), + ) return parser @@ -603,6 +608,13 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) + if parsed_args.qos_policy and not parsed_args.external_gateways: + msg = _( + "You must specify '--external-gateway' in order " + "to define a QoS policy" + ) + raise exceptions.CommandError(msg) + if parsed_args.enable_ndp_proxy is not None: attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy diff --git a/openstackclient/tests/functional/network/v2/test_router.py b/openstackclient/tests/functional/network/v2/test_router.py index 89d065235..90708324b 100644 --- a/openstackclient/tests/functional/network/v2/test_router.py +++ b/openstackclient/tests/functional/network/v2/test_router.py @@ -44,6 +44,44 @@ def test_router_create_and_delete(self): del_output = self.openstack('router delete ' + name1 + ' ' + name2) self.assertOutput('', del_output) + def test_router_create_with_external_gateway(self): + network_name = uuid.uuid4().hex + subnet_name = uuid.uuid4().hex + qos_policy = uuid.uuid4().hex + router_name = uuid.uuid4().hex + + cmd_net = self.openstack( + f'network create --external {network_name}', parse_output=True + ) + self.addCleanup(self.openstack, f'network delete {network_name}') + network_id = cmd_net['id'] + + self.openstack( + f'subnet create {subnet_name} ' + f'--network {network_name} --subnet-range 10.0.0.0/24' + ) + + cmd_qos = self.openstack( + f'network qos policy create {qos_policy}', parse_output=True + ) + self.addCleanup( + self.openstack, f'network qos policy delete {qos_policy}' + ) + qos_id = cmd_qos['id'] + + self.openstack( + f'router create --external-gateway {network_name} ' + f'--qos-policy {qos_policy} {router_name}' + ) + self.addCleanup(self.openstack, f'router delete {router_name}') + + cmd_output = self.openstack( + f'router show {router_name}', parse_output=True + ) + gw_info = cmd_output['external_gateway_info'] + self.assertEqual(network_id, gw_info['network_id']) + self.assertEqual(qos_id, gw_info['qos_policy_id']) + def test_router_list(self): """Test create, list filter""" # Get project IDs diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index aa6de7d2e..70993639c 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -552,6 +552,69 @@ def test_create_with_enable_default_route_ecmp_no_extension(self): parsed_args, ) + def test_create_with_qos_policy(self): + _network = network_fakes.create_one_network() + self.network_client.find_network = mock.Mock(return_value=_network) + _qos_policy = ( + network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + ) + self.network_client.find_qos_policy = mock.Mock( + return_value=_qos_policy + ) + arglist = [ + self.new_router.name, + '--external-gateway', + _network.id, + '--qos-policy', + _qos_policy.id, + ] + verifylist = [ + ('name', self.new_router.name), + ('enable', True), + ('distributed', False), + ('ha', False), + ('qos_policy', _qos_policy.id), + ('external_gateways', [_network.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + gw_info = {'network_id': _network.id, 'qos_policy_id': _qos_policy.id} + self.network_client.create_router.assert_called_once_with( + **{ + 'admin_state_up': True, + 'name': self.new_router.name, + **{'external_gateway_info': gw_info}, + } + ) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_create_with_qos_policy_no_external_gateway(self): + _qos_policy = ( + network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + ) + self.network_client.find_qos_policy = mock.Mock( + return_value=_qos_policy + ) + arglist = [ + self.new_router.name, + '--qos-policy', + _qos_policy.id, + ] + verifylist = [ + ('name', self.new_router.name), + ('enable', True), + ('distributed', False), + ('ha', False), + ('qos_policy', _qos_policy.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + class TestDeleteRouter(TestRouter): # The routers to delete. diff --git a/releasenotes/notes/router-create-with-qos-policy-b94967a35351cddd.yaml b/releasenotes/notes/router-create-with-qos-policy-b94967a35351cddd.yaml new file mode 100644 index 000000000..67150ece9 --- /dev/null +++ b/releasenotes/notes/router-create-with-qos-policy-b94967a35351cddd.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + The router creation command now has the parameter ``--qos-policy``, that + allows to set a QoS policy for the provided external gateways (one or + many). It is mandatory to define an external gateway if the QoS policy is + set. From 34831172598d20e211147a8c3758d2b3383ea256 Mon Sep 17 00:00:00 2001 From: Jan Ueberacker Date: Tue, 25 Mar 2025 15:08:24 +0100 Subject: [PATCH 098/245] Add filters to search for enabled/disabled users and projects Change-Id: Ie7d84f9e0158018083af2156d02dc86fefd79256 Signed-off-by: Jan Ueberacker --- openstackclient/identity/v3/project.py | 17 ++++++++++ openstackclient/identity/v3/user.py | 33 +++++++++++++++++-- .../tests/unit/identity/v3/test_project.py | 16 +++++++++ .../tests/unit/identity/v3/test_user.py | 18 ++++++++++ ...ject-enabled-filters-9f2090cdcc97b667.yaml | 4 +++ 5 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/add-user-project-enabled-filters-9f2090cdcc97b667.yaml diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index aa6f62949..2c00df0b4 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -239,6 +239,20 @@ def get_parser(self, prog_name): 'keys and directions.' ), ) + parser.add_argument( + '--enabled', + action='store_true', + dest='is_enabled', + default=None, + help=_('List only enabled projects'), + ) + parser.add_argument( + '--disabled', + action='store_false', + dest='is_enabled', + default=None, + help=_('List only disabled projects'), + ) tag.add_tag_filtering_option_to_parser(parser, _('projects')) return parser @@ -277,6 +291,9 @@ def take_action(self, parsed_args): kwargs['user'] = user_id + if parsed_args.is_enabled is not None: + kwargs['is_enabled'] = parsed_args.is_enabled + tag.get_tag_filtering_args(parsed_args, kwargs) if parsed_args.my_projects: diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 545b1cda7..9b119c80b 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -411,6 +411,24 @@ def get_parser(self, prog_name): default=False, help=_('List additional fields in output'), ) + parser.add_argument( + '--enabled', + action='store_true', + dest='is_enabled', + default=None, + help=_( + 'List only enabled users, does nothing with --project and --group' + ), + ) + parser.add_argument( + '--disabled', + action='store_false', + dest='is_enabled', + default=None, + help=_( + 'List only disabled users, does nothing with --project and --group' + ), + ) return parser def take_action(self, parsed_args): @@ -430,6 +448,9 @@ def take_action(self, parsed_args): ignore_missing=False, ).id + if parsed_args.is_enabled is not None: + enabled = parsed_args.is_enabled + if parsed_args.project: if domain is not None: project = identity_client.find_project( @@ -468,9 +489,15 @@ def take_action(self, parsed_args): group=group, ) else: - data = identity_client.users( - domain_id=domain, - ) + if parsed_args.is_enabled is not None: + data = identity_client.users( + domain_id=domain, + is_enabled=enabled, + ) + else: + data = identity_client.users( + domain_id=domain, + ) # Column handling if parsed_args.long: diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py index 9085498e3..7788cf02d 100644 --- a/openstackclient/tests/unit/identity/v3/test_project.py +++ b/openstackclient/tests/unit/identity/v3/test_project.py @@ -941,6 +941,22 @@ def test_project_list_my_projects(self): ) self.assertEqual(datalist, tuple(data)) + def test_project_list_with_option_enabled(self): + arglist = ['--enabled'] + verifylist = [('is_enabled', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + kwargs = {'is_enabled': True} + self.projects_mock.list.assert_called_with(**kwargs) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + class TestProjectSet(TestProject): domain = identity_fakes.FakeDomain.create_one_domain() diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index 7f4c2497e..4eea6c680 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -988,6 +988,24 @@ def test_user_list_project(self): self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) + def test_user_list_with_option_enabled(self): + arglist = ['--enabled'] + verifylist = [('is_enabled', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + kwargs = {'domain_id': None, 'is_enabled': True} + self.identity_sdk_client.users.assert_called_with(**kwargs) + self.identity_sdk_client.find_user.assert_not_called() + self.identity_sdk_client.group_users.assert_not_called() + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, tuple(data)) + class TestUserSet(identity_fakes.TestIdentityv3): project = sdk_fakes.generate_fake_resource(_project.Project) diff --git a/releasenotes/notes/add-user-project-enabled-filters-9f2090cdcc97b667.yaml b/releasenotes/notes/add-user-project-enabled-filters-9f2090cdcc97b667.yaml new file mode 100644 index 000000000..a812ca6b7 --- /dev/null +++ b/releasenotes/notes/add-user-project-enabled-filters-9f2090cdcc97b667.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add filters to search for enabled and disabled users and projects. From 4dbfc4755217c4d8690cd2c55e04b87476efb0db Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Tue, 25 Mar 2025 12:08:52 +0000 Subject: [PATCH 099/245] Return the ``port`` column headers expected in the list command In [1], it was added the ability to print in the "port list" command any field not defined in the hardcoded column set for this command. But in [2], it was added a filter list in the API call in order to reduce the CLI execution time. The unintentional drawback of this optimization was that is no longer possible to print any field outside the "port list" column set. Because the optimization if preferred and it is always possible to use "port show" to see all the port fields, the code added in [1] is removed. [1]https://review.opendev.org/c/openstack/python-openstackclient/+/522901 [2]https://review.opendev.org/c/openstack/python-openstackclient/+/754117 Closes-Bug: #2098980 Related-Bug: #1707848 Related-Bug: #1897100 Change-Id: Ia944b8e108c454219d642cfa595ffafdf060a57f --- openstackclient/network/v2/port.py | 7 ++----- .../tests/functional/network/v2/test_port.py | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 3579194bf..4ff2a6592 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -944,15 +944,12 @@ def take_action(self, parsed_args): for item in columns ] - headers, attrs = utils.calculate_header_and_attrs( - column_headers, columns, parsed_args - ) return ( - headers, + column_headers, ( utils.get_item_properties( s, - attrs, + columns, formatters=_list_formatters, ) for s in data diff --git a/openstackclient/tests/functional/network/v2/test_port.py b/openstackclient/tests/functional/network/v2/test_port.py index 453039550..e1f251abf 100644 --- a/openstackclient/tests/functional/network/v2/test_port.py +++ b/openstackclient/tests/functional/network/v2/test_port.py @@ -12,6 +12,8 @@ import uuid +from tempest.lib import exceptions as tempest_exc + from openstackclient.tests.functional.network.v2 import common @@ -162,8 +164,16 @@ def test_port_list(self): id_list = [p['ID'] for p in json_output] self.assertIn(id1, id_list) self.assertIn(id2, id_list) - # Check an unknown field exists - self.assertIn('device_id', json_output[0]) + # Check an unknown field does not exist + self.assertNotIn('device_id', json_output[0]) + + # Test list with only unknown fields + exc = self.assertRaises( + tempest_exc.CommandFailed, + self.openstack, + 'port list -c device_id', + ) + self.assertIn("No recognized column names in ['device_id']", str(exc)) def test_port_set(self): """Test create, set, show, delete""" From 107c6b143fcd4dca24627400c4a1948402cd657e Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Thu, 13 Mar 2025 17:10:27 -0400 Subject: [PATCH 100/245] Fix neutron typos and formatting Just trying to make things consistent in the neutron files. TrivialFix Change-Id: I9c0ac838f5a956f55161e1636472cca761b14781 --- .../network/v2/floating_ip_port_forwarding.py | 6 +-- .../network/v2/l3_conntrack_helper.py | 8 +-- openstackclient/network/v2/local_ip.py | 26 +++++----- openstackclient/network/v2/ndp_proxy.py | 12 ++--- openstackclient/network/v2/network.py | 14 +++--- .../v2/network_auto_allocated_topology.py | 6 +-- openstackclient/network/v2/network_flavor.py | 2 +- .../network/v2/network_flavor_profile.py | 8 +-- openstackclient/network/v2/network_meter.py | 2 +- .../network/v2/network_qos_policy.py | 6 +-- .../network/v2/network_qos_rule.py | 6 +-- openstackclient/network/v2/network_rbac.py | 2 +- .../network/v2/network_segment_range.py | 2 +- openstackclient/network/v2/network_trunk.py | 14 +++--- openstackclient/network/v2/port.py | 40 +++++++-------- openstackclient/network/v2/router.py | 42 ++++++++-------- openstackclient/network/v2/security_group.py | 4 +- openstackclient/network/v2/subnet.py | 50 +++++++++---------- 18 files changed, 126 insertions(+), 124 deletions(-) diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py index 9151f0498..b51326742 100644 --- a/openstackclient/network/v2/floating_ip_port_forwarding.py +++ b/openstackclient/network/v2/floating_ip_port_forwarding.py @@ -149,7 +149,7 @@ def get_parser(self, prog_name): '--description', metavar='', help=_( - "A text to describe/contextualize the use of the " + "Text to describe/contextualize the use of the " "port forwarding configuration" ), ) @@ -280,7 +280,7 @@ def get_parser(self, prog_name): ) parser.add_argument( '--protocol', - metavar='protocol', + metavar='', help=_("Filter the list result by the port protocol"), ) @@ -409,7 +409,7 @@ def get_parser(self, prog_name): '--description', metavar='', help=_( - "A text to describe/contextualize the use of " + "Text to describe/contextualize the use of " "the port forwarding configuration" ), ) diff --git a/openstackclient/network/v2/l3_conntrack_helper.py b/openstackclient/network/v2/l3_conntrack_helper.py index f200090b7..e5717c3f4 100644 --- a/openstackclient/network/v2/l3_conntrack_helper.py +++ b/openstackclient/network/v2/l3_conntrack_helper.py @@ -99,7 +99,7 @@ def get_parser(self, prog_name): parser.add_argument( 'router', metavar='', - help=_('Router that the conntrack helper belong to'), + help=_('Router that the conntrack helper belongs to'), ) parser.add_argument( 'conntrack_helper_id', @@ -147,7 +147,7 @@ def get_parser(self, prog_name): parser.add_argument( 'router', metavar='', - help=_('Router that the conntrack helper belong to'), + help=_('Router that the conntrack helper belongs to'), ) parser.add_argument( '--helper', @@ -210,7 +210,7 @@ def get_parser(self, prog_name): parser.add_argument( 'router', metavar='', - help=_('Router that the conntrack helper belong to'), + help=_('Router that the conntrack helper belongs to'), ) parser.add_argument( 'conntrack_helper_id', @@ -257,7 +257,7 @@ def get_parser(self, prog_name): parser.add_argument( 'router', metavar='', - help=_('Router that the conntrack helper belong to'), + help=_('Router that the conntrack helper belongs to'), ) parser.add_argument( 'conntrack_helper_id', diff --git a/openstackclient/network/v2/local_ip.py b/openstackclient/network/v2/local_ip.py index 0d92f2c11..14890c3da 100644 --- a/openstackclient/network/v2/local_ip.py +++ b/openstackclient/network/v2/local_ip.py @@ -74,30 +74,32 @@ class CreateLocalIP(command.ShowOne): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( - '--name', metavar="", help=_("New local IP name") + '--name', metavar="", help=_("New Local IP name") ) parser.add_argument( '--description', metavar="", - help=_("New local IP description"), + help=_("Description for Local IP"), ) parser.add_argument( '--network', metavar='', - help=_("Network to allocate Local IP (name or ID)"), + help=_("Network to allocate Local IP from (name or ID)"), ) parser.add_argument( '--local-port', metavar='', - help=_("Port to allocate Local IP (name or ID)"), + help=_("Port to allocate Local IP from (name or ID)"), ) parser.add_argument( "--local-ip-address", metavar="", - help=_("IP address or CIDR "), + help=_("IP address or CIDR for Local IP"), ) parser.add_argument( - '--ip-mode', metavar='', help=_("local IP ip mode") + '--ip-mode', + metavar='', + help=_("IP mode to use for Local IP"), ) identity_common.add_project_domain_option_to_parser(parser) @@ -116,7 +118,7 @@ def take_action(self, parsed_args): class DeleteLocalIP(command.Command): - _description = _("Delete local IP(s)") + _description = _("Delete Local IP(s)") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -156,7 +158,7 @@ def take_action(self, parsed_args): class SetLocalIP(command.Command): - _description = _("Set local ip properties") + _description = _("Set Local IP properties") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -171,7 +173,7 @@ def get_parser(self, prog_name): parser.add_argument( '--description', metavar="", - help=_('Set local IP description'), + help=_('Set Local IP description'), ) return parser @@ -188,7 +190,7 @@ def take_action(self, parsed_args): class ListLocalIP(command.Lister): - _description = _("List local IPs") + _description = _("List Local IPs") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -196,7 +198,7 @@ def get_parser(self, prog_name): parser.add_argument( '--name', metavar='', - help=_("List only local IPs of given name in output"), + help=_("List only Local IPs of given name in output"), ) parser.add_argument( '--project', @@ -295,7 +297,7 @@ def take_action(self, parsed_args): class ShowLocalIP(command.ShowOne): - _description = _("Display local IP details") + _description = _("Display Local IP details") def get_parser(self, prog_name): parser = super().get_parser(prog_name) diff --git a/openstackclient/network/v2/ndp_proxy.py b/openstackclient/network/v2/ndp_proxy.py index 4c1871172..51f77f5cf 100644 --- a/openstackclient/network/v2/ndp_proxy.py +++ b/openstackclient/network/v2/ndp_proxy.py @@ -69,7 +69,7 @@ def get_parser(self, prog_name): '--description', metavar='', help=_( - "A text to describe/contextualize the use of the " + "Text to describe/contextualize the use of the " "NDP proxy configuration" ), ) @@ -156,18 +156,18 @@ def get_parser(self, prog_name): ) parser.add_argument( '--ip-address', - metavar='ip-address', - help=_("List only NDP proxies according to their IPv6 address"), + metavar='', + help=_("List only NDP proxies associated to this IPv6 address"), ) parser.add_argument( '--project', metavar='', - help=_("List NDP proxies according to their project (name or ID)"), + help=_("List only NDP proxies of given project (name or ID)"), ) parser.add_argument( '--name', metavar='', - help=_("List NDP proxies according to their name"), + help=_("List only NDP proxies of given name"), ) identity_common.add_project_domain_option_to_parser(parser) @@ -247,7 +247,7 @@ def get_parser(self, prog_name): '--description', metavar='', help=_( - "A text to describe/contextualize the use of " + "Text to describe/contextualize the use of " "the NDP proxy configuration" ), ) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 7636de0c9..f63e791c3 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -166,7 +166,7 @@ def _add_additional_network_options(parser): help=_( "The physical mechanism by which the virtual network " "is implemented. For example: " - "flat, geneve, gre, local, vlan, vxlan." + "flat, geneve, gre, local, vlan or vxlan." ), ) parser.add_argument( @@ -292,8 +292,8 @@ def update_parser_network(self, parser): action='store_true', help=self.enhance_help_neutron( _( - "The network has an external routing facility that's not " - "managed by Neutron and can be used as in: " + "The network has an external routing facility that is not " + "managed by Neutron and can be used. For example: " "openstack router set --external-gateway NETWORK " "(external-net extension required)" ) @@ -537,7 +537,7 @@ def update_parser_network(self, parser): _( "List networks according to their physical mechanisms. " "The supported options are: flat, geneve, gre, local, " - "vlan, vxlan." + "vlan and vxlan." ) ), ) @@ -789,10 +789,10 @@ def get_parser(self, prog_name): '--external', action='store_true', help=_( - "The network has an external routing facility that's not " - "managed by Neutron and can be used as in: " + "The network has an external routing facility that is not " + "managed by Neutron and can be used. For example: " "openstack router set --external-gateway NETWORK " - "(external-net extension required)" + "(external-net extension required)." ), ) external_router_grp.add_argument( diff --git a/openstackclient/network/v2/network_auto_allocated_topology.py b/openstackclient/network/v2/network_auto_allocated_topology.py index 9f382eacf..a0dcfa47e 100644 --- a/openstackclient/network/v2/network_auto_allocated_topology.py +++ b/openstackclient/network/v2/network_auto_allocated_topology.py @@ -71,7 +71,7 @@ def get_parser(self, prog_name): metavar='', help=_( "Return the auto allocated topology for a given project. " - "Default is current project" + "Default is current project." ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -89,7 +89,7 @@ def get_parser(self, prog_name): default=True, help=_( "If topology exists returns the topology's " - "information (Default)" + "information (default)" ), ) @@ -132,7 +132,7 @@ def get_parser(self, prog_name): metavar='', help=_( 'Delete auto allocated topology for a given project. ' - 'Default is the current project' + 'Default is the current project.' ), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py index 8bc526201..c4d262dea 100644 --- a/openstackclient/network/v2/network_flavor.py +++ b/openstackclient/network/v2/network_flavor.py @@ -102,7 +102,7 @@ def get_parser(self, prog_name): metavar="", required=True, help=_( - 'Service type to which the flavor applies to: e.g. VPN ' + 'Service type to which the flavor applies. For example: VPN ' '(See openstack network service provider list for loaded ' 'examples.)' ), diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py index d9a4b5d2e..fc3f02cf9 100644 --- a/openstackclient/network/v2/network_flavor_profile.py +++ b/openstackclient/network/v2/network_flavor_profile.py @@ -79,14 +79,14 @@ def get_parser(self, prog_name): '--driver', help=_( "Python module path to driver. This becomes " - "required if --metainfo is missing and vice versa" + "required if --metainfo is missing and vice-versa." ), ) parser.add_argument( '--metainfo', help=_( "Metainfo for the flavor profile. This becomes " - "required if --driver is missing and vice versa" + "required if --driver is missing and vice-versa." ), ) @@ -217,14 +217,14 @@ def get_parser(self, prog_name): '--driver', help=_( "Python module path to driver. This becomes " - "required if --metainfo is missing and vice versa" + "required if --metainfo is missing and vice-versa." ), ) parser.add_argument( '--metainfo', help=_( "Metainfo for the flavor profile. This becomes " - "required if --driver is missing and vice versa" + "required if --driver is missing and vice-versa." ), ) diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py index 5a6a8f2f2..22df0f90b 100644 --- a/openstackclient/network/v2/network_meter.py +++ b/openstackclient/network/v2/network_meter.py @@ -70,7 +70,7 @@ def get_parser(self, prog_name): parser.add_argument( '--description', metavar='', - help=_("Create description for meter"), + help=_("Description for meter"), ) parser.add_argument( '--project', diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py index 5023ea988..07eb2ff2f 100644 --- a/openstackclient/network/v2/network_qos_policy.py +++ b/openstackclient/network/v2/network_qos_policy.py @@ -190,7 +190,7 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - "List qos policies according to their project (name or ID)" + "List QoS policies according to their project (name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -198,12 +198,12 @@ def get_parser(self, prog_name): shared_group.add_argument( '--share', action='store_true', - help=_("List qos policies shared between projects"), + help=_("List QoS policies shared between projects"), ) shared_group.add_argument( '--no-share', action='store_true', - help=_("List qos policies not shared between projects"), + help=_("List QoS policies not shared between projects"), ) return parser diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py index 0b5e06fe5..0784de890 100644 --- a/openstackclient/network/v2/network_qos_rule.py +++ b/openstackclient/network/v2/network_qos_rule.py @@ -378,7 +378,7 @@ def get_parser(self, prog_name): parser.add_argument( 'id', metavar='', - help=_('Network QoS rule to delete (ID)'), + help=_('Network QoS rule to set (ID)'), ) _add_rule_arguments(parser) return parser @@ -424,7 +424,7 @@ def get_parser(self, prog_name): parser.add_argument( 'id', metavar='', - help=_('Network QoS rule to delete (ID)'), + help=_('Network QoS rule to show (ID)'), ) return parser @@ -442,7 +442,7 @@ def take_action(self, parsed_args): rule_id, qos.id ) except Exception as e: - msg = _('Failed to set Network QoS rule ID "%(rule)s": %(e)s') % { + msg = _('Failed to show Network QoS rule ID "%(rule)s": %(e)s') % { 'rule': rule_id, 'e': e, } diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py index e3e5aff19..e51ac4baf 100644 --- a/openstackclient/network/v2/network_rbac.py +++ b/openstackclient/network/v2/network_rbac.py @@ -147,7 +147,7 @@ def get_parser(self, prog_name): target_project_group.add_argument( '--target-all-projects', action='store_true', - help=_('Allow creating RBAC policy for all projects.'), + help=_('Allow creating RBAC policy for all projects'), ) parser.add_argument( '--target-project-domain', diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py index aa0509969..9a440f4ae 100644 --- a/openstackclient/network/v2/network_segment_range.py +++ b/openstackclient/network/v2/network_segment_range.py @@ -125,7 +125,7 @@ def get_parser(self, prog_name): metavar='', help=_( 'Network segment range owner (name or ID). Optional when ' - 'the segment range is shared' + 'the segment range is shared.' ), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index a2b0fbf1f..403cb11b8 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -69,8 +69,8 @@ def get_parser(self, prog_name): help=_( "Subport to add. Subport is of form " "'port=,segmentation-type=," - "segmentation-id=' (--subport) option " - "can be repeated" + "segmentation-id=' (repeat option " + "to add multiple subports)" ), ) admin_group = parser.add_mutually_exclusive_group() @@ -199,9 +199,9 @@ def get_parser(self, prog_name): required_keys=['port'], help=_( "Subport to add. Subport is of form " - "'port=,segmentation-type=" - ",segmentation-id=' (--subport) option " - "can be repeated" + "'port=,segmentation-type=," + "segmentation-id=' (repeat option " + "to add multiple subports)" ), ) admin_group = parser.add_mutually_exclusive_group() @@ -308,8 +308,8 @@ def get_parser(self, prog_name): action='append', dest='unset_subports', help=_( - "Subport to delete (name or ID of the port) " - "(--subport) option can be repeated" + "Subport to unset (name or ID of the port) " + "(repeat option to unset multiple subports)" ), ) return parser diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 3579194bf..b500bf2fa 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -347,8 +347,8 @@ def _add_updatable_args(parser, create=False): help=_( "VNIC type for this port (direct | direct-physical | " "macvtap | normal | baremetal | virtio-forwarder | vdpa | " - "remote-managed, " - "default: normal)" + "remote-managed) " + "(default: normal)" ), ) parser.add_argument( @@ -406,7 +406,7 @@ def _add_updatable_args(parser, create=False): '(requires port-hints extension) ' '(requires port-hint-ovs-tx-steering extension for alias: ' 'ovs-tx-steering) ' - '(repeat option to set multiple hints)' + '(repeat option to set multiple hints).' ), ) port_trusted = parser.add_mutually_exclusive_group() @@ -416,7 +416,7 @@ def _add_updatable_args(parser, create=False): help=_( "Set port to be trusted. This will be populated into the " "'binding:profile' dictionary and passed to the services " - "which expect it in this dictionary (for example, Nova)" + "which expect it in this dictionary (for example, Nova)." ), ) port_trusted.add_argument( @@ -425,7 +425,7 @@ def _add_updatable_args(parser, create=False): help=_( "Set port to be not trusted. This will be populated into the " "'binding:profile' dictionary and passed to the services " - "which expect it in this dictionary (for example, Nova)" + "which expect it in this dictionary (for example, Nova)." ), ) @@ -512,7 +512,7 @@ def get_parser(self, prog_name): fixed_ip.add_argument( '--no-fixed-ip', action='store_true', - help=_("No IP or subnet for this port."), + help=_("No IP or subnet set for this port"), ) parser.add_argument( '--binding-profile', @@ -520,8 +520,8 @@ def get_parser(self, prog_name): action=JSONKeyValueAction, help=_( "Custom data to be passed as binding:profile. Data may " - "be passed as = or JSON. " - "(repeat option to set multiple binding:profile data)" + "be passed as = or JSON " + "(repeat option to set multiple binding:profile data)." ), ) admin_group = parser.add_mutually_exclusive_group() @@ -596,7 +596,7 @@ def get_parser(self, prog_name): port_security.add_argument( '--enable-port-security', action='store_true', - help=_("Enable port security for this port (Default)"), + help=_("Enable port security for this port (default)"), ) port_security.add_argument( '--disable-port-security', @@ -619,7 +619,7 @@ def get_parser(self, prog_name): parser.add_argument( '--device-profile', metavar='', - help=_('Cyborg port device profile'), + help=_('Port device profile'), ) parser.add_argument( '--hardware-offload-type', @@ -996,7 +996,7 @@ def get_parser(self, prog_name): '--no-fixed-ip', action='store_true', help=_( - "Clear existing information of fixed IP addresses." + "Clear existing information of fixed IP addresses. " "Specify both --fixed-ip and --no-fixed-ip " "to overwrite the current fixed IP addresses." ), @@ -1007,8 +1007,8 @@ def get_parser(self, prog_name): action=JSONKeyValueAction, help=_( "Custom data to be passed as binding:profile. Data may " - "be passed as = or JSON. " - "(repeat option to set multiple binding:profile data)" + "be passed as = or JSON " + "(repeat option to set multiple binding:profile data)." ), ) parser.add_argument( @@ -1075,8 +1075,8 @@ def get_parser(self, prog_name): help=_( "Clear existing allowed-address pairs associated " "with this port. " - "(Specify both --allowed-address and --no-allowed-address " - "to overwrite the current allowed-address pairs)" + "Specify both --allowed-address and --no-allowed-address " + "to overwrite the current allowed-address pairs." ), ) parser.add_argument( @@ -1100,7 +1100,7 @@ def get_parser(self, prog_name): help=_( "Set data plane status of this port (ACTIVE | DOWN). " "Unset it to None with the 'port unset' command " - "(requires data plane status extension)" + "(requires data plane status extension)." ), ) uplink_status_group = parser.add_mutually_exclusive_group() @@ -1262,7 +1262,7 @@ def get_parser(self, prog_name): action='append', help=_( "Desired key which should be removed from binding:profile " - "(repeat option to unset multiple binding:profile data)" + "(repeat option to unset multiple binding:profile keys)" ), ) parser.add_argument( @@ -1298,7 +1298,7 @@ def get_parser(self, prog_name): parser.add_argument( '--data-plane-status', action='store_true', - help=_("Clear existing information of data plane status"), + help=_("Clear existing data plane status information"), ) parser.add_argument( '--numa-policy', @@ -1309,13 +1309,13 @@ def get_parser(self, prog_name): '--host', action='store_true', default=False, - help=_("Clear host binding for the port."), + help=_("Clear host binding for the port"), ) parser.add_argument( '--hints', action='store_true', default=False, - help=_("Clear hints for the port."), + help=_("Clear hints for the port"), ) _tag.add_tag_option_to_parser_for_unset(parser, _('port')) parser.add_argument( diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 0deae3600..0989456bc 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -258,7 +258,7 @@ def _parser_add_bfd_ecmp_arguments(parser): action='store_true', help=_( "Enable BFD sessions for default routes inferred from " - "the external gateway port subnets for this router." + "the external gateway port subnets for this router" ), ) parser.add_argument( @@ -268,7 +268,7 @@ def _parser_add_bfd_ecmp_arguments(parser): action='store_false', help=_( "Disable BFD sessions for default routes inferred from " - "the external gateway port subnets for this router." + "the external gateway port subnets for this router" ), ) parser.add_argument( @@ -278,7 +278,7 @@ def _parser_add_bfd_ecmp_arguments(parser): action='store_true', help=_( "Add ECMP default routes if multiple are available via " - "different gateway ports." + "different gateway ports" ), ) parser.add_argument( @@ -286,7 +286,7 @@ def _parser_add_bfd_ecmp_arguments(parser): dest='enable_default_route_ecmp', default=None, action='store_false', - help=_("Add default route only for first gateway port."), + help=_("Add default route only for first gateway port"), ) @@ -367,7 +367,7 @@ def get_parser(self, prog_name): metavar='', help=_( "Router to which extra static routes " - "will be added (name or ID)." + "will be added (name or ID)" ), ) parser.add_argument( @@ -382,7 +382,7 @@ def get_parser(self, prog_name): "destination: destination subnet (in CIDR notation), " "gateway: nexthop IP address. " "Repeat option to add multiple routes. " - "Trying to add a route that's already present " + "Trying to add a route that is already present " "(exactly, including destination and nexthop) " "in the routing table is allowed and is considered " "a successful operation." @@ -418,7 +418,7 @@ def get_parser(self, prog_name): metavar='', help=_( "Router from which extra static routes " - "will be removed (name or ID)." + "will be removed (name or ID)" ), ) parser.add_argument( @@ -433,7 +433,7 @@ def get_parser(self, prog_name): "destination: destination subnet (in CIDR notation), " "gateway: nexthop IP address. " "Repeat option to remove multiple routes. " - "Trying to remove a route that's already missing " + "Trying to remove a route that is already missing " "(fully, including destination and nexthop) " "from the routing table is allowed and is considered " "a successful operation." @@ -525,9 +525,9 @@ def get_parser(self, prog_name): metavar="", action='append', help=_( - "External Network used as router's gateway (name or ID). " + "External Network used as router's gateway (name or ID) " "(repeat option to set multiple gateways per router " - "if the L3 service plugin in use supports it)." + "if the L3 service plugin in use supports it)" ), dest='external_gateways', ) @@ -541,7 +541,7 @@ def get_parser(self, prog_name): "Desired IP and/or subnet (name or ID) " "on external gateway: " "subnet=,ip-address= " - "(repeat option to set multiple fixed IP addresses)." + "(repeat option to set multiple fixed IP addresses)" ), ) snat_group = parser.add_mutually_exclusive_group() @@ -927,7 +927,7 @@ def get_parser(self, prog_name): default=None, required_keys=['destination', 'gateway'], help=_( - "Add routes to the router " + "Add routes to the router. " "destination: destination subnet (in CIDR notation) " "gateway: nexthop IP address " "(repeat option to add multiple routes). " @@ -967,7 +967,7 @@ def get_parser(self, prog_name): metavar="", action='append', help=_( - "External Network used as router's gateway (name or ID). " + "External Network used as router's gateway (name or ID) " "(repeat option to set multiple gateways per router " "if the L3 service plugin in use supports it)." ), @@ -983,7 +983,7 @@ def get_parser(self, prog_name): "Desired IP and/or subnet (name or ID) " "on external gateway: " "subnet=,ip-address= " - "(repeat option to set multiple fixed IP addresses)." + "(repeat option to set multiple fixed IP addresses)" ), ) snat_group = parser.add_mutually_exclusive_group() @@ -1163,7 +1163,7 @@ def get_parser(self, prog_name): default=None, required_keys=['destination', 'gateway'], help=_( - "Routes to be removed from the router " + "Routes to be removed from the router. " "destination: destination subnet (in CIDR notation) " "gateway: nexthop IP address " "(repeat option to unset multiple routes)" @@ -1216,7 +1216,7 @@ def take_action(self, parsed_args): ): pass except (KeyError, TypeError): - msg = _("Router does not have external network or qos policy") + msg = _("Router does not have external network or QoS policy") raise exceptions.CommandError(msg) else: attrs['external_gateway_info'] = { @@ -1253,13 +1253,13 @@ def get_parser(self, prog_name): parser.add_argument( 'router', metavar="", - help=_("Router to modify (name or ID)."), + help=_("Router to modify (name or ID)"), ) parser.add_argument( metavar="", help=_( "External Network to a attach a router gateway to (name or " - "ID)." + "ID)" ), dest='external_gateways', # The argument is stored in a list in order to reuse the @@ -1276,7 +1276,7 @@ def get_parser(self, prog_name): "Desired IP and/or subnet (name or ID) " "on external gateway: " "subnet=,ip-address= " - "(repeat option to set multiple fixed IP addresses)." + "(repeat option to set multiple fixed IP addresses)" ), ) return parser @@ -1327,7 +1327,7 @@ def get_parser(self, prog_name): metavar="", help=_( "External Network to remove a router gateway from (name or " - "ID)." + "ID)" ), dest='external_gateways', # The argument is stored in a list in order to reuse the @@ -1344,7 +1344,7 @@ def get_parser(self, prog_name): "IP and/or subnet (name or ID) on the external gateway " "which is used to identify a particular gateway if multiple " "are attached to the same network: subnet=," - "ip-address=." + "ip-address=" ), ) return parser diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index 556eff2c0..1735b7c63 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -126,7 +126,7 @@ def update_parser_network(self, parser): "--stateful", action='store_true', default=None, - help=_("Security group is stateful (Default)"), + help=_("Security group is stateful (default)"), ) stateful_group.add_argument( "--stateless", @@ -345,7 +345,7 @@ def update_parser_common(self, parser): "--stateful", action='store_true', default=None, - help=_("Security group is stateful (Default)"), + help=_("Security group is stateful (default)"), ) stateful_group.add_argument( "--stateless", diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 3d172adb1..543e61d0a 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -84,8 +84,8 @@ def _get_common_parse_arguments(parser, is_create=True): action=parseractions.MultiKeyValueAction, required_keys=['start', 'end'], help=_( - "Allocation pool IP addresses for this subnet " - "e.g.: start=192.168.199.2,end=192.168.199.254 " + "Allocation pool IP addresses for this subnet, " + "for example, start=192.168.199.2,end=192.168.199.254 " "(repeat option to add multiple IP addresses)" ), ) @@ -127,10 +127,10 @@ def _get_common_parse_arguments(parser, is_create=True): action=parseractions.MultiKeyValueAction, required_keys=['destination', 'gateway'], help=_( - "Additional route for this subnet " - "e.g.: destination=10.10.0.0/16,gateway=192.168.71.254 " + "Additional route for this subnet, " + "for example, destination=10.10.0.0/16,gateway=192.168.71.254 " "destination: destination subnet (in CIDR notation) " - "gateway: nexthop IP address " + "gateway: next-hop IP address " "(repeat option to add multiple routes)" ), ) @@ -150,8 +150,8 @@ def _get_common_parse_arguments(parser, is_create=True): action='append', dest='service_types', help=_( - "Service type for this subnet " - "e.g.: network:floatingip_agent_gateway. " + "Service type for this subnet, " + "for example, network:floatingip_agent_gateway. " "Must be a valid device owner value for a network port " "(repeat option to set multiple service types)" ), @@ -365,8 +365,8 @@ def get_parser(self, prog_name): ": Specific IP address to use as the gateway, " "'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')." + "not use a gateway. For example, --gateway 192.168.9.1, " + "--gateway auto or --gateway none (default is 'auto')." ), ) parser.add_argument( @@ -375,7 +375,7 @@ def get_parser(self, prog_name): default=4, choices=[4, 6], help=_( - "IP version (default is 4). Note that when subnet pool is " + "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." ), @@ -513,10 +513,10 @@ def get_parser(self, prog_name): action='append', dest='service_types', help=_( - "List only subnets of a given service type in output " - "e.g.: network:floatingip_agent_gateway. " + "List only subnets of a given service type in output, " + "for example, network:floatingip_agent_gateway. " "Must be a valid device owner value for a network port " - "(repeat option to list multiple service types)" + "(repeat option to list multiple service types)." ), ) parser.add_argument( @@ -551,8 +551,8 @@ def get_parser(self, prog_name): metavar='', help=_( "List only subnets of given subnet range " - "(in CIDR notation) in output " - "e.g.: --subnet-range 10.10.0.0/16" + "(in CIDR notation) in output. " + "For example, --subnet-range 10.10.0.0/16" ), ) parser.add_argument( @@ -560,7 +560,7 @@ def get_parser(self, prog_name): metavar='', help=_( "List only subnets which belong to a given subnet pool " - "in output (Name or ID)" + "in output (name or ID)" ), ) _tag.add_tag_filtering_option_to_parser(parser, _('subnets')) @@ -684,8 +684,8 @@ def get_parser(self, prog_name): help=_( "Specify a gateway for the subnet. The options are: " ": 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." + "'none': This subnet will not use a gateway. " + "For example, --gateway 192.168.9.1 or --gateway none." ), ) parser.add_argument( @@ -694,7 +694,7 @@ def get_parser(self, prog_name): help=_( "Network segment to associate with this subnet (name or " "ID). It is only allowed to set the segment if the current " - "value is `None`, the network must also have only one " + "value is `None`. The network must also have only one " "segment and only one subnet can exist on the network." ), ) @@ -774,7 +774,7 @@ def get_parser(self, prog_name): required_keys=['start', 'end'], help=_( 'Allocation pool IP addresses to be removed from this ' - 'subnet e.g.: start=192.168.199.2,end=192.168.199.254 ' + 'subnet, for example, start=192.168.199.2,end=192.168.199.254 ' '(repeat option to unset multiple allocation pools)' ), ) @@ -800,10 +800,10 @@ def get_parser(self, prog_name): action=parseractions.MultiKeyValueAction, required_keys=['destination', 'gateway'], help=_( - 'Route to be removed from this subnet ' - 'e.g.: destination=10.10.0.0/16,gateway=192.168.71.254 ' + 'Route to be removed from this subnet, ' + 'for example, destination=10.10.0.0/16,gateway=192.168.71.254 ' 'destination: destination subnet (in CIDR notation) ' - 'gateway: nexthop IP address ' + 'gateway: next-hop IP address ' '(repeat option to unset multiple host routes)' ), ) @@ -813,8 +813,8 @@ def get_parser(self, prog_name): action='append', dest='service_types', help=_( - 'Service type to be removed from this subnet ' - 'e.g.: network:floatingip_agent_gateway. ' + 'Service type to be removed from this subnet, ' + 'for example, network:floatingip_agent_gateway. ' 'Must be a valid device owner value for a network port ' '(repeat option to unset multiple service types)' ), From 9c7a5d4e51670f2685e90d5a74237a5dc6b0cd9e Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 19 Feb 2025 20:15:43 +0000 Subject: [PATCH 101/245] pre-commit: Bump versions Apply manual changes required by ruff. Automatic changes were done separately. Change-Id: I7db65bd2ac3f31b0479699946398752d8d729338 Signed-off-by: Stephen Finucane --- .pre-commit-config.yaml | 4 ++-- examples/object_api.py | 2 +- examples/osc-lib.py | 2 +- openstackclient/network/utils.py | 2 +- openstackclient/network/v2/router.py | 6 ++---- .../tests/functional/identity/v3/test_idp.py | 10 +++------- .../functional/identity/v3/test_service_provider.py | 10 +++------- 7 files changed, 13 insertions(+), 23 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 786c26378..14bfc2b85 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ --- repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - id: mixed-line-ending @@ -15,7 +15,7 @@ repos: files: .*\.(yaml|yml)$ args: ['--unsafe'] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.2 + rev: v0.11.2 hooks: - id: ruff args: ['--fix', '--unsafe-fixes'] diff --git a/examples/object_api.py b/examples/object_api.py index 4ff245d4c..5917d35fa 100755 --- a/examples/object_api.py +++ b/examples/object_api.py @@ -94,7 +94,7 @@ def run(opts): c_list = obj_api.container_list() print("Name\tCount\tBytes") for c in c_list: - print("%s\t%d\t%d" % (c['name'], c['count'], c['bytes'])) + print(f"{c['name']}\t{c['count']}\t{c['bytes']}") if len(c_list) > 0: # See what is in the first container diff --git a/examples/osc-lib.py b/examples/osc-lib.py index 6bd0347ac..fb8ea2dc3 100755 --- a/examples/osc-lib.py +++ b/examples/osc-lib.py @@ -87,7 +87,7 @@ def run(opts): c_list = client_manager.object_store.container_list() print("Name\tCount\tBytes") for c in c_list: - print("%s\t%d\t%d" % (c['name'], c['count'], c['bytes'])) + print(f"{c['name']}\t{c['count']}\t{c['bytes']}") if len(c_list) > 0: # See what is in the first container diff --git a/openstackclient/network/utils.py b/openstackclient/network/utils.py index 9cb3e1a36..648c9092a 100644 --- a/openstackclient/network/utils.py +++ b/openstackclient/network/utils.py @@ -23,7 +23,7 @@ def transform_compute_security_group_rule(sg_rule): from_port = info.pop('from_port') to_port = info.pop('to_port') if isinstance(from_port, int) and isinstance(to_port, int): - port_range = {'port_range': "%u:%u" % (from_port, to_port)} + port_range = {'port_range': f"{from_port}:{to_port}"} elif from_port is None and to_port is None: port_range = {'port_range': ""} else: diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index ce7813185..9f99bb5dd 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -1270,8 +1270,7 @@ def get_parser(self, prog_name): parser.add_argument( metavar="", help=_( - "External Network to a attach a router gateway to (name or " - "ID)" + "External Network to a attach a router gateway to (name or ID)" ), dest='external_gateways', # The argument is stored in a list in order to reuse the @@ -1338,8 +1337,7 @@ def get_parser(self, prog_name): parser.add_argument( metavar="", help=_( - "External Network to remove a router gateway from (name or " - "ID)" + "External Network to remove a router gateway from (name or ID)" ), dest='external_gateways', # The argument is stored in a list in order to reuse the diff --git a/openstackclient/tests/functional/identity/v3/test_idp.py b/openstackclient/tests/functional/identity/v3/test_idp.py index 11275795b..c9ef01d0f 100644 --- a/openstackclient/tests/functional/identity/v3/test_idp.py +++ b/openstackclient/tests/functional/identity/v3/test_idp.py @@ -54,13 +54,9 @@ def test_idp_set(self): identity_provider = self._create_dummy_idp(add_clean_up=True) new_remoteid = data_utils.rand_name('newRemoteId') raw_output = self.openstack( - 'identity provider set ' - '%(identity-provider)s ' - '--remote-id %(remote-id)s ' - % { - 'identity-provider': identity_provider, - 'remote-id': new_remoteid, - } + f'identity provider set ' + f'{identity_provider} ' + f'--remote-id {new_remoteid}' ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack( diff --git a/openstackclient/tests/functional/identity/v3/test_service_provider.py b/openstackclient/tests/functional/identity/v3/test_service_provider.py index 2f941d5e8..330f938c8 100644 --- a/openstackclient/tests/functional/identity/v3/test_service_provider.py +++ b/openstackclient/tests/functional/identity/v3/test_service_provider.py @@ -52,13 +52,9 @@ def test_sp_set(self): service_provider = self._create_dummy_sp(add_clean_up=True) new_description = data_utils.rand_name('newDescription') raw_output = self.openstack( - 'service provider set ' - '%(service-provider)s ' - '--description %(description)s ' - % { - 'service-provider': service_provider, - 'description': new_description, - } + f'service provider set ' + f'{service_provider} ' + f'--description {new_description}' ) updated_value = self.parse_show_as_object(raw_output) self.assertEqual(new_description, updated_value.get('description')) From bdd55d989da4412f1fa3dcb2937b94e4a0111820 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 19 Feb 2025 20:15:43 +0000 Subject: [PATCH 102/245] typing: Indicate tuples to be extended Change-Id: Ie5907de8d60f2f39e98f6a88227cebb2e2ff565c Signed-off-by: Stephen Finucane --- openstackclient/common/availability_zone.py | 7 ++---- openstackclient/common/extension.py | 12 ++-------- openstackclient/common/quota.py | 16 ++++++------- openstackclient/compute/v2/aggregate.py | 4 ++-- openstackclient/compute/v2/flavor.py | 4 ++-- openstackclient/compute/v2/hypervisor.py | 10 ++++++-- openstackclient/compute/v2/keypair.py | 2 +- openstackclient/compute/v2/server.py | 17 ++++++++----- openstackclient/compute/v2/server_event.py | 4 ++-- openstackclient/compute/v2/server_group.py | 4 ++-- .../compute/v2/server_migration.py | 4 ++-- openstackclient/compute/v2/server_volume.py | 4 ++-- openstackclient/compute/v2/service.py | 4 ++-- openstackclient/identity/v2_0/endpoint.py | 15 ++++++------ openstackclient/identity/v2_0/project.py | 5 ++-- openstackclient/identity/v2_0/service.py | 5 ++-- openstackclient/identity/v2_0/user.py | 20 ++++------------ openstackclient/identity/v3/endpoint.py | 2 +- openstackclient/identity/v3/group.py | 5 ++-- openstackclient/identity/v3/policy.py | 9 ++++--- openstackclient/identity/v3/project.py | 5 ++-- openstackclient/identity/v3/role.py | 2 +- openstackclient/identity/v3/service.py | 9 ++++--- openstackclient/image/v1/image.py | 4 ++-- openstackclient/image/v2/image.py | 10 ++++---- openstackclient/network/v2/floating_ip.py | 12 +++++----- openstackclient/network/v2/network.py | 4 ++-- openstackclient/network/v2/network_agent.py | 4 ++-- openstackclient/network/v2/network_rbac.py | 4 ++-- openstackclient/network/v2/network_segment.py | 8 +++---- .../network/v2/network_segment_range.py | 12 +++++----- openstackclient/network/v2/network_trunk.py | 16 +++++++++---- openstackclient/network/v2/router.py | 24 +++++++++---------- openstackclient/network/v2/security_group.py | 14 ++++------- .../network/v2/security_group_rule.py | 14 +++++------ openstackclient/network/v2/subnet.py | 4 ++-- openstackclient/network/v2/subnet_pool.py | 4 ++-- openstackclient/object/v1/container.py | 5 ++-- openstackclient/object/v1/object.py | 11 ++------- openstackclient/volume/v1/volume.py | 4 ++-- openstackclient/volume/v2/volume_backup.py | 10 ++++---- .../volume/v3/block_storage_cluster.py | 6 ++--- openstackclient/volume/v3/volume_backup.py | 10 ++++---- 43 files changed, 164 insertions(+), 185 deletions(-) diff --git a/openstackclient/common/availability_zone.py b/openstackclient/common/availability_zone.py index acc661a8d..e6644ffe2 100644 --- a/openstackclient/common/availability_zone.py +++ b/openstackclient/common/availability_zone.py @@ -172,17 +172,14 @@ def _get_network_availability_zones(self, parsed_args): return result def take_action(self, parsed_args): + columns: tuple[str, ...] = ('Zone Name', 'Zone Status') if parsed_args.long: - columns = ( - 'Zone Name', - 'Zone Status', + columns += ( 'Zone Resource', 'Host Name', 'Service Name', 'Service Status', ) - else: - columns = ('Zone Name', 'Zone Status') # Show everything by default. show_all = ( diff --git a/openstackclient/common/extension.py b/openstackclient/common/extension.py index ad7b1a934..00f04c750 100644 --- a/openstackclient/common/extension.py +++ b/openstackclient/common/extension.py @@ -73,17 +73,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + columns: tuple[str, ...] = ('Name', 'Alias', 'Description') if parsed_args.long: - columns = ( - 'Name', - 'Alias', - 'Description', - 'Namespace', - 'Updated At', - 'Links', - ) - else: - columns = ('Name', 'Alias', 'Description') + columns += ('Namespace', 'Updated At', 'Links') data = [] diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 6faae8b77..1ac50f80d 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -290,7 +290,7 @@ def _list_quota_compute(self, parsed_args, project_ids): if default_result != project_result: result += project_result - columns = ( + columns: tuple[str, ...] = ( 'id', 'cores', 'injected_files', @@ -303,7 +303,7 @@ def _list_quota_compute(self, parsed_args, project_ids): 'server_groups', 'server_group_members', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Project ID', 'Cores', 'Injected Files', @@ -352,7 +352,7 @@ def _list_quota_volume(self, parsed_args, project_ids): if default_result != project_result: result += project_result - columns = ( + columns: tuple[str, ...] = ( 'id', 'backups', 'backup_gigabytes', @@ -361,7 +361,7 @@ def _list_quota_volume(self, parsed_args, project_ids): 'snapshots', 'volumes', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Project ID', 'Backups', 'Backup Gigabytes', @@ -407,7 +407,7 @@ def _list_quota_network(self, parsed_args, project_ids): if default_result != project_result: result += project_result - columns = ( + columns: tuple[str, ...] = ( 'id', 'floating_ips', 'networks', @@ -419,7 +419,7 @@ def _list_quota_network(self, parsed_args, project_ids): 'subnets', 'subnet_pools', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Project ID', 'Floating IPs', 'Networks', @@ -823,11 +823,11 @@ def take_action(self, parsed_args): if k not in ('usage', 'reservation') ] - columns = ( + columns: tuple[str, ...] = ( 'resource', 'limit', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Resource', 'Limit', ) diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index 167b2e82c..293e323da 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -194,8 +194,8 @@ def take_action(self, parsed_args): aggregates = list(compute_client.aggregates()) if sdk_utils.supports_microversion(compute_client, '2.41'): - column_headers = ("ID", "UUID") - columns = ("id", "uuid") + column_headers: tuple[str, ...] = ("ID", "UUID") + columns: tuple[str, ...] = ("id", "uuid") else: column_headers = ("ID",) columns = ("id",) diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index 03092417c..fe6016148 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -329,7 +329,7 @@ def take_action(self, parsed_args): if parsed_args.long and not f.extra_specs: compute_client.fetch_flavor_extra_specs(f) - columns = ( + columns: tuple[str, ...] = ( "id", "name", "ram", @@ -345,7 +345,7 @@ def take_action(self, parsed_args): "extra_specs", ) - column_headers = ( + column_headers: tuple[str, ...] = ( "ID", "Name", "RAM", diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py index 8a82a30d9..ab81879b6 100644 --- a/openstackclient/compute/v2/hypervisor.py +++ b/openstackclient/compute/v2/hypervisor.py @@ -119,14 +119,20 @@ def take_action(self, parsed_args): if parsed_args.matching: list_opts['hypervisor_hostname_pattern'] = parsed_args.matching - column_headers = ( + column_headers: tuple[str, ...] = ( "ID", "Hypervisor Hostname", "Hypervisor Type", "Host IP", "State", ) - columns = ('id', 'name', 'hypervisor_type', 'host_ip', 'state') + columns: tuple[str, ...] = ( + 'id', + 'name', + 'hypervisor_type', + 'host_ip', + 'state', + ) if parsed_args.long: if not sdk_utils.supports_microversion(compute_client, '2.88'): diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index 377eb903c..85c340d58 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -372,7 +372,7 @@ def take_action(self, parsed_args): else: data = compute_client.keypairs(**kwargs) - columns = ("Name", "Fingerprint") + columns: tuple[str, ...] = ("Name", "Fingerprint") if sdk_utils.supports_microversion(compute_client, '2.2'): columns += ("Type",) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index ae816e6fe..cdb0f9676 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -430,7 +430,7 @@ def take_action(self, parsed_args): interface = compute_client.create_server_interface(server.id, **kwargs) - columns = ( + columns: tuple[str, ...] = ( 'port_id', 'server_id', 'net_id', @@ -438,7 +438,7 @@ def take_action(self, parsed_args): 'port_state', 'fixed_ips', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Port ID', 'Server ID', 'Network ID', @@ -842,8 +842,13 @@ def take_action(self, parsed_args): **kwargs, ) - columns = ('id', 'server id', 'volume id', 'device') - column_headers = ('ID', 'Server ID', 'Volume ID', 'Device') + columns: tuple[str, ...] = ('id', 'server id', 'volume id', 'device') + column_headers: tuple[str, ...] = ( + 'ID', + 'Server ID', + 'Volume ID', + 'Device', + ) if sdk_utils.supports_microversion(compute_client, '2.49'): columns += ('tag',) column_headers += ('Tag',) @@ -2802,12 +2807,12 @@ def take_action(self, parsed_args): msg % search_opts['changes-since'] ) - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'status', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Status', diff --git a/openstackclient/compute/v2/server_event.py b/openstackclient/compute/v2/server_event.py index 1fc467210..d48283f0e 100644 --- a/openstackclient/compute/v2/server_event.py +++ b/openstackclient/compute/v2/server_event.py @@ -220,13 +220,13 @@ def take_action(self, parsed_args): data = compute_client.server_actions(server_id, **kwargs) - columns = ( + columns: tuple[str, ...] = ( 'request_id', 'server_id', 'action', 'start_time', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Request ID', 'Server ID', 'Action', diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py index c4fe666e1..4a157faa2 100644 --- a/openstackclient/compute/v2/server_group.py +++ b/openstackclient/compute/v2/server_group.py @@ -216,12 +216,12 @@ def take_action(self, parsed_args): if sdk_utils.supports_microversion(compute_client, '2.64'): policy_key = 'Policy' - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', policy_key.lower(), ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', policy_key, diff --git a/openstackclient/compute/v2/server_migration.py b/openstackclient/compute/v2/server_migration.py index 75073a0c3..a0ec365a6 100644 --- a/openstackclient/compute/v2/server_migration.py +++ b/openstackclient/compute/v2/server_migration.py @@ -333,7 +333,7 @@ def take_action(self, parsed_args): ignore_missing=False, ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Server UUID', 'Status', @@ -352,7 +352,7 @@ def take_action(self, parsed_args): 'Updated At', ) - columns = ( + columns: tuple[str, ...] = ( 'id', 'server_id', 'status', diff --git a/openstackclient/compute/v2/server_volume.py b/openstackclient/compute/v2/server_volume.py index b4322c0b1..fe74ec7d1 100644 --- a/openstackclient/compute/v2/server_volume.py +++ b/openstackclient/compute/v2/server_volume.py @@ -42,8 +42,8 @@ def take_action(self, parsed_args): ) volumes = compute_client.volume_attachments(server) - columns = () - column_headers = () + columns: tuple[str, ...] = () + column_headers: tuple[str, ...] = () if not sdk_utils.supports_microversion(compute_client, '2.89'): columns += ('id',) diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index b0d77fbad..5ba2a3443 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -109,7 +109,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): compute_client = self.app.client_manager.sdk_connection.compute - columns = ( + columns: tuple[str, ...] = ( "id", "binary", "host", @@ -118,7 +118,7 @@ def take_action(self, parsed_args): "state", "updated_at", ) - column_headers = ( + column_headers: tuple[str, ...] = ( "ID", "Binary", "Host", diff --git a/openstackclient/identity/v2_0/endpoint.py b/openstackclient/identity/v2_0/endpoint.py index 94edc4cbe..db8efede1 100644 --- a/openstackclient/identity/v2_0/endpoint.py +++ b/openstackclient/identity/v2_0/endpoint.py @@ -133,18 +133,19 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity + + columns: tuple[str, ...] = ( + 'ID', + 'Region', + 'Service Name', + 'Service Type', + ) if parsed_args.long: - columns = ( - 'ID', - 'Region', - 'Service Name', - 'Service Type', + columns += ( 'PublicURL', 'AdminURL', 'InternalURL', ) - else: - columns = ('ID', 'Region', 'Service Name', 'Service Type') data = identity_client.endpoints.list() for ep in data: diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py index 1acaa29e5..a0c3c1e4d 100644 --- a/openstackclient/identity/v2_0/project.py +++ b/openstackclient/identity/v2_0/project.py @@ -170,10 +170,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + columns: tuple[str, ...] = ('ID', 'Name') if parsed_args.long: - columns = ('ID', 'Name', 'Description', 'Enabled') - else: - columns = ('ID', 'Name') + columns += ('Description', 'Enabled') data = self.app.client_manager.identity.tenants.list() if parsed_args.sort: data = utils.sort_items(data, parsed_args.sort) diff --git a/openstackclient/identity/v2_0/service.py b/openstackclient/identity/v2_0/service.py index 465f002b1..978e926f3 100644 --- a/openstackclient/identity/v2_0/service.py +++ b/openstackclient/identity/v2_0/service.py @@ -121,10 +121,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + columns: tuple[str, ...] = ('ID', 'Name', 'Type') if parsed_args.long: - columns = ('ID', 'Name', 'Type', 'Description') - else: - columns = ('ID', 'Name', 'Type') + columns += ('Description',) data = self.app.client_manager.identity.services.list() return ( columns, diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py index b48c95e27..2262d264d 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -230,21 +230,11 @@ def take_action(self, parsed_args): ) project = project.id + columns: tuple[str, ...] = ('id', 'name') + column_headers: tuple[str, ...] = ('ID', 'Name') if parsed_args.long: - columns = ( - 'ID', - 'Name', - 'tenantId', - 'Email', - 'Enabled', - ) - column_headers = ( - 'ID', - 'Name', - 'Project', - 'Email', - 'Enabled', - ) + columns += ('tenantId', 'email', 'enabled') + column_headers += ('Project', 'Email', 'Enabled') # Cache the project list project_cache = {} try: @@ -256,8 +246,6 @@ def take_action(self, parsed_args): formatters['tenantId'] = functools.partial( ProjectColumn, project_cache=project_cache ) - else: - columns = column_headers = ('ID', 'Name') data = identity_client.users.list(tenant_id=project) if parsed_args.project: diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index 72dd51570..4b79fdaa0 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -224,7 +224,7 @@ def take_action(self, parsed_args): ) if endpoint: - columns = ('ID', 'Name') + columns: tuple[str, ...] = ('ID', 'Name') data = identity_client.endpoint_filter.list_projects_for_endpoint( endpoint=endpoint.id ) diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index 62a5c2239..d0112064b 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -278,10 +278,9 @@ def take_action(self, parsed_args): user = None # List groups + columns: tuple[str, ...] = ('ID', 'Name') if parsed_args.long: - columns = ('ID', 'Name', 'Domain ID', 'Description') - else: - columns = ('ID', 'Name') + columns += ('Domain ID', 'Description') data = identity_client.groups.list( domain=domain, user=user, diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py index a7e2a07d9..d5b8fec6b 100644 --- a/openstackclient/identity/v3/policy.py +++ b/openstackclient/identity/v3/policy.py @@ -113,12 +113,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + columns: tuple[str, ...] = ('ID', 'Type') + column_headers: tuple[str, ...] = columns if parsed_args.long: - columns = ('ID', 'Type', 'Blob') - column_headers = ('ID', 'Type', 'Rules') - else: - columns = ('ID', 'Type') - column_headers = columns + columns += ('Blob',) + column_headers += ('Rules',) data = self.app.client_manager.identity.policies.list() return ( column_headers, diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 250fde4c5..a057fcbd3 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -245,10 +245,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity + columns: tuple[str, ...] = ('ID', 'Name') if parsed_args.long: - columns = ('ID', 'Name', 'Domain ID', 'Description', 'Enabled') - else: - columns = ('ID', 'Name') + columns += ('Domain ID', 'Description', 'Enabled') kwargs = {} domain_id = None diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index 4364931b6..c7bb5b054 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -428,7 +428,7 @@ def take_action(self, parsed_args): domain = identity_client.find_domain( name_or_id=parsed_args.domain, ) - columns = ('ID', 'Name', 'Domain') + columns: tuple[str, ...] = ('ID', 'Name', 'Domain') data = identity_client.roles(domain_id=domain.id) else: columns = ('ID', 'Name') diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index dd1bfa473..e8f483266 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -158,12 +158,11 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity + columns: tuple[str, ...] = ('id', 'name', 'type') + column_headers: tuple[str, ...] = ('ID', 'Name', 'Type') if parsed_args.long: - columns = ('id', 'name', 'type', 'description', 'is_enabled') - column_headers = ('ID', 'Name', 'Type', 'Description', 'Enabled') - else: - columns = ('id', 'name', 'type') - column_headers = ('ID', 'Name', 'Type') + columns += ('description', 'is_enabled') + column_headers += ('Description', 'Enabled') data = identity_client.services() diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 2391c1639..fba656cf6 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -467,7 +467,7 @@ def take_action(self, parsed_args): kwargs['is_private'] = True if parsed_args.long: - columns = ( + columns: tuple[str, ...] = ( 'ID', 'Name', 'Disk Format', @@ -480,7 +480,7 @@ def take_action(self, parsed_args): 'owner_id', 'properties', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Disk Format', diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index ad1637d70..dca1a8f26 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -881,7 +881,7 @@ def take_action(self, parsed_args): if parsed_args.is_hidden: kwargs['is_hidden'] = parsed_args.is_hidden if parsed_args.long: - columns = ( + columns: tuple[str, ...] = ( 'ID', 'Name', 'Disk Format', @@ -894,7 +894,7 @@ def take_action(self, parsed_args): 'owner_id', 'tags', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Disk Format', @@ -956,7 +956,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): image_client = self.app.client_manager.image - columns = ("Image ID", "Member ID", "Status") + columns: tuple[str, ...] = ("Image ID", "Member ID", "Status") image_id = image_client.find_image( parsed_args.image, @@ -1862,8 +1862,8 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): image_client = self.app.client_manager.image try: - columns = ("id", "description", "is_default") - column_headers = ("ID", "Description", "Default") + columns: tuple[str, ...] = ("id", "description", "is_default") + column_headers: tuple[str, ...] = ("ID", "Description", "Default") if parsed_args.detail: columns += ("properties",) column_headers += ("Properties",) diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index d190d14b1..266ff8fa3 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -318,7 +318,7 @@ def take_action_network(self, client, parsed_args): network_client = self.app.client_manager.network identity_client = self.app.client_manager.identity - columns = ( + columns: tuple[str, ...] = ( 'id', 'floating_ip_address', 'fixed_ip_address', @@ -326,7 +326,7 @@ def take_action_network(self, client, parsed_args): 'floating_network_id', 'project_id', ) - headers = ( + headers: tuple[str, ...] = ( 'ID', 'Floating IP Address', 'Fixed IP Address', @@ -335,7 +335,7 @@ def take_action_network(self, client, parsed_args): 'Project', ) if parsed_args.long: - columns = columns + ( + columns += ( 'router_id', 'status', 'description', @@ -343,7 +343,7 @@ def take_action_network(self, client, parsed_args): 'dns_name', 'dns_domain', ) - headers = headers + ( + headers += ( 'Router', 'Status', 'Description', @@ -403,14 +403,14 @@ def take_action_network(self, client, parsed_args): ) def take_action_compute(self, client, parsed_args): - columns = ( + columns: tuple[str, ...] = ( 'ID', 'IP', 'Fixed IP', 'Instance ID', 'Pool', ) - headers = ( + headers: tuple[str, ...] = ( 'ID', 'Floating IP Address', 'Fixed IP Address', diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 0db94f867..dbc665c97 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -573,7 +573,7 @@ def update_parser_network(self, parser): def take_action_network(self, client, parsed_args): identity_client = self.app.client_manager.identity if parsed_args.long: - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'status', @@ -586,7 +586,7 @@ def take_action_network(self, client, parsed_args): 'availability_zones', 'tags', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Status', diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py index 1555cb654..35b0fab66 100644 --- a/openstackclient/network/v2/network_agent.py +++ b/openstackclient/network/v2/network_agent.py @@ -223,7 +223,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network - columns = ( + columns: tuple[str, ...] = ( 'id', 'agent_type', 'host', @@ -232,7 +232,7 @@ def take_action(self, parsed_args): 'is_admin_state_up', 'binary', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Agent Type', 'Host', diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py index 0cf756046..0df74375d 100644 --- a/openstackclient/network/v2/network_rbac.py +++ b/openstackclient/network/v2/network_rbac.py @@ -265,12 +265,12 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network - columns = ( + columns: tuple[str, ...] = ( 'id', 'object_type', 'object_id', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Object Type', 'Object ID', diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py index 4ec2a2724..fbccba00e 100644 --- a/openstackclient/network/v2/network_segment.py +++ b/openstackclient/network/v2/network_segment.py @@ -178,14 +178,14 @@ def take_action(self, parsed_args): filters = {'network_id': _network.id} data = network_client.segments(**filters) - headers = ( + headers: tuple[str, ...] = ( 'ID', 'Name', 'Network', 'Network Type', 'Segment', ) - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'network_id', @@ -193,8 +193,8 @@ def take_action(self, parsed_args): 'segmentation_id', ) if parsed_args.long: - headers = headers + ('Physical Network',) - columns = columns + ('physical_network',) + headers += ('Physical Network',) + columns += ('physical_network',) return ( headers, diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py index f24e49da9..48a8ca1a8 100644 --- a/openstackclient/network/v2/network_segment_range.py +++ b/openstackclient/network/v2/network_segment_range.py @@ -42,9 +42,9 @@ def _get_columns(item): def _get_ranges(item): item = sorted([int(i) for i in item]) for a, b in itertools.groupby(enumerate(item), lambda xy: xy[1] - xy[0]): - b = list(b) + c = list(b) yield ( - f"{b[0][1]}-{b[-1][1]}" if b[0][1] != b[-1][1] else str(b[0][1]) + f"{c[0][1]}-{c[-1][1]}" if c[0][1] != c[-1][1] else str(c[0][1]) ) @@ -354,7 +354,7 @@ def take_action(self, parsed_args): filters = {} data = network_client.network_segment_ranges(**filters) - headers = ( + headers: tuple[str, ...] = ( 'ID', 'Name', 'Default', @@ -365,7 +365,7 @@ def take_action(self, parsed_args): 'Minimum ID', 'Maximum ID', ) - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'default', @@ -387,11 +387,11 @@ def take_action(self, parsed_args): # should be listed in output. parsed_args.long = True if parsed_args.long: - headers = headers + ( + headers += ( 'Used', 'Available', ) - columns = columns + ( + columns += ( 'used', 'available', ) diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index 5829be360..740c3f21d 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -151,8 +151,8 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.network data = client.trunks() - headers = ('ID', 'Name', 'Parent Port', 'Description') - columns = ('id', 'name', 'port_id', 'description') + headers: tuple[str, ...] = ('ID', 'Name', 'Parent Port', 'Description') + columns: tuple[str, ...] = ('id', 'name', 'port_id', 'description') if parsed_args.long: headers += ( 'Status', @@ -277,8 +277,16 @@ def take_action(self, parsed_args): client = self.app.client_manager.network trunk_id = client.find_trunk(parsed_args.trunk) data = client.get_trunk_subports(trunk_id) - headers = ('Port', 'Segmentation Type', 'Segmentation ID') - columns = ('port_id', 'segmentation_type', 'segmentation_id') + headers: tuple[str, ...] = ( + 'Port', + 'Segmentation Type', + 'Segmentation ID', + ) + columns: tuple[str, ...] = ( + 'port_id', + 'segmentation_type', + 'segmentation_id', + ) return ( headers, ( diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 9f99bb5dd..2794df43a 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -734,14 +734,14 @@ def take_action(self, parsed_args): identity_client = self.app.client_manager.identity client = self.app.client_manager.network - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'status', 'is_admin_state_up', 'project_id', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Status', @@ -787,27 +787,27 @@ def take_action(self, parsed_args): d.is_distributed is not None and 'is_distributed' not in columns ): - columns = columns + ('is_distributed',) - column_headers = column_headers + ('Distributed',) + columns += ('is_distributed',) + column_headers += ('Distributed',) if d.is_ha is not None and 'is_ha' not in columns: - columns = columns + ('is_ha',) - column_headers = column_headers + ('HA',) + columns += ('is_ha',) + column_headers += ('HA',) if parsed_args.long: - columns = columns + ( + columns += ( 'routes', 'external_gateway_info', ) - column_headers = column_headers + ( + column_headers += ( 'Routes', 'External gateway info', ) # availability zone will be available only when # router_availability_zone extension is enabled if client.find_extension("router_availability_zone"): - columns = columns + ('availability_zones',) - column_headers = column_headers + ('Availability zones',) - columns = columns + ('tags',) - column_headers = column_headers + ('Tags',) + columns += ('availability_zones',) + column_headers += ('Availability zones',) + columns += ('tags',) + column_headers += ('Tags',) return ( column_headers, diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index 5d78ba760..32b544fce 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -277,7 +277,7 @@ def take_action_network(self, client, parsed_args): fields=self.FIELDS_TO_RETRIEVE, **filters ) - columns = ("ID", "Name", "Description", "Project ID", "tags") + columns = ("id", "name", "description", "project_id", "tags") column_headers = ("ID", "Name", "Description", "Project", "Tags") return ( column_headers, @@ -297,15 +297,11 @@ def take_action_compute(self, client, parsed_args): all_projects=parsed_args.all_projects, ) - columns = ( - "ID", - "Name", - "Description", - ) - column_headers = columns + columns: tuple[str, ...] = ("id", "name", "description") + column_headers: tuple[str, ...] = ("ID", "Name", "Description") if parsed_args.all_projects: - columns = columns + ('Tenant ID',) - column_headers = column_headers + ('Project',) + columns += ('tenant_id',) + column_headers += ('Project',) return ( column_headers, ( diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index f9f8a4287..884e7a2f7 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -451,7 +451,7 @@ def update_parser_compute(self, parser): return parser def _get_column_headers(self, parsed_args): - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'IP Protocol', 'Ethertype', @@ -461,9 +461,9 @@ def _get_column_headers(self, parsed_args): 'Remote Security Group', ) if self.is_neutron: - column_headers = column_headers + ('Remote Address Group',) + column_headers += ('Remote Address Group',) if parsed_args.group is None: - column_headers = column_headers + ('Security Group',) + column_headers += ('Security Group',) return column_headers def take_action_network(self, client, parsed_args): @@ -474,7 +474,7 @@ def take_action_network(self, client, parsed_args): self.log.warning(msg) column_headers = self._get_column_headers(parsed_args) - columns = ( + columns: tuple[str, ...] = ( 'id', 'protocol', 'ether_type', @@ -496,7 +496,7 @@ def take_action_network(self, client, parsed_args): ).id query = {'security_group_id': security_group_id} else: - columns = columns + ('security_group_id',) + columns += ('security_group_id',) if parsed_args.ingress: query['direction'] = 'ingress' @@ -523,7 +523,7 @@ def take_action_network(self, client, parsed_args): def take_action_compute(self, client, parsed_args): column_headers = self._get_column_headers(parsed_args) - columns = ( + columns: tuple[str, ...] = ( "ID", "IP Protocol", "Ethertype", @@ -539,7 +539,7 @@ def take_action_compute(self, client, parsed_args): ) rules_to_list = security_group['rules'] else: - columns = columns + ('parent_group_id',) + columns += ('parent_group_id',) for security_group in compute_v2.list_security_groups( client, all_projects=parsed_args.all_projects ): diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index d48f94199..7e9c30d90 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -606,8 +606,8 @@ def take_action(self, parsed_args): _tag.get_tag_filtering_args(parsed_args, filters) data = network_client.subnets(**filters) - headers = ('ID', 'Name', 'Network', 'Subnet') - columns = ('id', 'name', 'network_id', 'cidr') + headers: tuple[str, ...] = ('ID', 'Name', 'Network', 'Subnet') + columns: tuple[str, ...] = ('id', 'name', 'network_id', 'cidr') if parsed_args.long: headers += ( 'Project', diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index fca3bfa66..2917c4026 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -356,8 +356,8 @@ def take_action(self, parsed_args): _tag.get_tag_filtering_args(parsed_args, filters) data = network_client.subnet_pools(**filters) - headers = ('ID', 'Name', 'Prefixes') - columns = ('id', 'name', 'prefixes') + headers: tuple[str, ...] = ('ID', 'Name', 'Prefixes') + columns: tuple[str, ...] = ('id', 'name', 'prefixes') if parsed_args.long: headers += ( 'Default Prefix Length', diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py index c4d0e42b9..f4250dbba 100644 --- a/openstackclient/object/v1/container.py +++ b/openstackclient/object/v1/container.py @@ -148,10 +148,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + columns: tuple[str, ...] = ('Name',) if parsed_args.long: - columns = ('Name', 'Bytes', 'Count') - else: - columns = ('Name',) + columns += ('Bytes', 'Count') kwargs = {} if parsed_args.prefix: diff --git a/openstackclient/object/v1/object.py b/openstackclient/object/v1/object.py index 4edb31493..cb8e4ffeb 100644 --- a/openstackclient/object/v1/object.py +++ b/openstackclient/object/v1/object.py @@ -162,16 +162,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + columns: tuple[str, ...] = ('Name',) if parsed_args.long: - columns = ( - 'Name', - 'Bytes', - 'Hash', - 'Content Type', - 'Last Modified', - ) - else: - columns = ('Name',) + columns += ('Bytes', 'Hash', 'Content Type', 'Last Modified') kwargs = {} if parsed_args.prefix: diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index 78619320f..1634624c0 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -376,7 +376,7 @@ def take_action(self, parsed_args): volume_client = self.app.client_manager.volume if parsed_args.long: - columns = ( + columns: tuple[str, ...] = ( 'ID', 'Display Name', 'Status', @@ -386,7 +386,7 @@ def take_action(self, parsed_args): 'Attachments', 'Metadata', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Status', diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index 5e0c75b6e..f6746eec8 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -118,7 +118,7 @@ def take_action(self, parsed_args): ignore_missing=False, ).id - columns = ( + columns: tuple[str, ...] = ( "id", "name", "volume_id", @@ -240,7 +240,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'description', @@ -249,7 +249,7 @@ def take_action(self, parsed_args): 'is_incremental', 'created_at', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Description', @@ -358,7 +358,7 @@ def take_action(self, parsed_args): ignore_missing=False, ) - columns = ( + columns: tuple[str, ...] = ( 'id', 'volume_id', 'volume_name', @@ -448,7 +448,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume backup = volume_client.find_backup(parsed_args.backup) - columns = ( + columns: tuple[str, ...] = ( "availability_zone", "container", "created_at", diff --git a/openstackclient/volume/v3/block_storage_cluster.py b/openstackclient/volume/v3/block_storage_cluster.py index 58bcae67c..8cf01b247 100644 --- a/openstackclient/volume/v3/block_storage_cluster.py +++ b/openstackclient/volume/v3/block_storage_cluster.py @@ -19,13 +19,13 @@ def _format_cluster(cluster, detailed=False): - columns = ( + columns: tuple[str, ...] = ( 'name', 'binary', 'state', 'status', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'Name', 'Binary', 'State', @@ -147,7 +147,7 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - columns = ('Name', 'Binary', 'State', 'Status') + columns: tuple[str, ...] = ('Name', 'Binary', 'State', 'Status') if parsed_args.long: columns += ( 'Num Hosts', diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py index 82384a11c..9892eadb0 100644 --- a/openstackclient/volume/v3/volume_backup.py +++ b/openstackclient/volume/v3/volume_backup.py @@ -162,7 +162,7 @@ def take_action(self, parsed_args): kwargs['availability_zone'] = parsed_args.availability_zone - columns = ( + columns: tuple[str, ...] = ( "id", "name", "volume_id", @@ -298,7 +298,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume - columns = ( + columns: tuple[str, ...] = ( 'id', 'name', 'description', @@ -307,7 +307,7 @@ def take_action(self, parsed_args): 'is_incremental', 'created_at', ) - column_headers = ( + column_headers: tuple[str, ...] = ( 'ID', 'Name', 'Description', @@ -411,7 +411,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume - columns = ( + columns: tuple[str, ...] = ( 'id', 'volume_id', 'volume_name', @@ -654,7 +654,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume backup = volume_client.find_backup(parsed_args.backup) - columns = ( + columns: tuple[str, ...] = ( "availability_zone", "container", "created_at", From 7380fbe30078afd4932f271a7ef893c3574241fa Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 19 Feb 2025 20:15:43 +0000 Subject: [PATCH 103/245] typing: Add types for empty dicts, tuples In some cases, simply remove them. Change-Id: I24a311a24eb533325dda83005777bcb2e0afc320 Signed-off-by: Stephen Finucane --- openstackclient/common/clientmanager.py | 3 ++- openstackclient/common/project_cleanup.py | 3 ++- openstackclient/compute/v2/aggregate.py | 3 ++- openstackclient/compute/v2/console.py | 3 +-- openstackclient/compute/v2/keypair.py | 3 +-- openstackclient/compute/v2/server.py | 7 ++----- openstackclient/compute/v2/server_event.py | 15 +++------------ openstackclient/image/v2/image.py | 14 ++++++++------ .../image/v2/metadef_resource_type_association.py | 3 +-- .../image/v2/metadef_resource_types.py | 3 +-- openstackclient/network/common.py | 11 ++++++----- openstackclient/network/utils.py | 4 ++-- openstackclient/network/v2/address_group.py | 3 +-- .../network/v2/default_security_group_rule.py | 3 +-- openstackclient/network/v2/floating_ip.py | 5 ++--- .../network/v2/floating_ip_port_forwarding.py | 6 +++--- openstackclient/network/v2/ip_availability.py | 3 +-- openstackclient/network/v2/l3_conntrack_helper.py | 3 +-- openstackclient/network/v2/local_ip.py | 3 +-- .../network/v2/local_ip_association.py | 3 +-- openstackclient/network/v2/ndp_proxy.py | 3 +-- openstackclient/network/v2/network.py | 3 +-- .../network/v2/network_auto_allocated_topology.py | 3 +-- openstackclient/network/v2/network_meter_rule.py | 3 +-- openstackclient/network/v2/network_qos_rule.py | 3 +-- openstackclient/network/v2/network_segment.py | 3 +-- .../network/v2/network_segment_range.py | 11 +++++------ openstackclient/network/v2/network_trunk.py | 3 +-- openstackclient/network/v2/security_group_rule.py | 3 +-- openstackclient/network/v2/subnet.py | 3 ++- .../tests/functional/network/v2/common.py | 2 +- openstackclient/tests/unit/image/v2/test_image.py | 8 +++++++- .../volume/v2/volume_transfer_request.py | 3 --- openstackclient/volume/v2/volume_type.py | 4 +--- openstackclient/volume/v3/volume_type.py | 4 +--- 35 files changed, 67 insertions(+), 93 deletions(-) diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 42e2b24fe..a0455bf1a 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -18,6 +18,7 @@ import importlib import logging import sys +import typing as ty from osc_lib import clientmanager from osc_lib import shell @@ -26,7 +27,7 @@ LOG = logging.getLogger(__name__) -PLUGIN_MODULES = [] +PLUGIN_MODULES: list[ty.Any] = [] USER_AGENT = 'python-openstackclient' diff --git a/openstackclient/common/project_cleanup.py b/openstackclient/common/project_cleanup.py index 0c143fad3..d8dc5ac9d 100644 --- a/openstackclient/common/project_cleanup.py +++ b/openstackclient/common/project_cleanup.py @@ -17,6 +17,7 @@ import logging import os import queue +import typing as ty from cliff.formatters import table from osc_lib.command import command @@ -100,7 +101,7 @@ def take_action(self, parsed_args): project_connect = sdk.connect_as_project(project) if project_connect: - status_queue = queue.Queue() + status_queue: queue.Queue[ty.Any] = queue.Queue() parsed_args.max_width = int( os.environ.get('CLIFF_MAX_TERM_WIDTH', 0) ) diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index 293e323da..b82209b78 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -17,6 +17,7 @@ """Compute v2 Aggregate action implementations""" import logging +import typing as ty from openstack import utils as sdk_utils from osc_lib.cli import format_columns @@ -321,7 +322,7 @@ def take_action(self, parsed_args): if kwargs: compute_client.update_aggregate(aggregate.id, **kwargs) - properties = {} + properties: dict[str, ty.Any] = {} if parsed_args.no_property: # NOTE(RuiChen): "availability_zone" can not be unset from # properties. It is already excluded from show and create output. diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py index 3059cb100..c2db25339 100644 --- a/openstackclient/compute/v2/console.py +++ b/openstackclient/compute/v2/console.py @@ -25,10 +25,9 @@ def _get_console_columns(item): # To maintain backwards compatibility we need to rename sdk props to # whatever OSC was using before - column_map = {} hidden_columns = ['id', 'links', 'location', 'name'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index 85c340d58..45a3b9021 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -61,14 +61,13 @@ def _generate_keypair(): def _get_keypair_columns(item, hide_pub_key=False, hide_priv_key=False): # To maintain backwards compatibility we need to rename sdk props to # whatever OSC was using before - column_map = {} hidden_columns = ['links', 'location'] if hide_pub_key: hidden_columns.append('public_key') if hide_priv_key: hidden_columns.append('private_key') return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index cdb0f9676..4c978da30 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1032,7 +1032,6 @@ def __call__(self, parser, namespace, values, option_string=None): class BDMAction(parseractions.MultiKeyValueAction): def __init__(self, option_strings, dest, **kwargs): - required_keys = [] optional_keys = [ 'uuid', 'source_type', @@ -1050,7 +1049,7 @@ def __init__(self, option_strings, dest, **kwargs): super().__init__( option_strings, dest, - required_keys=required_keys, + required_keys=[], optional_keys=optional_keys, **kwargs, ) @@ -3963,14 +3962,12 @@ def update_parser_common(self, parser): return parser def take_action_network(self, client, parsed_args): - attrs = {} obj = client.find_ip( parsed_args.ip_address, ignore_missing=False, ) - attrs['port_id'] = None - client.update_ip(obj, **attrs) + client.update_ip(obj, port_id=None) def take_action_compute(self, client, parsed_args): server = client.find_server(parsed_args.server, ignore_missing=False) diff --git a/openstackclient/compute/v2/server_event.py b/openstackclient/compute/v2/server_event.py index d48283f0e..6f242c759 100644 --- a/openstackclient/compute/v2/server_event.py +++ b/openstackclient/compute/v2/server_event.py @@ -65,17 +65,11 @@ class ServerActionEventColumn(columns.FormattableColumn): """ def _format_event(self, event): - column_map = {} hidden_columns = ['id', 'name', 'location'] _, columns = utils.get_osc_show_columns_for_sdk_resource( - event, - column_map, - hidden_columns, - ) - data = utils.get_item_properties( - event, - columns, + event, {}, hidden_columns ) + data = utils.get_item_properties(event, columns) return dict(zip(columns, data)) def human_readable(self): @@ -88,7 +82,6 @@ def machine_readable(self): def _get_server_event_columns(item, client): - column_map = {} hidden_columns = ['name', 'server_id', 'links', 'location'] if not sdk_utils.supports_microversion(client, '2.58'): @@ -96,9 +89,7 @@ def _get_server_event_columns(item, client): hidden_columns.append('updated_at') return utils.get_osc_show_columns_for_sdk_resource( - item, - column_map, - hidden_columns, + item, {}, hidden_columns ) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index dca1a8f26..dfe80a907 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -20,6 +20,7 @@ import logging import os import sys +import typing as ty from cinderclient import api_versions from openstack import exceptions as sdk_exceptions @@ -615,7 +616,10 @@ def _take_action_volume(self, parsed_args): volume_client.volumes, parsed_args.volume, ) - kwargs = {} + kwargs = { + 'visibility': None, + 'protected': None, + } if volume_client.api_version < api_versions.APIVersion('3.1'): if parsed_args.visibility or parsed_args.is_protected is not None: msg = _( @@ -625,10 +629,8 @@ def _take_action_volume(self, parsed_args): ) raise exceptions.CommandError(msg) else: - kwargs.update( - visibility=parsed_args.visibility or 'private', - protected=parsed_args.is_protected or False, - ) + kwargs['visibility'] = parsed_args.visibility or 'private' + kwargs['protected'] = parsed_args.is_protected or False response, body = volume_client.volumes.upload_to_image( source_volume.id, @@ -1453,7 +1455,6 @@ def take_action(self, parsed_args): ignore_missing=False, ) - kwargs = {} tagret = 0 propret = 0 if parsed_args.tags: @@ -1466,6 +1467,7 @@ def take_action(self, parsed_args): ) tagret += 1 + kwargs: dict[str, ty.Any] = {} if parsed_args.properties: for k in parsed_args.properties: if k in image: diff --git a/openstackclient/image/v2/metadef_resource_type_association.py b/openstackclient/image/v2/metadef_resource_type_association.py index e6461e5da..306783cdd 100644 --- a/openstackclient/image/v2/metadef_resource_type_association.py +++ b/openstackclient/image/v2/metadef_resource_type_association.py @@ -22,10 +22,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/image/v2/metadef_resource_types.py b/openstackclient/image/v2/metadef_resource_types.py index bbd77636f..3477df35e 100644 --- a/openstackclient/image/v2/metadef_resource_types.py +++ b/openstackclient/image/v2/metadef_resource_types.py @@ -23,8 +23,7 @@ class ListMetadefResourceTypes(command.Lister): def take_action(self, parsed_args): image_client = self.app.client_manager.image - kwargs = {} - data = image_client.metadef_resource_types(**kwargs) + data = image_client.metadef_resource_types() columns = ['Name'] column_headers = columns return ( diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index c32c98cfb..568d893d2 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -14,6 +14,7 @@ import abc import contextlib import logging +import typing as ty import openstack.exceptions from osc_lib.cli import parseractions @@ -295,10 +296,10 @@ class NeutronCommandWithExtraArgs(command.Command): } def _get_property_converter(self, _property): - if 'type' not in _property: - converter = str - else: + if 'type' in _property: converter = self._allowed_types_dict.get(_property['type']) + else: + converter = str if not converter: raise exceptions.CommandError( @@ -311,7 +312,7 @@ def _get_property_converter(self, _property): return converter def _parse_extra_properties(self, extra_properties): - result = {} + result: dict[str, ty.Any] = {} if extra_properties: for _property in extra_properties: converter = self._get_property_converter(_property) @@ -344,7 +345,7 @@ def get_parser(self, prog_name): class NeutronUnsetCommandWithExtraArgs(NeutronCommandWithExtraArgs): def _parse_extra_properties(self, extra_properties): - result = {} + result: dict[str, ty.Any] = {} if extra_properties: for _property in extra_properties: result[_property['name']] = None diff --git a/openstackclient/network/utils.py b/openstackclient/network/utils.py index 648c9092a..30c0da064 100644 --- a/openstackclient/network/utils.py +++ b/openstackclient/network/utils.py @@ -58,12 +58,12 @@ def str2list(strlist): return result -def str2dict(strdict): +def str2dict(strdict: str) -> dict[str, str]: """Convert key1:value1;key2:value2;... string into dictionary. :param strdict: string in the form of key1:value1;key2:value2 """ - result = {} + result: dict[str, str] = {} if not strdict: return result i = 0 diff --git a/openstackclient/network/v2/address_group.py b/openstackclient/network/v2/address_group.py index 893f4673c..d47c34cdd 100644 --- a/openstackclient/network/v2/address_group.py +++ b/openstackclient/network/v2/address_group.py @@ -28,10 +28,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/network/v2/default_security_group_rule.py b/openstackclient/network/v2/default_security_group_rule.py index 9c39b3f9c..fddec6874 100644 --- a/openstackclient/network/v2/default_security_group_rule.py +++ b/openstackclient/network/v2/default_security_group_rule.py @@ -28,10 +28,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'name', 'revision_number'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index 266ff8fa3..a28a810aa 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -27,10 +27,9 @@ def _get_network_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -571,7 +570,7 @@ def take_action(self, parsed_args): parsed_args.floating_ip, ignore_missing=False, ) - attrs = {} + attrs: dict[str, None] = {} if parsed_args.port: attrs['port_id'] = None if parsed_args.qos_policy: diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py index 185baf446..3810deddf 100644 --- a/openstackclient/network/v2/floating_ip_port_forwarding.py +++ b/openstackclient/network/v2/floating_ip_port_forwarding.py @@ -14,6 +14,7 @@ """Floating IP Port Forwarding action implementations""" import logging +import typing as ty from osc_lib.command import command from osc_lib import exceptions @@ -85,10 +86,9 @@ def validate_port(port): def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -165,7 +165,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - attrs = {} + attrs: dict[str, ty.Any] = {} client = self.app.client_manager.network floating_ip = client.find_ip( parsed_args.floating_ip, diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py index 30b6a0e49..51ea01199 100644 --- a/openstackclient/network/v2/ip_availability.py +++ b/openstackclient/network/v2/ip_availability.py @@ -26,10 +26,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['id', 'name', 'location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/network/v2/l3_conntrack_helper.py b/openstackclient/network/v2/l3_conntrack_helper.py index f4c7adcc3..011db39e0 100644 --- a/openstackclient/network/v2/l3_conntrack_helper.py +++ b/openstackclient/network/v2/l3_conntrack_helper.py @@ -25,10 +25,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/network/v2/local_ip.py b/openstackclient/network/v2/local_ip.py index a99951db4..7fd58818c 100644 --- a/openstackclient/network/v2/local_ip.py +++ b/openstackclient/network/v2/local_ip.py @@ -28,10 +28,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/network/v2/local_ip_association.py b/openstackclient/network/v2/local_ip_association.py index 816f04882..c6b59184b 100644 --- a/openstackclient/network/v2/local_ip_association.py +++ b/openstackclient/network/v2/local_ip_association.py @@ -28,10 +28,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'name', 'id', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/network/v2/ndp_proxy.py b/openstackclient/network/v2/ndp_proxy.py index 7e1370c50..fe97cd078 100644 --- a/openstackclient/network/v2/ndp_proxy.py +++ b/openstackclient/network/v2/ndp_proxy.py @@ -29,10 +29,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index dbc665c97..e04efed61 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -68,8 +68,7 @@ def _get_columns_network(item): def _get_columns_compute(item): - column_map = {} - return utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, {}) def _get_attrs_network(client_manager, parsed_args): diff --git a/openstackclient/network/v2/network_auto_allocated_topology.py b/openstackclient/network/v2/network_auto_allocated_topology.py index a0dcfa47e..3f5795e6a 100644 --- a/openstackclient/network/v2/network_auto_allocated_topology.py +++ b/openstackclient/network/v2/network_auto_allocated_topology.py @@ -25,10 +25,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['name', 'location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py index a3765456c..f884b2f6d 100644 --- a/openstackclient/network/v2/network_meter_rule.py +++ b/openstackclient/network/v2/network_meter_rule.py @@ -27,10 +27,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py index 0784de890..83f787389 100644 --- a/openstackclient/network/v2/network_qos_rule.py +++ b/openstackclient/network/v2/network_qos_rule.py @@ -73,10 +73,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py index fbccba00e..5e40108ca 100644 --- a/openstackclient/network/v2/network_segment.py +++ b/openstackclient/network/v2/network_segment.py @@ -26,10 +26,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py index 48a8ca1a8..e2716f13f 100644 --- a/openstackclient/network/v2/network_segment_range.py +++ b/openstackclient/network/v2/network_segment_range.py @@ -18,6 +18,7 @@ import itertools import logging +import typing as ty from osc_lib.command import command from osc_lib import exceptions @@ -32,10 +33,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) @@ -59,7 +59,7 @@ def _is_prop_empty(columns, props, prop_name): def _exchange_dict_keys_with_values(orig_dict): - updated_dict = dict() + updated_dict: dict[str, ty.Any] = {} for k, v in orig_dict.items(): k = [k] if not updated_dict.get(v): @@ -351,8 +351,7 @@ def take_action(self, parsed_args): ) % {'e': e} raise exceptions.CommandError(msg) - filters = {} - data = network_client.network_segment_ranges(**filters) + data = network_client.network_segment_ranges() headers: tuple[str, ...] = ( 'ID', @@ -396,7 +395,7 @@ def take_action(self, parsed_args): 'available', ) - display_props = tuple() + display_props: tuple[str, ...] = tuple() for s in data: props = utils.get_item_properties(s, columns) if ( diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index 740c3f21d..8d83e6175 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -336,10 +336,9 @@ def take_action(self, parsed_args): def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return osc_utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index 884e7a2f7..954ee4cbe 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -30,10 +30,9 @@ def _get_columns(item): - column_map = {} hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( - item, column_map, hidden_columns + item, {}, hidden_columns ) diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 7e9c30d90..30196bf86 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -15,6 +15,7 @@ import copy import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns @@ -831,7 +832,7 @@ def take_action(self, parsed_args): client = self.app.client_manager.network obj = client.find_subnet(parsed_args.subnet, ignore_missing=False) - attrs = {} + attrs: dict[str, ty.Any] = {} if parsed_args.gateway: attrs['gateway_ip'] = None if parsed_args.dns_nameservers: diff --git a/openstackclient/tests/functional/network/v2/common.py b/openstackclient/tests/functional/network/v2/common.py index 4cd14ebc7..715dab38e 100644 --- a/openstackclient/tests/functional/network/v2/common.py +++ b/openstackclient/tests/functional/network/v2/common.py @@ -56,7 +56,7 @@ def test_tag_operation(self): 'set', name1, '--tag red --tag green', ['red', 'green'] ) - list_expected = ( + list_expected: tuple[tuple[str, list[str]]] = ( (name1, ['red', 'green']), (name2, ['red', 'blue']), (name3, []), diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index f563828cb..049510c71 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -346,7 +346,13 @@ class FakeVolume: columns, data = self.cmd.take_action(parsed_args) self.volumes_mock.upload_to_image.assert_called_with( - fake_vol_id, False, self.new_image.name, 'bare', 'raw' + fake_vol_id, + False, + self.new_image.name, + 'bare', + 'raw', + visibility=None, + protected=None, ) @mock.patch('osc_lib.utils.find_resource') diff --git a/openstackclient/volume/v2/volume_transfer_request.py b/openstackclient/volume/v2/volume_transfer_request.py index 18094b626..3bd91d052 100644 --- a/openstackclient/volume/v2/volume_transfer_request.py +++ b/openstackclient/volume/v2/volume_transfer_request.py @@ -85,8 +85,6 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.volume - kwargs = {} - volume_id = utils.find_resource( volume_client.volumes, parsed_args.volume, @@ -94,7 +92,6 @@ def take_action(self, parsed_args): volume_transfer_request = volume_client.transfers.create( volume_id, parsed_args.name, - **kwargs, ) volume_transfer_request._info.pop("links", None) diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index aaf0cb51f..9e2ac20cb 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -649,9 +649,7 @@ def take_action(self, parsed_args): ) result += 1 - properties = {} - - properties = {} + properties: dict[str, str] = {} if parsed_args.properties: properties.update(parsed_args.properties) if parsed_args.multiattach: diff --git a/openstackclient/volume/v3/volume_type.py b/openstackclient/volume/v3/volume_type.py index 1cad79c0f..b5b328cb8 100644 --- a/openstackclient/volume/v3/volume_type.py +++ b/openstackclient/volume/v3/volume_type.py @@ -731,9 +731,7 @@ def take_action(self, parsed_args): ) result += 1 - properties = {} - - properties = {} + properties: dict[str, str] = {} if parsed_args.properties: properties.update(parsed_args.properties) if parsed_args.multiattach: From 2c0a3ba137cf78852d48b4bdfd1d48bb688f3bfc Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 19 Feb 2025 20:15:43 +0000 Subject: [PATCH 104/245] typing: Use consistent types Resolve 'Incompatible types in assignment' errors. Change-Id: I1ea186ff766e0f72cac384fab22d1c2f82e02ef0 Signed-off-by: Stephen Finucane --- openstackclient/network/v2/network_trunk.py | 5 +++-- openstackclient/tests/functional/base.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index 8d83e6175..f8f1c5bcb 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -17,6 +17,7 @@ """Network trunk and subports action implementations""" import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns @@ -343,7 +344,7 @@ def _get_columns(item): def _get_attrs_for_trunk(client_manager, parsed_args): - attrs = {} + attrs: dict[str, ty.Any] = {} if parsed_args.name is not None: attrs['name'] = str(parsed_args.name) if parsed_args.description is not None: @@ -400,7 +401,7 @@ def _format_subports(client_manager, subports): def _get_attrs_for_subports(client_manager, parsed_args): - attrs = {} + attrs = [] if 'set_subports' in parsed_args and parsed_args.set_subports is not None: attrs = _format_subports(client_manager, parsed_args.set_subports) if ( diff --git a/openstackclient/tests/functional/base.py b/openstackclient/tests/functional/base.py index 2539c56fc..97b2c1830 100644 --- a/openstackclient/tests/functional/base.py +++ b/openstackclient/tests/functional/base.py @@ -36,8 +36,8 @@ def execute(cmd, *, fail_ok=False): proc = subprocess.Popen(cmdlist, stdout=stdout, stderr=stderr, env=env) - result_out, result_err = proc.communicate() - result_out = result_out.decode('utf-8') + result_out_b, result_err = proc.communicate() + result_out = result_out_b.decode('utf-8') LOG.debug('stdout: %s', result_out) LOG.debug('stderr: %s', result_err) From 9435ef825a5720516d48304d57aa74c38d4a33e7 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 19 Feb 2025 20:15:44 +0000 Subject: [PATCH 105/245] typing: Remove use of optional imports Do them inline instead. Change-Id: Icab1a0452249efc79f214c4d7b369d02291e94b4 Signed-off-by: Stephen Finucane --- openstackclient/image/v1/image.py | 19 ++++++++----------- openstackclient/image/v2/image.py | 13 ++++--------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index fba656cf6..490b4122f 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -30,12 +30,6 @@ from openstackclient.i18n import _ -if os.name == "nt": - import msvcrt -else: - msvcrt = None - - CONTAINER_CHOICES = ["ami", "ari", "aki", "bare", "docker", "ova", "ovf"] DEFAULT_CONTAINER_FORMAT = 'bare' DEFAULT_DISK_FORMAT = 'raw' @@ -53,7 +47,6 @@ "ploop", ] - LOG = logging.getLogger(__name__) @@ -323,8 +316,10 @@ def take_action(self, parsed_args): else: # Read file from stdin if not sys.stdin.isatty(): - if msvcrt: - msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + if os.name == "nt": + import msvcrt + + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) # type: ignore if hasattr(sys.stdin, 'buffer'): kwargs['data'] = sys.stdin.buffer else: @@ -774,8 +769,10 @@ def take_action(self, parsed_args): # Read file from stdin if sys.stdin.isatty() is not True: if parsed_args.stdin: - if msvcrt: - msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + if os.name == "nt": + import msvcrt + + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) # type: ignore if hasattr(sys.stdin, 'buffer'): kwargs['data'] = sys.stdin.buffer else: diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index dfe80a907..a45f3c39e 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -37,12 +37,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common -if os.name == "nt": - import msvcrt -else: - msvcrt = None - - CONTAINER_CHOICES = ["ami", "ari", "aki", "bare", "docker", "ova", "ovf"] DEFAULT_CONTAINER_FORMAT = 'bare' DEFAULT_DISK_FORMAT = 'raw' @@ -61,7 +55,6 @@ ] MEMBER_STATUS_CHOICES = ["accepted", "pending", "rejected", "all"] - LOG = logging.getLogger(__name__) @@ -155,8 +148,10 @@ def get_data_from_stdin(): image = sys.stdin if hasattr(sys.stdin, 'buffer'): image = sys.stdin.buffer - if msvcrt: - msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + if os.name == "nt": + import msvcrt + + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) # type: ignore return image else: From e28046cc196310b2d495a6f121e3950bce5decc7 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 19 Feb 2025 20:15:44 +0000 Subject: [PATCH 106/245] typing: Correct type for missing attributes Change-Id: I55652220ecd663fa024937646dfef92595e1cd0f Signed-off-by: Stephen Finucane --- openstackclient/common/project_cleanup.py | 2 +- openstackclient/common/quota.py | 3 ++- openstackclient/compute/v2/keypair.py | 1 - openstackclient/compute/v2/server.py | 4 ++-- openstackclient/identity/v2_0/user.py | 5 +---- openstackclient/identity/v3/user.py | 3 ++- openstackclient/image/v2/image.py | 6 +++--- openstackclient/network/common.py | 8 ++++++-- openstackclient/network/v2/network_meter_rule.py | 3 ++- openstackclient/network/v2/router.py | 9 +++++---- openstackclient/tests/functional/network/v2/common.py | 10 +++++++--- .../tests/unit/common/test_project_cleanup.py | 11 +++++------ 12 files changed, 36 insertions(+), 29 deletions(-) diff --git a/openstackclient/common/project_cleanup.py b/openstackclient/common/project_cleanup.py index d8dc5ac9d..84a07353d 100644 --- a/openstackclient/common/project_cleanup.py +++ b/openstackclient/common/project_cleanup.py @@ -36,7 +36,7 @@ def ask_user_yesno(msg): :return bool: User choice """ while True: - answer = getpass._raw_input('{} [{}]: '.format(msg, 'y/n')) + answer = getpass.getpass('{} [{}]: '.format(msg, 'y/n')) if answer in ('y', 'Y', 'yes'): return True elif answer in ('n', 'N', 'no'): diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 1ac50f80d..03a4f0908 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -18,6 +18,7 @@ import itertools import logging import sys +import typing as ty from openstack import exceptions as sdk_exceptions from osc_lib.command import command @@ -199,7 +200,7 @@ def _network_quota_to_dict(network_quota, detail=False): # # so we need to make conversion to have data in same format from # all of the services - result = {"usage": {}, "reservation": {}} + result: dict[str, ty.Any] = {"usage": {}, "reservation": {}} for key, values in dict_quota.items(): if values is None: continue diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index 45a3b9021..52fe0b591 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -126,7 +126,6 @@ def take_action(self, parsed_args): kwargs = {'name': parsed_args.name} if parsed_args.public_key: - generated_keypair = None try: with open(os.path.expanduser(parsed_args.public_key)) as p: public_key = p.read() diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 4c978da30..8e88d8e90 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1970,7 +1970,7 @@ def _match_image(image_api, wanted_properties): # convert from the novaclient-derived "NIC" view to the actual # "network" view - network = {} + network: dict[str, str] = {} if nic['net-id']: network['uuid'] = nic['net-id'] @@ -1986,7 +1986,7 @@ def _match_image(image_api, wanted_properties): if nic.get('tag'): # tags are optional network['tag'] = nic['tag'] - networks.append(network) + networks.append(network) # type: ignore[union-attr] if not parsed_args.nics and sdk_utils.supports_microversion( compute_client, '2.37' diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py index 2262d264d..c4cd52b8d 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -249,10 +249,7 @@ def take_action(self, parsed_args): data = identity_client.users.list(tenant_id=project) if parsed_args.project: - d = {} - for s in data: - d[s.id] = s - data = d.values() + data = {s.id: s for s in data}.values() if parsed_args.long: # FIXME(dtroyer): Sometimes user objects have 'tenant_id' instead diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index fc727e957..bbf3f1df5 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -17,6 +17,7 @@ import copy import logging +import typing as ty from openstack import exceptions as sdk_exc from osc_lib.command import command @@ -58,7 +59,7 @@ def _format_user(user): def _get_options_for_user(identity_client, parsed_args): - options = {} + options: dict[str, ty.Any] = {} if parsed_args.ignore_lockout_failure_attempts: options['ignore_lockout_failure_attempts'] = True if parsed_args.no_ignore_lockout_failure_attempts: diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index a45f3c39e..57f278dcd 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -428,7 +428,7 @@ def _take_action_image(self, parsed_args): # Build an attribute dict from the parsed args, only include # attributes that were actually set on the command line - kwargs = {'allow_duplicates': True} + kwargs: dict[str, ty.Any] = {'allow_duplicates': True} copy_attrs = ( 'name', 'id', @@ -611,7 +611,7 @@ def _take_action_volume(self, parsed_args): volume_client.volumes, parsed_args.volume, ) - kwargs = { + kwargs: dict[str, ty.Any] = { 'visibility': None, 'protected': None, } @@ -1575,7 +1575,7 @@ def take_action(self, parsed_args): else: fp = get_data_from_stdin() - kwargs = {} + kwargs: dict[str, ty.Any] = {} if parsed_args.progress and parsed_args.filename: # NOTE(stephenfin): we only show a progress bar if the user diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index 568d893d2..c5fad534f 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -12,10 +12,12 @@ # import abc +import argparse import contextlib import logging import typing as ty +import cliff.app import openstack.exceptions from osc_lib.cli import parseractions from osc_lib.command import command @@ -66,6 +68,8 @@ class NetDetectionMixin(metaclass=abc.ABCMeta): present the options for both network types, often qualified accordingly. """ + app: cliff.app.App + @property def _network_type(self): """Discover whether the running cloud is using neutron or nova-network. @@ -132,9 +136,9 @@ def split_help(network_help, compute_help): ) ) - def get_parser(self, prog_name): + def get_parser(self, prog_name: str) -> argparse.ArgumentParser: LOG.debug('get_parser(%s)', prog_name) - parser = super().get_parser(prog_name) + parser = super().get_parser(prog_name) # type: ignore parser = self.update_parser_common(parser) LOG.debug('common parser: %s', parser) if self.is_neutron or self.is_docs_build: diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py index f884b2f6d..c7551543e 100644 --- a/openstackclient/network/v2/network_meter_rule.py +++ b/openstackclient/network/v2/network_meter_rule.py @@ -14,6 +14,7 @@ """Meter Rule Implementations""" import logging +import typing as ty from osc_lib.command import command from osc_lib import exceptions @@ -34,7 +35,7 @@ def _get_columns(item): def _get_attrs(client_manager, parsed_args): - attrs = {} + attrs: dict[str, ty.Any] = {} if parsed_args.exclude: attrs['excluded'] = True diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 2794df43a..0f6b622cd 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -18,6 +18,7 @@ import copy import json import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns @@ -104,21 +105,21 @@ def _passed_multiple_gateways(extension_supported, external_gateways): def _get_external_gateway_attrs(client_manager, parsed_args): - attrs = {} + attrs: dict[str, ty.Any] = {} if parsed_args.external_gateways: external_gateways: collections.defaultdict[str, list[dict]] = ( collections.defaultdict(list) ) n_client = client_manager.network - first_network_id = None + first_network_id = '' for gw_net_name_or_id in parsed_args.external_gateways: gateway_info = {} gw_net = n_client.find_network( gw_net_name_or_id, ignore_missing=False ) - if first_network_id is None: + if not first_network_id: first_network_id = gw_net.id gateway_info['network_id'] = gw_net.id if 'disable_snat' in parsed_args and parsed_args.disable_snat: @@ -146,7 +147,7 @@ def _get_external_gateway_attrs(client_manager, parsed_args): for ip_spec in parsed_args.fixed_ips: # If there is only one gateway, this value will represent the # network ID for it, otherwise it will be overridden. - ip_net_id = first_network_id + ip_net_id: str = first_network_id if ip_spec.get('subnet', False): subnet_name_id = ip_spec.pop('subnet') diff --git a/openstackclient/tests/functional/network/v2/common.py b/openstackclient/tests/functional/network/v2/common.py index 715dab38e..248758a84 100644 --- a/openstackclient/tests/functional/network/v2/common.py +++ b/openstackclient/tests/functional/network/v2/common.py @@ -33,7 +33,7 @@ def setUp(self): class NetworkTagTests(NetworkTests): """Functional tests with tag operation""" - base_command = None + base_command: str def test_tag_operation(self): # Get project IDs @@ -56,7 +56,7 @@ def test_tag_operation(self): 'set', name1, '--tag red --tag green', ['red', 'green'] ) - list_expected: tuple[tuple[str, list[str]]] = ( + list_expected: tuple[tuple[str, list[str]], ...] = ( (name1, ['red', 'green']), (name2, ['red', 'blue']), (name3, []), @@ -93,7 +93,11 @@ def _create_resource_for_tag_test(self, name, args): parse_output=True, ) - def _create_resource_and_tag_check(self, args, expected): + def _create_resource_and_tag_check( + self, + args: str, + expected: list[str], + ) -> str: name = uuid.uuid4().hex cmd_output = self._create_resource_for_tag_test(name, args) self.addCleanup(self.openstack, f'{self.base_command} delete {name}') diff --git a/openstackclient/tests/unit/common/test_project_cleanup.py b/openstackclient/tests/unit/common/test_project_cleanup.py index 50d4e6487..42020646c 100644 --- a/openstackclient/tests/unit/common/test_project_cleanup.py +++ b/openstackclient/tests/unit/common/test_project_cleanup.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from io import StringIO from unittest import mock from openstackclient.common import project_cleanup @@ -70,7 +69,7 @@ def test_project_cleanup_with_filters(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = None - with mock.patch('sys.stdin', StringIO('y')): + with mock.patch('getpass.getpass', return_value='y'): result = self.cmd.take_action(parsed_args) self.sdk_connect_as_project_mock.assert_called_with(self.project) @@ -143,7 +142,7 @@ def test_project_cleanup_with_project(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = None - with mock.patch('sys.stdin', StringIO('y')): + with mock.patch('getpass.getpass', return_value='y'): result = self.cmd.take_action(parsed_args) self.sdk_connect_as_project_mock.assert_called_with(self.project) @@ -178,7 +177,7 @@ def test_project_cleanup_with_project_abort(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = None - with mock.patch('sys.stdin', StringIO('n')): + with mock.patch('getpass.getpass', return_value='y'): result = self.cmd.take_action(parsed_args) self.sdk_connect_as_project_mock.assert_called_with(self.project) @@ -234,7 +233,7 @@ def test_project_cleanup_with_auth_project(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = None - with mock.patch('sys.stdin', StringIO('y')): + with mock.patch('getpass.getpass', return_value='y'): result = self.cmd.take_action(parsed_args) self.sdk_connect_as_project_mock.assert_not_called() @@ -268,7 +267,7 @@ def test_project_cleanup_with_skip_resource(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = None - with mock.patch('sys.stdin', StringIO('y')): + with mock.patch('getpass.getpass', return_value='y'): result = self.cmd.take_action(parsed_args) self.sdk_connect_as_project_mock.assert_called_with(self.project) From 9de592ebaf708cfab2c7f26e260b69efad470ee3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 19 Feb 2025 20:15:44 +0000 Subject: [PATCH 107/245] typing: Resolve incompatible operand issues Change-Id: I7f3dd908053b9ace5206d0a1bd3b8258fd0264ef Signed-off-by: Stephen Finucane --- openstackclient/identity/v3/limit.py | 9 ++------ .../identity/v3/registered_limit.py | 9 +++----- openstackclient/network/v2/port.py | 3 +++ .../tests/functional/common/test_quota.py | 2 +- .../tests/functional/compute/v2/common.py | 18 ++++++++++----- .../network/v2/test_address_group.py | 4 ++++ .../network/v2/test_l3_conntrack_helper.py | 23 +++++++++++-------- .../functional/network/v2/test_local_ip.py | 4 ++++ .../network/v2/test_network_meter_rule.py | 4 ++-- .../network/v2/test_network_rbac.py | 4 ++-- .../functional/network/v2/test_router.py | 4 ++++ .../functional/network/v2/test_subnet_pool.py | 4 ++++ 12 files changed, 55 insertions(+), 33 deletions(-) diff --git a/openstackclient/identity/v3/limit.py b/openstackclient/identity/v3/limit.py index 333078d03..7f2fe885d 100644 --- a/openstackclient/identity/v3/limit.py +++ b/openstackclient/identity/v3/limit.py @@ -77,8 +77,7 @@ def take_action(self, parsed_args): ) region = None if parsed_args.region: - val = getattr(parsed_args, 'region', None) - if 'None' not in val: + if 'None' not in parsed_args.region: # NOTE (vishakha): Due to bug #1799153 and for any another # related case where GET resource API does not support the # filter by name, osc_lib.utils.find_resource() method cannot @@ -149,11 +148,7 @@ def take_action(self, parsed_args): ) region = None if parsed_args.region: - region = utils.find_resource( - identity_client.regions, parsed_args.region - ) - val = getattr(parsed_args, 'region', None) - if 'None' not in val: + if 'None' not in parsed_args.region: # NOTE (vishakha): Due to bug #1799153 and for any another # related case where GET resource API does not support the # filter by name, osc_lib.utils.find_resource() method cannot diff --git a/openstackclient/identity/v3/registered_limit.py b/openstackclient/identity/v3/registered_limit.py index 630066f8a..34ce2101f 100644 --- a/openstackclient/identity/v3/registered_limit.py +++ b/openstackclient/identity/v3/registered_limit.py @@ -68,8 +68,7 @@ def take_action(self, parsed_args): ) region = None if parsed_args.region: - val = getattr(parsed_args, 'region', None) - if 'None' not in val: + if 'None' not in parsed_args.region: # NOTE (vishakha): Due to bug #1799153 and for any another # related case where GET resource API does not support the # filter by name, osc_lib.utils.find_resource() method cannot @@ -175,8 +174,7 @@ def take_action(self, parsed_args): ) region = None if parsed_args.region: - val = getattr(parsed_args, 'region', None) - if 'None' not in val: + if 'None' not in parsed_args.region: # NOTE (vishakha): Due to bug #1799153 and for any another # related case where GET resource API does not support the # filter by name, osc_lib.utils.find_resource() method cannot @@ -280,8 +278,7 @@ def take_action(self, parsed_args): region = None if parsed_args.region: - val = getattr(parsed_args, 'region', None) - if 'None' not in val: + if 'None' not in parsed_args.region: # NOTE (vishakha): Due to bug #1799153 and for any another # related case where GET resource API does not support the # filter by name, osc_lib.utils.find_resource() method cannot diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index a16707ff7..8dbe60511 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -17,6 +17,7 @@ import copy import json import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns @@ -39,6 +40,8 @@ def human_readable(self): class SubPortColumn(format_columns.ListDictColumn): + _value: ty.Any + def _retrieve_subports(self): if isinstance(self._value, dict): self._value = self._value['sub_ports'] diff --git a/openstackclient/tests/functional/common/test_quota.py b/openstackclient/tests/functional/common/test_quota.py index a2edc4259..373b178c1 100644 --- a/openstackclient/tests/functional/common/test_quota.py +++ b/openstackclient/tests/functional/common/test_quota.py @@ -25,7 +25,7 @@ class QuotaTests(base.TestCase): test runs as these may run in parallel and otherwise step on each other. """ - PROJECT_NAME = None + PROJECT_NAME: str @classmethod def setUpClass(cls): diff --git a/openstackclient/tests/functional/compute/v2/common.py b/openstackclient/tests/functional/compute/v2/common.py index 4ada4304c..5892ee3a0 100644 --- a/openstackclient/tests/functional/compute/v2/common.py +++ b/openstackclient/tests/functional/compute/v2/common.py @@ -22,9 +22,9 @@ class ComputeTestCase(base.TestCase): """Common functional test bits for Compute commands""" - flavor_name = None - image_name = None - network_arg = None + flavor_name: str + image_name: str + network_arg: str def setUp(self): """Select common resources""" @@ -34,7 +34,7 @@ def setUp(self): self.network_arg = self.get_network() @classmethod - def get_flavor(cls): + def get_flavor(cls) -> str: # NOTE(rtheis): Get cirros256 or m1.tiny flavors since functional # tests may create other flavors. flavors = cls.openstack("flavor list", parse_output=True) @@ -43,10 +43,13 @@ def get_flavor(cls): if flavor['Name'] in ['m1.tiny', 'cirros256']: server_flavor = flavor['Name'] break + + assert server_flavor is not None + return server_flavor @classmethod - def get_image(cls): + def get_image(cls) -> str: # NOTE(rtheis): Get first Cirros image since functional tests may # create other images. Image may be named '-uec' or # '-disk'. @@ -59,10 +62,13 @@ def get_image(cls): ): server_image = image['Name'] break + + assert server_image is not None + return server_image @classmethod - def get_network(cls): + def get_network(cls) -> str: try: # NOTE(rtheis): Get private network since functional tests may # create other networks. diff --git a/openstackclient/tests/functional/network/v2/test_address_group.py b/openstackclient/tests/functional/network/v2/test_address_group.py index 8146112a3..6bc13c75d 100644 --- a/openstackclient/tests/functional/network/v2/test_address_group.py +++ b/openstackclient/tests/functional/network/v2/test_address_group.py @@ -75,6 +75,10 @@ def test_address_group_list(self): self.assertNotEqual(admin_project_id, demo_project_id) self.assertEqual(admin_project_id, auth_project_id) + # type narrow + assert admin_project_id is not None + assert demo_project_id is not None + name1 = uuid.uuid4().hex cmd_output = self.openstack( 'address group create ' + name1, diff --git a/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py b/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py index 60ae6d5ff..ab0cb53e8 100644 --- a/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py +++ b/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py @@ -94,9 +94,14 @@ def test_l3_conntrack_helper_list(self): self.assertIn(ct, expected_helpers) def test_l3_conntrack_helper_set_and_show(self): - helper = {'helper': 'tftp', 'protocol': 'udp', 'port': 69} + helper = 'tftp' + proto = 'udp' + port = 69 router_id = self._create_router() - created_helper = self._create_helpers(router_id, [helper])[0] + created_helper = self._create_helpers( + router_id, + [{'helper': helper, 'protocol': proto, 'port': port}], + )[0] output = self.openstack( 'network l3 conntrack helper show {router_id} {ct_id} ' '-f json'.format( @@ -105,16 +110,16 @@ def test_l3_conntrack_helper_set_and_show(self): ), parse_output=True, ) - self.assertEqual(helper['helper'], output['helper']) - self.assertEqual(helper['protocol'], output['protocol']) - self.assertEqual(helper['port'], output['port']) + self.assertEqual(port, output['port']) + self.assertEqual(helper, output['helper']) + self.assertEqual(proto, output['protocol']) raw_output = self.openstack( 'network l3 conntrack helper set {router_id} {ct_id} ' '--port {port} '.format( router_id=router_id, ct_id=created_helper['id'], - port=helper['port'] + 1, + port=port + 1, ) ) self.assertOutput('', raw_output) @@ -127,6 +132,6 @@ def test_l3_conntrack_helper_set_and_show(self): ), parse_output=True, ) - self.assertEqual(helper['port'] + 1, output['port']) - self.assertEqual(helper['helper'], output['helper']) - self.assertEqual(helper['protocol'], output['protocol']) + self.assertEqual(port + 1, output['port']) + self.assertEqual(helper, output['helper']) + self.assertEqual(proto, output['protocol']) diff --git a/openstackclient/tests/functional/network/v2/test_local_ip.py b/openstackclient/tests/functional/network/v2/test_local_ip.py index a1553bf3c..d092be084 100644 --- a/openstackclient/tests/functional/network/v2/test_local_ip.py +++ b/openstackclient/tests/functional/network/v2/test_local_ip.py @@ -77,6 +77,10 @@ def test_local_ip_list(self): self.assertNotEqual(admin_project_id, demo_project_id) self.assertEqual(admin_project_id, auth_project_id) + # type narrow + assert admin_project_id is not None + assert demo_project_id is not None + name1 = uuid.uuid4().hex cmd_output = self.openstack( 'local ip create ' + name1, diff --git a/openstackclient/tests/functional/network/v2/test_network_meter_rule.py b/openstackclient/tests/functional/network/v2/test_network_meter_rule.py index 8258d5831..c80643e31 100644 --- a/openstackclient/tests/functional/network/v2/test_network_meter_rule.py +++ b/openstackclient/tests/functional/network/v2/test_network_meter_rule.py @@ -22,8 +22,8 @@ class TestMeterRule(common.NetworkTests): """Functional tests for meter rule""" - METER_ID = None - METER_RULE_ID = None + METER_ID: str + METER_RULE_ID: str @classmethod def setUpClass(cls): diff --git a/openstackclient/tests/functional/network/v2/test_network_rbac.py b/openstackclient/tests/functional/network/v2/test_network_rbac.py index 657175a28..d09b6e9b4 100644 --- a/openstackclient/tests/functional/network/v2/test_network_rbac.py +++ b/openstackclient/tests/functional/network/v2/test_network_rbac.py @@ -18,8 +18,8 @@ class NetworkRBACTests(common.NetworkTests): """Functional tests for network rbac""" - OBJECT_ID = None - ID = None + OBJECT_ID: str + ID: str HEADERS = ['ID'] FIELDS = ['id'] diff --git a/openstackclient/tests/functional/network/v2/test_router.py b/openstackclient/tests/functional/network/v2/test_router.py index 90708324b..cc7c8b1b7 100644 --- a/openstackclient/tests/functional/network/v2/test_router.py +++ b/openstackclient/tests/functional/network/v2/test_router.py @@ -106,6 +106,10 @@ def test_router_list(self): self.assertNotEqual(admin_project_id, demo_project_id) self.assertEqual(admin_project_id, auth_project_id) + # type narrow + assert admin_project_id is not None + assert demo_project_id is not None + name1 = uuid.uuid4().hex name2 = uuid.uuid4().hex cmd_output = self.openstack( diff --git a/openstackclient/tests/functional/network/v2/test_subnet_pool.py b/openstackclient/tests/functional/network/v2/test_subnet_pool.py index 89eb6f0ed..97d9f0b9d 100644 --- a/openstackclient/tests/functional/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/functional/network/v2/test_subnet_pool.py @@ -63,6 +63,10 @@ def test_subnet_pool_list(self): self.assertNotEqual(admin_project_id, demo_project_id) self.assertEqual(admin_project_id, auth_project_id) + # type narrow + assert admin_project_id is not None + assert demo_project_id is not None + name1 = uuid.uuid4().hex name2 = uuid.uuid4().hex From 7750fc1cf45a32563225092ec442c96c38ee6769 Mon Sep 17 00:00:00 2001 From: Antonia Gaete Date: Tue, 12 Nov 2024 17:45:51 +0000 Subject: [PATCH 108/245] identity: Migrate 'endpoint' commands to SDK Change-Id: I71b5ae8a4bbcb2fdebf894d8bd5cc8322c31bdb5 Depends-On: I599ff3e88d4e1e9ffafc638bb74186f2739b5a77 Depends-On: I9aa39810fe94f7ee9b68d34050f4adb9dbdfccb8 --- openstackclient/identity/common.py | 31 +- openstackclient/identity/v3/endpoint.py | 175 +++++---- .../tests/unit/identity/v3/test_endpoint.py | 339 +++++++++--------- ...rate-endpoint-to-sdk-8ca5a34794b6bd7e.yaml | 10 + 4 files changed, 288 insertions(+), 267 deletions(-) create mode 100644 releasenotes/notes/migrate-endpoint-to-sdk-8ca5a34794b6bd7e.yaml diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index 9a7f11d1a..65ff8852f 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -88,23 +88,22 @@ def find_service_sdk(identity_client, name_type_or_id): raise exceptions.CommandError(e.message) # search for service type - services = identity_client.services() - result = None - for service in services: - if name_type_or_id == service.type: - if result: - msg = _( - "Multiple service matches found for '%s', " - "use an ID or name to be more specific." - ) - raise exceptions.CommandError(msg % name_type_or_id) - result = service - - if result is None: - msg = _("No service with a type, name or ID of '%s' exists.") - raise exceptions.CommandError(msg % name_type_or_id) + services = identity_client.services(type=name_type_or_id) + try: + service = next(services) + except StopIteration: + msg = _( + "No service with a type, name or ID of '%(query)s' exists." + ) % {"query": name_type_or_id} + raise exceptions.CommandError(msg) + + if next(services, None): + msg = _( + "Multiple service matches found for '%(query)s', use an ID to be more specific." + ) % {"query": name_type_or_id} + raise exceptions.CommandError(msg) - return result + return service def get_resource(manager, name_type_or_id): diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index 4b79fdaa0..b84edb366 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -28,11 +28,31 @@ LOG = logging.getLogger(__name__) -def get_service_name(service): - if hasattr(service, 'name'): - return service.name - else: - return '' +def _format_endpoint(endpoint, service): + columns = ( + 'is_enabled', + 'id', + 'interface', + 'region_id', + 'region_id', + 'service_id', + 'url', + ) + column_headers = ( + 'enabled', + 'id', + 'interface', + 'region', + 'region_id', + 'service_id', + 'url', + 'service_name', + 'service_type', + ) + + data = utils.get_item_properties(endpoint, columns) + data += (getattr(service, 'name', ''), service.type) + return column_headers, data class AddProjectToEndpoint(command.Command): @@ -112,23 +132,23 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - service = common.find_service(identity_client, parsed_args.service) - - endpoint = identity_client.endpoints.create( - service=service.id, - url=parsed_args.url, - interface=parsed_args.interface, - region=parsed_args.region, - enabled=parsed_args.enabled, - ) + identity_client = self.app.client_manager.sdk_connection.identity + service = common.find_service_sdk(identity_client, parsed_args.service) + + kwargs = {} + + kwargs['service_id'] = service.id + kwargs['url'] = parsed_args.url + kwargs['interface'] = parsed_args.interface + kwargs['is_enabled'] = parsed_args.enabled + + if parsed_args.region: + region = identity_client.get_region(parsed_args.region) + kwargs['region_id'] = region.id + + endpoint = identity_client.create_endpoint(**kwargs) - info = {} - endpoint._info.pop('links') - info.update(endpoint._info) - info['service_name'] = get_service_name(service) - info['service_type'] = service.type - return zip(*sorted(info.items())) + return _format_endpoint(endpoint, service=service) class DeleteEndpoint(command.Command): @@ -145,14 +165,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity result = 0 for i in parsed_args.endpoint: try: - endpoint_id = utils.find_resource( - identity_client.endpoints, i - ).id - identity_client.endpoints.delete(endpoint_id) + endpoint_id = identity_client.find_endpoint(i).id + identity_client.delete_endpoint(endpoint_id) except Exception as e: result += 1 LOG.error( @@ -208,28 +226,24 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity endpoint = None if parsed_args.endpoint: - endpoint = utils.find_resource( - identity_client.endpoints, parsed_args.endpoint - ) + endpoint = identity_client.find_endpoint(parsed_args.endpoint) project = None if parsed_args.project: - project = common.find_project( - identity_client, + project = identity_client.find_project( parsed_args.project, parsed_args.project_domain, ) if endpoint: - columns: tuple[str, ...] = ('ID', 'Name') - data = identity_client.endpoint_filter.list_projects_for_endpoint( - endpoint=endpoint.id - ) + column_headers = ('ID', 'Name') + columns: tuple[str, ...] = ('id', 'name') + data = identity_client.endpoint_projects(endpoint=endpoint.id) else: - columns = ( + column_headers = ( 'ID', 'Region', 'Service Name', @@ -238,37 +252,41 @@ def take_action(self, parsed_args): 'Interface', 'URL', ) + columns = ( + 'id', + 'region_id', + 'service_name', + 'service_type', + 'is_enabled', + 'interface', + 'url', + ) kwargs = {} if parsed_args.service: - service = common.find_service( + service = common.find_service_sdk( identity_client, parsed_args.service ) - kwargs['service'] = service.id + kwargs['service_id'] = service.id if parsed_args.interface: kwargs['interface'] = parsed_args.interface if parsed_args.region: - kwargs['region'] = parsed_args.region + region = identity_client.get_region(parsed_args.region) + kwargs['region_id'] = region.id if project: - data = ( - identity_client.endpoint_filter.list_endpoints_for_project( - project=project.id - ) + data = list( + identity_client.project_endpoints(project=project.id) ) else: - data = identity_client.endpoints.list(**kwargs) - - service_list = identity_client.services.list() + data = list(identity_client.endpoints(**kwargs)) for ep in data: - service = common.find_service_in_list( - service_list, ep.service_id - ) - ep.service_name = get_service_name(service) + service = identity_client.find_service(ep.service_id) + ep.service_name = getattr(service, 'name', '') ep.service_type = service.type return ( - columns, + column_headers, ( utils.get_item_properties( s, @@ -363,28 +381,34 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - endpoint = utils.find_resource( - identity_client.endpoints, parsed_args.endpoint - ) + identity_client = self.app.client_manager.sdk_connection.identity + endpoint = identity_client.find_endpoint(parsed_args.endpoint) + + kwargs = {} - service_id = None if parsed_args.service: - service = common.find_service(identity_client, parsed_args.service) - service_id = service.id - enabled = None + service = common.find_service_sdk( + identity_client, parsed_args.service + ) + kwargs['service_id'] = service.id + if parsed_args.enabled: - enabled = True + kwargs['is_enabled'] = True if parsed_args.disabled: - enabled = False + kwargs['is_enabled'] = False + + if parsed_args.url: + kwargs['url'] = parsed_args.url + + if parsed_args.interface: + kwargs['interface'] = parsed_args.interface - identity_client.endpoints.update( + if parsed_args.region: + kwargs['region_id'] = parsed_args.region + + identity_client.update_endpoint( endpoint.id, - service=service_id, - url=parsed_args.url, - interface=parsed_args.interface, - region=parsed_args.region, - enabled=enabled, + **kwargs, ) @@ -404,16 +428,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - endpoint = utils.find_resource( - identity_client.endpoints, parsed_args.endpoint - ) + identity_client = self.app.client_manager.sdk_connection.identity + endpoint = identity_client.find_endpoint(parsed_args.endpoint) - service = common.find_service(identity_client, endpoint.service_id) + service = common.find_service_sdk(identity_client, endpoint.service_id) - info = {} - endpoint._info.pop('links') - info.update(endpoint._info) - info['service_name'] = get_service_name(service) - info['service_type'] = service.type - return zip(*sorted(info.items())) + return _format_endpoint(endpoint, service) diff --git a/openstackclient/tests/unit/identity/v3/test_endpoint.py b/openstackclient/tests/unit/identity/v3/test_endpoint.py index 1dafe48e4..84e10e55a 100644 --- a/openstackclient/tests/unit/identity/v3/test_endpoint.py +++ b/openstackclient/tests/unit/identity/v3/test_endpoint.py @@ -10,6 +10,13 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack.identity.v3 import domain as _domain +from openstack.identity.v3 import endpoint as _endpoint +from openstack.identity.v3 import project as _project +from openstack.identity.v3 import region as _region +from openstack.identity.v3 import service as _service +from openstack.test import fakes as sdk_fakes + from openstackclient.identity.v3 import endpoint from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes @@ -37,30 +44,34 @@ def setUp(self): self.projects_mock.reset_mock() -class TestEndpointCreate(TestEndpoint): - service = identity_fakes.FakeService.create_one_service() - +class TestEndpointCreate(identity_fakes.TestIdentityv3): columns = ( 'enabled', 'id', 'interface', 'region', + 'region_id', 'service_id', + 'url', 'service_name', 'service_type', - 'url', ) def setUp(self): super().setUp() - self.endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': self.service.id} + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=self.service.id, + interface='admin', + region_id=self.region.id, ) - self.endpoints_mock.create.return_value = self.endpoint - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service + self.identity_sdk_client.create_endpoint.return_value = self.endpoint + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region # Get the command object to test self.cmd = endpoint.CreateEndpoint(self.app, None) @@ -79,6 +90,9 @@ def test_endpoint_create_no_options(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + # Fake endpoints come with a region ID by default, so set it to None + setattr(self.endpoint, "region_id", None) + # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. @@ -86,25 +100,25 @@ def test_endpoint_create_no_options(self): # Set expected values kwargs = { - 'service': self.service.id, + 'service_id': self.service.id, 'url': self.endpoint.url, 'interface': self.endpoint.interface, - 'enabled': True, - 'region': None, + 'is_enabled': True, } - self.endpoints_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_endpoint.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( True, self.endpoint.id, self.endpoint.interface, - self.endpoint.region, + None, + None, self.service.id, + self.endpoint.url, self.service.name, self.service.type, - self.endpoint.url, ) self.assertEqual(datalist, data) @@ -114,14 +128,14 @@ def test_endpoint_create_region(self): self.endpoint.interface, self.endpoint.url, '--region', - self.endpoint.region, + self.region.id, ] verifylist = [ ('enabled', True), ('service', self.service.id), ('interface', self.endpoint.interface), ('url', self.endpoint.url), - ('region', self.endpoint.region), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -132,25 +146,26 @@ def test_endpoint_create_region(self): # Set expected values kwargs = { - 'service': self.service.id, + 'service_id': self.service.id, 'url': self.endpoint.url, 'interface': self.endpoint.interface, - 'enabled': True, - 'region': self.endpoint.region, + 'is_enabled': True, + 'region_id': self.region.id, } - self.endpoints_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_endpoint.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( True, self.endpoint.id, self.endpoint.interface, - self.endpoint.region, + self.region.id, + self.region.id, self.service.id, + self.endpoint.url, self.service.name, self.service.type, - self.endpoint.url, ) self.assertEqual(datalist, data) @@ -169,6 +184,9 @@ def test_endpoint_create_enable(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + # Fake endpoints come with a region ID by default, so set it to None + setattr(self.endpoint, "region_id", None) + # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. @@ -176,25 +194,25 @@ def test_endpoint_create_enable(self): # Set expected values kwargs = { - 'service': self.service.id, + 'service_id': self.service.id, 'url': self.endpoint.url, 'interface': self.endpoint.interface, - 'enabled': True, - 'region': None, + 'is_enabled': True, } - self.endpoints_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_endpoint.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( True, self.endpoint.id, self.endpoint.interface, - self.endpoint.region, + None, + None, self.service.id, + self.endpoint.url, self.service.name, self.service.type, - self.endpoint.url, ) self.assertEqual(datalist, data) @@ -213,6 +231,10 @@ def test_endpoint_create_disable(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + # Fake endpoints come with a region ID by default, so set it to None + setattr(self.endpoint, "region_id", None) + setattr(self.endpoint, "is_enabled", False) + # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. @@ -220,38 +242,37 @@ def test_endpoint_create_disable(self): # Set expected values kwargs = { - 'service': self.service.id, + 'service_id': self.service.id, 'url': self.endpoint.url, 'interface': self.endpoint.interface, - 'enabled': False, - 'region': None, + 'is_enabled': False, } - self.endpoints_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_endpoint.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( - True, + False, self.endpoint.id, self.endpoint.interface, - self.endpoint.region, + None, + None, self.service.id, + self.endpoint.url, self.service.name, self.service.type, - self.endpoint.url, ) self.assertEqual(datalist, data) -class TestEndpointDelete(TestEndpoint): - endpoint = identity_fakes.FakeEndpoint.create_one_endpoint() - +class TestEndpointDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - # This is the return value for utils.find_resource(endpoint) - self.endpoints_mock.get.return_value = self.endpoint - self.endpoints_mock.delete.return_value = None + self.endpoint = sdk_fakes.generate_fake_resource(_endpoint.Endpoint) + + self.identity_sdk_client.find_endpoint.return_value = self.endpoint + self.identity_sdk_client.delete_endpoint.return_value = None # Get the command object to test self.cmd = endpoint.DeleteEndpoint(self.app, None) @@ -267,18 +288,13 @@ def test_endpoint_delete(self): result = self.cmd.take_action(parsed_args) - self.endpoints_mock.delete.assert_called_with( + self.identity_sdk_client.delete_endpoint.assert_called_with( self.endpoint.id, ) self.assertIsNone(result) -class TestEndpointList(TestEndpoint): - service = identity_fakes.FakeService.create_one_service() - endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': service.id} - ) - +class TestEndpointList(identity_fakes.TestIdentityv3): columns = ( 'ID', 'Region', @@ -292,11 +308,19 @@ class TestEndpointList(TestEndpoint): def setUp(self): super().setUp() - self.endpoints_mock.list.return_value = [self.endpoint] + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=self.service.id, + interface='admin', + region_id=self.region.id, + ) - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service - self.services_mock.list.return_value = [self.service] + self.identity_sdk_client.endpoints.return_value = [self.endpoint] + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.services.return_value = [self.service] + self.identity_sdk_client.get_region.return_value = self.region # Get the command object to test self.cmd = endpoint.ListEndpoint(self.app, None) @@ -310,13 +334,13 @@ def test_endpoint_list_no_options(self): # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.endpoints_mock.list.assert_called_with() + self.identity_sdk_client.endpoints.assert_called_with() self.assertEqual(self.columns, columns) datalist = ( ( self.endpoint.id, - self.endpoint.region, + self.region.id, self.service.name, self.service.type, True, @@ -343,15 +367,15 @@ def test_endpoint_list_service(self): # Set expected values kwargs = { - 'service': self.service.id, + 'service_id': self.service.id, } - self.endpoints_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.endpoints.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( ( self.endpoint.id, - self.endpoint.region, + self.region.id, self.service.name, self.service.type, True, @@ -380,13 +404,13 @@ def test_endpoint_list_interface(self): kwargs = { 'interface': self.endpoint.interface, } - self.endpoints_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.endpoints.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( ( self.endpoint.id, - self.endpoint.region, + self.region.id, self.service.name, self.service.type, True, @@ -399,10 +423,10 @@ def test_endpoint_list_interface(self): def test_endpoint_list_region(self): arglist = [ '--region', - self.endpoint.region, + self.region.id, ] verifylist = [ - ('region', self.endpoint.region), + ('region', self.region.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -413,15 +437,15 @@ def test_endpoint_list_region(self): # Set expected values kwargs = { - 'region': self.endpoint.region, + 'region_id': self.region.id, } - self.endpoints_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.endpoints.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( ( self.endpoint.id, - self.endpoint.region, + self.region.id, self.service.name, self.service.type, True, @@ -432,13 +456,13 @@ def test_endpoint_list_region(self): self.assertEqual(datalist, tuple(data)) def test_endpoint_list_project_with_project_domain(self): - project = identity_fakes.FakeProject.create_one_project() - domain = identity_fakes.FakeDomain.create_one_domain() + project = sdk_fakes.generate_fake_resource(_project.Project) + domain = sdk_fakes.generate_fake_resource(_domain.Domain) - self.ep_filter_mock.list_endpoints_for_project.return_value = [ + self.identity_sdk_client.project_endpoints.return_value = [ self.endpoint ] - self.projects_mock.get.return_value = project + self.identity_sdk_client.find_project.return_value = project arglist = ['--project', project.name, '--project-domain', domain.name] verifylist = [ @@ -451,7 +475,7 @@ def test_endpoint_list_project_with_project_domain(self): # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.ep_filter_mock.list_endpoints_for_project.assert_called_with( + self.identity_sdk_client.project_endpoints.assert_called_with( project=project.id ) @@ -459,7 +483,7 @@ def test_endpoint_list_project_with_project_domain(self): datalist = ( ( self.endpoint.id, - self.endpoint.region, + self.region.id, self.service.name, self.service.type, True, @@ -470,22 +494,20 @@ def test_endpoint_list_project_with_project_domain(self): self.assertEqual(datalist, tuple(data)) -class TestEndpointSet(TestEndpoint): - service = identity_fakes.FakeService.create_one_service() - endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': service.id} - ) - +class TestEndpointSet(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - # This is the return value for utils.find_resource(endpoint) - self.endpoints_mock.get.return_value = self.endpoint - - self.endpoints_mock.update.return_value = self.endpoint + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=self.service.id, + interface='admin', + ) - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service + self.identity_sdk_client.find_endpoint.return_value = self.endpoint + self.identity_sdk_client.update_endpoint.return_value = self.endpoint + self.identity_sdk_client.find_service.return_value = self.service # Get the command object to test self.cmd = endpoint.SetEndpoint(self.app, None) @@ -501,15 +523,8 @@ def test_endpoint_set_no_options(self): result = self.cmd.take_action(parsed_args) - kwargs = { - 'enabled': None, - 'interface': None, - 'region': None, - 'service': None, - 'url': None, - } - self.endpoints_mock.update.assert_called_with( - self.endpoint.id, **kwargs + self.identity_sdk_client.update_endpoint.assert_called_with( + self.endpoint.id ) self.assertIsNone(result) @@ -525,13 +540,9 @@ def test_endpoint_set_interface(self): # Set expected values kwargs = { - 'enabled': None, 'interface': 'public', - 'url': None, - 'region': None, - 'service': None, } - self.endpoints_mock.update.assert_called_with( + self.identity_sdk_client.update_endpoint.assert_called_with( self.endpoint.id, **kwargs ) self.assertIsNone(result) @@ -548,13 +559,9 @@ def test_endpoint_set_url(self): # Set expected values kwargs = { - 'enabled': None, - 'interface': None, 'url': 'http://localhost:5000', - 'region': None, - 'service': None, } - self.endpoints_mock.update.assert_called_with( + self.identity_sdk_client.update_endpoint.assert_called_with( self.endpoint.id, **kwargs ) self.assertIsNone(result) @@ -571,13 +578,9 @@ def test_endpoint_set_service(self): # Set expected values kwargs = { - 'enabled': None, - 'interface': None, - 'url': None, - 'region': None, - 'service': self.service.id, + 'service_id': self.service.id, } - self.endpoints_mock.update.assert_called_with( + self.identity_sdk_client.update_endpoint.assert_called_with( self.endpoint.id, **kwargs ) self.assertIsNone(result) @@ -594,13 +597,9 @@ def test_endpoint_set_region(self): # Set expected values kwargs = { - 'enabled': None, - 'interface': None, - 'url': None, - 'region': 'e-rzzz', - 'service': None, + 'region_id': 'e-rzzz', } - self.endpoints_mock.update.assert_called_with( + self.identity_sdk_client.update_endpoint.assert_called_with( self.endpoint.id, **kwargs ) self.assertIsNone(result) @@ -617,13 +616,9 @@ def test_endpoint_set_enable(self): # Set expected values kwargs = { - 'enabled': True, - 'interface': None, - 'url': None, - 'region': None, - 'service': None, + 'is_enabled': True, } - self.endpoints_mock.update.assert_called_with( + self.identity_sdk_client.update_endpoint.assert_called_with( self.endpoint.id, **kwargs ) self.assertIsNone(result) @@ -640,31 +635,31 @@ def test_endpoint_set_disable(self): # Set expected values kwargs = { - 'enabled': False, - 'interface': None, - 'url': None, - 'region': None, - 'service': None, + 'is_enabled': False, } - self.endpoints_mock.update.assert_called_with( + self.identity_sdk_client.update_endpoint.assert_called_with( self.endpoint.id, **kwargs ) self.assertIsNone(result) -class TestEndpointShow(TestEndpoint): - service = identity_fakes.FakeService.create_one_service() - endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': service.id} - ) - +class TestEndpointShow(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.endpoints_mock.get.return_value = self.endpoint + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.region = sdk_fakes.generate_fake_resource(_region.Region) + self.endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=self.service.id, + interface='admin', + region_id=self.region.id, + ) - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service + self.identity_sdk_client.find_endpoint.return_value = self.endpoint + + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.get_region.return_value = self.region # Get the command object to test self.cmd = endpoint.ShowEndpoint(self.app, None) @@ -682,7 +677,7 @@ def test_endpoint_show(self): # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.endpoints_mock.get.assert_called_with( + self.identity_sdk_client.find_endpoint.assert_called_with( self.endpoint.id, ) @@ -691,82 +686,82 @@ def test_endpoint_show(self): 'id', 'interface', 'region', + 'region_id', 'service_id', + 'url', 'service_name', 'service_type', - 'url', ) self.assertEqual(collist, columns) datalist = ( True, self.endpoint.id, self.endpoint.interface, - self.endpoint.region, + self.region.id, + self.region.id, self.service.id, + self.endpoint.url, self.service.name, self.service.type, - self.endpoint.url, ) self.assertEqual(datalist, data) class TestEndpointCreateServiceWithoutName(TestEndpointCreate): - service = identity_fakes.FakeService.create_one_service( - attrs={'service_name': ''} + service = sdk_fakes.generate_fake_resource( + resource_type=_service.Service, + name='', + ) + region = sdk_fakes.generate_fake_resource(_region.Region) + endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=service.id, + interface='admin', + region_id=region.id, ) def setUp(self): - super(TestEndpointCreate, self).setUp() - - self.endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': self.service.id} - ) - - self.endpoints_mock.create.return_value = self.endpoint - - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service + super().setUp() # Get the command object to test self.cmd = endpoint.CreateEndpoint(self.app, None) class TestEndpointListServiceWithoutName(TestEndpointList): - service = identity_fakes.FakeService.create_one_service( - attrs={'service_name': ''} + service = sdk_fakes.generate_fake_resource( + resource_type=_service.Service, + name='', ) - endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': service.id} + region = sdk_fakes.generate_fake_resource(_region.Region) + endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=service.id, + interface='admin', + region_id=region.id, ) def setUp(self): - super(TestEndpointList, self).setUp() - - self.endpoints_mock.list.return_value = [self.endpoint] - - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service - self.services_mock.list.return_value = [self.service] + super().setUp() # Get the command object to test self.cmd = endpoint.ListEndpoint(self.app, None) class TestEndpointShowServiceWithoutName(TestEndpointShow): - service = identity_fakes.FakeService.create_one_service( - attrs={'service_name': ''} + service = sdk_fakes.generate_fake_resource( + resource_type=_service.Service, + name='', ) - endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( - attrs={'service_id': service.id} + region = sdk_fakes.generate_fake_resource(_region.Region) + endpoint = sdk_fakes.generate_fake_resource( + resource_type=_endpoint.Endpoint, + service_id=service.id, + interface='admin', + region_id=region.id, ) def setUp(self): - super(TestEndpointShow, self).setUp() - - self.endpoints_mock.get.return_value = self.endpoint - - # This is the return value for common.find_resource(service) - self.services_mock.get.return_value = self.service + super().setUp() # Get the command object to test self.cmd = endpoint.ShowEndpoint(self.app, None) diff --git a/releasenotes/notes/migrate-endpoint-to-sdk-8ca5a34794b6bd7e.yaml b/releasenotes/notes/migrate-endpoint-to-sdk-8ca5a34794b6bd7e.yaml new file mode 100644 index 000000000..ab715d164 --- /dev/null +++ b/releasenotes/notes/migrate-endpoint-to-sdk-8ca5a34794b6bd7e.yaml @@ -0,0 +1,10 @@ +--- +upgrade: + - | + The following commands have been migrated to SDK: + + - ``endpoint create`` + - ``endpoint delete`` + - ``endpoint list`` + - ``endpoint show`` + - ``endpoint set`` From b2eccdcb1ab079e3e43b436907dfce4532b76e07 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 14 Feb 2025 12:31:58 +0000 Subject: [PATCH 109/245] Permit use of tuple API_VERSIONS The values of these dictionaries are not used when SDK is in use, which should soon account for all use cases. Eventually we should probably look for plugins to return a proper class or typeddict but that's a job for another day. This began as a fix for in openstackclient/object/client.py which referenced a non-existent class and quickly snowballed. Change-Id: I7b807ec3a97124b35828ffdecbb36f6fde11e7b5 Signed-off-by: Stephen Finucane --- openstackclient/compute/client.py | 7 ++----- openstackclient/identity/client.py | 3 +-- openstackclient/image/client.py | 7 ++----- openstackclient/network/client.py | 10 +++------- openstackclient/object/client.py | 5 ++--- openstackclient/shell.py | 27 ++++++++++++++++++++++----- 6 files changed, 32 insertions(+), 27 deletions(-) diff --git a/openstackclient/compute/client.py b/openstackclient/compute/client.py index 23979ec7f..74979bef3 100644 --- a/openstackclient/compute/client.py +++ b/openstackclient/compute/client.py @@ -11,7 +11,6 @@ # 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 logging @@ -21,13 +20,11 @@ LOG = logging.getLogger(__name__) +# global variables used when building the shell DEFAULT_API_VERSION = '2.1' API_VERSION_OPTION = 'os_compute_api_version' API_NAME = 'compute' -API_VERSIONS = { - '2': 'openstack.connection.Connection', - '2.1': 'openstack.connection.Connection', -} +API_VERSIONS = ('2', '2.1') def make_client(instance): diff --git a/openstackclient/identity/client.py b/openstackclient/identity/client.py index 013c599da..707112cd0 100644 --- a/openstackclient/identity/client.py +++ b/openstackclient/identity/client.py @@ -11,7 +11,6 @@ # 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 logging @@ -20,9 +19,9 @@ from openstackclient.i18n import _ - LOG = logging.getLogger(__name__) +# global variables used when building the shell DEFAULT_API_VERSION = '3' API_VERSION_OPTION = 'os_identity_api_version' API_NAME = 'identity' diff --git a/openstackclient/image/client.py b/openstackclient/image/client.py index d5ee52b35..69a08d82f 100644 --- a/openstackclient/image/client.py +++ b/openstackclient/image/client.py @@ -11,7 +11,6 @@ # 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 logging @@ -21,13 +20,11 @@ LOG = logging.getLogger(__name__) +# global variables used when building the shell DEFAULT_API_VERSION = '2' API_VERSION_OPTION = 'os_image_api_version' API_NAME = 'image' -API_VERSIONS = { - '1': 'openstack.connection.Connection', - '2': 'openstack.connection.Connection', -} +API_VERSIONS = ('1', '2') def make_client(instance): diff --git a/openstackclient/network/client.py b/openstackclient/network/client.py index 1970ab7a8..faaa54e2b 100644 --- a/openstackclient/network/client.py +++ b/openstackclient/network/client.py @@ -9,7 +9,6 @@ # 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 logging @@ -17,16 +16,13 @@ from openstackclient.i18n import _ - LOG = logging.getLogger(__name__) +# global variables used when building the shell DEFAULT_API_VERSION = '2.0' API_VERSION_OPTION = 'os_network_api_version' -API_NAME = "network" -API_VERSIONS = { - "2.0": "openstack.connection.Connection", - "2": "openstack.connection.Connection", -} +API_NAME = 'network' +API_VERSIONS = ('2.0', '2') def make_client(instance): diff --git a/openstackclient/object/client.py b/openstackclient/object/client.py index 9eb7ad7ba..466f132ec 100644 --- a/openstackclient/object/client.py +++ b/openstackclient/object/client.py @@ -19,12 +19,11 @@ from openstackclient.api import object_store_v1 +# global variables used when building the shell DEFAULT_API_VERSION = '1' API_VERSION_OPTION = 'os_object_api_version' API_NAME = 'object_store' -API_VERSIONS = { - '1': 'openstackclient.object.client.ObjectClientv1', -} +API_VERSIONS = ('1',) def make_client(instance): diff --git a/openstackclient/shell.py b/openstackclient/shell.py index df60d3c07..50a767ec3 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -88,17 +88,34 @@ def _load_plugins(self): # this throws an exception if invalid skip_old_check = mod_check_api_version(version_opt) + # NOTE(stephenfin): API_VERSIONS has traditionally been a + # dictionary but the values are only used internally and are + # ignored for the modules using SDK. So we now support tuples + # instead. mod_versions = getattr(mod, 'API_VERSIONS', None) - if not skip_old_check and mod_versions: + if mod_versions is not None and not isinstance( + mod_versions, (dict, tuple) + ): + raise TypeError( + f'Plugin {mod} has incompatible API_VERSIONS. ' + f'Expected: tuple, dict. Got: {type(mod_versions)}. ' + f'Please report this to your package maintainer.' + ) + + if mod_versions and not skip_old_check: if version_opt not in mod_versions: sorted_versions = sorted( - mod.API_VERSIONS.keys(), + list(mod.API_VERSIONS), key=lambda s: list(map(int, s.split('.'))), ) self.log.warning( - "{} version {} is not in supported versions: {}".format( - api, version_opt, ', '.join(sorted_versions) - ) + "%(name)s API version %(version)s is not in " + "supported versions: %(supported)s", + { + 'name': api, + 'version': version_opt, + 'supported': ', '.join(sorted_versions), + }, ) # Command groups deal only with major versions From 0b9c998d8afffa6ec51d92a14614def5c36c1e26 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 13 Mar 2025 15:10:11 +0000 Subject: [PATCH 110/245] Remove contributor specs There is only one and it was never implemented. Remove the directory. Change-Id: Ibfffe7936556a626b407deca0e8de17b45db5313 Signed-off-by: Stephen Finucane --- doc/source/contributor/index.rst | 1 - .../specs/command-objects/example.rst | 86 ------------------- doc/source/contributor/specs/commands.rst | 44 ---------- .../contributor/specs/network-topology.rst | 44 ---------- 4 files changed, 175 deletions(-) delete mode 100644 doc/source/contributor/specs/command-objects/example.rst delete mode 100644 doc/source/contributor/specs/commands.rst delete mode 100644 doc/source/contributor/specs/network-topology.rst diff --git a/doc/source/contributor/index.rst b/doc/source/contributor/index.rst index 2aa9498f1..445aac92d 100644 --- a/doc/source/contributor/index.rst +++ b/doc/source/contributor/index.rst @@ -11,7 +11,6 @@ command-wrappers command-errors command-logs - specs/commands plugins humaninterfaceguide api/modules diff --git a/doc/source/contributor/specs/command-objects/example.rst b/doc/source/contributor/specs/command-objects/example.rst deleted file mode 100644 index fa559433e..000000000 --- a/doc/source/contributor/specs/command-objects/example.rst +++ /dev/null @@ -1,86 +0,0 @@ -======= -example -======= - -This is a specification for the ``example`` command object. It is not intended -to be a complete template for new commands since other actions, options -and/or arguments may be used. You can include general specification information -before the commands below. This information could include links to related material -or descriptions of similar commands. - -[example API name] [example API version] - -example create --------------- - -Create new example - -.. program:: example create -.. code:: bash - - openstack example create - - -.. describe:: - - New example name - -example delete --------------- - -Delete example(s) - -.. program:: example delete -.. code:: bash - - openstack example delete - [ ...] - -.. describe:: - - Example(s) to delete (name or ID) - -example list ------------- - -List examples - -.. program:: example list -.. code:: bash - - openstack example list - -example set ------------ - -Set example properties - -.. program:: example set -.. code:: bash - - openstack example set - [--name ] - - -.. option:: --name - - New example name - -.. describe:: - - Example to modify (name or ID) - -example show ------------- - -Display example details - -.. program:: example show -.. code:: bash - - openstack example show - - -.. describe:: - - Example to display (name or ID) diff --git a/doc/source/contributor/specs/commands.rst b/doc/source/contributor/specs/commands.rst deleted file mode 100644 index f9d757e78..000000000 --- a/doc/source/contributor/specs/commands.rst +++ /dev/null @@ -1,44 +0,0 @@ -============= -Command Specs -============= - -Specifications for new commands, objects and actions are listed below. -These specifications have not been implemented. See -:ref:`command-list` for implemented commands and -:ref:`command-structure` for implemented objects and actions. - -It is optional to propose a specifications patch for new commands, -objects and actions here before submitting the implementation. Once your -specifications patch merges then you may proceed with the implementation. -Your implementation patches should move applicable portions of the -specifications patch to the official :ref:`command-list` -and :ref:`command-structure` documentation. - -Objects Specs -------------- - -Add specifications for new objects based on the ``example`` object. - -Actions Specs -------------- - -Add specifications for new actions based on the ``example`` action. - -.. toctree:: - :maxdepth: 1 - - network-topology - -Commands Specs --------------- - -Add specifications for new commands based on the commands for the -``example`` object. The ``example`` commands are not intended to -be a complete template for new commands since other actions, options -and/or arguments may be used. - -.. toctree:: - :glob: - :maxdepth: 2 - - command-objects/* diff --git a/doc/source/contributor/specs/network-topology.rst b/doc/source/contributor/specs/network-topology.rst deleted file mode 100644 index 6789ee975..000000000 --- a/doc/source/contributor/specs/network-topology.rst +++ /dev/null @@ -1,44 +0,0 @@ -================ -network topology -================ - -A **network topology** shows a topological graph about -devices which connect to the specific network. Also, it -will return availability information for each individual -device within the network as well. One other thing to note -is that it is the intention for OSC to collect data from -existing REST APIs - -Network v2 - -network topology list ---------------------- - -List network topologies - -.. program:: network topology list -.. code:: bash - - openstack network topology list - [--project ] - -.. option:: --project - - List network topologies for given project - (name or ID) - -network topology show ---------------------- - -Show network topology details - -.. program:: network topology show -.. code:: bash - - openstack network topology show - - -.. _network_topology_show-network: -.. describe:: - - Show network topology for a specific network (name or ID) From 3eb063d4f788bfa205a28469d74b369c89a2de24 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 13 Mar 2025 15:10:34 +0000 Subject: [PATCH 111/245] docs: Migrate remaining block storage commands to autocommand Change-Id: I6924f7053e14e843420deea1a023201fad7d4999 Signed-off-by: Stephen Finucane --- .../cli/command-objects/access-rules.rst | 54 +-- .../consistency-group-snapshot.rst | 97 +---- .../cli/command-objects/consistency-group.rst | 177 +------- .../cli/command-objects/role-assignment.rst | 102 +---- doc/source/cli/command-objects/volume.rst | 400 +----------------- 5 files changed, 50 insertions(+), 780 deletions(-) diff --git a/doc/source/cli/command-objects/access-rules.rst b/doc/source/cli/command-objects/access-rules.rst index bc8458283..6e811fc7d 100644 --- a/doc/source/cli/command-objects/access-rules.rst +++ b/doc/source/cli/command-objects/access-rules.rst @@ -9,53 +9,11 @@ rule comprises of a service type, a request path, and a request method. Access rules may only be created as attributes of application credentials, but they may be viewed and deleted independently. +.. autoprogram-cliff:: openstack.identity.v3 + :command: access rule delete -access rule delete ------------------- +.. autoprogram-cliff:: openstack.identity.v3 + :command: access rule list -Delete access rule(s) - -.. program:: access rule delete -.. code:: bash - - openstack access rule delete [ ...] - -.. describe:: - - Access rule(s) to delete (ID) - -access rule list ----------------- - -List access rules - -.. program:: access rule list -.. code:: bash - - openstack access rule list - [--user ] - [--user-domain ] - -.. option:: --user - - User whose access rules to list (name or ID). If not provided, looks up the - current user's access rules. - -.. option:: --user-domain - - Domain the user belongs to (name or ID). This can be - used in case collisions between user names exist. - -access rule show ---------------------------- - -Display access rule details - -.. program:: access rule show -.. code:: bash - - openstack access rule show - -.. describe:: - - Access rule to display (ID) +.. autoprogram-cliff:: openstack.identity.v3 + :command: access rule show diff --git a/doc/source/cli/command-objects/consistency-group-snapshot.rst b/doc/source/cli/command-objects/consistency-group-snapshot.rst index 29d506566..51241685f 100644 --- a/doc/source/cli/command-objects/consistency-group-snapshot.rst +++ b/doc/source/cli/command-objects/consistency-group-snapshot.rst @@ -2,95 +2,16 @@ consistency group snapshot ========================== -Block Storage v2 +Block Storage v2, v3 -consistency group snapshot create ---------------------------------- +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group snapshot create -Create new consistency group snapshot. +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group snapshot delete -.. program:: consistency group snapshot create -.. code:: bash +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group snapshot list - openstack consistency group snapshot create - [--consistency-group ] - [--description ] - [] - -.. option:: --consistency-group - - Consistency group to snapshot (name or ID) - (default to be the same as ) - -.. option:: --description - - Description of this consistency group snapshot - -.. _consistency_group_snapshot_create-snapshot-name: -.. describe:: - - Name of new consistency group snapshot (default to None) - -consistency group snapshot delete ---------------------------------- - -Delete consistency group snapshot(s) - -.. program:: consistency group snapshot delete -.. code:: bash - - openstack consistency group snapshot delete - [ ...] - -.. _consistency_group_snapshot_delete-consistency-group-snapshot: -.. describe:: - - Consistency group snapshot(s) to delete (name or ID) - -consistency group snapshot list -------------------------------- - -List consistency group snapshots. - -.. program:: consistency group snapshot list -.. code:: bash - - openstack consistency group snapshot list - [--all-projects] - [--long] - [--status ] - [--consistency-group ] - -.. option:: --all-projects - - Show detail for all projects. Admin only. - (defaults to False) - -.. option:: --long - - List additional fields in output - -.. option:: --status - - Filters results by a status - ("available", "error", "creating", "deleting" or "error_deleting") - -.. option:: --consistency-group - - Filters results by a consistency group (name or ID) - -consistency group snapshot show -------------------------------- - -Display consistency group snapshot details. - -.. program:: consistency group snapshot show -.. code:: bash - - openstack consistency group snapshot show - - -.. _consistency_group_snapshot_show-consistency-group-snapshot: -.. describe:: - - Consistency group snapshot to display (name or ID) +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group snapshot show diff --git a/doc/source/cli/command-objects/consistency-group.rst b/doc/source/cli/command-objects/consistency-group.rst index 57082c6df..9ff207ff7 100644 --- a/doc/source/cli/command-objects/consistency-group.rst +++ b/doc/source/cli/command-objects/consistency-group.rst @@ -2,172 +2,25 @@ consistency group ================= -Block Storage v2 +Block Storage v2, v3 -consistency group add volume ----------------------------- +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group add volume -Add volume(s) to consistency group. +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group create -.. program:: consistency group add volume -.. code:: bash +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group delete - openstack consistency group add volume - - [ ...] +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group list -.. _consistency_group_add_volume: -.. describe:: +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group remove volume - Consistency group to contain (name or ID) +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group set -.. describe:: - - Volume(s) to add to (name or ID) - (repeat option to add multiple volumes) - -consistency group create ------------------------- - -Create new consistency group. - -.. program:: consistency group create -.. code:: bash - - openstack consistency group create - --volume-type | --consistency-group-source | --consistency-group-snapshot - [--description ] - [--availability-zone ] - [] - -.. option:: --volume-type - - Volume type of this consistency group (name or ID) - -.. option:: --consistency-group-source - - Existing consistency group (name or ID) - -.. option:: --consistency-group-snapshot - - Existing consistency group snapshot (name or ID) - -.. option:: --description - - Description of this consistency group - -.. option:: --availability-zone - - Availability zone for this consistency group - (not available if creating consistency group from source) - -.. _consistency_group_create-name: -.. describe:: - - Name of new consistency group (default to None) - -consistency group delete ------------------------- - -Delete consistency group(s). - -.. program:: consistency group delete -.. code:: bash - - openstack consistency group delete - [--force] - [ ...] - -.. option:: --force - - Allow delete in state other than error or available - -.. _consistency_group_delete-consistency-group: -.. describe:: - - Consistency group(s) to delete (name or ID) - -consistency group list ----------------------- - -List consistency groups. - -.. program:: consistency group list -.. code:: bash - - openstack consistency group list - [--all-projects] - [--long] - -.. option:: --all-projects - - Show detail for all projects. Admin only. - (defaults to False) - -.. option:: --long - - List additional fields in output - -consistency group remove volume -------------------------------- - -Remove volume(s) from consistency group. - -.. program:: consistency group remove volume -.. code:: bash - - openstack consistency group remove volume - - [ ...] - -.. _consistency_group_remove_volume: -.. describe:: - - Consistency group containing (name or ID) - -.. describe:: - - Volume(s) to remove from (name or ID) - (repeat option to remove multiple volumes) - -consistency group set ---------------------- - -Set consistency group properties. - -.. program:: consistency group set -.. code:: bash - - openstack consistency group set - [--name ] - [--description ] - - -.. option:: --name - - New consistency group name - -.. option:: --description - - New consistency group description - -.. _consistency_group_set-consistency-group: -.. describe:: - - Consistency group to modify (name or ID) - -consistency group show ----------------------- - -Display consistency group details. - -.. program:: consistency group show -.. code:: bash - - openstack consistency group show - - -.. _consistency_group_show-consistency-group: -.. describe:: - - Consistency group to display (name or ID) +.. autoprogram-cliff:: openstack.volume.v3 + :command: consistency group show diff --git a/doc/source/cli/command-objects/role-assignment.rst b/doc/source/cli/command-objects/role-assignment.rst index b29f32c69..aa618d4dd 100644 --- a/doc/source/cli/command-objects/role-assignment.rst +++ b/doc/source/cli/command-objects/role-assignment.rst @@ -4,103 +4,5 @@ role assignment Identity v2, v3 -role assignment list --------------------- - -List role assignments - -.. program:: role assignment list -.. code:: bash - - openstack role assignment list - [--role ] - [--role-domain ] - [--user ] - [--user-domain ] - [--group ] - [--group-domain ] - [--domain ] - [--project ] - [--project-domain ] - [--effective] - [--inherited] - [--names] - -.. option:: --role - - Role to filter (name or ID) - - .. versionadded:: 3 - -.. option:: --role-domain - - Domain the role belongs to (name or ID). - This can be used in case collisions between role names exist. - - .. versionadded:: 3 - -.. option:: --user - - User to filter (name or ID) - -.. option:: --user-domain - - Domain the user belongs to (name or ID). - This can be used in case collisions between user names exist. - - .. versionadded:: 3 - -.. option:: --group - - Group to filter (name or ID) - - .. versionadded:: 3 - -.. option:: --group-domain - - Domain the group belongs to (name or ID). - This can be used in case collisions between group names exist. - - .. versionadded:: 3 - -.. option:: --domain - - Domain to filter (name or ID) - - .. versionadded:: 3 - -.. option:: --project - - Project to filter (name or ID) - -.. option:: --project-domain - - Domain the project belongs to (name or ID). - This can be used in case collisions between project names exist. - - .. versionadded:: 3 - -.. option:: --effective - - Returns only effective role assignments (defaults to False) - - .. versionadded:: 3 - -.. option:: --inherited - - Specifies if the role grant is inheritable to the sub projects - - .. versionadded:: 3 - -.. option:: --names - - Returns role assignments with names instead of IDs - -.. option:: --auth-user - - Returns role assignments for the authenticated user. - -.. option:: --auth-project - - Returns role assignments for the project to which the authenticated user - is scoped. +.. autoprogram-cliff:: openstack.identity.v3 + :command: role assignment list diff --git a/doc/source/cli/command-objects/volume.rst b/doc/source/cli/command-objects/volume.rst index 9b4917726..d7e829133 100644 --- a/doc/source/cli/command-objects/volume.rst +++ b/doc/source/cli/command-objects/volume.rst @@ -4,395 +4,31 @@ volume Block Storage v1, v2 -volume create -------------- +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume create -Create new volume +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume delete -.. program:: volume create -.. code:: bash +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume list - openstack volume create - [--size ] - [--type ] - [--image | --snapshot | --source ] - [--description ] - [--availability-zone ] - [--consistency-group ] - [--property [...] ] - [--hint [...] ] - [--bootable | --non-bootable] - [--read-only | --read-write] - +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume migrate -.. option:: --size +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume set - Volume size in GB - (Required unless --snapshot or --source is specified) +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume show -.. option:: --type - - Set the type of volume - - Select ```` from the available types as shown - by ``volume type list``. - -.. option:: --image - - Use ```` as source of volume (name or ID) - - This is commonly used to create a boot volume for a server. - -.. option:: --snapshot - - Use ```` as source of volume (name or ID) - -.. option:: --source - - Volume to clone (name or ID) - -.. option:: --description - - Volume description - -.. option:: --availability-zone - - Create volume in ```` - -.. option:: --consistency-group - - Consistency group where the new volume belongs to - -.. option:: --property - - Set a property on this volume (repeat option to set multiple properties) - -.. option:: --hint - - Arbitrary scheduler hint key-value pairs to help boot an instance - (repeat option to set multiple hints) - -.. option:: --bootable - - Mark volume as bootable - -.. option:: --non-bootable - - Mark volume as non-bootable (default) - -.. option:: --read-only - - Set volume to read-only access mode - -.. option:: --read-write - - Set volume to read-write access mode (default) - -.. _volume_create-name: -.. describe:: - - Volume name - -volume delete -------------- - -Delete volume(s) - -.. program:: volume delete -.. code:: bash - - openstack volume delete - [--force | --purge] - [ ...] - -.. option:: --force - - Attempt forced removal of volume(s), regardless of state (defaults to False) - -.. option:: --purge - - Remove any snapshots along with volume(s) (defaults to False) - - *Volume version 2 only* - -.. _volume_delete-volume: -.. describe:: - - Volume(s) to delete (name or ID) - -volume list ------------ - -List volumes - -.. program:: volume list -.. code:: bash - - openstack volume list - [--project [--project-domain ]] - [--user [--user-domain ]] - [--name ] - [--status ] - [--all-projects] - [--long] - [--limit ] - [--marker ] - -.. option:: --project - - Filter results by ```` (name or ID) (admin only) - - *Volume version 2 only* - -.. option:: --project-domain - - Domain the project belongs to (name or ID). - - This can be used in case collisions between project names exist. - - *Volume version 2 only* - -.. option:: --user - - Filter results by ```` (name or ID) (admin only) - - *Volume version 2 only* - -.. option:: --user-domain - - Domain the user belongs to (name or ID). - - This can be used in case collisions between user names exist. - - *Volume version 2 only* - -.. option:: --name - - Filter results by volume name - -.. option:: --status - - Filter results by status - -.. option:: --all-projects - - Include all projects (admin only) - -.. option:: --long - - List additional fields in output - -.. option:: --limit - - Maximum number of volumes to display - -.. option:: --marker - - The last volume ID of the previous page - - *Volume version 2 only* - -volume migrate --------------- - -Migrate volume to a new host - -.. program:: volume migrate -.. code:: bash - - openstack volume migrate - --host - [--force-host-copy] - [--lock-volume] - - -.. option:: --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* - -.. _volume_migrate-volume: -.. describe:: - - Volume to migrate (name or ID) - -volume set ----------- - -Set volume properties - -.. program:: volume set -.. code:: bash - - openstack volume set - [--name ] - [--size ] - [--description ] - [--no-property] - [--property [...] ] - [--image-property [...] ] - [--state ] - [--attached | --detached ] - [--type ] - [--retype-policy ] - [--bootable | --non-bootable] - [--read-only | --read-write] - - -.. option:: --name - - New volume name - -.. option:: --size - - Extend volume size in GB - -.. option:: --description - - New volume description - -.. option:: --no-property - - Remove all properties from :ref:`\ ` - (specify both :option:`--no-property` and :option:`--property` to - remove the current properties before setting new properties.) - -.. option:: --property - - Set a property on this volume (repeat option to set multiple properties) - -.. option:: --type - - New volume type (name or ID) - - *Volume version 2 only* - -.. option:: --retype-policy - - Migration policy while re-typing volume - ("never" or "on-demand", default is "never" ) - (available only when :option:`--type` option is specified) - - *Volume version 2 only* - -.. option:: --bootable - - Mark volume as bootable - -.. option:: --non-bootable - - Mark volume as non-bootable - -.. option:: --read-only - - Set volume to read-only access mode - -.. option:: --read-write - - Set volume to read-write access mode - -.. option:: --image-property - - Set an image property on this volume - (repeat option to set multiple image properties) - - Image properties are copied along with the image when creating a volume - using ``--image``. Note that these properties are immutable on the image - itself, this option updates the copy attached to this volume. - - *Volume version 2 only* - -.. option:: --state - - New volume state - ("available", "error", "creating", "deleting", "in-use", - "attaching", "detaching", "error_deleting" or "maintenance") (admin only) - (This option simply changes the state of the volume in the database with - no regard to actual status, exercise caution when using) - - *Volume version 2 only* - -.. option:: --attached - - Set volume attachment status to "attached" (admin only) - (This option simply changes the state of the volume in the database with - no regard to actual status, exercise caution when using) - - *Volume version 2 only* - -.. option:: --deattach - - Set volume attachment status to "detached" (admin only) - (This option simply changes the state of the volume in the database with - no regard to actual status, exercise caution when using) - - *Volume version 2 only* - -.. _volume_set-volume: -.. describe:: - - Volume to modify (name or ID) - -volume show ------------ - -Show volume details - -.. program:: volume show -.. code:: bash - - openstack volume show - - -.. _volume_show-volume: -.. describe:: - - Volume to display (name or ID) - -volume unset ------------- - -Unset volume properties - -.. program:: volume unset -.. code:: bash - - openstack volume unset - [--property ] - [--image-property ] - - -.. option:: --property - - Remove a property from volume (repeat option to remove multiple properties) - -.. option:: --image-property - - Remove an image property from volume - (repeat option to remove multiple image properties) - - *Volume version 2 only* - -.. _volume_unset-volume: -.. describe:: - - Volume to modify (name or ID) +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume unset Block Storage v3 - .. autoprogram-cliff:: openstack.volume.v3 - :command: volume summary +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume summary - .. autoprogram-cliff:: openstack.volume.v3 - :command: volume revert +.. autoprogram-cliff:: openstack.volume.v3 + :command: volume revert From e6be9a3edf0904762cb6816a04b8d3d146a1f639 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 13 Mar 2025 11:39:24 +0000 Subject: [PATCH 112/245] volume: Remove Cinder v1 support The Cinder v1 API was removed in Queens [1]. Its replacement, the v2 API, has existed since Grizzly [2]. More importantly, the v1 commands are implemented using python-cinderclient but support for the v1 API was removed from python-cinderclient in Train [3], meaning none of these have worked since then. Clearly if no one has noticed or cared in the 6 years or so since that happened, it's safe to say we can delete these commands. [1] https://opendev.org/openstack/cinder/commit/3e91de956e1947a7014709010b99df380242ac74 [2] https://opendev.org/openstack/cinder/commit/75ca60f619c953df1d95ff2eab7de78f7d5aebe8 [3] https://opendev.org/openstack/python-cinderclient/commit/2189e5702b7ba91a87e1db21024799e1520d8ad0 Change-Id: Ibe1cd6461d2cb78826467078aa17272f171746aa Signed-off-by: Stephen Finucane --- doc/source/cli/_hidden/image.rst | 2 +- doc/source/cli/command-objects/limits.rst | 2 +- doc/source/cli/command-objects/quota.rst | 2 +- .../cli/command-objects/volume-backup.rst | 2 +- doc/source/cli/command-objects/volume-qos.rst | 2 +- .../cli/command-objects/volume-service.rst | 2 +- .../cli/command-objects/volume-snapshot.rst | 2 +- .../volume-transfer-request.rst | 2 +- .../cli/command-objects/volume-type.rst | 2 +- doc/source/cli/command-objects/volume.rst | 2 +- .../tests/functional/volume/v1/__init__.py | 0 .../tests/functional/volume/v1/common.py | 35 - .../tests/functional/volume/v1/test_qos.py | 100 -- .../functional/volume/v1/test_service.py | 76 - .../functional/volume/v1/test_snapshot.py | 232 --- .../volume/v1/test_transfer_request.py | 111 -- .../tests/functional/volume/v1/test_volume.py | 228 --- .../functional/volume/v1/test_volume_type.py | 213 --- openstackclient/tests/unit/image/v1/fakes.py | 4 +- .../tests/unit/volume/v1/__init__.py | 0 openstackclient/tests/unit/volume/v1/fakes.py | 615 ------- .../tests/unit/volume/v1/test_qos_specs.py | 471 ------ .../tests/unit/volume/v1/test_service.py | 295 ---- .../unit/volume/v1/test_transfer_request.py | 380 ----- .../tests/unit/volume/v1/test_type.py | 633 ------- .../tests/unit/volume/v1/test_volume.py | 1447 ----------------- .../unit/volume/v1/test_volume_backup.py | 435 ----- openstackclient/volume/v1/__init__.py | 0 openstackclient/volume/v1/qos_specs.py | 376 ----- openstackclient/volume/v1/service.py | 136 -- openstackclient/volume/v1/volume.py | 727 --------- openstackclient/volume/v1/volume_backup.py | 301 ---- openstackclient/volume/v1/volume_snapshot.py | 432 ----- .../volume/v1/volume_transfer_request.py | 200 --- openstackclient/volume/v1/volume_type.py | 519 ------ ...e-volume-v1-commands-bfa14e9cae54929f.yaml | 35 + setup.cfg | 47 - 37 files changed, 47 insertions(+), 8021 deletions(-) delete mode 100644 openstackclient/tests/functional/volume/v1/__init__.py delete mode 100644 openstackclient/tests/functional/volume/v1/common.py delete mode 100644 openstackclient/tests/functional/volume/v1/test_qos.py delete mode 100644 openstackclient/tests/functional/volume/v1/test_service.py delete mode 100644 openstackclient/tests/functional/volume/v1/test_snapshot.py delete mode 100644 openstackclient/tests/functional/volume/v1/test_transfer_request.py delete mode 100644 openstackclient/tests/functional/volume/v1/test_volume.py delete mode 100644 openstackclient/tests/functional/volume/v1/test_volume_type.py delete mode 100644 openstackclient/tests/unit/volume/v1/__init__.py delete mode 100644 openstackclient/tests/unit/volume/v1/fakes.py delete mode 100644 openstackclient/tests/unit/volume/v1/test_qos_specs.py delete mode 100644 openstackclient/tests/unit/volume/v1/test_service.py delete mode 100644 openstackclient/tests/unit/volume/v1/test_transfer_request.py delete mode 100644 openstackclient/tests/unit/volume/v1/test_type.py delete mode 100644 openstackclient/tests/unit/volume/v1/test_volume.py delete mode 100644 openstackclient/tests/unit/volume/v1/test_volume_backup.py delete mode 100644 openstackclient/volume/v1/__init__.py delete mode 100644 openstackclient/volume/v1/qos_specs.py delete mode 100644 openstackclient/volume/v1/service.py delete mode 100644 openstackclient/volume/v1/volume.py delete mode 100644 openstackclient/volume/v1/volume_backup.py delete mode 100644 openstackclient/volume/v1/volume_snapshot.py delete mode 100644 openstackclient/volume/v1/volume_transfer_request.py delete mode 100644 openstackclient/volume/v1/volume_type.py create mode 100644 releasenotes/notes/remove-volume-v1-commands-bfa14e9cae54929f.yaml diff --git a/doc/source/cli/_hidden/image.rst b/doc/source/cli/_hidden/image.rst index 85ffde6f3..06919e7ab 100644 --- a/doc/source/cli/_hidden/image.rst +++ b/doc/source/cli/_hidden/image.rst @@ -3,7 +3,7 @@ image ===== .. NOTE(efried): This page is hidden from the main TOC; it's here so links in - the wild redirect somewhere sane, because previously identity v2 and v3 were + the wild redirect somewhere sane, because previously image v2 and v3 were combined in a single page. .. toctree:: diff --git a/doc/source/cli/command-objects/limits.rst b/doc/source/cli/command-objects/limits.rst index 3a0f99b37..11d53802c 100644 --- a/doc/source/cli/command-objects/limits.rst +++ b/doc/source/cli/command-objects/limits.rst @@ -4,7 +4,7 @@ limits The Compute and Block Storage APIs have resource usage limits. -Compute v2, Block Storage v1 +Block Storage v2, v3; Compute v2 .. autoprogram-cliff:: openstack.common diff --git a/doc/source/cli/command-objects/quota.rst b/doc/source/cli/command-objects/quota.rst index cab126524..59a8a9bb4 100644 --- a/doc/source/cli/command-objects/quota.rst +++ b/doc/source/cli/command-objects/quota.rst @@ -5,7 +5,7 @@ quota Resource quotas appear in multiple APIs, OpenStackClient presents them as a single object with multiple properties. -Block Storage v1, v2, Compute v2, Network v2 +Block Storage v1, v3; Compute v2; Network v2 .. autoprogram-cliff:: openstack.common :command: quota * diff --git a/doc/source/cli/command-objects/volume-backup.rst b/doc/source/cli/command-objects/volume-backup.rst index 63e5f3655..7b036ca0c 100644 --- a/doc/source/cli/command-objects/volume-backup.rst +++ b/doc/source/cli/command-objects/volume-backup.rst @@ -2,7 +2,7 @@ volume backup ============= -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume backup * diff --git a/doc/source/cli/command-objects/volume-qos.rst b/doc/source/cli/command-objects/volume-qos.rst index 3475b9386..82c4d540c 100644 --- a/doc/source/cli/command-objects/volume-qos.rst +++ b/doc/source/cli/command-objects/volume-qos.rst @@ -2,7 +2,7 @@ volume qos ========== -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume qos * diff --git a/doc/source/cli/command-objects/volume-service.rst b/doc/source/cli/command-objects/volume-service.rst index 3283b29e2..43d455ff5 100644 --- a/doc/source/cli/command-objects/volume-service.rst +++ b/doc/source/cli/command-objects/volume-service.rst @@ -2,7 +2,7 @@ volume service ============== -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume service * diff --git a/doc/source/cli/command-objects/volume-snapshot.rst b/doc/source/cli/command-objects/volume-snapshot.rst index be3ad303b..e63e436df 100644 --- a/doc/source/cli/command-objects/volume-snapshot.rst +++ b/doc/source/cli/command-objects/volume-snapshot.rst @@ -2,7 +2,7 @@ volume snapshot =============== -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume snapshot * diff --git a/doc/source/cli/command-objects/volume-transfer-request.rst b/doc/source/cli/command-objects/volume-transfer-request.rst index 61e38c1cf..97dac02a0 100644 --- a/doc/source/cli/command-objects/volume-transfer-request.rst +++ b/doc/source/cli/command-objects/volume-transfer-request.rst @@ -2,7 +2,7 @@ volume transfer request ======================= -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume transfer request * diff --git a/doc/source/cli/command-objects/volume-type.rst b/doc/source/cli/command-objects/volume-type.rst index 1a74a8a66..003ee6730 100644 --- a/doc/source/cli/command-objects/volume-type.rst +++ b/doc/source/cli/command-objects/volume-type.rst @@ -2,7 +2,7 @@ volume type =========== -Block Storage v1, v2, v3 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume type * diff --git a/doc/source/cli/command-objects/volume.rst b/doc/source/cli/command-objects/volume.rst index d7e829133..337bb9fa2 100644 --- a/doc/source/cli/command-objects/volume.rst +++ b/doc/source/cli/command-objects/volume.rst @@ -2,7 +2,7 @@ volume ====== -Block Storage v1, v2 +Block Storage v2, v3 .. autoprogram-cliff:: openstack.volume.v3 :command: volume create diff --git a/openstackclient/tests/functional/volume/v1/__init__.py b/openstackclient/tests/functional/volume/v1/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/openstackclient/tests/functional/volume/v1/common.py b/openstackclient/tests/functional/volume/v1/common.py deleted file mode 100644 index 755874785..000000000 --- a/openstackclient/tests/functional/volume/v1/common.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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 fixtures - -from openstackclient.tests.functional.volume import base as volume_base - - -class BaseVolumeTests(volume_base.BaseVolumeTests): - """Base class for Volume functional tests""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.haz_volume_v1 = cls.is_service_enabled('block-storage', '1.0') - - def setUp(self): - super().setUp() - - if not self.haz_volume_v1: - self.skipTest("No Volume v1 service present") - - ver_fixture = fixtures.EnvironmentVariable( - 'OS_VOLUME_API_VERSION', '1' - ) - self.useFixture(ver_fixture) diff --git a/openstackclient/tests/functional/volume/v1/test_qos.py b/openstackclient/tests/functional/volume/v1/test_qos.py deleted file mode 100644 index 5d0aa41f6..000000000 --- a/openstackclient/tests/functional/volume/v1/test_qos.py +++ /dev/null @@ -1,100 +0,0 @@ -# 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 QosTests(common.BaseVolumeTests): - """Functional tests for volume qos.""" - - def test_volume_qos_create_list(self): - """Test create, list, delete multiple""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume qos create ' + name1, - parse_output=True, - ) - self.assertEqual(name1, cmd_output['name']) - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume qos create ' + name2, - parse_output=True, - ) - self.assertEqual(name2, cmd_output['name']) - - # Test list - cmd_output = self.openstack( - 'volume qos list', - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertIn(name2, names) - - # Test delete multiple - del_output = self.openstack('volume qos delete ' + name1 + ' ' + name2) - self.assertOutput('', del_output) - - def test_volume_qos_set_show_unset(self): - """Tests create volume qos, set, unset, show, delete""" - - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume qos create ' - + '--consumer front-end ' - + '--property Alpha=a ' - + name, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume qos delete ' + name) - self.assertEqual(name, cmd_output['name']) - - self.assertEqual("front-end", cmd_output['consumer']) - - # Test volume qos set - raw_output = self.openstack( - 'volume qos set ' - + '--no-property ' - + '--property Beta=b ' - + '--property Charlie=c ' - + name, - ) - self.assertOutput('', raw_output) - - # Test volume qos show - cmd_output = self.openstack( - 'volume qos show ' + name, - parse_output=True, - ) - self.assertEqual(name, cmd_output['name']) - self.assertEqual( - {'Beta': 'b', 'Charlie': 'c'}, - cmd_output['properties'], - ) - - # Test volume qos unset - raw_output = self.openstack( - 'volume qos unset ' + '--property Charlie ' + name, - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume qos show ' + name, - parse_output=True, - ) - self.assertEqual(name, cmd_output['name']) - self.assertEqual({'Beta': 'b'}, cmd_output['properties']) - - # TODO(qiangjiahui): Add tests for associate and disassociate volume type diff --git a/openstackclient/tests/functional/volume/v1/test_service.py b/openstackclient/tests/functional/volume/v1/test_service.py deleted file mode 100644 index 8d058c26f..000000000 --- a/openstackclient/tests/functional/volume/v1/test_service.py +++ /dev/null @@ -1,76 +0,0 @@ -# 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. - -from openstackclient.tests.functional.volume.v1 import common - - -class VolumeServiceTests(common.BaseVolumeTests): - """Functional tests for volume service.""" - - def test_volume_service_list(self): - cmd_output = self.openstack('volume service list', parse_output=True) - - # Get the nonredundant services and hosts - services = list({x['Binary'] for x in cmd_output}) - - # Test volume service list --service - cmd_output = self.openstack( - 'volume service list ' + '--service ' + services[0], - parse_output=True, - ) - for x in cmd_output: - self.assertEqual(services[0], x['Binary']) - - # TODO(zhiyong.dai): test volume service list --host after solving - # https://bugs.launchpad.net/python-openstackclient/+bug/1664451 - - def test_volume_service_set(self): - # Get a service and host - cmd_output = self.openstack( - 'volume service list', - parse_output=True, - ) - service_1 = cmd_output[0]['Binary'] - host_1 = cmd_output[0]['Host'] - - # Test volume service set --enable - raw_output = self.openstack( - 'volume service set --enable ' + host_1 + ' ' + service_1 - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume service list --long', - parse_output=True, - ) - self.assertEqual('enabled', cmd_output[0]['Status']) - self.assertIsNone(cmd_output[0]['Disabled Reason']) - - # Test volume service set --disable and --disable-reason - disable_reason = 'disable_reason' - raw_output = self.openstack( - 'volume service set --disable ' - + '--disable-reason ' - + disable_reason - + ' ' - + host_1 - + ' ' - + service_1 - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume service list --long', - parse_output=True, - ) - self.assertEqual('disabled', cmd_output[0]['Status']) - self.assertEqual(disable_reason, cmd_output[0]['Disabled Reason']) diff --git a/openstackclient/tests/functional/volume/v1/test_snapshot.py b/openstackclient/tests/functional/volume/v1/test_snapshot.py deleted file mode 100644 index 1acd014a6..000000000 --- a/openstackclient/tests/functional/volume/v1/test_snapshot.py +++ /dev/null @@ -1,232 +0,0 @@ -# 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 VolumeSnapshotTests(common.BaseVolumeTests): - """Functional tests for volume snapshot.""" - - VOLLY = uuid.uuid4().hex - - @classmethod - def setUpClass(cls): - super().setUpClass() - # create a volume for all tests to create snapshot - cmd_output = cls.openstack( - 'volume create ' + '--size 1 ' + cls.VOLLY, - parse_output=True, - ) - cls.wait_for_status('volume', cls.VOLLY, 'available') - cls.VOLUME_ID = cmd_output['id'] - - @classmethod - def tearDownClass(cls): - try: - cls.wait_for_status('volume', cls.VOLLY, 'available') - raw_output = cls.openstack('volume delete --force ' + cls.VOLLY) - cls.assertOutput('', raw_output) - finally: - super().tearDownClass() - - def test_volume_snapshot_delete(self): - """Test create, delete multiple""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.assertEqual( - name1, - cmd_output["display_name"], - ) - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.assertEqual( - name2, - cmd_output["display_name"], - ) - - self.wait_for_status('volume snapshot', name1, 'available') - self.wait_for_status('volume snapshot', name2, 'available') - - del_output = self.openstack( - 'volume snapshot delete ' + name1 + ' ' + name2 - ) - self.assertOutput('', del_output) - self.wait_for_delete('volume snapshot', name1) - self.wait_for_delete('volume snapshot', name2) - - def test_volume_snapshot_list(self): - """Test create, list filter""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', name1) - self.addCleanup(self.openstack, 'volume snapshot delete ' + name1) - self.assertEqual( - name1, - cmd_output["display_name"], - ) - self.assertEqual( - self.VOLUME_ID, - cmd_output["volume_id"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status('volume snapshot', name1, 'available') - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', name2) - self.addCleanup(self.openstack, 'volume snapshot delete ' + name2) - self.assertEqual( - name2, - cmd_output["display_name"], - ) - self.assertEqual( - self.VOLUME_ID, - cmd_output["volume_id"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status('volume snapshot', name2, 'available') - - # Test list --long, --status - cmd_output = self.openstack( - 'volume snapshot list ' + '--long ' + '--status error', - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertNotIn(name1, names) - self.assertNotIn(name2, names) - - # Test list --volume - cmd_output = self.openstack( - 'volume snapshot list ' + '--volume ' + self.VOLLY, - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertIn(name2, names) - - # Test list --name - cmd_output = self.openstack( - 'volume snapshot list ' + '--name ' + name1, - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertNotIn(name2, names) - - def test_snapshot_set(self): - """Test create, set, unset, show, delete volume snapshot""" - name = uuid.uuid4().hex - new_name = name + "_" - cmd_output = self.openstack( - 'volume snapshot create ' - + '--volume ' - + self.VOLLY - + ' --description aaaa ' - + name, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', new_name) - self.addCleanup(self.openstack, 'volume snapshot delete ' + new_name) - self.assertEqual( - name, - cmd_output["display_name"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.assertEqual( - 'aaaa', - cmd_output["display_description"], - ) - self.wait_for_status('volume snapshot', name, 'available') - - # Test volume snapshot set - raw_output = self.openstack( - 'volume snapshot set ' - + '--name ' - + new_name - + ' --description bbbb ' - + '--property Alpha=a ' - + '--property Beta=b ' - + name, - ) - self.assertOutput('', raw_output) - - # Show snapshot set result - cmd_output = self.openstack( - 'volume snapshot show ' + new_name, - parse_output=True, - ) - self.assertEqual( - new_name, - cmd_output["display_name"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.assertEqual( - 'bbbb', - cmd_output["display_description"], - ) - self.assertEqual( - {'Alpha': 'a', 'Beta': 'b'}, - cmd_output["properties"], - ) - - # Test volume unset - raw_output = self.openstack( - 'volume snapshot unset ' + '--property Alpha ' + new_name, - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume snapshot show ' + new_name, - parse_output=True, - ) - self.assertEqual( - {'Beta': 'b'}, - cmd_output["properties"], - ) - - # Test volume snapshot set --no-property - raw_output = self.openstack( - 'volume snapshot set ' + '--no-property ' + new_name, - ) - self.assertOutput('', raw_output) - cmd_output = self.openstack( - 'volume snapshot show ' + new_name, - parse_output=True, - ) - self.assertEqual({}, cmd_output["properties"]) diff --git a/openstackclient/tests/functional/volume/v1/test_transfer_request.py b/openstackclient/tests/functional/volume/v1/test_transfer_request.py deleted file mode 100644 index 68fe6666f..000000000 --- a/openstackclient/tests/functional/volume/v1/test_transfer_request.py +++ /dev/null @@ -1,111 +0,0 @@ -# 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 - - @classmethod - def setUpClass(cls): - super().setUpClass() - cmd_output = cls.openstack( - 'volume create --size 1 ' + cls.VOLUME_NAME, - parse_output=True, - ) - cls.assertOutput(cls.VOLUME_NAME, cmd_output['name']) - - cls.wait_for_status("volume", cls.VOLUME_NAME, "available") - - @classmethod - def tearDownClass(cls): - try: - raw_output_volume = cls.openstack( - 'volume delete ' + cls.VOLUME_NAME - ) - cls.assertOutput('', raw_output_volume) - finally: - super().tearDownClass() - - def test_volume_transfer_request_accept(self): - volume_name = uuid.uuid4().hex - name = uuid.uuid4().hex - - # create a volume - cmd_output = self.openstack( - 'volume create --size 1 ' + volume_name, - parse_output=True, - ) - self.assertEqual(volume_name, cmd_output['name']) - - # create volume transfer request for the volume - # and get the auth_key of the new transfer request - cmd_output = self.openstack( - 'volume transfer request create ' - + volume_name - + ' --name ' - + name, - parse_output=True, - ) - auth_key = cmd_output['auth_key'] - self.assertTrue(auth_key) - - # accept the volume transfer request - output = self.openstack( - 'volume transfer request accept ' - + name - + ' ' - + '--auth-key ' - + auth_key, - parse_output=True, - ) - self.assertEqual(name, output.get('name')) - - # the volume transfer will be removed by default after accepted - # so just need to delete the volume here - raw_output = self.openstack('volume delete ' + volume_name) - self.assertEqual('', raw_output) - - def test_volume_transfer_request_list_show(self): - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume transfer request create ' - + ' --name ' - + name - + ' ' - + self.VOLUME_NAME, - parse_output=True, - ) - self.addCleanup( - self.openstack, 'volume transfer request delete ' + name - ) - self.assertOutput(name, cmd_output['name']) - auth_key = cmd_output['auth_key'] - self.assertTrue(auth_key) - - cmd_output = self.openstack( - 'volume transfer request list', - parse_output=True, - ) - self.assertIn(name, [req['Name'] for req in cmd_output]) - - cmd_output = self.openstack( - 'volume transfer request show ' + name, - parse_output=True, - ) - self.assertEqual(name, cmd_output['name']) diff --git a/openstackclient/tests/functional/volume/v1/test_volume.py b/openstackclient/tests/functional/volume/v1/test_volume.py deleted file mode 100644 index 7c6cc0356..000000000 --- a/openstackclient/tests/functional/volume/v1/test_volume.py +++ /dev/null @@ -1,228 +0,0 @@ -# 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 VolumeTests(common.BaseVolumeTests): - """Functional tests for volume.""" - - def test_volume_create_and_delete(self): - """Test create, delete multiple""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' + '--size 1 ' + name1, - parse_output=True, - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' + '--size 2 ' + name2, - parse_output=True, - ) - self.assertEqual( - 2, - cmd_output["size"], - ) - - self.wait_for_status("volume", name1, "available") - self.wait_for_status("volume", name2, "available") - del_output = self.openstack('volume delete ' + name1 + ' ' + name2) - self.assertOutput('', del_output) - - def test_volume_list(self): - """Test create, list filter""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' + '--size 1 ' + name1, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume delete ' + name1) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status("volume", name1, "available") - - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' + '--size 2 ' + name2, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume delete ' + name2) - self.assertEqual( - 2, - cmd_output["size"], - ) - self.wait_for_status("volume", name2, "available") - - # Test list - cmd_output = self.openstack( - 'volume list ', - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertIn(name2, names) - - # Test list --long - cmd_output = self.openstack( - 'volume list --long', - parse_output=True, - ) - bootable = [x["Bootable"] for x in cmd_output] - self.assertIn('false', bootable) - - # Test list --name - cmd_output = self.openstack( - 'volume list ' + '--name ' + name1, - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertNotIn(name2, names) - - def test_volume_set_and_unset(self): - """Tests create volume, set, unset, show, delete""" - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume create ' - + '--size 1 ' - + '--description aaaa ' - + '--property Alpha=a ' - + name, - parse_output=True, - ) - self.assertEqual( - name, - cmd_output["name"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.assertEqual( - 'aaaa', - cmd_output["display_description"], - ) - self.assertEqual( - {'Alpha': 'a'}, - cmd_output["properties"], - ) - self.assertEqual( - 'false', - cmd_output["bootable"], - ) - self.wait_for_status("volume", name, "available") - - # Test volume set - new_name = uuid.uuid4().hex - self.addCleanup(self.openstack, 'volume delete ' + new_name) - raw_output = self.openstack( - 'volume set ' - + '--name ' - + new_name - + ' --size 2 ' - + '--description bbbb ' - + '--no-property ' - + '--property Beta=b ' - + '--property Gamma=c ' - + '--bootable ' - + name, - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume show ' + new_name, - parse_output=True, - ) - self.assertEqual( - new_name, - cmd_output["name"], - ) - self.assertEqual( - 2, - cmd_output["size"], - ) - self.assertEqual( - 'bbbb', - cmd_output["display_description"], - ) - self.assertEqual( - {'Beta': 'b', 'Gamma': 'c'}, - cmd_output["properties"], - ) - self.assertEqual( - 'true', - cmd_output["bootable"], - ) - - # Test volume unset - raw_output = self.openstack( - 'volume unset ' + '--property Beta ' + new_name, - ) - self.assertOutput('', raw_output) - - cmd_output = self.openstack( - 'volume show ' + new_name, - parse_output=True, - ) - self.assertEqual( - {'Gamma': 'c'}, - cmd_output["properties"], - ) - - def test_volume_create_and_list_and_show_backward_compatibility(self): - """Test backward compatibility of create, list, show""" - name1 = uuid.uuid4().hex - output = self.openstack( - 'volume create ' + '-c display_name -c id ' + '--size 1 ' + name1, - parse_output=True, - ) - self.assertIn('display_name', output) - self.assertEqual(name1, output['display_name']) - self.assertIn('id', output) - volume_id = output['id'] - self.assertIsNotNone(volume_id) - self.assertNotIn('name', output) - self.addCleanup(self.openstack, 'volume delete ' + volume_id) - - self.wait_for_status("volume", name1, "available") - - output = self.openstack( - 'volume list ' + '-c "Display Name"', - parse_output=True, - ) - for each_volume in output: - self.assertIn('Display Name', each_volume) - - output = self.openstack( - 'volume list ' + '-c "Name"', - parse_output=True, - ) - for each_volume in output: - self.assertIn('Name', each_volume) - - output = self.openstack( - 'volume show ' + '-c display_name -c id ' + name1, - parse_output=True, - ) - self.assertIn('display_name', output) - self.assertEqual(name1, output['display_name']) - self.assertIn('id', output) - self.assertNotIn('name', output) diff --git a/openstackclient/tests/functional/volume/v1/test_volume_type.py b/openstackclient/tests/functional/volume/v1/test_volume_type.py deleted file mode 100644 index b48dec642..000000000 --- a/openstackclient/tests/functional/volume/v1/test_volume_type.py +++ /dev/null @@ -1,213 +0,0 @@ -# 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 time -import uuid - -from openstackclient.tests.functional.volume.v1 import common - - -class VolumeTypeTests(common.BaseVolumeTests): - """Functional tests for volume type.""" - - def test_volume_type_create_list(self): - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume type create --private ' + name, - parse_output=True, - ) - self.addCleanup( - self.openstack, - 'volume type delete ' + name, - ) - self.assertEqual(name, cmd_output['name']) - - cmd_output = self.openstack( - f'volume type show {name}', - parse_output=True, - ) - self.assertEqual(self.NAME, cmd_output['name']) - - cmd_output = self.openstack('volume type list', parse_output=True) - self.assertIn(self.NAME, [t['Name'] for t in cmd_output]) - - cmd_output = self.openstack( - 'volume type list --default', - parse_output=True, - ) - self.assertEqual(1, len(cmd_output)) - self.assertEqual('lvmdriver-1', cmd_output[0]['Name']) - - def test_volume_type_set_unset_properties(self): - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume type create --private ' + name, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume type delete ' + name) - self.assertEqual(name, cmd_output['name']) - - raw_output = self.openstack( - f'volume type set --property a=b --property c=d {name}' - ) - self.assertEqual("", raw_output) - cmd_output = self.openstack( - f'volume type show {name}', - parse_output=True, - ) - self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) - - raw_output = self.openstack(f'volume type unset --property a {name}') - self.assertEqual("", raw_output) - cmd_output = self.openstack( - f'volume type show {name}', - parse_output=True, - ) - self.assertEqual({'c': 'd'}, cmd_output['properties']) - - def test_volume_type_set_unset_multiple_properties(self): - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume type create --private ' + name, - parse_output=True, - ) - self.addCleanup(self.openstack, 'volume type delete ' + name) - self.assertEqual(name, cmd_output['name']) - - raw_output = self.openstack( - f'volume type set --property a=b --property c=d {name}' - ) - self.assertEqual("", raw_output) - cmd_output = self.openstack( - f'volume type show {name}', - parse_output=True, - ) - self.assertEqual({'a': 'b', 'c': 'd'}, cmd_output['properties']) - - raw_output = self.openstack( - f'volume type unset --property a --property c {name}' - ) - self.assertEqual("", raw_output) - cmd_output = self.openstack( - f'volume type show {name}', - parse_output=True, - ) - self.assertEqual({}, cmd_output['properties']) - - def test_multi_delete(self): - vol_type1 = uuid.uuid4().hex - vol_type2 = uuid.uuid4().hex - self.openstack(f'volume type create {vol_type1}') - time.sleep(5) - self.openstack(f'volume type create {vol_type2}') - time.sleep(5) - cmd = f'volume type delete {vol_type1} {vol_type2}' - raw_output = self.openstack(cmd) - self.assertOutput('', raw_output) - - # NOTE: Add some basic functional tests with the old format to - # make sure the command works properly, need to change - # these to new test format when beef up all tests for - # volume type commands. - def test_encryption_type(self): - encryption_type = uuid.uuid4().hex - # test create new encryption type - cmd_output = self.openstack( - 'volume type create ' - '--encryption-provider LuksEncryptor ' - '--encryption-cipher aes-xts-plain64 ' - '--encryption-key-size 128 ' - '--encryption-control-location front-end ' + encryption_type - ) - expected = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - for attr, value in expected.items(): - self.assertEqual(value, cmd_output['encryption'][attr]) - # test show encryption type - cmd_output = self.openstack( - 'volume type show --encryption-type ' + encryption_type, - parse_output=True, - ) - expected = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - for attr, value in expected.items(): - self.assertEqual(value, cmd_output['encryption'][attr]) - # test list encryption type - cmd_output = self.openstack( - 'volume type list --encryption-type', - parse_output=True, - ) - encryption_output = [ - t['Encryption'] for t in cmd_output if t['Name'] == encryption_type - ][0] - expected = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - for attr, value in expected.items(): - self.assertEqual(value, encryption_output[attr]) - # test set new encryption type - raw_output = self.openstack( - 'volume type set ' - '--encryption-provider LuksEncryptor ' - '--encryption-cipher aes-xts-plain64 ' - '--encryption-key-size 128 ' - '--encryption-control-location front-end ' + self.NAME - ) - self.assertEqual('', raw_output) - - name = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume type create --private ' + name, - parse_output=True, - ) - self.addCleanup( - self.openstack, - 'volume type delete ' + name, - ) - self.assertEqual(name, cmd_output['name']) - - cmd_output = self.openstack( - 'volume type show --encryption-type ' + name, - parse_output=True, - ) - expected = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - for attr, value in expected.items(): - self.assertEqual(value, cmd_output['encryption'][attr]) - # test unset encryption type - raw_output = self.openstack( - 'volume type unset --encryption-type ' + name - ) - self.assertEqual('', raw_output) - cmd_output = self.openstack( - 'volume type show --encryption-type ' + name, - parse_output=True, - ) - self.assertEqual({}, cmd_output['encryption']) - # test delete encryption type - raw_output = self.openstack('volume type delete ' + encryption_type) - self.assertEqual('', raw_output) diff --git a/openstackclient/tests/unit/image/v1/fakes.py b/openstackclient/tests/unit/image/v1/fakes.py index 44845fb82..30503e1f0 100644 --- a/openstackclient/tests/unit/image/v1/fakes.py +++ b/openstackclient/tests/unit/image/v1/fakes.py @@ -20,7 +20,7 @@ from openstackclient.tests.unit import fakes from openstackclient.tests.unit import utils -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes class FakeClientMixin: @@ -35,7 +35,7 @@ class TestImagev1(FakeClientMixin, utils.TestCommand): def setUp(self): super().setUp() - self.app.client_manager.volume = volume_fakes.FakeVolumev1Client( + self.app.client_manager.volume = volume_fakes.FakeVolumeClient( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN, ) diff --git a/openstackclient/tests/unit/volume/v1/__init__.py b/openstackclient/tests/unit/volume/v1/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/openstackclient/tests/unit/volume/v1/fakes.py b/openstackclient/tests/unit/volume/v1/fakes.py deleted file mode 100644 index 9b4dc126d..000000000 --- a/openstackclient/tests/unit/volume/v1/fakes.py +++ /dev/null @@ -1,615 +0,0 @@ -# Copyright 2013 Nebula Inc. -# -# 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 copy -import random -from unittest import mock -import uuid - -from openstack.image.v1 import _proxy as image_v1_proxy - -from openstackclient.tests.unit import fakes -from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes -from openstackclient.tests.unit import utils - - -class FakeVolumev1Client: - def __init__(self, **kwargs): - self.volumes = mock.Mock() - self.volumes.resource_class = fakes.FakeResource(None, {}) - self.services = mock.Mock() - self.services.resource_class = fakes.FakeResource(None, {}) - self.extensions = mock.Mock() - self.extensions.resource_class = fakes.FakeResource(None, {}) - self.qos_specs = mock.Mock() - self.qos_specs.resource_class = fakes.FakeResource(None, {}) - self.volume_types = mock.Mock() - self.volume_types.resource_class = fakes.FakeResource(None, {}) - self.volume_encryption_types = mock.Mock() - self.volume_encryption_types.resource_class = fakes.FakeResource( - None, {} - ) - self.transfers = mock.Mock() - self.transfers.resource_class = fakes.FakeResource(None, {}) - self.volume_snapshots = mock.Mock() - self.volume_snapshots.resource_class = fakes.FakeResource(None, {}) - self.backups = mock.Mock() - self.backups.resource_class = fakes.FakeResource(None, {}) - self.restores = mock.Mock() - self.restores.resource_class = fakes.FakeResource(None, {}) - self.auth_token = kwargs['token'] - self.management_url = kwargs['endpoint'] - - -class FakeClientMixin: - def setUp(self): - super().setUp() - - self.app.client_manager.volume = FakeVolumev1Client( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, - ) - self.volume_client = self.app.client_manager.volume - - -class TestVolumev1( - identity_fakes.FakeClientMixin, - FakeClientMixin, - utils.TestCommand, -): - def setUp(self): - super().setUp() - - # avoid circular imports by defining this manually rather than using - # openstackclient.tests.unit.image.v1.fakes.FakeClientMixin - self.app.client_manager.image = mock.Mock(spec=image_v1_proxy.Proxy) - self.image_client = self.app.client_manager.image - - -def create_one_transfer(attrs=None): - """Create a fake transfer. - - :param Dictionary attrs: - A dictionary with all attributes of Transfer Request - :return: - A FakeResource object with volume_id, name, id. - """ - # Set default attribute - transfer_info = { - 'volume_id': 'volume-id-' + uuid.uuid4().hex, - 'name': 'fake_transfer_name', - 'id': 'id-' + uuid.uuid4().hex, - 'links': 'links-' + uuid.uuid4().hex, - } - - # Overwrite default attributes if there are some attributes set - attrs = attrs or {} - - transfer_info.update(attrs) - - transfer = fakes.FakeResource(None, transfer_info, loaded=True) - - return transfer - - -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(create_one_transfer(attrs)) - - return transfers - - -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 = create_transfers(count) - - return mock.Mock(side_effect=transfers) - - -def create_one_service(attrs=None): - """Create a fake service. - - :param Dictionary attrs: - A dictionary with all attributes of service - :return: - A FakeResource object with host, status, etc. - """ - # Set default attribute - service_info = { - 'host': 'host_test', - 'binary': 'cinder_test', - 'status': 'enabled', - 'disabled_reason': 'LongHoliday-GoldenWeek', - 'zone': 'fake_zone', - 'updated_at': 'fake_date', - 'state': 'fake_state', - } - - # Overwrite default attributes if there are some attributes set - attrs = attrs or {} - - service_info.update(attrs) - - service = fakes.FakeResource(None, service_info, loaded=True) - - return service - - -def create_services(attrs=None, count=2): - """Create multiple fake services. - - :param Dictionary attrs: - A dictionary with all attributes of service - :param Integer count: - The number of services to be faked - :return: - A list of FakeResource objects - """ - services = [] - for n in range(0, count): - services.append(create_one_service(attrs)) - - return services - - -def get_services(services=None, count=2): - """Get an iterable MagicMock object with a list of faked services. - - If services list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List services: - A list of FakeResource objects faking services - :param Integer count: - The number of services to be faked - :return - An iterable Mock object with side_effect set to a list of faked - services - """ - if services is None: - services = create_services(count) - - return mock.Mock(side_effect=services) - - -def create_one_qos(attrs=None): - """Create a fake Qos specification. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with id, name, consumer, etc. - """ - attrs = attrs or {} - - # Set default attributes. - qos_info = { - "id": 'qos-id-' + uuid.uuid4().hex, - "name": 'qos-name-' + uuid.uuid4().hex, - "consumer": 'front-end', - "specs": {"foo": "bar", "iops": "9001"}, - } - - # Overwrite default attributes. - qos_info.update(attrs) - - qos = fakes.FakeResource(info=copy.deepcopy(qos_info), loaded=True) - return qos - - -def create_one_qos_association(attrs=None): - """Create a fake Qos specification association. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with id, name, association_type, etc. - """ - attrs = attrs or {} - - # Set default attributes. - qos_association_info = { - "id": 'type-id-' + uuid.uuid4().hex, - "name": 'type-name-' + uuid.uuid4().hex, - "association_type": 'volume_type', - } - - # Overwrite default attributes. - qos_association_info.update(attrs) - - qos_association = fakes.FakeResource( - info=copy.deepcopy(qos_association_info), loaded=True - ) - return qos_association - - -def create_qoses(attrs=None, count=2): - """Create multiple fake Qos specifications. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of Qos specifications to fake - :return: - A list of FakeResource objects faking the Qos specifications - """ - qoses = [] - for i in range(0, count): - qos = create_one_qos(attrs) - qoses.append(qos) - - return qoses - - -def get_qoses(qoses=None, count=2): - """Get an iterable MagicMock object with a list of faked qoses. - - If qoses list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volumes: - A list of FakeResource objects faking qoses - :param Integer count: - The number of qoses to be faked - :return - An iterable Mock object with side_effect set to a list of faked - qoses - """ - if qoses is None: - qoses = create_qoses(count) - - return mock.Mock(side_effect=qoses) - - -def create_one_volume(attrs=None): - """Create a fake volume. - - :param Dictionary attrs: - A dictionary with all attributes of volume - :return: - A FakeResource object with id, name, status, etc. - """ - attrs = attrs or {} - - # Set default attribute - volume_info = { - 'id': 'volume-id' + uuid.uuid4().hex, - 'display_name': 'volume-name' + uuid.uuid4().hex, - 'display_description': 'description' + uuid.uuid4().hex, - 'status': 'available', - 'size': 10, - 'volume_type': random.choice(['fake_lvmdriver-1', 'fake_lvmdriver-2']), - 'bootable': 'true', - 'metadata': { - 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex, - 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex, - 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex, - }, - 'snapshot_id': 'snapshot-id-' + uuid.uuid4().hex, - 'availability_zone': 'zone' + uuid.uuid4().hex, - 'attachments': [ - { - 'device': '/dev/' + uuid.uuid4().hex, - 'server_id': uuid.uuid4().hex, - }, - ], - 'created_at': 'time-' + uuid.uuid4().hex, - } - - # Overwrite default attributes if there are some attributes set - volume_info.update(attrs) - - volume = fakes.FakeResource(None, volume_info, loaded=True) - return volume - - -def create_volumes(attrs=None, count=2): - """Create multiple fake volumes. - - :param Dictionary attrs: - A dictionary with all attributes of volume - :param Integer count: - The number of volumes to be faked - :return: - A list of FakeResource objects - """ - volumes = [] - for n in range(0, count): - volumes.append(create_one_volume(attrs)) - - return volumes - - -def get_volumes(volumes=None, count=2): - """Get an iterable MagicMock object with a list of faked volumes. - - If volumes list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volumes: - A list of FakeResource objects faking volumes - :param Integer count: - The number of volumes to be faked - :return - An iterable Mock object with side_effect set to a list of faked - volumes - """ - if volumes is None: - volumes = create_volumes(count) - - return mock.Mock(side_effect=volumes) - - -def create_one_volume_type(attrs=None, methods=None): - """Create a fake volume type. - - :param Dictionary attrs: - A dictionary with all attributes - :param Dictionary methods: - A dictionary with all methods - :return: - A FakeResource object with id, name, description, etc. - """ - attrs = attrs or {} - methods = methods or {} - - # Set default attributes. - volume_type_info = { - "id": 'type-id-' + uuid.uuid4().hex, - "name": 'type-name-' + uuid.uuid4().hex, - "description": 'type-description-' + uuid.uuid4().hex, - "extra_specs": {"foo": "bar"}, - "is_public": True, - } - - # Overwrite default attributes. - volume_type_info.update(attrs) - - volume_type = fakes.FakeResource( - info=copy.deepcopy(volume_type_info), methods=methods, loaded=True - ) - return volume_type - - -def create_volume_types(attrs=None, count=2): - """Create multiple fake types. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of types to fake - :return: - A list of FakeResource objects faking the types - """ - volume_types = [] - for i in range(0, count): - volume_type = create_one_volume_type(attrs) - volume_types.append(volume_type) - - return volume_types - - -def get_volume_types(volume_types=None, count=2): - """Get an iterable MagicMock object with a list of faked types. - - If types list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volume_types: - A list of FakeResource objects faking types - :param Integer count: - The number of types to be faked - :return - An iterable Mock object with side_effect set to a list of faked - types - """ - if volume_types is None: - volume_types = create_volume_types(count) - - return mock.Mock(side_effect=volume_types) - - -def create_one_encryption_volume_type(attrs=None): - """Create a fake encryption volume type. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with volume_type_id etc. - """ - attrs = attrs or {} - - # Set default attributes. - encryption_info = { - "volume_type_id": 'type-id-' + uuid.uuid4().hex, - 'provider': 'LuksEncryptor', - 'cipher': None, - 'key_size': None, - 'control_location': 'front-end', - } - - # Overwrite default attributes. - encryption_info.update(attrs) - - encryption_type = fakes.FakeResource( - info=copy.deepcopy(encryption_info), loaded=True - ) - return encryption_type - - -def create_one_snapshot(attrs=None): - """Create a fake snapshot. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with id, name, description, etc. - """ - attrs = attrs or {} - - # Set default attributes. - snapshot_info = { - "id": 'snapshot-id-' + uuid.uuid4().hex, - "display_name": 'snapshot-name-' + uuid.uuid4().hex, - "display_description": 'snapshot-description-' + uuid.uuid4().hex, - "size": 10, - "status": "available", - "metadata": {"foo": "bar"}, - "created_at": "2015-06-03T18:49:19.000000", - "volume_id": 'vloume-id-' + uuid.uuid4().hex, - } - - # Overwrite default attributes. - snapshot_info.update(attrs) - - snapshot_method = {'update': None} - - snapshot = fakes.FakeResource( - info=copy.deepcopy(snapshot_info), - methods=copy.deepcopy(snapshot_method), - loaded=True, - ) - return snapshot - - -def create_snapshots(attrs=None, count=2): - """Create multiple fake snapshots. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of snapshots to fake - :return: - A list of FakeResource objects faking the snapshots - """ - snapshots = [] - for i in range(0, count): - snapshot = create_one_snapshot(attrs) - snapshots.append(snapshot) - - return snapshots - - -def get_snapshots(snapshots=None, count=2): - """Get an iterable MagicMock object with a list of faked snapshots. - - If snapshots list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volumes: - A list of FakeResource objects faking snapshots - :param Integer count: - The number of snapshots to be faked - :return - An iterable Mock object with side_effect set to a list of faked - snapshots - """ - if snapshots is None: - snapshots = create_snapshots(count) - - return mock.Mock(side_effect=snapshots) - - -def create_one_backup(attrs=None): - """Create a fake backup. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with id, name, volume_id, etc. - """ - attrs = attrs or {} - - # Set default attributes. - backup_info = { - "id": 'backup-id-' + uuid.uuid4().hex, - "name": 'backup-name-' + uuid.uuid4().hex, - "volume_id": 'volume-id-' + uuid.uuid4().hex, - "snapshot_id": 'snapshot-id' + uuid.uuid4().hex, - "description": 'description-' + uuid.uuid4().hex, - "object_count": None, - "container": 'container-' + uuid.uuid4().hex, - "size": random.randint(1, 20), - "status": "error", - "availability_zone": 'zone' + uuid.uuid4().hex, - "links": 'links-' + uuid.uuid4().hex, - } - - # Overwrite default attributes. - backup_info.update(attrs) - - backup = fakes.FakeResource(info=copy.deepcopy(backup_info), loaded=True) - return backup - - -def create_backups(attrs=None, count=2): - """Create multiple fake backups. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of backups to fake - :return: - A list of FakeResource objects faking the backups - """ - backups = [] - for i in range(0, count): - backup = create_one_backup(attrs) - backups.append(backup) - - return backups - - -def get_backups(backups=None, count=2): - """Get an iterable MagicMock object with a list of faked backups. - - If backups list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List volumes: - A list of FakeResource objects faking backups - :param Integer count: - The number of backups to be faked - :return - An iterable Mock object with side_effect set to a list of faked - backups - """ - if backups is None: - backups = create_backups(count) - - return mock.Mock(side_effect=backups) diff --git a/openstackclient/tests/unit/volume/v1/test_qos_specs.py b/openstackclient/tests/unit/volume/v1/test_qos_specs.py deleted file mode 100644 index 004749fa2..000000000 --- a/openstackclient/tests/unit/volume/v1/test_qos_specs.py +++ /dev/null @@ -1,471 +0,0 @@ -# Copyright 2015 iWeb Technologies Inc. -# -# 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 copy -from unittest import mock -from unittest.mock import call - -from osc_lib.cli import format_columns -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import qos_specs - - -class TestQos(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - self.qos_mock = self.volume_client.qos_specs - self.qos_mock.reset_mock() - - self.types_mock = self.volume_client.volume_types - self.types_mock.reset_mock() - - -class TestQosAssociate(TestQos): - volume_type = volume_fakes.create_one_volume_type() - qos_spec = volume_fakes.create_one_qos() - - def setUp(self): - super().setUp() - - self.qos_mock.get.return_value = self.qos_spec - self.types_mock.get.return_value = self.volume_type - # Get the command object to test - self.cmd = qos_specs.AssociateQos(self.app, None) - - def test_qos_associate(self): - arglist = [self.qos_spec.id, self.volume_type.id] - verifylist = [ - ('qos_spec', self.qos_spec.id), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.associate.assert_called_with( - self.qos_spec.id, self.volume_type.id - ) - self.assertIsNone(result) - - -class TestQosCreate(TestQos): - columns = ('consumer', 'id', 'name', 'properties') - - def setUp(self): - super().setUp() - self.new_qos_spec = volume_fakes.create_one_qos() - self.datalist = ( - self.new_qos_spec.consumer, - self.new_qos_spec.id, - self.new_qos_spec.name, - format_columns.DictColumn(self.new_qos_spec.specs), - ) - self.qos_mock.create.return_value = self.new_qos_spec - # Get the command object to test - self.cmd = qos_specs.CreateQos(self.app, None) - - def test_qos_create_without_properties(self): - arglist = [ - self.new_qos_spec.name, - ] - verifylist = [ - ('name', self.new_qos_spec.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.qos_mock.create.assert_called_with( - self.new_qos_spec.name, {'consumer': 'both'} - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_qos_create_with_consumer(self): - arglist = [ - '--consumer', - self.new_qos_spec.consumer, - self.new_qos_spec.name, - ] - verifylist = [ - ('consumer', self.new_qos_spec.consumer), - ('name', self.new_qos_spec.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.qos_mock.create.assert_called_with( - self.new_qos_spec.name, {'consumer': self.new_qos_spec.consumer} - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_qos_create_with_properties(self): - arglist = [ - '--consumer', - self.new_qos_spec.consumer, - '--property', - 'foo=bar', - '--property', - 'iops=9001', - self.new_qos_spec.name, - ] - verifylist = [ - ('consumer', self.new_qos_spec.consumer), - ('property', self.new_qos_spec.specs), - ('name', self.new_qos_spec.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.new_qos_spec.specs.update( - {'consumer': self.new_qos_spec.consumer} - ) - self.qos_mock.create.assert_called_with( - self.new_qos_spec.name, self.new_qos_spec.specs - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - -class TestQosDelete(TestQos): - qos_specs = volume_fakes.create_qoses(count=2) - - def setUp(self): - super().setUp() - - self.qos_mock.get = volume_fakes.get_qoses(self.qos_specs) - # Get the command object to test - self.cmd = qos_specs.DeleteQos(self.app, None) - - def test_qos_delete_with_id(self): - arglist = [self.qos_specs[0].id] - verifylist = [('qos_specs', [self.qos_specs[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.delete.assert_called_with(self.qos_specs[0].id, False) - self.assertIsNone(result) - - def test_qos_delete_with_name(self): - arglist = [self.qos_specs[0].name] - verifylist = [('qos_specs', [self.qos_specs[0].name])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.delete.assert_called_with(self.qos_specs[0].id, False) - self.assertIsNone(result) - - def test_qos_delete_with_force(self): - arglist = ['--force', self.qos_specs[0].id] - verifylist = [('force', True), ('qos_specs', [self.qos_specs[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.delete.assert_called_with(self.qos_specs[0].id, True) - self.assertIsNone(result) - - def test_delete_multiple_qoses(self): - arglist = [] - for q in self.qos_specs: - arglist.append(q.id) - verifylist = [ - ('qos_specs', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - calls = [] - for q in self.qos_specs: - calls.append(call(q.id, False)) - self.qos_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_delete_multiple_qoses_with_exception(self): - arglist = [ - self.qos_specs[0].id, - 'unexist_qos', - ] - verifylist = [ - ('qos_specs', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [self.qos_specs[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 QoS specifications failed to delete.', str(e) - ) - - find_mock.assert_any_call(self.qos_mock, self.qos_specs[0].id) - find_mock.assert_any_call(self.qos_mock, 'unexist_qos') - - self.assertEqual(2, find_mock.call_count) - self.qos_mock.delete.assert_called_once_with( - self.qos_specs[0].id, False - ) - - -class TestQosDisassociate(TestQos): - volume_type = volume_fakes.create_one_volume_type() - qos_spec = volume_fakes.create_one_qos() - - def setUp(self): - super().setUp() - - self.qos_mock.get.return_value = self.qos_spec - self.types_mock.get.return_value = self.volume_type - # Get the command object to test - self.cmd = qos_specs.DisassociateQos(self.app, None) - - def test_qos_disassociate_with_volume_type(self): - arglist = [ - '--volume-type', - self.volume_type.id, - self.qos_spec.id, - ] - verifylist = [ - ('volume_type', self.volume_type.id), - ('qos_spec', self.qos_spec.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.disassociate.assert_called_with( - self.qos_spec.id, self.volume_type.id - ) - self.assertIsNone(result) - - def test_qos_disassociate_with_all_volume_types(self): - arglist = [ - '--all', - self.qos_spec.id, - ] - verifylist = [('qos_spec', self.qos_spec.id)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.disassociate_all.assert_called_with(self.qos_spec.id) - self.assertIsNone(result) - - -class TestQosList(TestQos): - qos_specs = volume_fakes.create_qoses(count=2) - qos_association = volume_fakes.create_one_qos_association() - - columns = ( - 'ID', - 'Name', - 'Consumer', - 'Associations', - 'Properties', - ) - data = [] - for q in qos_specs: - data.append( - ( - q.id, - q.name, - q.consumer, - format_columns.ListColumn([qos_association.name]), - format_columns.DictColumn(q.specs), - ) - ) - - def setUp(self): - super().setUp() - - self.qos_mock.list.return_value = self.qos_specs - self.qos_mock.get_associations.return_value = [self.qos_association] - - # Get the command object to test - self.cmd = qos_specs.ListQos(self.app, None) - - def test_qos_list(self): - arglist = [] - verifylist = [] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.qos_mock.list.assert_called_with() - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, list(data)) - - def test_qos_list_no_association(self): - self.qos_mock.reset_mock() - self.qos_mock.get_associations.side_effect = [ - [self.qos_association], - exceptions.NotFound("NotFound"), - ] - - arglist = [] - verifylist = [] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.qos_mock.list.assert_called_with() - - self.assertEqual(self.columns, columns) - - ex_data = copy.deepcopy(self.data) - ex_data[1] = ( - self.qos_specs[1].id, - self.qos_specs[1].name, - self.qos_specs[1].consumer, - format_columns.ListColumn(None), - format_columns.DictColumn(self.qos_specs[1].specs), - ) - self.assertCountEqual(ex_data, list(data)) - - -class TestQosSet(TestQos): - qos_spec = volume_fakes.create_one_qos() - - def setUp(self): - super().setUp() - - self.qos_mock.get.return_value = self.qos_spec - # Get the command object to test - self.cmd = qos_specs.SetQos(self.app, None) - - def test_qos_set_with_properties_with_id(self): - arglist = [ - '--no-property', - '--property', - 'a=b', - '--property', - 'c=d', - self.qos_spec.id, - ] - new_property = {"a": "b", "c": "d"} - verifylist = [ - ('no_property', True), - ('property', new_property), - ('qos_spec', self.qos_spec.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.unset_keys.assert_called_with( - self.qos_spec.id, - list(self.qos_spec.specs.keys()), - ) - self.qos_mock.set_keys.assert_called_with( - self.qos_spec.id, - {"a": "b", "c": "d"}, - ) - self.assertIsNone(result) - - -class TestQosShow(TestQos): - qos_spec = volume_fakes.create_one_qos() - qos_association = volume_fakes.create_one_qos_association() - - def setUp(self): - super().setUp() - self.qos_mock.get.return_value = self.qos_spec - self.qos_mock.get_associations.return_value = [self.qos_association] - # Get the command object to test - self.cmd = qos_specs.ShowQos(self.app, None) - - def test_qos_show(self): - arglist = [self.qos_spec.id] - verifylist = [('qos_spec', self.qos_spec.id)] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.qos_mock.get.assert_called_with(self.qos_spec.id) - - collist = ('associations', 'consumer', 'id', 'name', 'properties') - self.assertEqual(collist, columns) - datalist = ( - format_columns.ListColumn([self.qos_association.name]), - self.qos_spec.consumer, - self.qos_spec.id, - self.qos_spec.name, - format_columns.DictColumn(self.qos_spec.specs), - ) - self.assertCountEqual(datalist, tuple(data)) - - -class TestQosUnset(TestQos): - qos_spec = volume_fakes.create_one_qos() - - def setUp(self): - super().setUp() - - self.qos_mock.get.return_value = self.qos_spec - # Get the command object to test - self.cmd = qos_specs.UnsetQos(self.app, None) - - def test_qos_unset_with_properties(self): - arglist = [ - '--property', - 'iops', - '--property', - 'foo', - self.qos_spec.id, - ] - verifylist = [ - ('property', ['iops', 'foo']), - ('qos_spec', self.qos_spec.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.qos_mock.unset_keys.assert_called_with( - self.qos_spec.id, ['iops', 'foo'] - ) - self.assertIsNone(result) - - def test_qos_unset_nothing(self): - arglist = [ - self.qos_spec.id, - ] - - verifylist = [ - ('qos_spec', self.qos_spec.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.assertIsNone(result) diff --git a/openstackclient/tests/unit/volume/v1/test_service.py b/openstackclient/tests/unit/volume/v1/test_service.py deleted file mode 100644 index 95a21994f..000000000 --- a/openstackclient/tests/unit/volume/v1/test_service.py +++ /dev/null @@ -1,295 +0,0 @@ -# -# 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. -# - -from osc_lib import exceptions - -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import service - - -class TestService(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - # Get a shortcut to the ServiceManager Mock - self.service_mock = self.volume_client.services - self.service_mock.reset_mock() - - -class TestServiceList(TestService): - # The service to be listed - services = volume_fakes.create_one_service() - - def setUp(self): - super().setUp() - - self.service_mock.list.return_value = [self.services] - - # Get the command object to test - self.cmd = service.ListService(self.app, None) - - def test_service_list(self): - arglist = [ - '--host', - self.services.host, - '--service', - self.services.binary, - ] - verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. - columns, data = self.cmd.take_action(parsed_args) - - expected_columns = [ - 'Binary', - 'Host', - 'Zone', - 'Status', - 'State', - 'Updated At', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( - ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, - ), - ) - - # confirming if all expected values are present in the result. - self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list services - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, - ) - - # checking if prohibited columns are present in output - self.assertNotIn("Disabled Reason", columns) - self.assertNotIn(self.services.disabled_reason, tuple(data)) - - def test_service_list_with_long_option(self): - arglist = [ - '--host', - self.services.host, - '--service', - self.services.binary, - '--long', - ] - verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), - ('long', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. - columns, data = self.cmd.take_action(parsed_args) - - expected_columns = [ - 'Binary', - 'Host', - 'Zone', - 'Status', - 'State', - 'Updated At', - 'Disabled Reason', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( - ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, - self.services.disabled_reason, - ), - ) - - # confirming if all expected values are present in the result. - self.assertEqual(datalist, tuple(data)) - - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, - ) - - -class TestServiceSet(TestService): - service = volume_fakes.create_one_service() - - def setUp(self): - super().setUp() - - self.service_mock.enable.return_value = self.service - self.service_mock.disable.return_value = self.service - self.service_mock.disable_log_reason.return_value = self.service - - self.cmd = service.SetService(self.app, None) - - def test_service_set_nothing(self): - arglist = [ - self.service.host, - self.service.binary, - ] - verifylist = [ - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - self.service_mock.enable.assert_not_called() - self.service_mock.disable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() - self.assertIsNone(result) - - def test_service_set_enable(self): - arglist = [ - '--enable', - self.service.host, - self.service.binary, - ] - verifylist = [ - ('enable', True), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.service_mock.enable.assert_called_with( - self.service.host, self.service.binary - ) - self.service_mock.disable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() - self.assertIsNone(result) - - def test_service_set_disable(self): - arglist = [ - '--disable', - self.service.host, - self.service.binary, - ] - verifylist = [ - ('disable', True), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.service_mock.disable.assert_called_with( - self.service.host, self.service.binary - ) - self.service_mock.enable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() - self.assertIsNone(result) - - def test_service_set_disable_with_reason(self): - reason = 'earthquake' - arglist = [ - '--disable', - '--disable-reason', - reason, - self.service.host, - self.service.binary, - ] - verifylist = [ - ('disable', True), - ('disable_reason', reason), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.service_mock.disable_log_reason.assert_called_with( - self.service.host, self.service.binary, reason - ) - self.assertIsNone(result) - - def test_service_set_only_with_disable_reason(self): - reason = 'earthquake' - arglist = [ - '--disable-reason', - reason, - self.service.host, - self.service.binary, - ] - verifylist = [ - ('disable_reason', reason), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail("CommandError should be raised.") - except exceptions.CommandError as e: - self.assertEqual( - "Cannot specify option --disable-reason without " - "--disable specified.", - str(e), - ) - - def test_service_set_enable_with_disable_reason(self): - reason = 'earthquake' - arglist = [ - '--enable', - '--disable-reason', - reason, - self.service.host, - self.service.binary, - ] - verifylist = [ - ('enable', True), - ('disable_reason', reason), - ('host', self.service.host), - ('service', self.service.binary), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail("CommandError should be raised.") - except exceptions.CommandError as e: - self.assertEqual( - "Cannot specify option --disable-reason without " - "--disable specified.", - str(e), - ) diff --git a/openstackclient/tests/unit/volume/v1/test_transfer_request.py b/openstackclient/tests/unit/volume/v1/test_transfer_request.py deleted file mode 100644 index 47d925d4c..000000000 --- a/openstackclient/tests/unit/volume/v1/test_transfer_request.py +++ /dev/null @@ -1,380 +0,0 @@ -# -# 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. -# - -from unittest import mock -from unittest.mock import call - -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import volume_transfer_request - - -class TestTransfer(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - # Get a shortcut to the TransferManager Mock - self.transfer_mock = self.volume_client.transfers - self.transfer_mock.reset_mock() - - # Get a shortcut to the VolumeManager Mock - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - - -class TestTransferAccept(TestTransfer): - columns = ( - 'id', - 'name', - 'volume_id', - ) - - def setUp(self): - super().setUp() - - self.volume_transfer = volume_fakes.create_one_transfer() - self.data = ( - self.volume_transfer.id, - self.volume_transfer.name, - self.volume_transfer.volume_id, - ) - - self.transfer_mock.get.return_value = self.volume_transfer - self.transfer_mock.accept.return_value = self.volume_transfer - - # Get the command object to test - self.cmd = volume_transfer_request.AcceptTransferRequest( - self.app, None - ) - - def test_transfer_accept(self): - arglist = [ - '--auth-key', - 'key_value', - self.volume_transfer.id, - ] - verifylist = [ - ('transfer_request', self.volume_transfer.id), - ('auth_key', 'key_value'), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.transfer_mock.get.assert_called_once_with( - self.volume_transfer.id, - ) - self.transfer_mock.accept.assert_called_once_with( - self.volume_transfer.id, - 'key_value', - ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - def test_transfer_accept_no_option(self): - arglist = [ - self.volume_transfer.id, - ] - verifylist = [ - ('transfer_request', self.volume_transfer.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, - self.cmd.take_action, - parsed_args, - ) - - -class TestTransferCreate(TestTransfer): - volume = volume_fakes.create_one_volume() - - columns = ( - 'auth_key', - 'created_at', - 'id', - 'name', - 'volume_id', - ) - - def setUp(self): - super().setUp() - - self.volume_transfer = volume_fakes.create_one_transfer( - attrs={ - 'volume_id': self.volume.id, - 'auth_key': 'key', - 'created_at': 'time', - }, - ) - self.data = ( - self.volume_transfer.auth_key, - self.volume_transfer.created_at, - self.volume_transfer.id, - self.volume_transfer.name, - self.volume_transfer.volume_id, - ) - - self.transfer_mock.create.return_value = self.volume_transfer - self.volumes_mock.get.return_value = self.volume - - # Get the command object to test - self.cmd = volume_transfer_request.CreateTransferRequest( - self.app, None - ) - - def test_transfer_create_without_name(self): - arglist = [ - self.volume.id, - ] - verifylist = [ - ('volume', self.volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.transfer_mock.create.assert_called_once_with(self.volume.id, None) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - def test_transfer_create_with_name(self): - arglist = [ - '--name', - self.volume_transfer.name, - self.volume.id, - ] - verifylist = [ - ('name', self.volume_transfer.name), - ('volume', self.volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.transfer_mock.create.assert_called_once_with( - self.volume.id, - self.volume_transfer.name, - ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - -class TestTransferDelete(TestTransfer): - volume_transfers = volume_fakes.create_transfers(count=2) - - def setUp(self): - super().setUp() - - self.transfer_mock.get = volume_fakes.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 - volume_transfers = volume_fakes.create_one_transfer() - - def setUp(self): - super().setUp() - - self.transfer_mock.list.return_value = [self.volume_transfers] - - # Get the command object to test - self.cmd = volume_transfer_request.ListTransferRequest(self.app, None) - - def test_transfer_list_without_argument(self): - arglist = [] - verifylist = [] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. - columns, data = self.cmd.take_action(parsed_args) - - expected_columns = [ - 'ID', - 'Name', - 'Volume', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( - ( - self.volume_transfers.id, - self.volume_transfers.name, - self.volume_transfers.volume_id, - ), - ) - - # confirming if all expected values are present in the result. - self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list volume_transfers - self.transfer_mock.list.assert_called_with( - detailed=True, search_opts={'all_tenants': 0} - ) - - def test_transfer_list_with_argument(self): - arglist = ["--all-projects"] - verifylist = [("all_projects", True)] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. - columns, data = self.cmd.take_action(parsed_args) - - expected_columns = [ - 'ID', - 'Name', - 'Volume', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( - ( - self.volume_transfers.id, - self.volume_transfers.name, - self.volume_transfers.volume_id, - ), - ) - - # confirming if all expected values are present in the result. - self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list volume_transfers - self.transfer_mock.list.assert_called_with( - detailed=True, search_opts={'all_tenants': 1} - ) - - -class TestTransferShow(TestTransfer): - columns = ( - 'created_at', - 'id', - 'name', - 'volume_id', - ) - - def setUp(self): - super().setUp() - - self.volume_transfer = volume_fakes.create_one_transfer( - attrs={'created_at': 'time'} - ) - self.data = ( - self.volume_transfer.created_at, - self.volume_transfer.id, - self.volume_transfer.name, - self.volume_transfer.volume_id, - ) - - self.transfer_mock.get.return_value = self.volume_transfer - - # Get the command object to test - self.cmd = volume_transfer_request.ShowTransferRequest(self.app, None) - - def test_transfer_show(self): - arglist = [ - self.volume_transfer.id, - ] - verifylist = [ - ('transfer_request', self.volume_transfer.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.transfer_mock.get.assert_called_once_with(self.volume_transfer.id) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v1/test_type.py b/openstackclient/tests/unit/volume/v1/test_type.py deleted file mode 100644 index bc15bcc4d..000000000 --- a/openstackclient/tests/unit/volume/v1/test_type.py +++ /dev/null @@ -1,633 +0,0 @@ -# -# 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. -# - -from unittest import mock -from unittest.mock import call - -from osc_lib.cli import format_columns -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit import utils as tests_utils -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import volume_type - - -class TestType(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - self.types_mock = self.volume_client.volume_types - self.types_mock.reset_mock() - - self.encryption_types_mock = self.volume_client.volume_encryption_types - self.encryption_types_mock.reset_mock() - - -class TestTypeCreate(TestType): - columns = ( - 'description', - 'id', - 'is_public', - 'name', - ) - - def setUp(self): - super().setUp() - - self.new_volume_type = volume_fakes.create_one_volume_type( - methods={'set_keys': {'myprop': 'myvalue'}}, - ) - self.data = ( - self.new_volume_type.description, - self.new_volume_type.id, - True, - self.new_volume_type.name, - ) - - self.types_mock.create.return_value = self.new_volume_type - # Get the command object to test - self.cmd = volume_type.CreateVolumeType(self.app, None) - - def test_type_create(self): - arglist = [ - self.new_volume_type.name, - ] - verifylist = [ - ("name", self.new_volume_type.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.create.assert_called_with( - self.new_volume_type.name, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) - - def test_type_create_with_encryption(self): - encryption_info = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': '128', - 'control_location': 'front-end', - } - encryption_type = volume_fakes.create_one_encryption_volume_type( - attrs=encryption_info, - ) - self.new_volume_type = volume_fakes.create_one_volume_type( - attrs={'encryption': encryption_info}, - ) - self.types_mock.create.return_value = self.new_volume_type - self.encryption_types_mock.create.return_value = encryption_type - encryption_columns = ( - 'description', - 'encryption', - 'id', - 'is_public', - 'name', - ) - encryption_data = ( - self.new_volume_type.description, - format_columns.DictColumn(encryption_info), - self.new_volume_type.id, - True, - self.new_volume_type.name, - ) - arglist = [ - '--encryption-provider', - 'LuksEncryptor', - '--encryption-cipher', - 'aes-xts-plain64', - '--encryption-key-size', - '128', - '--encryption-control-location', - 'front-end', - self.new_volume_type.name, - ] - verifylist = [ - ('encryption_provider', 'LuksEncryptor'), - ('encryption_cipher', 'aes-xts-plain64'), - ('encryption_key_size', 128), - ('encryption_control_location', 'front-end'), - ('name', self.new_volume_type.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.create.assert_called_with( - self.new_volume_type.name, - ) - body = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - self.encryption_types_mock.create.assert_called_with( - self.new_volume_type, - body, - ) - self.assertEqual(encryption_columns, columns) - self.assertCountEqual(encryption_data, data) - - -class TestTypeDelete(TestType): - volume_types = volume_fakes.create_volume_types(count=2) - - def setUp(self): - super().setUp() - - self.types_mock.get = volume_fakes.get_volume_types(self.volume_types) - self.types_mock.delete.return_value = None - - # Get the command object to mock - self.cmd = volume_type.DeleteVolumeType(self.app, None) - - def test_type_delete(self): - arglist = [self.volume_types[0].id] - verifylist = [("volume_types", [self.volume_types[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.types_mock.delete.assert_called_with(self.volume_types[0]) - self.assertIsNone(result) - - def test_delete_multiple_types(self): - arglist = [] - for t in self.volume_types: - arglist.append(t.id) - verifylist = [ - ('volume_types', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - calls = [] - for t in self.volume_types: - calls.append(call(t)) - self.types_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_delete_multiple_types_with_exception(self): - arglist = [ - self.volume_types[0].id, - 'unexist_type', - ] - verifylist = [ - ('volume_types', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [self.volume_types[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 types failed to delete.', str(e) - ) - - find_mock.assert_any_call(self.types_mock, self.volume_types[0].id) - find_mock.assert_any_call(self.types_mock, 'unexist_type') - - self.assertEqual(2, find_mock.call_count) - self.types_mock.delete.assert_called_once_with( - self.volume_types[0] - ) - - -class TestTypeList(TestType): - volume_types = volume_fakes.create_volume_types() - - columns = [ - "ID", - "Name", - "Is Public", - ] - columns_long = ["ID", "Name", "Is Public", "Properties"] - - data = [] - for t in volume_types: - data.append( - ( - t.id, - t.name, - t.is_public, - ) - ) - data_long = [] - for t in volume_types: - data_long.append( - ( - t.id, - t.name, - t.is_public, - format_columns.DictColumn(t.extra_specs), - ) - ) - - def setUp(self): - super().setUp() - - self.types_mock.list.return_value = self.volume_types - self.encryption_types_mock.create.return_value = None - self.encryption_types_mock.update.return_value = None - # get the command to test - self.cmd = volume_type.ListVolumeType(self.app, None) - - def test_type_list_without_options(self): - arglist = [] - verifylist = [ - ("long", False), - ("encryption_type", False), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.list.assert_called_once_with() - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, list(data)) - - def test_type_list_with_options(self): - arglist = [ - "--long", - ] - verifylist = [ - ("long", True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.list.assert_called_once_with() - self.assertEqual(self.columns_long, columns) - self.assertCountEqual(self.data_long, list(data)) - - def test_type_list_with_encryption(self): - encryption_type = volume_fakes.create_one_encryption_volume_type( - attrs={'volume_type_id': self.volume_types[0].id}, - ) - encryption_info = { - 'provider': 'LuksEncryptor', - 'cipher': None, - 'key_size': None, - 'control_location': 'front-end', - } - encryption_columns = self.columns + [ - "Encryption", - ] - encryption_data = [] - encryption_data.append( - ( - self.volume_types[0].id, - self.volume_types[0].name, - self.volume_types[0].is_public, - volume_type.EncryptionInfoColumn( - self.volume_types[0].id, - {self.volume_types[0].id: encryption_info}, - ), - ) - ) - encryption_data.append( - ( - self.volume_types[1].id, - self.volume_types[1].name, - self.volume_types[1].is_public, - volume_type.EncryptionInfoColumn(self.volume_types[1].id, {}), - ) - ) - - self.encryption_types_mock.list.return_value = [encryption_type] - arglist = [ - "--encryption-type", - ] - verifylist = [ - ("encryption_type", True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.encryption_types_mock.list.assert_called_once_with() - self.types_mock.list.assert_called_once_with() - self.assertEqual(encryption_columns, columns) - self.assertCountEqual(encryption_data, list(data)) - - -class TestTypeSet(TestType): - volume_type = volume_fakes.create_one_volume_type( - methods={'set_keys': None}, - ) - - def setUp(self): - super().setUp() - - self.types_mock.get.return_value = self.volume_type - - # Get the command object to test - self.cmd = volume_type.SetVolumeType(self.app, None) - - def test_type_set_nothing(self): - arglist = [ - self.volume_type.id, - ] - verifylist = [ - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.assertIsNone(result) - - def test_type_set_property(self): - arglist = [ - '--property', - 'myprop=myvalue', - self.volume_type.id, - ] - verifylist = [ - ('property', {'myprop': 'myvalue'}), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volume_type.set_keys.assert_called_once_with( - {'myprop': 'myvalue'} - ) - self.assertIsNone(result) - - def test_type_set_new_encryption(self): - arglist = [ - '--encryption-provider', - 'LuksEncryptor', - '--encryption-cipher', - 'aes-xts-plain64', - '--encryption-key-size', - '128', - '--encryption-control-location', - 'front-end', - self.volume_type.id, - ] - verifylist = [ - ('encryption_provider', 'LuksEncryptor'), - ('encryption_cipher', 'aes-xts-plain64'), - ('encryption_key_size', 128), - ('encryption_control_location', 'front-end'), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - body = { - 'provider': 'LuksEncryptor', - 'cipher': 'aes-xts-plain64', - 'key_size': 128, - 'control_location': 'front-end', - } - self.encryption_types_mock.create.assert_called_with( - self.volume_type, - body, - ) - self.assertIsNone(result) - - def test_type_set_new_encryption_without_provider(self): - arglist = [ - '--encryption-cipher', - 'aes-xts-plain64', - '--encryption-key-size', - '128', - '--encryption-control-location', - 'front-end', - self.volume_type.id, - ] - verifylist = [ - ('encryption_cipher', 'aes-xts-plain64'), - ('encryption_key_size', 128), - ('encryption_control_location', 'front-end'), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - "Command Failed: One or more of the operations failed", - str(e), - ) - self.encryption_types_mock.create.assert_not_called() - self.encryption_types_mock.update.assert_not_called() - - -class TestTypeShow(TestType): - columns = ( - 'description', - 'id', - 'is_public', - 'name', - 'properties', - ) - - def setUp(self): - super().setUp() - - self.volume_type = volume_fakes.create_one_volume_type() - self.data = ( - self.volume_type.description, - self.volume_type.id, - True, - self.volume_type.name, - format_columns.DictColumn(self.volume_type.extra_specs), - ) - - self.types_mock.get.return_value = self.volume_type - - # Get the command object to test - self.cmd = volume_type.ShowVolumeType(self.app, None) - - def test_type_show(self): - arglist = [self.volume_type.id] - verifylist = [ - ("volume_type", self.volume_type.id), - ("encryption_type", False), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.get.assert_called_with(self.volume_type.id) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) - - def test_type_show_with_encryption(self): - encryption_type = volume_fakes.create_one_encryption_volume_type() - encryption_info = { - 'provider': 'LuksEncryptor', - 'cipher': None, - 'key_size': None, - 'control_location': 'front-end', - } - self.volume_type = volume_fakes.create_one_volume_type( - attrs={'encryption': encryption_info}, - ) - self.types_mock.get.return_value = self.volume_type - self.encryption_types_mock.get.return_value = encryption_type - encryption_columns = ( - 'description', - 'encryption', - 'id', - 'is_public', - 'name', - 'properties', - ) - encryption_data = ( - self.volume_type.description, - format_columns.DictColumn(encryption_info), - self.volume_type.id, - True, - self.volume_type.name, - format_columns.DictColumn(self.volume_type.extra_specs), - ) - arglist = ['--encryption-type', self.volume_type.id] - verifylist = [ - ('encryption_type', True), - ("volume_type", self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.types_mock.get.assert_called_with(self.volume_type.id) - self.encryption_types_mock.get.assert_called_with(self.volume_type.id) - self.assertEqual(encryption_columns, columns) - self.assertCountEqual(encryption_data, data) - - -class TestTypeUnset(TestType): - volume_type = volume_fakes.create_one_volume_type( - methods={'unset_keys': None}, - ) - - def setUp(self): - super().setUp() - - self.types_mock.get.return_value = self.volume_type - - # Get the command object to test - self.cmd = volume_type.UnsetVolumeType(self.app, None) - - def test_type_unset_property(self): - arglist = [ - '--property', - 'property', - '--property', - 'multi_property', - self.volume_type.id, - ] - verifylist = [ - ('encryption_type', False), - ('property', ['property', 'multi_property']), - ('volume_type', self.volume_type.id), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volume_type.unset_keys.assert_called_once_with( - ['property', 'multi_property'] - ) - self.encryption_types_mock.delete.assert_not_called() - self.assertIsNone(result) - - def test_type_unset_failed_with_missing_volume_type_argument(self): - arglist = [ - '--property', - 'property', - '--property', - 'multi_property', - ] - verifylist = [ - ('property', ['property', 'multi_property']), - ] - - self.assertRaises( - tests_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - verifylist, - ) - - def test_type_unset_nothing(self): - arglist = [ - self.volume_type.id, - ] - verifylist = [ - ('volume_type', self.volume_type.id), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.assertIsNone(result) - - def test_type_unset_encryption_type(self): - arglist = [ - '--encryption-type', - self.volume_type.id, - ] - verifylist = [ - ('encryption_type', True), - ('volume_type', self.volume_type.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.encryption_types_mock.delete.assert_called_with(self.volume_type) - self.assertIsNone(result) - - -class TestColumns(TestType): - def test_encryption_info_column_with_info(self): - fake_volume_type = volume_fakes.create_one_volume_type() - type_id = fake_volume_type.id - - encryption_info = { - 'provider': 'LuksEncryptor', - 'cipher': None, - 'key_size': None, - 'control_location': 'front-end', - } - col = volume_type.EncryptionInfoColumn( - type_id, {type_id: encryption_info} - ) - self.assertEqual( - utils.format_dict(encryption_info), col.human_readable() - ) - self.assertEqual(encryption_info, col.machine_readable()) - - def test_encryption_info_column_without_info(self): - fake_volume_type = volume_fakes.create_one_volume_type() - type_id = fake_volume_type.id - - col = volume_type.EncryptionInfoColumn(type_id, {}) - self.assertEqual('-', col.human_readable()) - self.assertIsNone(col.machine_readable()) diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py deleted file mode 100644 index 0f0d532dd..000000000 --- a/openstackclient/tests/unit/volume/v1/test_volume.py +++ /dev/null @@ -1,1447 +0,0 @@ -# Copyright 2013 Nebula Inc. -# -# 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. - -from unittest import mock -from unittest.mock import call - -from osc_lib.cli import format_columns -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes -from openstackclient.tests.unit.image.v1 import fakes as image_fakes -from openstackclient.tests.unit import utils as test_utils -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import volume - - -class TestVolume(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - # Get a shortcut to the VolumeManager Mock - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - - # Get a shortcut to the TenantManager Mock - self.projects_mock = self.identity_client.tenants - self.projects_mock.reset_mock() - - # Get a shortcut to the UserManager Mock - self.users_mock = self.identity_client.users - self.users_mock.reset_mock() - - def setup_volumes_mock(self, count): - volumes = volume_fakes.create_volumes(count=count) - - self.volumes_mock.get = volume_fakes.get_volumes(volumes, 0) - return volumes - - -class TestVolumeCreate(TestVolume): - project = identity_fakes.FakeProject.create_one_project() - user = identity_fakes.FakeUser.create_one_user() - - columns = ( - 'attachments', - 'availability_zone', - 'bootable', - 'created_at', - 'display_description', - 'id', - 'name', - 'properties', - 'size', - 'snapshot_id', - 'status', - 'type', - ) - - def setUp(self): - super().setUp() - self.new_volume = volume_fakes.create_one_volume() - self.datalist = ( - self.new_volume.attachments, - self.new_volume.availability_zone, - self.new_volume.bootable, - self.new_volume.created_at, - self.new_volume.display_description, - self.new_volume.id, - self.new_volume.display_name, - format_columns.DictColumn(self.new_volume.metadata), - self.new_volume.size, - self.new_volume.snapshot_id, - self.new_volume.status, - self.new_volume.volume_type, - ) - self.volumes_mock.create.return_value = self.new_volume - - # Get the command object to test - self.cmd = volume.CreateVolume(self.app, None) - - def test_volume_create_min_options(self): - arglist = [ - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_options(self): - arglist = [ - '--size', - str(self.new_volume.size), - '--description', - self.new_volume.display_description, - '--type', - self.new_volume.volume_type, - '--availability-zone', - self.new_volume.availability_zone, - self.new_volume.display_name, - ] - verifylist = [ - ('size', self.new_volume.size), - ('description', self.new_volume.display_description), - ('type', self.new_volume.volume_type), - ('availability_zone', self.new_volume.availability_zone), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - self.new_volume.display_description, - self.new_volume.volume_type, - None, - None, - self.new_volume.availability_zone, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_user_project_id(self): - # Return a project - self.projects_mock.get.return_value = self.project - # Return a user - self.users_mock.get.return_value = self.user - - arglist = [ - '--size', - str(self.new_volume.size), - '--project', - self.project.id, - '--user', - self.user.id, - self.new_volume.display_name, - ] - verifylist = [ - ('size', self.new_volume.size), - ('project', self.project.id), - ('user', self.user.id), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - self.user.id, - self.project.id, - None, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_user_project_name(self): - # Return a project - self.projects_mock.get.return_value = self.project - # Return a user - self.users_mock.get.return_value = self.user - - arglist = [ - '--size', - str(self.new_volume.size), - '--project', - self.project.name, - '--user', - self.user.name, - self.new_volume.display_name, - ] - verifylist = [ - ('size', self.new_volume.size), - ('project', self.project.name), - ('user', self.user.name), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - self.user.id, - self.project.id, - None, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_properties(self): - arglist = [ - '--property', - 'Alpha=a', - '--property', - 'Beta=b', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('property', {'Alpha': 'a', 'Beta': 'b'}), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - {'Alpha': 'a', 'Beta': 'b'}, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_image_id(self): - image = image_fakes.create_one_image() - self.image_client.find_image.return_value = image - - arglist = [ - '--image', - image.id, - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('image', image.id), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - image.id, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_image_name(self): - image = image_fakes.create_one_image() - self.image_client.find_image.return_value = image - - arglist = [ - '--image', - image.name, - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('image', image.name), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.create(size, snapshot_id=, source_volid=, - # display_name=, display_description=, - # volume_type=, user_id=, - # project_id=, availability_zone=, - # metadata=, imageRef=) - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - image.id, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_with_source(self): - self.volumes_mock.get.return_value = self.new_volume - arglist = [ - '--source', - self.new_volume.id, - self.new_volume.display_name, - ] - verifylist = [ - ('source', self.new_volume.id), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - None, - None, - self.new_volume.id, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - @mock.patch.object(utils, 'wait_for_status', return_value=True) - def test_volume_create_with_bootable_and_readonly(self, mock_wait): - arglist = [ - '--bootable', - '--read-only', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('bootable', True), - ('non_bootable', False), - ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True - ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True - ) - - @mock.patch.object(utils, 'wait_for_status', return_value=True) - def test_volume_create_with_nonbootable_and_readwrite(self, mock_wait): - arglist = [ - '--non-bootable', - '--read-write', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('bootable', False), - ('non_bootable', True), - ('read_only', False), - ('read_write', True), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, False - ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, False - ) - - @mock.patch.object(volume.LOG, 'error') - @mock.patch.object(utils, 'wait_for_status', return_value=True) - def test_volume_create_with_bootable_and_readonly_fail( - self, mock_wait, mock_error - ): - self.volumes_mock.set_bootable.side_effect = exceptions.CommandError() - - self.volumes_mock.update_readonly_flag.side_effect = ( - exceptions.CommandError() - ) - - arglist = [ - '--bootable', - '--read-only', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('bootable', True), - ('non_bootable', False), - ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - - self.assertEqual(2, mock_error.call_count) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True - ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True - ) - - @mock.patch.object(volume.LOG, 'error') - @mock.patch.object(utils, 'wait_for_status', return_value=False) - def test_volume_create_non_available_with_readonly( - self, mock_wait, mock_error - ): - arglist = [ - '--non-bootable', - '--read-only', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('bootable', False), - ('non_bootable', True), - ('read_only', True), - ('read_write', False), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - - self.assertEqual(2, mock_error.call_count) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_without_size(self): - arglist = [ - self.new_volume.display_name, - ] - verifylist = [ - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - - def test_volume_create_with_multi_source(self): - arglist = [ - '--image', - 'source_image', - '--source', - 'source_volume', - '--snapshot', - 'source_snapshot', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('image', 'source_image'), - ('source', 'source_volume'), - ('snapshot', 'source_snapshot'), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - - self.assertRaises( - test_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - verifylist, - ) - - def test_volume_create_backward_compatibility(self): - arglist = [ - '-c', - 'display_name', - '--size', - str(self.new_volume.size), - self.new_volume.display_name, - ] - verifylist = [ - ('columns', ['display_name']), - ('size', self.new_volume.size), - ('name', self.new_volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - self.new_volume.size, - None, - None, - self.new_volume.display_name, - None, - None, - None, - None, - None, - None, - None, - ) - self.assertIn('display_name', columns) - self.assertNotIn('name', columns) - self.assertIn(self.new_volume.display_name, data) - - -class TestVolumeDelete(TestVolume): - def setUp(self): - super().setUp() - - self.volumes_mock.delete.return_value = None - - # Get the command object to mock - self.cmd = volume.DeleteVolume(self.app, None) - - def test_volume_delete_one_volume(self): - volumes = self.setup_volumes_mock(count=1) - - arglist = [volumes[0].id] - verifylist = [ - ("force", False), - ("volumes", [volumes[0].id]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.volumes_mock.delete.assert_called_once_with(volumes[0].id) - self.assertIsNone(result) - - def test_volume_delete_multi_volumes(self): - volumes = self.setup_volumes_mock(count=3) - - arglist = [v.id for v in volumes] - verifylist = [ - ('force', False), - ('volumes', arglist), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - calls = [call(v.id) for v in volumes] - self.volumes_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_volume_delete_multi_volumes_with_exception(self): - volumes = self.setup_volumes_mock(count=2) - - arglist = [ - volumes[0].id, - 'unexist_volume', - ] - verifylist = [ - ('force', False), - ('volumes', arglist), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [volumes[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 volumes failed to delete.', str(e)) - - find_mock.assert_any_call(self.volumes_mock, volumes[0].id) - find_mock.assert_any_call(self.volumes_mock, 'unexist_volume') - - self.assertEqual(2, find_mock.call_count) - self.volumes_mock.delete.assert_called_once_with(volumes[0].id) - - def test_volume_delete_with_force(self): - volumes = self.setup_volumes_mock(count=1) - - arglist = [ - '--force', - volumes[0].id, - ] - verifylist = [ - ('force', True), - ('volumes', [volumes[0].id]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.volumes_mock.force_delete.assert_called_once_with(volumes[0].id) - self.assertIsNone(result) - - -class TestVolumeList(TestVolume): - _volume = volume_fakes.create_one_volume() - columns = ( - 'ID', - 'Name', - 'Status', - 'Size', - 'Attached to', - ) - datalist = ( - ( - _volume.id, - _volume.display_name, - _volume.status, - _volume.size, - volume.AttachmentsColumn(_volume.attachments), - ), - ) - - def setUp(self): - super().setUp() - - self.volumes_mock.list.return_value = [self._volume] - - # Get the command object to test - self.cmd = volume.ListVolume(self.app, None) - - def test_volume_list_no_options(self): - arglist = [] - verifylist = [ - ('long', False), - ('all_projects', False), - ('name', None), - ('status', None), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_name(self): - arglist = [ - '--name', - self._volume.display_name, - ] - verifylist = [ - ('long', False), - ('all_projects', False), - ('name', self._volume.display_name), - ('status', None), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.columns, tuple(columns)) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_status(self): - arglist = [ - '--status', - self._volume.status, - ] - verifylist = [ - ('long', False), - ('all_projects', False), - ('name', None), - ('status', self._volume.status), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.columns, tuple(columns)) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_all_projects(self): - arglist = [ - '--all-projects', - ] - verifylist = [ - ('long', False), - ('all_projects', True), - ('name', None), - ('status', None), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.columns, tuple(columns)) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_long(self): - arglist = [ - '--long', - ] - verifylist = [ - ('long', True), - ('all_projects', False), - ('name', None), - ('status', None), - ('limit', None), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - collist = ( - 'ID', - 'Name', - 'Status', - 'Size', - 'Type', - 'Bootable', - 'Attached to', - 'Properties', - ) - self.assertEqual(collist, columns) - - datalist = ( - ( - self._volume.id, - self._volume.display_name, - self._volume.status, - self._volume.size, - self._volume.volume_type, - self._volume.bootable, - volume.AttachmentsColumn(self._volume.attachments), - format_columns.DictColumn(self._volume.metadata), - ), - ) - self.assertCountEqual(datalist, tuple(data)) - - def test_volume_list_with_limit_and_offset(self): - arglist = [ - '--limit', - '2', - '--offset', - '5', - ] - verifylist = [ - ('long', False), - ('all_projects', False), - ('name', None), - ('status', None), - ('limit', 2), - ('offset', 5), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.list.assert_called_once_with( - limit=2, - search_opts={ - 'offset': 5, - 'status': None, - 'display_name': None, - 'all_tenants': False, - }, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, tuple(data)) - - def test_volume_list_negative_limit(self): - arglist = [ - "--limit", - "-2", - ] - verifylist = [ - ("limit", -2), - ] - self.assertRaises( - test_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - verifylist, - ) - - def test_volume_list_backward_compatibility(self): - arglist = [ - '-c', - 'Display Name', - ] - verifylist = [ - ('columns', ['Display Name']), - ('long', False), - ('all_projects', False), - ('name', None), - ('status', None), - ('limit', None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.assertIn('Display Name', columns) - self.assertNotIn('Name', columns) - for each_volume in data: - self.assertIn(self._volume.display_name, each_volume) - - -class TestVolumeMigrate(TestVolume): - _volume = volume_fakes.create_one_volume() - - def setUp(self): - super().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( - test_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - verifylist, - ) - - -class TestVolumeSet(TestVolume): - _volume = volume_fakes.create_one_volume() - - def setUp(self): - super().setUp() - - self.volumes_mock.get.return_value = self._volume - - self.volumes_mock.update.return_value = self._volume - # Get the command object to test - self.cmd = volume.SetVolume(self.app, None) - - def test_volume_set_no_options(self): - arglist = [ - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', None), - ('size', None), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.assertIsNone(result) - - def test_volume_set_name(self): - arglist = [ - '--name', - 'qwerty', - self._volume.display_name, - ] - verifylist = [ - ('name', 'qwerty'), - ('description', None), - ('size', None), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - kwargs = { - 'display_name': 'qwerty', - } - self.volumes_mock.update.assert_called_with(self._volume.id, **kwargs) - self.assertIsNone(result) - - def test_volume_set_description(self): - arglist = [ - '--description', - 'new desc', - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', 'new desc'), - ('size', None), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - kwargs = { - 'display_description': 'new desc', - } - self.volumes_mock.update.assert_called_with(self._volume.id, **kwargs) - self.assertIsNone(result) - - def test_volume_set_size(self): - arglist = [ - '--size', - '130', - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', None), - ('size', 130), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - size = 130 - self.volumes_mock.extend.assert_called_with(self._volume.id, size) - self.assertIsNone(result) - - def test_volume_set_size_smaller(self): - self._volume.status = 'available' - arglist = [ - '--size', - '1', - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', None), - ('size', 1), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - - def test_volume_set_size_not_available(self): - self._volume.status = 'error' - arglist = [ - '--size', - '130', - self._volume.display_name, - ] - verifylist = [ - ('name', None), - ('description', None), - ('size', 130), - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - - def test_volume_set_property(self): - arglist = [ - '--no-property', - '--property', - 'myprop=myvalue', - self._volume.display_name, - ] - verifylist = [ - ('read_only', False), - ('read_write', False), - ('name', None), - ('description', None), - ('size', None), - ('no_property', True), - ('property', {'myprop': 'myvalue'}), - ('volume', self._volume.display_name), - ('bootable', False), - ('non_bootable', False), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - metadata = {'myprop': 'myvalue'} - self.volumes_mock.set_metadata.assert_called_with( - self._volume.id, metadata - ) - self.volumes_mock.delete_metadata.assert_called_with( - self._volume.id, self._volume.metadata.keys() - ) - self.volumes_mock.update_readonly_flag.assert_not_called() - self.assertIsNone(result) - - def test_volume_set_bootable(self): - arglist = [ - ['--bootable', self._volume.id], - ['--non-bootable', self._volume.id], - ] - verifylist = [ - [ - ('bootable', True), - ('non_bootable', False), - ('volume', self._volume.id), - ], - [ - ('bootable', False), - ('non_bootable', True), - ('volume', self._volume.id), - ], - ] - for index in range(len(arglist)): - parsed_args = self.check_parser( - self.cmd, arglist[index], verifylist[index] - ) - - self.cmd.take_action(parsed_args) - self.volumes_mock.set_bootable.assert_called_with( - self._volume.id, verifylist[index][0][1] - ) - - def test_volume_set_readonly(self): - arglist = ['--read-only', self._volume.id] - verifylist = [ - ('read_only', True), - ('read_write', False), - ('volume', self._volume.id), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volumes_mock.update_readonly_flag.assert_called_once_with( - self._volume.id, True - ) - self.assertIsNone(result) - - def test_volume_set_read_write(self): - arglist = ['--read-write', self._volume.id] - verifylist = [ - ('read_only', False), - ('read_write', True), - ('volume', self._volume.id), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.volumes_mock.update_readonly_flag.assert_called_once_with( - self._volume.id, False - ) - self.assertIsNone(result) - - -class TestVolumeShow(TestVolume): - columns = ( - 'attachments', - 'availability_zone', - 'bootable', - 'created_at', - 'display_description', - 'id', - 'name', - 'properties', - 'size', - 'snapshot_id', - 'status', - 'type', - ) - - def setUp(self): - super().setUp() - self._volume = volume_fakes.create_one_volume() - self.datalist = ( - self._volume.attachments, - self._volume.availability_zone, - self._volume.bootable, - self._volume.created_at, - self._volume.display_description, - self._volume.id, - self._volume.display_name, - format_columns.DictColumn(self._volume.metadata), - self._volume.size, - self._volume.snapshot_id, - self._volume.status, - self._volume.volume_type, - ) - self.volumes_mock.get.return_value = self._volume - # Get the command object to test - self.cmd = volume.ShowVolume(self.app, None) - - def test_volume_show(self): - arglist = [self._volume.id] - verifylist = [("volume", self._volume.id)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_with(self._volume.id) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_show_backward_compatibility(self): - arglist = [ - '-c', - 'display_name', - self._volume.id, - ] - verifylist = [ - ('columns', ['display_name']), - ('volume', self._volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.get.assert_called_with(self._volume.id) - - self.assertIn('display_name', columns) - self.assertNotIn('name', columns) - self.assertIn(self._volume.display_name, data) - - -class TestVolumeUnset(TestVolume): - _volume = volume_fakes.create_one_volume() - - def setUp(self): - super().setUp() - - self.volumes_mock.get.return_value = self._volume - - self.volumes_mock.delete_metadata.return_value = None - # Get the command object to test - self.cmd = volume.UnsetVolume(self.app, None) - - def test_volume_unset_no_options(self): - arglist = [ - self._volume.display_name, - ] - verifylist = [ - ('property', None), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.assertIsNone(result) - - def test_volume_unset_property(self): - arglist = [ - '--property', - 'myprop', - self._volume.display_name, - ] - verifylist = [ - ('property', ['myprop']), - ('volume', self._volume.display_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.volumes_mock.delete_metadata.assert_called_with( - self._volume.id, ['myprop'] - ) - self.assertIsNone(result) - - -class TestColumns(TestVolume): - def test_attachments_column_without_server_cache(self): - _volume = volume_fakes.create_one_volume() - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] - - col = volume.AttachmentsColumn(_volume.attachments, {}) - self.assertEqual( - f'Attached to {server_id} on {device} ', - col.human_readable(), - ) - self.assertEqual(_volume.attachments, col.machine_readable()) - - def test_attachments_column_with_server_cache(self): - _volume = volume_fakes.create_one_volume() - - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] - fake_server = mock.Mock() - fake_server.name = 'fake-server-name' - server_cache = {server_id: fake_server} - - col = volume.AttachmentsColumn(_volume.attachments, server_cache) - self.assertEqual( - 'Attached to {} on {} '.format('fake-server-name', device), - col.human_readable(), - ) - self.assertEqual(_volume.attachments, col.machine_readable()) diff --git a/openstackclient/tests/unit/volume/v1/test_volume_backup.py b/openstackclient/tests/unit/volume/v1/test_volume_backup.py deleted file mode 100644 index c551d159e..000000000 --- a/openstackclient/tests/unit/volume/v1/test_volume_backup.py +++ /dev/null @@ -1,435 +0,0 @@ -# -# 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. -# - -from unittest import mock -from unittest.mock import call - -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -from openstackclient.volume.v1 import volume_backup - - -class TestBackup(volume_fakes.TestVolumev1): - def setUp(self): - super().setUp() - - self.backups_mock = self.volume_client.backups - self.backups_mock.reset_mock() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - self.restores_mock = self.volume_client.restores - self.restores_mock.reset_mock() - - -class TestBackupCreate(TestBackup): - volume = volume_fakes.create_one_volume() - - columns = ( - 'availability_zone', - 'container', - 'description', - 'id', - 'name', - 'object_count', - 'size', - 'snapshot_id', - 'status', - 'volume_id', - ) - - def setUp(self): - super().setUp() - self.new_backup = volume_fakes.create_one_backup( - attrs={'volume_id': self.volume.id}, - ) - self.data = ( - self.new_backup.availability_zone, - self.new_backup.container, - self.new_backup.description, - self.new_backup.id, - self.new_backup.name, - self.new_backup.object_count, - self.new_backup.size, - self.new_backup.snapshot_id, - self.new_backup.status, - self.new_backup.volume_id, - ) - self.volumes_mock.get.return_value = self.volume - self.backups_mock.create.return_value = self.new_backup - - # Get the command object to test - self.cmd = volume_backup.CreateVolumeBackup(self.app, None) - - def test_backup_create(self): - arglist = [ - "--name", - self.new_backup.name, - "--description", - self.new_backup.description, - "--container", - self.new_backup.container, - self.new_backup.volume_id, - ] - verifylist = [ - ("name", self.new_backup.name), - ("description", self.new_backup.description), - ("container", self.new_backup.container), - ("volume", self.new_backup.volume_id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.backups_mock.create.assert_called_with( - self.new_backup.volume_id, - self.new_backup.container, - self.new_backup.name, - self.new_backup.description, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) - - def test_backup_create_without_name(self): - arglist = [ - "--description", - self.new_backup.description, - "--container", - self.new_backup.container, - self.new_backup.volume_id, - ] - verifylist = [ - ("description", self.new_backup.description), - ("container", self.new_backup.container), - ("volume", self.new_backup.volume_id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - self.backups_mock.create.assert_called_with( - self.new_backup.volume_id, - self.new_backup.container, - None, - self.new_backup.description, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) - - -class TestBackupDelete(TestBackup): - backups = volume_fakes.create_backups(count=2) - - def setUp(self): - super().setUp() - - self.backups_mock.get = volume_fakes.get_backups(self.backups) - self.backups_mock.delete.return_value = None - - # Get the command object to mock - self.cmd = volume_backup.DeleteVolumeBackup(self.app, None) - - def test_backup_delete(self): - arglist = [self.backups[0].id] - verifylist = [("backups", [self.backups[0].id])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - self.backups_mock.delete.assert_called_with(self.backups[0].id) - self.assertIsNone(result) - - def test_delete_multiple_backups(self): - arglist = [] - for b in self.backups: - arglist.append(b.id) - verifylist = [ - ('backups', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - calls = [] - for b in self.backups: - calls.append(call(b.id)) - self.backups_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - def test_delete_multiple_backups_with_exception(self): - arglist = [ - self.backups[0].id, - 'unexist_backup', - ] - verifylist = [ - ('backups', arglist), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - find_mock_result = [self.backups[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 backups failed to delete.', str(e)) - - find_mock.assert_any_call(self.backups_mock, self.backups[0].id) - find_mock.assert_any_call(self.backups_mock, 'unexist_backup') - - self.assertEqual(2, find_mock.call_count) - self.backups_mock.delete.assert_called_once_with( - self.backups[0].id, - ) - - -class TestBackupList(TestBackup): - volume = volume_fakes.create_one_volume() - backups = volume_fakes.create_backups( - attrs={'volume_id': volume.display_name}, - count=3, - ) - - columns = [ - 'ID', - 'Name', - 'Description', - 'Status', - 'Size', - ] - columns_long = columns + [ - 'Availability Zone', - 'Volume', - 'Container', - ] - - data = [] - for b in backups: - data.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - ) - ) - data_long = [] - for b in backups: - data_long.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - b.availability_zone, - volume_backup.VolumeIdColumn(b.volume_id), - b.container, - ) - ) - - def setUp(self): - super().setUp() - - self.volumes_mock.list.return_value = [self.volume] - self.backups_mock.list.return_value = self.backups - self.volumes_mock.get.return_value = self.volume - # Get the command to test - self.cmd = volume_backup.ListVolumeBackup(self.app, None) - - def test_backup_list_without_options(self): - arglist = [] - verifylist = [ - ("long", False), - ("name", None), - ("status", None), - ("volume", None), - ('all_projects', False), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - - search_opts = { - "name": None, - "status": None, - "volume_id": None, - "all_tenants": False, - } - self.volumes_mock.get.assert_not_called() - self.backups_mock.list.assert_called_with( - search_opts=search_opts, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, list(data)) - - def test_backup_list_with_options(self): - arglist = [ - "--long", - "--name", - self.backups[0].name, - "--status", - "error", - "--volume", - self.volume.id, - "--all-projects", - ] - verifylist = [ - ("long", True), - ("name", self.backups[0].name), - ("status", "error"), - ("volume", self.volume.id), - ('all_projects', True), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - - search_opts = { - "name": self.backups[0].name, - "status": "error", - "volume_id": self.volume.id, - "all_tenants": True, - } - self.volumes_mock.get.assert_called_once_with(self.volume.id) - self.backups_mock.list.assert_called_with( - search_opts=search_opts, - ) - self.assertEqual(self.columns_long, columns) - self.assertCountEqual(self.data_long, list(data)) - - -class TestBackupRestore(TestBackup): - volume = volume_fakes.create_one_volume() - backup = volume_fakes.create_one_backup( - attrs={'volume_id': volume.id}, - ) - - def setUp(self): - super().setUp() - - self.backups_mock.get.return_value = self.backup - self.volumes_mock.get.return_value = self.volume - self.restores_mock.restore.return_value = ( - volume_fakes.create_one_volume( - {'id': self.volume['id']}, - ) - ) - # Get the command object to mock - self.cmd = volume_backup.RestoreVolumeBackup(self.app, None) - - def test_backup_restore(self): - arglist = [ - self.backup.id, - ] - verifylist = [ - ("backup", self.backup.id), - ("volume", None), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.restores_mock.restore.assert_called_with(self.backup.id, None) - self.assertIsNotNone(result) - - def test_backup_restore_with_existing_volume(self): - arglist = [ - self.backup.id, - self.backup.volume_id, - ] - verifylist = [ - ("backup", self.backup.id), - ("volume", self.backup.volume_id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - self.restores_mock.restore.assert_called_with( - self.backup.id, - self.backup.volume_id, - ) - self.assertIsNotNone(result) - - def test_backup_restore_with_invalid_volume(self): - arglist = [ - self.backup.id, - "unexist_volume", - ] - verifylist = [ - ("backup", self.backup.id), - ("volume", "unexist_volume"), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object( - utils, - 'find_resource', - side_effect=exceptions.CommandError(), - ): - self.assertRaises( - exceptions.CommandError, - self.cmd.take_action, - parsed_args, - ) - - -class TestBackupShow(TestBackup): - columns = ( - 'availability_zone', - 'container', - 'description', - 'id', - 'name', - 'object_count', - 'size', - 'snapshot_id', - 'status', - 'volume_id', - ) - - def setUp(self): - super().setUp() - self.backup = volume_fakes.create_one_backup() - self.data = ( - self.backup.availability_zone, - self.backup.container, - self.backup.description, - self.backup.id, - self.backup.name, - self.backup.object_count, - self.backup.size, - self.backup.snapshot_id, - self.backup.status, - self.backup.volume_id, - ) - self.backups_mock.get.return_value = self.backup - # Get the command object to test - self.cmd = volume_backup.ShowVolumeBackup(self.app, None) - - def test_backup_show(self): - arglist = [self.backup.id] - verifylist = [("backup", self.backup.id)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.backups_mock.get.assert_called_with(self.backup.id) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) diff --git a/openstackclient/volume/v1/__init__.py b/openstackclient/volume/v1/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/openstackclient/volume/v1/qos_specs.py b/openstackclient/volume/v1/qos_specs.py deleted file mode 100644 index 93f7f48c6..000000000 --- a/openstackclient/volume/v1/qos_specs.py +++ /dev/null @@ -1,376 +0,0 @@ -# Copyright 2015 iWeb Technologies Inc. -# -# 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. -# - -"""Volume v1 QoS action implementations""" - -import logging - -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class AssociateQos(command.Command): - _description = _("Associate a QoS specification to a volume type") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to modify (name or ID)'), - ) - parser.add_argument( - 'volume_type', - metavar='', - help=_('Volume type to associate the QoS (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type - ) - - volume_client.qos_specs.associate(qos_spec.id, volume_type.id) - - -class CreateQos(command.ShowOne): - _description = _("Create new QoS specification") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'name', - metavar='', - help=_('New QoS specification name'), - ) - consumer_choices = ['front-end', 'back-end', 'both'] - parser.add_argument( - '--consumer', - metavar='', - choices=consumer_choices, - default='both', - help=( - _( - 'Consumer of the QoS. Valid consumers: %s ' - "(defaults to 'both')" - ) - % utils.format_list(consumer_choices) - ), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a QoS specification property ' - '(repeat option to set multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - specs = {} - specs.update({'consumer': parsed_args.consumer}) - - if parsed_args.property: - specs.update(parsed_args.property) - - qos_spec = volume_client.qos_specs.create(parsed_args.name, specs) - qos_spec._info.update( - { - 'properties': format_columns.DictColumn( - qos_spec._info.pop('specs') - ) - } - ) - return zip(*sorted(qos_spec._info.items())) - - -class DeleteQos(command.Command): - _description = _("Delete QoS specification") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_specs', - metavar='', - nargs="+", - help=_('QoS specification(s) to delete (name or ID)'), - ) - parser.add_argument( - '--force', - action='store_true', - default=False, - help=_("Allow to delete in-use QoS specification(s)"), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for i in parsed_args.qos_specs: - try: - qos_spec = utils.find_resource(volume_client.qos_specs, i) - volume_client.qos_specs.delete(qos_spec.id, parsed_args.force) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete QoS specification with " - "name or ID '%(qos)s': %(e)s" - ), - {'qos': i, 'e': e}, - ) - - if result > 0: - total = len(parsed_args.qos_specs) - msg = _( - "%(result)s of %(total)s QoS specifications failed to delete." - ) % {'result': result, 'total': total} - raise exceptions.CommandError(msg) - - -class DisassociateQos(command.Command): - _description = _("Disassociate a QoS specification from a volume type") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to modify (name or ID)'), - ) - volume_type_group = parser.add_mutually_exclusive_group() - volume_type_group.add_argument( - '--volume-type', - metavar='', - help=_('Volume type to disassociate the QoS from (name or ID)'), - ) - volume_type_group.add_argument( - '--all', - action='store_true', - default=False, - help=_('Disassociate the QoS from every volume type'), - ) - - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - - if parsed_args.volume_type: - volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type - ) - volume_client.qos_specs.disassociate(qos_spec.id, volume_type.id) - elif parsed_args.all: - volume_client.qos_specs.disassociate_all(qos_spec.id) - - -class ListQos(command.Lister): - _description = _("List QoS specifications") - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_specs_list = volume_client.qos_specs.list() - - for qos in qos_specs_list: - try: - qos_associations = volume_client.qos_specs.get_associations( - qos, - ) - if qos_associations: - associations = [ - association.name for association in qos_associations - ] - qos._info.update({'associations': associations}) - except Exception as ex: - if type(ex).__name__ == 'NotFound': - qos._info.update({'associations': None}) - else: - raise - - display_columns = ( - 'ID', - 'Name', - 'Consumer', - 'Associations', - 'Properties', - ) - columns = ('ID', 'Name', 'Consumer', 'Associations', 'Specs') - return ( - display_columns, - ( - utils.get_dict_properties( - s._info, - columns, - formatters={ - 'Specs': format_columns.DictColumn, - 'Associations': format_columns.ListColumn, - }, - ) - for s in qos_specs_list - ), - ) - - -class SetQos(command.Command): - _description = _("Set QoS specification properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to modify (name or ID)'), - ) - parser.add_argument( - '--no-property', - dest='no_property', - action='store_true', - help=_( - 'Remove all properties from ' - '(specify both --no-property and --property to remove the ' - 'current properties before setting new properties)' - ), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Property to add or modify for this QoS specification ' - '(repeat option to set multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - - result = 0 - if parsed_args.no_property: - try: - key_list = list(qos_spec._info['specs'].keys()) - volume_client.qos_specs.unset_keys(qos_spec.id, key_list) - except Exception as e: - LOG.error(_("Failed to clean qos properties: %s"), e) - result += 1 - - if parsed_args.property: - try: - volume_client.qos_specs.set_keys( - qos_spec.id, - parsed_args.property, - ) - except Exception as e: - LOG.error(_("Failed to set qos property: %s"), e) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("One or more of the set operations failed") - ) - - -class ShowQos(command.ShowOne): - _description = _("Display QoS specification details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to display (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - - qos_associations = volume_client.qos_specs.get_associations(qos_spec) - if qos_associations: - associations = [ - association.name for association in qos_associations - ] - qos_spec._info.update( - {'associations': format_columns.ListColumn(associations)} - ) - qos_spec._info.update( - { - 'properties': format_columns.DictColumn( - qos_spec._info.pop('specs') - ) - } - ) - - return zip(*sorted(qos_spec._info.items())) - - -class UnsetQos(command.Command): - _description = _("Unset QoS specification properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'qos_spec', - metavar='', - help=_('QoS specification to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action='append', - help=_( - 'Property to remove from the QoS specification. ' - '(repeat option to unset multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - qos_spec = utils.find_resource( - volume_client.qos_specs, parsed_args.qos_spec - ) - - if parsed_args.property: - volume_client.qos_specs.unset_keys( - qos_spec.id, parsed_args.property - ) diff --git a/openstackclient/volume/v1/service.py b/openstackclient/volume/v1/service.py deleted file mode 100644 index 2a33fc0b9..000000000 --- a/openstackclient/volume/v1/service.py +++ /dev/null @@ -1,136 +0,0 @@ -# -# 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. -# - -"""Service action implementations""" - -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -class ListService(command.Lister): - _description = _("List service command") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - "--host", - metavar="", - help=_("List services on specified host (name only)"), - ) - parser.add_argument( - "--service", - metavar="", - help=_("List only specified service (name only)"), - ) - parser.add_argument( - "--long", - action="store_true", - default=False, - help=_("List additional fields in output"), - ) - return parser - - def take_action(self, parsed_args): - service_client = self.app.client_manager.volume - - if parsed_args.long: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - "Disabled Reason", - ] - else: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - ] - - data = service_client.services.list( - parsed_args.host, parsed_args.service - ) - return ( - columns, - ( - utils.get_item_properties( - s, - columns, - ) - for s in data - ), - ) - - -class SetService(command.Command): - _description = _("Set volume service properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument("host", metavar="", help=_("Name of host")) - parser.add_argument( - "service", - metavar="", - help=_("Name of service (Binary name)"), - ) - enabled_group = parser.add_mutually_exclusive_group() - enabled_group.add_argument( - "--enable", action="store_true", help=_("Enable volume service") - ) - enabled_group.add_argument( - "--disable", action="store_true", help=_("Disable volume service") - ) - parser.add_argument( - "--disable-reason", - metavar="", - help=_( - "Reason for disabling the service " - "(should be used with --disable option)" - ), - ) - return parser - - def take_action(self, parsed_args): - if parsed_args.disable_reason and not parsed_args.disable: - msg = _( - "Cannot specify option --disable-reason without " - "--disable specified." - ) - raise exceptions.CommandError(msg) - - service_client = self.app.client_manager.volume - if parsed_args.enable: - service_client.services.enable( - parsed_args.host, parsed_args.service - ) - if parsed_args.disable: - if parsed_args.disable_reason: - service_client.services.disable_log_reason( - parsed_args.host, - parsed_args.service, - parsed_args.disable_reason, - ) - else: - service_client.services.disable( - parsed_args.host, parsed_args.service - ) diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py deleted file mode 100644 index 1634624c0..000000000 --- a/openstackclient/volume/v1/volume.py +++ /dev/null @@ -1,727 +0,0 @@ -# Copyright 2012-2013 OpenStack Foundation -# -# 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. -# - -"""Volume v1 Volume action implementations""" - -import argparse -import functools -import logging - -from cliff import columns as cliff_columns -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.common import pagination -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class AttachmentsColumn(cliff_columns.FormattableColumn): - """Formattable column for attachments column. - - Unlike the parent FormattableColumn class, the initializer of the - class takes server_cache as the second argument. - osc_lib.utils.get_item_properties instantiate cliff FormattableColumn - object with a single parameter "column value", so you need to pass - a partially initialized class like - ``functools.partial(AttachmentsColumn, server_cache)``. - """ - - def __init__(self, value, server_cache=None): - super().__init__(value) - self._server_cache = server_cache or {} - - def human_readable(self): - """Return a formatted string of a volume's attached instances - - :rtype: a string of formatted instances - """ - - msg = '' - for attachment in self._value: - server = attachment['server_id'] - if server in self._server_cache.keys(): - server = self._server_cache[server].name - device = attachment['device'] - msg += f'Attached to {server} on {device} ' - return msg - - -def _check_size_arg(args): - """Check whether --size option is required or not. - - Require size parameter only in case when snapshot or source - volume is not specified. - """ - - if (args.snapshot or args.source) is None and args.size is None: - msg = _( - "--size is a required option if snapshot " - "or source volume is not specified." - ) - raise exceptions.CommandError(msg) - - -class CreateVolume(command.ShowOne): - _description = _("Create new volume") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'name', - metavar='', - help=_('Volume name'), - ) - parser.add_argument( - '--size', - metavar='', - type=int, - help=_( - "Volume size in GB (Required unless --snapshot or " - "--source is specified)" - ), - ) - parser.add_argument( - '--type', - metavar='', - help=_("Set the type of volume"), - ) - source_group = parser.add_mutually_exclusive_group() - source_group.add_argument( - '--image', - metavar='', - help=_('Use as source of volume (name or ID)'), - ) - source_group.add_argument( - '--snapshot', - metavar='', - help=_('Use as source of volume (name or ID)'), - ) - source_group.add_argument( - '--snapshot-id', - metavar='', - help=argparse.SUPPRESS, - ) - source_group.add_argument( - '--source', - metavar='', - help=_('Volume to clone (name or ID)'), - ) - parser.add_argument( - '--description', - metavar='', - help=_('Volume description'), - ) - parser.add_argument( - '--user', - metavar='', - help=_('Specify an alternate user (name or ID)'), - ) - parser.add_argument( - '--project', - metavar='', - help=_('Specify an alternate project (name or ID)'), - ) - parser.add_argument( - '--availability-zone', - metavar='', - help=_('Create volume in '), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a property on this volume ' - '(repeat option to set multiple properties)' - ), - ) - bootable_group = parser.add_mutually_exclusive_group() - bootable_group.add_argument( - "--bootable", - action="store_true", - help=_("Mark volume as bootable"), - ) - bootable_group.add_argument( - "--non-bootable", - action="store_true", - help=_("Mark volume as non-bootable (default)"), - ) - readonly_group = parser.add_mutually_exclusive_group() - readonly_group.add_argument( - "--read-only", - action="store_true", - help=_("Set volume to read-only access mode"), - ) - readonly_group.add_argument( - "--read-write", - action="store_true", - help=_("Set volume to read-write access mode (default)"), - ) - - return parser - - def take_action(self, parsed_args): - _check_size_arg(parsed_args) - identity_client = self.app.client_manager.identity - image_client = self.app.client_manager.image - volume_client = self.app.client_manager.volume - - source_volume = None - if parsed_args.source: - source_volume = utils.find_resource( - volume_client.volumes, - parsed_args.source, - ).id - - project = None - if parsed_args.project: - project = utils.find_resource( - identity_client.tenants, - parsed_args.project, - ).id - - user = None - if parsed_args.user: - user = utils.find_resource( - identity_client.users, - parsed_args.user, - ).id - - image = None - if parsed_args.image: - image = image_client.find_image( - parsed_args.image, - ignore_missing=False, - ).id - - snapshot = parsed_args.snapshot or parsed_args.snapshot_id - - volume = volume_client.volumes.create( - parsed_args.size, - snapshot, - source_volume, - parsed_args.name, - parsed_args.description, - parsed_args.type, - user, - project, - parsed_args.availability_zone, - parsed_args.property, - image, - ) - - if parsed_args.bootable or parsed_args.non_bootable: - try: - if utils.wait_for_status( - volume_client.volumes.get, - volume.id, - success_status=['available'], - error_status=['error'], - sleep_time=1, - ): - volume_client.volumes.set_bootable( - volume.id, parsed_args.bootable - ) - else: - msg = _( - "Volume status is not available for setting boot state" - ) - raise exceptions.CommandError(msg) - except Exception as e: - LOG.error(_("Failed to set volume bootable property: %s"), e) - if parsed_args.read_only or parsed_args.read_write: - try: - if utils.wait_for_status( - volume_client.volumes.get, - volume.id, - success_status=['available'], - error_status=['error'], - sleep_time=1, - ): - volume_client.volumes.update_readonly_flag( - volume.id, parsed_args.read_only - ) - else: - msg = _( - "Volume status is not available for setting it" - "read only." - ) - raise exceptions.CommandError(msg) - except Exception as e: - LOG.error( - _("Failed to set volume read-only access mode flag: %s"), - e, - ) - - # Map 'metadata' column to 'properties' - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - }, - ) - # Replace "display_name" by "name", keep consistent in v1 and v2 - if 'display_name' in volume._info: - volume._info.update({'name': volume._info.pop('display_name')}) - volume_info = utils.backward_compat_col_showone( - volume._info, parsed_args.columns, {'display_name': 'name'} - ) - - return zip(*sorted(volume_info.items())) - - -class DeleteVolume(command.Command): - _description = _("Delete volume(s)") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volumes', - metavar='', - nargs="+", - help=_('Volume(s) to delete (name or ID)'), - ) - parser.add_argument( - '--force', - action='store_true', - default=False, - help=_( - 'Attempt forced removal of volume(s), regardless of state ' - '(defaults to False)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for i in parsed_args.volumes: - try: - volume_obj = utils.find_resource(volume_client.volumes, i) - if parsed_args.force: - volume_client.volumes.force_delete(volume_obj.id) - else: - volume_client.volumes.delete(volume_obj.id) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete volume with " - "name or ID '%(volume)s': %(e)s" - ), - {'volume': i, 'e': e}, - ) - - if result > 0: - total = len(parsed_args.volumes) - msg = _("%(result)s of %(total)s volumes failed to delete.") % { - 'result': result, - 'total': total, - } - raise exceptions.CommandError(msg) - - -class ListVolume(command.Lister): - _description = _("List volumes") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--name', - metavar='', - help=_('Filter results by volume name'), - ) - parser.add_argument( - '--status', - metavar='', - help=_('Filter results by status'), - ) - parser.add_argument( - '--all-projects', - action='store_true', - default=False, - help=_('Include all projects (admin only)'), - ) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_('List additional fields in output'), - ) - pagination.add_offset_pagination_option_to_parser(parser) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - - if parsed_args.long: - columns: tuple[str, ...] = ( - 'ID', - 'Display Name', - 'Status', - 'Size', - 'Volume Type', - 'Bootable', - 'Attachments', - 'Metadata', - ) - column_headers: tuple[str, ...] = ( - 'ID', - 'Name', - 'Status', - 'Size', - 'Type', - 'Bootable', - 'Attached to', - 'Properties', - ) - else: - columns = ( - 'ID', - 'Display Name', - 'Status', - 'Size', - 'Attachments', - ) - column_headers = ( - 'ID', - 'Name', - 'Status', - 'Size', - 'Attached to', - ) - - # Cache the server list - server_cache = {} - try: - compute_client = self.app.client_manager.sdk_connection.compute - for s in compute_client.servers(): - server_cache[s.id] = s - except Exception: # noqa: S110 - # Just forget it if there's any trouble - pass - AttachmentsColumnWithCache = functools.partial( - AttachmentsColumn, server_cache=server_cache - ) - - search_opts = { - 'all_tenants': parsed_args.all_projects, - 'display_name': parsed_args.name, - 'status': parsed_args.status, - } - - if parsed_args.offset: - search_opts['offset'] = parsed_args.offset - - data = volume_client.volumes.list( - search_opts=search_opts, - limit=parsed_args.limit, - ) - column_headers = utils.backward_compat_col_lister( - column_headers, parsed_args.columns, {'Display Name': 'Name'} - ) - - return ( - column_headers, - ( - utils.get_item_properties( - s, - columns, - formatters={ - 'Metadata': format_columns.DictColumn, - 'Attachments': AttachmentsColumnWithCache, - }, - ) - for s in data - ), - ) - - -class MigrateVolume(command.Command): - _description = _("Migrate volume to a new host") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar="", - help=_("Volume to migrate (name or ID)"), - ) - parser.add_argument( - '--host', - metavar="", - 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): - _description = _("Set volume properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar='', - help=_('Volume to modify (name or ID)'), - ) - parser.add_argument( - '--name', - metavar='', - help=_('New volume name'), - ) - parser.add_argument( - '--description', - metavar='', - help=_('New volume description'), - ) - parser.add_argument( - '--size', - metavar='', - type=int, - help=_('Extend volume size in GB'), - ) - parser.add_argument( - "--no-property", - dest="no_property", - action="store_true", - help=_( - "Remove all properties from " - "(specify both --no-property and --property to " - "remove the current properties before setting " - "new properties.)" - ), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a property on this volume ' - '(repeat option to set multiple properties)' - ), - ) - bootable_group = parser.add_mutually_exclusive_group() - bootable_group.add_argument( - "--bootable", - action="store_true", - help=_("Mark volume as bootable"), - ) - bootable_group.add_argument( - "--non-bootable", - action="store_true", - help=_("Mark volume as non-bootable"), - ) - readonly_group = parser.add_mutually_exclusive_group() - readonly_group.add_argument( - "--read-only", - action="store_true", - help=_("Set volume to read-only access mode"), - ) - readonly_group.add_argument( - "--read-write", - action="store_true", - help=_("Set volume to read-write access mode"), - ) - 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) - - result = 0 - if parsed_args.size: - try: - if volume.status != 'available': - msg = ( - _( - "Volume is in %s state, it must be available " - "before size can be extended" - ) - % volume.status - ) - raise exceptions.CommandError(msg) - if parsed_args.size <= volume.size: - msg = ( - _("New size must be greater than %s GB") % volume.size - ) - raise exceptions.CommandError(msg) - volume_client.volumes.extend(volume.id, parsed_args.size) - except Exception as e: - LOG.error(_("Failed to set volume size: %s"), e) - result += 1 - - if parsed_args.no_property: - try: - volume_client.volumes.delete_metadata( - volume.id, volume.metadata.keys() - ) - except Exception as e: - LOG.error(_("Failed to clean volume properties: %s"), e) - result += 1 - - if parsed_args.property: - try: - volume_client.volumes.set_metadata( - volume.id, parsed_args.property - ) - except Exception as e: - LOG.error(_("Failed to set volume property: %s"), e) - result += 1 - if parsed_args.bootable or parsed_args.non_bootable: - try: - volume_client.volumes.set_bootable( - volume.id, parsed_args.bootable - ) - except Exception as e: - LOG.error(_("Failed to set volume bootable property: %s"), e) - result += 1 - if parsed_args.read_only or parsed_args.read_write: - try: - volume_client.volumes.update_readonly_flag( - volume.id, parsed_args.read_only - ) - except Exception as e: - LOG.error( - _("Failed to set volume read-only access mode flag: %s"), - e, - ) - result += 1 - kwargs = {} - if parsed_args.name: - kwargs['display_name'] = parsed_args.name - if parsed_args.description: - kwargs['display_description'] = parsed_args.description - if kwargs: - try: - volume_client.volumes.update(volume.id, **kwargs) - except Exception as e: - LOG.error( - _( - "Failed to update volume display name " - "or display description: %s" - ), - e, - ) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("One or more of the set operations failed") - ) - - -class ShowVolume(command.ShowOne): - _description = _("Show volume details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar='', - help=_('Volume to display (name or ID)'), - ) - 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) - # Map 'metadata' column to 'properties' - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - }, - ) - if 'os-vol-tenant-attr:tenant_id' in volume._info: - volume._info.update( - { - 'project_id': volume._info.pop( - 'os-vol-tenant-attr:tenant_id' - ) - } - ) - # Replace "display_name" by "name", keep consistent in v1 and v2 - if 'display_name' in volume._info: - volume._info.update({'name': volume._info.pop('display_name')}) - - volume_info = utils.backward_compat_col_showone( - volume._info, parsed_args.columns, {'display_name': 'name'} - ) - - return zip(*sorted(volume_info.items())) - - -class UnsetVolume(command.Command): - _description = _("Unset volume properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar='', - help=_('Volume to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action='append', - help=_( - 'Remove a property from volume ' - '(repeat option to remove multiple properties)' - ), - ) - 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) - - if parsed_args.property: - volume_client.volumes.delete_metadata( - volume.id, - parsed_args.property, - ) diff --git a/openstackclient/volume/v1/volume_backup.py b/openstackclient/volume/v1/volume_backup.py deleted file mode 100644 index 4819609d3..000000000 --- a/openstackclient/volume/v1/volume_backup.py +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright 2012-2013 OpenStack Foundation -# -# 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. -# - -"""Volume v1 Backup action implementations""" - -import copy -import functools -import logging - -from cliff import columns as cliff_columns -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class VolumeIdColumn(cliff_columns.FormattableColumn): - """Formattable column for volume ID column. - - Unlike the parent FormattableColumn class, the initializer of the - class takes volume_cache as the second argument. - osc_lib.utils.get_item_properties instantiate cliff FormattableColumn - object with a single parameter "column value", so you need to pass - a partially initialized class like - ``functools.partial(VolumeIdColumn, volume_cache)``. - """ - - def __init__(self, value, volume_cache=None): - super().__init__(value) - self._volume_cache = volume_cache or {} - - def human_readable(self): - """Return a volume name if available - - :rtype: either the volume ID or name - """ - volume_id = self._value - volume = volume_id - if volume_id in self._volume_cache.keys(): - volume = self._volume_cache[volume_id].display_name - return volume - - -class CreateVolumeBackup(command.ShowOne): - _description = _("Create new volume backup") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume', - metavar='', - help=_('Volume to backup (name or ID)'), - ) - parser.add_argument( - '--container', - metavar='', - required=False, - help=_('Optional backup container name'), - ) - parser.add_argument( - '--name', - metavar='', - help=_('Name of the backup'), - ) - parser.add_argument( - '--description', - metavar='', - help=_('Description of the backup'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_id = utils.find_resource( - volume_client.volumes, parsed_args.volume - ).id - backup = volume_client.backups.create( - volume_id, - parsed_args.container, - parsed_args.name, - parsed_args.description, - ) - - backup._info.pop('links') - return zip(*sorted(backup._info.items())) - - -class DeleteVolumeBackup(command.Command): - _description = _("Delete volume backup(s)") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'backups', - metavar='', - nargs="+", - help=_('Backup(s) to delete (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for i in parsed_args.backups: - try: - backup_id = utils.find_resource(volume_client.backups, i).id - volume_client.backups.delete(backup_id) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete backup with " - "name or ID '%(backup)s': %(e)s" - ), - {'backup': i, 'e': e}, - ) - - if result > 0: - total = len(parsed_args.backups) - msg = _("%(result)s of %(total)s backups failed to delete.") % { - 'result': result, - 'total': total, - } - raise exceptions.CommandError(msg) - - -class ListVolumeBackup(command.Lister): - _description = _("List volume backups") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_('List additional fields in output'), - ) - parser.add_argument( - "--name", - metavar="", - help=_("Filters results by the backup name"), - ) - parser.add_argument( - "--status", - metavar="", - choices=[ - 'creating', - 'available', - 'deleting', - 'error', - 'restoring', - 'error_restoring', - ], - help=_( - "Filters results by the backup status " - "('creating', 'available', 'deleting', " - "'error', 'restoring' or 'error_restoring')" - ), - ) - parser.add_argument( - "--volume", - metavar="", - help=_( - "Filters results by the volume which they backup (name or ID)" - ), - ) - parser.add_argument( - '--all-projects', - action='store_true', - default=False, - help=_('Include all projects (admin only)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - - if parsed_args.long: - columns = [ - 'ID', - 'Name', - 'Description', - 'Status', - 'Size', - 'Availability Zone', - 'Volume ID', - 'Container', - ] - column_headers = copy.deepcopy(columns) - column_headers[6] = 'Volume' - else: - columns = ['ID', 'Name', 'Description', 'Status', 'Size'] - column_headers = columns - - # Cache the volume list - volume_cache = {} - try: - for s in volume_client.volumes.list(): - volume_cache[s.id] = s - except Exception: # noqa: S110 - # Just forget it if there's any trouble - pass - VolumeIdColumnWithCache = functools.partial( - VolumeIdColumn, volume_cache=volume_cache - ) - - filter_volume_id = None - if parsed_args.volume: - filter_volume_id = utils.find_resource( - volume_client.volumes, parsed_args.volume - ).id - search_opts = { - 'name': parsed_args.name, - 'status': parsed_args.status, - 'volume_id': filter_volume_id, - 'all_tenants': parsed_args.all_projects, - } - data = volume_client.backups.list( - search_opts=search_opts, - ) - - return ( - column_headers, - ( - utils.get_item_properties( - s, - columns, - formatters={'Volume ID': VolumeIdColumnWithCache}, - ) - for s in data - ), - ) - - -class RestoreVolumeBackup(command.Command): - _description = _("Restore volume backup") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'backup', - metavar='', - help=_('Backup to restore (name or ID)'), - ) - parser.add_argument( - 'volume', - metavar='', - nargs='?', - help=_('Volume to restore to (name or ID) (default to None)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - backup = utils.find_resource( - volume_client.backups, - parsed_args.backup, - ) - volume_id = None - if parsed_args.volume is not None: - volume_id = utils.find_resource( - volume_client.volumes, - parsed_args.volume, - ).id - return volume_client.restores.restore(backup.id, volume_id) - - -class ShowVolumeBackup(command.ShowOne): - _description = _("Display volume backup details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'backup', - metavar='', - help=_('Backup to display (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - backup = utils.find_resource(volume_client.backups, parsed_args.backup) - backup._info.pop('links') - return zip(*sorted(backup._info.items())) diff --git a/openstackclient/volume/v1/volume_snapshot.py b/openstackclient/volume/v1/volume_snapshot.py deleted file mode 100644 index 1d9fb86b1..000000000 --- a/openstackclient/volume/v1/volume_snapshot.py +++ /dev/null @@ -1,432 +0,0 @@ -# Copyright 2012-2013 OpenStack Foundation -# -# 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. -# - -"""Volume v1 Snapshot action implementations""" - -import copy -import functools -import logging - -from cliff import columns as cliff_columns -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class VolumeIdColumn(cliff_columns.FormattableColumn): - """Formattable column for volume ID column. - - Unlike the parent FormattableColumn class, the initializer of the - class takes volume_cache as the second argument. - osc_lib.utils.get_item_properties instantiate cliff FormattableColumn - object with a single parameter "column value", so you need to pass - a partially initialized class like - ``functools.partial(VolumeIdColumn, volume_cache)``. - """ - - def __init__(self, value, volume_cache=None): - super().__init__(value) - self._volume_cache = volume_cache or {} - - def human_readable(self): - """Return a volume name if available - - :rtype: either the volume ID or name - """ - volume_id = self._value - volume = volume_id - if volume_id in self._volume_cache.keys(): - volume = self._volume_cache[volume_id].display_name - return volume - - -class CreateVolumeSnapshot(command.ShowOne): - _description = _("Create new volume snapshot") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshot_name', - metavar='', - help=_('Name of the new snapshot'), - ) - parser.add_argument( - '--volume', - metavar='', - help=_( - 'Volume to snapshot (name or ID) (default is )' - ), - ) - parser.add_argument( - '--description', - metavar='', - help=_('Description of the snapshot'), - ) - parser.add_argument( - '--force', - dest='force', - action='store_true', - default=False, - help=_( - 'Create a snapshot attached to an instance. Default is False' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume = parsed_args.volume - if not parsed_args.volume: - volume = parsed_args.snapshot_name - volume_id = utils.find_resource(volume_client.volumes, volume).id - snapshot = volume_client.volume_snapshots.create( - volume_id, - parsed_args.force, - parsed_args.snapshot_name, - parsed_args.description, - ) - - snapshot._info.update( - { - 'properties': format_columns.DictColumn( - snapshot._info.pop('metadata') - ) - } - ) - - return zip(*sorted(snapshot._info.items())) - - -class DeleteVolumeSnapshot(command.Command): - _description = _("Delete volume snapshot(s)") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshots', - metavar='', - nargs="+", - help=_('Snapshot(s) to delete (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for i in parsed_args.snapshots: - try: - snapshot_id = utils.find_resource( - volume_client.volume_snapshots, i - ).id - volume_client.volume_snapshots.delete(snapshot_id) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete snapshot with " - "name or ID '%(snapshot)s': %(e)s" - ), - {'snapshot': i, 'e': e}, - ) - - if result > 0: - total = len(parsed_args.snapshots) - msg = _("%(result)s of %(total)s snapshots failed to delete.") % { - 'result': result, - 'total': total, - } - raise exceptions.CommandError(msg) - - -class ListVolumeSnapshot(command.Lister): - _description = _("List volume snapshots") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--all-projects', - action='store_true', - default=False, - help=_('Include all projects (admin only)'), - ) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_('List additional fields in output'), - ) - parser.add_argument( - '--name', - metavar='', - default=None, - help=_('Filters results by a name.'), - ) - parser.add_argument( - '--status', - metavar='', - choices=[ - 'available', - 'error', - 'creating', - 'deleting', - 'error_deleting', - ], - help=_( - "Filters results by a status. " - "('available', 'error', 'creating', 'deleting'" - " or 'error_deleting')" - ), - ) - parser.add_argument( - '--volume', - metavar='', - default=None, - help=_('Filters results by a volume (name or ID).'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - - if parsed_args.long: - columns = [ - 'ID', - 'Display Name', - 'Display Description', - 'Status', - 'Size', - 'Created At', - 'Volume ID', - 'Metadata', - ] - column_headers = copy.deepcopy(columns) - column_headers[6] = 'Volume' - column_headers[7] = 'Properties' - else: - columns = [ - 'ID', - 'Display Name', - 'Display Description', - 'Status', - 'Size', - ] - column_headers = copy.deepcopy(columns) - - # Always update Name and Description - column_headers[1] = 'Name' - column_headers[2] = 'Description' - - # Cache the volume list - volume_cache = {} - try: - for s in volume_client.volumes.list(): - volume_cache[s.id] = s - except Exception: # noqa: S110 - # Just forget it if there's any trouble - pass - VolumeIdColumnWithCache = functools.partial( - VolumeIdColumn, volume_cache=volume_cache - ) - - volume_id = None - if parsed_args.volume: - volume_id = utils.find_resource( - volume_client.volumes, parsed_args.volume - ).id - - search_opts = { - 'all_tenants': parsed_args.all_projects, - 'display_name': parsed_args.name, - 'status': parsed_args.status, - 'volume_id': volume_id, - } - - data = volume_client.volume_snapshots.list(search_opts=search_opts) - return ( - column_headers, - ( - utils.get_item_properties( - s, - columns, - formatters={ - 'Metadata': format_columns.DictColumn, - 'Volume ID': VolumeIdColumnWithCache, - }, - ) - for s in data - ), - ) - - -class SetVolumeSnapshot(command.Command): - _description = _("Set volume snapshot properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshot', - metavar='', - help=_('Snapshot to modify (name or ID)'), - ) - parser.add_argument( - '--name', metavar='', help=_('New snapshot name') - ) - parser.add_argument( - '--description', - metavar='', - help=_('New snapshot description'), - ) - parser.add_argument( - "--no-property", - dest="no_property", - action="store_true", - help=_( - "Remove all properties from " - "(specify both --no-property and --property to " - "remove the current properties before setting " - "new properties.)" - ), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Property to add/change for this snapshot ' - '(repeat option to set multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot - ) - - result = 0 - if parsed_args.no_property: - try: - key_list = snapshot.metadata.keys() - volume_client.volume_snapshots.delete_metadata( - snapshot.id, - list(key_list), - ) - except Exception as e: - LOG.error(_("Failed to clean snapshot properties: %s"), e) - result += 1 - - if parsed_args.property: - try: - volume_client.volume_snapshots.set_metadata( - snapshot.id, parsed_args.property - ) - except Exception as e: - LOG.error(_("Failed to set snapshot property: %s"), e) - result += 1 - - kwargs = {} - if parsed_args.name: - kwargs['display_name'] = parsed_args.name - if parsed_args.description: - kwargs['display_description'] = parsed_args.description - if kwargs: - try: - snapshot.update(**kwargs) - except Exception as e: - LOG.error( - _( - "Failed to update snapshot display name " - "or display description: %s" - ), - e, - ) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("One or more of the set operations failed") - ) - - -class ShowVolumeSnapshot(command.ShowOne): - _description = _("Display volume snapshot details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshot', - metavar='', - help=_('Snapshot to display (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot - ) - - snapshot._info.update( - { - 'properties': format_columns.DictColumn( - snapshot._info.pop('metadata') - ) - } - ) - - return zip(*sorted(snapshot._info.items())) - - -class UnsetVolumeSnapshot(command.Command): - _description = _("Unset volume snapshot properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshot', - metavar='', - help=_('Snapshot to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action='append', - help=_( - 'Property to remove from snapshot ' - '(repeat option to remove multiple properties)' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot - ) - - if parsed_args.property: - volume_client.volume_snapshots.delete_metadata( - snapshot.id, - parsed_args.property, - ) diff --git a/openstackclient/volume/v1/volume_transfer_request.py b/openstackclient/volume/v1/volume_transfer_request.py deleted file mode 100644 index d82584dc7..000000000 --- a/openstackclient/volume/v1/volume_transfer_request.py +++ /dev/null @@ -1,200 +0,0 @@ -# -# 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. -# - -"""Volume v1 transfer action implementations""" - -import logging - -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class AcceptTransferRequest(command.ShowOne): - _description = _("Accept volume transfer request.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'transfer_request', - metavar="", - help=_('Volume transfer request to accept (ID only)'), - ) - parser.add_argument( - '--auth-key', - metavar="", - help=_('Volume transfer request authentication key'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - - try: - transfer_request_id = utils.find_resource( - volume_client.transfers, parsed_args.transfer_request - ).id - except exceptions.CommandError: - # Non-admin users will fail to lookup name -> ID so we just - # move on and attempt with the user-supplied information - transfer_request_id = parsed_args.transfer_request - - if not parsed_args.auth_key: - msg = _("argument --auth-key is required") - raise exceptions.CommandError(msg) - - transfer_accept = volume_client.transfers.accept( - transfer_request_id, - parsed_args.auth_key, - ) - transfer_accept._info.pop("links", None) - - return zip(*sorted(transfer_accept._info.items())) - - -class CreateTransferRequest(command.ShowOne): - _description = _("Create volume transfer request.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--name', - metavar="", - help=_('New transfer request name (default to None)'), - ) - parser.add_argument( - 'volume', - metavar="", - help=_('Volume to transfer (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_id = utils.find_resource( - volume_client.volumes, - parsed_args.volume, - ).id - volume_transfer_request = volume_client.transfers.create( - volume_id, - parsed_args.name, - ) - volume_transfer_request._info.pop("links", None) - - return zip(*sorted(volume_transfer_request._info.items())) - - -class DeleteTransferRequest(command.Command): - _description = _("Delete volume transfer request(s).") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'transfer_request', - metavar="", - 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 ListTransferRequest(command.Lister): - _description = _("Lists all volume transfer requests.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--all-projects', - dest='all_projects', - action="store_true", - default=False, - help=_('Include all projects (admin only)'), - ) - return parser - - def take_action(self, parsed_args): - columns = ['ID', 'Name', 'Volume ID'] - column_headers = ['ID', 'Name', 'Volume'] - - volume_client = self.app.client_manager.volume - - volume_transfer_result = volume_client.transfers.list( - detailed=True, - search_opts={'all_tenants': parsed_args.all_projects}, - ) - - return ( - column_headers, - ( - utils.get_item_properties(s, columns) - for s in volume_transfer_result - ), - ) - - -class ShowTransferRequest(command.ShowOne): - _description = _("Show volume transfer request details.") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'transfer_request', - metavar="", - help=_('Volume transfer request to display (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_transfer_request = utils.find_resource( - volume_client.transfers, - parsed_args.transfer_request, - ) - volume_transfer_request._info.pop("links", None) - - return zip(*sorted(volume_transfer_request._info.items())) diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py deleted file mode 100644 index 5bd1cfdc9..000000000 --- a/openstackclient/volume/v1/volume_type.py +++ /dev/null @@ -1,519 +0,0 @@ -# Copyright 2012-2013 OpenStack Foundation -# -# 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. -# - -"""Volume v1 Type action implementations""" - -import functools -import logging - -from cliff import columns as cliff_columns -from osc_lib.cli import format_columns -from osc_lib.cli import parseractions -from osc_lib.command import command -from osc_lib import exceptions -from osc_lib import utils - -from openstackclient.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class EncryptionInfoColumn(cliff_columns.FormattableColumn): - """Formattable column for encryption info column. - - Unlike the parent FormattableColumn class, the initializer of the - class takes encryption_data as the second argument. - osc_lib.utils.get_item_properties instantiate cliff FormattableColumn - object with a single parameter "column value", so you need to pass - a partially initialized class like - ``functools.partial(EncryptionInfoColumn encryption_data)``. - """ - - def __init__(self, value, encryption_data=None): - super().__init__(value) - self._encryption_data = encryption_data or {} - - def _get_encryption_info(self): - type_id = self._value - return self._encryption_data.get(type_id) - - def human_readable(self): - encryption_info = self._get_encryption_info() - if encryption_info: - return utils.format_dict(encryption_info) - else: - return '-' - - def machine_readable(self): - return self._get_encryption_info() - - -def _create_encryption_type(volume_client, volume_type, parsed_args): - if not parsed_args.encryption_provider: - msg = _( - "'--encryption-provider' should be specified while " - "creating a new encryption type" - ) - raise exceptions.CommandError(msg) - # set the default of control location while creating - control_location = 'front-end' - if parsed_args.encryption_control_location: - control_location = parsed_args.encryption_control_location - body = { - 'provider': parsed_args.encryption_provider, - 'cipher': parsed_args.encryption_cipher, - 'key_size': parsed_args.encryption_key_size, - 'control_location': control_location, - } - encryption = volume_client.volume_encryption_types.create( - volume_type, body - ) - return encryption - - -class CreateVolumeType(command.ShowOne): - _description = _("Create new volume type") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'name', - metavar='', - help=_('Volume type name'), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a property on this volume type ' - '(repeat option to set multiple properties)' - ), - ) - # TODO(Huanxuan Ao): Add choices for each "--encryption-*" option. - parser.add_argument( - '--encryption-provider', - metavar='', - help=_( - 'Set the encryption provider format for ' - 'this volume type (e.g "luks" or "plain") (admin only) ' - '(This option is required when setting encryption type ' - 'of a volume. Consider using other encryption options ' - 'such as: "--encryption-cipher", "--encryption-key-size" ' - 'and "--encryption-control-location")' - ), - ) - parser.add_argument( - '--encryption-cipher', - metavar='', - help=_( - 'Set the encryption algorithm or mode for this ' - 'volume type (e.g "aes-xts-plain64") (admin only)' - ), - ) - parser.add_argument( - '--encryption-key-size', - metavar='', - type=int, - help=_( - 'Set the size of the encryption key of this ' - 'volume type (e.g "128" or "256") (admin only)' - ), - ) - parser.add_argument( - '--encryption-control-location', - metavar='', - choices=['front-end', 'back-end'], - help=_( - 'Set the notional service where the encryption is ' - 'performed ("front-end" or "back-end") (admin only) ' - '(The default value for this option is "front-end" ' - 'when setting encryption type of a volume. Consider ' - 'using other encryption options such as: ' - '"--encryption-cipher", "--encryption-key-size" and ' - '"--encryption-provider")' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_type = volume_client.volume_types.create(parsed_args.name) - volume_type._info.pop('extra_specs') - if parsed_args.property: - result = volume_type.set_keys(parsed_args.property) - volume_type._info.update( - {'properties': format_columns.DictColumn(result)} - ) - if ( - parsed_args.encryption_provider - or parsed_args.encryption_cipher - or parsed_args.encryption_key_size - or parsed_args.encryption_control_location - ): - try: - # create new encryption - encryption = _create_encryption_type( - volume_client, volume_type, parsed_args - ) - except Exception as e: - LOG.error( - _( - "Failed to set encryption information for this " - "volume type: %s" - ), - e, - ) - # add encryption info in result - encryption._info.pop("volume_type_id", None) - volume_type._info.update( - {'encryption': format_columns.DictColumn(encryption._info)} - ) - volume_type._info.pop("os-volume-type-access:is_public", None) - - return zip(*sorted(volume_type._info.items())) - - -class DeleteVolumeType(command.Command): - _description = _("Delete volume type(s)") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume_types', - metavar='', - nargs='+', - help=_('Volume type(s) to delete (name or ID)'), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - result = 0 - - for volume_type in parsed_args.volume_types: - try: - vol_type = utils.find_resource( - volume_client.volume_types, volume_type - ) - - volume_client.volume_types.delete(vol_type) - except Exception as e: - result += 1 - LOG.error( - _( - "Failed to delete volume type with " - "name or ID '%(volume_type)s': %(e)s" - ) - % {'volume_type': volume_type, 'e': e} - ) - - if result > 0: - total = len(parsed_args.volume_types) - msg = _( - "%(result)s of %(total)s volume types failed to delete." - ) % {'result': result, 'total': total} - raise exceptions.CommandError(msg) - - -class ListVolumeType(command.Lister): - _description = _("List volume types") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=_('List additional fields in output'), - ) - parser.add_argument( - "--encryption-type", - action="store_true", - help=_( - "Display encryption information for each volume type " - "(admin only)" - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - if parsed_args.long: - columns = ['ID', 'Name', 'Is Public', 'Extra Specs'] - column_headers = ['ID', 'Name', 'Is Public', 'Properties'] - else: - columns = ['ID', 'Name', 'Is Public'] - column_headers = ['ID', 'Name', 'Is Public'] - data = volume_client.volume_types.list() - - formatters = {'Extra Specs': format_columns.DictColumn} - - if parsed_args.encryption_type: - encryption = {} - for d in volume_client.volume_encryption_types.list(): - volume_type_id = d._info['volume_type_id'] - # remove some redundant information - del_key = [ - 'deleted', - 'created_at', - 'updated_at', - 'deleted_at', - 'volume_type_id', - ] - for key in del_key: - d._info.pop(key, None) - # save the encryption information with their volume type ID - encryption[volume_type_id] = d._info - # We need to get volume type ID, then show encryption - # information according to the ID, so use "id" to keep - # difference to the real "ID" column. - columns += ['id'] - column_headers += ['Encryption'] - - _EncryptionInfoColumn = functools.partial( - EncryptionInfoColumn, encryption_data=encryption - ) - formatters['id'] = _EncryptionInfoColumn - - return ( - column_headers, - ( - utils.get_item_properties( - s, - columns, - formatters=formatters, - ) - for s in data - ), - ) - - -class SetVolumeType(command.Command): - _description = _("Set volume type properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume_type', - metavar='', - help=_('Volume type to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - help=_( - 'Set a property on this volume type ' - '(repeat option to set multiple properties)' - ), - ) - # TODO(Huanxuan Ao): Add choices for each "--encryption-*" option. - parser.add_argument( - '--encryption-provider', - metavar='', - help=_( - 'Set the encryption provider format for ' - 'this volume type (e.g "luks" or "plain") (admin only) ' - '(This option is required when setting encryption type ' - 'of a volume. Consider using other encryption options ' - 'such as: "--encryption-cipher", "--encryption-key-size" ' - 'and "--encryption-control-location")' - ), - ) - parser.add_argument( - '--encryption-cipher', - metavar='', - help=_( - 'Set the encryption algorithm or mode for this ' - 'volume type (e.g "aes-xts-plain64") (admin only)' - ), - ) - parser.add_argument( - '--encryption-key-size', - metavar='', - type=int, - help=_( - 'Set the size of the encryption key of this ' - 'volume type (e.g "128" or "256") (admin only)' - ), - ) - parser.add_argument( - '--encryption-control-location', - metavar='', - choices=['front-end', 'back-end'], - help=_( - 'Set the notional service where the encryption is ' - 'performed ("front-end" or "back-end") (admin only) ' - '(The default value for this option is "front-end" ' - 'when setting encryption type of a volume. Consider ' - 'using other encryption options such as: ' - '"--encryption-cipher", "--encryption-key-size" and ' - '"--encryption-provider")' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type - ) - - result = 0 - if parsed_args.property: - try: - volume_type.set_keys(parsed_args.property) - except Exception as e: - LOG.error(_("Failed to set volume type property: %s"), e) - result += 1 - - if ( - parsed_args.encryption_provider - or parsed_args.encryption_cipher - or parsed_args.encryption_key_size - or parsed_args.encryption_control_location - ): - try: - _create_encryption_type( - volume_client, volume_type, parsed_args - ) - except Exception as e: - LOG.error( - _( - "Failed to set encryption information for this " - "volume type: %s" - ), - e, - ) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("Command Failed: One or more of the operations failed") - ) - - -class ShowVolumeType(command.ShowOne): - _description = _("Display volume type details") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - "volume_type", - metavar="", - help=_("Volume type to display (name or ID)"), - ) - parser.add_argument( - "--encryption-type", - action="store_true", - help=_( - "Display encryption information of this volume type " - "(admin only)" - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type - ) - properties = format_columns.DictColumn( - volume_type._info.pop('extra_specs') - ) - volume_type._info.update({'properties': properties}) - if parsed_args.encryption_type: - # show encryption type information for this volume type - try: - encryption = volume_client.volume_encryption_types.get( - volume_type.id - ) - encryption._info.pop("volume_type_id", None) - volume_type._info.update( - {'encryption': format_columns.DictColumn(encryption._info)} - ) - except Exception as e: - LOG.error( - _( - "Failed to display the encryption information " - "of this volume type: %s" - ), - e, - ) - volume_type._info.pop("os-volume-type-access:is_public", None) - return zip(*sorted(volume_type._info.items())) - - -class UnsetVolumeType(command.Command): - _description = _("Unset volume type properties") - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'volume_type', - metavar='', - help=_('Volume type to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action='append', - help=_( - 'Remove a property from this volume type ' - '(repeat option to remove multiple properties)' - ), - ) - parser.add_argument( - "--encryption-type", - action="store_true", - help=_( - "Remove the encryption type for this volume type (admin only)" - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_type = utils.find_resource( - volume_client.volume_types, - parsed_args.volume_type, - ) - - result = 0 - if parsed_args.property: - try: - volume_type.unset_keys(parsed_args.property) - except Exception as e: - LOG.error(_("Failed to unset volume type property: %s"), e) - result += 1 - if parsed_args.encryption_type: - try: - volume_client.volume_encryption_types.delete(volume_type) - except Exception as e: - LOG.error( - _( - "Failed to remove the encryption type for this " - "volume type: %s" - ), - e, - ) - result += 1 - - if result > 0: - raise exceptions.CommandError( - _("Command Failed: One or more of the operations failed") - ) diff --git a/releasenotes/notes/remove-volume-v1-commands-bfa14e9cae54929f.yaml b/releasenotes/notes/remove-volume-v1-commands-bfa14e9cae54929f.yaml new file mode 100644 index 000000000..0aa6929b0 --- /dev/null +++ b/releasenotes/notes/remove-volume-v1-commands-bfa14e9cae54929f.yaml @@ -0,0 +1,35 @@ +--- +upgrade: + - | + Support for the Block Storage (Cinder) v1 API has been officially removed + as it had been broken for some time. If you haven't noticed then you likely + don't need to do anything. However, in the unlikely event that your cloud + is using the Block Storage v1 API - or incorrectly advertises the Block + Storage v1 API - consider overriding the API version to use v2 as this + behaves very similarly. It may also be necessary to set an endpoint + override for the Block Storage API if your clouds service catalog is not + configured correctly. For example: + + .. code-block:: yaml + + example: + regions: + - name: regionOne + values: + block_storage_endpoint_override: 'https://blockstorage.api.cloud.example/' + volume_api_version: 2 + + If using a public cloud provider, there may also be a profile already + published that sets these. These are listed in the `Vendor Support`__ + doc. For example: + + .. code-block:: yaml + + example: + profile: rackspace + + Alternatively, consider use versions of OSC < 3.19 and python-cinderclient + < 5.0 (both Stein), since these were the last versions to fully support + Cinder v1. + + .. __: https://docs.openstack.org/openstacksdk/latest/user/config/vendor-support.html diff --git a/setup.cfg b/setup.cfg index 43d9588bf..a770b4b62 100644 --- a/setup.cfg +++ b/setup.cfg @@ -635,53 +635,6 @@ openstack.object_store.v1 = object_show = openstackclient.object.v1.object:ShowObject object_unset = openstackclient.object.v1.object:UnsetObject -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 - - volume_backup_create = openstackclient.volume.v1.volume_backup:CreateVolumeBackup - volume_backup_delete = openstackclient.volume.v1.volume_backup:DeleteVolumeBackup - volume_backup_list = openstackclient.volume.v1.volume_backup:ListVolumeBackup - volume_backup_restore = openstackclient.volume.v1.volume_backup:RestoreVolumeBackup - volume_backup_show = openstackclient.volume.v1.volume_backup:ShowVolumeBackup - - volume_snapshot_create = openstackclient.volume.v1.volume_snapshot:CreateVolumeSnapshot - volume_snapshot_delete = openstackclient.volume.v1.volume_snapshot:DeleteVolumeSnapshot - volume_snapshot_list = openstackclient.volume.v1.volume_snapshot:ListVolumeSnapshot - volume_snapshot_set = openstackclient.volume.v1.volume_snapshot:SetVolumeSnapshot - volume_snapshot_show = openstackclient.volume.v1.volume_snapshot:ShowVolumeSnapshot - volume_snapshot_unset = openstackclient.volume.v1.volume_snapshot:UnsetVolumeSnapshot - - volume_type_create = openstackclient.volume.v1.volume_type:CreateVolumeType - volume_type_delete = openstackclient.volume.v1.volume_type:DeleteVolumeType - volume_type_list = openstackclient.volume.v1.volume_type:ListVolumeType - volume_type_set = openstackclient.volume.v1.volume_type:SetVolumeType - volume_type_show = openstackclient.volume.v1.volume_type:ShowVolumeType - volume_type_unset = openstackclient.volume.v1.volume_type:UnsetVolumeType - - volume_qos_associate = openstackclient.volume.v1.qos_specs:AssociateQos - volume_qos_create = openstackclient.volume.v1.qos_specs:CreateQos - volume_qos_delete = openstackclient.volume.v1.qos_specs:DeleteQos - volume_qos_disassociate = openstackclient.volume.v1.qos_specs:DisassociateQos - volume_qos_list = openstackclient.volume.v1.qos_specs:ListQos - volume_qos_set = openstackclient.volume.v1.qos_specs:SetQos - volume_qos_show = openstackclient.volume.v1.qos_specs:ShowQos - volume_qos_unset = openstackclient.volume.v1.qos_specs:UnsetQos - - volume_service_list = openstackclient.volume.v1.service:ListService - volume_service_set = openstackclient.volume.v1.service:SetService - - volume_transfer_request_accept = openstackclient.volume.v1.volume_transfer_request:AcceptTransferRequest - 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:ListTransferRequest - volume_transfer_request_show = openstackclient.volume.v1.volume_transfer_request:ShowTransferRequest - openstack.volume.v2 = consistency_group_add_volume = openstackclient.volume.v2.consistency_group:AddVolumeToConsistencyGroup consistency_group_create = openstackclient.volume.v2.consistency_group:CreateConsistencyGroup From 662405e55c5b18189b70ade31f99e733d9973f77 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 13 Mar 2025 13:47:27 +0000 Subject: [PATCH 113/245] docs: Remove irrelevant TODO cue is a dead project. Change-Id: Ie860312a9ea481741bf5e7ab29610f621daba702 Signed-off-by: Stephen Finucane --- doc/source/cli/plugin-commands/index.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/doc/source/cli/plugin-commands/index.rst b/doc/source/cli/plugin-commands/index.rst index ac61cc0e0..2622ee58b 100644 --- a/doc/source/cli/plugin-commands/index.rst +++ b/doc/source/cli/plugin-commands/index.rst @@ -25,10 +25,3 @@ Plugin Commands watcher zaqar zun - -.. TODO(efried): Make pages for the following once they're fixed. - -.. cue -.. # cueclient is not in global-requirements -.. # list-plugins:: openstack.mb.v1 -.. # :detailed: From 62c8b8217e48ffae555822cce56fa835f442f739 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 1 Apr 2025 13:14:33 +0100 Subject: [PATCH 114/245] typing: Indicate another tuple to be extended One has been introduced since Ie5907de8d60f2f39e98f6a88227cebb2e2ff565c merged. Change-Id: I37f7bf58a2cbecb69b370e832e56daa310cea3b6 Signed-off-by: Stephen Finucane --- openstackclient/identity/v3/endpoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index b84edb366..1dfa88120 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -239,7 +239,7 @@ def take_action(self, parsed_args): ) if endpoint: - column_headers = ('ID', 'Name') + column_headers: tuple[str, ...] = ('ID', 'Name') columns: tuple[str, ...] = ('id', 'name') data = identity_client.endpoint_projects(endpoint=endpoint.id) else: From ec4fd81c1121acbd333f5a169f3f59e73946a3be Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 19 Feb 2025 20:15:44 +0000 Subject: [PATCH 115/245] pre-commit: Enable mypy To ease migration of various commands from OSC to SDK. Change-Id: I4645237e8808239e4d605f7f45138449c9439949 Signed-off-by: Stephen Finucane --- .pre-commit-config.yaml | 13 +++++++++++++ pyproject.toml | 22 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 14bfc2b85..a8382cf11 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,3 +26,16 @@ repos: - id: hacking additional_dependencies: [] exclude: '^(doc|releasenotes)/.*$' + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.15.0 + hooks: + - id: mypy + additional_dependencies: + - types-requests + # keep this in-sync with '[mypy] exclude' in 'setup.cfg' + exclude: | + (?x)( + doc/.* + | examples/.* + | releasenotes/.* + ) diff --git a/pyproject.toml b/pyproject.toml index 9a30d5bc5..2a532cef6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,25 @@ +[tool.mypy] +python_version = "3.9" +show_column_numbers = true +show_error_context = true +ignore_missing_imports = true +follow_imports = "normal" +incremental = true +check_untyped_defs = true +warn_unused_ignores = true +# keep this in-sync with 'mypy.exclude' in '.pre-commit-config.yaml' +exclude = ''' +(?x)( + doc + | examples + | releasenotes + ) +''' + +[[tool.mypy.overrides]] +module = ["openstackclient.tests.unit.*"] +ignore_errors = true + [tool.ruff] line-length = 79 From dc8596fe74accfd2f2013dbd95906260f04fc3f1 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 1 Apr 2025 13:34:24 +0100 Subject: [PATCH 116/245] Prepare for osc-lib changes Change-Id: I665cd61272f881dce2d387da6035a2f35c866add Signed-off-by: Stephen Finucane --- openstackclient/common/envvars.py | 57 +++++++++++++++++++ openstackclient/compute/v2/server.py | 53 ++--------------- openstackclient/tests/unit/test_shell.py | 29 +++++----- .../volume/v3/volume_attachment.py | 3 +- openstackclient/volume/v3/volume_group.py | 3 +- .../volume/v3/volume_group_snapshot.py | 3 +- 6 files changed, 85 insertions(+), 63 deletions(-) create mode 100644 openstackclient/common/envvars.py diff --git a/openstackclient/common/envvars.py b/openstackclient/common/envvars.py new file mode 100644 index 000000000..5f702ff3d --- /dev/null +++ b/openstackclient/common/envvars.py @@ -0,0 +1,57 @@ +# 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 os + +from openstackclient.i18n import _ + + +def bool_from_str(value, strict=False): + true_strings = ('1', 't', 'true', 'on', 'y', 'yes') + false_strings = ('0', 'f', 'false', 'off', 'n', 'no') + + if isinstance(value, bool): + return value + + lowered = value.strip().lower() + if lowered in true_strings: + return True + elif lowered in false_strings or not strict: + return False + + msg = _( + "Unrecognized value '%(value)s'; acceptable values are: %(valid)s" + ) % { + 'value': value, + 'valid': ', '.join( + f"'{s}'" for s in sorted(true_strings + false_strings) + ), + } + raise ValueError(msg) + + +def boolenv(*vars, default=False): + """Search for the first defined of possibly many bool-like env vars. + + Returns the first environment variable defined in vars, or returns the + default. + + :param vars: Arbitrary strings to search for. Case sensitive. + :param default: The default to return if no value found. + :returns: A boolean corresponding to the value found, else the default if + no value found. + """ + for v in vars: + value = os.environ.get(v, None) + if value: + return bool_from_str(value) + return default diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 8e88d8e90..d415a8453 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -34,6 +34,7 @@ from osc_lib import utils from openstackclient.api import compute_v2 +from openstackclient.common import envvars from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -324,48 +325,6 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): return info -def bool_from_str(value, strict=False): - true_strings = ('1', 't', 'true', 'on', 'y', 'yes') - false_strings = ('0', 'f', 'false', 'off', 'n', 'no') - - if isinstance(value, bool): - return value - - lowered = value.strip().lower() - if lowered in true_strings: - return True - elif lowered in false_strings or not strict: - return False - - msg = _( - "Unrecognized value '%(value)s'; acceptable values are: %(valid)s" - ) % { - 'value': value, - 'valid': ', '.join( - f"'{s}'" for s in sorted(true_strings + false_strings) - ), - } - raise ValueError(msg) - - -def boolenv(*vars, default=False): - """Search for the first defined of possibly many bool-like env vars. - - Returns the first environment variable defined in vars, or returns the - default. - - :param vars: Arbitrary strings to search for. Case sensitive. - :param default: The default to return if no value found. - :returns: A boolean corresponding to the value found, else the default if - no value found. - """ - for v in vars: - value = os.environ.get(v, None) - if value: - return bool_from_str(value) - return default - - class AddFixedIP(command.ShowOne): _description = _("Add fixed IP address to server") @@ -1876,7 +1835,7 @@ def _match_image(image_api, wanted_properties): if 'delete_on_termination' in mapping: try: - value = bool_from_str( + value = envvars.bool_from_str( mapping['delete_on_termination'], strict=True, ) @@ -2223,7 +2182,7 @@ def get_parser(self, prog_name): parser.add_argument( '--all-projects', action='store_true', - default=boolenv('ALL_PROJECTS'), + default=envvars.boolenv('ALL_PROJECTS'), help=_( 'Delete server(s) in another project by name (admin only)' '(can be specified using the ALL_PROJECTS envvar)' @@ -2389,7 +2348,7 @@ def get_parser(self, prog_name): parser.add_argument( '--all-projects', action='store_true', - default=boolenv('ALL_PROJECTS'), + default=envvars.boolenv('ALL_PROJECTS'), help=_( 'Include all projects (admin only) ' '(can be specified using the ALL_PROJECTS envvar)' @@ -4967,7 +4926,7 @@ def get_parser(self, prog_name): parser.add_argument( '--all-projects', action='store_true', - default=boolenv('ALL_PROJECTS'), + default=envvars.boolenv('ALL_PROJECTS'), help=_( 'Start server(s) in another project by name (admin only) ' '(can be specified using the ALL_PROJECTS envvar)' @@ -5002,7 +4961,7 @@ def get_parser(self, prog_name): parser.add_argument( '--all-projects', action='store_true', - default=boolenv('ALL_PROJECTS'), + default=envvars.boolenv('ALL_PROJECTS'), help=_( 'Stop server(s) in another project by name (admin only) ' '(can be specified using the ALL_PROJECTS envvar)' diff --git a/openstackclient/tests/unit/test_shell.py b/openstackclient/tests/unit/test_shell.py index fd95fce61..628e362bb 100644 --- a/openstackclient/tests/unit/test_shell.py +++ b/openstackclient/tests/unit/test_shell.py @@ -183,14 +183,14 @@ def _assert_token_auth(self, cmd_options, default_args): osc_lib_test_utils.fake_execute(_shell, _cmd) self.app.assert_called_with(["list", "role"]) - self.assertEqual( - default_args.get("token", ''), _shell.options.token, "token" - ) - self.assertEqual( - default_args.get("auth_url", ''), - _shell.options.auth_url, - "auth_url", - ) + + if default_args.get('token'): + self.assertEqual(default_args['token'], _shell.options.token) + + if default_args.get('auth_url'): + self.assertEqual( + default_args['auth_url'], _shell.options.auth_url + ) def _assert_cli(self, cmd_options, default_args): with mock.patch( @@ -204,25 +204,28 @@ def _assert_cli(self, cmd_options, default_args): osc_lib_test_utils.fake_execute(_shell, _cmd) self.app.assert_called_with(["list", "server"]) + + # TODO(stephenfin): Remove "or ''" when we bump osc-lib minimum to + # a version that includes I1d26133c9d9ed299d1035f207059aa8fe463a001 self.assertEqual( default_args["compute_api_version"], - _shell.options.os_compute_api_version, + _shell.options.os_compute_api_version or '', ) self.assertEqual( default_args["identity_api_version"], - _shell.options.os_identity_api_version, + _shell.options.os_identity_api_version or '', ) self.assertEqual( default_args["image_api_version"], - _shell.options.os_image_api_version, + _shell.options.os_image_api_version or '', ) self.assertEqual( default_args["volume_api_version"], - _shell.options.os_volume_api_version, + _shell.options.os_volume_api_version or '', ) self.assertEqual( default_args["network_api_version"], - _shell.options.os_network_api_version, + _shell.options.os_network_api_version or '', ) diff --git a/openstackclient/volume/v3/volume_attachment.py b/openstackclient/volume/v3/volume_attachment.py index fe4765c7b..b24c96b83 100644 --- a/openstackclient/volume/v3/volume_attachment.py +++ b/openstackclient/volume/v3/volume_attachment.py @@ -18,6 +18,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import envvars from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -399,7 +400,7 @@ def get_parser(self, prog_name): '--all-projects', dest='all_projects', action='store_true', - default=utils.env('ALL_PROJECTS', default=False), + default=envvars.boolenv('ALL_PROJECTS'), help=_('Shows details for all projects (admin only).'), ) parser.add_argument( diff --git a/openstackclient/volume/v3/volume_group.py b/openstackclient/volume/v3/volume_group.py index 201e7a6d7..f943b8030 100644 --- a/openstackclient/volume/v3/volume_group.py +++ b/openstackclient/volume/v3/volume_group.py @@ -17,6 +17,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import envvars from openstackclient.i18n import _ @@ -410,7 +411,7 @@ def get_parser(self, prog_name): '--all-projects', dest='all_projects', action='store_true', - default=utils.env('ALL_PROJECTS', default=False), + default=envvars.boolenv('ALL_PROJECTS'), help=_('Shows details for all projects (admin only).'), ) # TODO(stephenfin): Add once we have an equivalent command for diff --git a/openstackclient/volume/v3/volume_group_snapshot.py b/openstackclient/volume/v3/volume_group_snapshot.py index eabf70028..5a760ac9b 100644 --- a/openstackclient/volume/v3/volume_group_snapshot.py +++ b/openstackclient/volume/v3/volume_group_snapshot.py @@ -17,6 +17,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import envvars from openstackclient.i18n import _ LOG = logging.getLogger(__name__) @@ -145,7 +146,7 @@ def get_parser(self, prog_name): '--all-projects', dest='all_projects', action='store_true', - default=utils.env('ALL_PROJECTS', default=False), + default=envvars.boolenv('ALL_PROJECTS'), help=_('Shows details for all projects (admin only).'), ) # TODO(stephenfin): Add once we have an equivalent command for From d95e23d92b0ba71a6f8552a066e117e0e3ac1993 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 1 Apr 2025 16:32:31 +0100 Subject: [PATCH 117/245] Remove use of formatter function Change-Id: I9ef88a4d69ffc3eaae183c77445ac10358d86337 Signed-off-by: Stephen Finucane --- openstackclient/identity/v3/role.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index c7bb5b054..61e30e5e0 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -428,23 +428,22 @@ def take_action(self, parsed_args): domain = identity_client.find_domain( name_or_id=parsed_args.domain, ) - columns: tuple[str, ...] = ('ID', 'Name', 'Domain') data = identity_client.roles(domain_id=domain.id) + return ( + ('ID', 'Name', 'Domain'), + ( + utils.get_item_properties(s, ('id', 'name')) + + (domain.name,) + for s in data + ), + ) + else: - columns = ('ID', 'Name') data = identity_client.roles() - - return ( - columns, - ( - utils.get_item_properties( - s, - columns, - formatters={'Domain': lambda _: domain.name}, - ) - for s in data - ), - ) + return ( + ('ID', 'Name'), + (utils.get_item_properties(s, ('id', 'name')) for s in data), + ) class RemoveRole(command.Command): From 0554ff60b41ec41439d9a129e578b16c8e3ac4d2 Mon Sep 17 00:00:00 2001 From: OpenStack Proposal Bot Date: Wed, 2 Apr 2025 03:07:33 +0000 Subject: [PATCH 118/245] Imported Translations from Zanata For more information about this automatic import see: https://docs.openstack.org/i18n/latest/reviewing-translation-import.html Change-Id: I2493ee06a6d47791be683577f0a9b2c63199a67c --- .../tr_TR/LC_MESSAGES/openstackclient.po | 1379 +---------------- 1 file changed, 9 insertions(+), 1370 deletions(-) diff --git a/openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po b/openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po index b44baf889..f49640ecc 100644 --- a/openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po +++ b/openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po @@ -1,32 +1,20 @@ -# Andreas Jaeger , 2017. #zanata # işbaran akçayır , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: python-openstackclient VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" -"POT-Creation-Date: 2018-02-25 01:10+0000\n" +"POT-Creation-Date: 2025-04-01 18:07+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2017-08-15 12:09+0000\n" -"Last-Translator: Andreas Jaeger \n" +"PO-Revision-Date: 2017-08-14 07:58+0000\n" +"Last-Translator: Copied by Zanata \n" "Language-Team: Turkish (Turkey)\n" "Language: tr_TR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Generator: Zanata 4.3.3\n" "X-POOTLE-MTIME: 1502656444.000000\n" -#, python-format -msgid "" -"\"Create\" rule command for type \"%(rule_type)s\" requires arguments " -"%(args)s" -msgstr "" -"\"%(rule_type)s\" türü için \"create\" kural komutu %(args)s argümanlarını " -"gerektirir" - -msgid "\"Create\" rule command requires argument \"type\"" -msgstr "\"Create\" kural komutu için \"type\" argümanı zorunludur" - #, python-format msgid "%(errors)s of %(total)s groups failed to delete." msgstr "%(total)s gruptan %(errors)s grup silinirken hata oluştu." @@ -51,10 +39,6 @@ msgstr "%(total)s kullanıcıdan %(errors)s kullanıcıyı silme işlemi başar msgid "%(num)s of %(total)s %(resource)ss failed to delete." msgstr "%(total)s %(resource)s'tan %(num)s tanesi silinirken hata oluştu." -#, python-format -msgid "%(result)s of %(total)s %(resource)ss failed to delete." -msgstr "%(total)s'ın %(result)s %(resource)s'ların silinmesi başarısız." - #, python-format msgid "%(result)s of %(total)s EC2 keys failed to delete." msgstr "" @@ -165,10 +149,6 @@ msgstr "%(total)s ağ ajanından %(result)s tanesi silinirken hata oluştu." msgid "%(result)s of %(total)s network segments failed to delete." msgstr "%(total)s ağ dilimlerinin %(result)s tanesi silinirken hata oluştu." -#, python-format -msgid "%(result)s of %(total)s policys failed to delete." -msgstr "%(total)s politikadan %(result)s tanesi silinirken hata oluştu." - #, python-format msgid "%(result)s of %(total)s ports failed to delete." msgstr "" @@ -279,24 +259,12 @@ msgstr "" "Varolan uzak disk bölümünden yeni görüntüsünden yeni disk bölümü anlık " "görüntüsü oluştururken 'force' seçeneği çalışmaz" -msgid "'--retype-policy' option will not work without '--type' option" -msgstr "'--retype-policy' seçeneği '--type' seçeneği olmadan çalışmaz" - msgid "--project is only allowed with --private" msgstr "--project sadece --private ile kullanılabilir" -msgid "" -"--size is a required option if snapshot or source volume is not specified." -msgstr "" -"Anlık görüntü veya kaynak disk bölümü belirtilmezse --size gerekli bir " -"seçenektir." - msgid "A service URL where SAML assertions are being sent (required)" msgstr "SAML bildirimlerinin gönderildiği bir hizmet URL'i (gerekli)" -msgid "Accept the image membership" -msgstr "İmaj üyeliğini kabul et" - msgid "Accept volume transfer request." msgstr "Disk bölümü aktarım isteğini kabul et." @@ -358,27 +326,12 @@ msgstr "L3 aracısına yönlendirici ekleyin" msgid "Add router to an agent" msgstr "Bir ajana yönlendirici ekle" -msgid "Add security group to server" -msgstr "Sunucuya güvenlik grubu ekle" - msgid "Add user to group" msgstr "Gruba kullanıcı ekle" -msgid "Add volume to server" -msgstr "Disk bölümünü sunucuya ekle" - msgid "Add volume(s) to consistency group" msgstr "Uyum grubuna disk bölümleri ekle" -msgid "" -"Additional route for this subnet e.g.: destination=10.10.0.0/16," -"gateway=192.168.71.254 destination: destination subnet (in CIDR notation) " -"gateway: nexthop IP address (repeat option to add multiple routes)" -msgstr "" -"Bu alt ağ için ek yönlendirici örn: hedef=10.10.0.0/16,geçit=192.168.71.254 " -"hedef: hedef alt ağ (CIDR gösteriminde) geçit: bir sonraki durak IP adresi " -"(birden fazla yölendirici eklemek için tekrarlanacak seçenek)" - msgid "Address scope to display (name or ID)" msgstr "Gösterilecek adres kapsamı (isim veya ID)" @@ -388,9 +341,6 @@ msgstr "Değiştirilecek adres kapsamı (ad veya kimlik)" msgid "Address scope(s) to delete (name or ID)" msgstr "Silinecek adres kapsam(lar)ı (isim veya ID)" -msgid "Adds a role assignment to a user or group on a domain or project" -msgstr "Bir alandaki veya projedeki bir kullanıcıya veya gruba rol atama ekler" - msgid "Agent from which router will be removed (ID only)" msgstr "Yönlendiricinin kaldırılacağı ajan (yalnızca ID)" @@ -418,22 +368,6 @@ msgstr "Silinecek küme(ler) (isim veya ID)" msgid "Allocate port on host (ID only)" msgstr " ana bilgisayarında bağlantı noktası ayır (sadece ID)" -msgid "" -"Allocation pool IP addresses for this subnet e.g.: start=192.168.199.2," -"end=192.168.199.254 (repeat option to add multiple IP addresses)" -msgstr "" -"Bu alt ağ için ayırma havuzu IP adresleri örn: başlangıç=192.168.199.2," -"bitiş=192.168.199.254 (birden fazla IP adresi eklemek için seçeneği tekrarla)" - -msgid "" -"Allocation pool IP addresses to be removed from this subnet e.g.: " -"start=192.168.199.2,end=192.168.199.254 (repeat option to unset multiple " -"allocation pools)" -msgstr "" -"Bu altağdan silinecek IP adres tahsis havuzu örn: başlangıç=192.168.199.2," -"bitiş=192.168.199.254 (birden fazla tahsis havuzu ayarını kaldırmak için " -"seçeneği tekrarla)" - msgid "" "Allow to access private flavor (name or ID) (Must be used with --" "private option)" @@ -441,19 +375,9 @@ msgstr "" "'nin özel flavor'a erişmesine izin verin (isim veya ID) (--private " "seçeneği ile beraber kullanılmalı)" -msgid "" -"Allow to access private type (name or ID) (Must be used with --" -"private option)" -msgstr "" -"Özel türe erişimek için 'ye izin ver (isim veya ID) (--private " -"seçeneği ile kullanılması zorunludur)" - msgid "Allow delete in state other than error or available" msgstr "Hata veya kullanılabilirden başka durumda silinmesine izin ver" -msgid "Allow disk over-commit on the destination host" -msgstr "Hedef ana bilgisayarda disk aşırı-işlemeye izin ver" - msgid "Allow image to be deleted (default)" msgstr "İmajın silinmesine izin ver (varsayılan)" @@ -463,29 +387,16 @@ msgstr "Kullanımdaki birimi yedeklemeye izin ver" msgid "Allow to delete in-use QoS specification(s)" msgstr "Kullanımdaki QoS özelliklerini silmeye izin ver" -msgid "Allow volume to be attached more than once (default to False)" -msgstr "Diskin birden fazla eklenmesine izin ver (varsayılan olarak False)" - #, python-format msgid "An error occurred when reading rules from file %(path)s: %(error)s" msgstr "%(path)s dosaysından kurallar okunurken hata oluştu: %(error)s" -msgid "Anchor for paging" -msgstr "Sayfalama için sabitleyici" - msgid "Apply rule to incoming network traffic (default)" msgstr "Kuralı gelen trafiğe uygula (varsayılan)" msgid "Apply rule to outgoing network traffic" msgstr "Giden ağ trafiğine kural uygula" -msgid "" -"Arbitrary scheduler hint key-value pairs to help boot an instance (repeat " -"option to set multiple hints)" -msgstr "" -"İsteğe bağlı bir önyüklemeye yardımcı olmak için keyfi zamanlayıcı ipucu " -"anahtar-değer çiftleri (birden fazla ipucu ayarlamak için seçeneği tekrarla)" - msgid "" "Argument --dst-port not allowed with arguments --icmp-type and --icmp-code" msgstr "" @@ -494,9 +405,6 @@ msgstr "" msgid "Argument --icmp-type required with argument --icmp-code" msgstr "--icmp-type argümanı --icmp-code ile kullanılması zorunlu" -msgid "Assocaite the floating IP with port (name or ID)" -msgstr "Yüzen IP'yi bağlantı noktasıyla ilişkilendirin (ad veya kimlik)" - msgid "Associate a QoS specification to a volume type" msgstr "Bir disk bölümü türüyle QoS özelliklerini ilişkilendir" @@ -532,9 +440,6 @@ msgstr "" msgid "Authentication URL of remote federated service provider (required)" msgstr "Uzak federe servis sağlayıcının kimlik doğrulama URL'si (gerekli)" -msgid "Authentication token to use" -msgstr "Kullanılacak yetkilendirme jetonu" - msgid "Authorize a request token" msgstr "Bir istek jetonu yetkilendir" @@ -615,16 +520,9 @@ msgstr "Mevcut kullanıcının parolasını değiştir" msgid "Check user membership in group" msgstr "Kullanıcının grup üyeliğini kontrol et" -msgid "Clean project resources, but don't delete the project" -msgstr "Projenin kaynaklarını temizle ama projeyi silme" - msgid "Clean resources associated with a project" msgstr "Bir proje ile alakalı kaynakları temizle" -#, python-format -msgid "Clear all tags associated with the %s" -msgstr "%s ile ilişkili tüm etiketleri sil" - msgid "" "Clear associated allocation-pools from the subnet. Specify both --allocation-" "pool and --no-allocation-pool to overwrite the current allocation pool " @@ -642,15 +540,6 @@ msgstr "" "yönlendirme bilgisinin üzerine yazmak için --host-route ve --no-host-route " "seçeneklerinin her ikisini de belirtin." -msgid "" -"Clear existing allowed-address pairs associatedwith this port.(Specify both " -"--allowed-address and --no-allowed-addressto overwrite the current allowed-" -"address pairs)" -msgstr "" -"Bu bağlantı noktasıyla ilişkili mevcut izinli adres çiftlerini temizleyin." -"(Mevcut izinli adres çiftinin üzerinde yazmak için --allowed-address ve --" -"no-allowed-addressto seçeneklerinin her ikisini de belirtiniz)" - msgid "" "Clear existing information of DNS Nameservers. Specify both --dns-nameserver " "and --no-dns-nameserver to overwrite the current DNS Nameserver information." @@ -659,26 +548,6 @@ msgstr "" "bilgisinin üzerine yazmak için --dns-nameserver ve --no-dns-nameserver " "özelliklerini belirle." -msgid "" -"Clear existing information of binding:profile.Specify both --binding-profile " -"and --no-binding-profile to overwrite the current binding:profile " -"information." -msgstr "" -"binding:profile'in mevcut bilgilerini temizle. Mevcut binding:profile " -"bilgisinin üzerine yazmak için --binding-profile ve --no-binding-profile her " -"ikisini de belirtin." - -msgid "Clear existing information of data plane status" -msgstr "Mevcut veri düzlemi durumu bilgilerini temizle" - -msgid "" -"Clear existing information of fixed IP addresses.Specify both --fixed-ip and " -"--no-fixed-ip to overwrite the current fixed IP addresses." -msgstr "" -"Sabit IP adresleri için mevcut bilgileri silin. Geçerli sabit IP " -"adreslerinin üzerine yazmak için hem --fixed-ip hem de --no-fixed-ip " -"belirtin." - msgid "Clear existing security groups associated with this port" msgstr "Bu bağlantı noktasıyla ilişkili mevcut güvenlik gruplarını temizle" @@ -687,21 +556,6 @@ msgstr "" "Yönlendiricinin yüksek kullanılabilirlik özelliğini temizle (sadece devre " "dışı bırakılmış yönlendirici)" -msgid "" -"Clear routes associated with the router. Specify both --route and --no-route " -"to overwrite current value of route." -msgstr "" -"Yönlendirici ile ilişkili yönleri temizle. Mevcut yön değerinin üzerine " -"yazmak için hem --route hem de --no-route seçeneklerini belirtin." - -#, python-format -msgid "" -"Clear tags associated with the %s. Specify both --tag and --no-tag to " -"overwrite current tags" -msgstr "" -"%s ile ilgili etiketleri temizle. Mevcut etiketlerin üzerine yazmak için hem " -"--tag hem de --no-tag seçeneğini belirtin" - msgid "Command Failed: One or more of the operations failed" msgstr "Komut başarısız: Bir veya birden fazla işlem başarısız" @@ -716,12 +570,6 @@ msgstr "Hesaplama API sürümü, varsayılan=%s (Env: OS_COMPUTE_API_VERSION)" msgid "Compute service %(service)s of host %(host)s failed to set." msgstr "Ana bilgisayar %(host)s'ın hesap hizmeti %(service)s ayarlanamadı." -msgid "Compute service(s) to delete (ID only)" -msgstr "Hesaplama servis(ler)ini sil" - -msgid "Confirm server resize is complete" -msgstr "Sunucu yeniden boyutlandırmasının tamamlandığını doğrula" - msgid "Consistency group containing (name or ID)" msgstr "'ü içeren tutarlılık grubu (isim veya ID)" @@ -775,10 +623,6 @@ msgstr "Silinecek alıcı(lar)" msgid "Container for new object" msgstr "Yeni nesne için kap" -#, python-format -msgid "Container name is %s characters long, the default limit is 256" -msgstr "Kap ismi %s karakter uzunluğunda, varsayılan sınır 256'dır" - msgid "Container to display" msgstr "Gösterilecek kap" @@ -817,63 +661,9 @@ msgstr "" "net-id=' parametresi için bir sarmalayıcıdır. Daha gelişmiş " "kullanım durumları için, '--nic' parametresine bakın." -msgid "" -"Create a NIC on the server and connect it to port. Specify option multiple " -"times to create multiple NICs. This is a wrapper for the '--nic port-" -"id=' parameter that provides simple syntax for the standard use case " -"of connecting a new server to a given port. For more advanced use cases, " -"refer to the '--nic' parameter." -msgstr "" -"Sunucuda bir NIC oluşturun ve bağlantı noktasına bağlayın. Birden çok NIC " -"oluşturmak için seçeneği birden çok kez belirtin. Bu, belirli bir bağlantı " -"noktasına yeni bir sunucu bağlamak için standart kullanım örneği için basit " -"sözdizimi sağlayan '--nic port-id=' parametresi için bir " -"sarmalayıcıdır. Daha gelişmiş kullanım durumları için, '--nic' parametresine " -"bakın." - -msgid "" -"Create a NIC on the server. Specify option multiple times to create multiple " -"NICs. Either net-id or port-id must be provided, but not both. net-id: " -"attach NIC to network with this UUID, port-id: attach NIC to port with this " -"UUID, v4-fixed-ip: IPv4 fixed address for NIC (optional), v6-fixed-ip: IPv6 " -"fixed address for NIC (optional), none: (v2.37+) no network is attached, " -"auto: (v2.37+) the compute service will automatically allocate a network. " -"Specifying a --nic of auto or none cannot be used with any other --nic value." -msgstr "" -"Sunucuda bir NIC oluştur. Birden fazla NIC oluşturmak için seçeneği birden " -"fazla kere belirtin. Ya net-id ya da port-id sağlanmalı, ikisi bir arada " -"değil. net-id: NIC'nin ağa ekleneceği UUID, port-id: NIC'nin bağlantı " -"noktasına takılacağı UUID, v4-fixed-ip: NIC için sabit IPv4 adresi " -"(seçimli), v6-fixed-ip: NIC için sabit IPv6 adresi (seçimli), none: (v2.37+) " -"hiç ağ takılmaz, auto: (v2.37+) hesaplama servisi otomatik olarak bir ağ " -"ayırır. Auto veya none'ı bir --nic ile belirtmek başka bir --nic değeri ile " -"kullanılamaz." - msgid "Create a QoS policy" msgstr "QoS politikası oluştur" -msgid "" -"Create a block device on the server.\n" -"Block device mapping in the format\n" -"=:::\n" -": block device name, like: vdb, xvdc (required)\n" -": UUID of the volume or snapshot (required)\n" -": volume or snapshot; default: volume (optional)\n" -": volume size if create from snapshot (optional)\n" -": true or false; default: false (optional)\n" -"(optional extension)" -msgstr "" -"Sunucu üzerinde blok aygıtı oluştur.\n" -"Blok aygıtı eşleşme formatı\n" -"=:::\n" -": blok aygıt ismi, örn: vdb, xvdc (gerekli)\n" -": disk bölümünün veya anlık görüntünün UUID'si (gerekli)\n" -": disk bölümü veya anlık görüntü; varsayılan: disk bölümü (seçimli)\n" -": eğer anlık görüntüden oluşturulduysa disk bölümü boyutu " -"(seçimli)\n" -": true veya false; varsayılan: false (seçimli)\n" -"(seçimli uzantı)" - msgid "Create a centralized router" msgstr "Merkezi bir yönlendirici oluştur" @@ -931,9 +721,6 @@ msgstr "Bir altağ oluşturun" msgid "Create an access token" msgstr "Erişim jetonu oluştur" -msgid "Create compute agent" -msgstr "Hesaplama ajanı oluştur" - msgid "" "Create credentials for user (name or ID; default: current authenticated user)" msgstr "" @@ -947,9 +734,6 @@ msgstr "" "Projede kimlik bilgileri oluştur (isim veya ID; varsayılan: mevcut kimlik " "doğrulama yapılmış proje)" -msgid "Create description for meter" -msgstr "Sayaç için açıklama oluştur" - msgid "Create floating IP" msgstr "Yüzen IP oluştur" @@ -968,9 +752,6 @@ msgstr "Yeni Ağ QoS kuralı oluştur" msgid "Create new QoS specification" msgstr "Yeni QoS özelliği oluştur" -msgid "Create new backup" -msgstr "Yeni yedek oluştur" - msgid "Create new consistency group snapshot." msgstr "Yeni tutarlılık grubu anlık görüntüsü oluştur." @@ -1040,9 +821,6 @@ msgstr "Yeni servis oluştur" msgid "Create new service provider" msgstr "Yeni servis sağlayıcı oluştur" -msgid "Create new snapshot" -msgstr "Yeni anlık görüntü oluştur" - msgid "Create new trust" msgstr "Yeni güven oluştur" @@ -1067,20 +845,6 @@ msgstr "Bu güvenlik grubunda kural oluştur (isim veya ID)" msgid "Create server boot disk from this image (name or ID)" msgstr "Bu imajdan sunucu ön yükleme diski oluştur (isim veya ID)" -msgid "" -"Create server using this volume as the boot disk (name or ID).\n" -"This option automatically creates a block device mapping with a boot index " -"of 0. On many hypervisors (libvirt/kvm for example) this will be device vda. " -"Do not create a duplicate mapping using --block-device-mapping for this " -"volume." -msgstr "" -"Bu disk bölümünü ön yüklenebilir disk olarak kullanarak sunucu oluştur (isim " -"veya ID).\n" -"Bu seçenek otomatik olarak 0 ön yükleme diziniyle bir blok aygıt eşleşmesi " -"oluşturur. Bir çok yönetici arakatman (örneğin libvirt/kvm) üzerinde bu " -"aygıt vda'dir. Bu disk bölümü için --block-device-mapping kullanarak birden " -"fazla eşleşme oluşturmayın." - msgid "Create server with this flavor (name or ID)" msgstr "Bu flavor ile sunucu oluştur (isim veya ID)" @@ -1105,14 +869,6 @@ msgstr "Kimlik bilgilerine erişim anahtarı" msgid "Credentials access key(s)" msgstr "Kimlik bilgilerine erişim anahtar(lar)ı" -msgid "" -"Custom data to be passed as binding:profile. Data may be passed as " -"= or JSON. (repeat option to set multiple binding:profile data)" -msgstr "" -"binding:profile olarak verilecek özel veri. Veri = şeklinde veya " -"JSON olarak verilebilir. (birden fazla binding:profile verisi ayarlamak için " -"seçeneği tekrarlayın)" - msgid "DNS server for this subnet (repeat option to set multiple DNS servers)" msgstr "" "Bu alt ağ için DNS sunucu (birden fazla DNS sunucusu ayarlamak için seçeneği " @@ -1156,22 +912,9 @@ msgstr "Qos Politika(lar/s)ını sil" msgid "Delete address scope(s)" msgstr "Adres kapsam(lar)ını sil" -msgid "" -"Delete auto allocated topology for a given project. Default is the current " -"project" -msgstr "" -"Belirli bir proje için otomatik ayrılan topolojiyi silin. Varsayılan geçerli " -"projedir" - msgid "Delete auto allocated topology for project" msgstr "Projeye otomatik ayrılan topolojiyi sil" -msgid "Delete backup(s)" -msgstr "Yedek(ler)i sil" - -msgid "Delete compute agent(s)" -msgstr "Hesaplama ajan(lar)ını sil" - msgid "Delete compute service(s)" msgstr "Hesaplama servis(ler)ini sil" @@ -1295,9 +1038,6 @@ msgstr "Servis sağlayıcı(ları/yı) sil" msgid "Delete service(s)" msgstr "Servis(ler)i sil" -msgid "Delete snapshot(s)" -msgstr "Anlık görüntü(yü/leri) sil" - msgid "Delete subnet pool(s)" msgstr "Altağ havuzunu sil" @@ -1325,14 +1065,6 @@ msgstr "Disk bölümü türlerini sil" msgid "Delete volume(s)" msgstr "Disk bölümlerini sil" -#, python-format -msgid "Deleting %(resource)s : %(id)s" -msgstr "%(resource)s siliniyor: %(id)s" - -#, python-format -msgid "Deleting project: %s" -msgstr "Proje siliniyor: %s" - msgid "Description for the flavor" msgstr "Flavor için açıklama" @@ -1357,22 +1089,6 @@ msgstr "Bu tutarlılık grubu anlık görüntüsünün açıklaması" msgid "Description of this port" msgstr "Bu bağlantı noktasının açıklaması" -msgid "" -"Desired IP and/or subnet (name or ID)on external gateway: subnet=,ip-" -"address= (repeat option to set multiple fixed IP addresses)" -msgstr "" -"Harici geçit üzerinde istenen IP ve/veya altağ: subnet=,ip-" -"address= (birden fazla sabit IP adres ayarlamak için seçeneği " -"tekrarla)" - -msgid "" -"Desired IP and/or subnet for filtering ports (name or ID): subnet=," -"ip-address= (repeat option to set multiple fixed IP addresses)" -msgstr "" -"Bağlantı noktalarını filtrelemek için tasarlanan IP ve/veya alt ağ (isim " -"veya ID): subnet=,ip-address= (birden fazla sabit IP " -"adresi ayarlamak için seçeneği tekrarlayın)" - msgid "" "Desired IP and/or subnet for this port (name or ID): subnet=,ip-" "address= (repeat option to set multiple fixed IP addresses)" @@ -1390,22 +1106,6 @@ msgstr "" "veya ID): subnet=,ip-address= (birden fazla sabit IP " "adresini kaldırmak için seçeneği tekrarlayın)" -msgid "" -"Desired allowed-address pair which should be removed from this port: ip-" -"address= [,mac-address=] (repeat option to set " -"multiple allowed-address pairs)" -msgstr "" -"Bu bağlantıdan kaldırılması gereken tasarlanan erişilebilir adres çifti: ip-" -"address= [,mac-address=] (birden fazla izin verilen " -"adres çifti ayarlamak için seçeneği tekrarlayın)" - -msgid "" -"Desired key which should be removed from binding:profile(repeat option to " -"unset multiple binding:profile data)" -msgstr "" -"binding:profile'den çıkarılması gereken istenen anahtar (çoklu binding:" -"profile verisinin ayarını kaldırmak için seçeneği tekrarlayın)" - msgid "" "Destination filename (defaults to object name); using '-' as the filename " "will print the file to stdout" @@ -1416,9 +1116,6 @@ msgstr "" msgid "Destination host (takes the form: host@backend-name#pool)" msgstr "Hedef ana bilgisayar (biçemi: anabilgisayar@artalanismi#havuz)" -msgid "Destination port (ssh -p option)" -msgstr "Hedef bağlantı noktası (ssh -p seçeneği)" - msgid "" "Destination port, may be a single port or a starting and ending port range: " "137:139. Required for IP protocols TCP and UDP. Ignored for ICMP IP " @@ -1539,9 +1236,6 @@ msgstr "Adres kapsam detaylarını göster" msgid "Display aggregate details" msgstr "Küme detaylarını göster" -msgid "Display backup details" -msgstr "Yedek detaylarını göster" - msgid "Display configuration details" msgstr "Yapılandırma detaylarını göster" @@ -1584,9 +1278,6 @@ msgstr "Yüzen IP ayrıntılarını görüntüle" msgid "Display group details" msgstr "Grup detaylarını göster" -msgid "Display host details" -msgstr "Sunucu detaylarını göster" - msgid "Display hypervisor details" msgstr "Yönetici ara katman detaylarını göster" @@ -1671,9 +1362,6 @@ msgstr "Servis detaylarını göster" msgid "Display service provider details" msgstr "Servis sağlayıcı detaylarını göster" -msgid "Display snapshot details" -msgstr "Anlık görüntü detaylarını göster" - msgid "Display subnet details" msgstr "Altağ detaylarını göster" @@ -1701,9 +1389,6 @@ msgstr "Disk bölümü türü detaylarını göster" msgid "Do not make the network VLAN transparent" msgstr "Ağ VLAN'ını transparan yapma" -msgid "Do not over-commit disk on the destination host (default)" -msgstr "Hedef ana bilgisayarda disk aşırı işleme yapma (varsayılan)" - msgid "Do not share meter between projects" msgstr "Ölçümleri projeler arasında paylaşma" @@ -1750,10 +1435,6 @@ msgstr "" "Grubun ait olduğu alan (isim veya ID). Grup isimlerinde bir çatışma olması " "durumunda kullanılabilir." -msgid "Domain the project belongs to (name or ID) [only valid with --absolute]" -msgstr "" -"Projenin ait olduğu alan (isim veya ID) [sadece --absolute ile geçerli]" - msgid "" "Domain the project belongs to (name or ID). This can be used in case " "collisions between project names exist." @@ -1898,11 +1579,6 @@ msgstr "" msgid "Enable port security for this port" msgstr "Bu bağlantı noktası için bağlantı noktası güvenliğini etkinleştir" -msgid "Enable port security for this port (Default)" -msgstr "" -"Bu bağlantı noktası için bağlantı noktası güvenliğini etkinleştir " -"(Varsayılan)" - msgid "Enable project" msgstr "Projeyi etkinleştir" @@ -1962,9 +1638,6 @@ msgstr "Silinecek uç nokta(lar) (sadece ID)" msgid "Ephemeral disk size in GB (default 0G)" msgstr "GB cinsinden geçici disk boyutu (varsayılan 0G)" -msgid "Error creating server\n" -msgstr "Sunucu oluşturulurken hata\n" - #, python-format msgid "Error creating server backup: %s" msgstr "Sunucu yedeği oluşturulurken hata: %s" @@ -1977,55 +1650,30 @@ msgstr "Sunucu imajı oluşturma hatası: %s" msgid "Error creating server: %s" msgstr "Sunucu oluşturma başarısız: %s" -msgid "Error deleting server\n" -msgstr "Sunucu silinirken hata\n" - #, python-format msgid "Error deleting server: %s" msgstr "Sunucu silinirken hata: %s" -msgid "Error migrating server\n" -msgstr "Sunucu göçü sırasında hata\n" - #, python-format msgid "Error migrating server: %s" msgstr "Sunucu göçü sırasında hata: %s" -msgid "Error rebooting server\n" -msgstr "Sunucu yeniden başlatma hatası\n" - #, python-format msgid "Error rebooting server: %s" msgstr "Sunucu yeniden başlatma hatası: %s" -msgid "Error rebuilding server\n" -msgstr "Sunucu yeniden yapılandırması sırasında hata\n" - #, python-format msgid "Error rebuilding server: %s" msgstr "Sunucu yeniden yapılandırması sırasında hata: %s" -msgid "Error resizing server\n" -msgstr "Sunucunun yeniden boyutlandırması sırasında hata\n" - #, python-format msgid "Error resizing server: %s" msgstr "Sunucu yeniden boyutlandırma hatası: %s" -msgid "Error retrieving diagnostics data\n" -msgstr "Teşhis verisi alınırken hata oluştu\n" - #, python-format msgid "Error while executing command: %s" msgstr "Komut çalıştırılırken hata oluştu: %s" -msgid "" -"Error: If a user or group is specified, either --domain or --project must " -"also be specified to list role grants." -msgstr "" -"Hata: Bir kullanıcı veya grup belirtilmişse, rol izinlerini listelemek için " -"--domain veya --project de belirtilmelidir." - msgid "" "Ethertype of network traffic (IPv4, IPv6; default: based on IP protocol)" msgstr "" @@ -2079,9 +1727,6 @@ msgstr "Hesaplama API'ı uzantıların listelemeyi desteklemiyor" msgid "Extensions list not supported by Identity API" msgstr "Kimlik API için uzantıların listlenmesi desteklenmiyor" -msgid "External Network used as router's gateway (name or ID)" -msgstr "Yönlendirici geçidi olarak kullanılan Harici Ağ (isim veya ID)" - #, python-format msgid "Failed to add project %(project)s access to flavor: %(e)s" msgstr "%(project)s projesinin flavor'a erişimi başarısız: %(e)s" @@ -2098,22 +1743,10 @@ msgstr "Anlık görüntü özellikleri silinirken hata oluştu: %s" msgid "Failed to clean volume properties: %s" msgstr "Disk bölümü özelliklerini silme başarısız: %s" -#, python-format -msgid "Failed to clear flavor property: %s" -msgstr "Flavor özelliğinin silinmesi başarısız: %s" - #, python-format msgid "Failed to create Network QoS rule: %(e)s" msgstr "Ağ QoS kuralı oluşturulurken hata oluştu: %(e)s" -#, python-format -msgid "Failed to delete %(dresult)s of %(total)s images." -msgstr "%(total)s imajdan %(dresult)s tanesi silinirken hata oluştu." - -#, python-format -msgid "Failed to delete %(resource)s with ID '%(id)s': %(e)s" -msgstr "'%(id)s' ID'li %(resource)s silinemedi: %(e)s" - #, python-format msgid "Failed to delete %(resource)s with name or ID '%(name_or_id)s': %(e)s" msgstr "" @@ -2355,9 +1988,6 @@ msgstr "" "'%(flavor)s' flavor için erişen projelerin listesinin alınma işlemi " "başarısız: %(e)s" -msgid "Failed to get an image file." -msgstr "İmaj dosyası alma başarısız." - #, python-format msgid "Failed to remove flavor access from project: %s" msgstr "Projeden flavor erişiminin kaldırılması başarısız: %s" @@ -2389,10 +2019,6 @@ msgstr "Bu disk bölümü türü için şifreleme bilgisini ayarlama başarısı msgid "Failed to set flavor access to project: %s" msgstr "Projeye flavor erişiminin ayarlanması başarısız: %s" -#, python-format -msgid "Failed to set flavor property: %s" -msgstr "Flavor özelliğinin ayarlanması başarısız: %s" - #, python-format msgid "Failed to set image property: %s" msgstr "İmaj özelliği ayarlarken hata oluştu: %s" @@ -2429,10 +2055,6 @@ msgstr "Disk bölümü durumunu ayarlama başarısız: %s" msgid "Failed to set volume type access to project: %s" msgstr "Projeye erişim için disk bölümü türü ayarlama başarısız: %s" -#, python-format -msgid "Failed to set volume type property: %s" -msgstr "Disk bölümü türü özelliğini ayarlarken hata oluştu: %s" - #, python-format msgid "Failed to set volume type: %s" msgstr "Disk bölümü türü ayarlanırken hata oluştu: %s" @@ -2469,20 +2091,6 @@ msgstr "İmaj özelliği ayarını kaldırma başarısız: %s" msgid "Failed to unset volume property: %s" msgstr "Disk bölümü özelliği ayarlarını kaldırma başarısız: %s" -#, python-format -msgid "Failed to unset volume type property: %s" -msgstr "Disk bölümü türü özelliğini kaldırma işlemi başarısız: %s" - -#, python-format -msgid "Failed to update backup name or description: %s" -msgstr "Yedek ismi ve açıklaması güncellenirken hata oluştu: %s" - -#, python-format -msgid "Failed to update snapshot display name or display description: %s" -msgstr "" -"Anlık görüntü görünür ismi veya görünür açıklamasını güncelleme işlemi " -"başarısız: %s" - #, python-format msgid "Failed to update snapshot name or description: %s" msgstr "Anlık görüntü adı veya açıklaması güncellenemedi: %s" @@ -2508,23 +2116,12 @@ msgstr "Düzenlenecek federasyon protokolü (isim veya ID)" msgid "Federation protocol(s) to delete (name or ID)" msgstr "Silinecek federasyon protokol(ü/leri) (isim veya ID)" -msgid "" -"File to inject into image before boot (repeat option to set multiple files)" -msgstr "" -"Önyüklemeden önce imaja enjekte etmek için dosya (birden çok dosyayı " -"ayarlamak için seçeneği tekrar edin)" - msgid "" "Filename for private key to save. If not used, print private key in console." msgstr "" "Kapalı anahtarın kaydedileceği dosyanın ismi. Kullanılmazsa, kapalı anahtar " "konsola basılır." -msgid "Filename for public key to add. If not used, creates a private key." -msgstr "" -"Eklenecek açık anahtarın dosya ismi. Kullanılmazsa, bir kapalı anahtar " -"oluşturur." - msgid "Filename that contains a new set of mapping rules" msgstr "Eşleşme kurallarının yeni bir kümesini içeren dosya adı" @@ -2552,10 +2149,6 @@ msgstr "Grup listesini 'e göre filtrele (isim veya ID)" msgid "Filter group list by (name or ID)" msgstr "'ya göre grup listesini filtrele (isim veya ID)" -msgid "Filter hypervisors using substring" -msgstr "" -" alt karakter dizisini kullanarak yönetici arakatmanları filtrele" - msgid "Filter images based on name." msgstr "İmajları isme göre filtrele." @@ -2589,12 +2182,6 @@ msgstr "Sonuçları kullanıcıya göre filtrele (isim veya ID) (sadece yönetic msgid "Filter results by volume name" msgstr "Sonuçları disk bölümü ismine göre filtrele" -msgid "Filter roles by (name or ID)" -msgstr "Rolleri 'ye göre filtrele (isim veya ID)" - -msgid "Filter roles by (name or ID)" -msgstr "Rolleri 'ya göre filtrele (isim veya ID)" - msgid "Filter users by (name or ID)" msgstr "Kullanıcıları 'e göre filtrele (isim veya ID)" @@ -2620,35 +2207,18 @@ msgstr "" "Sonuçları bir duruma göre filtrele (\"available\", \"error\", \"creating\", " "\"deleting\" veya \"error_deleting\")" -msgid "" -"Filters results by a status. ('available', 'error', 'creating', 'deleting' " -"or 'error-deleting')" -msgstr "" -"Bir duruma göre sonuçları filtrele. ('kullanılabilir', 'hata', " -"'oluşturuluyor', 'siliniyor' veya 'silinirken hata')" - msgid "Filters results by a volume (name or ID)." msgstr "Bir disk bölümüne göre sonuçları filtrele (isim veya ID)." msgid "Filters results by the backup name" msgstr "Yedek ismine göre sonuçları listele" -msgid "" -"Filters results by the backup status ('creating', 'available', 'deleting', " -"'error', 'restoring' or 'error_restoring')" -msgstr "" -"Yedek durumuna göre sonuçları filtrele ('oluşturuluyor', 'kullanılabilir', " -"'siliniyor', 'hata' veya 'geri yüklenirken hata')" - msgid "Filters results by the volume which they backup (name or ID)" msgstr "Yedeklenen disk bölümüne göre sonuçları filtrele (isim veya ID)" msgid "Fixed IP address mapped to the floating IP" msgstr "Sabit IP adres yüzen IP adresi ile eşleşti" -msgid "Fixed IP address to associate with this floating IP address" -msgstr "Bu kayan IP adresi ile ilişkili sabit IP adresi" - msgid "Fixed IP address to remove from the server (IP only)" msgstr "Sunucudan kaldırılacak sabit IP adresi (sadece IP)" @@ -2687,15 +2257,9 @@ msgstr "Silinecek flavor(lar) (isim veya ID)" msgid "Floating IP address" msgstr "Yüzen IP adresi" -msgid "Floating IP address to assign to server (IP only)" -msgstr "Sunucuya atanacak kayan IP adresi (sadece IP)" - msgid "Floating IP address to remove from server (IP only)" msgstr "Sunucudan kaldırılacak kayan IP adresi (sadece IP)" -msgid "Floating IP to associate (IP address or ID)" -msgstr "İlişkilendirilecek yüzen IP adresi (IP adres veya ID)" - msgid "Floating IP to disassociate (IP address or ID)" msgstr "Bağlantıyı kesmek için kayan IP (IP adresi veya kimliği)" @@ -2709,9 +2273,6 @@ msgid "Floating ip pool operations are only available for Compute v2 network." msgstr "" "Yüzen IP havuz işlemleri sadece Hesaplama v2 ağı için kullanılabilirdir." -msgid "Force down service" -msgstr "Servisi durmaya zorla" - msgid "Force image change if volume is in use (only meaningful with --volume)" msgstr "" "Disk bölümü kullanımda ise imajı değişmeye zorla (sadece --volume ile " @@ -2723,9 +2284,6 @@ msgstr "" "Disk bölümü kullanımda ise imaj oluşturmaya zorla (sadece --volume ile " "anlamlıdır)" -msgid "Force up service" -msgstr "Servisi açılmaya zorla" - msgid "Freeze and disable the specified volume host" msgstr "Belirtilen disk bölümü sunucusunu dondur ve devre dışı bırak" @@ -2753,9 +2311,6 @@ msgstr "Silinecek grup(lar) (isim veya ID)" msgid "Helper class capable of reading rules from files" msgstr "Yardımcı sınıf kuralları dosyadan okuyabilir" -msgid "Hints for the scheduler (optional extension)" -msgstr "Zamanlayıcı için ipuçları (isteğe bağlı eklenti)" - msgid "Host to add to " msgstr " için eklenecek sunucu" @@ -2802,25 +2357,12 @@ msgstr "Bu imajı oluşturmak için kullanılan sunucu örneği kimliği" msgid "ID of the agent" msgstr "Ajanın ID'si" -msgid "IP address to add to server (name only)" -msgstr "Sunucuya eklenecek IP adresi (sadece isim)" - -msgid "IP address to remove from server (name only)" -msgstr "Sunucudan kaldırılacak IP adresi (sadece isim)" - msgid "IP protocol (icmp, tcp, udp; default: tcp)" msgstr "IP protokolü (icmp, tcp, udp; varsayılan: tcp)" msgid "IP version (default is 4)" msgstr "IP sürümü (varsayılan 4)" -msgid "" -"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." -msgstr "" -"IP sürümü (varsayılan 4). Alt ağ havuzu belirtildiğinde, IP sürümü alt ağ " -"havuzundan belirlenir ve bu seçenek göz ardı edilir." - msgid "IPv4 subnet for fixed IPs (in CIDR notation)" msgstr "Sabit IP'ler için IPv4 alt ağı (CIDR gösteriminde)" @@ -2871,16 +2413,6 @@ msgstr "" "Belirtilirse, disk bölümü durumu kilitlenir ve geçiş işleminin iptal " "edilmesine izin vermeyecektir (muhtemelen başka bir işlemle)" -msgid "" -"If specified, the volume state will not be locked and the a migration can be " -"aborted (default) (possibly by another operation)" -msgstr "" -"Belirtilirse, disk bölümü durumu kilitlenmez ve bir taşıma işlemi iptal " -"edilebilir (varsayılan) (muhtemelen başka bir işlemle)" - -msgid "If topology exists returns the topology's information (Default)" -msgstr "Topoloji var ise, topolojinin bilgisini döndürür (Varsayılan)" - #, python-format msgid "Image %(id)s was %(status)s." msgstr "%(id)s imaj %(status)s idi." @@ -2892,9 +2424,6 @@ msgstr "İmaj API sürümü, öntanımlı=%s (Env: OS_IMAGE_API_VERSION)" msgid "Image ID to reserve" msgstr "Ayrılacak imaj ID'si" -msgid "Image can be shared" -msgstr "İmaj paylaşılabilir" - #, python-format msgid "" "Image container format. The supported options are: %(option_list)s. The " @@ -2925,9 +2454,6 @@ msgstr "" msgid "Image hash used for verification" msgstr "Doğrulama için kullanılan imaj özeti" -msgid "Image is accessible to the community" -msgstr "İmaj topluluk tarafından erişilebilir" - msgid "Image is accessible to the public" msgstr "İmaj genele açıktır" @@ -2974,9 +2500,6 @@ msgstr "Tüm projeleri dahil et (sadece yönetici)" msgid "Include remote IP prefix from traffic count (default)" msgstr "Trafik sayımından uzak IP önekini ekle (varsayılan)" -msgid "Include reservations count [only valid with --absolute]" -msgstr "Rezervasyon sayısını ekle [sadece --absolute ile geçerlidir]" - msgid "" "Incorrect set of arguments provided. See openstack --help for more details" msgstr "" @@ -2986,22 +2509,10 @@ msgstr "" msgid "Ingress traffic direction from the project point of view" msgstr "Projenin bakış açısından gelen trafik yönü" -#, python-format -msgid "Invalid argument %s, characters ',' and '=' are not allowed" -msgstr "%s geçersiz değişken, ',' ve '=' karakterlerine izin verilmiyor" - #, python-format msgid "Invalid changes-since value: %s" msgstr "Geçersiz değişiklikler-şu değerden beri:%s" -#, python-format -msgid "" -"Invalid nic argument '%s'. Nic arguments must be of the form --nic ." -msgstr "" -"Geçersiz '%s' nic argümanı, Nic argümanları --nic biçiminde olmalıdır." - msgid "Issue new token" msgstr "Yeni jeton yayınla" @@ -3013,18 +2524,9 @@ msgstr "%(private_key)s anahtar dosyası kaydedilemedi: %(exception)s" msgid "Key file %(public_key)s not found: %(exception)s" msgstr "%(public_key)s anahtar dosyası bulunamadı: %(exception)s" -msgid "Keypair to inject into this server (optional extension)" -msgstr "Bu sunucuya enjekte etmek için anahtarlık (isteğe bağlı uzantı)" - msgid "Label to associate with this metering rule (name or ID)" msgstr "Bu ölçüm kuralı ile ilişkili etiket (isim veya ID)" -msgid "Limit the number of containers returned" -msgstr "İade edilen kap sayısını sınırla" - -msgid "Limit the number of objects returned" -msgstr "Döndürülen nesnelerin sayısını sınırla" - #, python-format msgid "List %s which have all given tag(s) (Comma-separated list of tags)" msgstr "" @@ -3069,9 +2571,6 @@ msgstr "QoS özelliklerini listele" msgid "List Service Providers" msgstr "Servis Sağlayıcıları listele" -msgid "List a project's resources" -msgstr "Bir projenin kaynaklarını listele" - msgid "List accessible domains" msgstr "Erişilebilir alanları listele" @@ -3126,21 +2625,12 @@ msgstr "Kullanılabilirlik alanlarını ve durumlarını listele" msgid "List available images" msgstr "Kullanılabilir imajlar listesi" -msgid "List backups" -msgstr "Yedekleri listele" - -msgid "List compute agents" -msgstr "Hesaplama ajanlarını listele" - msgid "List compute availability zones" msgstr "Hesaplama kullanılabilirlik alanlarını listele" msgid "List compute quota" msgstr "Hesaplama kotasını listele" -msgid "List compute services" -msgstr "Hesaplama servislerini listele" - msgid "List consistency group snapshots." msgstr "Tutarlılık grubu anlık görüntülerini listele." @@ -3219,9 +2709,6 @@ msgstr "Verilen duruma göre yüzen IP(ler) listesi ('AKTİF', 'KAPALI')" msgid "List groups" msgstr "Grupları listele" -msgid "List hosts" -msgstr "Sunucuları listele" - msgid "List hypervisors" msgstr "Yönetici arakatman listesi" @@ -3253,13 +2740,6 @@ msgstr "" "Verilen eyleme göre ağ RBAC ilkelerini listele (\"access_as_external\" veya " "\"access_as_shared\")" -msgid "" -"List network RBAC policies according to given object type (\"qos_policy\" or " -"\"network\")" -msgstr "" -"Verilen nesne türüne göre Ağ RBAC politikalarının listesi (\"qos_policy\" " -"veya \"network\")" - msgid "List network agents" msgstr "Ağ ajanlarını listele" @@ -3300,13 +2780,6 @@ msgstr "Ağları fiziksel ağın adına göre listeleme" msgid "List networks according to their name" msgstr "Ağları isimlerine göre listele" -msgid "" -"List networks according to their physical mechanisms. The supported options " -"are: flat, geneve, gre, local, vlan, vxlan." -msgstr "" -"Ağları fiziksel mekanizmalarına göre listeleyin. Desteklenen seçenekler " -"şunlardır: flat, geneve, gre, local, vlan, vxlan." - msgid "List networks according to their project (name or ID)" msgstr "Projelerine göre ağları listele (isim veya ID)" @@ -3333,15 +2806,6 @@ msgstr "Çıktıda verilen adın sadece adres kapsamlarını listeleyin" msgid "List only agents running on the specified host" msgstr "Yalnızca belirtilen ana bilgisayarda çalışan ajanları listele" -msgid "" -"List only agents with the specified agent type. The supported agent types " -"are: bgp, dhcp, open-vswitch, linux-bridge, ofa, l3, loadbalancer, metering, " -"metadata, macvtap, nic." -msgstr "" -"Yalnızca belirtilen aracı türü olan aracıları listeleyin. Desteklenen aracı " -"türleri şunlardır: bgp, dhcp, open-vswitch, linux-bridge, ofa, l3, " -"loadbalancer, metering, metadata, macvtap, nic." - msgid "List only ports attached to this router (name or ID)" msgstr "" "Yalnızca bu yönlendiriciye bağlı bağlantı noktalarını listeleyin (adı veya " @@ -3382,16 +2846,6 @@ msgstr "Sadece genele açık imajları listele" msgid "List only public types" msgstr "Sadece genele açık türleri listele" -msgid "" -"List only servers changed after a certain point of time. The provided time " -"should be an ISO 8061 formatted time. ex 2016-03-04T06:27:59Z ." -msgstr "" -"Yalnızca belirli bir zaman noktasından sonra sunucuları listeleyin. Verilen " -"süre bir ISO 8061 biçiminde olmalıdır. Örn 2016-03-04T06: 27: 59Z ." - -msgid "List only shared images" -msgstr "Sadece paylaşılan imajları listele" - msgid "List only specified service (name only)" msgstr "Sadece belirtilen servisleri listele (sadece isim)" @@ -3403,36 +2857,12 @@ msgstr "" msgid "List only subnet pools of given name in output" msgstr "Verilen isimdeki sadece altağ havuzlarını çıktıda listele" -msgid "" -"List only subnets of a given service type in output e.g.: network:" -"floatingip_agent_gateway. Must be a valid device owner value for a network " -"port (repeat option to list multiple service types)" -msgstr "" -"Çıktıda verilen servisin sadece altağlarını listele örn: network:" -"floatingip_agent_gateway. Bir ağ bağlantı noktası için geçerli bir aygıt " -"sahibi değeri olmalıdır (birden fazla servis türü listelemek için seçeneği " -"tekrarla)" - -msgid "" -"List only subnets of given IP version in output.Allowed values for IP " -"version are 4 and 6." -msgstr "" -"Verilen IP sürümünün sadece altağlarını çıktıda listele. IP sürümü için izin " -"verilen sürümler 4 ve 6." - msgid "List only subnets of given gateway IP in output" msgstr "Verilen geçit IP'sinin sadece alt ağlarını çıktıda listele" msgid "List only subnets of given name in output" msgstr "Verilen isimdeki sadece altağları çıktıda listele" -msgid "" -"List only subnets of given subnet range (in CIDR notation) in output e.g.: --" -"subnet-range 10.10.0.0/16" -msgstr "" -"Verilen alt ağ aralığında (CIDR notasyonu ile) sadece altağları çıktıda " -"listele örn: --subnet-range 10.10.0.0/16" - msgid "" "List only subnets which belong to a given network in output (name or ID)" msgstr "Verilen bir ağa ait sadece altağları çıktıda listele (isim veya ID)" @@ -3462,22 +2892,6 @@ msgstr "" "Yetkilendirilmiş kullanıcı için projeleri listele. Diğer filtrelerin yerini " "alır." -msgid "List qos policies according to their project (name or ID)" -msgstr "Projelerine göre QoS politikalarını listele (isim veya ID)" - -msgid "List qos policies not shared between projects" -msgstr "Projeler arasında paylaştırılmayan qos ilkelerini listele" - -msgid "List qos policies shared between projects" -msgstr "Projeler arasında paylaşılan qos politikalarını listele" - -msgid "List quotas for all projects with non-default quota values" -msgstr "" -"Varsayılan olmayan kota değerlerine sahip tüm projelerin kotalarını listeleme" - -msgid "List recent events of a server" -msgstr "Bir sunucunun son olaylarını listele" - msgid "List recognized commands by group" msgstr "Grup tarafından tanınan komutları listele" @@ -3511,15 +2925,6 @@ msgstr "Gelen ağ trafiğine uygulanan kurallar listesi" msgid "List rules applied to outgoing network traffic" msgstr "Giden ağ trafiğine uygulanan kuralları listele" -msgid "" -"List rules by the IP protocol (ah, dhcp, egp, esp, gre, icmp, igmp, ipv6-" -"encap, ipv6-frag, ipv6-icmp, ipv6-nonxt, ipv6-opts, ipv6-route, ospf, pgm, " -"rsvp, sctp, tcp, udp, udplite, vrrp and integer representations [0-255])." -msgstr "" -"Kuralları IP protokolüne göre listele (ah, dhcp, egp, esp, gre, icmp, igmp, " -"ipv6-encap, ipv6-frag, ipv6-icmp, ipv6-nonxt, ipv6-opts, ipv6-route, ospf, " -"pgm, rsvp, sctp, tcp, udp, udplite, vrrp ve tam sayı gösterimleri [0-255])" - msgid "List security group rules" msgstr "Güvenlik grubu kurallarını listele" @@ -3547,9 +2952,6 @@ msgstr "Servis katalogundaki servisleri listele" msgid "List services on specified host (name only)" msgstr "Belirtilen sunucu üzerindeki servisleri listele (sadece isim)" -msgid "List snapshots" -msgstr "Anlık görüntüleri listele" - msgid "List subnet pools" msgstr "Altağ havuzlarını listele" @@ -3585,9 +2987,6 @@ msgstr "Varsayılan disk bölümü türünü listele" msgid "List trusts" msgstr "Güvenleri listele" -msgid "List user-role assignments" -msgstr "Kullanıcı rol atamalarını listele" - msgid "List users" msgstr "Kullanıcıları listele" @@ -3609,76 +3008,12 @@ msgstr "Disk bölümü türlerini listele" msgid "List volumes" msgstr "Disk bölümlerini listele" -msgid "" -"Listing assignments using role list is deprecated as of the Newton release. " -"Use role assignment list --user --project --names " -"instead." -msgstr "" -"Rol listesi kullanarak liste atamaları, Newton sürümünden itibaren " -"kullanımdan kaldırılmıştır. Onun yerine rol atama listesi --user " -"--project --names komutunu kullanın." - -msgid "" -"Listing assignments using role list is deprecated. Use role assignment list " -"--group --domain --names instead." -msgstr "" -"Rol listesini kullanarak liste atamaları kullanımdan kaldırıldı. Onun yerine " -"role assignment list --group --domain --names " -"kullanın." - -msgid "" -"Listing assignments using role list is deprecated. Use role assignment list " -"--group --project --names instead." -msgstr "" -"Rol listesini kullanarak liste atamaları kullanımdan kaldırıldı. Onun yerine " -"role assignment list --group --project --names " -"kullanın." - -msgid "" -"Listing assignments using role list is deprecated. Use role assignment list " -"--user --domain --names instead." -msgstr "" -"Rol listesini kullanarak liste atamaları kullanımdan kaldırıldı. Bunun " -"yerine role assignment list --user --domain --" -"names kullanın." - -msgid "" -"Listing assignments using role list is deprecated. Use role assignment list " -"--user --domain default --names instead." -msgstr "" -"Rol listesini kullanarak liste atamaları kullanımdan kaldırıldı. Onun yerine " -"role assignment list --user --domain default --names kullanın." - -msgid "" -"Listing assignments using role list is deprecated. Use role assignment list " -"--user --project --names instead." -msgstr "" -"Rol listesini kullanarak liste atamaları kullanımdan kaldırıldı. Bunun " -"yerine, role assignment list --user --project --" -"names kullanın." - -msgid "" -"Listing assignments using user role list is deprecated as of the Newton " -"release. Use role assignment list --user --project --names instead." -msgstr "" -"Atamaları kullanıcı rol listesi kullanarak listelemek Newton sürümünden " -"itibaren kullanımdan kaldırılmıştır. Onun yerine role assignment list --user " -" --project --names komutunu kullanın." - msgid "Lists all volume transfer requests." msgstr "Tüm disk bölümü aktarım isteklerini listele." msgid "Local filename(s) to upload" msgstr "Yüklenecek yerel dosya ad(lar)ı" -msgid "Lock server(s). A non-admin user will not be able to execute actions" -msgstr "" -"Sunucu(ları/yu) kilitle. Yönetici olmayan kullanıcılar işlem yapamayacaktır." - -msgid "Login name (ssh -l option)" -msgstr "Giriş adı (ssh -l seçeneği)" - msgid "MAC address of this port (admin only)" msgstr "Bu bağlantı noktasının MAC adresi (yalnızca yönetici)" @@ -3729,47 +3064,14 @@ msgstr "Disk bölümünü ön yüklemesiz olarak işaretle (varsayılan)" msgid "Maximum bandwidth in kbps" msgstr "En büyük bant genişliği kbps cinsinden" -msgid "Maximum burst in kilobits, 0 means automatic" -msgstr "Kilo bit cinsinden en büyük atış, 0 otomatik anlamına gelir" +msgid "Maximum number of servers to launch (default=1)" +msgstr "Başlatılması gereken en fazla sunucu sayısı (varsayılan=1)" -msgid "Maximum number of backups to display" -msgstr "En fazla gösterilecek yedek sayısı " +msgid "Memory size in MB (default 256M)" +msgstr "MB cinsinden bellek boyutu (varsayılan 256M)" -msgid "Maximum number of flavors to display" -msgstr "Gösterilecek en fazla flavor sayısı" - -msgid "Maximum number of images to display." -msgstr "Gösterilecek en fazla imaj sayısı." - -msgid "" -"Maximum number of servers to display. If limit equals -1, all servers will " -"be displayed. If limit is greater than 'osapi_max_limit' option of Nova API, " -"'osapi_max_limit' will be used instead." -msgstr "" -"Gösterilecek en fazla sunucu sayısı. Eğer sınır -1'e eşitse, tüm sunucular " -"gösterilir. Eğer limit Nova API'nın 'osapi_max_limit' seçeneğinden daha " -"büyükse, onun yerine 'osapi_max_limit' kullanılacaktır." - -msgid "Maximum number of servers to launch (default=1)" -msgstr "Başlatılması gereken en fazla sunucu sayısı (varsayılan=1)" - -msgid "Maximum number of snapshots to display" -msgstr "Gösterilecek en büyük anlık görüntü sayısı" - -msgid "Maximum number of volumes to display" -msgstr "Gösterilecek en çok disk bölümü sayısı" - -msgid "Memory size in MB (default 256M)" -msgstr "MB cinsinden bellek boyutu (varsayılan 256M)" - -msgid "" -"Metainfo for the flavor profile. This becomes required if --driver is " -"missing and vice versa" -msgstr "" -"Flavor profili için Metainfo. --driver eksikse bu gereklidir ya da tam tersi" - -msgid "Meter rule (ID only)" -msgstr "Ölçek kuralı (sadece ID)" +msgid "Meter rule (ID only)" +msgstr "Ölçek kuralı (sadece ID)" msgid "Meter rule to delete (ID only)" msgstr "Silinecek ölçüm kuralı (sadece ID)" @@ -3780,9 +3082,6 @@ msgstr "Silinecek ölçek (isim veya ID)" msgid "Meter to display (name or ID)" msgstr "Gösterilecek ölçek (isim veya ID)" -msgid "Migrate server to different host" -msgstr "Farklı konakçıya sunucuyu göç ettir" - msgid "Migrate volume to a new host" msgstr "Disk bölümünü yeni bir sunucuya göç ettir" @@ -3872,9 +3171,6 @@ msgid "" "Name of the physical network over which the virtual network is implemented" msgstr "Sanal ağın üzerinde uygulandığı fiziksel ağın adı" -msgid "Name of the snapshot" -msgstr "Anlık görüntü ismi" - msgid "Name of this port" msgstr "Bu bağlantı noktasının ismi" @@ -3884,12 +3180,6 @@ msgstr "Disk bölümü anabilgisayarının ismi" msgid "Name or ID of project to show usage for" msgstr "Kullanımı gösterilecek projenin isim veya ID'si" -msgid "Name or ID of security group to remove from server" -msgstr "Sunucudan kaldırılacak güvenlik grubunun isim veya ID'si" - -msgid "Name or ID of server to use" -msgstr "Kullanılacak sunucunun isim veya ID'si" - #, python-format msgid "Network API version, default=%s (Env: OS_NETWORK_API_VERSION)" msgstr "Ağ API sürümü, varsayılan=%s (Env: OS_NETWORK_API_VERSION)" @@ -3948,9 +3238,6 @@ msgstr "Bir ajandan kaldırılacak ağ (isim veya ID)" msgid "Network to display (name or ID)" msgstr "Gösterilecek ağ (isim veya ID)" -msgid "Network to fetch an IP address from (name or ID)" -msgstr "IP adresi çekilecek ağ (isim veya ID)" - msgid "Network to modify (name or ID)" msgstr "Düzenlenecek ağ (isim veya ID)" @@ -3981,21 +3268,6 @@ msgstr "Yeni adres kapsam ismi" msgid "New aggregate name" msgstr "Yeni küme ismi" -msgid "New backup description" -msgstr "Yeni yedek açıklaması" - -msgid "New backup name" -msgstr "Yeni yedek ismi" - -msgid "" -"New backup state (\"available\" or \"error\") (admin only) (This option " -"simply changes the state of the backup in the database with no regard to " -"actual status, exercise caution when using)" -msgstr "" -"Yeni yedek durumu (\"kullanılabilir\" veya \"hata\") (sadece yönetici) (Bu " -"seçenek, veritabanındaki yedeklemenin durumunu gerçek durumu dikkate almadan " -"değiştirir, kullanırken dikkatli olun.)" - msgid "New consistency group description" msgstr "Yeni tutarlılık grubu açıklaması" @@ -4109,9 +3381,6 @@ msgstr "Yeni sunucu grup ismi" msgid "New server name" msgstr "Yeni sunucu ismi" -msgid "New server state (valid value: active, error)" -msgstr "Yeni sunucu durumu (geçerli değer: aktif, hata)" - msgid "New service description" msgstr "Yeni servis tanımı" @@ -4218,10 +3487,6 @@ msgstr "'%s' türünde, isminde veya ID'si ile bir servis kataloğu yok." msgid "No service with a type, name or ID of '%s' exists." msgstr "'%s' türünde, isminde veya ID'si ile bir servis bulunamadı." -#, python-format -msgid "No tags associated with the %s" -msgstr "%s ile ilişkilendirilmiş hiç etiket yok" - msgid "Number of backups to keep (default: 1)" msgstr "Tutulacak yedekleme sayısı (varsayılan: 1)" @@ -4256,9 +3521,6 @@ msgstr "Bir veya birden fazla ayar kaldırma işlemi başarısız" msgid "Only an authorized user may issue a new token." msgstr "Yalnızca yetkili bir kullanıcı yeni bir jeton verebilir." -msgid "Only display deleted servers (Admin only)." -msgstr "Sadece silinen sunucuları göster (sadece admin)." - msgid "Only return hosts in the availability zone" msgstr "Sadece kullanılabilirlik bölgesindeki sunucuları döndürür" @@ -4277,9 +3539,6 @@ msgstr "Işletim sistemi dağıtım sürümü" msgid "Optional backup container name" msgstr "Seçimli yedek kap ismi" -msgid "Options in ssh_config(5) format (ssh -o option)" -msgstr "ssh_config(5) biçemi içerisindeki seçenekler (ssh -o seçeneği)" - msgid "Original user password" msgstr "Orijinal kullanıcı parolası" @@ -4298,18 +3557,12 @@ msgstr "Parolalar eşleşmedi, parola değiştirilmedi" msgid "Pause server(s)" msgstr "Sunucu(ları/yu) durdur" -msgid "Perform a block live migration" -msgstr "Bir blok gerçek göç gerçekleştir" - msgid "Perform a hard or soft server reboot" msgstr "Sert veya yumuşak sunucu yeniden başlatmayı gerçekleştir" msgid "Perform a hard reboot" msgstr "Sert yeniden başlatmayı gerçekleştir" -msgid "Perform a shared live migration (default)" -msgstr "Paylaşımlı canlı göç gerçekleştir (varsayılan)" - msgid "Perform a soft reboot" msgstr "Yumuşak yeniden başlatmayı gerçekleştir" @@ -4380,9 +3633,6 @@ msgstr "İmajı silinmekten koru" msgid "Print image size in a human-friendly format." msgstr "İmaj boyutunu insan dostu bir biçimde bastırın." -msgid "Private key file (ssh -i option)" -msgstr "Gizli anahtar dosyası (ssh -i seçeneği)" - msgid "Project and User must be specified" msgstr "Proje ve kullanıcı belirtilmeli" @@ -4392,15 +3642,9 @@ msgstr "Devredilen proje (isim veya ID) (gerekli)" msgid "Project description" msgstr "Proje açıklaması" -msgid "Project must be specified" -msgstr "Proje belirtilmek zorunda" - msgid "Project that consumer wants to access (name or ID) (required)" msgstr "Alıcının erişmek istediği proje (isim veya ID) (gerekli)" -msgid "Project to associate with image (name or ID)" -msgstr "İmaj ile ilişkili proje (isim veya ID)" - msgid "Project to clean (name or ID)" msgstr "Temizlenecek proje (isim veya ID)" @@ -4525,16 +3769,6 @@ msgstr "" msgid "Public or private key to display (name only)" msgstr "Gösterilecek açık veya kapalı anahtar (sadece isim)" -msgid "Put server in rescue mode" -msgstr "Sunucuyu kurtarma kipine getir" - -msgid "" -"Python module path to driver. This becomes required if --metainfo is missing " -"and vice versa" -msgstr "" -"Python modülünün sürücüye yolu. --metainfo eksikse bu gereklidir ya da tam " -"tersi" - msgid "QoS policy that contains the rule (name or ID)" msgstr "Kuralı içeren QoS politikası (isim veya ID)" @@ -4607,18 +3841,12 @@ msgstr "Düzenlenecek bölge" msgid "Regular expression to match IP addresses" msgstr "IP adresleriyle eşleşen düzenli ifadeler" -msgid "Regular expression to match IPv6 addresses" -msgstr "IPv6 adresleriyle eşleşen düzenli ifadeler" - msgid "Regular expression to match instance name (admin only)" msgstr "Sunucu adıyla eşleşen düzenli ifadeler (yalnızca yönetici)" msgid "Regular expression to match names" msgstr "Adları eşleştirmek için düzenli ifadeler" -msgid "Reject the image membership" -msgstr "İmaj üyeliğini red et" - msgid "" "Remote IDs to associate with the Identity Provider (repeat option to provide " "multiple values)" @@ -4626,13 +3854,6 @@ msgstr "" "Kimlik Sağlayıcısı ile ilişkilendirilecek uzak kimlikler (birden fazla değer " "sağlamak için seçeneği tekrarlayın)" -msgid "" -"Remote IP address block (may use CIDR notation; default for IPv4 rule: " -"0.0.0.0/0)" -msgstr "" -"Uzak IP adres bloğu (CIDR notasyonu kullanılabilir; IPv4 kuralı için " -"varsayılan: 0.0.0.0/0)" - msgid "Remote security group (name or ID)" msgstr "Uzak güvenlik grubu (isim veya ID)" @@ -4740,43 +3961,24 @@ msgstr "Sunucudan güvenlik grubunu kaldır" msgid "Remove service profile from network flavor" msgstr "Servis profilini ağ flavor'ından kaldır" -msgid "" -"Remove subnet pool prefixes (in CIDR notation). (repeat option to unset " -"multiple prefixes)." -msgstr "" -"Alt ağ havuzu öneklerini kaldır (CIDR gösteriminde). (Birden fazla önekin " -"ayarını kaldırmak için seçeneği tekrarlayın)." - msgid "Remove the QoS policy attached to the port" msgstr "Bağlantı noktasına eklenmiş QoS politikasını kaldır" msgid "Remove the QoS policy attached to this network" msgstr "Bu ağa bağlı QoS politikasını kaldırın" -msgid "Remove the encryption type for this volume type (admin oly)" -msgstr "Bu disk bölümü için şifreleme türünü sil (sadece yönetici)" - msgid "Remove the encryption type for this volume type (admin only)" msgstr "Bu disk bölümü türü için şifreleme türü kaldırıldı (sadece yönetici)" msgid "Remove user from group" msgstr "Kullanıcıyı gruptan kaldır" -msgid "Remove volume from server" -msgstr "Disk bölümünü sunucudan kaldır" - msgid "Remove volume(s) from consistency group" msgstr "Tutarlılık grubundan disk bölümlerini sil" -msgid "Removes a role assignment from domain/project : user/group" -msgstr "Bir rol atamasını alan/proje : kullanıcı/gruptan kaldırır." - msgid "Removes volume type access to project (name or ID) (admin only)" msgstr "Projeye disk türü erişimini kaldırır (isim veya ID) (sadece yönetici)" -msgid "Replicated volume to clone (name or ID)" -msgstr "Klonlanacak yinelenmiş disk bölümü (isim veya ID)" - msgid "Request ID of the event to show (ID only)" msgstr "Gösterilecek olayın ID'sini iste (sadece ID)" @@ -4795,15 +3997,9 @@ msgstr "İmaj üyeliğini 'beklemede' olarak sıfırlayın" msgid "Resize server to specified flavor" msgstr "Sunucuyu belirtilen flavor'a yeniden boyutlandır" -msgid "Restore backup" -msgstr "Yedeği yeniden yükle" - msgid "Restore server from rescue mode" msgstr "Kurtarma kipinden sunucuyu onar" -msgid "Restore server state before resize" -msgstr "Sunucu durumunu yeniden boyutlandırmadan önceki haline geri getir" - msgid "Restore server(s)" msgstr "Sunucu(ları/yu) onar " @@ -4828,13 +4024,6 @@ msgstr "Mevcut rolü döndür" msgid "Return existing user" msgstr "Mevcut kullanıcıyı döndür" -msgid "" -"Return the auto allocated topology for a given project. Default is current " -"project" -msgstr "" -"Belirli bir proje için otomatik olarak ayrılan topolojiyi döndürür. " -"Varsayılan geçerli projedir" - #, python-format msgid "Returning existing domain %s" msgstr "Mevcut alanı döndür %s" @@ -4909,15 +4098,6 @@ msgstr "" msgid "Roll up items with " msgstr "Elemanları ile toparla" -msgid "" -"Route to be removed from this subnet e.g.: destination=10.10.0.0/16," -"gateway=192.168.71.254 destination: destination subnet (in CIDR notation) " -"gateway: nexthop IP address (repeat option to unset multiple host routes)" -msgstr "" -"Bu altağdan silinecek yön örn: hedef=10.10.0.0/16,geçit=192.168.71.254 hedef:" -"hedef altağ (CIDR notasyonunda) geçit: bir sonraki uğranacak IP adresi " -"(birden fazla sunucu yönü ayarı kaldırmak için seçeneği tekrar et)" - #, python-format msgid "Router does not contain route %s" msgstr "Yönlendirici %s yönünü içermiyor" @@ -4949,23 +4129,6 @@ msgstr "Alt ağın ekleneceği yönlendirici (isim veya ID)" msgid "Router(s) to delete (name or ID)" msgstr "Silinecek yönlendirici(ler) (isim veya ID)" -msgid "" -"Routes associated with the router destination: destination subnet (in CIDR " -"notation) gateway: nexthop IP address (repeat option to set multiple routes)" -msgstr "" -"Yönlendirici hedefi ile ilişkili yönlendiriciler: hedef altağ (CIDR " -"notasyonunda) geçit: bir sonraki atlanacak IP adresi (birden fazla " -"yönlendirici ayarlamak için seçeneği tekrarla)" - -msgid "" -"Routes to be removed from the router destination: destination subnet (in " -"CIDR notation) gateway: nexthop IP address (repeat option to unset multiple " -"routes)" -msgstr "" -"Yönlendirici hedefinden kaldırılacak yönlendiriciler: hedef alt ağ (CIDR " -"notasyonuyla) geçit: atlanacak sonraki IP adresi (birden fazla " -"yönlendiricinin ayarını kaldırmak için seçeneği tekrarla)" - #, python-format msgid "Rule ID %(rule_id)s not found" msgstr "%(rule_id)s kural ID'si bulunamadı" @@ -4976,10 +4139,6 @@ msgstr "Gelen ağ trafiğine uygulanacak kural (varsayılan)" msgid "Rule applies to outgoing network traffic" msgstr "Dışarıya doğru ağ trafiğine uygulanacak kural" -#, python-format -msgid "Rule type \"%(rule_type)s\" only requires arguments %(args)s" -msgstr "\"%(rule_type)s\" kural türü sadece %(args)s argümanlarını gerektirir" - msgid "SSH to server" msgstr "Sunucuya SSH ile bağlan" @@ -4992,29 +4151,6 @@ msgstr "Kap içeriğini yerel olarak kaydet" msgid "Save object locally" msgstr "Nesneyi yerel olarak kaydet" -msgid "" -"Scale server to a new flavor.\n" -"\n" -"A resize operation is implemented by creating a new server and copying the\n" -"contents of the original disk into a new one. It is also a two-step process " -"for\n" -"the user: the first is to perform the resize, the second is to either " -"confirm\n" -"(verify) success and release the old server, or to declare a revert to " -"release\n" -"the new server and restart the old one." -msgstr "" -"Sunucuyu yeni bir flavor'a ölçekle.\n" -"\n" -"Bir yeniden boyutlandırma işlemi yeni bir sunucu oluşturma ve orijinal \n" -"diskteki içeriğin yeni bir tanesine kopyalanması ile gerçekleştirilir. Bu " -"ayrıca\n" -"kullanıcı için iki adımlı bir süreçtir: birincisi yeniden boyutlandırmayı " -"gerçekleştirmek,\n" -"ikincisi ya başarılı olduğunu doğrulamak ve eski sunucuyu silmek, ya da " -"yenisini silip\n" -"eskisini yeniden başlatma isteğini belirtmek." - msgid "Search by flavor (name or ID)" msgstr "Flavor'a göre ara (isim veya ID)" @@ -5030,9 +4166,6 @@ msgstr "Projeye göre ara (sadece yönetici) (isim veya ID)" msgid "Search by server status" msgstr "Sunucu durumuna göre ara" -msgid "Search by user (admin only) (name or ID)" -msgstr "Kullanıcıya göre ara (sadece yönetici) (isim veya ID)" - msgid "Secret associated with (required)" msgstr " ile ilişkili gizli anahtar (gerekli)" @@ -5045,16 +4178,6 @@ msgstr "Gösterilecek güvenlik grubu kuralları (sadece ID)" msgid "Security group rule(s) to delete (ID only)" msgstr "Silinecek güvenlik grubu kuralları (sadece ID)" -msgid "Security group to add (name or ID)" -msgstr "Eklenecek güvenlik grubu (isim veya ID)" - -msgid "" -"Security group to assign to this server (name or ID) (repeat option to set " -"multiple groups)" -msgstr "" -"Bu sunucuya atanan güvenlik grubu (isim veya ID) (birden fazla grup " -"ayarlamak için seçeneği tekrar edin)" - msgid "" "Security group to associate with this port (name or ID) (repeat option to " "set multiple security groups)" @@ -5086,9 +4209,6 @@ msgstr "" "Ağ türüne göre bölüm belirteci, vlan ağ türü için VLAN ID, geneve, gre ve " "vxlan ağ türleri için tünel ID" -msgid "Select an availability zone for the server" -msgstr "Sunucu için kullanılabilirlik bölgesi seçin" - msgid "Server (name or ID)" msgstr "Sunucu (isim veya ID)" @@ -5107,18 +4227,12 @@ msgstr "İmaj oluşturulacak sunucu (isim veya ID)" msgid "Server to list events (name or ID)" msgstr "Olayları listelenecek sunucu (isim veya ID)" -msgid "Server to receive the IP address (name or ID)" -msgstr "IP adresin alınacağı sunucu (isim veya ID)" - msgid "Server to receive the fixed IP address (name or ID)" msgstr "Sabit IP adresleri alınacak sunucu (isim veya ID)" msgid "Server to receive the floating IP address (name or ID)" msgstr "Kayan IP adresi alınacak sunucu (isim veya ID)" -msgid "Server to remove the IP address from (name or ID)" -msgstr "IP adresin kaldırılacağı sunucu (isim veya ID)" - msgid "Server to remove the fixed IP address from (name or ID)" msgstr "Sabit IP adresin silineceği sunucu (isim veya ID)" @@ -5200,39 +4314,9 @@ msgstr "Gösterilecek servis (tür, isim veya ID)" msgid "Service to modify (type, name or ID)" msgstr "Düzenlenecek servis (tür, isim veya ID)" -msgid "" -"Service type for this subnet e.g.: network:floatingip_agent_gateway. Must be " -"a valid device owner value for a network port (repeat option to set multiple " -"service types)" -msgstr "" -"Bu alt ağ için servis türü örn: network:floatingip_agent_gateway. Bir ağ " -"bağlantı noktası için geçerli bir cihaz sahibi değeri olmalıdır (birden " -"fazla servis türü ayarlamak için seçeneği tekrarlayın)" - -msgid "" -"Service type to be removed from this subnet e.g.: network:" -"floatingip_agent_gateway. Must be a valid device owner value for a network " -"port (repeat option to unset multiple service types)" -msgstr "" -"Bu altağdan kaldırılacak servis türü örn: network:floatingip_agent_gateway. " -"Bir ağ bağlantı noktası için geçerli bir aygıt sahibi değeri olmalıdır " -"(birden fazla servis türünün ayarını kaldırmak için seçeneği tekrarlayın)" - -msgid "" -"Service type to which the flavor applies to: e.g. VPN (See openstack network " -"service provider list for loaded examples.)" -msgstr "" -"Flavor'ın uygulanacağı hizmet türü: ör. VPN (Yüklü sunucular için açık devre " -"ağ servis sağlayıcısı listesine bakın.)" - msgid "Service(s) to delete (type, name or ID)" msgstr "Silinecek servis(ler) (tür, isim veya ID)" -msgid "Set DNS name to this port (requires DNS integration extension)" -msgstr "" -"DNS adını bu bağlantı noktasına ayarlayın (DNS entegrasyon uzantısı " -"gerektirir)" - msgid "Set Network QoS rule properties" msgstr "Ağ QoS kural özelliklerini ayarla" @@ -5353,9 +4437,6 @@ msgstr "" msgid "Set availability zone name" msgstr "Kullanılabilirlik bölge adını ayarla" -msgid "Set compute agent properties" -msgstr "Hesaplama ajanı özelliklerini ayarla" - msgid "Set compute service properties" msgstr "Hesaplama servisi özelliklerini ayarla" @@ -5371,24 +4452,9 @@ msgstr "Kap özelliklerini ayarla" msgid "Set credential properties" msgstr "Kimlik bilgileri özelliklerini ayarla" -msgid "" -"Set data plane status of this port (ACTIVE | DOWN). Unset it to None with " -"the 'port unset' command (requires data plane status extension)" -msgstr "" -"Bu bağlantı noktasının veri düzlemi durumunu ayarlayın (ACTIVE | DOWN). " -"'port unset' komutu ile None'a getirin (veri düzlemi durum uzantısı " -"gerektirir)" - msgid "Set default project (name or ID)" msgstr "Varsayılan projeyi ayarla (isim veya ID)" -msgid "" -"Set default quota for subnet pool as the number ofIP addresses allowed in a " -"subnet" -msgstr "" -"Bir alt ağda izin verilen IP adreslerinin sayısı olarak alt ağ havuzu için " -"varsayılan kota ayarla" - msgid "Set domain properties" msgstr "Alan özelliklerini ayarla" @@ -5416,9 +4482,6 @@ msgstr "Yüzen IP açıklaması ayarla" msgid "Set group properties" msgstr "Grup özelliklerini ayarla" -msgid "Set host properties" -msgstr "Sunucu özelliklerini ayarla" - msgid "Set identity provider description" msgstr "Kimlik sağlayıcı tanımlarını ayarla" @@ -5467,9 +4530,6 @@ msgstr "Ağ bölüm ismi ayarla" msgid "Set network segment properties" msgstr "Ağ kesimi özelliklerini ayarla" -msgid "Set new root password (interactive only)" -msgstr "Yeni root parolası ayarla (sadece interaktif)" - msgid "Set object properties" msgstr "Nesne özelliklerini ayarla" @@ -5491,18 +4551,12 @@ msgstr "Proje ismini ayarla" msgid "Set project properties" msgstr "Proje özelliklerini ayarla" -msgid "Set quotas for " -msgstr " için kotaları ayarla" - msgid "Set quotas for a specific " msgstr "Belirli bir için kota ayarla" msgid "Set quotas for project or class" msgstr "Proje veya sınıf için kotaları ayarla" -msgid "Set quotas for this project or class (name/ID)" -msgstr "Bu proje veya sınıf için (isim/ID) kotaları ayarla" - msgid "Set region properties" msgstr "Bölgenin özelliklerini ayarla" @@ -5546,9 +4600,6 @@ msgstr "Servis özelliklerini ayarla" msgid "Set service provider properties" msgstr "Servis sağlayıcı özelliklerini ayarlayın" -msgid "Set snapshot properties" -msgstr "Anlık görüntü özelliklerini ayarlayın" - msgid "Set subnet description" msgstr "Alt ağ açıklaması ayarla" @@ -5580,32 +4631,6 @@ msgstr "Alt ağ havuzu özellikleri ayarla" msgid "Set subnet properties" msgstr "Altağ özelliklerini ayarla" -msgid "" -"Set the class that provides encryption support for this volume type (e.g " -"\"LuksEncryptor\") (admin only) (This option is required when setting " -"encryption type of a volume for the first time. Consider using other " -"encryption options such as: \"--encryption-cipher\", \"--encryption-key-size" -"\" and \"--encryption-control-location\")" -msgstr "" -"Bu birim türü için şifreleme desteği sağlayan sınıfı ayarlayın (örn " -"\"LuksEncryptor\") (sadece yönetici) (Bu seçenek, bir disk bölümü şifreleme " -"türünü ilk kez ayarlarken gereklidir. Şu diğer şifreleme seçeneklerini " -"kullanmayı düşünün: \"--encryption-cipher\", \"--encryption-key-size\" ve " -"\"--encryption-control-location\")" - -msgid "" -"Set the class that provides encryption support for this volume type (e.g " -"\"LuksEncryptor\") (admin only) (This option is required when setting " -"encryption type of a volume. Consider using other encryption options such " -"as: \"--encryption-cipher\", \"--encryption-key-size\" and \"--encryption-" -"control-location\")" -msgstr "" -"Bu disk bölümü türü için şifreleme desteği sağlayan sınıfı ayarla (örn " -"\"LuksEncryptor\") (sadece yönetici) (Bir disk bölümünün şifreleme türü " -"ayarlanırken bu seçenek zorunludur. \"--encryption-cipher\", \"--encryption-" -"key-size\" ve \"--encryption-control-location\" gibi diğer şifreleme " -"seçeneklerini kullanmayı göz önünde bulundurun)" - msgid "" "Set the encryption algorithm or mode for this volume type (e.g \"aes-xts-" "plain64\") (admin only)" @@ -5642,9 +4667,6 @@ msgstr "" "encryption-cipher\", \"--encryption-key-size\" ve \"--encryption-provider\" " "gibi diğer şifreleme seçeneklerini kullanmayı göz önünde bulundurun)" -msgid "Set the password on the rebuilt instance" -msgstr "Yeniden oluşturulmuş sunucudaki parolayı ayarla" - msgid "Set the router as highly available (disabled router only)" msgstr "" "Yönlendiriciyi yüksek kullanılabilirlikli olarak ayarla (sadece " @@ -5672,17 +4694,6 @@ msgstr "Bunu varsayılan olmayan bir ağ QoS politikası olarak ayarla" msgid "Set this as a non-default subnet pool" msgstr "Bunu varsayılan olmayan altağ havuzu olarak ayarla" -msgid "" -"Set this network as an external network (external-net extension required)" -msgstr "" -"Bu şebekeyi harici bir ağ olarak ayarlayın (harici ağ uzantısı gerekir)" - -msgid "Set this network as an internal network" -msgstr "Bu şebekeyi dahili bir şebeke olarak ayarlayın" - -msgid "Set this network as an internal network (default)" -msgstr "Bu ağı dahili bir ağ olarak ayarlayın (varsayılan)" - msgid "Set this subnet pool as not shared" msgstr "Bu alt ağ havuzunu paylaşılmayan olarak ayarlayın" @@ -5754,9 +4765,6 @@ msgstr "Projeler arasında adres kapsamını paylaş" msgid "Share the network between projects" msgstr "Ağları projeler arasında paylaşın" -msgid "Shelve server(s)" -msgstr "Sunucu(yu/ları) raftan çıkart" - msgid "Show API extension" msgstr "API uzantısını göster" @@ -5804,12 +4812,6 @@ msgstr "" "Tüm projeler için detayları göster. Sadece yönetici. (varsayılan olarak " "False)" -msgid "" -"Show limits for a specific project (name or ID) [only valid with --absolute]" -msgstr "" -"Belirli bir proje için sınırları göster (isim veya ID) [sadece --absolute " -"ile geçerli]" - msgid "Show network IP availability details" msgstr "Ağ IP kullanılabilirlik detaylarını göster" @@ -5832,30 +4834,12 @@ msgstr "Parolaları düz metin olarak göster" msgid "Show project's subtree (children) as a list" msgstr "Projenin alt projelerini (çocuklarını) bir liste olarak göster" -msgid "Show quotas for " -msgstr " için kotaları göster" - -msgid "Show quotas for project or class" -msgstr "Proje veya sınıf için kotaları göster" - -msgid "Show quotas for this project or class (name or ID)" -msgstr "Bu proje veya sınıf (isim veaya ID) için kotaları göster" - -msgid "Show rate limits" -msgstr "Oran sınırları göster" - msgid "Show resource usage for a single project" msgstr "Tek bir proje için kaynak kullanımını göster" msgid "Show serial console URL" msgstr "serial konsol URL'ini göster" -msgid "Show server details" -msgstr "Sunucu detaylarını göster" - -msgid "Show server event details" -msgstr "Sunucu olay detaylarını listele" - msgid "Show server's console output" msgstr "Sunucunun konsol çıktısını göster" @@ -5868,9 +4852,6 @@ msgstr "Servis katalog bilgisini göster" msgid "Show the project's parents as a list" msgstr "Projenin üst projelerini bir liste olarak göster" -msgid "Show volume details" -msgstr "Disk bölümü detaylarını göster" - msgid "Show volume transfer request details." msgstr "Disk bölümü aktarım isteği detaylarını göster." @@ -5880,9 +4861,6 @@ msgstr "xvpvnc konsol URL'ini göster" msgid "Size of image data (in bytes)" msgstr "İmaj verisinin boyutu (bayt cinsinden)" -msgid "Skip flavor and image name lookup." -msgstr "Flavor veya imaj ismi aramasını atla." - msgid "Snapshot to backup (name or ID)" msgstr "Yedeği alınacak anlık görüntü (isim veya ID)" @@ -5911,40 +4889,10 @@ msgstr "" "(varsayılan: ad:artan), virgülle ayrılmış olarak birden çok anahtar ve yön " "belirlenebilir" -msgid "Specific service endpoint to use" -msgstr "Kullanılacak belirli bir servis uç noktası" - msgid "Specifies if the role grant is inheritable to the sub projects" msgstr "" "Rol atamasının alt projelere miras bırakılıp bırakılmayacağını belirler" -msgid "" -"Specify a gateway for the subnet. The three options are: : " -"Specific IP address to use as the gateway, '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')." -msgstr "" -"Alt ağ için bir geçit belirle. Üç seçenek: : geçit olarak " -"kullanılacak belirli bir IP adresi, 'auto': Geçit adresi ağdan otomatik " -"olarak seçilmelidir, 'none': Bu alt ağ bir geçit kullanmayacak, örn: --" -"gateway 192.168.9.1, --gateway auto, --gateway none (varsayılan 'auto')." - -msgid "" -"Specify a gateway for the subnet. The options are: : 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." -msgstr "" -"Alt ağ için bir geçit belirle. Seçenekler: : Geçit olarak " -"kullanılacak belirli bir IP adresi, 'none': Bu alt ağ geçit olarak " -"kullanılmayacak, örn: --gateway 192.168.9.1, --gateway none." - -msgid "Specify an alternate project (name or ID)" -msgstr "Alternatif bir proje belirle (isim veya ID)" - -msgid "Specify an alternate user (name or ID)" -msgstr "Alternatif bir kullanıcı belirle (isim veya ID)" - msgid "Specify if this network should be used as the default external network" msgstr "" "Bu ağın varsayılan dış ağ olarak kullanılması gerekip gerekmediğini " @@ -5957,19 +4905,6 @@ msgstr "" "auto veya none --nic'i belirtmek başka bir --nic, --network veya --port " "değeriyle kullanılamaz." -msgid "" -"Specifying the auth-key as a positional argument has been deprecated. " -"Please use the --auth-key option in the future." -msgstr "" -"Yetki anahtarını pozisyonel argüman olarak belirleme kullanımdan " -"kaldırıldı. Lütfen gelecekte --auth-key seçeneğini kullanın." - -msgid "Start server(s)." -msgstr "Sunucu(ları/yu) başlat." - -msgid "Stop server(s)." -msgstr "Sunucu(ları/yu) durdur." - #, python-format msgid "Subnet does not contain %(option)s %(value)s" msgstr "Ağ %(option)s %(value)s'yü içermiyor" @@ -5977,10 +4912,6 @@ msgstr "Ağ %(option)s %(value)s'yü içermiyor" msgid "Subnet on which you want to create the floating IP (name or ID)" msgstr "Yüzen IP'yi oluşturmak istediğiniz alt ağ (isim veya ID)" -#, python-format -msgid "Subnet pool does not contain prefix %s" -msgstr "%s önekini içermeyen altağ havuzu" - msgid "Subnet pool from which this subnet will obtain a CIDR (Name or ID)" msgstr "Bu alt ağın bir CIDR alacağı alt ağ havuzu (isim veya ID)" @@ -6024,242 +4955,27 @@ msgstr "" "%s'e eklenecek etiketler (birden fazla etiket ayarlamak için seçeneği tekrar " "et)" -#, python-format -msgid "Tag to be removed from the %s (repeat option to remove multiple tags)" -msgstr "" -"%s'den kaldırılacak etiket (birden fazla etiket silmek için seçenekleri " -"tekrarlayın)" - -msgid "Target hostname" -msgstr "Hedef sunucu adı" - msgid "Thaw and enable the specified volume host" msgstr "Belirtilen disk bölümü barındırıcısını çöz ve etkinleştir" -#, python-format -msgid "The %(old)s option is deprecated, please use %(new)s instead." -msgstr "" -"%(old)s seçeneği kullanımdan kaldırıldı, lütfen onun yerine %(new)s kullanın." - -msgid "The --clear-routes option is deprecated, please use --no-route instead." -msgstr "" -"--clear-routes seçeneği kullanımdan kaldırıldı, onun yerine --no-route " -"kullanın." - -msgid "The --device-id option is deprecated, please use --device instead." -msgstr "" -"--device-id seçeneği kullanımdan kaldırıldı, lütfen onun yerine --device " -"komutunu kullanın." - -msgid "The --host-id option is deprecated, please use --host instead." -msgstr "" -"--host-id seçeneği kullanımdan kaldırıldı, lütfen onun yerine --host " -"komutunu kullanın." - -msgid "The --owner option is deprecated, please use --project instead." -msgstr "" -"--owner seçeneği kullanımdan kaldırıldı, onun yerine lütfen --project " -"seçeneğini kullanın." - msgid "" "The ID of the volume backend replication target where the host will failover " "to (required)" msgstr "" "Ana bilgisayarın üstleneceği disk bölümü arkaplan kopyası ID'si (zorunlu)" -msgid "" -"The argument --type is deprecated, use service create --name " -"type instead." -msgstr "" -"--type argümanı kullanımdan kaldırılmıştır, onun yerine service create --" -"name type komutunu kullanın." - -msgid "" -"The attribute(s) of the exsiting remote volume snapshot (admin required) " -"(repeat option to specify multiple attributes) e.g.: '--remote-source source-" -"name=test_name --remote-source source-id=test_id'" -msgstr "" -"Varolan uzak birimin anlık görüntüsünün nitelikleri (yönetici gerekli) " -"(birden fazla özellik belirlemek için seçeneği tekrarlayın) örn: '--remote-" -"source source-name=test_name --remote-source source-id=test_id'" - -msgid "The last backup of the previous page (name or ID)" -msgstr "Bir önceki sayfanın son yedeği (isim veya ID)" - -msgid "The last flavor ID of the previous page" -msgstr "Bir önceki sayfanın son flavor ID'si" - -msgid "" -"The last image of the previous page. Display list of images after marker. " -"Display all images if not specified. (name or ID)" -msgstr "" -"Bir önceki sayfanın son imajı. İşaretçiden sonraki imajların listesini " -"görüntüle. Belirtilmemişse tüm imajları görüntüleyin. (isim veya ID)" - -msgid "" -"The last server of the previous page. Display list of servers after marker. " -"Display all servers if not specified. (name or ID)" -msgstr "" -"Bir önceki sayfanın son sunucusu. İşaretçi sonrasındaki sunucuların " -"listesini görüntüle. Belirtilmemişse tüm sunucuları görüntüleyin. (isim veya " -"ID)" - -msgid "The last snapshot ID of the previous page" -msgstr "Bir önceki sayfanın son anlık görüntü ID'si" - -msgid "The last volume ID of the previous page" -msgstr "Bir önceki sayfanın son disk bölümü ID'si" - msgid "The object to which this RBAC policy affects (name or ID)" msgstr "Bu RBAC politikasının etkilediği nesne (ad veya kimlik)" msgid "The owner project (name or ID)" msgstr "Projenin sahibi (isim veya ID)" -msgid "" -"The physical mechanism by which the virtual network is implemented. For " -"example: flat, geneve, gre, local, vlan, vxlan." -msgstr "" -"Sanal ağın uygulandığı fiziksel mekanizma. Örneğin: düz, geneve, gre, yerel, " -"vlan, vxlan." - msgid "The project to which the RBAC policy will be enforced (name or ID)" msgstr "RBAC politikası dayatılacak proje (isim veya ID)" msgid "The remote IP prefix to associate with this rule" msgstr "Bu kural ile ilişkilendirilecek uzak IP ön eki" -msgid "" -"This command has been deprecated. Please use \"floating ip create\" instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"floating ip create\" " -"kullanın." - -msgid "" -"This command has been deprecated. Please use \"floating ip delete\" instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen yerine \"floating ip delete\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"floating ip list\" instead." -msgstr "" -"Bu komut kullanımdan kalktı. Onun yerine lütfen \"floating ip list\" " -"kullanın." - -msgid "" -"This command has been deprecated. Please use \"floating ip pool list\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen, onun yerine \"floating ip pool list" -"\" komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"floating ip show\" instead." -msgstr "" -"Bu komut önerilmiyor. Lütfen bunun yerine \"floating ip show\"ı kullanın." - -msgid "" -"This command has been deprecated. Please use \"server add fixed ip\" instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Onun yerine lütfen \"server add fixed ip\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"server add floating ip\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırılmıştır. Onun yerine lütfen \"server add " -"floating ip\" kullanın." - -msgid "" -"This command has been deprecated. Please use \"server remove fixed ip\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"server remove fixed ip" -"\" komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"server remove floating ip\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Onun yerine lütfen \"server remove floating " -"ip\" komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume backup create\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Onun yerine lütfen \"volume backup create\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume backup delete\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume backup delete\" " -"kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume backup list\" instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume backup list\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume backup restore\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume backup restore" -"\" komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume backup show\" instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume backup show\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume snapshot create\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume snapshot create" -"\" komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume snapshot delete\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume snapshot delete" -"\" komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume snapshot list\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Onun yerine lütfen \"volume snapshot list\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume snapshot set\" instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen bunun yerine \"volume snapshot set\" " -"komutunu kullanın." - -msgid "" -"This command has been deprecated. Please use \"volume snapshot show\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume snapshot show\" " -"komutunu kullan." - -msgid "" -"This command has been deprecated. Please use \"volume snapshot unset\" " -"instead." -msgstr "" -"Bu komut kullanımdan kaldırıldı. Lütfen onun yerine \"volume snapshot unset" -"\" komutunu kullanın." - msgid "Token to be deleted" msgstr "Silinecek jeton" @@ -6283,20 +4999,12 @@ msgstr "Mimari türü" msgid "Type of hypervisor" msgstr "Arakatman türü" -msgid "" -"Type of the object that RBAC policy affects (\"qos_policy\" or \"network\")" -msgstr "" -"RBAC politikasını etkileyen nesne türü (\"qos_policy\" veya \"network\")" - msgid "URL" msgstr "URL" msgid "URL of the agent" msgstr "Ajanın URL'i" -msgid "Unique flavor ID; 'auto' creates a UUID (default: auto)" -msgstr "Benzersiz flavor ID'si; 'auto' bir UUID oluşturur (varsayılan: auto)" - msgid "Unlock server(s)" msgstr "Sunucu(ların/nun) kilidini kaldır" @@ -6344,12 +5052,6 @@ msgstr "Proje özelliklerini kaldır" msgid "Unset router properties" msgstr "Yönlendirici özelliklerini kaldır" -msgid "Unset server properties" -msgstr "Sunucu özelliklerini kaldır" - -msgid "Unset snapshot properties" -msgstr "Anlık görüntü özelliklerinin ayarını kaldır" - msgid "Unset subnet pool properties" msgstr "Altağ havuz özelliklerinin ayarlarını kaldır" @@ -6432,12 +5134,6 @@ msgstr "Gizli IP adresi kullan" msgid "Use public IP address" msgstr "Genel IP adresi kullan" -msgid "" -"Use specified volume as the config drive, or 'True' to use an ephemeral drive" -msgstr "" -"Belirtilen birimi yapılandırma sürücüsü olarak kullanın veya kısa ömürlü bir " -"sürücüyü kullanmak için 'True' kullanın" - msgid "" "Used to populate the backup_type property of the backup image (default: " "empty)" @@ -6451,9 +5147,6 @@ msgstr "Üst veri sunucusundan sunmak için kullanıcı veri dosyası" msgid "User description" msgstr "Kullanıcı tanımı" -msgid "User must be specified" -msgstr "Kullanıcı belirtilmeli" - msgid "User that is assuming authorization (name or ID)" msgstr "Yetki varsayan kullanıcı (adı veya kimliği)" @@ -6472,9 +5165,6 @@ msgstr "Gösterilecek kullanıcı (isim veya ID)" msgid "User to filter (name or ID)" msgstr "Filtrelenecek kullanıcı (isim veya ID)" -msgid "User to list (name or ID)" -msgstr "Listelenecek kullanıcı (isim veya ID)" - msgid "User to modify (name or ID)" msgstr "Düzenlenecek kullanıcı (isim veya ID)" @@ -6497,13 +5187,6 @@ msgstr "" msgid "VLAN ID for VLAN networks or Tunnel ID for GENEVE/GRE/VXLAN networks" msgstr "VLAN ağları için VLAN ID veya GENEVA/GRE/VXLAN ağları için Tünel ID" -msgid "" -"VNIC type for this port (direct | direct-physical | macvtap | normal | " -"baremetal | virtio-forwarder, default: normal)" -msgstr "" -"Bu bağlantı noktası için VNIC türü (direct | direct-physical | macvtap | " -"normal | baremetal | virtio-forwarder, varsayılan: normal)" - msgid "" "Validate the requirements for auto allocated topology. Does not return a " "topology." @@ -6536,25 +5219,6 @@ msgstr "" msgid "Volume name" msgstr "Disk bölümü ismi" -msgid "" -"Volume or snapshot (name or ID) must be specified if --block-device-mapping " -"is specified" -msgstr "" -"Disk bölümü veya anlık görüntü (isim veya ID), eğer --block-device-mapping " -"belirtildiyse, belirtilmek zorundadır." - -msgid "Volume size in GB (Required unless --snapshot or --source is specified)" -msgstr "" -"GB cinsinden disk bölümü boyutu (--snapshot veya --source belirtilmezse " -"gereklidir)" - -msgid "" -"Volume size in GB (Required unless --snapshot or --source or --source-" -"replicated is specified)" -msgstr "" -"GB cinsinden disk bölümü boyutu (--snapshot veya --source veya --source-" -"replicated belirtilmedikçe gereklidir)" - msgid "Volume to add (name or ID)" msgstr "Eklenecek disk bölümü (isim veya ID)" @@ -6576,12 +5240,6 @@ msgstr "Düzenlenecek disk bölümü (isim veya ID)" msgid "Volume to remove (name or ID)" msgstr "Kaldırılacak disk bölümü (isim veya ID)" -msgid "Volume to restore to (name or ID)" -msgstr "Geri yüklenecek disk bölümü (isim veya ID)" - -msgid "Volume to snapshot (name or ID)" -msgstr "Anlık görüntüsü oluşturulacak disk bölümü (isim veya ID)" - msgid "Volume to snapshot (name or ID) (default is )" msgstr "" "Anlık görüntüsü alınacak disk bölümü (isim veya ID) (varsayılan Date: Wed, 2 Apr 2025 12:36:23 +0100 Subject: [PATCH 119/245] Remove use of formatter function We also update tests to use proper SDK fakes so we actually test this. Change-Id: Ib98348cab613b7139f0faa0b5df90ff44353974f Signed-off-by: Stephen Finucane --- openstackclient/network/v2/floating_ip.py | 3 +- .../network/v2/test_floating_ip_network.py | 84 +++++++++++-------- 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index a28a810aa..ea1281177 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -13,6 +13,7 @@ """IP Floating action implementations""" from openstack import exceptions as sdk_exceptions +from osc_lib.cli import format_columns from osc_lib import utils from osc_lib.utils import tags as _tag @@ -22,7 +23,7 @@ from openstackclient.network import common _formatters = { - 'port_details': utils.format_dict, + 'port_details': format_columns.DictColumn, } diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py index 34ca21ecd..dcdeea962 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py @@ -14,6 +14,9 @@ from unittest import mock from unittest.mock import call +from openstack.network.v2 import floating_ip as _floating_ip +from openstack.test import fakes as sdk_fakes +from osc_lib.cli import format_columns from osc_lib import exceptions from openstackclient.network.v2 import floating_ip as fip @@ -706,46 +709,57 @@ def test_list_with_tag_options(self): class TestShowFloatingIPNetwork(TestFloatingIPNetwork): - # The floating ip to display. - floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - - columns = ( - 'description', - 'dns_domain', - 'dns_name', - 'fixed_ip_address', - 'floating_ip_address', - 'floating_network_id', - 'id', - 'port_id', - 'project_id', - 'qos_policy_id', - 'router_id', - 'status', - 'tags', - ) - - data = ( - floating_ip.description, - floating_ip.dns_domain, - floating_ip.dns_name, - floating_ip.fixed_ip_address, - floating_ip.floating_ip_address, - floating_ip.floating_network_id, - floating_ip.id, - floating_ip.port_id, - floating_ip.project_id, - floating_ip.qos_policy_id, - floating_ip.router_id, - floating_ip.status, - floating_ip.tags, - ) - def setUp(self): super().setUp() + self.floating_ip = sdk_fakes.generate_fake_resource( + _floating_ip.FloatingIP + ) self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + self.columns = ( + 'created_at', + 'description', + 'dns_domain', + 'dns_name', + 'fixed_ip_address', + 'floating_ip_address', + 'floating_network_id', + 'id', + 'name', + 'port_details', + 'port_id', + 'project_id', + 'qos_policy_id', + 'revision_number', + 'router_id', + 'status', + 'subnet_id', + 'tags', + 'updated_at', + ) + self.data = ( + self.floating_ip.created_at, + self.floating_ip.description, + self.floating_ip.dns_domain, + self.floating_ip.dns_name, + self.floating_ip.fixed_ip_address, + self.floating_ip.floating_ip_address, + self.floating_ip.floating_network_id, + self.floating_ip.id, + self.floating_ip.name, + format_columns.DictColumn(self.floating_ip.port_details), + self.floating_ip.port_id, + self.floating_ip.project_id, + self.floating_ip.qos_policy_id, + self.floating_ip.revision_number, + self.floating_ip.router_id, + self.floating_ip.status, + self.floating_ip.subnet_id, + self.floating_ip.tags, + self.floating_ip.updated_at, + ) + # Get the command object to test self.cmd = fip.ShowFloatingIP(self.app, None) From dae2539490312008aba8e633a9e2c44cd4afaa7f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 18 Jul 2024 13:30:22 +0100 Subject: [PATCH 120/245] compute: Migrate to 'compute' client alias This is no longer assigned to novaclient, meaning we can use it for SDK. Change-Id: I43d9ede39d36cc29301f94fa462b9b9d9441807c Signed-off-by: Stephen Finucane --- doc/source/contributor/command-errors.rst | 2 +- openstackclient/common/availability_zone.py | 2 +- openstackclient/common/extension.py | 2 +- openstackclient/common/limits.py | 2 +- openstackclient/common/quota.py | 8 +- openstackclient/compute/v2/agent.py | 8 +- openstackclient/compute/v2/aggregate.py | 18 ++--- openstackclient/compute/v2/console.py | 4 +- openstackclient/compute/v2/flavor.py | 12 +-- openstackclient/compute/v2/host.py | 6 +- openstackclient/compute/v2/hypervisor.py | 4 +- .../compute/v2/hypervisor_stats.py | 2 +- openstackclient/compute/v2/keypair.py | 8 +- openstackclient/compute/v2/server.py | 78 +++++++++---------- openstackclient/compute/v2/server_backup.py | 2 +- openstackclient/compute/v2/server_event.py | 4 +- openstackclient/compute/v2/server_group.py | 8 +- openstackclient/compute/v2/server_image.py | 2 +- .../compute/v2/server_migration.py | 8 +- openstackclient/compute/v2/server_volume.py | 4 +- openstackclient/compute/v2/service.py | 6 +- openstackclient/compute/v2/usage.py | 4 +- openstackclient/network/common.py | 6 +- openstackclient/network/v2/port.py | 2 +- .../tests/unit/compute/v2/fakes.py | 8 +- .../tests/unit/network/test_common.py | 4 +- openstackclient/tests/unit/volume/v3/fakes.py | 8 +- openstackclient/volume/v2/volume.py | 2 +- openstackclient/volume/v3/volume.py | 2 +- .../volume/v3/volume_attachment.py | 2 +- 30 files changed, 110 insertions(+), 118 deletions(-) diff --git a/doc/source/contributor/command-errors.rst b/doc/source/contributor/command-errors.rst index 5204e06b4..f47dfec7c 100644 --- a/doc/source/contributor/command-errors.rst +++ b/doc/source/contributor/command-errors.rst @@ -41,7 +41,7 @@ opened. ## ... def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute public_key = parsed_args.public_key if public_key: diff --git a/openstackclient/common/availability_zone.py b/openstackclient/common/availability_zone.py index e6644ffe2..61f77cc06 100644 --- a/openstackclient/common/availability_zone.py +++ b/openstackclient/common/availability_zone.py @@ -117,7 +117,7 @@ def get_parser(self, prog_name): return parser def _get_compute_availability_zones(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute try: data = list(compute_client.availability_zones(details=True)) except sdk_exceptions.ForbiddenException: # policy doesn't allow diff --git a/openstackclient/common/extension.py b/openstackclient/common/extension.py index 00f04c750..42c2e9b9f 100644 --- a/openstackclient/common/extension.py +++ b/openstackclient/common/extension.py @@ -98,7 +98,7 @@ def take_action(self, parsed_args): LOG.warning(message) if parsed_args.compute or show_all: - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute try: data += compute_client.extensions() except Exception: diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py index ac613fd83..0c9280593 100644 --- a/openstackclient/common/limits.py +++ b/openstackclient/common/limits.py @@ -122,7 +122,7 @@ def take_action(self, parsed_args): volume_limits = None if self.app.client_manager.is_compute_endpoint_enabled(): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute compute_limits = compute_client.get_limits( reserved=parsed_args.is_reserved, tenant_id=project_id ) diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 03a4f0908..f706a5974 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -133,7 +133,7 @@ def get_compute_quotas( default=False, ): try: - client = app.client_manager.sdk_connection.compute + client = app.client_manager.compute if default: quota = client.get_quota_set_defaults(project_id) else: @@ -256,7 +256,7 @@ def get_parser(self, prog_name): return parser def _list_quota_compute(self, parsed_args, project_ids): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute result = [] for project_id in project_ids: @@ -588,7 +588,7 @@ def take_action(self, parsed_args): network_kwargs = {} if self.app.client_manager.is_compute_endpoint_enabled(): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for k, v in COMPUTE_QUOTAS.items(): value = getattr(parsed_args, k, None) @@ -907,7 +907,7 @@ def take_action(self, parsed_args): # compute quotas if parsed_args.service in {'all', 'compute'}: - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute compute_client.revert_quota_set(project.id) # volume quotas diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py index 799b828bb..5bc6603f4 100644 --- a/openstackclient/compute/v2/agent.py +++ b/openstackclient/compute/v2/agent.py @@ -56,7 +56,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute # doing this since openstacksdk has decided not to support this # deprecated command @@ -95,7 +95,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute result = 0 for id in parsed_args.id: try: @@ -139,7 +139,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute columns = ( "Agent ID", "Hypervisor", @@ -194,7 +194,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute response = compute_client.get('/os-agents', microversion='2.1') sdk_exceptions.raise_from_response(response) diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index b82209b78..e64960721 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -68,7 +68,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute aggregate = compute_client.find_aggregate( parsed_args.aggregate, ignore_missing=False @@ -111,7 +111,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute attrs = {'name': parsed_args.name} @@ -147,7 +147,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute result = 0 for a in parsed_args.aggregate: try: @@ -190,7 +190,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute aggregates = list(compute_client.aggregates()) @@ -252,7 +252,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute aggregate = compute_client.find_aggregate( parsed_args.aggregate, ignore_missing=False @@ -309,7 +309,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute aggregate = compute_client.find_aggregate( parsed_args.aggregate, ignore_missing=False ) @@ -354,7 +354,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute aggregate = compute_client.find_aggregate( parsed_args.aggregate, ignore_missing=False ) @@ -394,7 +394,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute aggregate = compute_client.find_aggregate( parsed_args.aggregate, ignore_missing=False ) @@ -429,7 +429,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if not sdk_utils.supports_microversion(compute_client, '2.81'): msg = _( diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py index c2db25339..0968dcb01 100644 --- a/openstackclient/compute/v2/console.py +++ b/openstackclient/compute/v2/console.py @@ -55,7 +55,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( name_or_id=parsed_args.server, ignore_missing=False @@ -130,7 +130,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index fe6016148..76635e12b 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -149,7 +149,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity if parsed_args.project and parsed_args.public: @@ -223,7 +223,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute result = 0 for f in parsed_args.flavor: try: @@ -296,7 +296,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute # is_public is ternary - None means give all flavors, # True is public only and False is private only # By default Nova assumes True and gives admins public flavors @@ -418,7 +418,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity try: @@ -497,7 +497,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute flavor = compute_client.find_flavor( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) @@ -565,7 +565,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity try: diff --git a/openstackclient/compute/v2/host.py b/openstackclient/compute/v2/host.py index 27e8e2705..aa1d1a5eb 100644 --- a/openstackclient/compute/v2/host.py +++ b/openstackclient/compute/v2/host.py @@ -35,7 +35,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute self.log.warning( "API has been deprecated; " @@ -83,7 +83,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute self.log.warning( "API has been deprecated; " @@ -121,7 +121,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute self.log.warning( "API has been deprecated; " diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py index ab81879b6..0a6cc5326 100644 --- a/openstackclient/compute/v2/hypervisor.py +++ b/openstackclient/compute/v2/hypervisor.py @@ -90,7 +90,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute list_opts = {} @@ -170,7 +170,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute hypervisor_id = compute_client.find_hypervisor( parsed_args.hypervisor, ignore_missing=False, details=False diff --git a/openstackclient/compute/v2/hypervisor_stats.py b/openstackclient/compute/v2/hypervisor_stats.py index 63ca71fdb..3b9a68614 100644 --- a/openstackclient/compute/v2/hypervisor_stats.py +++ b/openstackclient/compute/v2/hypervisor_stats.py @@ -42,7 +42,7 @@ class ShowHypervisorStats(command.ShowOne): def take_action(self, parsed_args): # The command is deprecated since it is being dropped in Nova. self.log.warning(_("This command is deprecated.")) - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute # We do API request directly cause this deprecated method is not and # will not be supported by OpenStackSDK. response = compute_client.get( diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index 52fe0b591..d10939b4f 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -120,7 +120,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity kwargs = {'name': parsed_args.name} @@ -228,7 +228,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity kwargs = {} @@ -298,7 +298,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity kwargs = {} @@ -409,7 +409,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity kwargs = {} diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index d415a8453..a3ca4d07a 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -358,7 +358,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) @@ -452,7 +452,7 @@ def update_parser_common(self, parser): return parser def take_action_network(self, client, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute attrs = {} obj = client.find_ip( @@ -545,7 +545,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -599,7 +599,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -650,7 +650,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -752,7 +752,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute volume_client = self.app.client_manager.sdk_connection.volume server = compute_client.find_server( @@ -1517,7 +1517,7 @@ def _show_progress(progress): self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute volume_client = self.app.client_manager.volume image_client = self.app.client_manager.image @@ -2157,7 +2157,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for name_or_id in parsed_args.server: server = compute_client.find_server(name_or_id) server.trigger_crash_dump(compute_client) @@ -2201,7 +2201,7 @@ def _show_progress(progress): self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_obj = compute_client.find_server( server, @@ -2609,7 +2609,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity image_client = self.app.client_manager.image @@ -3085,7 +3085,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute kwargs = {} if parsed_args.reason: @@ -3209,7 +3209,7 @@ def _show_progress(progress): self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -3312,7 +3312,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -3361,7 +3361,7 @@ def _show_progress(progress): self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server_id = compute_client.find_server( parsed_args.server, ignore_missing=False, @@ -3566,7 +3566,7 @@ def _show_progress(progress): self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute image_client = self.app.client_manager.image server = compute_client.find_server( @@ -3827,7 +3827,7 @@ def _show_progress(progress): self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute image_client = self.app.client_manager.image if parsed_args.host: @@ -3892,7 +3892,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -3951,7 +3951,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -3990,7 +3990,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -4034,7 +4034,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -4104,7 +4104,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute volume_client = self.app.client_manager.sdk_connection.volume server = compute_client.find_server( @@ -4157,7 +4157,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute image_client = self.app.client_manager.image image_ref = None @@ -4229,7 +4229,7 @@ def _show_progress(progress): self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) @@ -4291,7 +4291,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) @@ -4339,7 +4339,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) @@ -4383,7 +4383,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -4406,7 +4406,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -4506,7 +4506,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) @@ -4624,7 +4624,7 @@ def _show_progress(progress): self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server_ids = [] for server in parsed_args.servers: @@ -4719,7 +4719,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute image_client = self.app.client_manager.image server = compute_client.find_server( @@ -4849,7 +4849,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -4935,7 +4935,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -4970,7 +4970,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -4995,7 +4995,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -5018,7 +5018,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -5041,7 +5041,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for server in parsed_args.server: server_id = compute_client.find_server( server, @@ -5063,7 +5063,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False ) @@ -5130,7 +5130,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False @@ -5221,7 +5221,7 @@ def _show_progress(progress): self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute kwargs = {} if parsed_args.availability_zone: diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py index 96b325b10..b0395db26 100644 --- a/openstackclient/compute/v2/server_backup.py +++ b/openstackclient/compute/v2/server_backup.py @@ -71,7 +71,7 @@ def _show_progress(progress): self.app.stderr.write(f'\rProgress: {progress}') self.app.stderr.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False diff --git a/openstackclient/compute/v2/server_event.py b/openstackclient/compute/v2/server_event.py index 6f242c759..3666bfe29 100644 --- a/openstackclient/compute/v2/server_event.py +++ b/openstackclient/compute/v2/server_event.py @@ -139,7 +139,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute kwargs = {} @@ -266,7 +266,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute try: server_id = compute_client.find_server( diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py index 4a157faa2..73567488c 100644 --- a/openstackclient/compute/v2/server_group.py +++ b/openstackclient/compute/v2/server_group.py @@ -93,7 +93,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if parsed_args.policy in ('soft-affinity', 'soft-anti-affinity'): if not sdk_utils.supports_microversion(compute_client, '2.15'): @@ -153,7 +153,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute result = 0 for group in parsed_args.server_group: try: @@ -197,7 +197,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute kwargs = {} @@ -264,7 +264,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute group = compute_client.find_server_group( parsed_args.server_group, ignore_missing=False ) diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py index e49a5d36c..cb19173bc 100644 --- a/openstackclient/compute/v2/server_image.py +++ b/openstackclient/compute/v2/server_image.py @@ -72,7 +72,7 @@ def _show_progress(progress): self.app.stdout.write(f'\rProgress: {progress}') self.app.stdout.flush() - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute image_client = self.app.client_manager.image server = compute_client.find_server( diff --git a/openstackclient/compute/v2/server_migration.py b/openstackclient/compute/v2/server_migration.py index a0ec365a6..c356bd81f 100644 --- a/openstackclient/compute/v2/server_migration.py +++ b/openstackclient/compute/v2/server_migration.py @@ -154,7 +154,7 @@ def print_migrations(self, parsed_args, compute_client, migrations): ) def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity search_opts = {} @@ -289,7 +289,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if not sdk_utils.supports_microversion(compute_client, '2.24'): msg = _( @@ -404,7 +404,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if not sdk_utils.supports_microversion(compute_client, '2.24'): msg = _( @@ -469,7 +469,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if not sdk_utils.supports_microversion(compute_client, '2.22'): msg = _( diff --git a/openstackclient/compute/v2/server_volume.py b/openstackclient/compute/v2/server_volume.py index fe74ec7d1..a27c63f53 100644 --- a/openstackclient/compute/v2/server_volume.py +++ b/openstackclient/compute/v2/server_volume.py @@ -34,7 +34,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, @@ -114,7 +114,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute volume_client = self.app.client_manager.sdk_connection.volume if parsed_args.delete_on_termination is not None: diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index 5ba2a3443..3660ddd49 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -52,7 +52,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute result = 0 for s in parsed_args.service: try: @@ -108,7 +108,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute columns: tuple[str, ...] = ( "id", "binary", @@ -221,7 +221,7 @@ def _find_service_by_host_and_binary(compute_client, host, binary): return services[0] def take_action(self, parsed_args): - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if ( parsed_args.enable or not parsed_args.disable diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index b4bfedac9..d045c1efe 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -135,7 +135,7 @@ def _format_project(project): else: return project - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute columns = ( "project_id", "server_usages", @@ -234,7 +234,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute date_cli_format = "%Y-%m-%d" now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index c5fad534f..9bcd16583 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -167,7 +167,7 @@ def take_action(self, parsed_args): ) elif self.is_nova_network: return self.take_action_compute( - self.app.client_manager.sdk_connection.compute, parsed_args + self.app.client_manager.compute, parsed_args ) def take_action_network(self, client, parsed_args): @@ -216,7 +216,7 @@ def take_action(self, parsed_args): ) else: self.take_action_compute( - self.app.client_manager.sdk_connection.compute, + self.app.client_manager.compute, parsed_args, ) except Exception as e: @@ -274,7 +274,7 @@ def take_action(self, parsed_args): ) else: return self.take_action_compute( - self.app.client_manager.sdk_connection.compute, parsed_args + self.app.client_manager.compute, parsed_args ) except openstack.exceptions.HttpException as exc: msg = _("Error while executing command: %s") % exc.message diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 7d043f95a..25dfdbbe8 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -903,7 +903,7 @@ def take_action(self, parsed_args): ) filters['device_id'] = _router.id if parsed_args.server: - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute server = compute_client.find_server( parsed_args.server, ignore_missing=False, diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 62758c816..670401b8d 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -100,12 +100,8 @@ def setUp(self): # TODO(stephenfin): Rename to 'compute_client' once all commands are # migrated to SDK - self.app.client_manager.sdk_connection.compute = mock.Mock( - _proxy.Proxy - ) - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) + self.app.client_manager.compute = mock.Mock(_proxy.Proxy) + self.compute_sdk_client = self.app.client_manager.compute self.set_compute_api_version() # default to the lowest def set_compute_api_version(self, version: str = '2.1'): diff --git a/openstackclient/tests/unit/network/test_common.py b/openstackclient/tests/unit/network/test_common.py index a84697b2c..c1ad9b28d 100644 --- a/openstackclient/tests/unit/network/test_common.py +++ b/openstackclient/tests/unit/network/test_common.py @@ -132,8 +132,8 @@ def setUp(self): return_value='take_action_network' ) - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_client = self.app.client_manager.sdk_connection.compute + self.app.client_manager.compute = mock.Mock() + self.compute_client = self.app.client_manager.compute self.compute_client.compute_action = mock.Mock( return_value='take_action_compute' ) diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index 208cd7622..01519d70d 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -131,12 +131,8 @@ def setUp(self): # openstackclient.tests.unit.compute.v2.fakes.FakeClientMixin # TODO(stephenfin): Rename to 'compute_client' once all commands are # migrated to SDK - self.app.client_manager.sdk_connection.compute = mock.Mock( - _compute_proxy.Proxy - ) - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) + self.app.client_manager.compute = mock.Mock(_compute_proxy.Proxy) + self.compute_sdk_client = self.app.client_manager.compute # avoid circular imports by defining this manually rather than using # openstackclient.tests.unit.image.v2.fakes.FakeClientMixin diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 8bbee4cb4..8b55ce2a8 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -503,7 +503,7 @@ def take_action(self, parsed_args): server_cache = {} if do_server_list: try: - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for s in compute_client.servers(): server_cache[s.id] = s except sdk_exceptions.SDKException: # noqa: S110 diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index e0c033997..4556b195f 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -519,7 +519,7 @@ def take_action(self, parsed_args): server_cache = {} if do_server_list: try: - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute for s in compute_client.servers(): server_cache[s.id] = s except sdk_exceptions.SDKException: # noqa: S110 diff --git a/openstackclient/volume/v3/volume_attachment.py b/openstackclient/volume/v3/volume_attachment.py index b24c96b83..5773a121c 100644 --- a/openstackclient/volume/v3/volume_attachment.py +++ b/openstackclient/volume/v3/volume_attachment.py @@ -172,7 +172,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume - compute_client = self.app.client_manager.sdk_connection.compute + compute_client = self.app.client_manager.compute if not sdk_utils.supports_microversion(volume_client, '3.27'): msg = _( From dc68be6b7b81e46cb85f4a643667b3c6a30b58da Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 1 Apr 2025 10:44:02 +0100 Subject: [PATCH 121/245] tests: Rename 'compute_sdk_client' -> 'compute_client' Resolve a TODO. Achieved using: sed -i 's/self.compute_sdk_client/self.compute_client/' \ $(ag -w self.compute_sdk_client -l) Change-Id: I76798058b9dee9fc7ef01ff8656543fbb1266d43 Signed-off-by: Stephen Finucane --- .../tests/unit/api/test_compute_v2.py | 154 +-- .../unit/common/test_availability_zone.py | 20 +- .../tests/unit/common/test_extension.py | 12 +- .../tests/unit/common/test_limits.py | 2 +- .../tests/unit/common/test_quota.py | 50 +- .../tests/unit/compute/v2/fakes.py | 8 +- .../tests/unit/compute/v2/test_agent.py | 32 +- .../tests/unit/compute/v2/test_aggregate.py | 116 +- .../tests/unit/compute/v2/test_console.py | 32 +- .../tests/unit/compute/v2/test_flavor.py | 142 +-- .../tests/unit/compute/v2/test_host.py | 16 +- .../tests/unit/compute/v2/test_hypervisor.py | 52 +- .../unit/compute/v2/test_hypervisor_stats.py | 4 +- .../tests/unit/compute/v2/test_keypair.py | 48 +- .../tests/unit/compute/v2/test_server.py | 1080 ++++++++--------- .../unit/compute/v2/test_server_backup.py | 12 +- .../unit/compute/v2/test_server_event.py | 34 +- .../unit/compute/v2/test_server_group.py | 56 +- .../unit/compute/v2/test_server_image.py | 14 +- .../unit/compute/v2/test_server_migration.py | 74 +- .../unit/compute/v2/test_server_volume.py | 24 +- .../tests/unit/compute/v2/test_service.py | 84 +- .../tests/unit/compute/v2/test_usage.py | 10 +- .../network/v2/test_floating_ip_compute.py | 22 +- .../v2/test_floating_ip_pool_compute.py | 2 +- .../unit/network/v2/test_network_compute.py | 22 +- .../tests/unit/network/v2/test_port.py | 4 +- .../network/v2/test_security_group_compute.py | 30 +- .../v2/test_security_group_rule_compute.py | 28 +- openstackclient/tests/unit/volume/v3/fakes.py | 4 +- .../unit/volume/v3/test_volume_attachment.py | 6 +- 31 files changed, 1040 insertions(+), 1154 deletions(-) diff --git a/openstackclient/tests/unit/api/test_compute_v2.py b/openstackclient/tests/unit/api/test_compute_v2.py index cae8db9a7..a609025b2 100644 --- a/openstackclient/tests/unit/api/test_compute_v2.py +++ b/openstackclient/tests/unit/api/test_compute_v2.py @@ -29,7 +29,7 @@ class TestSecurityGroup(utils.TestCase): def setUp(self): super().setUp() - self.compute_sdk_client = mock.Mock(_proxy.Proxy) + self.compute_client = mock.Mock(_proxy.Proxy) def test_create_security_group(self): sg_name = 'name-' + uuid.uuid4().hex @@ -43,15 +43,13 @@ def test_create_security_group(self): 'rules': [], } } - self.compute_sdk_client.post.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.post.return_value = fakes.FakeResponse(data=data) result = compute.create_security_group( - self.compute_sdk_client, sg_name, sg_description + self.compute_client, sg_name, sg_description ) - self.compute_sdk_client.post.assert_called_once_with( + self.compute_client.post.assert_called_once_with( '/os-security-groups', data={'name': sg_name, 'description': sg_description}, microversion='2.1', @@ -70,13 +68,11 @@ def test_list_security_groups(self): } ], } - self.compute_sdk_client.get.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.get.return_value = fakes.FakeResponse(data=data) - result = compute.list_security_groups(self.compute_sdk_client) + result = compute.list_security_groups(self.compute_client) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( '/os-security-groups', microversion='2.1' ) self.assertEqual(data['security_groups'], result) @@ -93,13 +89,13 @@ def test_find_security_group_by_id(self): 'rules': [], } } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(data=data), ] - result = compute.find_security_group(self.compute_sdk_client, sg_id) + result = compute.find_security_group(self.compute_client, sg_id) - self.compute_sdk_client.get.assert_has_calls( + self.compute_client.get.assert_has_calls( [ mock.call(f'/os-security-groups/{sg_id}', microversion='2.1'), ] @@ -120,14 +116,14 @@ def test_find_security_group_by_name(self): } ], } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), fakes.FakeResponse(data=data), ] - result = compute.find_security_group(self.compute_sdk_client, sg_name) + result = compute.find_security_group(self.compute_client, sg_name) - self.compute_sdk_client.get.assert_has_calls( + self.compute_client.get.assert_has_calls( [ mock.call( f'/os-security-groups/{sg_name}', microversion='2.1' @@ -139,14 +135,14 @@ def test_find_security_group_by_name(self): def test_find_security_group_not_found(self): data = {'security_groups': []} - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), fakes.FakeResponse(data=data), ] self.assertRaises( osc_lib_exceptions.NotFound, compute.find_security_group, - self.compute_sdk_client, + self.compute_client, 'invalid-sg', ) @@ -170,7 +166,7 @@ def test_find_security_group_by_name_duplicate(self): }, ], } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), fakes.FakeResponse(data=data), ] @@ -178,7 +174,7 @@ def test_find_security_group_by_name_duplicate(self): self.assertRaises( osc_lib_exceptions.NotFound, compute.find_security_group, - self.compute_sdk_client, + self.compute_client, sg_name, ) @@ -195,15 +191,13 @@ def test_update_security_group(self): 'rules': [], } } - self.compute_sdk_client.put.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.put.return_value = fakes.FakeResponse(data=data) result = compute.update_security_group( - self.compute_sdk_client, sg_id, sg_name, sg_description + self.compute_client, sg_id, sg_name, sg_description ) - self.compute_sdk_client.put.assert_called_once_with( + self.compute_client.put.assert_called_once_with( f'/os-security-groups/{sg_id}', data={'name': sg_name, 'description': sg_description}, microversion='2.1', @@ -212,13 +206,13 @@ def test_update_security_group(self): def test_delete_security_group(self): sg_id = uuid.uuid4().hex - self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + self.compute_client.delete.return_value = fakes.FakeResponse( status_code=http.HTTPStatus.NO_CONTENT ) - result = compute.delete_security_group(self.compute_sdk_client, sg_id) + result = compute.delete_security_group(self.compute_client, sg_id) - self.compute_sdk_client.delete.assert_called_once_with( + self.compute_client.delete.assert_called_once_with( f'/os-security-groups/{sg_id}', microversion='2.1', ) @@ -229,7 +223,7 @@ class TestSecurityGroupRule(utils.TestCase): def setUp(self): super().setUp() - self.compute_sdk_client = mock.Mock(_proxy.Proxy) + self.compute_client = mock.Mock(_proxy.Proxy) def test_create_security_group_rule(self): sg_id = uuid.uuid4().hex @@ -242,12 +236,10 @@ def test_create_security_group_rule(self): 'cidr': '10.0.0.0/24', } } - self.compute_sdk_client.post.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.post.return_value = fakes.FakeResponse(data=data) result = compute.create_security_group_rule( - self.compute_sdk_client, + self.compute_client, security_group_id=sg_id, ip_protocol='tcp', from_port=22, @@ -256,7 +248,7 @@ def test_create_security_group_rule(self): remote_group=None, ) - self.compute_sdk_client.post.assert_called_once_with( + self.compute_client.post.assert_called_once_with( '/os-security-group-rules', data={ 'parent_group_id': sg_id, @@ -272,15 +264,13 @@ def test_create_security_group_rule(self): def test_delete_security_group_rule(self): sg_id = uuid.uuid4().hex - self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + self.compute_client.delete.return_value = fakes.FakeResponse( status_code=http.HTTPStatus.NO_CONTENT ) - result = compute.delete_security_group_rule( - self.compute_sdk_client, sg_id - ) + result = compute.delete_security_group_rule(self.compute_client, sg_id) - self.compute_sdk_client.delete.assert_called_once_with( + self.compute_client.delete.assert_called_once_with( f'/os-security-group-rules/{sg_id}', microversion='2.1', ) @@ -291,7 +281,7 @@ class TestNetwork(utils.TestCase): def setUp(self): super().setUp() - self.compute_sdk_client = mock.Mock(_proxy.Proxy) + self.compute_client = mock.Mock(_proxy.Proxy) def test_create_network(self): net_name = 'name-' + uuid.uuid4().hex @@ -305,18 +295,16 @@ def test_create_network(self): # other fields omitted for brevity } } - self.compute_sdk_client.post.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.post.return_value = fakes.FakeResponse(data=data) result = compute.create_network( - self.compute_sdk_client, + self.compute_client, name=net_name, subnet=net_subnet, share_subnet=True, ) - self.compute_sdk_client.post.assert_called_once_with( + self.compute_client.post.assert_called_once_with( '/os-networks', data={ 'label': net_name, @@ -337,13 +325,11 @@ def test_list_networks(self): } ], } - self.compute_sdk_client.get.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.get.return_value = fakes.FakeResponse(data=data) - result = compute.list_networks(self.compute_sdk_client) + result = compute.list_networks(self.compute_client) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( '/os-networks', microversion='2.1' ) self.assertEqual(data['networks'], result) @@ -358,13 +344,13 @@ def test_find_network_by_id(self): # other fields omitted for brevity } } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(data=data), ] - result = compute.find_network(self.compute_sdk_client, net_id) + result = compute.find_network(self.compute_client, net_id) - self.compute_sdk_client.get.assert_has_calls( + self.compute_client.get.assert_has_calls( [ mock.call(f'/os-networks/{net_id}', microversion='2.1'), ] @@ -383,14 +369,14 @@ def test_find_network_by_name(self): } ], } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), fakes.FakeResponse(data=data), ] - result = compute.find_network(self.compute_sdk_client, net_name) + result = compute.find_network(self.compute_client, net_name) - self.compute_sdk_client.get.assert_has_calls( + self.compute_client.get.assert_has_calls( [ mock.call(f'/os-networks/{net_name}', microversion='2.1'), mock.call('/os-networks', microversion='2.1'), @@ -400,14 +386,14 @@ def test_find_network_by_name(self): def test_find_network_not_found(self): data = {'networks': []} - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), fakes.FakeResponse(data=data), ] self.assertRaises( osc_lib_exceptions.NotFound, compute.find_network, - self.compute_sdk_client, + self.compute_client, 'invalid-net', ) @@ -427,7 +413,7 @@ def test_find_network_by_name_duplicate(self): }, ], } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), fakes.FakeResponse(data=data), ] @@ -435,19 +421,19 @@ def test_find_network_by_name_duplicate(self): self.assertRaises( osc_lib_exceptions.NotFound, compute.find_network, - self.compute_sdk_client, + self.compute_client, net_name, ) def test_delete_network(self): net_id = uuid.uuid4().hex - self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + self.compute_client.delete.return_value = fakes.FakeResponse( status_code=http.HTTPStatus.NO_CONTENT ) - result = compute.delete_network(self.compute_sdk_client, net_id) + result = compute.delete_network(self.compute_client, net_id) - self.compute_sdk_client.delete.assert_called_once_with( + self.compute_client.delete.assert_called_once_with( f'/os-networks/{net_id}', microversion='2.1' ) self.assertIsNone(result) @@ -457,7 +443,7 @@ class TestFloatingIP(utils.TestCase): def setUp(self): super().setUp() - self.compute_sdk_client = mock.Mock(_proxy.Proxy) + self.compute_client = mock.Mock(_proxy.Proxy) def test_create_floating_ip(self): network = 'network-' + uuid.uuid4().hex @@ -470,15 +456,13 @@ def test_create_floating_ip(self): 'pool': network, } } - self.compute_sdk_client.post.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.post.return_value = fakes.FakeResponse(data=data) result = compute.create_floating_ip( - self.compute_sdk_client, network=network + self.compute_client, network=network ) - self.compute_sdk_client.post.assert_called_once_with( + self.compute_client.post.assert_called_once_with( '/os-floating-ips', data={'pool': network}, microversion='2.1' ) self.assertEqual(data['floating_ip'], result) @@ -495,13 +479,11 @@ def test_list_floating_ips(self): } ], } - self.compute_sdk_client.get.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.get.return_value = fakes.FakeResponse(data=data) - result = compute.list_floating_ips(self.compute_sdk_client) + result = compute.list_floating_ips(self.compute_client) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( '/os-floating-ips', microversion='2.1' ) self.assertEqual(data['floating_ips'], result) @@ -517,26 +499,26 @@ def test_get_floating_ip(self): 'pool': f'network-{uuid.uuid4().hex}', } } - self.compute_sdk_client.get.side_effect = [ + self.compute_client.get.side_effect = [ fakes.FakeResponse(data=data), ] - result = compute.get_floating_ip(self.compute_sdk_client, fip_id) + result = compute.get_floating_ip(self.compute_client, fip_id) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( f'/os-floating-ips/{fip_id}', microversion='2.1' ) self.assertEqual(data['floating_ip'], result) def test_delete_floating_ip(self): fip_id = uuid.uuid4().hex - self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + self.compute_client.delete.return_value = fakes.FakeResponse( status_code=http.HTTPStatus.NO_CONTENT ) - result = compute.delete_floating_ip(self.compute_sdk_client, fip_id) + result = compute.delete_floating_ip(self.compute_client, fip_id) - self.compute_sdk_client.delete.assert_called_once_with( + self.compute_client.delete.assert_called_once_with( f'/os-floating-ips/{fip_id}', microversion='2.1' ) self.assertIsNone(result) @@ -546,7 +528,7 @@ class TestFloatingIPPool(utils.TestCase): def setUp(self): super().setUp() - self.compute_sdk_client = mock.Mock(_proxy.Proxy) + self.compute_client = mock.Mock(_proxy.Proxy) def test_list_floating_ip_pools(self): data = { @@ -556,13 +538,11 @@ def test_list_floating_ip_pools(self): } ], } - self.compute_sdk_client.get.return_value = fakes.FakeResponse( - data=data - ) + self.compute_client.get.return_value = fakes.FakeResponse(data=data) - result = compute.list_floating_ip_pools(self.compute_sdk_client) + result = compute.list_floating_ip_pools(self.compute_client) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( '/os-floating-ip-pools', microversion='2.1' ) self.assertEqual(data['floating_ip_pools'], result) diff --git a/openstackclient/tests/unit/common/test_availability_zone.py b/openstackclient/tests/unit/common/test_availability_zone.py index 0965c01ce..d1383b540 100644 --- a/openstackclient/tests/unit/common/test_availability_zone.py +++ b/openstackclient/tests/unit/common/test_availability_zone.py @@ -99,9 +99,7 @@ class TestAvailabilityZoneList( def setUp(self): super().setUp() - self.compute_sdk_client.availability_zones.return_value = ( - self.compute_azs - ) + self.compute_client.availability_zones.return_value = self.compute_azs self.volume_sdk_client.availability_zones.return_value = ( self.volume_azs ) @@ -120,9 +118,7 @@ def test_availability_zone_list_no_options(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.availability_zones.assert_called_with( - details=True - ) + self.compute_client.availability_zones.assert_called_with(details=True) self.volume_sdk_client.availability_zones.assert_called_with() self.network_client.availability_zones.assert_called_with() @@ -150,9 +146,7 @@ def test_availability_zone_list_long(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.availability_zones.assert_called_with( - details=True - ) + self.compute_client.availability_zones.assert_called_with(details=True) self.volume_sdk_client.availability_zones.assert_called_with() self.network_client.availability_zones.assert_called_with() @@ -186,9 +180,7 @@ def test_availability_zone_list_compute(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.availability_zones.assert_called_with( - details=True - ) + self.compute_client.availability_zones.assert_called_with(details=True) self.volume_sdk_client.availability_zones.assert_not_called() self.network_client.availability_zones.assert_not_called() @@ -212,7 +204,7 @@ def test_availability_zone_list_volume(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.availability_zones.assert_not_called() + self.compute_client.availability_zones.assert_not_called() self.volume_sdk_client.availability_zones.assert_called_with() self.network_client.availability_zones.assert_not_called() @@ -236,7 +228,7 @@ def test_availability_zone_list_network(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.availability_zones.assert_not_called() + self.compute_client.availability_zones.assert_not_called() self.volume_sdk_client.availability_zones.assert_not_called() self.network_client.availability_zones.assert_called_with() diff --git a/openstackclient/tests/unit/common/test_extension.py b/openstackclient/tests/unit/common/test_extension.py index b22365899..53d9d65d9 100644 --- a/openstackclient/tests/unit/common/test_extension.py +++ b/openstackclient/tests/unit/common/test_extension.py @@ -52,9 +52,7 @@ def setUp(self): self.identity_client.extensions.list.return_value = [ self.identity_extension ] - self.compute_sdk_client.extensions.return_value = [ - self.compute_extension - ] + self.compute_client.extensions.return_value = [self.compute_extension] self.volume_sdk_client.extensions.return_value = [ self.volume_extension ] @@ -106,7 +104,7 @@ def test_extension_list_no_options(self): ) self._test_extension_list_helper(arglist, verifylist, datalist) self.identity_client.extensions.list.assert_called_with() - self.compute_sdk_client.extensions.assert_called_with() + self.compute_client.extensions.assert_called_with() self.volume_sdk_client.extensions.assert_called_with() self.network_client.extensions.assert_called_with() @@ -153,7 +151,7 @@ def test_extension_list_long(self): ) self._test_extension_list_helper(arglist, verifylist, datalist, True) self.identity_client.extensions.list.assert_called_with() - self.compute_sdk_client.extensions.assert_called_with() + self.compute_client.extensions.assert_called_with() self.volume_sdk_client.extensions.assert_called_with() self.network_client.extensions.assert_called_with() @@ -230,7 +228,7 @@ def test_extension_list_compute(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist) - self.compute_sdk_client.extensions.assert_called_with() + self.compute_client.extensions.assert_called_with() def test_extension_list_compute_and_network(self): arglist = [ @@ -254,7 +252,7 @@ def test_extension_list_compute_and_network(self): ), ) self._test_extension_list_helper(arglist, verifylist, datalist) - self.compute_sdk_client.extensions.assert_called_with() + self.compute_client.extensions.assert_called_with() self.network_client.extensions.assert_called_with() def test_extension_list_volume(self): diff --git a/openstackclient/tests/unit/common/test_limits.py b/openstackclient/tests/unit/common/test_limits.py index b7309ab7c..a375a2ae5 100644 --- a/openstackclient/tests/unit/common/test_limits.py +++ b/openstackclient/tests/unit/common/test_limits.py @@ -68,7 +68,7 @@ def setUp(self): ('DELETE', '*', 100, 100, 'MINUTE', '2011-12-15T22:42:45Z'), ] - self.compute_sdk_client.get_limits.return_value = self.fake_limits + self.compute_client.get_limits.return_value = self.fake_limits def test_compute_show_absolute(self): arglist = ['--absolute'] diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index fd18b0619..53df4da84 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -98,7 +98,7 @@ def setUp(self): _compute_quota_set.QuotaSet ) # the defaults are global hence use of return_value here - self.compute_sdk_client.get_quota_set_defaults.return_value = ( + self.compute_client.get_quota_set_defaults.return_value = ( self.default_compute_quotas ) self.compute_reference_data = ( @@ -164,7 +164,7 @@ def setUp(self): def test_quota_list_compute(self): # Two projects with non-default quotas - self.compute_sdk_client.get_quota_set.side_effect = self.compute_quotas + self.compute_client.get_quota_set.side_effect = self.compute_quotas arglist = [ '--compute', @@ -183,7 +183,7 @@ def test_quota_list_compute(self): def test_quota_list_compute_default(self): # One of the projects is at defaults - self.compute_sdk_client.get_quota_set.side_effect = [ + self.compute_client.get_quota_set.side_effect = [ self.compute_quotas[0], self.default_compute_quotas, ] @@ -205,7 +205,7 @@ def test_quota_list_compute_default(self): def test_quota_list_compute_project_not_found(self): # Make one of the projects disappear - self.compute_sdk_client.get_quota_set.side_effect = [ + self.compute_client.get_quota_set.side_effect = [ self.compute_quotas[0], sdk_exceptions.NotFoundException("NotFound"), ] @@ -227,7 +227,7 @@ def test_quota_list_compute_project_not_found(self): def test_quota_list_compute_project_inaccessible(self): # Make one of the projects inaccessible - self.compute_sdk_client.get_quota_set.side_effect = [ + self.compute_client.get_quota_set.side_effect = [ self.compute_quotas[0], sdk_exceptions.ForbiddenException("Forbidden"), ] @@ -249,7 +249,7 @@ def test_quota_list_compute_project_inaccessible(self): def test_quota_list_compute_server_error(self): # Make the server "break" - self.compute_sdk_client.get_quota_set.side_effect = ( + self.compute_client.get_quota_set.side_effect = ( sdk_exceptions.HttpException("Not implemented?") ) @@ -470,7 +470,7 @@ def test_quota_set(self): 'server_group_members': servgroup_members_num, } - self.compute_sdk_client.update_quota_set.assert_called_once_with( + self.compute_client.update_quota_set.assert_called_once_with( self.projects[0].id, **kwargs ) self.assertIsNone(result) @@ -744,7 +744,7 @@ def test_quota_set_with_class(self): 'volumes': volumes, } - self.compute_sdk_client.update_quota_class_set.assert_called_with( + self.compute_client.update_quota_class_set.assert_called_with( self.projects[0].name, **kwargs_compute ) self.volume_sdk_client.update_quota_class_set.assert_called_with( @@ -842,7 +842,7 @@ def test_quota_set_default(self): 'volumes': volumes, } - self.compute_sdk_client.update_quota_class_set.assert_called_with( + self.compute_client.update_quota_class_set.assert_called_with( 'default', **kwargs_compute ) self.volume_sdk_client.update_quota_class_set.assert_called_with( @@ -899,7 +899,7 @@ def test_quota_set_with_force(self): 'subnet': subnet, 'force': True, } - self.compute_sdk_client.update_quota_set.assert_called_once_with( + self.compute_client.update_quota_set.assert_called_once_with( self.projects[0].id, **kwargs_compute ) self.volume_sdk_client.update_quota_set.assert_called_once_with( @@ -942,7 +942,7 @@ def test_quota_set_with_no_force(self): 'subnet': 10, 'check_limit': True, } - self.compute_sdk_client.update_quota_set.assert_called_once_with( + self.compute_client.update_quota_set.assert_called_once_with( self.projects[0].id, **kwargs_compute ) self.volume_sdk_client.update_quota_set.assert_called_once_with( @@ -977,13 +977,13 @@ def setUp(self): self.identity_sdk_client.find_project.return_value = self.projects[0] - self.compute_sdk_client.get_quota_set.return_value = ( + self.compute_client.get_quota_set.return_value = ( sdk_fakes.generate_fake_resource(_compute_quota_set.QuotaSet) ) self.default_compute_quotas = sdk_fakes.generate_fake_resource( _compute_quota_set.QuotaSet ) - self.compute_sdk_client.get_quota_set_defaults.return_value = ( + self.compute_client.get_quota_set_defaults.return_value = ( self.default_compute_quotas ) @@ -1027,7 +1027,7 @@ def test_quota_show(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set.assert_called_once_with( + self.compute_client.get_quota_set.assert_called_once_with( self.projects[0].id, usage=False, ) @@ -1054,7 +1054,7 @@ def test_quota_show__with_compute(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set.assert_called_once_with( + self.compute_client.get_quota_set.assert_called_once_with( self.projects[0].id, usage=False, ) @@ -1074,7 +1074,7 @@ def test_quota_show__with_volume(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set.assert_not_called() + self.compute_client.get_quota_set.assert_not_called() self.volume_sdk_client.get_quota_set.assert_called_once_with( self.projects[0].id, usage=False, @@ -1094,7 +1094,7 @@ def test_quota_show__with_network(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set.assert_not_called() + self.compute_client.get_quota_set.assert_not_called() self.volume_sdk_client.get_quota_set.assert_not_called() self.network_client.get_quota.assert_called_once_with( self.projects[0].id, @@ -1115,7 +1115,7 @@ def test_quota_show__with_default(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set_defaults.assert_called_once_with( + self.compute_client.get_quota_set_defaults.assert_called_once_with( self.projects[0].id, ) self.volume_sdk_client.get_quota_set_defaults.assert_called_once_with( @@ -1139,7 +1139,7 @@ def test_quota_show__with_usage(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set.assert_called_once_with( + self.compute_client.get_quota_set.assert_called_once_with( self.projects[0].id, usage=True, ) @@ -1161,7 +1161,7 @@ def test_quota_show__no_project(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_quota_set.assert_called_once_with( + self.compute_client.get_quota_set.assert_called_once_with( self.projects[1].id, usage=False ) self.volume_sdk_client.get_quota_set.assert_called_once_with( @@ -1181,7 +1181,7 @@ def setUp(self): self.identity_sdk_client.find_project.return_value = self.projects[0] - self.compute_sdk_client.revert_quota_set.return_value = None + self.compute_client.revert_quota_set.return_value = None self.volume_sdk_client.revert_quota_set.return_value = None self.network_client.delete_quota.return_value = None @@ -1205,7 +1205,7 @@ def test_delete(self): self.identity_sdk_client.find_project.assert_called_once_with( self.projects[0].id, ignore_missing=False ) - self.compute_sdk_client.revert_quota_set.assert_called_once_with( + self.compute_client.revert_quota_set.assert_called_once_with( self.projects[0].id, ) self.volume_sdk_client.revert_quota_set.assert_called_once_with( @@ -1234,7 +1234,7 @@ def test_delete__compute(self): self.identity_sdk_client.find_project.assert_called_once_with( self.projects[0].id, ignore_missing=False ) - self.compute_sdk_client.revert_quota_set.assert_called_once_with( + self.compute_client.revert_quota_set.assert_called_once_with( self.projects[0].id, ) self.volume_sdk_client.revert_quota_set.assert_not_called() @@ -1259,7 +1259,7 @@ def test_delete__volume(self): self.identity_sdk_client.find_project.assert_called_once_with( self.projects[0].id, ignore_missing=False ) - self.compute_sdk_client.revert_quota_set.assert_not_called() + self.compute_client.revert_quota_set.assert_not_called() self.volume_sdk_client.revert_quota_set.assert_called_once_with( self.projects[0].id, ) @@ -1284,7 +1284,7 @@ def test_delete__network(self): self.identity_sdk_client.find_project.assert_called_once_with( self.projects[0].id, ignore_missing=False ) - self.compute_sdk_client.revert_quota_set.assert_not_called() + self.compute_client.revert_quota_set.assert_not_called() self.volume_sdk_client.revert_quota_set.assert_not_called() self.network_client.delete_quota.assert_called_once_with( self.projects[0].id, diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 670401b8d..1eff82d11 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -98,10 +98,8 @@ class FakeClientMixin: def setUp(self): super().setUp() - # TODO(stephenfin): Rename to 'compute_client' once all commands are - # migrated to SDK self.app.client_manager.compute = mock.Mock(_proxy.Proxy) - self.compute_sdk_client = self.app.client_manager.compute + self.compute_client = self.app.client_manager.compute self.set_compute_api_version() # default to the lowest def set_compute_api_version(self, version: str = '2.1'): @@ -113,8 +111,8 @@ def set_compute_api_version(self, version: str = '2.1'): """ assert re.match(r'2.\d+', version) - self.compute_sdk_client.default_microversion = version - self.compute_sdk_client.get_endpoint_data.return_value = ( + self.compute_client.default_microversion = version + self.compute_client.get_endpoint_data.return_value = ( discover.EndpointData( min_microversion='2.1', # nova has not bumped this yet max_microversion=version, diff --git a/openstackclient/tests/unit/compute/v2/test_agent.py b/openstackclient/tests/unit/compute/v2/test_agent.py index eb8c0774a..f04ba1066 100644 --- a/openstackclient/tests/unit/compute/v2/test_agent.py +++ b/openstackclient/tests/unit/compute/v2/test_agent.py @@ -61,7 +61,7 @@ def setUp(self): self._agent['version'], ) - self.compute_sdk_client.post.return_value = fakes.FakeResponse( + self.compute_client.post.return_value = fakes.FakeResponse( data={'agent': self._agent} ) self.cmd = agent.CreateAgent(self.app, None) @@ -87,7 +87,7 @@ def test_agent_create(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.post.assert_called_with( + self.compute_client.post.assert_called_with( '/os-agents', json={ 'agent': { @@ -110,7 +110,7 @@ class TestAgentDelete(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + self.compute_client.delete.return_value = fakes.FakeResponse( status_code=http.HTTPStatus.NO_CONTENT ) @@ -125,7 +125,7 @@ def test_delete_one_agent(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.delete.assert_called_once_with( + self.compute_client.delete.assert_called_once_with( '/os-agents/123', microversion='2.1', ) @@ -143,7 +143,7 @@ def test_delete_multiple_agents(self): calls = [ mock.call(f'/os-agents/{x}', microversion='2.1') for x in arglist ] - self.compute_sdk_client.delete.assert_has_calls(calls) + self.compute_client.delete.assert_has_calls(calls) self.assertIsNone(result) def test_delete_multiple_agents_exception(self): @@ -154,7 +154,7 @@ def test_delete_multiple_agents_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.delete.side_effect = [ + self.compute_client.delete.side_effect = [ fakes.FakeResponse(status_code=http.HTTPStatus.NO_CONTENT), fakes.FakeResponse(status_code=http.HTTPStatus.NO_CONTENT), fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), @@ -166,7 +166,7 @@ def test_delete_multiple_agents_exception(self): calls = [ mock.call(f'/os-agents/{x}', microversion='2.1') for x in arglist ] - self.compute_sdk_client.delete.assert_has_calls(calls) + self.compute_client.delete.assert_has_calls(calls) def test_agent_delete_no_input(self): arglist = [] @@ -208,7 +208,7 @@ def setUp(self): for _agent in _agents ] - self.compute_sdk_client.get.return_value = fakes.FakeResponse( + self.compute_client.get.return_value = fakes.FakeResponse( data={'agents': _agents}, ) self.cmd = agent.ListAgent(self.app, None) @@ -222,7 +222,7 @@ def test_agent_list(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( '/os-agents', microversion='2.1', ) @@ -241,7 +241,7 @@ def test_agent_list_with_hypervisor(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) - self.compute_sdk_client.get.assert_called_once_with( + self.compute_client.get.assert_called_once_with( '/os-agents?hypervisor=hypervisor', microversion='2.1', ) @@ -252,10 +252,10 @@ def setUp(self): super().setUp() self.agent = _generate_fake_agent() - self.compute_sdk_client.get.return_value = fakes.FakeResponse( + self.compute_client.get.return_value = fakes.FakeResponse( data={'agents': [self.agent]}, ) - self.compute_sdk_client.put.return_value = fakes.FakeResponse() + self.compute_client.put.return_value = fakes.FakeResponse() self.cmd = agent.SetAgent(self.app, None) @@ -269,7 +269,7 @@ def test_agent_set_nothing(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.put.assert_called_once_with( + self.compute_client.put.assert_called_once_with( f'/os-agents/{self.agent["agent_id"]}', json={ 'para': { @@ -297,7 +297,7 @@ def test_agent_set_version(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.put.assert_called_once_with( + self.compute_client.put.assert_called_once_with( f'/os-agents/{self.agent["agent_id"]}', json={ 'para': { @@ -325,7 +325,7 @@ def test_agent_set_url(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.put.assert_called_once_with( + self.compute_client.put.assert_called_once_with( f'/os-agents/{self.agent["agent_id"]}', json={ 'para': { @@ -353,7 +353,7 @@ def test_agent_set_md5hash(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.put.assert_called_once_with( + self.compute_client.put.assert_called_once_with( f'/os-agents/{self.agent["agent_id"]}', json={ 'para': { diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index f905d67e3..813cbb95f 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -66,10 +66,8 @@ class TestAggregateAddHost(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate.return_value = self.fake_ag - self.compute_sdk_client.add_host_to_aggregate.return_value = ( - self.fake_ag - ) + self.compute_client.find_aggregate.return_value = self.fake_ag + self.compute_client.add_host_to_aggregate.return_value = self.fake_ag self.cmd = aggregate.AddAggregateHost(self.app, None) def test_aggregate_add_host(self): @@ -83,10 +81,10 @@ def test_aggregate_add_host(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.add_host_to_aggregate.assert_called_once_with( + self.compute_client.add_host_to_aggregate.assert_called_once_with( self.fake_ag.id, parsed_args.host ) self.assertEqual(self.columns, columns) @@ -97,10 +95,8 @@ class TestAggregateCreate(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.create_aggregate.return_value = self.fake_ag - self.compute_sdk_client.set_aggregate_metadata.return_value = ( - self.fake_ag - ) + self.compute_client.create_aggregate.return_value = self.fake_ag + self.compute_client.set_aggregate_metadata.return_value = self.fake_ag self.cmd = aggregate.CreateAggregate(self.app, None) def test_aggregate_create(self): @@ -112,7 +108,7 @@ def test_aggregate_create(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_aggregate.assert_called_once_with( + self.compute_client.create_aggregate.assert_called_once_with( name=parsed_args.name ) self.assertEqual(self.columns, columns) @@ -131,7 +127,7 @@ def test_aggregate_create_with_zone(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_aggregate.assert_called_once_with( + self.compute_client.create_aggregate.assert_called_once_with( name=parsed_args.name, availability_zone=parsed_args.zone ) self.assertEqual(self.columns, columns) @@ -151,10 +147,10 @@ def test_aggregate_create_with_property(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_aggregate.assert_called_once_with( + self.compute_client.create_aggregate.assert_called_once_with( name=parsed_args.name ) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, parsed_args.properties ) self.assertEqual(self.columns, columns) @@ -169,7 +165,7 @@ def setUp(self): sdk_fakes.generate_fake_resources(_aggregate.Aggregate, 2) ) - self.compute_sdk_client.find_aggregate = mock.Mock( + self.compute_client.find_aggregate = mock.Mock( side_effect=[self.fake_ags[0], self.fake_ags[1]] ) self.cmd = aggregate.DeleteAggregate(self.app, None) @@ -181,10 +177,10 @@ def test_aggregate_delete(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( self.fake_ags[0].id, ignore_missing=False ) - self.compute_sdk_client.delete_aggregate.assert_called_once_with( + self.compute_client.delete_aggregate.assert_called_once_with( self.fake_ags[0].id, ignore_missing=False ) @@ -202,8 +198,8 @@ def test_delete_multiple_aggregates(self): calls = [] for a in self.fake_ags: calls.append(call(a.id, ignore_missing=False)) - self.compute_sdk_client.find_aggregate.assert_has_calls(calls) - self.compute_sdk_client.delete_aggregate.assert_has_calls(calls) + self.compute_client.find_aggregate.assert_has_calls(calls) + self.compute_client.delete_aggregate.assert_has_calls(calls) def test_delete_multiple_agggregates_with_exception(self): arglist = [ @@ -216,7 +212,7 @@ def test_delete_multiple_agggregates_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.find_aggregate.side_effect = [ + self.compute_client.find_aggregate.side_effect = [ self.fake_ags[0], sdk_exceptions.NotFoundException, ] @@ -229,8 +225,8 @@ def test_delete_multiple_agggregates_with_exception(self): calls = [] for a in arglist: calls.append(call(a, ignore_missing=False)) - self.compute_sdk_client.find_aggregate.assert_has_calls(calls) - self.compute_sdk_client.delete_aggregate.assert_called_with( + self.compute_client.find_aggregate.assert_has_calls(calls) + self.compute_client.delete_aggregate.assert_called_with( self.fake_ags[0].id, ignore_missing=False ) @@ -239,7 +235,7 @@ class TestAggregateList(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.aggregates.return_value = [self.fake_ag] + self.compute_client.aggregates.return_value = [self.fake_ag] self.cmd = aggregate.ListAggregate(self.app, None) def test_aggregate_list(self): @@ -333,8 +329,8 @@ class TestAggregateRemoveHost(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate.return_value = self.fake_ag - self.compute_sdk_client.remove_host_from_aggregate.return_value = ( + self.compute_client.find_aggregate.return_value = self.fake_ag + self.compute_client.remove_host_from_aggregate.return_value = ( self.fake_ag ) self.cmd = aggregate.RemoveAggregateHost(self.app, None) @@ -350,10 +346,10 @@ def test_aggregate_remove_host(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.remove_host_from_aggregate.assert_called_once_with( + self.compute_client.remove_host_from_aggregate.assert_called_once_with( self.fake_ag.id, parsed_args.host ) self.assertEqual(self.columns, columns) @@ -364,7 +360,7 @@ class TestAggregateSet(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate.return_value = self.fake_ag + self.compute_client.find_aggregate.return_value = self.fake_ag self.cmd = aggregate.SetAggregate(self.app, None) def test_aggregate_set_no_option(self): @@ -377,11 +373,11 @@ def test_aggregate_set_no_option(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.assertNotCalled(self.compute_sdk_client.update_aggregate) - self.assertNotCalled(self.compute_sdk_client.set_aggregate_metadata) + self.assertNotCalled(self.compute_client.update_aggregate) + self.assertNotCalled(self.compute_client.set_aggregate_metadata) self.assertIsNone(result) def test_aggregate_set_with_name(self): @@ -397,13 +393,13 @@ def test_aggregate_set_with_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.update_aggregate.assert_called_once_with( + self.compute_client.update_aggregate.assert_called_once_with( self.fake_ag.id, name=parsed_args.name ) - self.assertNotCalled(self.compute_sdk_client.set_aggregate_metadata) + self.assertNotCalled(self.compute_client.set_aggregate_metadata) self.assertIsNone(result) def test_aggregate_set_with_zone(self): @@ -419,13 +415,13 @@ def test_aggregate_set_with_zone(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.update_aggregate.assert_called_once_with( + self.compute_client.update_aggregate.assert_called_once_with( self.fake_ag.id, availability_zone=parsed_args.zone ) - self.assertNotCalled(self.compute_sdk_client.set_aggregate_metadata) + self.assertNotCalled(self.compute_client.set_aggregate_metadata) self.assertIsNone(result) def test_aggregate_set_with_property(self): @@ -443,11 +439,11 @@ def test_aggregate_set_with_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.assertNotCalled(self.compute_sdk_client.update_aggregate) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.assertNotCalled(self.compute_client.update_aggregate) + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, parsed_args.properties ) self.assertIsNone(result) @@ -466,11 +462,11 @@ def test_aggregate_set_with_no_property_and_property(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.assertNotCalled(self.compute_sdk_client.update_aggregate) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.assertNotCalled(self.compute_client.update_aggregate) + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'key1': None, 'key2': 'value2'} ) self.assertIsNone(result) @@ -486,11 +482,11 @@ def test_aggregate_set_with_no_property(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.assertNotCalled(self.compute_sdk_client.update_aggregate) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.assertNotCalled(self.compute_client.update_aggregate) + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'key1': None} ) self.assertIsNone(result) @@ -509,13 +505,13 @@ def test_aggregate_set_with_zone_and_no_property(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.update_aggregate.assert_called_once_with( + self.compute_client.update_aggregate.assert_called_once_with( self.fake_ag.id, availability_zone=parsed_args.zone ) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'key1': None} ) self.assertIsNone(result) @@ -538,7 +534,7 @@ class TestAggregateShow(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate.return_value = self.fake_ag + self.compute_client.find_aggregate.return_value = self.fake_ag self.cmd = aggregate.ShowAggregate(self.app, None) self.data = ( @@ -563,7 +559,7 @@ def test_aggregate_show(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) @@ -575,7 +571,7 @@ class TestAggregateUnset(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate.return_value = self.fake_ag + self.compute_client.find_aggregate.return_value = self.fake_ag self.cmd = aggregate.UnsetAggregate(self.app, None) def test_aggregate_unset(self): @@ -591,7 +587,7 @@ def test_aggregate_unset(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'unset_key': None} ) self.assertIsNone(result) @@ -611,7 +607,7 @@ def test_aggregate_unset_multiple_properties(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( + self.compute_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'unset_key1': None, 'unset_key2': None} ) self.assertIsNone(result) @@ -626,7 +622,7 @@ def test_aggregate_unset_no_option(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.assertNotCalled(self.compute_sdk_client.set_aggregate_metadata) + self.assertNotCalled(self.compute_client.set_aggregate_metadata) self.assertIsNone(result) @@ -636,7 +632,7 @@ class TestAggregateCacheImage(TestAggregate): def setUp(self): super().setUp() - self.compute_sdk_client.find_aggregate.return_value = self.fake_ag + self.compute_client.find_aggregate.return_value = self.fake_ag self.find_image_mock = mock.Mock(side_effect=self.images) self.app.client_manager.sdk_connection.image.find_image = ( self.find_image_mock @@ -667,10 +663,10 @@ def test_aggregate_cache_add_single_image(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.aggregate_precache_images.assert_called_once_with( + self.compute_client.aggregate_precache_images.assert_called_once_with( self.fake_ag.id, [self.images[0].id] ) @@ -688,9 +684,9 @@ def test_aggregate_cache_add_multiple_images(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_aggregate.assert_called_once_with( + self.compute_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.compute_sdk_client.aggregate_precache_images.assert_called_once_with( + self.compute_client.aggregate_precache_images.assert_called_once_with( self.fake_ag.id, [self.images[0].id, self.images[1].id] ) diff --git a/openstackclient/tests/unit/compute/v2/test_console.py b/openstackclient/tests/unit/compute/v2/test_console.py index d4c6940b6..bcff81993 100644 --- a/openstackclient/tests/unit/compute/v2/test_console.py +++ b/openstackclient/tests/unit/compute/v2/test_console.py @@ -28,7 +28,7 @@ def setUp(self): super().setUp() self._server = sdk_fakes.generate_fake_resource(_server.Server) - self.compute_sdk_client.find_server.return_value = self._server + self.compute_client.find_server.return_value = self._server self.cmd = console.ShowConsoleLog(self.app, None) @@ -49,13 +49,13 @@ def test_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) output = {'output': '1st line\n2nd line\n'} - self.compute_sdk_client.get_server_console_output.return_value = output + self.compute_client.get_server_console_output.return_value = output self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( name_or_id='fake_server', ignore_missing=False ) - self.compute_sdk_client.get_server_console_output.assert_called_with( + self.compute_client.get_server_console_output.assert_called_with( self._server.id, length=None ) stdout = self.app.stdout.content @@ -67,13 +67,13 @@ def test_show_lines(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) output = {'output': '1st line\n2nd line'} - self.compute_sdk_client.get_server_console_output.return_value = output + self.compute_client.get_server_console_output.return_value = output self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( name_or_id='fake_server', ignore_missing=False ) - self.compute_sdk_client.get_server_console_output.assert_called_with( + self.compute_client.get_server_console_output.assert_called_with( self._server.id, length=15 ) @@ -83,14 +83,14 @@ def setUp(self): super().setUp() self._server = sdk_fakes.generate_fake_resource(_server.Server) - self.compute_sdk_client.find_server.return_value = self._server + self.compute_client.find_server.return_value = self._server fake_console_data = { 'url': 'http://localhost', 'protocol': 'fake_protocol', 'type': 'fake_type', } - self.compute_sdk_client.create_console = mock.Mock( + self.compute_client.create_console = mock.Mock( return_value=fake_console_data ) @@ -117,7 +117,7 @@ def test_console_url_show_by_default(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='novnc' ) self.assertEqual(self.columns, columns) @@ -134,7 +134,7 @@ def test_console_url_show_with_novnc(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='novnc' ) self.assertEqual(self.columns, columns) @@ -151,7 +151,7 @@ def test_console_url_show_with_xvpvnc(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='xvpvnc' ) self.assertEqual(self.columns, columns) @@ -168,7 +168,7 @@ def test_console_url_show_with_spice(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='spice-html5' ) self.assertEqual(self.columns, columns) @@ -185,7 +185,7 @@ def test_console_url_show_with_rdp(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='rdp-html5' ) self.assertEqual(self.columns, columns) @@ -202,7 +202,7 @@ def test_console_url_show_with_serial(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='serial' ) self.assertEqual(self.columns, columns) @@ -219,7 +219,7 @@ def test_console_url_show_with_mks(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_console.assert_called_once_with( + self.compute_client.create_console.assert_called_once_with( self._server.id, console_type='webmks' ) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py index 1a93b5fc8..4234fbbd5 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -86,7 +86,7 @@ def setUp(self): # Return a project self.projects_mock.get.return_value = self.project - self.compute_sdk_client.create_flavor.return_value = self.flavor + self.compute_client.create_flavor.return_value = self.flavor self.cmd = flavor.CreateFlavor(self.app, None) def test_flavor_create_default_options(self): @@ -109,7 +109,7 @@ def test_flavor_create_default_options(self): } columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_flavor.assert_called_once_with( + self.compute_client.create_flavor.assert_called_once_with( **default_args ) @@ -180,17 +180,17 @@ def test_flavor_create_all_options(self): # convert expected data tuple to list to be able to modify it cmp_data = list(self.data) cmp_data[7] = format_columns.DictColumn(props) - self.compute_sdk_client.create_flavor.return_value = create_flavor - self.compute_sdk_client.create_flavor_extra_specs.return_value = ( + self.compute_client.create_flavor.return_value = create_flavor + self.compute_client.create_flavor_extra_specs.return_value = ( expected_flavor ) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_flavor.assert_called_once_with(**args) - self.compute_sdk_client.create_flavor_extra_specs.assert_called_once_with( + self.compute_client.create_flavor.assert_called_once_with(**args) + self.compute_client.create_flavor_extra_specs.assert_called_once_with( create_flavor, props ) - self.compute_sdk_client.get_flavor_access.assert_not_called() + self.compute_client.get_flavor_access.assert_not_called() self.assertEqual(self.columns, columns) self.assertCountEqual(tuple(cmp_data), data) @@ -265,19 +265,19 @@ def test_flavor_create_other_options(self): # convert expected data tuple to list to be able to modify it cmp_data = list(self.data_private) cmp_data[7] = format_columns.DictColumn(props) - self.compute_sdk_client.create_flavor.return_value = create_flavor - self.compute_sdk_client.create_flavor_extra_specs.return_value = ( + self.compute_client.create_flavor.return_value = create_flavor + self.compute_client.create_flavor_extra_specs.return_value = ( expected_flavor ) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_flavor.assert_called_once_with(**args) - self.compute_sdk_client.flavor_add_tenant_access.assert_called_with( + self.compute_client.create_flavor.assert_called_once_with(**args) + self.compute_client.flavor_add_tenant_access.assert_called_with( self.flavor.id, self.project.id, ) - self.compute_sdk_client.create_flavor_extra_specs.assert_called_with( + self.compute_client.create_flavor_extra_specs.assert_called_with( create_flavor, props ) self.assertEqual(self.columns, columns) @@ -362,7 +362,7 @@ def test_flavor_create_with_description(self): 'description': 'fake description', } - self.compute_sdk_client.create_flavor.assert_called_once_with(**args) + self.compute_client.create_flavor.assert_called_once_with(**args) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data_private, data) @@ -400,7 +400,7 @@ class TestFlavorDelete(TestFlavor): def setUp(self): super().setUp() - self.compute_sdk_client.delete_flavor.return_value = None + self.compute_client.delete_flavor.return_value = None self.cmd = flavor.DeleteFlavor(self.app, None) @@ -411,14 +411,14 @@ def test_flavor_delete(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.find_flavor.return_value = self.flavors[0] + self.compute_client.find_flavor.return_value = self.flavors[0] result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( self.flavors[0].id, ignore_missing=False ) - self.compute_sdk_client.delete_flavor.assert_called_with( + self.compute_client.delete_flavor.assert_called_with( self.flavors[0].id ) self.assertIsNone(result) @@ -433,7 +433,7 @@ def test_delete_multiple_flavors(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.find_flavor.side_effect = self.flavors + self.compute_client.find_flavor.side_effect = self.flavors result = self.cmd.take_action(parsed_args) @@ -441,8 +441,8 @@ def test_delete_multiple_flavors(self): mock.call(i.id, ignore_missing=False) for i in self.flavors ] delete_calls = [mock.call(i.id) for i in self.flavors] - self.compute_sdk_client.find_flavor.assert_has_calls(find_calls) - self.compute_sdk_client.delete_flavor.assert_has_calls(delete_calls) + self.compute_client.find_flavor.assert_has_calls(find_calls) + self.compute_client.delete_flavor.assert_has_calls(delete_calls) self.assertIsNone(result) def test_multi_flavors_delete_with_exception(self): @@ -453,7 +453,7 @@ def test_multi_flavors_delete_with_exception(self): verifylist = [('flavor', [self.flavors[0].id, 'unexist_flavor'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.find_flavor.side_effect = [ + self.compute_client.find_flavor.side_effect = [ self.flavors[0], sdk_exceptions.ResourceNotFound, ] @@ -469,8 +469,8 @@ def test_multi_flavors_delete_with_exception(self): mock.call('unexist_flavor', ignore_missing=False), ] delete_calls = [mock.call(self.flavors[0].id)] - self.compute_sdk_client.find_flavor.assert_has_calls(find_calls) - self.compute_sdk_client.delete_flavor.assert_has_calls(delete_calls) + self.compute_client.find_flavor.assert_has_calls(find_calls) + self.compute_client.delete_flavor.assert_has_calls(delete_calls) class TestFlavorList(TestFlavor): @@ -516,7 +516,7 @@ def setUp(self): [], ] - self.compute_sdk_client.flavors = self.api_mock + self.compute_client.flavors = self.api_mock # Get the command object to test self.cmd = flavor.ListFlavor(self.app, None) @@ -541,8 +541,8 @@ def test_flavor_list_no_options(self): 'is_public': True, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -567,8 +567,8 @@ def test_flavor_list_all_flavors(self): 'is_public': None, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -593,8 +593,8 @@ def test_flavor_list_private_flavors(self): 'is_public': False, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -619,8 +619,8 @@ def test_flavor_list_public_flavors(self): 'is_public': True, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -645,8 +645,8 @@ def test_flavor_list_long(self): 'is_public': True, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns_long, columns) self.assertCountEqual(self.data_long, tuple(data)) @@ -678,8 +678,8 @@ def test_flavor_list_long_no_extra_specs(self): [], ] - self.compute_sdk_client.flavors = self.api_mock - self.compute_sdk_client.fetch_flavor_extra_specs = mock.Mock( + self.compute_client.flavors = self.api_mock + self.compute_client.fetch_flavor_extra_specs = mock.Mock( return_value=None ) @@ -702,8 +702,8 @@ def test_flavor_list_long_no_extra_specs(self): 'is_public': True, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_called_once_with( + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_called_once_with( flavor ) @@ -736,15 +736,15 @@ def test_flavor_list_min_disk_min_ram(self): 'min_ram': 2048, } - self.compute_sdk_client.flavors.assert_called_with(**kwargs) - self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_client.flavors.assert_called_with(**kwargs) + self.compute_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) class TestFlavorSet(TestFlavor): - # Return value of self.compute_sdk_client.find_flavor(). + # Return value of self.compute_client.find_flavor(). flavor = compute_fakes.create_one_flavor( attrs={'os-flavor-access:is_public': False} ) @@ -753,7 +753,7 @@ class TestFlavorSet(TestFlavor): def setUp(self): super().setUp() - self.compute_sdk_client.find_flavor.return_value = self.flavor + self.compute_client.find_flavor.return_value = self.flavor # Return a project self.projects_mock.get.return_value = self.project self.cmd = flavor.SetFlavor(self.app, None) @@ -767,10 +767,10 @@ def test_flavor_set_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.compute_sdk_client.create_flavor_extra_specs.assert_called_with( + self.compute_client.create_flavor_extra_specs.assert_called_with( self.flavor.id, {'FOO': '"B A R"'} ) self.assertIsNone(result) @@ -781,10 +781,10 @@ def test_flavor_set_no_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.compute_sdk_client.delete_flavor_extra_specs_property.assert_called_with( + self.compute_client.delete_flavor_extra_specs_property.assert_called_with( self.flavor.id, 'property' ) self.assertIsNone(result) @@ -803,14 +803,14 @@ def test_flavor_set_project(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.compute_sdk_client.flavor_add_tenant_access.assert_called_with( + self.compute_client.flavor_add_tenant_access.assert_called_with( self.flavor.id, self.project.id, ) - self.compute_sdk_client.create_flavor_extra_specs.assert_not_called() + self.compute_client.create_flavor_extra_specs.assert_not_called() self.assertIsNone(result) def test_flavor_set_no_project(self): @@ -847,7 +847,7 @@ def test_flavor_set_no_flavor(self): ) def test_flavor_set_with_unexist_flavor(self): - self.compute_sdk_client.find_flavor.side_effect = [ + self.compute_client.find_flavor.side_effect = [ sdk_exceptions.ResourceNotFound() ] @@ -876,10 +876,10 @@ def test_flavor_set_nothing(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.compute_sdk_client.flavor_add_tenant_access.assert_not_called() + self.compute_client.flavor_add_tenant_access.assert_not_called() self.assertIsNone(result) def test_flavor_set_description(self): @@ -897,7 +897,7 @@ def test_flavor_set_description(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_flavor.assert_called_with( + self.compute_client.update_flavor.assert_called_with( flavor=self.flavor.id, description='description' ) self.assertIsNone(result) @@ -935,7 +935,7 @@ def test_flavor_set_description_using_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_flavor.assert_called_with( + self.compute_client.update_flavor.assert_called_with( flavor=self.flavor.id, description='description' ) self.assertIsNone(result) @@ -960,7 +960,7 @@ def test_flavor_set_description_using_name_pre_v255(self): class TestFlavorShow(TestFlavor): - # Return value of self.compute_sdk_client.find_flavor(). + # Return value of self.compute_client.find_flavor(). flavor_access = compute_fakes.create_one_flavor_access() flavor = compute_fakes.create_one_flavor() @@ -1000,8 +1000,8 @@ def setUp(self): super().setUp() # Return value of _find_resource() - self.compute_sdk_client.find_flavor.return_value = self.flavor - self.compute_sdk_client.get_flavor_access.return_value = [ + self.compute_client.find_flavor.return_value = self.flavor + self.compute_client.get_flavor_access.return_value = [ self.flavor_access ] self.cmd = flavor.ShowFlavor(self.app, None) @@ -1040,7 +1040,7 @@ def test_private_flavor_show(self): 'os-flavor-access:is_public': False, } ) - self.compute_sdk_client.find_flavor.return_value = private_flavor + self.compute_client.find_flavor.return_value = private_flavor arglist = [ private_flavor.name, @@ -1069,7 +1069,7 @@ def test_private_flavor_show(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_flavor_access.assert_called_with( + self.compute_client.get_flavor_access.assert_called_with( flavor=private_flavor.id ) self.assertEqual(self.columns, columns) @@ -1077,7 +1077,7 @@ def test_private_flavor_show(self): class TestFlavorUnset(TestFlavor): - # Return value of self.compute_sdk_client.find_flavor(). + # Return value of self.compute_client.find_flavor(). flavor = compute_fakes.create_one_flavor( attrs={'os-flavor-access:is_public': False} ) @@ -1086,13 +1086,13 @@ class TestFlavorUnset(TestFlavor): def setUp(self): super().setUp() - self.compute_sdk_client.find_flavor.return_value = self.flavor + self.compute_client.find_flavor.return_value = self.flavor # Return a project self.projects_mock.get.return_value = self.project self.cmd = flavor.UnsetFlavor(self.app, None) self.mock_shortcut = ( - self.compute_sdk_client.delete_flavor_extra_specs_property + self.compute_client.delete_flavor_extra_specs_property ) def test_flavor_unset_property(self): @@ -1104,11 +1104,11 @@ def test_flavor_unset_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) self.mock_shortcut.assert_called_with(self.flavor.id, 'property') - self.compute_sdk_client.flavor_remove_tenant_access.assert_not_called() + self.compute_client.flavor_remove_tenant_access.assert_not_called() self.assertIsNone(result) def test_flavor_unset_properties(self): @@ -1126,7 +1126,7 @@ def test_flavor_unset_properties(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) calls = [ @@ -1141,7 +1141,7 @@ def test_flavor_unset_properties(self): AssertionError, self.mock_shortcut.assert_has_calls, calls ) - self.compute_sdk_client.flavor_remove_tenant_access.assert_not_called() + self.compute_client.flavor_remove_tenant_access.assert_not_called() def test_flavor_unset_project(self): arglist = [ @@ -1158,14 +1158,14 @@ def test_flavor_unset_project(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.find_flavor.assert_called_with( + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.compute_sdk_client.flavor_remove_tenant_access.assert_called_with( + self.compute_client.flavor_remove_tenant_access.assert_called_with( self.flavor.id, self.project.id, ) - self.compute_sdk_client.delete_flavor_extra_specs_property.assert_not_called() + self.compute_client.delete_flavor_extra_specs_property.assert_not_called() self.assertIsNone(result) def test_flavor_unset_no_project(self): @@ -1202,7 +1202,7 @@ def test_flavor_unset_no_flavor(self): ) def test_flavor_unset_with_unexist_flavor(self): - self.compute_sdk_client.find_flavor.side_effect = [ + self.compute_client.find_flavor.side_effect = [ sdk_exceptions.ResourceNotFound ] @@ -1232,4 +1232,4 @@ def test_flavor_unset_nothing(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.flavor_remove_tenant_access.assert_not_called() + self.compute_client.flavor_remove_tenant_access.assert_not_called() diff --git a/openstackclient/tests/unit/compute/v2/test_host.py b/openstackclient/tests/unit/compute/v2/test_host.py index 1de802311..8d38f8353 100644 --- a/openstackclient/tests/unit/compute/v2/test_host.py +++ b/openstackclient/tests/unit/compute/v2/test_host.py @@ -72,7 +72,7 @@ def setUp(self): ) ] - self.compute_sdk_client.get.return_value = fakes.FakeResponse( + self.compute_client.get.return_value = fakes.FakeResponse( data={'hosts': [self._host]} ) self.cmd = host.ListHost(self.app, None) @@ -85,7 +85,7 @@ def test_host_list_no_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.get.assert_called_with( + self.compute_client.get.assert_called_with( '/os-hosts', microversion='2.1' ) self.assertEqual(self.columns, columns) @@ -104,7 +104,7 @@ def test_host_list_with_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.get.assert_called_with( + self.compute_client.get.assert_called_with( '/os-hosts', microversion='2.1' ) self.assertEqual(self.columns, columns) @@ -116,7 +116,7 @@ def setUp(self): super().setUp() self._host = _generate_fake_host() - self.compute_sdk_client.put.return_value = fakes.FakeResponse() + self.compute_client.put.return_value = fakes.FakeResponse() self.cmd = host.SetHost(self.app, None) @@ -132,7 +132,7 @@ def test_host_set_no_option(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.put.assert_not_called() + self.compute_client.put.assert_not_called() def test_host_set(self): arglist = [ @@ -150,7 +150,7 @@ def test_host_set(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.put.assert_called_with( + self.compute_client.put.assert_called_with( f'/os-hosts/{self._host["host"]}', json={ 'maintenance_mode': 'disable', @@ -183,7 +183,7 @@ def setUp(self): ) ] - self.compute_sdk_client.get.return_value = fakes.FakeResponse( + self.compute_client.get.return_value = fakes.FakeResponse( data={ 'host': [ { @@ -226,7 +226,7 @@ def test_host_show_with_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.get.assert_called_with( + self.compute_client.get.assert_called_with( '/os-hosts/' + self._host['host_name'], microversion='2.1' ) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor.py b/openstackclient/tests/unit/compute/v2/test_hypervisor.py index da6e7e56d..4282982a2 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor.py @@ -32,9 +32,7 @@ def setUp(self): self.hypervisors = list( sdk_fakes.generate_fake_resources(_hypervisor.Hypervisor, count=2) ) - self.compute_sdk_client.hypervisors.return_value = iter( - self.hypervisors - ) + self.compute_client.hypervisors.return_value = iter(self.hypervisors) self.columns = ( "ID", @@ -108,7 +106,7 @@ def test_hypervisor_list_no_option(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.hypervisors.assert_called_with(details=True) + self.compute_client.hypervisors.assert_called_with(details=True) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -123,9 +121,7 @@ def test_hypervisor_list_matching_option_found(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) # Fake the return value of search() - self.compute_sdk_client.hypervisors.return_value = [ - self.hypervisors[0] - ] + self.compute_client.hypervisors.return_value = [self.hypervisors[0]] self.data = ( ( @@ -142,7 +138,7 @@ def test_hypervisor_list_matching_option_found(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.hypervisors.assert_called_with( + self.compute_client.hypervisors.assert_called_with( hypervisor_hostname_pattern=self.hypervisors[0].name, details=True ) self.assertEqual(self.columns, columns) @@ -159,9 +155,7 @@ def test_hypervisor_list_matching_option_not_found(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) # Fake exception raised from search() - self.compute_sdk_client.hypervisors.side_effect = exceptions.NotFound( - None - ) + self.compute_client.hypervisors.side_effect = exceptions.NotFound(None) self.assertRaises( exceptions.NotFound, self.cmd.take_action, parsed_args @@ -205,7 +199,7 @@ def test_hypervisor_list_long_option(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.hypervisors.assert_called_with(details=True) + self.compute_client.hypervisors.assert_called_with(details=True) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, tuple(data)) @@ -223,7 +217,7 @@ def test_hypervisor_list_with_limit(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.hypervisors.assert_called_with( + self.compute_client.hypervisors.assert_called_with( limit=1, details=True ) @@ -261,7 +255,7 @@ def test_hypervisor_list_with_marker(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.hypervisors.assert_called_with( + self.compute_client.hypervisors.assert_called_with( marker='test_hyp', details=True ) @@ -303,10 +297,10 @@ def setUp(self): cpu_info={"aaa": "aaa"}, ) - self.compute_sdk_client.find_hypervisor.return_value = self.hypervisor - self.compute_sdk_client.get_hypervisor.return_value = self.hypervisor + self.compute_client.find_hypervisor.return_value = self.hypervisor + self.compute_client.get_hypervisor.return_value = self.hypervisor - self.compute_sdk_client.aggregates.return_value = [] + self.compute_client.aggregates.return_value = [] uptime_info = { 'status': self.hypervisor.status, @@ -315,9 +309,7 @@ def setUp(self): 'hypervisor_hostname': self.hypervisor.name, 'uptime': uptime_string, } - self.compute_sdk_client.get_hypervisor_uptime.return_value = ( - uptime_info - ) + self.compute_client.get_hypervisor_uptime.return_value = uptime_info self.columns_v288 = ( 'aggregates', @@ -434,10 +426,10 @@ def test_hypervisor_show(self): self.assertEqual(self.columns_v288, columns) self.assertCountEqual(self.data_v288, data) - self.compute_sdk_client.find_hypervisor.assert_called_once_with( + self.compute_client.find_hypervisor.assert_called_once_with( self.hypervisor.name, ignore_missing=False, details=False ) - self.compute_sdk_client.get_hypervisor.assert_called_once_with( + self.compute_client.get_hypervisor.assert_called_once_with( self.hypervisor.id ) @@ -460,10 +452,10 @@ def test_hypervisor_show_pre_v288(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) - self.compute_sdk_client.find_hypervisor.assert_called_once_with( + self.compute_client.find_hypervisor.assert_called_once_with( self.hypervisor.name, ignore_missing=False, details=False ) - self.compute_sdk_client.get_hypervisor.assert_called_once_with( + self.compute_client.get_hypervisor.assert_called_once_with( self.hypervisor.id ) @@ -473,7 +465,7 @@ def test_hypervisor_show_pre_v228(self): # before microversion 2.28, nova returned a stringified version of this # field self.hypervisor.cpu_info = json.dumps(self.hypervisor.cpu_info) - self.compute_sdk_client.find_hypervisor.return_value = self.hypervisor + self.compute_client.find_hypervisor.return_value = self.hypervisor arglist = [ self.hypervisor.name, @@ -491,10 +483,10 @@ def test_hypervisor_show_pre_v228(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) - self.compute_sdk_client.find_hypervisor.assert_called_once_with( + self.compute_client.find_hypervisor.assert_called_once_with( self.hypervisor.name, ignore_missing=False, details=False ) - self.compute_sdk_client.get_hypervisor.assert_called_once_with( + self.compute_client.get_hypervisor.assert_called_once_with( self.hypervisor.id ) @@ -509,7 +501,7 @@ def test_hypervisor_show_uptime_not_implemented(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.get_hypervisor_uptime.side_effect = ( + self.compute_client.get_hypervisor_uptime.side_effect = ( sdk_exceptions.HttpException(http_status=501) ) @@ -570,9 +562,9 @@ def test_hypervisor_show_uptime_not_implemented(self): self.assertEqual(expected_columns, columns) self.assertCountEqual(expected_data, data) - self.compute_sdk_client.find_hypervisor.assert_called_once_with( + self.compute_client.find_hypervisor.assert_called_once_with( self.hypervisor.name, ignore_missing=False, details=False ) - self.compute_sdk_client.get_hypervisor.assert_called_once_with( + self.compute_client.get_hypervisor.assert_called_once_with( self.hypervisor.id ) diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py index 5a4e3c52f..045efda2d 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py @@ -23,7 +23,7 @@ class TestHypervisorStats(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.compute_sdk_client.get = mock.Mock() + self.compute_client.get = mock.Mock() # Not in fakes.py because hypervisor stats has been deprecated @@ -67,7 +67,7 @@ class TestHypervisorStatsShow(TestHypervisorStats): def setUp(self): super().setUp() - self.compute_sdk_client.get.return_value = fakes.FakeResponse( + self.compute_client.get.return_value = fakes.FakeResponse( data={'hypervisor_statistics': self._stats} ) diff --git a/openstackclient/tests/unit/compute/v2/test_keypair.py b/openstackclient/tests/unit/compute/v2/test_keypair.py index 0ce08b48c..85a4547a6 100644 --- a/openstackclient/tests/unit/compute/v2/test_keypair.py +++ b/openstackclient/tests/unit/compute/v2/test_keypair.py @@ -66,7 +66,7 @@ def setUp(self): # Get the command object to test self.cmd = keypair.CreateKeypair(self.app, None) - self.compute_sdk_client.create_keypair.return_value = self.keypair + self.compute_client.create_keypair.return_value = self.keypair @mock.patch.object( keypair, @@ -84,7 +84,7 @@ def test_keypair_create_no_options(self, mock_generate): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_keypair.assert_called_with( + self.compute_client.create_keypair.assert_called_with( name=self.keypair.name, public_key=mock_generate.return_value.public_key, ) @@ -124,7 +124,7 @@ def test_keypair_create_public_key(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_keypair.assert_called_with( + self.compute_client.create_keypair.assert_called_with( name=self.keypair.name, public_key=self.keypair.public_key, ) @@ -159,7 +159,7 @@ def test_keypair_create_private_key(self, mock_generate): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_keypair.assert_called_with( + self.compute_client.create_keypair.assert_called_with( name=self.keypair.name, public_key=mock_generate.return_value.public_key, ) @@ -176,7 +176,7 @@ def test_keypair_create_with_key_type(self): self.set_compute_api_version('2.2') for key_type in ['x509', 'ssh']: - self.compute_sdk_client.create_keypair.return_value = self.keypair + self.compute_client.create_keypair.return_value = self.keypair self.data = ( self.keypair.created_at, @@ -209,7 +209,7 @@ def test_keypair_create_with_key_type(self): m_file.read.return_value = self.keypair.public_key columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_keypair.assert_called_with( + self.compute_client.create_keypair.assert_called_with( name=self.keypair.name, public_key=self.keypair.public_key, key_type=key_type, @@ -272,7 +272,7 @@ def test_key_pair_create_with_user(self, mock_generate): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_keypair.assert_called_with( + self.compute_client.create_keypair.assert_called_with( name=self.keypair.name, user_id=self._user.id, public_key=mock_generate.return_value.public_key, @@ -324,7 +324,7 @@ def test_keypair_delete(self): ret = self.cmd.take_action(parsed_args) self.assertIsNone(ret) - self.compute_sdk_client.delete_keypair.assert_called_with( + self.compute_client.delete_keypair.assert_called_with( self.keypairs[0].name, ignore_missing=False ) @@ -342,7 +342,7 @@ def test_delete_multiple_keypairs(self): calls = [] for k in self.keypairs: calls.append(call(k.name, ignore_missing=False)) - self.compute_sdk_client.delete_keypair.assert_has_calls(calls) + self.compute_client.delete_keypair.assert_has_calls(calls) self.assertIsNone(result) def test_delete_multiple_keypairs_with_exception(self): @@ -356,7 +356,7 @@ def test_delete_multiple_keypairs_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.delete_keypair.side_effect = [ + self.compute_client.delete_keypair.side_effect = [ None, exceptions.CommandError, ] @@ -369,7 +369,7 @@ def test_delete_multiple_keypairs_with_exception(self): calls = [] for k in arglist: calls.append(call(k, ignore_missing=False)) - self.compute_sdk_client.delete_keypair.assert_has_calls(calls) + self.compute_client.delete_keypair.assert_has_calls(calls) def test_keypair_delete_with_user(self): self.set_compute_api_version('2.10') @@ -384,7 +384,7 @@ def test_keypair_delete_with_user(self): ret = self.cmd.take_action(parsed_args) self.assertIsNone(ret) - self.compute_sdk_client.delete_keypair.assert_called_with( + self.compute_client.delete_keypair.assert_called_with( self.keypairs[0].name, user_id=self._user.id, ignore_missing=False, @@ -415,7 +415,7 @@ def setUp(self): self.keypairs = list( sdk_fakes.generate_fake_resources(_keypair.Keypair, count=1) ) - self.compute_sdk_client.keypairs.return_value = iter(self.keypairs) + self.compute_client.keypairs.return_value = iter(self.keypairs) # Get the command object to test self.cmd = keypair.ListKeypair(self.app, None) @@ -435,7 +435,7 @@ def test_keypair_list_no_options(self): # Set expected values - self.compute_sdk_client.keypairs.assert_called_with() + self.compute_client.keypairs.assert_called_with() self.assertEqual(('Name', 'Fingerprint'), columns) self.assertEqual( @@ -458,7 +458,7 @@ def test_keypair_list_v22(self): # Set expected values - self.compute_sdk_client.keypairs.assert_called_with() + self.compute_client.keypairs.assert_called_with() self.assertEqual(('Name', 'Fingerprint', 'Type'), columns) self.assertEqual( @@ -491,7 +491,7 @@ def test_keypair_list_with_user(self): columns, data = self.cmd.take_action(parsed_args) users_mock.get.assert_called_with(self._user.name) - self.compute_sdk_client.keypairs.assert_called_with( + self.compute_client.keypairs.assert_called_with( user_id=self._user.id, ) @@ -545,7 +545,7 @@ def test_keypair_list_with_project(self): projects_mock.get.assert_called_with(self._project.name) users_mock.list.assert_called_with(tenant_id=self._project.id) - self.compute_sdk_client.keypairs.assert_called_with( + self.compute_client.keypairs.assert_called_with( user_id=self._user.id, ) @@ -605,7 +605,7 @@ def test_keypair_list_with_limit(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.keypairs.assert_called_with(limit=1) + self.compute_client.keypairs.assert_called_with(limit=1) def test_keypair_list_with_limit_pre_v235(self): self.set_compute_api_version('2.34') @@ -641,7 +641,7 @@ def test_keypair_list_with_marker(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.keypairs.assert_called_with(marker='test_kp') + self.compute_client.keypairs.assert_called_with(marker='test_kp') def test_keypair_list_with_marker_pre_v235(self): self.set_compute_api_version('2.34') @@ -696,7 +696,7 @@ def test_keypair_show_no_options(self): def test_keypair_show(self): self.keypair = sdk_fakes.generate_fake_resource(_keypair.Keypair) - self.compute_sdk_client.find_keypair.return_value = self.keypair + self.compute_client.find_keypair.return_value = self.keypair self.data = ( self.keypair.created_at, @@ -715,7 +715,7 @@ def test_keypair_show(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_keypair.assert_called_with( + self.compute_client.find_keypair.assert_called_with( self.keypair.name, ignore_missing=False ) @@ -724,7 +724,7 @@ def test_keypair_show(self): def test_keypair_show_public(self): self.keypair = sdk_fakes.generate_fake_resource(_keypair.Keypair) - self.compute_sdk_client.find_keypair.return_value = self.keypair + self.compute_client.find_keypair.return_value = self.keypair arglist = ['--public-key', self.keypair.name] verifylist = [('public_key', True), ('name', self.keypair.name)] @@ -740,7 +740,7 @@ def test_keypair_show_with_user(self): self.set_compute_api_version('2.10') self.keypair = sdk_fakes.generate_fake_resource(_keypair.Keypair) - self.compute_sdk_client.find_keypair.return_value = self.keypair + self.compute_client.find_keypair.return_value = self.keypair self.data = ( self.keypair.created_at, @@ -767,7 +767,7 @@ def test_keypair_show_with_user(self): columns, data = self.cmd.take_action(parsed_args) self.users_mock.get.assert_called_with(self._user.name) - self.compute_sdk_client.find_keypair.assert_called_with( + self.compute_client.find_keypair.assert_called_with( self.keypair.name, ignore_missing=False, user_id=self._user.id, diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 2fa3aa7e1..39af6af7a 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -76,7 +76,7 @@ def setup_sdk_servers_mock(self, count): ) # This is the return value for compute_client.find_server() - self.compute_sdk_client.find_server.side_effect = servers + self.compute_client.find_server.side_effect = servers return servers @@ -132,9 +132,7 @@ def test_server_add_fixed_ip(self): servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() interface = compute_fakes.create_one_server_interface() - self.compute_sdk_client.create_server_interface.return_value = ( - interface - ) + self.compute_client.create_server_interface.return_value = interface with mock.patch.object( self.app.client_manager, @@ -169,7 +167,7 @@ def test_server_add_fixed_ip(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, tuple(data)) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'] ) @@ -179,9 +177,7 @@ def test_server_add_fixed_ip_with_fixed_ip(self): servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() interface = compute_fakes.create_one_server_interface() - self.compute_sdk_client.create_server_interface.return_value = ( - interface - ) + self.compute_client.create_server_interface.return_value = interface with mock.patch.object( self.app.client_manager, @@ -222,7 +218,7 @@ def test_server_add_fixed_ip_with_fixed_ip(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, tuple(data)) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'], fixed_ips=[{'ip_address': '5.6.7.8'}], @@ -234,9 +230,7 @@ def test_server_add_fixed_ip_with_tag(self): servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() interface = compute_fakes.create_one_server_interface() - self.compute_sdk_client.create_server_interface.return_value = ( - interface - ) + self.compute_client.create_server_interface.return_value = interface with mock.patch.object( self.app.client_manager, @@ -282,7 +276,7 @@ def test_server_add_fixed_ip_with_tag(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, tuple(data)) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'], fixed_ips=[{'ip_address': '5.6.7.8'}], @@ -295,9 +289,7 @@ def test_server_add_fixed_ip_with_fixed_ip_with_tag(self): servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() interface = compute_fakes.create_one_server_interface() - self.compute_sdk_client.create_server_interface.return_value = ( - interface - ) + self.compute_client.create_server_interface.return_value = interface with mock.patch.object( self.app.client_manager, @@ -343,7 +335,7 @@ def test_server_add_fixed_ip_with_fixed_ip_with_tag(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, tuple(data)) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'], fixed_ips=[{'ip_address': '5.6.7.8'}], @@ -357,7 +349,7 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server self.cmd = server.AddFloatingIP(self.app, None) @@ -374,10 +366,10 @@ def test_server_add_floating_ip_default(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False ) - self.compute_sdk_client.add_floating_ip_to_server.assert_called_once_with( + self.compute_client.add_floating_ip_to_server.assert_called_once_with( self.server, '1.2.3.4', fixed_address=None ) @@ -397,10 +389,10 @@ def test_server_add_floating_ip_fixed(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False ) - self.compute_sdk_client.add_floating_ip_to_server.assert_called_once_with( + self.compute_client.add_floating_ip_to_server.assert_called_once_with( self.server, '1.2.3.4', fixed_address='5.6.7.8' ) @@ -413,7 +405,7 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server self.network_client.update_ip = mock.Mock(return_value=None) @@ -637,7 +629,7 @@ def _test_server_add_port(self, port_id): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0], port_id=port_id ) self.assertIsNone(result) @@ -674,7 +666,7 @@ def test_server_add_port_with_tag(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0], port_id='fake-port', tag='tag1' ) @@ -709,7 +701,7 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server self.volume = volume_fakes.create_one_sdk_volume() self.volume_sdk_client.find_volume.return_value = self.volume @@ -722,7 +714,7 @@ def setUp(self): attrs=attrs ) - self.compute_sdk_client.create_volume_attachment.return_value = ( + self.compute_client.create_volume_attachment.return_value = ( self.volume_attachment ) @@ -762,7 +754,7 @@ def test_server_add_volume(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) - self.compute_sdk_client.create_volume_attachment.assert_called_once_with( + self.compute_client.create_volume_attachment.assert_called_once_with( self.server, volumeId=self.volume.id, device='/dev/sdb' ) @@ -799,7 +791,7 @@ def test_server_add_volume_with_tag(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) - self.compute_sdk_client.create_volume_attachment.assert_called_once_with( + self.compute_client.create_volume_attachment.assert_called_once_with( self.server, volumeId=self.volume.id, device='/dev/sdb', @@ -869,7 +861,7 @@ def test_server_add_volume_with_enable_delete_on_termination(self): columns, data = self.cmd.take_action(parsed_args) self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) - self.compute_sdk_client.create_volume_attachment.assert_called_once_with( + self.compute_client.create_volume_attachment.assert_called_once_with( self.server, volumeId=self.volume.id, device='/dev/sdb', @@ -918,7 +910,7 @@ def test_server_add_volume_with_disable_delete_on_termination(self): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) - self.compute_sdk_client.create_volume_attachment.assert_called_once_with( + self.compute_client.create_volume_attachment.assert_called_once_with( self.server, volumeId=self.volume.id, device='/dev/sdb', @@ -1031,7 +1023,7 @@ def test_server_remove_volume(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.delete_volume_attachment.assert_called_once_with( + self.compute_client.delete_volume_attachment.assert_called_once_with( self.volume, self.server, ignore_missing=False, @@ -1061,7 +1053,7 @@ def _test_server_add_network(self, net_id): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0], net_id=net_id ) self.assertIsNone(result) @@ -1099,7 +1091,7 @@ def test_server_add_network_with_tag(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.create_server_interface.assert_called_once_with( + self.compute_client.create_server_interface.assert_called_once_with( servers[0], net_id='fake-network', tag='tag1' ) @@ -1135,10 +1127,8 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.add_security_group_to_server.return_value = ( - None - ) + self.compute_client.find_server.return_value = self.server + self.compute_client.add_security_group_to_server.return_value = None # Get the command object to test self.cmd = server.AddServerSecurityGroup(self.app, None) @@ -1163,14 +1153,14 @@ def test_server_add_security_group__nova_network(self): ) as mock_find_nova_net_sg: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.add_security_group_to_server.assert_called_once_with( + self.compute_client.add_security_group_to_server.assert_called_once_with( self.server, {'name': 'fake_sg'} ) mock_find_nova_net_sg.assert_called_once_with( - self.compute_sdk_client, 'fake_sg' + self.compute_client, 'fake_sg' ) self.assertIsNone(result) @@ -1184,10 +1174,10 @@ def test_server_add_security_group(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.add_security_group_to_server.assert_called_once_with( + self.compute_client.add_security_group_to_server.assert_called_once_with( self.server, {'name': 'fake_sg'} ) self.assertIsNone(result) @@ -1297,7 +1287,7 @@ def setUp(self): self.image_client.get_image.return_value = self.image self.flavor = compute_fakes.create_one_flavor() - self.compute_sdk_client.find_flavor.return_value = self.flavor + self.compute_client.find_flavor.return_value = self.flavor attrs = { 'addresses': {}, @@ -1307,8 +1297,8 @@ def setUp(self): } self.server = compute_fakes.create_one_sdk_server(attrs=attrs) - self.compute_sdk_client.create_server.return_value = self.server - self.compute_sdk_client.get_server.return_value = self.server + self.compute_client.create_server.return_value = self.server + self.compute_client.get_server.return_value = self.server self.volume = volume_fakes.create_one_volume() self.snapshot = volume_fakes.create_one_snapshot() @@ -1350,13 +1340,13 @@ def test_server_create_minimal(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_has_calls( + self.compute_client.find_flavor.assert_has_calls( [mock.call(self.flavor.id, ignore_missing=False)] * 2 ) self.image_client.find_image.assert_called_once_with( self.image.id, ignore_missing=False ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1380,7 +1370,7 @@ def test_server_create_with_options(self): server_group = sdk_fakes.generate_fake_resource( _server_group.ServerGroup ) - self.compute_sdk_client.find_server_group.return_value = server_group + self.compute_client.find_server_group.return_value = server_group security_group = network_fakes.create_one_security_group() self.network_client.find_security_group.return_value = security_group @@ -1423,7 +1413,7 @@ def test_server_create_with_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_has_calls( + self.compute_client.find_flavor.assert_has_calls( [mock.call(self.flavor.id, ignore_missing=False)] * 2 ) self.network_client.find_security_group.assert_called_once_with( @@ -1432,10 +1422,10 @@ def test_server_create_with_options(self): self.image_client.find_image.assert_called_once_with( self.image.id, ignore_missing=False ) - self.compute_sdk_client.find_server_group.assert_called_once_with( + self.compute_client.find_server_group.assert_called_once_with( server_group.id, ignore_missing=False ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1525,8 +1515,8 @@ def test_server_create_with_security_group_in_nova_network(self): ) as mock_find: columns, data = self.cmd.take_action(parsed_args) - mock_find.assert_called_once_with(self.compute_sdk_client, sg_name) - self.compute_sdk_client.create_server.assert_called_once_with( + mock_find.assert_called_once_with(self.compute_client, sg_name) + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1573,14 +1563,14 @@ def test_server_create_with_no_security_group(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_has_calls( + self.compute_client.find_flavor.assert_has_calls( [mock.call(self.flavor.id, ignore_missing=False)] * 2 ) self.network_client.find_security_group.assert_not_called() self.image_client.find_image.assert_called_once_with( self.image.id, ignore_missing=False ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1702,7 +1692,7 @@ def find_port(name_or_id, ignore_missing): mock.call(port_port2.id, ignore_missing=False), ] ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1778,7 +1768,7 @@ def test_server_create_with_network_tag(self): self.network_client.find_network.assert_called_once_with( network.id, ignore_missing=False ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1839,7 +1829,7 @@ def test_server_create_with_network_tag_pre_v243(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) self.network_client.find_network.assert_not_called() - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def _test_server_create_with_auto_network(self, arglist): # requires API microversion 2.37 or later @@ -1857,7 +1847,7 @@ def _test_server_create_with_auto_network(self, arglist): columns, data = self.cmd.take_action(parsed_args) self.network_client.find_network.assert_not_called() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -1937,7 +1927,7 @@ def test_server_create_with_auto_network_pre_v237(self): 'allocation', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_auto_network_default(self): """Tests creating a server without specifying --nic using 2.37.""" @@ -1963,7 +1953,7 @@ def test_server_create_with_auto_network_default(self): columns, data = self.cmd.take_action(parsed_args) self.network_client.find_network.assert_not_called() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2000,7 +1990,7 @@ def _test_server_create_with_none_network(self, arglist): columns, data = self.cmd.take_action(parsed_args) self.network_client.find_network.assert_not_called() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2080,7 +2070,7 @@ def test_server_create_with_none_network_pre_v237(self): 'allocation', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_conflicting_network_options(self): arglist = [ @@ -2125,7 +2115,7 @@ def test_server_create_with_conflicting_network_options(self): 'other --nic, --network or --port value.', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_invalid_network_options(self): arglist = [ @@ -2148,7 +2138,7 @@ def test_server_create_with_invalid_network_options(self): 'Invalid argument abcdefgh; argument must be of form ', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_invalid_network_key(self): arglist = [ @@ -2171,7 +2161,7 @@ def test_server_create_with_invalid_network_key(self): 'Invalid argument abcdefgh=12324; argument must be of form ', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_empty_network_key_value(self): arglist = [ @@ -2194,7 +2184,7 @@ def test_server_create_with_empty_network_key_value(self): 'Invalid argument net-id=; argument must be of form ', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_only_network_key(self): arglist = [ @@ -2217,7 +2207,7 @@ def test_server_create_with_only_network_key(self): 'Invalid argument net-id; argument must be of form ', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_network_in_nova_network(self): net_name = 'nova-net-net' @@ -2263,8 +2253,8 @@ def test_server_create_with_network_in_nova_network(self): ) as mock_find: columns, data = self.cmd.take_action(parsed_args) - mock_find.assert_called_once_with(self.compute_sdk_client, net_name) - self.compute_sdk_client.create_server.assert_called_once_with( + mock_find.assert_called_once_with(self.compute_client, net_name) + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2307,7 +2297,7 @@ def test_server_create_with_conflicting_net_port_filters(self): [], ) self.assertIn("either 'network' or 'port'", str(exc)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_conflicting_fixed_ip_filters(self): arglist = [ @@ -2327,7 +2317,7 @@ def test_server_create_with_conflicting_fixed_ip_filters(self): [], ) self.assertIn("either 'v4-fixed-ip' or 'v6-fixed-ip'", str(exc)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() @mock.patch.object(common_utils, 'wait_for_status', return_value=True) def test_server_create_with_wait_ok(self, mock_wait_for_status): @@ -2350,7 +2340,7 @@ def test_server_create_with_wait_ok(self, mock_wait_for_status): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2368,7 +2358,7 @@ def test_server_create_with_wait_ok(self, mock_wait_for_status): ], ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, ) @@ -2399,7 +2389,7 @@ def test_server_create_with_wait_fails(self, mock_wait_for_status): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2417,7 +2407,7 @@ def test_server_create_with_wait_fails(self, mock_wait_for_status): ], ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, ) @@ -2449,7 +2439,7 @@ def test_server_create_userdata(self): columns, data = self.cmd.take_action(parsed_args) mock_file.assert_called_with('userdata.sh', 'rb') - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2493,7 +2483,7 @@ def test_server_create_with_volume(self): self.volume_client.volumes.get.assert_called_once_with( self.volume.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id='', flavor_id=self.flavor.id, @@ -2535,7 +2525,7 @@ def test_server_create_with_snapshot(self): self.volume_client.volume_snapshots.get.assert_called_once_with( self.snapshot.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id='', flavor_id=self.flavor.id, @@ -2586,7 +2576,7 @@ def test_server_create_with_block_device(self): # we don't do any validation of IDs when using the legacy option self.volume_client.volumes.get.assert_not_called() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id='', flavor_id=self.flavor.id, @@ -2664,7 +2654,7 @@ def test_server_create_with_block_device_full(self): # we don't do any validation of IDs when using the legacy option self.volume_client.volumes.get.assert_not_called() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2746,7 +2736,7 @@ def test_server_create_with_block_device_from_file(self): # we don't do any validation of IDs when using the legacy option self.volume_client.volumes.get.assert_not_called() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -2799,7 +2789,7 @@ def test_server_create_with_block_device_invalid_boot_index(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn('The boot_index key of --block-device ', str(ex)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_invalid_source_type(self): block_device = f'uuid={self.volume.name},source_type=foo' @@ -2817,7 +2807,7 @@ def test_server_create_with_block_device_invalid_source_type(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn('The source_type key of --block-device ', str(ex)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_invalid_destination_type(self): block_device = f'uuid={self.volume.name},destination_type=foo' @@ -2835,7 +2825,7 @@ def test_server_create_with_block_device_invalid_destination_type(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn('The destination_type key of --block-device ', str(ex)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_invalid_shutdown(self): block_device = f'uuid={self.volume.name},delete_on_termination=foo' @@ -2855,7 +2845,7 @@ def test_server_create_with_block_device_invalid_shutdown(self): self.assertIn( 'The delete_on_termination key of --block-device ', str(ex) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_tag_pre_v242(self): self.set_compute_api_version('2.41') @@ -2877,7 +2867,7 @@ def test_server_create_with_block_device_tag_pre_v242(self): self.assertIn( '--os-compute-api-version 2.42 or greater is required', str(ex) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_volume_type_pre_v267(self): self.set_compute_api_version('2.66') @@ -2899,7 +2889,7 @@ def test_server_create_with_block_device_volume_type_pre_v267(self): self.assertIn( '--os-compute-api-version 2.67 or greater is required', str(ex) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_mapping(self): self.volume_client.volumes.get.return_value = self.volume @@ -2938,7 +2928,7 @@ def test_server_create_with_block_device_mapping(self): self.volume_client.volumes.get.assert_called_once_with( self.volume.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3002,7 +2992,7 @@ def test_server_create_with_block_device_mapping_min_input(self): self.volume_client.volumes.get.assert_called_once_with( self.volume.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3065,7 +3055,7 @@ def test_server_create_with_block_device_mapping_default_input(self): self.volume_client.volumes.get.assert_called_once_with( self.volume.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3130,7 +3120,7 @@ def test_server_create_with_block_device_mapping_full_input(self): self.volume_client.volumes.get.assert_called_once_with( self.volume.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3198,7 +3188,7 @@ def test_server_create_with_block_device_mapping_snapshot(self): self.volume_client.volume_snapshots.get.assert_called_once_with( self.snapshot.name ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3273,7 +3263,7 @@ def test_server_create_with_block_device_mapping_multiple(self): self.volume_client.volumes.get.assert_has_calls( [mock.call(self.volume.name)] * 2 ) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3329,7 +3319,7 @@ def test_server_create_with_block_device_mapping_invalid_format(self): self.assertIn( 'argument --block-device-mapping: Invalid argument ', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() # block device mapping don't contain device name "=uuid:::true" arglist = [ @@ -3351,7 +3341,7 @@ def test_server_create_with_block_device_mapping_invalid_format(self): self.assertIn( 'argument --block-device-mapping: Invalid argument ', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_block_device_mapping_no_uuid(self): arglist = [ @@ -3373,7 +3363,7 @@ def test_server_create_with_block_device_mapping_no_uuid(self): self.assertIn( 'argument --block-device-mapping: Invalid argument ', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_volume_boot_from_volume_conflict(self): # Tests that specifying --volume and --boot-from-volume results in @@ -3406,7 +3396,7 @@ def test_server_create_volume_boot_from_volume_conflict(self): self.assertIn( '--volume is not allowed with --boot-from-volume', str(ex) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_boot_from_volume_no_image(self): # Test --boot-from-volume option without --image or @@ -3434,7 +3424,7 @@ def test_server_create_boot_from_volume_no_image(self): 'to support --boot-from-volume option', str(ex), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_image_property(self): image = image_fakes.create_one_image({'hypervisor_type': 'qemu'}) @@ -3458,7 +3448,7 @@ def test_server_create_image_property(self): columns, data = self.cmd.take_action(parsed_args) self.image_client.images.assert_called_once_with() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=image.id, flavor_id=self.flavor.id, @@ -3508,7 +3498,7 @@ def test_server_create_image_property_multi(self): columns, data = self.cmd.take_action(parsed_args) self.image_client.images.assert_called_once_with() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=image.id, flavor_id=self.flavor.id, @@ -3563,7 +3553,7 @@ def test_server_create_image_property_missed(self): 'No images match the property expected by --image-property', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_image_property_with_image_list(self): target_image = image_fakes.create_one_image( @@ -3596,7 +3586,7 @@ def test_server_create_image_property_with_image_list(self): columns, data = self.cmd.take_action(parsed_args) self.image_client.images.assert_called_once_with() - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=target_image.id, flavor_id=self.flavor.id, @@ -3652,7 +3642,7 @@ def test_server_create_no_boot_device(self): '(--volume, --snapshot, --block-device) is required', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_swap(self): arglist = [ @@ -3674,7 +3664,7 @@ def test_server_create_with_swap(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3723,7 +3713,7 @@ def test_server_create_with_ephemeral(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3770,7 +3760,7 @@ def test_server_create_with_ephemeral_missing_key(self): [], ) self.assertIn('Argument parse failed', str(exc)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_ephemeral_invalid_key(self): arglist = [ @@ -3790,7 +3780,7 @@ def test_server_create_with_ephemeral_invalid_key(self): [], ) self.assertIn('Argument parse failed', str(exc)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_invalid_hint(self): # Not a key-value pair @@ -3811,7 +3801,7 @@ def test_server_create_invalid_hint(self): [], ) self.assertIn('Argument parse failed', str(exc)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() # Empty key arglist = [ @@ -3831,7 +3821,7 @@ def test_server_create_invalid_hint(self): [], ) self.assertIn('Argument parse failed', str(exc)) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_description(self): # Description is supported for nova api version 2.19 or above @@ -3857,7 +3847,7 @@ def test_server_create_with_description(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3904,7 +3894,7 @@ def test_server_create_with_description_pre_v219(self): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_tag(self): self.set_compute_api_version('2.52') @@ -3931,7 +3921,7 @@ def test_server_create_with_tag(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -3982,7 +3972,7 @@ def test_server_create_with_tag_pre_v252(self): self.assertIn( '--os-compute-api-version 2.52 or greater is required', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_host(self): # Explicit host is supported for nova api version 2.74 or above @@ -4008,7 +3998,7 @@ def test_server_create_with_host(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -4057,7 +4047,7 @@ def test_server_create_with_host_pre_v274(self): self.assertIn( '--os-compute-api-version 2.74 or greater is required', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_hypervisor_hostname(self): # Explicit hypervisor_hostname is supported for nova api version @@ -4084,7 +4074,7 @@ def test_server_create_with_hypervisor_hostname(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -4133,7 +4123,7 @@ def test_server_create_with_hypervisor_hostname_pre_v274(self): self.assertIn( '--os-compute-api-version 2.74 or greater is required', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_hostname(self): self.set_compute_api_version('2.90') @@ -4158,7 +4148,7 @@ def test_server_create_with_hostname(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -4206,7 +4196,7 @@ def test_server_create_with_hostname_pre_v290(self): self.assertIn( '--os-compute-api-version 2.90 or greater is required', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert(self): self.set_compute_api_version('2.63') @@ -4233,7 +4223,7 @@ def test_server_create_with_trusted_image_cert(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server.assert_called_once_with( + self.compute_client.create_server.assert_called_once_with( name=self.server.name, image_id=self.image.id, flavor_id=self.flavor.id, @@ -4283,7 +4273,7 @@ def test_server_create_with_trusted_image_cert_pre_v263(self): self.assertIn( '--os-compute-api-version 2.63 or greater is required', str(exc) ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert_from_volume(self): self.set_compute_api_version('2.63') @@ -4316,7 +4306,7 @@ def test_server_create_with_trusted_image_cert_from_volume(self): 'directly from images', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert_from_snapshot(self): self.set_compute_api_version('2.63') @@ -4349,7 +4339,7 @@ def test_server_create_with_trusted_image_cert_from_snapshot(self): 'directly from images', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert_boot_from_volume(self): self.set_compute_api_version('2.63') @@ -4385,7 +4375,7 @@ def test_server_create_with_trusted_image_cert_boot_from_volume(self): 'directly from images', str(exc), ) - self.compute_sdk_client.create_server.assert_not_called() + self.compute_client.create_server.assert_not_called() class TestServerDelete(compute_fakes.TestComputev2): @@ -4393,8 +4383,8 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.delete_server.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.delete_server.return_value = None # Get the command object to test self.cmd = server.DeleteServer(self.app, None) @@ -4410,10 +4400,10 @@ def test_server_delete_no_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, all_projects=False ) - self.compute_sdk_client.delete_server.assert_called_once_with( + self.compute_client.delete_server.assert_called_once_with( self.server, force=False ) self.assertIsNone(result) @@ -4430,18 +4420,18 @@ def test_server_delete_with_force(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, all_projects=False ) - self.compute_sdk_client.delete_server.assert_called_once_with( + self.compute_client.delete_server.assert_called_once_with( self.server, force=True ) self.assertIsNone(result) def test_server_delete_multi_servers(self): servers = compute_fakes.create_sdk_servers(count=3) - self.compute_sdk_client.find_server.return_value = None - self.compute_sdk_client.find_server.side_effect = servers + self.compute_client.find_server.return_value = None + self.compute_client.find_server.side_effect = servers arglist = [] verifylist = [] @@ -4454,13 +4444,13 @@ def test_server_delete_multi_servers(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_has_calls( + self.compute_client.find_server.assert_has_calls( [ mock.call(s.id, ignore_missing=False, all_projects=False) for s in servers ] ) - self.compute_sdk_client.delete_server.assert_has_calls( + self.compute_client.delete_server.assert_has_calls( [mock.call(s, force=False) for s in servers] ) self.assertIsNone(result) @@ -4478,10 +4468,10 @@ def test_server_delete_with_all_projects(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, all_projects=True ) - self.compute_sdk_client.delete_server.assert_called_once_with( + self.compute_client.delete_server.assert_called_once_with( self.server, force=False ) self.assertIsNone(result) @@ -4499,20 +4489,20 @@ def test_server_delete_wait_ok(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, all_projects=False ) - self.compute_sdk_client.delete_server.assert_called_once_with( + self.compute_client.delete_server.assert_called_once_with( self.server, force=False ) - self.compute_sdk_client.wait_for_delete.assert_called_once_with( + self.compute_client.wait_for_delete.assert_called_once_with( self.server, callback=mock.ANY, ) self.assertIsNone(result) def test_server_delete_wait_fails(self): - self.compute_sdk_client.wait_for_delete.side_effect = ( + self.compute_client.wait_for_delete.side_effect = ( sdk_exceptions.ResourceTimeout() ) @@ -4530,13 +4520,13 @@ def test_server_delete_wait_fails(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, all_projects=False ) - self.compute_sdk_client.delete_server.assert_called_once_with( + self.compute_client.delete_server.assert_called_once_with( self.server, force=False ) - self.compute_sdk_client.wait_for_delete.assert_called_once_with( + self.compute_client.wait_for_delete.assert_called_once_with( self.server, callback=mock.ANY, ) @@ -4567,9 +4557,7 @@ def run_test_server_dump(self, server_count): self.assertIsNone(result) for s in servers: - s.trigger_crash_dump.assert_called_once_with( - self.compute_sdk_client - ) + s.trigger_crash_dump.assert_called_once_with(self.compute_client) def test_server_dump_one_server(self): self.run_test_server_dump(1) @@ -4645,12 +4633,12 @@ def setUp(self): self.image_client.get_image.return_value = self.image self.flavor = compute_fakes.create_one_flavor() - self.compute_sdk_client.find_flavor.return_value = self.flavor + self.compute_client.find_flavor.return_value = self.flavor self.attrs['flavor'] = {'original_name': self.flavor.name} # The servers to be listed. self.servers = self.setup_sdk_servers_mock(3) - self.compute_sdk_client.servers.return_value = self.servers + self.compute_client.servers.return_value = self.servers # Get the command object to test self.cmd = server.ListServer(self.app, None) @@ -4669,7 +4657,7 @@ def setUp(self): ] Flavor = collections.namedtuple('Flavor', 'id name') - self.compute_sdk_client.flavors.return_value = [ + self.compute_client.flavors.return_value = [ Flavor(id=s.flavor['id'], name=self.flavor.name) for s in self.servers ] @@ -4699,9 +4687,9 @@ def test_server_list_no_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_called() - self.compute_sdk_client.flavors.assert_called() + self.compute_client.flavors.assert_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4713,14 +4701,14 @@ def test_server_list_no_servers(self): ('deleted', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.servers.return_value = [] + self.compute_client.servers.return_value = [] self.data = () columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.compute_sdk_client.flavors.assert_not_called() + self.compute_client.flavors.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4755,12 +4743,12 @@ def test_server_list_long_option(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) image_ids = {s.image['id'] for s in self.servers if s.image} self.image_client.images.assert_called_once_with( id=f'in:{",".join(image_ids)}', ) - self.compute_sdk_client.flavors.assert_called_once_with(is_public=None) + self.compute_client.flavors.assert_called_once_with(is_public=None) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data, tuple(data)) @@ -4799,7 +4787,7 @@ def test_server_list_column_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertIn('Project ID', columns) self.assertIn('User ID', columns) self.assertIn('Created At', columns) @@ -4839,9 +4827,9 @@ def test_server_list_no_name_lookup_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.compute_sdk_client.flavors.assert_not_called() + self.compute_client.flavors.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4870,9 +4858,9 @@ def test_server_list_n_option(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.compute_sdk_client.flavors.assert_not_called() + self.compute_client.flavors.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4887,11 +4875,11 @@ def test_server_list_name_lookup_one_by_one(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.compute_sdk_client.flavors.assert_not_called() + self.compute_client.flavors.assert_not_called() self.image_client.get_image.assert_called() - self.compute_sdk_client.find_flavor.assert_called() + self.compute_client.find_flavor.assert_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4908,9 +4896,9 @@ def test_server_list_with_image(self): ) self.kwargs['image'] = self.image.id - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.compute_sdk_client.flavors.assert_called_once() + self.compute_client.flavors.assert_called_once() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4922,14 +4910,14 @@ def test_server_list_with_flavor(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_flavor.assert_has_calls( + self.compute_client.find_flavor.assert_has_calls( [mock.call(self.flavor.id, ignore_missing=False)] ) self.kwargs['flavor'] = self.flavor.id - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_called_once() - self.compute_sdk_client.flavors.assert_not_called() + self.compute_client.flavors.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4946,7 +4934,7 @@ def test_server_list_with_changes_since(self): self.kwargs['changes-since'] = '2016-03-04T06:27:59Z' self.kwargs['deleted'] = True - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4989,7 +4977,7 @@ def test_server_list_with_tag(self): self.kwargs['tags'] = 'tag1,tag2' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -5032,7 +5020,7 @@ def test_server_list_with_not_tag(self): self.kwargs['not-tags'] = 'tag1,tag2' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -5071,7 +5059,7 @@ def test_server_list_with_availability_zone(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['availability_zone'] = 'test-az' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5088,7 +5076,7 @@ def test_server_list_with_key_name(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['key_name'] = 'test-key' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5104,7 +5092,7 @@ def test_server_list_with_config_drive(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['config_drive'] = True - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5120,7 +5108,7 @@ def test_server_list_with_no_config_drive(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['config_drive'] = False - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5137,7 +5125,7 @@ def test_server_list_with_progress(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['progress'] = '100' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5168,7 +5156,7 @@ def test_server_list_with_vm_state(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['vm_state'] = 'active' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5185,7 +5173,7 @@ def test_server_list_with_task_state(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['task_state'] = 'deleting' - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5202,7 +5190,7 @@ def test_server_list_with_power_state(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['power_state'] = 1 - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5239,18 +5227,18 @@ def test_server_list_long_with_host_status_v216(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns_long, columns) self.assertEqual(tuple(self.data1), tuple(data)) # Next test with host_status in the data -- the column should be # present in this case. - self.compute_sdk_client.servers.reset_mock() + self.compute_client.servers.reset_mock() self.attrs['host_status'] = 'UP' servers = self.setup_sdk_servers_mock(3) - self.compute_sdk_client.servers.return_value = servers + self.compute_client.servers.return_value = servers # Make sure the returned image and flavor IDs match the servers. Image = collections.namedtuple('Image', 'id name') @@ -5287,7 +5275,7 @@ def test_server_list_long_with_host_status_v216(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertEqual(columns_long, columns) self.assertEqual(tuple(self.data2), tuple(data)) @@ -5336,7 +5324,7 @@ def setUp(self): # The servers to be listed. self.servers = self.setup_sdk_servers_mock(3) - self.compute_sdk_client.servers.return_value = self.servers + self.compute_client.servers.return_value = self.servers Image = collections.namedtuple('Image', 'id name') self.image_client.images.return_value = [ @@ -5348,7 +5336,7 @@ def setUp(self): # The flavor information is embedded, so now reason for this to be # called - self.compute_sdk_client.flavors = mock.NonCallableMock() + self.compute_client.flavors = mock.NonCallableMock() self.data = tuple( ( @@ -5384,7 +5372,7 @@ def test_server_list_with_locked(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['locked'] = True - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, tuple(data)) @@ -5399,7 +5387,7 @@ def test_server_list_with_unlocked_v273(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['locked'] = False - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, tuple(data)) @@ -5432,7 +5420,7 @@ def test_server_list_with_changes_before(self): self.kwargs['changes-before'] = '2016-03-05T06:27:59Z' self.kwargs['deleted'] = True - self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_client.servers.assert_called_with(**self.kwargs) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, tuple(data)) @@ -5516,7 +5504,7 @@ def test_server_list_v269_with_partial_constructs(self): class TestServerAction(compute_fakes.TestComputev2): def run_method_with_sdk_servers(self, method_name, server_count): servers = compute_fakes.create_sdk_servers(count=server_count) - self.compute_sdk_client.find_server.side_effect = servers + self.compute_client.find_server.side_effect = servers arglist = [s.id for s in servers] verifylist = [ @@ -5527,7 +5515,7 @@ def run_method_with_sdk_servers(self, method_name, server_count): result = self.cmd.take_action(parsed_args) calls = [mock.call(s.id) for s in servers] - method = getattr(self.compute_sdk_client, method_name) + method = getattr(self.compute_client, method_name) method.assert_has_calls(calls) self.assertIsNone(result) @@ -5549,8 +5537,8 @@ def test_server_lock_with_reason(self): self.set_compute_api_version('2.73') self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.lock_server.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.lock_server.return_value = None arglist = [ self.server.id, @@ -5565,11 +5553,11 @@ def test_server_lock_with_reason(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False, ) - self.compute_sdk_client.lock_server.assert_called_with( + self.compute_client.lock_server.assert_called_with( self.server.id, locked_reason="blah", ) @@ -5580,8 +5568,8 @@ def test_server_lock_with_reason_multi_servers(self): server_a = compute_fakes.create_one_sdk_server() server_b = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.side_effect = [server_a, server_b] - self.compute_sdk_client.lock_server.return_value = None + self.compute_client.find_server.side_effect = [server_a, server_b] + self.compute_client.lock_server.return_value = None arglist = [ server_a.id, server_b.id, @@ -5596,8 +5584,8 @@ def test_server_lock_with_reason_multi_servers(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.assertEqual(2, self.compute_sdk_client.find_server.call_count) - self.compute_sdk_client.lock_server.assert_has_calls( + self.assertEqual(2, self.compute_client.find_server.call_count) + self.compute_client.lock_server.assert_has_calls( [ mock.call(server_a.id, locked_reason="choo..choo"), mock.call(server_b.id, locked_reason="choo..choo"), @@ -5636,9 +5624,9 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.migrate_server.return_value = None - self.compute_sdk_client.live_migrate_server.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.migrate_server.return_value = None + self.compute_client.live_migrate_server.return_value = None # Get the command object to test self.cmd = server.MigrateServer(self.app, None) @@ -5657,13 +5645,13 @@ def test_server_migrate_no_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_called_once_with( + self.compute_client.migrate_server.assert_called_once_with( self.server, ) - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() self.assertIsNone(result) def test_server_migrate_with_host(self): @@ -5687,13 +5675,13 @@ def test_server_migrate_with_host(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_called_once_with( + self.compute_client.migrate_server.assert_called_once_with( self.server, host='fakehost' ) - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() self.assertIsNone(result) def test_server_migrate_with_block_migration(self): @@ -5713,11 +5701,11 @@ def test_server_migrate_with_block_migration(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_not_called() - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() def test_server_migrate_with_disk_overcommit(self): arglist = [ @@ -5736,11 +5724,11 @@ def test_server_migrate_with_disk_overcommit(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_not_called() - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() def test_server_migrate_with_host_pre_v256(self): # Tests that --host is not allowed for a cold migration @@ -5772,11 +5760,11 @@ def test_server_migrate_with_host_pre_v256(self): str(ex), ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_not_called() - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() def test_server_live_migrate(self): # Tests the --live-migration option without --host or --live. @@ -5795,16 +5783,16 @@ def test_server_live_migrate(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.live_migrate_server.assert_called_once_with( + self.compute_client.live_migrate_server.assert_called_once_with( self.server, block_migration=False, host=None, disk_overcommit=False, ) - self.compute_sdk_client.migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() self.assertIsNone(result) def test_server_live_migrate_with_host(self): @@ -5828,17 +5816,17 @@ def test_server_live_migrate_with_host(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) # No disk_overcommit and block_migration defaults to auto with # microversion >= 2.25 - self.compute_sdk_client.live_migrate_server.assert_called_once_with( + self.compute_client.live_migrate_server.assert_called_once_with( self.server, block_migration='auto', host='fakehost', ) - self.compute_sdk_client.migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() self.assertIsNone(result) def test_server_live_migrate_with_host_pre_v230(self): @@ -5871,11 +5859,11 @@ def test_server_live_migrate_with_host_pre_v230(self): str(ex), ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_not_called() - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() def test_server_block_live_migrate(self): self.set_compute_api_version('2.24') @@ -5895,18 +5883,18 @@ def test_server_block_live_migrate(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) # No disk_overcommit and block_migration defaults to auto with # microversion >= 2.25 - self.compute_sdk_client.live_migrate_server.assert_called_once_with( + self.compute_client.live_migrate_server.assert_called_once_with( self.server, block_migration=True, disk_overcommit=False, host=None, ) - self.compute_sdk_client.migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() self.assertIsNone(result) def test_server_live_migrate_with_disk_overcommit(self): @@ -5927,16 +5915,16 @@ def test_server_live_migrate_with_disk_overcommit(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.live_migrate_server.assert_called_once_with( + self.compute_client.live_migrate_server.assert_called_once_with( self.server, block_migration=False, disk_overcommit=True, host=None, ) - self.compute_sdk_client.migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() self.assertIsNone(result) def test_server_live_migrate_with_disk_overcommit_post_v224(self): @@ -5958,16 +5946,16 @@ def test_server_live_migrate_with_disk_overcommit_post_v224(self): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) # There should be no 'disk_over_commit' value present - self.compute_sdk_client.live_migrate_server.assert_called_once_with( + self.compute_client.live_migrate_server.assert_called_once_with( self.server, block_migration='auto', host=None, ) - self.compute_sdk_client.migrate_server.assert_not_called() + self.compute_client.migrate_server.assert_not_called() self.assertIsNone(result) # A warning should have been logged for using --disk-overcommit. @@ -5993,15 +5981,15 @@ def test_server_migrate_with_wait(self, mock_wait_for_status): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_called_once_with( + self.compute_client.migrate_server.assert_called_once_with( self.server, ) - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, success_status=('active', 'verify_resize'), callback=mock.ANY, @@ -6026,15 +6014,15 @@ def test_server_migrate_with_wait_fails(self, mock_wait_for_status): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.migrate_server.assert_called_once_with( + self.compute_client.migrate_server.assert_called_once_with( self.server, ) - self.compute_sdk_client.live_migrate_server.assert_not_called() + self.compute_client.live_migrate_server.assert_not_called() mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, success_status=('active', 'verify_resize'), callback=mock.ANY, @@ -6045,7 +6033,7 @@ class TestServerReboot(TestServer): def setUp(self): super().setUp() - self.compute_sdk_client.reboot_server.return_value = None + self.compute_client.reboot_server.return_value = None self.cmd = server.RebootServer(self.app, None) @@ -6064,7 +6052,7 @@ def test_server_reboot(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.reboot_server.assert_called_once_with( + self.compute_client.reboot_server.assert_called_once_with( servers[0].id, 'SOFT', ) @@ -6086,7 +6074,7 @@ def test_server_reboot_with_hard(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.reboot_server.assert_called_once_with( + self.compute_client.reboot_server.assert_called_once_with( servers[0].id, 'HARD', ) @@ -6110,12 +6098,12 @@ def test_server_reboot_with_wait(self, mock_wait_for_status): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.reboot_server.assert_called_once_with( + self.compute_client.reboot_server.assert_called_once_with( servers[0].id, 'SOFT', ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, servers[0].id, callback=mock.ANY, ) @@ -6144,12 +6132,12 @@ def test_server_reboot_with_wait_fails( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.reboot_server.assert_called_once_with( + self.compute_client.reboot_server.assert_called_once_with( servers[0].id, 'SOFT', ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, servers[0].id, callback=mock.ANY, ) @@ -6181,8 +6169,8 @@ def setUp(self): 'image': {'id': self.image.id}, } self.server = compute_fakes.create_one_sdk_server(attrs=attrs) - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.rebuild_server.return_value = self.server + self.compute_client.find_server.return_value = self.server + self.compute_client.rebuild_server.return_value = self.server self.cmd = server.RebuildServer(self.app, None) @@ -6204,14 +6192,14 @@ def test_rebuild_with_image_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_called_with( image_name, ignore_missing=False ) self.image_client.get_image.assert_called_with(self.image.id) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, image ) @@ -6225,14 +6213,14 @@ def test_rebuild_with_current_image(self): # Get the command object to test. self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image ) @@ -6269,14 +6257,14 @@ def test_rebuild_with_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, name=name ) @@ -6293,14 +6281,14 @@ def test_rebuild_with_preserve_ephemeral(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, preserve_ephemeral=True ) @@ -6318,14 +6306,14 @@ def test_rebuild_with_no_preserve_ephemeral(self): # Get the command object to test self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, preserve_ephemeral=False ) @@ -6337,14 +6325,14 @@ def test_rebuild_with_password(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, admin_password=password, @@ -6360,14 +6348,14 @@ def test_rebuild_with_description(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, description=description ) @@ -6397,19 +6385,19 @@ def test_rebuild_with_wait_ok(self, mock_wait_for_status): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=['active'], @@ -6431,17 +6419,17 @@ def test_rebuild_with_wait_fails(self, mock_wait_for_status): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_called_once_with(self.image.id) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=['active'], @@ -6462,19 +6450,19 @@ def test_rebuild_with_wait_shutoff_status(self, mock_wait_for_status): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=['shutoff'], @@ -6495,19 +6483,19 @@ def test_rebuild_with_wait_error_status(self, mock_wait_for_status): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image ) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=['active'], @@ -6527,12 +6515,12 @@ def test_rebuild_wrong_status_fails(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_called_once_with(self.image.id) - self.compute_sdk_client.rebuild_server.assert_not_called() + self.compute_client.rebuild_server.assert_not_called() def test_rebuild_with_property(self): arglist = [ @@ -6551,14 +6539,14 @@ def test_rebuild_with_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, metadata=expected_properties ) @@ -6579,14 +6567,14 @@ def test_rebuild_with_keypair_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, key_name=self.server.key_name ) @@ -6624,14 +6612,14 @@ def test_rebuild_with_no_keypair_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, key_name=None ) @@ -6679,14 +6667,14 @@ def test_rebuild_with_user_data(self): # Ensure the userdata file is opened mock_file.assert_called_with('userdata.sh', 'rb') - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, user_data=base64.b64encode(user_data).decode('utf-8'), @@ -6726,14 +6714,14 @@ def test_rebuild_with_no_user_data(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, user_data=None ) @@ -6787,14 +6775,14 @@ def test_rebuild_with_trusted_image_cert(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, trusted_image_certificates=['foo', 'bar'] ) @@ -6833,14 +6821,14 @@ def test_rebuild_with_no_trusted_image_cert(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, trusted_image_certificates=None ) @@ -6877,14 +6865,14 @@ def test_rebuild_with_hostname(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_not_called() self.image_client.get_image.assert_has_calls( [mock.call(self.image.id), mock.call(self.image.id)] ) - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.image, hostname='new-hostname' ) @@ -6916,8 +6904,8 @@ def setUp(self): 'image': '', } self.server = compute_fakes.create_one_sdk_server(attrs=attrs) - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.rebuild_server.return_value = self.server + self.compute_client.find_server.return_value = self.server + self.compute_client.rebuild_server.return_value = self.server self.cmd = server.RebuildServer(self.app, None) @@ -6939,14 +6927,14 @@ def test_rebuild_with_reimage_boot_volume(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.image_client.find_image.assert_called_with( self.new_image.id, ignore_missing=False ) self.image_client.get_image.assert_not_called() - self.compute_sdk_client.rebuild_server.assert_called_once_with( + self.compute_client.rebuild_server.assert_called_once_with( self.server, self.new_image ) @@ -7011,8 +6999,8 @@ def setUp(self): self.new_server = compute_fakes.create_one_sdk_server(attrs=attrs) # Return value for utils.find_resource for server. - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.get_server.return_value = self.server + self.compute_client.find_server.return_value = self.server + self.compute_client.get_server.return_value = self.server self.cmd = server.EvacuateServer(self.app, None) @@ -7020,15 +7008,13 @@ def _test_evacuate(self, args, verify_args, evac_args): parsed_args = self.check_parser(self.cmd, args, verify_args) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.evacuate_server.assert_called_once_with( + self.compute_client.evacuate_server.assert_called_once_with( self.server, **evac_args ) - self.compute_sdk_client.get_server.assert_called_once_with( - self.server.id - ) + self.compute_client.get_server.assert_called_once_with(self.server.id) def test_evacuate(self): args = [ @@ -7142,7 +7128,7 @@ def test_evacuate_with_wait_ok(self, mock_wait_for_status): } self._test_evacuate(args, verify_args, evac_args) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, ) @@ -7170,12 +7156,10 @@ def test_server_remove_fixed_ip(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.remove_fixed_ip_from_server( - self.server, '1.2.3.4' - ) + self.compute_client.remove_fixed_ip_from_server(self.server, '1.2.3.4') self.assertIsNone(result) @@ -7184,7 +7168,7 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server self.cmd = server.RescueServer(self.app, None) @@ -7199,10 +7183,10 @@ def test_rescue(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.rescue_server.assert_called_once_with( + self.compute_client.rescue_server.assert_called_once_with( self.server, admin_pass=None, image_ref=None ) self.assertIsNone(result) @@ -7226,10 +7210,10 @@ def test_rescue_with_image(self): self.image_client.find_image.assert_called_with( new_image.id, ignore_missing=False ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.rescue_server.assert_called_once_with( + self.compute_client.rescue_server.assert_called_once_with( self.server, admin_pass=None, image_ref=new_image.id ) self.assertIsNone(result) @@ -7249,10 +7233,10 @@ def test_rescue_with_password(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.rescue_server.assert_called_once_with( + self.compute_client.rescue_server.assert_called_once_with( self.server, admin_pass=password, image_ref=None ) self.assertIsNone(result) @@ -7264,7 +7248,7 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server self.cmd = server.RemoveFloatingIP(self.app, None) @@ -7281,10 +7265,10 @@ def test_server_remove_floating_ip(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False ) - self.compute_sdk_client.remove_floating_ip_from_server.assert_called_once_with( + self.compute_client.remove_floating_ip_from_server.assert_called_once_with( self.server, '1.2.3.4' ) @@ -7352,7 +7336,7 @@ def _test_server_remove_port(self, port_id): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.delete_server_interface.assert_called_with( + self.compute_client.delete_server_interface.assert_called_with( port_id, server=servers[0], ignore_missing=False ) self.assertIsNone(result) @@ -7381,9 +7365,7 @@ def setUp(self): self.find_network = mock.Mock() self.app.client_manager.network.find_network = self.find_network - self.compute_sdk_client.server_interfaces.return_value = [ - self.fake_inf - ] + self.compute_client.server_interfaces.return_value = [self.fake_inf] def _test_server_remove_network(self, network_id): self.fake_inf.net_id = network_id @@ -7403,10 +7385,10 @@ def _test_server_remove_network(self, network_id): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_interfaces.assert_called_once_with( + self.compute_client.server_interfaces.assert_called_once_with( servers[0] ) - self.compute_sdk_client.delete_server_interface.assert_called_once_with( + self.compute_client.delete_server_interface.assert_called_once_with( 'fake-port', server=servers[0] ) self.assertIsNone(result) @@ -7428,8 +7410,10 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.remove_security_group_from_server.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.remove_security_group_from_server.return_value = ( + None + ) # Get the command object to test self.cmd = server.RemoveServerSecurityGroup(self.app, None) @@ -7454,14 +7438,14 @@ def test_server_remove_security_group__nova_network(self): ) as mock_find_nova_net_sg: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.remove_security_group_from_server.assert_called_once_with( + self.compute_client.remove_security_group_from_server.assert_called_once_with( self.server, {'name': 'fake_sg'} ) mock_find_nova_net_sg.assert_called_once_with( - self.compute_sdk_client, 'fake_sg' + self.compute_client, 'fake_sg' ) self.assertIsNone(result) @@ -7475,10 +7459,10 @@ def test_server_remove_security_group(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.remove_security_group_from_server.assert_called_once_with( + self.compute_client.remove_security_group_from_server.assert_called_once_with( self.server, {'name': 'fake_sg'} ) self.assertIsNone(result) @@ -7489,12 +7473,12 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server self.flavor = compute_fakes.create_one_flavor() - self.compute_sdk_client.find_flavor.return_value = self.flavor - self.compute_sdk_client.resize_server.return_value = None - self.compute_sdk_client.revert_server_resize.return_value = None - self.compute_sdk_client.confirm_server_resize.return_value = None + self.compute_client.find_flavor.return_value = self.flavor + self.compute_client.resize_server.return_value = None + self.compute_client.revert_server_resize.return_value = None + self.compute_client.confirm_server_resize.return_value = None # Get the command object to test self.cmd = server.ResizeServer(self.app, None) @@ -7512,11 +7496,11 @@ def test_server_resize_no_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.find_flavor.assert_not_called() - self.compute_sdk_client.resize_server.assert_not_called() + self.compute_client.find_flavor.assert_not_called() + self.compute_client.resize_server.assert_not_called() self.assertIsNone(result) def test_server_resize(self): @@ -7535,17 +7519,17 @@ def test_server_resize(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.find_flavor.assert_called_once_with( + self.compute_client.find_flavor.assert_called_once_with( self.flavor.id, ignore_missing=False ) - self.compute_sdk_client.resize_server.assert_called_once_with( + self.compute_client.resize_server.assert_called_once_with( self.server, self.flavor ) - self.compute_sdk_client.confirm_server_resize.assert_not_called() - self.compute_sdk_client.revert_server_resize.assert_not_called() + self.compute_client.confirm_server_resize.assert_not_called() + self.compute_client.revert_server_resize.assert_not_called() self.assertIsNone(result) def test_server_resize_confirm(self): @@ -7563,15 +7547,15 @@ def test_server_resize_confirm(self): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.find_flavor.assert_not_called() - self.compute_sdk_client.resize_server.assert_not_called() - self.compute_sdk_client.confirm_server_resize.assert_called_once_with( + self.compute_client.find_flavor.assert_not_called() + self.compute_client.resize_server.assert_not_called() + self.compute_client.confirm_server_resize.assert_called_once_with( self.server ) - self.compute_sdk_client.revert_server_resize.assert_not_called() + self.compute_client.revert_server_resize.assert_not_called() self.assertIsNone(result) # A warning should have been logged for using --confirm. @@ -7596,13 +7580,13 @@ def test_server_resize_revert(self): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.find_flavor.assert_not_called() - self.compute_sdk_client.resize_server.assert_not_called() - self.compute_sdk_client.confirm_server_resize.assert_not_called() - self.compute_sdk_client.revert_server_resize.assert_called_once_with( + self.compute_client.find_flavor.assert_not_called() + self.compute_client.resize_server.assert_not_called() + self.compute_client.confirm_server_resize.assert_not_called() + self.compute_client.revert_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -7632,20 +7616,20 @@ def test_server_resize_with_wait_ok(self, mock_wait_for_status): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.find_flavor.assert_called_once_with( + self.compute_client.find_flavor.assert_called_once_with( self.flavor.id, ignore_missing=False ) - self.compute_sdk_client.resize_server.assert_called_once_with( + self.compute_client.resize_server.assert_called_once_with( self.server, self.flavor ) - self.compute_sdk_client.confirm_server_resize.assert_not_called() - self.compute_sdk_client.revert_server_resize.assert_not_called() + self.compute_client.confirm_server_resize.assert_not_called() + self.compute_client.revert_server_resize.assert_not_called() mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, success_status=('active', 'verify_resize'), callback=mock.ANY, @@ -7672,20 +7656,20 @@ def test_server_resize_with_wait_fails(self, mock_wait_for_status): exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.find_flavor.assert_called_once_with( + self.compute_client.find_flavor.assert_called_once_with( self.flavor.id, ignore_missing=False ) - self.compute_sdk_client.resize_server.assert_called_once_with( + self.compute_client.resize_server.assert_called_once_with( self.server, self.flavor ) - self.compute_sdk_client.confirm_server_resize.assert_not_called() - self.compute_sdk_client.revert_server_resize.assert_not_called() + self.compute_client.confirm_server_resize.assert_not_called() + self.compute_client.revert_server_resize.assert_not_called() mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, success_status=('active', 'verify_resize'), callback=mock.ANY, @@ -7697,8 +7681,8 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.confirm_server_resize.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.confirm_server_resize.return_value = None # Get the command object to test self.cmd = server.ResizeConfirm(self.app, None) @@ -7714,10 +7698,10 @@ def test_resize_confirm(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.confirm_server_resize.assert_called_once_with( + self.compute_client.confirm_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -7729,8 +7713,8 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.confirm_server_resize.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.confirm_server_resize.return_value = None # Get the command object to test self.cmd = server.MigrateConfirm(self.app, None) @@ -7747,10 +7731,10 @@ def test_migrate_confirm(self): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.confirm_server_resize.assert_called_once_with( + self.compute_client.confirm_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -7767,8 +7751,8 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.confirm_server_resize.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.confirm_server_resize.return_value = None # Get the command object to test self.cmd = server.ConfirmMigration(self.app, None) @@ -7784,10 +7768,10 @@ def test_migration_confirm(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.confirm_server_resize.assert_called_once_with( + self.compute_client.confirm_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -7798,8 +7782,8 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.revert_server_resize.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.revert_server_resize.return_value = None # Get the command object to test self.cmd = server.ResizeRevert(self.app, None) @@ -7815,10 +7799,10 @@ def test_resize_revert(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.revert_server_resize.assert_called_once_with( + self.compute_client.revert_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -7830,8 +7814,8 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.revert_server_resize.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.revert_server_resize.return_value = None # Get the command object to test self.cmd = server.MigrateRevert(self.app, None) @@ -7848,10 +7832,10 @@ def test_migrate_revert(self): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.revert_server_resize.assert_called_once_with( + self.compute_client.revert_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -7868,8 +7852,8 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.revert_server_resize.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.revert_server_resize.return_value = None # Get the command object to test self.cmd = server.RevertMigration(self.app, None) @@ -7885,10 +7869,10 @@ def test_migration_revert(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.revert_server_resize.assert_called_once_with( + self.compute_client.revert_server_resize.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -7927,7 +7911,7 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server # Get the command object to test self.cmd = server.SetServer(self.app, None) @@ -7939,12 +7923,12 @@ def test_server_set_no_option(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_state(self): @@ -7961,14 +7945,14 @@ def test_server_set_with_state(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.reset_server_state.assert_called_once_with( + self.compute_client.reset_server_state.assert_called_once_with( self.server, state='active' ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_invalid_state(self): @@ -8003,14 +7987,14 @@ def test_server_set_with_name(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_server.assert_called_once_with( + self.compute_client.update_server.assert_called_once_with( self.server, name='foo_name' ) - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_property(self): @@ -8029,14 +8013,14 @@ def test_server_set_with_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.set_server_metadata.assert_called_once_with( + self.compute_client.set_server_metadata.assert_called_once_with( self.server, key1='value1', key2='value2' ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_password(self): @@ -8053,14 +8037,14 @@ def test_server_set_with_password(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.change_server_password.assert_called_once_with( + self.compute_client.change_server_password.assert_called_once_with( self.server, 'foo' ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_no_password(self): @@ -8076,14 +8060,14 @@ def test_server_set_with_no_password(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.clear_server_password.assert_called_once_with( + self.compute_client.clear_server_password.assert_called_once_with( self.server ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) # TODO(stephenfin): Remove this in a future major version @@ -8103,14 +8087,14 @@ def test_server_set_with_root_password(self, mock_getpass): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.change_server_password.assert_called_once_with( + self.compute_client.change_server_password.assert_called_once_with( self.server, mock.sentinel.fake_pass ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_description(self): @@ -8129,14 +8113,14 @@ def test_server_set_with_description(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_server.assert_called_once_with( + self.compute_client.update_server.assert_called_once_with( self.server, description='foo_description' ) - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_description_pre_v219(self): @@ -8175,17 +8159,17 @@ def test_server_set_with_tag(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.add_tag_to_server.assert_has_calls( + self.compute_client.add_tag_to_server.assert_has_calls( [ mock.call(self.server, tag='tag1'), mock.call(self.server, tag='tag2'), ] ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() self.assertIsNone(result) def test_server_set_with_tag_pre_v226(self): @@ -8227,14 +8211,14 @@ def test_server_set_with_hostname(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_server.assert_called_once_with( + self.compute_client.update_server.assert_called_once_with( self.server, hostname='foo-hostname' ) - self.compute_sdk_client.set_server_metadata.assert_not_called() - self.compute_sdk_client.reset_server_state.assert_not_called() - self.compute_sdk_client.change_server_password.assert_not_called() - self.compute_sdk_client.clear_server_password.assert_not_called() - self.compute_sdk_client.add_tag_to_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) def test_server_set_with_hostname_pre_v290(self): @@ -8263,8 +8247,8 @@ def setUp(self): attrs={'status': 'ACTIVE'}, ) - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.shelve_server.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.shelve_server.return_value = None # Get the command object to test self.cmd = server.ShelveServer(self.app, None) @@ -8281,14 +8265,12 @@ def test_shelve(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.name, ignore_missing=False, ) - self.compute_sdk_client.shelve_server.assert_called_with( - self.server.id - ) - self.compute_sdk_client.shelve_offload_server.assert_not_called() + self.compute_client.shelve_server.assert_called_with(self.server.id) + self.compute_client.shelve_offload_server.assert_not_called() def test_shelve_already_shelved(self): self.server.status = 'SHELVED' @@ -8304,12 +8286,12 @@ def test_shelve_already_shelved(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.name, ignore_missing=False, ) - self.compute_sdk_client.shelve_server.assert_not_called() - self.compute_sdk_client.shelve_offload_server.assert_not_called() + self.compute_client.shelve_server.assert_not_called() + self.compute_client.shelve_offload_server.assert_not_called() @mock.patch.object(common_utils, 'wait_for_status', return_value=True) def test_shelve_with_wait(self, mock_wait_for_status): @@ -8324,16 +8306,14 @@ def test_shelve_with_wait(self, mock_wait_for_status): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.name, ignore_missing=False, ) - self.compute_sdk_client.shelve_server.assert_called_with( - self.server.id - ) - self.compute_sdk_client.shelve_offload_server.assert_not_called() + self.compute_client.shelve_server.assert_called_with(self.server.id) + self.compute_client.shelve_offload_server.assert_not_called() mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=('shelved', 'shelved_offloaded'), @@ -8353,25 +8333,21 @@ def test_shelve_offload(self, mock_wait_for_status): self.assertIsNone(result) # one call to retrieve to retrieve the server state before shelving - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False, ) # one call to retrieve the server state before offloading - self.compute_sdk_client.get_server.assert_called_once_with( - self.server.id - ) + self.compute_client.get_server.assert_called_once_with(self.server.id) # one call to shelve the server - self.compute_sdk_client.shelve_server.assert_called_with( - self.server.id - ) + self.compute_client.shelve_server.assert_called_with(self.server.id) # one call to shelve offload the server - self.compute_sdk_client.shelve_offload_server.assert_called_once_with( + self.compute_client.shelve_offload_server.assert_called_once_with( self.server.id, ) # one call to wait for the shelve offload to complete mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=('shelved', 'shelved_offloaded'), @@ -8386,7 +8362,7 @@ def setUp(self): self.image_client.get_image.return_value = self.image self.flavor = compute_fakes.create_one_flavor() - self.compute_sdk_client.find_flavor.return_value = self.flavor + self.compute_client.find_flavor.return_value = self.flavor self.topology = { 'nodes': [{'vcpu_set': [0, 1]}, {'vcpu_set': [2, 3]}], @@ -8398,14 +8374,14 @@ def setUp(self): 'tenant_id': 'tenant-id-xxx', 'addresses': {'public': ['10.20.30.40', '2001:db8::f']}, } - self.compute_sdk_client.get_server_diagnostics.return_value = { + self.compute_client.get_server_diagnostics.return_value = { 'test': 'test' } self.server = compute_fakes.create_one_sdk_server( attrs=server_info, ) self.server.fetch_topology = mock.MagicMock(return_value=self.topology) - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server # Get the command object to test self.cmd = server.ShowServer(self.app, None) @@ -8534,10 +8510,10 @@ def test_show(self): self.assertTupleEqual(self.columns, columns) self.assertTupleEqual(self.data, data) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False, details=True ) - self.compute_sdk_client.get_server.assert_not_called() + self.compute_client.get_server.assert_not_called() def test_show_embedded_flavor(self): # Tests using --os-compute-api-version >= 2.47 where the flavor @@ -8566,10 +8542,10 @@ def test_show_embedded_flavor(self): # Since the flavor details are in a dict we can't be sure of the # ordering so just assert that one of the keys is in the output. self.assertIn('original_name', data[columns.index('flavor')]._value) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False, details=True ) - self.compute_sdk_client.get_server.assert_not_called() + self.compute_client.get_server.assert_not_called() def test_show_diagnostics(self): arglist = [ @@ -8587,13 +8563,13 @@ def test_show_diagnostics(self): self.assertEqual(('test',), columns) self.assertEqual(('test',), data) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False, details=True ) - self.compute_sdk_client.get_server_diagnostics.assert_called_once_with( + self.compute_client.get_server_diagnostics.assert_called_once_with( self.server ) - self.compute_sdk_client.get_server.assert_not_called() + self.compute_client.get_server.assert_not_called() def test_show_topology(self): self.set_compute_api_version('2.78') @@ -8616,13 +8592,11 @@ def test_show_topology(self): self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False, details=True ) - self.server.fetch_topology.assert_called_once_with( - self.compute_sdk_client - ) - self.compute_sdk_client.get_server.assert_not_called() + self.server.fetch_topology.assert_called_once_with(self.compute_client) + self.compute_client.get_server.assert_not_called() def test_show_topology_pre_v278(self): self.set_compute_api_version('2.77') @@ -8641,11 +8615,11 @@ def test_show_topology_pre_v278(self): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False, details=True ) self.server.fetch_topology.assert_not_called() - self.compute_sdk_client.get_server.assert_not_called() + self.compute_client.get_server.assert_not_called() @mock.patch('openstackclient.compute.v2.server.os.system') @@ -8673,7 +8647,7 @@ def setUp(self): self.server = compute_fakes.create_one_sdk_server( attrs=self.attrs, ) - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server def test_server_ssh_no_opts(self, mock_exec): arglist = [ @@ -8696,7 +8670,7 @@ def test_server_ssh_no_opts(self, mock_exec): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False ) self.assertIsNone(result) @@ -8729,7 +8703,7 @@ def test_server_ssh_passthrough_opts(self, mock_exec): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False ) self.assertIsNone(result) @@ -8763,7 +8737,7 @@ def test_server_ssh_deprecated_opts(self, mock_exec): with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.name, ignore_missing=False ) self.assertIsNone(result) @@ -8792,7 +8766,7 @@ def test_server_start_multi_servers(self): def test_server_start_with_all_projects(self): server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = server + self.compute_client.find_server.return_value = server arglist = [ server.id, @@ -8805,7 +8779,7 @@ def test_server_start_with_all_projects(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( server.id, ignore_missing=False, details=False, @@ -8828,7 +8802,7 @@ def test_server_stop_multi_servers(self): def test_server_start_with_all_projects(self): server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = server + self.compute_client.find_server.return_value = server arglist = [ server.id, @@ -8841,7 +8815,7 @@ def test_server_start_with_all_projects(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( server.id, ignore_missing=False, details=False, @@ -8896,7 +8870,7 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server self.cmd = server.UnrescueServer(self.app, None) @@ -8911,10 +8885,10 @@ def test_rescue(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.unrescue_server.assert_called_once_with( + self.compute_client.unrescue_server.assert_called_once_with( self.server ) self.assertIsNone(result) @@ -8925,7 +8899,7 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server # Get the command object to test self.cmd = server.UnsetServer(self.app, None) @@ -8941,12 +8915,10 @@ def test_server_unset_no_option(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server( - self.server.id, ignore_missing=False - ) - self.compute_sdk_client.delete_server_metadata.assert_not_called() - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.remove_tag_from_server.assert_not_called() + self.compute_client.find_server(self.server.id, ignore_missing=False) + self.compute_client.delete_server_metadata.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.remove_tag_from_server.assert_not_called() self.assertIsNone(result) def test_server_unset_with_property(self): @@ -8965,15 +8937,13 @@ def test_server_unset_with_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server( - self.server.id, ignore_missing=False - ) - self.compute_sdk_client.delete_server_metadata.assert_called_once_with( + self.compute_client.find_server(self.server.id, ignore_missing=False) + self.compute_client.delete_server_metadata.assert_called_once_with( self.server, ['key1', 'key2'], ) - self.compute_sdk_client.update_server.assert_not_called() - self.compute_sdk_client.remove_tag_from_server.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.remove_tag_from_server.assert_not_called() self.assertIsNone(result) def test_server_unset_with_description(self): @@ -8992,14 +8962,12 @@ def test_server_unset_with_description(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server( - self.server.id, ignore_missing=False - ) - self.compute_sdk_client.update_server.assert_called_once_with( + self.compute_client.find_server(self.server.id, ignore_missing=False) + self.compute_client.update_server.assert_called_once_with( self.server, description='' ) - self.compute_sdk_client.delete_server_metadata.assert_not_called() - self.compute_sdk_client.remove_tag_from_server.assert_not_called() + self.compute_client.delete_server_metadata.assert_not_called() + self.compute_client.remove_tag_from_server.assert_not_called() self.assertIsNone(result) def test_server_unset_with_description_pre_v219(self): @@ -9042,17 +9010,15 @@ def test_server_unset_with_tag(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.find_server( - self.server.id, ignore_missing=False - ) - self.compute_sdk_client.remove_tag_from_server.assert_has_calls( + self.compute_client.find_server(self.server.id, ignore_missing=False) + self.compute_client.remove_tag_from_server.assert_has_calls( [ mock.call(self.server, 'tag1'), mock.call(self.server, 'tag2'), ] ) - self.compute_sdk_client.delete_server_metadata.assert_not_called() - self.compute_sdk_client.update_server.assert_not_called() + self.compute_client.delete_server_metadata.assert_not_called() + self.compute_client.update_server.assert_not_called() def test_server_unset_with_tag_pre_v226(self): self.set_compute_api_version('2.25') @@ -9086,8 +9052,8 @@ def setUp(self): attrs={'status': 'SHELVED'}, ) - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.unshelve_server.return_value = None + self.compute_client.find_server.return_value = self.server + self.compute_client.unshelve_server.return_value = None # Get the command object to test self.cmd = server.UnshelveServer(self.app, None) @@ -9103,11 +9069,11 @@ def test_unshelve(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, ) - self.compute_sdk_client.unshelve_server.assert_called_once_with( + self.compute_client.unshelve_server.assert_called_once_with( self.server.id ) @@ -9127,11 +9093,11 @@ def test_unshelve_with_az(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, ) - self.compute_sdk_client.unshelve_server.assert_called_once_with( + self.compute_client.unshelve_server.assert_called_once_with( self.server.id, availability_zone='foo-az', ) @@ -9173,11 +9139,11 @@ def test_unshelve_with_host(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, ) - self.compute_sdk_client.unshelve_server.assert_called_once_with( + self.compute_client.unshelve_server.assert_called_once_with( self.server.id, host='server1', ) @@ -9219,11 +9185,11 @@ def test_unshelve_with_no_az(self): self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, ) - self.compute_sdk_client.unshelve_server.assert_called_once_with( + self.compute_client.unshelve_server.assert_called_once_with( self.server.id, availability_zone=None, ) @@ -9295,15 +9261,13 @@ def test_unshelve_with_wait(self, mock_wait_for_status): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.name, ignore_missing=False, ) - self.compute_sdk_client.unshelve_server.assert_called_with( - self.server.id - ) + self.compute_client.unshelve_server.assert_called_with(self.server.id) mock_wait_for_status.assert_called_once_with( - self.compute_sdk_client.get_server, + self.compute_client.get_server, self.server.id, callback=mock.ANY, success_status=('active', 'shutoff'), @@ -9395,7 +9359,7 @@ def test_prep_server_detail(self): self.image_client.get_image.return_value = _image _flavor = compute_fakes.create_one_flavor() - self.compute_sdk_client.find_flavor.return_value = _flavor + self.compute_client.find_flavor.return_value = _flavor server_info = { 'image': {'id': _image.id}, @@ -9407,7 +9371,7 @@ def test_prep_server_detail(self): 'volumes_attached': [{"id": "6344fe9d-ef20-45b2-91a6"}], } _server = compute_fakes.create_one_sdk_server(server_info) - self.compute_sdk_client.get_server.return_value = _server + self.compute_client.get_server.return_value = _server expected = { 'OS-DCF:diskConfig': None, @@ -9458,14 +9422,14 @@ def test_prep_server_detail(self): } actual = server._prep_server_detail( - self.compute_sdk_client, + self.compute_client, self.image_client, _server, ) self.assertCountEqual(expected, actual) # this should be called since we need the flavor (< 2.47) - self.compute_sdk_client.find_flavor.assert_called_once_with( + self.compute_client.find_flavor.assert_called_once_with( _flavor.id, ignore_missing=False ) @@ -9474,7 +9438,7 @@ def test_prep_server_detail_v247(self): self.image_client.get_image.return_value = _image _flavor = compute_fakes.create_one_flavor() - self.compute_sdk_client.find_flavor.return_value = _flavor + self.compute_client.find_flavor.return_value = _flavor server_info = { 'image': {'id': _image.id}, @@ -9494,7 +9458,7 @@ def test_prep_server_detail_v247(self): 'volumes_attached': [{"id": "6344fe9d-ef20-45b2-91a6"}], } _server = compute_fakes.create_one_sdk_server(server_info) - self.compute_sdk_client.get_server.return_value = _server + self.compute_client.get_server.return_value = _server expected = { 'OS-DCF:diskConfig': None, @@ -9545,11 +9509,11 @@ def test_prep_server_detail_v247(self): } actual = server._prep_server_detail( - self.compute_sdk_client, + self.compute_client, self.image_client, _server, ) self.assertCountEqual(expected, actual) # this shouldn't be called since we have a full flavor (>= 2.47) - self.compute_sdk_client.find_flavor.assert_not_called() + self.compute_client.find_flavor.assert_not_called() diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py index 9d5269ddb..bc1fca2b8 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_backup.py +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -27,9 +27,7 @@ def setup_servers_mock(self, count): servers = compute_fakes.create_sdk_servers( count=count, ) - - # This is the return value for compute_client.find_server() - self.compute_sdk_client.find_server = compute_fakes.get_servers( + self.compute_client.find_server = compute_fakes.get_servers( servers, 0, ) @@ -110,7 +108,7 @@ def test_server_backup_defaults(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.backup_server.assert_called_with( + self.compute_client.backup_server.assert_called_with( servers[0].id, servers[0].name, '', @@ -146,7 +144,7 @@ def test_server_backup_create_options(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.backup_server.assert_called_with( + self.compute_client.backup_server.assert_called_with( servers[0].id, 'image', 'daily', @@ -186,7 +184,7 @@ def test_server_backup_wait_fail(self, mock_wait_for_status): parsed_args, ) - self.compute_sdk_client.backup_server.assert_called_with( + self.compute_client.backup_server.assert_called_with( servers[0].id, 'image', 'daily', @@ -227,7 +225,7 @@ def test_server_backup_wait_ok(self, mock_wait_for_status): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.backup_server.assert_called_with( + self.compute_client.backup_server.assert_called_with( servers[0].id, 'image', 'daily', diff --git a/openstackclient/tests/unit/compute/v2/test_server_event.py b/openstackclient/tests/unit/compute/v2/test_server_event.py index 728d9f842..a5e5116a1 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_event.py +++ b/openstackclient/tests/unit/compute/v2/test_server_event.py @@ -64,8 +64,8 @@ class TestListServerEvent(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.compute_sdk_client.find_server.return_value = self.fake_server - self.compute_sdk_client.server_actions.return_value = [ + self.compute_client.find_server.return_value = self.fake_server + self.compute_client.server_actions.return_value = [ self.fake_event, ] @@ -83,11 +83,11 @@ def test_server_event_list(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.compute_sdk_client.server_actions.assert_called_with( + self.compute_client.server_actions.assert_called_with( self.fake_server.id ) @@ -107,11 +107,11 @@ def test_server_event_list_long(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.compute_sdk_client.server_actions.assert_called_with( + self.compute_client.server_actions.assert_called_with( self.fake_server.id ) @@ -134,11 +134,11 @@ def test_server_event_list_with_changes_since(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.compute_sdk_client.server_actions.assert_called_with( + self.compute_client.server_actions.assert_called_with( self.fake_server.id, changes_since='2016-03-04T06:27:59Z', ) @@ -214,11 +214,11 @@ def test_server_event_list_with_changes_before(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.compute_sdk_client.server_actions.assert_called_with( + self.compute_client.server_actions.assert_called_with( self.fake_server.id, changes_before='2016-03-04T06:27:59Z', ) @@ -290,7 +290,7 @@ def test_server_event_list_with_limit(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_actions.assert_called_with( + self.compute_client.server_actions.assert_called_with( self.fake_server.id, limit=1, paginated=False, @@ -337,7 +337,7 @@ def test_server_event_list_with_marker(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_actions.assert_called_with( + self.compute_client.server_actions.assert_called_with( self.fake_server.id, marker='test_event', ) @@ -392,10 +392,8 @@ class TestShowServerEvent(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.compute_sdk_client.find_server.return_value = self.fake_server - self.compute_sdk_client.get_server_action.return_value = ( - self.fake_event - ) + self.compute_client.find_server.return_value = self.fake_server + self.compute_client.get_server_action.return_value = self.fake_event self.cmd = server_event.ShowServerEvent(self.app, None) @@ -412,11 +410,11 @@ def test_server_event_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.compute_sdk_client.get_server_action.assert_called_with( + self.compute_client.get_server_action.assert_called_with( self.fake_event.request_id, self.fake_server.id, ) diff --git a/openstackclient/tests/unit/compute/v2/test_server_group.py b/openstackclient/tests/unit/compute/v2/test_server_group.py index 2e3449323..a91824a9c 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_group.py +++ b/openstackclient/tests/unit/compute/v2/test_server_group.py @@ -55,7 +55,7 @@ class TestServerGroupCreate(TestServerGroup): def setUp(self): super().setUp() - self.compute_sdk_client.create_server_group.return_value = ( + self.compute_client.create_server_group.return_value = ( self.fake_server_group ) self.cmd = server_group.CreateServerGroup(self.app, None) @@ -74,7 +74,7 @@ def test_server_group_create(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_group.assert_called_once_with( + self.compute_client.create_server_group.assert_called_once_with( name=parsed_args.name, policy=parsed_args.policy, ) @@ -96,7 +96,7 @@ def test_server_group_create_with_soft_policies(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_group.assert_called_once_with( + self.compute_client.create_server_group.assert_called_once_with( name=parsed_args.name, policy=parsed_args.policy, ) @@ -141,7 +141,7 @@ def test_server_group_create_with_rules(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_group.assert_called_once_with( + self.compute_client.create_server_group.assert_called_once_with( name=parsed_args.name, policy=parsed_args.policy, rules=parsed_args.rules, @@ -179,7 +179,7 @@ class TestServerGroupDelete(TestServerGroup): def setUp(self): super().setUp() - self.compute_sdk_client.find_server_group.return_value = ( + self.compute_client.find_server_group.return_value = ( self.fake_server_group ) self.cmd = server_group.DeleteServerGroup(self.app, None) @@ -193,10 +193,10 @@ def test_server_group_delete(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server_group.assert_called_once_with( + self.compute_client.find_server_group.assert_called_once_with( 'affinity_group', ignore_missing=False ) - self.compute_sdk_client.delete_server_group.assert_called_once_with( + self.compute_client.delete_server_group.assert_called_once_with( self.fake_server_group.id ) self.assertIsNone(result) @@ -208,21 +208,17 @@ def test_server_group_multiple_delete(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server_group.assert_any_call( + self.compute_client.find_server_group.assert_any_call( 'affinity_group', ignore_missing=False ) - self.compute_sdk_client.find_server_group.assert_any_call( + self.compute_client.find_server_group.assert_any_call( 'anti_affinity_group', ignore_missing=False ) - self.compute_sdk_client.delete_server_group.assert_called_with( + self.compute_client.delete_server_group.assert_called_with( self.fake_server_group.id ) - self.assertEqual( - 2, self.compute_sdk_client.find_server_group.call_count - ) - self.assertEqual( - 2, self.compute_sdk_client.delete_server_group.call_count - ) + self.assertEqual(2, self.compute_client.find_server_group.call_count) + self.assertEqual(2, self.compute_client.delete_server_group.call_count) self.assertIsNone(result) def test_server_group_delete_no_input(self): @@ -243,7 +239,7 @@ def test_server_group_multiple_delete_with_exception(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_sdk_client.find_server_group.side_effect = [ + self.compute_client.find_server_group.side_effect = [ self.fake_server_group, exceptions.CommandError, ] @@ -253,16 +249,14 @@ def test_server_group_multiple_delete_with_exception(self): except exceptions.CommandError as e: self.assertEqual('1 of 2 server groups failed to delete.', str(e)) - self.compute_sdk_client.find_server_group.assert_any_call( + self.compute_client.find_server_group.assert_any_call( 'affinity_group', ignore_missing=False ) - self.compute_sdk_client.find_server_group.assert_any_call( + self.compute_client.find_server_group.assert_any_call( 'anti_affinity_group', ignore_missing=False ) - self.assertEqual( - 2, self.compute_sdk_client.find_server_group.call_count - ) - self.compute_sdk_client.delete_server_group.assert_called_once_with( + self.assertEqual(2, self.compute_client.find_server_group.call_count) + self.compute_client.delete_server_group.assert_called_once_with( self.fake_server_group.id ) @@ -271,7 +265,7 @@ class TestServerGroupList(TestServerGroup): def setUp(self): super().setUp() - self.compute_sdk_client.server_groups.return_value = [ + self.compute_client.server_groups.return_value = [ self.fake_server_group ] self.cmd = server_group.ListServerGroup(self.app, None) @@ -287,7 +281,7 @@ def test_server_group_list(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_groups.assert_called_once_with() + self.compute_client.server_groups.assert_called_once_with() expected_columns = ( 'ID', @@ -318,7 +312,7 @@ def test_server_group_list_with_all_projects_and_long(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_groups.assert_called_once_with( + self.compute_client.server_groups.assert_called_once_with( all_projects=True ) @@ -359,7 +353,7 @@ def test_server_group_list_with_limit(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_groups.assert_called_once_with(limit=1) + self.compute_client.server_groups.assert_called_once_with(limit=1) def test_server_group_list_with_offset(self): arglist = [ @@ -376,7 +370,7 @@ def test_server_group_list_with_offset(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_groups.assert_called_once_with(offset=5) + self.compute_client.server_groups.assert_called_once_with(offset=5) def test_server_group_list_v264(self): self.set_compute_api_version('2.64') @@ -388,7 +382,7 @@ def test_server_group_list_v264(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_groups.assert_called_once_with() + self.compute_client.server_groups.assert_called_once_with() expected_columns = ( 'ID', @@ -419,7 +413,7 @@ def test_server_group_list_with_all_projects_and_long_v264(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.server_groups.assert_called_once_with( + self.compute_client.server_groups.assert_called_once_with( all_projects=True ) @@ -450,7 +444,7 @@ class TestServerGroupShow(TestServerGroup): def setUp(self): super().setUp() - self.compute_sdk_client.find_server_group.return_value = ( + self.compute_client.find_server_group.return_value = ( self.fake_server_group ) self.cmd = server_group.ShowServerGroup(self.app, None) diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py index 8ba6d1262..7feb0b22f 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_image.py +++ b/openstackclient/tests/unit/compute/v2/test_server_image.py @@ -26,9 +26,7 @@ def setup_servers_mock(self, count): servers = compute_fakes.create_sdk_servers( count=count, ) - - # This is the return value for compute_client.find_server() - self.compute_sdk_client.find_server = compute_fakes.get_servers( + self.compute_client.find_server = compute_fakes.get_servers( servers, 0, ) @@ -85,7 +83,7 @@ def setup_images_mock(self, count, servers=None): ) self.image_client.find_image = mock.Mock(side_effect=images) - self.compute_sdk_client.create_server_image = mock.Mock( + self.compute_client.create_server_image = mock.Mock( return_value=images[0], ) return images @@ -107,7 +105,7 @@ def test_server_image_create_defaults(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_image.assert_called_with( + self.compute_client.create_server_image.assert_called_with( servers[0].id, servers[0].name, None, @@ -139,7 +137,7 @@ def test_server_image_create_options(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_image.assert_called_with( + self.compute_client.create_server_image.assert_called_with( servers[0].id, 'img-nam', {'key': 'value'}, @@ -169,7 +167,7 @@ def test_server_create_image_wait_fail(self, mock_wait_for_status): parsed_args, ) - self.compute_sdk_client.create_server_image.assert_called_with( + self.compute_client.create_server_image.assert_called_with( servers[0].id, servers[0].name, None, @@ -199,7 +197,7 @@ def test_server_create_image_wait_ok(self, mock_wait_for_status): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.create_server_image.assert_called_with( + self.compute_client.create_server_image.assert_called_with( servers[0].id, servers[0].name, None, diff --git a/openstackclient/tests/unit/compute/v2/test_server_migration.py b/openstackclient/tests/unit/compute/v2/test_server_migration.py index 09ceae6fe..362c9bae6 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_migration.py +++ b/openstackclient/tests/unit/compute/v2/test_server_migration.py @@ -53,10 +53,10 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server self.migrations = compute_fakes.create_migrations(count=3) - self.compute_sdk_client.migrations.return_value = self.migrations + self.compute_client.migrations.return_value = self.migrations self.data = ( common_utils.get_item_properties(s, self.MIGRATION_FIELDS) @@ -76,7 +76,7 @@ def test_server_migration_list_no_options(self): # Set expected values kwargs = {} - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -108,10 +108,10 @@ def test_server_migration_list(self): 'migration_type': 'migration', } - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( 'server1', ignore_missing=False ) - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -169,7 +169,7 @@ def test_server_migration_list(self): 'status': 'migrating', } - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -247,7 +247,7 @@ def test_server_migration_list(self): 'changes_since': '2019-08-09T08:03:25Z', } - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -376,7 +376,7 @@ def test_server_migration_list_with_changes_before(self): 'changes_before': '2019-08-09T08:03:25Z', } - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -495,7 +495,7 @@ def test_server_migration_list_with_project(self): 'changes_before': "2019-08-09T08:03:25Z", } - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.MIGRATION_COLUMNS.insert( len(self.MIGRATION_COLUMNS) - 2, "Project" @@ -572,7 +572,7 @@ def test_server_migration_list_with_user(self): 'changes_before': "2019-08-09T08:03:25Z", } - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.MIGRATION_COLUMNS.insert(len(self.MIGRATION_COLUMNS) - 2, "User") self.MIGRATION_FIELDS.insert(len(self.MIGRATION_FIELDS) - 2, "user_id") @@ -644,7 +644,7 @@ def test_server_migration_list_with_project_and_user(self): 'changes_before': "2019-08-09T08:03:25Z", } - self.compute_sdk_client.migrations.assert_called_with(**kwargs) + self.compute_client.migrations.assert_called_with(**kwargs) self.MIGRATION_COLUMNS.insert( len(self.MIGRATION_COLUMNS) - 2, "Project" @@ -695,13 +695,13 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server self.server_migration = compute_fakes.create_one_server_migration() - self.compute_sdk_client.get_server_migration.return_value = ( + self.compute_client.get_server_migration.return_value = ( self.server_migration ) - self.compute_sdk_client.server_migrations.return_value = iter( + self.compute_client.server_migrations.return_value = iter( [self.server_migration] ) @@ -759,10 +759,10 @@ def _test_server_migration_show(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.get_server_migration.assert_called_with( + self.compute_client.get_server_migration.assert_called_with( self.server.id, '2', ignore_missing=False ) @@ -811,7 +811,7 @@ def test_server_migration_show_pre_v224(self): def test_server_migration_show_by_uuid(self): self.set_compute_api_version('2.59') - self.compute_sdk_client.server_migrations.return_value = iter( + self.compute_client.server_migrations.return_value = iter( [self.server_migration] ) @@ -830,18 +830,18 @@ def test_server_migration_show_by_uuid(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.server_migrations.assert_called_with( + self.compute_client.server_migrations.assert_called_with( self.server.id ) - self.compute_sdk_client.get_server_migration.assert_not_called() + self.compute_client.get_server_migration.assert_not_called() def test_server_migration_show_by_uuid_no_matches(self): self.set_compute_api_version('2.59') - self.compute_sdk_client.server_migrations.return_value = iter([]) + self.compute_client.server_migrations.return_value = iter([]) arglist = [ self.server.id, @@ -900,7 +900,7 @@ def setUp(self): self.server = compute_fakes.create_one_sdk_server() # Return value for utils.find_resource for server. - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server # Get the command object to test self.cmd = server_migration.AbortMigration(self.app, None) @@ -917,10 +917,10 @@ def test_migration_abort(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.abort_server_migration.assert_called_with( + self.compute_client.abort_server_migration.assert_called_with( '2', self.server.id, ignore_missing=False ) self.assertIsNone(result) @@ -946,7 +946,7 @@ def test_server_migration_abort_by_uuid(self): self.set_compute_api_version('2.59') self.server_migration = compute_fakes.create_one_server_migration() - self.compute_sdk_client.server_migrations.return_value = iter( + self.compute_client.server_migrations.return_value = iter( [self.server_migration] ) @@ -959,13 +959,13 @@ def test_server_migration_abort_by_uuid(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.server_migrations.assert_called_with( + self.compute_client.server_migrations.assert_called_with( self.server.id ) - self.compute_sdk_client.abort_server_migration.assert_called_with( + self.compute_client.abort_server_migration.assert_called_with( self.server_migration.id, self.server.id, ignore_missing=False ) self.assertIsNone(result) @@ -973,7 +973,7 @@ def test_server_migration_abort_by_uuid(self): def test_server_migration_abort_by_uuid_no_matches(self): self.set_compute_api_version('2.59') - self.compute_sdk_client.server_migrations.return_value = iter([]) + self.compute_client.server_migrations.return_value = iter([]) arglist = [ self.server.id, @@ -1015,7 +1015,7 @@ def setUp(self): self.server = compute_fakes.create_one_sdk_server() # Return value for utils.find_resource for server. - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server # Get the command object to test self.cmd = server_migration.ForceCompleteMigration(self.app, None) @@ -1032,10 +1032,10 @@ def test_migration_force_complete(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.force_complete_server_migration.assert_called_with( + self.compute_client.force_complete_server_migration.assert_called_with( '2', self.server.id ) self.assertIsNone(result) @@ -1061,7 +1061,7 @@ def test_server_migration_force_complete_by_uuid(self): self.set_compute_api_version('2.59') self.server_migration = compute_fakes.create_one_server_migration() - self.compute_sdk_client.server_migrations.return_value = iter( + self.compute_client.server_migrations.return_value = iter( [self.server_migration] ) @@ -1074,13 +1074,13 @@ def test_server_migration_force_complete_by_uuid(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.find_server.assert_called_with( + self.compute_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.compute_sdk_client.server_migrations.assert_called_with( + self.compute_client.server_migrations.assert_called_with( self.server.id ) - self.compute_sdk_client.force_complete_server_migration.assert_called_with( + self.compute_client.force_complete_server_migration.assert_called_with( self.server_migration.id, self.server.id ) self.assertIsNone(result) @@ -1088,7 +1088,7 @@ def test_server_migration_force_complete_by_uuid(self): def test_server_migration_force_complete_by_uuid_no_matches(self): self.set_compute_api_version('2.59') - self.compute_sdk_client.server_migrations.return_value = iter([]) + self.compute_client.server_migrations.return_value = iter([]) arglist = [ self.server.id, diff --git a/openstackclient/tests/unit/compute/v2/test_server_volume.py b/openstackclient/tests/unit/compute/v2/test_server_volume.py index d42da9557..9f26c6d5e 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_volume.py +++ b/openstackclient/tests/unit/compute/v2/test_server_volume.py @@ -31,8 +31,8 @@ def setUp(self): ) ) - self.compute_sdk_client.find_server.return_value = self.server - self.compute_sdk_client.volume_attachments.return_value = ( + self.compute_client.find_server.return_value = self.server + self.compute_client.volume_attachments.return_value = ( self.volume_attachments ) @@ -68,7 +68,7 @@ def test_server_volume_list(self): ), tuple(data), ) - self.compute_sdk_client.volume_attachments.assert_called_once_with( + self.compute_client.volume_attachments.assert_called_once_with( self.server, ) @@ -114,7 +114,7 @@ def test_server_volume_list_with_tags(self): ), tuple(data), ) - self.compute_sdk_client.volume_attachments.assert_called_once_with( + self.compute_client.volume_attachments.assert_called_once_with( self.server, ) @@ -163,7 +163,7 @@ def test_server_volume_list_with_delete_on_attachment(self): ), tuple(data), ) - self.compute_sdk_client.volume_attachments.assert_called_once_with( + self.compute_client.volume_attachments.assert_called_once_with( self.server, ) @@ -215,7 +215,7 @@ def test_server_volume_list_with_attachment_ids(self): ), tuple(data), ) - self.compute_sdk_client.volume_attachments.assert_called_once_with( + self.compute_client.volume_attachments.assert_called_once_with( self.server, ) @@ -225,7 +225,7 @@ def setUp(self): super().setUp() self.server = sdk_fakes.generate_fake_resource(_server.Server) - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) self.volume_sdk_client.find_volume.return_value = self.volume @@ -248,7 +248,7 @@ def test_server_volume_update(self): result = self.cmd.take_action(parsed_args) # This is a no-op - self.compute_sdk_client.update_volume_attachment.assert_not_called() + self.compute_client.update_volume_attachment.assert_not_called() self.assertIsNone(result) def test_server_volume_update_with_delete_on_termination(self): @@ -268,7 +268,7 @@ def test_server_volume_update_with_delete_on_termination(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_volume_attachment.assert_called_once_with( + self.compute_client.update_volume_attachment.assert_called_once_with( self.server, self.volume, delete_on_termination=True, @@ -292,7 +292,7 @@ def test_server_volume_update_with_preserve_on_termination(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_volume_attachment.assert_called_once_with( + self.compute_client.update_volume_attachment.assert_called_once_with( self.server, self.volume, delete_on_termination=False ) self.assertIsNone(result) @@ -317,7 +317,7 @@ def test_server_volume_update_with_delete_on_termination_pre_v285(self): self.cmd.take_action, parsed_args, ) - self.compute_sdk_client.update_volume_attachment.assert_not_called() + self.compute_client.update_volume_attachment.assert_not_called() def test_server_volume_update_with_preserve_on_termination_pre_v285(self): self.set_compute_api_version('2.84') @@ -339,4 +339,4 @@ def test_server_volume_update_with_preserve_on_termination_pre_v285(self): self.cmd.take_action, parsed_args, ) - self.compute_sdk_client.update_volume_attachment.assert_not_called() + self.compute_client.update_volume_attachment.assert_not_called() diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py index ce7791d23..c64a2b9c8 100644 --- a/openstackclient/tests/unit/compute/v2/test_service.py +++ b/openstackclient/tests/unit/compute/v2/test_service.py @@ -30,7 +30,7 @@ def setUp(self): sdk_fakes.generate_fake_resources(_service.Service, count=2) ) - self.compute_sdk_client.delete_service.return_value = None + self.compute_client.delete_service.return_value = None # Get the command object to test self.cmd = service.DeleteService(self.app, None) @@ -46,7 +46,7 @@ def test_service_delete(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.delete_service.assert_called_with( + self.compute_client.delete_service.assert_called_with( self.services[0].binary, ignore_missing=False ) self.assertIsNone(result) @@ -65,7 +65,7 @@ def test_multi_services_delete(self): calls = [] for s in self.services: calls.append(mock.call(s.binary, ignore_missing=False)) - self.compute_sdk_client.delete_service.assert_has_calls(calls) + self.compute_client.delete_service.assert_has_calls(calls) self.assertIsNone(result) def test_multi_services_delete_with_exception(self): @@ -77,7 +77,7 @@ def test_multi_services_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) delete_mock_result = [None, exceptions.CommandError] - self.compute_sdk_client.delete_service = mock.Mock( + self.compute_client.delete_service = mock.Mock( side_effect=delete_mock_result ) @@ -89,10 +89,10 @@ def test_multi_services_delete_with_exception(self): '1 of 2 compute services failed to delete.', str(e) ) - self.compute_sdk_client.delete_service.assert_any_call( + self.compute_client.delete_service.assert_any_call( self.services[0].binary, ignore_missing=False ) - self.compute_sdk_client.delete_service.assert_any_call( + self.compute_client.delete_service.assert_any_call( 'unexist_service', ignore_missing=False ) @@ -103,7 +103,7 @@ def setUp(self): self.service = sdk_fakes.generate_fake_resource(_service.Service) - self.compute_sdk_client.services.return_value = [self.service] + self.compute_client.services.return_value = [self.service] # Get the command object to test self.cmd = service.ListService(self.app, None) @@ -126,7 +126,7 @@ def test_service_list(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.services.assert_called_with( + self.compute_client.services.assert_called_with( host=self.service.host, binary=self.service.binary, ) @@ -175,7 +175,7 @@ def test_service_list_with_long_option(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.services.assert_called_with( + self.compute_client.services.assert_called_with( host=self.service.host, binary=self.service.binary, ) @@ -228,7 +228,7 @@ def test_service_list_with_long_option_2_11(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.services.assert_called_with( + self.compute_client.services.assert_called_with( host=self.service.host, binary=self.service.binary, ) @@ -269,8 +269,8 @@ def setUp(self): self.service = sdk_fakes.generate_fake_resource(_service.Service) - self.compute_sdk_client.enable_service.return_value = self.service - self.compute_sdk_client.disable_service.return_value = self.service + self.compute_client.enable_service.return_value = self.service + self.compute_client.disable_service.return_value = self.service self.cmd = service.SetService(self.app, None) @@ -286,8 +286,8 @@ def test_set_nothing(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.enable_service.assert_not_called() - self.compute_sdk_client.disable_service.assert_not_called() + self.compute_client.enable_service.assert_not_called() + self.compute_client.disable_service.assert_not_called() self.assertIsNone(result) def test_service_set_enable(self): @@ -305,7 +305,7 @@ def test_service_set_enable(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.enable_service.assert_called_with( + self.compute_client.enable_service.assert_called_with( None, self.service.host, self.service.binary ) self.assertIsNone(result) @@ -325,7 +325,7 @@ def test_service_set_disable(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.disable_service.assert_called_with( + self.compute_client.disable_service.assert_called_with( None, self.service.host, self.service.binary, None ) self.assertIsNone(result) @@ -349,7 +349,7 @@ def test_service_set_disable_with_reason(self): result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.disable_service.assert_called_with( + self.compute_client.disable_service.assert_called_with( None, self.service.host, self.service.binary, reason ) self.assertIsNone(result) @@ -419,11 +419,11 @@ def test_service_set_state_up(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_client.update_service_forced_down.assert_called_once_with( None, self.service.host, self.service.binary, False ) - self.assertNotCalled(self.compute_sdk_client.enable_service) - self.assertNotCalled(self.compute_sdk_client.disable_service) + self.assertNotCalled(self.compute_client.enable_service) + self.assertNotCalled(self.compute_client.disable_service) self.assertIsNone(result) def test_service_set_state_down(self): @@ -441,11 +441,11 @@ def test_service_set_state_down(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_client.update_service_forced_down.assert_called_once_with( None, self.service.host, self.service.binary, True ) - self.assertNotCalled(self.compute_sdk_client.enable_service) - self.assertNotCalled(self.compute_sdk_client.disable_service) + self.assertNotCalled(self.compute_client.enable_service) + self.assertNotCalled(self.compute_client.disable_service) self.assertIsNone(result) def test_service_set_enable_and_state_down(self): @@ -465,10 +465,10 @@ def test_service_set_enable_and_state_down(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.enable_service.assert_called_once_with( + self.compute_client.enable_service.assert_called_once_with( None, self.service.host, self.service.binary ) - self.compute_sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_client.update_service_forced_down.assert_called_once_with( None, self.service.host, self.service.binary, True ) self.assertIsNone(result) @@ -491,12 +491,12 @@ def test_service_set_enable_and_state_down_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch.object( - self.compute_sdk_client, 'enable_service', side_effect=Exception() + self.compute_client, 'enable_service', side_effect=Exception() ): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.compute_sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_client.update_service_forced_down.assert_called_once_with( None, self.service.host, self.service.binary, True ) @@ -519,14 +519,12 @@ def test_service_set_disable_down(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c' - self.compute_sdk_client.services.return_value = [ - mock.Mock(id=service_id) - ] + self.compute_client.services.return_value = [mock.Mock(id=service_id)] result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.disable_service.assert_called_once_with( + self.compute_client.disable_service.assert_called_once_with( service_id, self.service.host, self.service.binary, None ) - self.compute_sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_client.update_service_forced_down.assert_called_once_with( service_id, self.service.host, self.service.binary, True ) self.assertIsNone(result) @@ -552,11 +550,9 @@ def test_service_set_disable_reason(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c' - self.compute_sdk_client.services.return_value = [ - mock.Mock(id=service_id) - ] + self.compute_client.services.return_value = [mock.Mock(id=service_id)] result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.disable_service.assert_called_once_with( + self.compute_client.disable_service.assert_called_once_with( service_id, self.service.host, self.service.binary, reason ) self.assertIsNone(result) @@ -580,25 +576,23 @@ def test_service_set_enable_up(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c' - self.compute_sdk_client.services.return_value = [ - mock.Mock(id=service_id) - ] + self.compute_client.services.return_value = [mock.Mock(id=service_id)] result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.enable_service.assert_called_once_with( + self.compute_client.enable_service.assert_called_once_with( service_id, self.service.host, self.service.binary ) - self.compute_sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_client.update_service_forced_down.assert_called_once_with( service_id, self.service.host, self.service.binary, False ) self.assertIsNone(result) def test_service_set_find_service_by_host_and_binary_no_results(self): # Tests that no compute services are found by host and binary. - self.compute_sdk_client.services.return_value = [] + self.compute_client.services.return_value = [] ex = self.assertRaises( exceptions.CommandError, self.cmd._find_service_by_host_and_binary, - self.compute_sdk_client, + self.compute_client, 'fake-host', 'nova-compute', ) @@ -610,14 +604,14 @@ def test_service_set_find_service_by_host_and_binary_no_results(self): def test_service_set_find_service_by_host_and_binary_many_results(self): # Tests that more than one compute service is found by host and binary. - self.compute_sdk_client.services.return_value = [ + self.compute_client.services.return_value = [ mock.Mock(), mock.Mock(), ] ex = self.assertRaises( exceptions.CommandError, self.cmd._find_service_by_host_and_binary, - self.compute_sdk_client, + self.compute_client, 'fake-host', 'nova-compute', ) diff --git a/openstackclient/tests/unit/compute/v2/test_usage.py b/openstackclient/tests/unit/compute/v2/test_usage.py index 346e2fb0b..616967818 100644 --- a/openstackclient/tests/unit/compute/v2/test_usage.py +++ b/openstackclient/tests/unit/compute/v2/test_usage.py @@ -58,7 +58,7 @@ class TestUsageList(TestUsage): def setUp(self): super().setUp() - self.compute_sdk_client.usages.return_value = self.usages + self.compute_client.usages.return_value = self.usages self.projects_mock.list.return_value = [self.project] # Get the command object to test @@ -97,7 +97,7 @@ def test_usage_list_with_options(self): columns, data = self.cmd.take_action(parsed_args) self.projects_mock.list.assert_called_with() - self.compute_sdk_client.usages.assert_called_with( + self.compute_client.usages.assert_called_with( start=datetime.datetime(2016, 11, 11, 0, 0), end=datetime.datetime(2016, 12, 20, 0, 0), detailed=True, @@ -118,7 +118,7 @@ def test_usage_list_with_pagination(self): columns, data = self.cmd.take_action(parsed_args) self.projects_mock.list.assert_called_with() - self.compute_sdk_client.usages.assert_has_calls( + self.compute_client.usages.assert_has_calls( [mock.call(start=mock.ANY, end=mock.ANY, detailed=True)] ) self.assertCountEqual(self.columns, columns) @@ -151,7 +151,7 @@ class TestUsageShow(TestUsage): def setUp(self): super().setUp() - self.compute_sdk_client.get_usage.return_value = self.usage + self.compute_client.get_usage.return_value = self.usage self.projects_mock.get.return_value = self.project # Get the command object to test @@ -194,7 +194,7 @@ def test_usage_show_with_options(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_sdk_client.get_usage.assert_called_with( + self.compute_client.get_usage.assert_called_with( project=self.project.id, start=datetime.datetime(2016, 11, 11, 0, 0), end=datetime.datetime(2016, 12, 20, 0, 0), diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py b/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py index d310f199a..89137d6e9 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py @@ -73,7 +73,7 @@ def test_floating_ip_create_default(self, fip_mock): columns, data = self.cmd.take_action(parsed_args) fip_mock.assert_called_once_with( - self.compute_sdk_client, self._floating_ip['pool'] + self.compute_client, self._floating_ip['pool'] ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -103,7 +103,7 @@ def test_floating_ip_delete(self, fip_mock): result = self.cmd.take_action(parsed_args) fip_mock.assert_called_once_with( - self.compute_sdk_client, self._floating_ips[0]['id'] + self.compute_client, self._floating_ips[0]['id'] ) self.assertIsNone(result) @@ -122,12 +122,8 @@ def test_floating_ip_delete_multi(self, fip_mock): fip_mock.assert_has_calls( [ - mock.call( - self.compute_sdk_client, self._floating_ips[0]['id'] - ), - mock.call( - self.compute_sdk_client, self._floating_ips[1]['id'] - ), + mock.call(self.compute_client, self._floating_ips[0]['id']), + mock.call(self.compute_client, self._floating_ips[1]['id']), ] ) self.assertIsNone(result) @@ -157,11 +153,9 @@ def test_floating_ip_delete_multi_exception(self, fip_mock): self.assertEqual('1 of 2 floating_ips failed to delete.', str(e)) fip_mock.assert_any_call( - self.compute_sdk_client, self._floating_ips[0]['id'] - ) - fip_mock.assert_any_call( - self.compute_sdk_client, 'unexist_floating_ip' + self.compute_client, self._floating_ips[0]['id'] ) + fip_mock.assert_any_call(self.compute_client, 'unexist_floating_ip') @mock.patch.object(compute_v2, 'list_floating_ips') @@ -203,7 +197,7 @@ def test_floating_ip_list(self, fip_mock): columns, data = self.cmd.take_action(parsed_args) - fip_mock.assert_called_once_with(self.compute_sdk_client) + fip_mock.assert_called_once_with(self.compute_client) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -248,7 +242,7 @@ def test_floating_ip_show(self, fip_mock): columns, data = self.cmd.take_action(parsed_args) fip_mock.assert_called_once_with( - self.compute_sdk_client, self._floating_ip['id'] + self.compute_client, self._floating_ip['id'] ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py b/openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py index 26fc3918e..90ded0628 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_pool_compute.py @@ -44,6 +44,6 @@ def test_floating_ip_list(self, fipp_mock): columns, data = self.cmd.take_action(parsed_args) - fipp_mock.assert_called_once_with(self.compute_sdk_client) + fipp_mock.assert_called_once_with(self.compute_client) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) diff --git a/openstackclient/tests/unit/network/v2/test_network_compute.py b/openstackclient/tests/unit/network/v2/test_network_compute.py index 045fa1f6b..e08e6baf0 100644 --- a/openstackclient/tests/unit/network/v2/test_network_compute.py +++ b/openstackclient/tests/unit/network/v2/test_network_compute.py @@ -148,7 +148,7 @@ def test_network_create_default_options(self, net_mock): columns, data = self.cmd.take_action(parsed_args) net_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, subnet=self._network['cidr'], name=self._network['label'], ) @@ -182,7 +182,7 @@ def test_network_delete_one(self, find_net_mock, delete_net_mock): result = self.cmd.take_action(parsed_args) delete_net_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, self._networks[0]['id'], ) self.assertIsNone(result) @@ -203,8 +203,8 @@ def test_network_delete_multi(self, find_net_mock, delete_net_mock): delete_net_mock.assert_has_calls( [ - mock.call(self.compute_sdk_client, self._networks[0]['id']), - mock.call(self.compute_sdk_client, self._networks[1]['id']), + mock.call(self.compute_client, self._networks[0]['id']), + mock.call(self.compute_client, self._networks[1]['id']), ] ) self.assertIsNone(result) @@ -238,15 +238,15 @@ def test_network_delete_multi_with_exception( find_net_mock.assert_has_calls( [ - mock.call(self.compute_sdk_client, self._networks[0]['id']), - mock.call(self.compute_sdk_client, 'xxxx-yyyy-zzzz'), - mock.call(self.compute_sdk_client, self._networks[1]['id']), + mock.call(self.compute_client, self._networks[0]['id']), + mock.call(self.compute_client, 'xxxx-yyyy-zzzz'), + mock.call(self.compute_client, self._networks[1]['id']), ] ) delete_net_mock.assert_has_calls( [ - mock.call(self.compute_sdk_client, self._networks[0]['id']), - mock.call(self.compute_sdk_client, self._networks[1]['id']), + mock.call(self.compute_client, self._networks[0]['id']), + mock.call(self.compute_client, self._networks[1]['id']), ] ) @@ -286,7 +286,7 @@ def test_network_list_no_options(self, net_mock): columns, data = self.cmd.take_action(parsed_args) - net_mock.assert_called_once_with(self.compute_sdk_client) + net_mock.assert_called_once_with(self.compute_client) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -398,7 +398,7 @@ def test_show_all_options(self, net_mock): columns, data = self.cmd.take_action(parsed_args) net_mock.assert_called_once_with( - self.compute_sdk_client, self._network['label'] + self.compute_client, self._network['label'] ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 7f5271d10..4484ec42e 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -1378,7 +1378,7 @@ def test_port_list_router_opt(self): def test_port_list_with_server_option(self): fake_server = compute_fakes.create_one_sdk_server() - self.compute_sdk_client.find_server.return_value = fake_server + self.compute_client.find_server.return_value = fake_server arglist = [ '--server', @@ -1393,7 +1393,7 @@ def test_port_list_with_server_option(self): self.network_client.ports.assert_called_once_with( device_id=fake_server.id, fields=LIST_FIELDS_TO_RETRIEVE ) - self.compute_sdk_client.find_server.aassert_called_once_with( + self.compute_client.find_server.aassert_called_once_with( mock.ANY, 'fake-server-name' ) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/unit/network/v2/test_security_group_compute.py b/openstackclient/tests/unit/network/v2/test_security_group_compute.py index 243f83021..e23bb2497 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_compute.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_compute.py @@ -72,7 +72,7 @@ def test_security_group_create_min_options(self, sg_mock): columns, data = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, self._security_group['name'], self._security_group['name'], ) @@ -95,7 +95,7 @@ def test_security_group_create_all_options(self, sg_mock): columns, data = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, self._security_group['name'], self._security_group['description'], ) @@ -133,7 +133,7 @@ def test_security_group_delete(self, sg_mock): result = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, self._security_groups[0]['id'], ) self.assertIsNone(result) @@ -153,12 +153,8 @@ def test_security_group_multi_delete(self, sg_mock): sg_mock.assert_has_calls( [ - mock.call( - self.compute_sdk_client, self._security_groups[0]['id'] - ), - mock.call( - self.compute_sdk_client, self._security_groups[1]['id'] - ), + mock.call(self.compute_client, self._security_groups[0]['id']), + mock.call(self.compute_client, self._security_groups[1]['id']), ] ) self.assertIsNone(result) @@ -187,9 +183,7 @@ def test_security_group_multi_delete_with_exception(self, sg_mock): sg_mock.assert_has_calls( [ - mock.call( - self.compute_sdk_client, self._security_groups[0]['id'] - ), + mock.call(self.compute_client, self._security_groups[0]['id']), ] ) @@ -250,7 +244,7 @@ def test_security_group_list_no_options(self, sg_mock): columns, data = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, all_projects=False + self.compute_client, all_projects=False ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) @@ -267,9 +261,7 @@ def test_security_group_list_all_projects(self, sg_mock): columns, data = self.cmd.take_action(parsed_args) - sg_mock.assert_called_once_with( - self.compute_sdk_client, all_projects=True - ) + sg_mock.assert_called_once_with(self.compute_client, all_projects=True) self.assertEqual(self.columns_all_projects, columns) self.assertCountEqual(self.data_all_projects, list(data)) @@ -309,7 +301,7 @@ def test_security_group_set_no_updates(self, sg_mock): result = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, self._security_group['id'] + self.compute_client, self._security_group['id'] ) self.assertIsNone(result) @@ -334,7 +326,7 @@ def test_security_group_set_all_options(self, sg_mock): result = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, self._security_group['id'], name=new_name, description=new_description, @@ -394,7 +386,7 @@ def test_security_group_show_all_options(self, sg_mock): columns, data = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self.compute_sdk_client, self._security_group['id'] + self.compute_client, self._security_group['id'] ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py b/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py index fc3c0ddb1..678ff8a30 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py @@ -159,7 +159,7 @@ def test_security_group_rule_create_default_rule(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) sgr_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], from_port=self._security_group_rule['from_port'], @@ -202,7 +202,7 @@ def test_security_group_rule_create_remote_group(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) sgr_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], from_port=self._security_group_rule['from_port'], @@ -240,7 +240,7 @@ def test_security_group_rule_create_remote_ip(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) sgr_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], from_port=self._security_group_rule['from_port'], @@ -279,7 +279,7 @@ def test_security_group_rule_create_proto_option(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) sgr_mock.assert_called_once_with( - self.compute_sdk_client, + self.compute_client, security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], from_port=self._security_group_rule['from_port'], @@ -316,7 +316,7 @@ def test_security_group_rule_delete(self, sgr_mock): result = self.cmd.take_action(parsed_args) sgr_mock.assert_called_once_with( - self.compute_sdk_client, self._security_group_rules[0]['id'] + self.compute_client, self._security_group_rules[0]['id'] ) self.assertIsNone(result) @@ -335,11 +335,11 @@ def test_security_group_rule_delete_multi(self, sgr_mock): sgr_mock.assert_has_calls( [ mock.call( - self.compute_sdk_client, + self.compute_client, self._security_group_rules[0]['id'], ), mock.call( - self.compute_sdk_client, + self.compute_client, self._security_group_rules[1]['id'], ), ] @@ -367,10 +367,10 @@ def test_security_group_rule_delete_multi_with_exception(self, sgr_mock): sgr_mock.assert_has_calls( [ mock.call( - self.compute_sdk_client, + self.compute_client, self._security_group_rules[0]['id'], ), - mock.call(self.compute_sdk_client, 'unexist_rule'), + mock.call(self.compute_client, 'unexist_rule'), ] ) @@ -457,7 +457,7 @@ def test_security_group_rule_list_default(self): columns, data = self.cmd.take_action(parsed_args) compute_v2.list_security_groups.assert_called_once_with( - self.compute_sdk_client, all_projects=False + self.compute_client, all_projects=False ) self.assertEqual(self.expected_columns_no_group, columns) self.assertEqual(self.expected_data_no_group, list(data)) @@ -473,7 +473,7 @@ def test_security_group_rule_list_with_group(self): columns, data = self.cmd.take_action(parsed_args) compute_v2.find_security_group.assert_called_once_with( - self.compute_sdk_client, self._security_group['id'] + self.compute_client, self._security_group['id'] ) self.assertEqual(self.expected_columns_with_group, columns) self.assertEqual(self.expected_data_with_group, list(data)) @@ -489,7 +489,7 @@ def test_security_group_rule_list_all_projects(self): columns, data = self.cmd.take_action(parsed_args) compute_v2.list_security_groups.assert_called_once_with( - self.compute_sdk_client, all_projects=True + self.compute_client, all_projects=True ) self.assertEqual(self.expected_columns_no_group, columns) self.assertEqual(self.expected_data_no_group, list(data)) @@ -505,7 +505,7 @@ def test_security_group_rule_list_with_ignored_options(self): columns, data = self.cmd.take_action(parsed_args) compute_v2.list_security_groups.assert_called_once_with( - self.compute_sdk_client, all_projects=False + self.compute_client, all_projects=False ) self.assertEqual(self.expected_columns_no_group, columns) self.assertEqual(self.expected_data_no_group, list(data)) @@ -551,7 +551,7 @@ def test_security_group_rule_show_all_options(self): columns, data = self.cmd.take_action(parsed_args) compute_v2.list_security_groups.assert_called_once_with( - self.compute_sdk_client + self.compute_client ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index 01519d70d..eb5c170f6 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -129,10 +129,8 @@ def setUp(self): # avoid circular imports by defining this manually rather than using # openstackclient.tests.unit.compute.v2.fakes.FakeClientMixin - # TODO(stephenfin): Rename to 'compute_client' once all commands are - # migrated to SDK self.app.client_manager.compute = mock.Mock(_compute_proxy.Proxy) - self.compute_sdk_client = self.app.client_manager.compute + self.compute_client = self.app.client_manager.compute # avoid circular imports by defining this manually rather than using # openstackclient.tests.unit.image.v2.fakes.FakeClientMixin diff --git a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py index aca2e3399..d4dc469e3 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py @@ -61,7 +61,7 @@ def setUp(self): self.volume_sdk_client.create_attachment.return_value = ( self.volume_attachment.to_dict() ) - self.compute_sdk_client.find_server.return_value = self.server + self.compute_client.find_server.return_value = self.server self.cmd = volume_attachment.CreateVolumeAttachment(self.app, None) @@ -92,7 +92,7 @@ def test_volume_attachment_create(self): self.volume_sdk_client.find_volume.assert_called_once_with( self.volume.id, ignore_missing=False ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.volume_sdk_client.create_attachment.assert_called_once_with( @@ -159,7 +159,7 @@ def test_volume_attachment_create_with_connect(self): self.volume_sdk_client.find_volume.assert_called_once_with( self.volume.id, ignore_missing=False ) - self.compute_sdk_client.find_server.assert_called_once_with( + self.compute_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False ) self.volume_sdk_client.create_attachment.assert_called_once_with( From 181bb194c7956fa2a909169a86980c55c55d39b7 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 15 Jul 2024 10:44:52 +0100 Subject: [PATCH 122/245] image: Migrate 'create image' volume calls to SDK Change-Id: Ie57a5c17a6df5a333abd6b11e28b65833740e102 Signed-off-by: Stephen Finucane --- openstackclient/image/v2/image.py | 22 ++--- .../tests/unit/image/v2/test_image.py | 92 +++++++++---------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 57f278dcd..7dbd1609f 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -17,14 +17,15 @@ import argparse from base64 import b64encode +import copy import logging import os import sys import typing as ty -from cinderclient import api_versions from openstack import exceptions as sdk_exceptions from openstack.image import image_signer +from openstack import utils as sdk_utils from osc_lib.api import utils as api_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions @@ -576,7 +577,7 @@ def _take_action_image(self, parsed_args): return _format_image(image) def _take_action_volume(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume unsupported_opts = { # 'name', # 'name' is a positional argument and will always exist @@ -607,15 +608,14 @@ def _take_action_volume(self, parsed_args): # version LOG.warning(msg % opt_name) - source_volume = utils.find_resource( - volume_client.volumes, - parsed_args.volume, + source_volume = volume_client.find_volume( + parsed_args.volume, ignore_missing=False ) kwargs: dict[str, ty.Any] = { 'visibility': None, 'protected': None, } - if volume_client.api_version < api_versions.APIVersion('3.1'): + if not sdk_utils.supports_microversion(volume_client, '3.1'): if parsed_args.visibility or parsed_args.is_protected is not None: msg = _( '--os-volume-api-version 3.1 or greater is required ' @@ -627,15 +627,15 @@ def _take_action_volume(self, parsed_args): kwargs['visibility'] = parsed_args.visibility or 'private' kwargs['protected'] = parsed_args.is_protected or False - response, body = volume_client.volumes.upload_to_image( + response = volume_client.upload_volume_to_image( source_volume.id, - parsed_args.force, parsed_args.name, - parsed_args.container_format, - parsed_args.disk_format, + force=parsed_args.force, + disk_format=parsed_args.disk_format, + container_format=parsed_args.container_format, **kwargs, ) - info = body['os-volume_upload_image'] + info = copy.deepcopy(response) try: info['volume_type'] = info['volume_type']['name'] except TypeError: diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index 049510c71..9b44c1578 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -17,8 +17,9 @@ import tempfile from unittest import mock -from cinderclient import api_versions +from openstack.block_storage.v2 import volume as _volume from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions @@ -37,12 +38,6 @@ def setUp(self): self.project_mock.reset_mock() self.domain_mock = self.identity_client.domains self.domain_mock.reset_mock() - self.volumes_mock = self.volume_client.volumes - fake_body = { - 'os-volume_upload_image': {'volume_type': {'name': 'fake_type'}} - } - self.volumes_mock.upload_to_image.return_value = (200, fake_body) - self.volumes_mock.reset_mock() class TestImageCreate(TestImage): @@ -312,7 +307,6 @@ def test_image_create_import(self, raw_input): columns, data = self.cmd.take_action(parsed_args) - # ImageManager.create(name=, **) self.image_client.create_image.assert_called_with( name=self.new_image.name, allow_duplicates=True, @@ -322,20 +316,19 @@ def test_image_create_import(self, raw_input): ) self.image_client.get_image.assert_called_once_with(self.new_image) - @mock.patch('osc_lib.utils.find_resource') @mock.patch('openstackclient.image.v2.image.get_data_from_stdin') - def test_image_create_from_volume(self, mock_get_data_f, mock_get_vol): - fake_vol_id = 'fake-volume-id' + def test_image_create_from_volume(self, mock_get_data_f): mock_get_data_f.return_value = None - class FakeVolume: - id = fake_vol_id - - mock_get_vol.return_value = FakeVolume() + volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = volume + self.volume_sdk_client.upload_volume_to_image.return_value = { + 'volume_type': {'name': 'fake_type'} + } arglist = [ '--volume', - fake_vol_id, + volume.id, self.new_image.name, ] verifylist = [ @@ -345,53 +338,60 @@ class FakeVolume: columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.upload_to_image.assert_called_with( - fake_vol_id, - False, + self.volume_sdk_client.upload_volume_to_image.assert_called_once_with( + volume.id, self.new_image.name, - 'bare', - 'raw', + force=False, + disk_format='raw', + container_format='bare', visibility=None, protected=None, ) - @mock.patch('osc_lib.utils.find_resource') @mock.patch('openstackclient.image.v2.image.get_data_from_stdin') - def test_image_create_from_volume_fail( - self, mock_get_data_f, mock_get_vol - ): - fake_vol_id = 'fake-volume-id' + def test_image_create_from_volume_pre_v31(self, mock_get_data_f): mock_get_data_f.return_value = None - class FakeVolume: - id = fake_vol_id - - mock_get_vol.return_value = FakeVolume() + volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = volume + self.volume_sdk_client.upload_volume_to_image.return_value = { + 'volume_type': {'name': 'fake_type'} + } - arglist = ['--volume', fake_vol_id, self.new_image.name, '--public'] + arglist = [ + '--volume', + volume.id, + self.new_image.name, + '--public', + ] verifylist = [ ('name', self.new_image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn('--os-volume-api-version 3.1 or greater ', str(exc)) - @mock.patch('osc_lib.utils.find_resource') @mock.patch('openstackclient.image.v2.image.get_data_from_stdin') - def test_image_create_from_volume_v31(self, mock_get_data_f, mock_get_vol): - self.volume_client.api_version = api_versions.APIVersion('3.1') + def test_image_create_from_volume_v31(self, mock_get_data_f): + self.set_volume_api_version('3.1') - fake_vol_id = 'fake-volume-id' mock_get_data_f.return_value = None - class FakeVolume: - id = fake_vol_id - - mock_get_vol.return_value = FakeVolume() + volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = volume + self.volume_sdk_client.upload_volume_to_image.return_value = { + 'volume_type': {'name': 'fake_type'} + } - arglist = ['--volume', fake_vol_id, self.new_image.name, '--public'] + arglist = [ + '--volume', + volume.id, + self.new_image.name, + '--public', + ] verifylist = [ ('name', self.new_image.name), ] @@ -399,12 +399,12 @@ class FakeVolume: columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.upload_to_image.assert_called_with( - fake_vol_id, - False, + self.volume_sdk_client.upload_volume_to_image.assert_called_once_with( + volume.id, self.new_image.name, - 'bare', - 'raw', + force=False, + disk_format='raw', + container_format='bare', visibility='public', protected=False, ) From 80eaa33ffe8ad2f5e931341d564f538a5a94618e Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 18 Jul 2024 13:24:04 +0100 Subject: [PATCH 123/245] volume: Make better use of argparse The latest in a series. Change-Id: I8273c817e38120ba0b25aebdbfa1c2872222765e Signed-off-by: Stephen Finucane --- .../tests/unit/volume/v2/test_volume.py | 94 +++++++++--------- .../tests/unit/volume/v3/test_volume.py | 99 +++++++++---------- openstackclient/volume/v2/volume.py | 71 +++++++++---- openstackclient/volume/v3/volume.py | 63 +++++++----- 4 files changed, 182 insertions(+), 145 deletions(-) diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index b293ee57d..5df96f27d 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -188,7 +188,7 @@ def test_volume_create_properties(self): self.new_volume.name, ] verifylist = [ - ('property', {'Alpha': 'a', 'Beta': 'b'}), + ('properties', {'Alpha': 'a', 'Beta': 'b'}), ('size', self.new_volume.size), ('name', self.new_volume.name), ] @@ -382,9 +382,7 @@ def test_volume_create_with_bootable_and_readonly(self, mock_wait): ] verifylist = [ ('bootable', True), - ('non_bootable', False), ('read_only', True), - ('read_write', False), ('size', self.new_volume.size), ('name', self.new_volume.name), ] @@ -427,9 +425,7 @@ def test_volume_create_with_nonbootable_and_readwrite(self, mock_wait): ] verifylist = [ ('bootable', False), - ('non_bootable', True), ('read_only', False), - ('read_write', True), ('size', self.new_volume.size), ('name', self.new_volume.name), ] @@ -481,9 +477,7 @@ def test_volume_create_with_bootable_and_readonly_fail( ] verifylist = [ ('bootable', True), - ('non_bootable', False), ('read_only', True), - ('read_write', False), ('size', self.new_volume.size), ('name', self.new_volume.name), ] @@ -532,9 +526,7 @@ def test_volume_create_non_available_with_readonly( ] verifylist = [ ('bootable', False), - ('non_bootable', True), ('read_only', True), - ('read_write', False), ('size', self.new_volume.size), ('name', self.new_volume.name), ] @@ -1407,16 +1399,16 @@ def test_volume_set_property(self): self.new_volume.id, ] verifylist = [ - ('property', {'a': 'b', 'c': 'd'}), + ('properties', {'a': 'b', 'c': 'd'}), ('volume', self.new_volume.id), - ('bootable', False), - ('non_bootable', False), + ('bootable', None), + ('read_only', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.volumes_mock.set_metadata.assert_called_with( - self.new_volume.id, parsed_args.property + self.new_volume.id, parsed_args.properties ) def test_volume_set_image_property(self): @@ -1428,10 +1420,10 @@ def test_volume_set_image_property(self): self.new_volume.id, ] verifylist = [ - ('image_property', {'Alpha': 'a', 'Beta': 'b'}), + ('image_properties', {'Alpha': 'a', 'Beta': 'b'}), ('volume', self.new_volume.id), - ('bootable', False), - ('non_bootable', False), + ('bootable', None), + ('read_only', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1439,14 +1431,13 @@ def test_volume_set_image_property(self): # returns nothing self.cmd.take_action(parsed_args) self.volumes_mock.set_image_metadata.assert_called_with( - self.new_volume.id, parsed_args.image_property + self.new_volume.id, parsed_args.image_properties ) def test_volume_set_state(self): arglist = ['--state', 'error', self.new_volume.id] verifylist = [ - ('read_only', False), - ('read_write', False), + ('read_only', None), ('state', 'error'), ('volume', self.new_volume.id), ] @@ -1511,36 +1502,40 @@ def test_volume_set_detached(self): def test_volume_set_bootable(self): arglist = [ - ['--bootable', self.new_volume.id], - ['--non-bootable', self.new_volume.id], + '--bootable', + self.new_volume.id, ] verifylist = [ - [ - ('bootable', True), - ('non_bootable', False), - ('volume', self.new_volume.id), - ], - [ - ('bootable', False), - ('non_bootable', True), - ('volume', self.new_volume.id), - ], - ] - for index in range(len(arglist)): - parsed_args = self.check_parser( - self.cmd, arglist[index], verifylist[index] - ) + ('bootable', True), + ('volume', self.new_volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, verifylist[index][0][1] - ) + self.cmd.take_action(parsed_args) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, verifylist[0][1] + ) + + def test_volume_set_non_bootable(self): + arglist = [ + '--non-bootable', + self.new_volume.id, + ] + verifylist = [ + ('bootable', False), + ('volume', self.new_volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, verifylist[0][1] + ) - def test_volume_set_readonly(self): + def test_volume_set_read_only(self): arglist = ['--read-only', self.new_volume.id] verifylist = [ ('read_only', True), - ('read_write', False), ('volume', self.new_volume.id), ] @@ -1556,7 +1551,6 @@ def test_volume_set_read_write(self): arglist = ['--read-write', self.new_volume.id] verifylist = [ ('read_only', False), - ('read_write', True), ('volume', self.new_volume.id), ] @@ -1686,7 +1680,7 @@ def test_volume_unset_image_property(self): self.new_volume.id, ] verifylist = [ - ('image_property', {'Alpha': 'a', 'Beta': 'b'}), + ('image_properties', {'Alpha': 'a', 'Beta': 'b'}), ('volume', self.new_volume.id), ] parsed_args = self.check_parser(self.cmd_set, arglist, verifylist) @@ -1702,7 +1696,7 @@ def test_volume_unset_image_property(self): self.new_volume.id, ] verifylist_unset = [ - ('image_property', ['Alpha']), + ('image_properties', ['Alpha']), ('volume', self.new_volume.id), ] parsed_args_unset = self.check_parser( @@ -1714,7 +1708,7 @@ def test_volume_unset_image_property(self): self.cmd_unset.take_action(parsed_args_unset) self.volumes_mock.delete_image_metadata.assert_called_with( - self.new_volume.id, parsed_args_unset.image_property + self.new_volume.id, parsed_args_unset.image_properties ) def test_volume_unset_image_property_fail(self): @@ -1729,8 +1723,8 @@ def test_volume_unset_image_property_fail(self): self.new_volume.id, ] verifylist = [ - ('image_property', ['Alpha']), - ('property', ['Beta']), + ('image_properties', ['Alpha']), + ('properties', ['Beta']), ('volume', self.new_volume.id), ] parsed_args = self.check_parser(self.cmd_unset, arglist, verifylist) @@ -1743,10 +1737,10 @@ def test_volume_unset_image_property_fail(self): 'One or more of the unset operations failed', str(e) ) self.volumes_mock.delete_image_metadata.assert_called_with( - self.new_volume.id, parsed_args.image_property + self.new_volume.id, parsed_args.image_properties ) self.volumes_mock.delete_metadata.assert_called_with( - self.new_volume.id, parsed_args.property + self.new_volume.id, parsed_args.properties ) diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index e3dc01665..6bf43d73f 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -179,7 +179,7 @@ def test_volume_create_properties(self): self.new_volume.name, ] verifylist = [ - ('property', {'Alpha': 'a', 'Beta': 'b'}), + ('properties', {'Alpha': 'a', 'Beta': 'b'}), ('size', self.new_volume.size), ('name', self.new_volume.name), ] @@ -440,9 +440,7 @@ def test_volume_create_with_bootable_and_readonly(self, mock_wait): ] verifylist = [ ('bootable', True), - ('non_bootable', False), ('read_only', True), - ('read_write', False), ('size', self.new_volume.size), ('name', self.new_volume.name), ] @@ -486,9 +484,7 @@ def test_volume_create_with_nonbootable_and_readwrite(self, mock_wait): ] verifylist = [ ('bootable', False), - ('non_bootable', True), ('read_only', False), - ('read_write', True), ('size', self.new_volume.size), ('name', self.new_volume.name), ] @@ -541,9 +537,7 @@ def test_volume_create_with_bootable_and_readonly_fail( ] verifylist = [ ('bootable', True), - ('non_bootable', False), ('read_only', True), - ('read_write', False), ('size', self.new_volume.size), ('name', self.new_volume.name), ] @@ -593,9 +587,7 @@ def test_volume_create_non_available_with_readonly( ] verifylist = [ ('bootable', False), - ('non_bootable', True), ('read_only', True), - ('read_write', False), ('size', self.new_volume.size), ('name', self.new_volume.name), ] @@ -838,7 +830,7 @@ def test_volume_create_remote_source(self): description=parsed_args.description, volume_type=parsed_args.type, availability_zone=parsed_args.availability_zone, - metadata=parsed_args.property, + metadata=parsed_args.properties, bootable=parsed_args.bootable, cluster=getattr(parsed_args, 'cluster', None), ) @@ -1815,16 +1807,16 @@ def test_volume_set_property(self): self.new_volume.id, ] verifylist = [ - ('property', {'a': 'b', 'c': 'd'}), + ('properties', {'a': 'b', 'c': 'd'}), + ('read_only', None), + ('bootable', None), ('volume', self.new_volume.id), - ('bootable', False), - ('non_bootable', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.volumes_mock.set_metadata.assert_called_with( - self.new_volume.id, parsed_args.property + self.new_volume.id, parsed_args.properties ) def test_volume_set_image_property(self): @@ -1836,10 +1828,10 @@ def test_volume_set_image_property(self): self.new_volume.id, ] verifylist = [ - ('image_property', {'Alpha': 'a', 'Beta': 'b'}), + ('image_properties', {'Alpha': 'a', 'Beta': 'b'}), + ('read_only', None), + ('bootable', None), ('volume', self.new_volume.id), - ('bootable', False), - ('non_bootable', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1847,15 +1839,15 @@ def test_volume_set_image_property(self): # returns nothing self.cmd.take_action(parsed_args) self.volumes_mock.set_image_metadata.assert_called_with( - self.new_volume.id, parsed_args.image_property + self.new_volume.id, parsed_args.image_properties ) def test_volume_set_state(self): arglist = ['--state', 'error', self.new_volume.id] verifylist = [ - ('read_only', False), - ('read_write', False), ('state', 'error'), + ('read_only', None), + ('bootable', None), ('volume', self.new_volume.id), ] @@ -1919,36 +1911,40 @@ def test_volume_set_detached(self): def test_volume_set_bootable(self): arglist = [ - ['--bootable', self.new_volume.id], - ['--non-bootable', self.new_volume.id], - ] - verifylist = [ - [ - ('bootable', True), - ('non_bootable', False), - ('volume', self.new_volume.id), - ], - [ - ('bootable', False), - ('non_bootable', True), - ('volume', self.new_volume.id), - ], - ] - for index in range(len(arglist)): - parsed_args = self.check_parser( - self.cmd, arglist[index], verifylist[index] - ) + '--bootable', + self.new_volume.id, + ] + verifylist = [ + ('bootable', True), + ('volume', self.new_volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, verifylist[index][0][1] - ) + self.cmd.take_action(parsed_args) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, verifylist[0][1] + ) + + def test_volume_set_non_bootable(self): + arglist = [ + '--non-bootable', + self.new_volume.id, + ] + verifylist = [ + ('bootable', False), + ('volume', self.new_volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, verifylist[0][1] + ) def test_volume_set_readonly(self): arglist = ['--read-only', self.new_volume.id] verifylist = [ ('read_only', True), - ('read_write', False), ('volume', self.new_volume.id), ] @@ -1964,7 +1960,6 @@ def test_volume_set_read_write(self): arglist = ['--read-write', self.new_volume.id] verifylist = [ ('read_only', False), - ('read_write', True), ('volume', self.new_volume.id), ] @@ -2100,7 +2095,7 @@ def test_volume_unset_image_property(self): self.new_volume.id, ] verifylist = [ - ('image_property', {'Alpha': 'a', 'Beta': 'b'}), + ('image_properties', {'Alpha': 'a', 'Beta': 'b'}), ('volume', self.new_volume.id), ] parsed_args = self.check_parser(self.cmd_set, arglist, verifylist) @@ -2116,7 +2111,7 @@ def test_volume_unset_image_property(self): self.new_volume.id, ] verifylist_unset = [ - ('image_property', ['Alpha']), + ('image_properties', ['Alpha']), ('volume', self.new_volume.id), ] parsed_args_unset = self.check_parser( @@ -2128,7 +2123,7 @@ def test_volume_unset_image_property(self): self.cmd_unset.take_action(parsed_args_unset) self.volumes_mock.delete_image_metadata.assert_called_with( - self.new_volume.id, parsed_args_unset.image_property + self.new_volume.id, parsed_args_unset.image_properties ) def test_volume_unset_image_property_fail(self): @@ -2143,8 +2138,8 @@ def test_volume_unset_image_property_fail(self): self.new_volume.id, ] verifylist = [ - ('image_property', ['Alpha']), - ('property', ['Beta']), + ('image_properties', ['Alpha']), + ('properties', ['Beta']), ('volume', self.new_volume.id), ] parsed_args = self.check_parser(self.cmd_unset, arglist, verifylist) @@ -2157,10 +2152,10 @@ def test_volume_unset_image_property_fail(self): 'One or more of the unset operations failed', str(e) ) self.volumes_mock.delete_image_metadata.assert_called_with( - self.new_volume.id, parsed_args.image_property + self.new_volume.id, parsed_args.image_properties ) self.volumes_mock.delete_metadata.assert_called_with( - self.new_volume.id, parsed_args.property + self.new_volume.id, parsed_args.properties ) diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 8b55ce2a8..193e82f01 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -169,6 +169,7 @@ def _get_parser(self, prog_name): "--property", metavar="", action=parseractions.KeyValueAction, + dest="properties", help=_( "Set a property to this volume " "(repeat option to set multiple properties)" @@ -189,22 +190,30 @@ def _get_parser(self, prog_name): bootable_group.add_argument( "--bootable", action="store_true", + dest="bootable", + default=None, help=_("Mark volume as bootable"), ) bootable_group.add_argument( "--non-bootable", - action="store_true", + action="store_false", + dest="bootable", + default=None, help=_("Mark volume as non-bootable (default)"), ) readonly_group = parser.add_mutually_exclusive_group() readonly_group.add_argument( "--read-only", action="store_true", + dest="read_only", + default=None, help=_("Set volume to read-only access mode"), ) readonly_group.add_argument( "--read-write", - action="store_true", + action="store_false", + dest="read_only", + default=None, help=_("Set volume to read-write access mode (default)"), ) return parser, source_group @@ -265,14 +274,14 @@ def take_action(self, parsed_args): description=parsed_args.description, volume_type=parsed_args.type, availability_zone=parsed_args.availability_zone, - metadata=parsed_args.property, + metadata=parsed_args.properties, imageRef=image, source_volid=source_volume, consistencygroup_id=consistency_group, scheduler_hints=parsed_args.hint, ) - if parsed_args.bootable or parsed_args.non_bootable: + if parsed_args.bootable is not None: try: if utils.wait_for_status( volume_client.volumes.get, @@ -291,7 +300,8 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) except Exception as e: LOG.error(_("Failed to set volume bootable property: %s"), e) - if parsed_args.read_only or parsed_args.read_write: + + if parsed_args.read_only is not None: try: if utils.wait_for_status( volume_client.volumes.get, @@ -622,6 +632,7 @@ def get_parser(self, prog_name): '--property', metavar='', action=parseractions.KeyValueAction, + dest="properties", help=_( 'Set a property on this volume ' '(repeat option to set multiple properties)' @@ -631,6 +642,7 @@ def get_parser(self, prog_name): '--image-property', metavar='', action=parseractions.KeyValueAction, + dest="image_properties", help=_( 'Set an image property on this volume ' '(repeat option to set multiple image properties)' @@ -707,22 +719,30 @@ def get_parser(self, prog_name): bootable_group.add_argument( "--bootable", action="store_true", + dest="bootable", + default=None, help=_("Mark volume as bootable"), ) bootable_group.add_argument( "--non-bootable", - action="store_true", + action="store_false", + dest="bootable", + default=None, help=_("Mark volume as non-bootable"), ) readonly_group = parser.add_mutually_exclusive_group() readonly_group.add_argument( "--read-only", action="store_true", + dest="read_only", + default=None, help=_("Set volume to read-only access mode"), ) readonly_group.add_argument( "--read-write", - action="store_true", + action="store_false", + dest="read_only", + default=None, help=_("Set volume to read-write access mode"), ) return parser @@ -771,28 +791,31 @@ def take_action(self, parsed_args): LOG.error(_("Failed to clean volume properties: %s"), e) result += 1 - if parsed_args.property: + if parsed_args.properties: try: volume_client.volumes.set_metadata( - volume.id, parsed_args.property + volume.id, parsed_args.properties ) except Exception as e: - LOG.error(_("Failed to set volume property: %s"), e) + LOG.error(_("Failed to set volume properties: %s"), e) result += 1 - if parsed_args.image_property: + + if parsed_args.image_properties: try: volume_client.volumes.set_image_metadata( - volume.id, parsed_args.image_property + volume.id, parsed_args.image_properties ) except Exception as e: - LOG.error(_("Failed to set image property: %s"), e) + LOG.error(_("Failed to set image properties: %s"), e) result += 1 + if parsed_args.state: try: volume_client.volumes.reset_state(volume.id, parsed_args.state) except Exception as e: LOG.error(_("Failed to set volume state: %s"), e) result += 1 + if parsed_args.attached: try: volume_client.volumes.reset_state( @@ -801,6 +824,7 @@ def take_action(self, parsed_args): except Exception as e: LOG.error(_("Failed to set volume attach-status: %s"), e) result += 1 + if parsed_args.detached: try: volume_client.volumes.reset_state( @@ -809,7 +833,8 @@ def take_action(self, parsed_args): except Exception as e: LOG.error(_("Failed to set volume attach-status: %s"), e) result += 1 - if parsed_args.bootable or parsed_args.non_bootable: + + if parsed_args.bootable is not None: try: volume_client.volumes.set_bootable( volume.id, parsed_args.bootable @@ -817,7 +842,8 @@ def take_action(self, parsed_args): except Exception as e: LOG.error(_("Failed to set volume bootable property: %s"), e) result += 1 - if parsed_args.read_only or parsed_args.read_write: + + if parsed_args.read_only is not None: try: volume_client.volumes.update_readonly_flag( volume.id, parsed_args.read_only @@ -828,6 +854,7 @@ def take_action(self, parsed_args): e, ) result += 1 + policy = parsed_args.migration_policy or parsed_args.retype_policy if parsed_args.type: # get the migration policy @@ -928,6 +955,7 @@ def get_parser(self, prog_name): '--property', metavar='', action='append', + dest='properties', help=_( 'Remove a property from volume ' '(repeat option to remove multiple properties)' @@ -937,6 +965,7 @@ def get_parser(self, prog_name): '--image-property', metavar='', action='append', + dest='image_properties', help=_( 'Remove an image property from volume ' '(repeat option to remove multiple image properties)' @@ -949,22 +978,22 @@ def take_action(self, parsed_args): volume = utils.find_resource(volume_client.volumes, parsed_args.volume) result = 0 - if parsed_args.property: + if parsed_args.properties: try: volume_client.volumes.delete_metadata( - volume.id, parsed_args.property + volume.id, parsed_args.properties ) except Exception as e: - LOG.error(_("Failed to unset volume property: %s"), e) + LOG.error(_("Failed to unset volume properties: %s"), e) result += 1 - if parsed_args.image_property: + if parsed_args.image_properties: try: volume_client.volumes.delete_image_metadata( - volume.id, parsed_args.image_property + volume.id, parsed_args.image_properties ) except Exception as e: - LOG.error(_("Failed to unset image property: %s"), e) + LOG.error(_("Failed to unset image properties: %s"), e) result += 1 if result > 0: diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 3a07a6534..64aa5f076 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -194,8 +194,7 @@ def take_action(self, parsed_args): parsed_args.size or parsed_args.consistency_group or parsed_args.hint - or parsed_args.read_only - or parsed_args.read_write + or parsed_args.read_only is not None ): msg = _( "The --size, --consistency-group, --hint, --read-only " @@ -232,7 +231,7 @@ def take_action(self, parsed_args): description=parsed_args.description, volume_type=parsed_args.type, availability_zone=parsed_args.availability_zone, - metadata=parsed_args.property, + metadata=parsed_args.properties, bootable=parsed_args.bootable, ) return zip(*sorted(volume.items())) @@ -287,7 +286,7 @@ def take_action(self, parsed_args): description=parsed_args.description, volume_type=parsed_args.type, availability_zone=parsed_args.availability_zone, - metadata=parsed_args.property, + metadata=parsed_args.properties, imageRef=image, source_volid=source_volume, consistencygroup_id=consistency_group, @@ -295,7 +294,7 @@ def take_action(self, parsed_args): backup_id=backup, ) - if parsed_args.bootable or parsed_args.non_bootable: + if parsed_args.bootable is not None: try: if utils.wait_for_status( volume_client.volumes.get, @@ -314,7 +313,8 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) except Exception as e: LOG.error(_("Failed to set volume bootable property: %s"), e) - if parsed_args.read_only or parsed_args.read_write: + + if parsed_args.read_only is not None: try: if utils.wait_for_status( volume_client.volumes.get, @@ -638,6 +638,7 @@ def get_parser(self, prog_name): '--property', metavar='', action=parseractions.KeyValueAction, + dest='properties', help=_( 'Set a property on this volume ' '(repeat option to set multiple properties)' @@ -647,6 +648,7 @@ def get_parser(self, prog_name): '--image-property', metavar='', action=parseractions.KeyValueAction, + dest='image_properties', help=_( 'Set an image property on this volume ' '(repeat option to set multiple image properties)' @@ -723,22 +725,30 @@ def get_parser(self, prog_name): bootable_group.add_argument( "--bootable", action="store_true", + dest="bootable", + default=None, help=_("Mark volume as bootable"), ) bootable_group.add_argument( "--non-bootable", - action="store_true", + action="store_false", + dest="bootable", + default=None, help=_("Mark volume as non-bootable"), ) readonly_group = parser.add_mutually_exclusive_group() readonly_group.add_argument( "--read-only", action="store_true", + dest="read_only", + default=None, help=_("Set volume to read-only access mode"), ) readonly_group.add_argument( "--read-write", - action="store_true", + action="store_false", + dest="read_only", + default=None, help=_("Set volume to read-write access mode"), ) return parser @@ -796,28 +806,31 @@ def take_action(self, parsed_args): LOG.error(_("Failed to clean volume properties: %s"), e) result += 1 - if parsed_args.property: + if parsed_args.properties: try: volume_client.volumes.set_metadata( - volume.id, parsed_args.property + volume.id, parsed_args.properties ) except Exception as e: - LOG.error(_("Failed to set volume property: %s"), e) + LOG.error(_("Failed to set volume properties: %s"), e) result += 1 - if parsed_args.image_property: + + if parsed_args.image_properties: try: volume_client.volumes.set_image_metadata( - volume.id, parsed_args.image_property + volume.id, parsed_args.image_properties ) except Exception as e: - LOG.error(_("Failed to set image property: %s"), e) + LOG.error(_("Failed to set image properties: %s"), e) result += 1 + if parsed_args.state: try: volume_client.volumes.reset_state(volume.id, parsed_args.state) except Exception as e: LOG.error(_("Failed to set volume state: %s"), e) result += 1 + if parsed_args.attached: try: volume_client.volumes.reset_state( @@ -826,6 +839,7 @@ def take_action(self, parsed_args): except Exception as e: LOG.error(_("Failed to set volume attach-status: %s"), e) result += 1 + if parsed_args.detached: try: volume_client.volumes.reset_state( @@ -834,7 +848,8 @@ def take_action(self, parsed_args): except Exception as e: LOG.error(_("Failed to set volume attach-status: %s"), e) result += 1 - if parsed_args.bootable or parsed_args.non_bootable: + + if parsed_args.bootable is not None: try: volume_client.volumes.set_bootable( volume.id, parsed_args.bootable @@ -842,7 +857,8 @@ def take_action(self, parsed_args): except Exception as e: LOG.error(_("Failed to set volume bootable property: %s"), e) result += 1 - if parsed_args.read_only or parsed_args.read_write: + + if parsed_args.read_only is not None: try: volume_client.volumes.update_readonly_flag( volume.id, parsed_args.read_only @@ -853,6 +869,7 @@ def take_action(self, parsed_args): e, ) result += 1 + policy = parsed_args.migration_policy or parsed_args.retype_policy if parsed_args.type: # get the migration policy @@ -953,6 +970,7 @@ def get_parser(self, prog_name): '--property', metavar='', action='append', + dest='properties', help=_( 'Remove a property from volume ' '(repeat option to remove multiple properties)' @@ -962,6 +980,7 @@ def get_parser(self, prog_name): '--image-property', metavar='', action='append', + dest='image_properties', help=_( 'Remove an image property from volume ' '(repeat option to remove multiple image properties)' @@ -974,22 +993,22 @@ def take_action(self, parsed_args): volume = utils.find_resource(volume_client.volumes, parsed_args.volume) result = 0 - if parsed_args.property: + if parsed_args.properties: try: volume_client.volumes.delete_metadata( - volume.id, parsed_args.property + volume.id, parsed_args.properties ) except Exception as e: - LOG.error(_("Failed to unset volume property: %s"), e) + LOG.error(_("Failed to unset volume properties: %s"), e) result += 1 - if parsed_args.image_property: + if parsed_args.image_properties: try: volume_client.volumes.delete_image_metadata( - volume.id, parsed_args.image_property + volume.id, parsed_args.image_properties ) except Exception as e: - LOG.error(_("Failed to unset image property: %s"), e) + LOG.error(_("Failed to unset image properties: %s"), e) result += 1 if result > 0: From f870548c7f391dc23ec5fc8b056b5db7d9c896bb Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 2 Apr 2025 14:47:40 +0100 Subject: [PATCH 124/245] Refactor network fakes to sdk properties PART 5 Included resources: qos_policy qos_rule qos_rule_type Note: Parameters in unittests was modified for compatibility with sdk Change-Id: Iaa902d64ff1b29a07c28ed2100d437da506be475 --- .../network/v2/network_qos_rule.py | 14 +- openstackclient/tests/functional/base.py | 6 +- .../tests/unit/network/v2/fakes.py | 454 +++++++++--------- .../network/v2/test_floating_ip_network.py | 18 +- .../tests/unit/network/v2/test_network.py | 8 +- .../network/v2/test_network_qos_policy.py | 35 +- .../unit/network/v2/test_network_qos_rule.py | 231 +++------ .../network/v2/test_network_qos_rule_type.py | 10 +- .../unit/network/v2/test_network_rbac.py | 4 +- .../tests/unit/network/v2/test_port.py | 4 +- .../tests/unit/network/v2/test_router.py | 24 +- 11 files changed, 345 insertions(+), 463 deletions(-) diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py index 83f787389..86f342f6e 100644 --- a/openstackclient/network/v2/network_qos_rule.py +++ b/openstackclient/network/v2/network_qos_rule.py @@ -73,7 +73,7 @@ def _get_columns(item): - hidden_columns = ['location', 'tenant_id'] + hidden_columns = ['location', 'name', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( item, {}, hidden_columns ) @@ -148,14 +148,6 @@ def _get_attrs(network_client, parsed_args, is_create=False): return attrs -def _get_item_properties(item, fields): - """Return a tuple containing the item properties.""" - row = [] - for field in fields: - row.append(item.get(field, '')) - return tuple(row) - - def _rule_action_call(client, action, rule_type): rule_type = rule_type.replace('-', '_') func_name = f'{action}_qos_{rule_type}_rule' @@ -357,10 +349,10 @@ def take_action(self, parsed_args): qos = client.find_qos_policy( parsed_args.qos_policy, ignore_missing=False ) - data = qos.rules + return ( column_headers, - (_get_item_properties(s, columns) for s in data), + (utils.get_dict_properties(s, columns) for s in qos.rules), ) diff --git a/openstackclient/tests/functional/base.py b/openstackclient/tests/functional/base.py index 97b2c1830..96c9accf6 100644 --- a/openstackclient/tests/functional/base.py +++ b/openstackclient/tests/functional/base.py @@ -97,7 +97,11 @@ def openstack( ) if parse_output: - return json.loads(output) + try: + return json.loads(output) + except json.JSONDecodeError: + print(f'failed to decode: {output}') + raise else: return output diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index d0b1c1ebb..1ae1a253b 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -31,6 +31,20 @@ from openstack.network.v2 import network_ip_availability as _ip_availability from openstack.network.v2 import network_segment_range as _segment_range from openstack.network.v2 import port as _port +from openstack.network.v2 import ( + qos_bandwidth_limit_rule as _qos_bandwidth_limit_rule, +) +from openstack.network.v2 import ( + qos_dscp_marking_rule as _qos_dscp_marking_rule, +) +from openstack.network.v2 import ( + qos_minimum_bandwidth_rule as _qos_minimum_bandwidth_rule, +) +from openstack.network.v2 import ( + qos_minimum_packet_rate_rule as _qos_minimum_packet_rate_rule, +) +from openstack.network.v2 import qos_policy as _qos_policy +from openstack.network.v2 import qos_rule_type as _qos_rule_type from openstack.network.v2 import rbac_policy as network_rbac from openstack.network.v2 import security_group as _security_group from openstack.network.v2 import segment as _segment @@ -118,245 +132,6 @@ def create_one_extension(attrs=None): return extension -class FakeNetworkQosPolicy: - """Fake one or more QoS policies.""" - - @staticmethod - def create_one_qos_policy(attrs=None): - """Create a fake QoS policy. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with name, id, etc. - """ - attrs = attrs or {} - qos_id = attrs.get('id') or 'qos-policy-id-' + uuid.uuid4().hex - rule_attrs = {'qos_policy_id': qos_id} - rules = [FakeNetworkQosRule.create_one_qos_rule(rule_attrs)] - - # Set default attributes. - qos_policy_attrs = { - 'name': 'qos-policy-name-' + uuid.uuid4().hex, - 'id': qos_id, - 'is_default': False, - 'project_id': 'project-id-' + uuid.uuid4().hex, - 'shared': False, - 'description': 'qos-policy-description-' + uuid.uuid4().hex, - 'rules': rules, - 'location': 'MUNCHMUNCHMUNCH', - } - - # Overwrite default attributes. - qos_policy_attrs.update(attrs) - - qos_policy = fakes.FakeResource( - info=copy.deepcopy(qos_policy_attrs), loaded=True - ) - - # Set attributes with special mapping in OpenStack SDK. - qos_policy.is_shared = qos_policy_attrs['shared'] - - return qos_policy - - @staticmethod - def create_qos_policies(attrs=None, count=2): - """Create multiple fake QoS policies. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of QoS policies to fake - :return: - A list of FakeResource objects faking the QoS policies - """ - qos_policies = [] - for i in range(0, count): - qos_policies.append( - FakeNetworkQosPolicy.create_one_qos_policy(attrs) - ) - - return qos_policies - - @staticmethod - def get_qos_policies(qos_policies=None, count=2): - """Get an iterable MagicMock object with a list of faked QoS policies. - - If qos policies list is provided, then initialize the Mock object - with the list. Otherwise create one. - - :param List qos_policies: - A list of FakeResource objects faking qos policies - :param int count: - The number of QoS policies to fake - :return: - An iterable Mock object with side_effect set to a list of faked - QoS policies - """ - if qos_policies is None: - qos_policies = FakeNetworkQosPolicy.create_qos_policies(count) - return mock.Mock(side_effect=qos_policies) - - -class FakeNetworkSecGroup: - """Fake one security group.""" - - @staticmethod - def create_one_security_group(attrs=None): - """Create a fake security group. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with name, id, etc. - """ - attrs = attrs or {} - sg_id = attrs.get('id') or 'security-group-id-' + uuid.uuid4().hex - - # Set default attributes. - security_group_attrs = { - 'name': 'security-group-name-' + uuid.uuid4().hex, - 'id': sg_id, - 'project_id': 'project-id-' + uuid.uuid4().hex, - 'description': 'security-group-description-' + uuid.uuid4().hex, - 'location': 'MUNCHMUNCHMUNCH', - } - - security_group = fakes.FakeResource( - info=copy.deepcopy(security_group_attrs), loaded=True - ) - - return security_group - - -class FakeNetworkQosRule: - """Fake one or more Network QoS rules.""" - - @staticmethod - def create_one_qos_rule(attrs=None): - """Create a fake Network QoS rule. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with name, id, etc. - """ - attrs = attrs or {} - - # Set default attributes. - type = attrs.get('type') or choice(VALID_QOS_RULES) - qos_rule_attrs = { - 'id': 'qos-rule-id-' + uuid.uuid4().hex, - 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, - 'project_id': 'project-id-' + uuid.uuid4().hex, - 'type': type, - 'location': 'MUNCHMUNCHMUNCH', - } - if type == RULE_TYPE_BANDWIDTH_LIMIT: - qos_rule_attrs['max_kbps'] = randint(1, 10000) - qos_rule_attrs['max_burst_kbits'] = randint(1, 10000) - qos_rule_attrs['direction'] = 'egress' - elif type == RULE_TYPE_DSCP_MARKING: - qos_rule_attrs['dscp_mark'] = choice(VALID_DSCP_MARKS) - elif type == RULE_TYPE_MINIMUM_BANDWIDTH: - qos_rule_attrs['min_kbps'] = randint(1, 10000) - qos_rule_attrs['direction'] = 'egress' - elif type == RULE_TYPE_MINIMUM_PACKET_RATE: - qos_rule_attrs['min_kpps'] = randint(1, 10000) - qos_rule_attrs['direction'] = 'egress' - - # Overwrite default attributes. - qos_rule_attrs.update(attrs) - - qos_rule = fakes.FakeResource( - info=copy.deepcopy(qos_rule_attrs), loaded=True - ) - - return qos_rule - - @staticmethod - def create_qos_rules(attrs=None, count=2): - """Create multiple fake Network QoS rules. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of Network QoS rule to fake - :return: - A list of FakeResource objects faking the Network QoS rules - """ - qos_rules = [] - for i in range(0, count): - qos_rules.append(FakeNetworkQosRule.create_one_qos_rule(attrs)) - return qos_rules - - @staticmethod - def get_qos_rules(qos_rules=None, count=2): - """Get a list of faked Network QoS rules. - - If Network QoS rules list is provided, then initialize the Mock - object with the list. Otherwise create one. - - :param List qos_rules: - A list of FakeResource objects faking Network QoS rules - :param int count: - The number of QoS minimum bandwidth rules to fake - :return: - An iterable Mock object with side_effect set to a list of faked - qos minimum bandwidth rules - """ - if qos_rules is None: - qos_rules = FakeNetworkQosRule.create_qos_rules(count) - return mock.Mock(side_effect=qos_rules) - - -class FakeNetworkQosRuleType: - """Fake one or more Network QoS rule types.""" - - @staticmethod - def create_one_qos_rule_type(attrs=None): - """Create a fake Network QoS rule type. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object with name, id, etc. - """ - attrs = attrs or {} - - # Set default attributes. - qos_rule_type_attrs = { - 'type': 'rule-type-' + uuid.uuid4().hex, - 'location': 'MUNCHMUNCHMUNCH', - } - - # Overwrite default attributes. - qos_rule_type_attrs.update(attrs) - - return fakes.FakeResource( - info=copy.deepcopy(qos_rule_type_attrs), loaded=True - ) - - @staticmethod - def create_qos_rule_types(attrs=None, count=2): - """Create multiple fake Network QoS rule types. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of QoS rule types to fake - :return: - A list of FakeResource objects faking the QoS rule types - """ - qos_rule_types = [] - for i in range(0, count): - qos_rule_types.append( - FakeNetworkQosRuleType.create_one_qos_rule_type(attrs) - ) - - return qos_rule_types - - class FakeRouter: """Fake one or more routers.""" @@ -1926,6 +1701,207 @@ def get_service_profile(flavor_profile=None, count=2): return mock.Mock(side_effect=flavor_profile) +def create_one_qos_policy(attrs=None): + """Create a fake QoS policy. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A QoSPolicy object with name, id, etc. + """ + attrs = attrs or {} + qos_id = attrs.get('id') or 'qos-policy-id-' + uuid.uuid4().hex + rules = [] + + # Set default attributes. + qos_policy_attrs = { + 'name': 'qos-policy-name-' + uuid.uuid4().hex, + 'id': qos_id, + 'is_default': False, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'shared': False, + 'description': 'qos-policy-description-' + uuid.uuid4().hex, + 'rules': rules, + 'location': 'MUNCHMUNCHMUNCH', + } + + # Overwrite default attributes. + qos_policy_attrs.update(attrs) + + qos_policy = _qos_policy.QoSPolicy(**qos_policy_attrs) + + return qos_policy + + +def create_qos_policies(attrs=None, count=2): + """Create multiple fake QoS policies. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of QoS policies to fake + :return: + A list of QoSPolicy objects faking the QoS policies + """ + qos_policies = [] + for i in range(0, count): + qos_policies.append(create_one_qos_policy(attrs)) + + return qos_policies + + +def get_qos_policies(qos_policies=None, count=2): + """Get an iterable MagicMock object with a list of faked QoS policies. + + If qos policies list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List qos_policies: + A list of QoSPolicy objects faking qos policies + :param int count: + The number of QoS policies to fake + :return: + An iterable Mock object with side_effect set to a list of faked + QoS policies + """ + if qos_policies is None: + qos_policies = create_qos_policies(count) + return mock.Mock(side_effect=qos_policies) + + +def create_one_qos_rule(attrs=None): + """Create a fake Network QoS rule. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A QoSRule object with id, type, etc. + """ + attrs = attrs or {} + + # Set default attributes. + type = attrs.get('type') or choice(VALID_QOS_RULES) + qos_rule_attrs = { + 'id': 'qos-rule-id-' + uuid.uuid4().hex, + 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'type': type, + 'location': 'MUNCHMUNCHMUNCH', + } + + if type == RULE_TYPE_BANDWIDTH_LIMIT: + qos_rule_attrs['max_kbps'] = randint(1, 10000) + qos_rule_attrs['max_burst_kbps'] = randint(1, 10000) + qos_rule_attrs['direction'] = 'egress' + + qos_rule_attrs.update(attrs) + qos_rule = _qos_bandwidth_limit_rule.QoSBandwidthLimitRule( + **qos_rule_attrs + ) + + elif type == RULE_TYPE_DSCP_MARKING: + qos_rule_attrs['dscp_mark'] = choice(VALID_DSCP_MARKS) + + qos_rule_attrs.update(attrs) + qos_rule = _qos_dscp_marking_rule.QoSDSCPMarkingRule(**qos_rule_attrs) + + elif type == RULE_TYPE_MINIMUM_BANDWIDTH: + qos_rule_attrs['min_kbps'] = randint(1, 10000) + qos_rule_attrs['direction'] = 'egress' + + qos_rule_attrs.update(attrs) + + qos_rule = _qos_minimum_bandwidth_rule.QoSMinimumBandwidthRule( + **qos_rule_attrs + ) + else: # type == RULE_TYPE_MINIMUM_PACKET_RATE: + qos_rule_attrs['min_kpps'] = randint(1, 10000) + qos_rule_attrs['direction'] = 'egress' + + qos_rule_attrs.update(attrs) + + qos_rule = _qos_minimum_packet_rate_rule.QoSMinimumPacketRateRule( + **qos_rule_attrs + ) + + return qos_rule + + +def create_qos_rules(attrs=None, count=2): + """Create multiple fake Network QoS rules. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of Network QoS rule to fake + :return: + A list of QoS Rules objects faking the Network QoS rules + """ + qos_rules = [] + for i in range(0, count): + qos_rules.append(create_one_qos_rule(attrs)) + return qos_rules + + +def get_qos_rules(qos_rules=None, count=2): + """Get a list of faked Network QoS rules. + + If Network QoS rules list is provided, then initialize the Mock + object with the list. Otherwise create one. + + :param List qos_rules: + A list of FakeResource objects faking Network QoS rules + :param int count: + The number of QoS minimum bandwidth rules to fake + :return: + An iterable Mock object with side_effect set to a list of faked + qos minimum bandwidth rules + """ + if qos_rules is None: + qos_rules = create_qos_rules(count) + return mock.Mock(side_effect=qos_rules) + + +def create_one_qos_rule_type(attrs=None): + """Create a fake Network QoS rule type. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A QoSRuleType object with name, id, etc. + """ + attrs = attrs or {} + + # Set default attributes. + qos_rule_type_attrs = { + 'type': 'rule-type-' + uuid.uuid4().hex, + 'location': 'MUNCHMUNCHMUNCH', + } + + # Overwrite default attributes. + qos_rule_type_attrs.update(attrs) + qos_rule_type = _qos_rule_type.QoSRuleType(**qos_rule_type_attrs) + + return qos_rule_type + + +def create_qos_rule_types(attrs=None, count=2): + """Create multiple fake Network QoS rule types. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of QoS rule types to fake + :return: + A list of QoSRuleType objects faking the QoS rule types + """ + qos_rule_types = [] + for i in range(0, count): + qos_rule_types.append(create_one_qos_rule_type(attrs)) + + return qos_rule_types + + def create_one_local_ip(attrs=None): """Create a fake local ip. diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py index dcdeea962..03d21cffc 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py @@ -234,10 +234,8 @@ def test_floating_ip_create_project_domain(self): self.assertEqual(self.data, data) def test_create_floating_ip_with_qos(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy arglist = [ '--qos-policy', qos_policy.id, @@ -893,10 +891,8 @@ def test_description_option(self): ) def test_qos_policy_option(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy arglist = [ "--qos-policy", qos_policy.id, @@ -922,10 +918,8 @@ def test_qos_policy_option(self): ) def test_port_and_qos_policy_option(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + qos_policy = network_fakes.create_one_qos_policy() + self.network_client.find_qos_policy.return_value = qos_policy arglist = [ "--qos-policy", qos_policy.id, diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index 0222be68d..f809cad0a 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -47,7 +47,7 @@ class TestCreateNetworkIdentityV3(TestNetwork): 'availability_zone_hints': ["nova"], } ) - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy( + qos_policy = network_fakes.create_one_qos_policy( attrs={'id': _network.qos_policy_id} ) @@ -622,7 +622,7 @@ def setUp(self): self.network_client.networks = mock.Mock(return_value=self._network) self._agent = network_fakes.create_one_network_agent() - self.network_client.get_agent = mock.Mock(return_value=self._agent) + self.network_client.get_agent.return_value = self._agent self.network_client.dhcp_agent_hosting_networks = mock.Mock( return_value=self._network @@ -967,7 +967,7 @@ def test_list_with_tag_options(self): class TestSetNetwork(TestNetwork): # The network to set. _network = network_fakes.create_one_network({'tags': ['green', 'red']}) - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy( + qos_policy = network_fakes.create_one_qos_policy( attrs={'id': _network.qos_policy_id} ) @@ -1266,7 +1266,7 @@ def test_show_all_options(self): class TestUnsetNetwork(TestNetwork): # The network to set. _network = network_fakes.create_one_network({'tags': ['green', 'red']}) - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy( + qos_policy = network_fakes.create_one_qos_policy( attrs={'id': _network.qos_policy_id} ) diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_policy.py b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py index 0a7cbb980..fae5b69d5 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_policy.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py @@ -35,11 +35,10 @@ class TestCreateNetworkQosPolicy(TestQosPolicy): project = identity_fakes_v3.FakeProject.create_one_project() # The new qos policy created. - new_qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy( - attrs={ - 'project_id': project.id, - } + new_qos_policy = network_fakes.create_one_qos_policy( + attrs={'project_id': project.id} ) + columns = ( 'description', 'id', @@ -48,6 +47,7 @@ class TestCreateNetworkQosPolicy(TestQosPolicy): 'project_id', 'rules', 'shared', + 'tags', ) data = ( @@ -57,7 +57,8 @@ class TestCreateNetworkQosPolicy(TestQosPolicy): new_qos_policy.name, new_qos_policy.project_id, new_qos_policy.rules, - new_qos_policy.shared, + new_qos_policy.is_shared, + new_qos_policy.tags, ) def setUp(self): @@ -158,17 +159,13 @@ def test_create_no_default(self): class TestDeleteNetworkQosPolicy(TestQosPolicy): # The address scope to delete. - _qos_policies = network_fakes.FakeNetworkQosPolicy.create_qos_policies( - count=2 - ) + _qos_policies = network_fakes.create_qos_policies(count=2) def setUp(self): super().setUp() self.network_client.delete_qos_policy = mock.Mock(return_value=None) - self.network_client.find_qos_policy = ( - network_fakes.FakeNetworkQosPolicy.get_qos_policies( - qos_policies=self._qos_policies - ) + self.network_client.find_qos_policy = network_fakes.get_qos_policies( + qos_policies=self._qos_policies ) # Get the command object to test @@ -245,9 +242,7 @@ def test_multi_qos_policies_delete_with_exception(self): class TestListNetworkQosPolicy(TestQosPolicy): # The QoS policies to list up. - qos_policies = network_fakes.FakeNetworkQosPolicy.create_qos_policies( - count=3 - ) + qos_policies = network_fakes.create_qos_policies(count=3) columns = ( 'ID', 'Name', @@ -261,7 +256,7 @@ class TestListNetworkQosPolicy(TestQosPolicy): ( qos_policy.id, qos_policy.name, - qos_policy.shared, + qos_policy.is_shared, qos_policy.is_default, qos_policy.project_id, ) @@ -345,7 +340,7 @@ def test_network_qos_list_project(self): class TestSetNetworkQosPolicy(TestQosPolicy): # The QoS policy to set. - _qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + _qos_policy = network_fakes.create_one_qos_policy() def setUp(self): super().setUp() @@ -428,7 +423,7 @@ def test_set_no_share_no_default(self): class TestShowNetworkQosPolicy(TestQosPolicy): # The QoS policy to show. - _qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + _qos_policy = network_fakes.create_one_qos_policy() columns = ( 'description', 'id', @@ -437,6 +432,7 @@ class TestShowNetworkQosPolicy(TestQosPolicy): 'project_id', 'rules', 'shared', + 'tags', ) data = ( _qos_policy.description, @@ -445,7 +441,8 @@ class TestShowNetworkQosPolicy(TestQosPolicy): _qos_policy.name, _qos_policy.project_id, network_qos_policy.RulesColumn(_qos_policy.rules), - _qos_policy.shared, + _qos_policy.is_shared, + _qos_policy.tags, ) def setUp(self): diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py index 39e636873..e6586849e 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py @@ -14,6 +14,7 @@ # under the License. from unittest import mock +import uuid from osc_lib import exceptions @@ -54,9 +55,7 @@ class TestNetworkQosRule(network_fakes.TestNetworkV2): def setUp(self): super().setUp() - self.qos_policy = ( - network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - ) + self.qos_policy = network_fakes.create_one_qos_policy() self.network_client.find_qos_policy = mock.Mock( return_value=self.qos_policy ) @@ -72,15 +71,12 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_BANDWIDTH, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.columns = ( 'direction', 'id', 'min_kbps', 'project_id', - 'qos_policy_id', 'type', ) @@ -89,7 +85,6 @@ def setUp(self): self.new_rule.id, self.new_rule.min_kbps, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) self.network_client.create_qos_minimum_bandwidth_rule = mock.Mock( @@ -179,15 +174,12 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_PACKET_RATE, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.columns = ( 'direction', 'id', 'min_kpps', 'project_id', - 'qos_policy_id', 'type', ) @@ -196,7 +188,6 @@ def setUp(self): self.new_rule.id, self.new_rule.min_kpps, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) self.network_client.create_qos_minimum_packet_rate_rule = mock.Mock( @@ -286,14 +277,11 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_DSCP_MARKING, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.columns = ( 'dscp_mark', 'id', 'project_id', - 'qos_policy_id', 'type', ) @@ -301,7 +289,6 @@ def setUp(self): self.new_rule.dscp_mark, self.new_rule.id, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) self.network_client.create_qos_dscp_marking_rule = mock.Mock( @@ -384,26 +371,22 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.columns = ( 'direction', 'id', - 'max_burst_kbits', + 'max_burst_kbps', 'max_kbps', 'project_id', - 'qos_policy_id', 'type', ) self.data = ( self.new_rule.direction, self.new_rule.id, - self.new_rule.max_burst_kbits, + self.new_rule.max_burst_kbps, self.new_rule.max_kbps, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) self.network_client.create_qos_bandwidth_limit_rule = mock.Mock( @@ -443,20 +426,19 @@ def test_create_default_options(self): ('qos_policy', self.new_rule.qos_policy_id), ] - rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( + rule = network_fakes.create_one_qos_rule( { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, } ) - rule.max_burst_kbits = 0 + rule.max_burst_kbps = 0 expected_data = ( rule.direction, rule.id, - rule.max_burst_kbits, + rule.max_burst_kbps, rule.max_kbps, rule.project_id, - rule.qos_policy_id, rule.type, ) @@ -485,7 +467,7 @@ def test_create_all_options(self): '--max-kbps', str(self.new_rule.max_kbps), '--max-burst-kbits', - str(self.new_rule.max_burst_kbits), + str(self.new_rule.max_burst_kbps), '--egress', self.new_rule.qos_policy_id, ] @@ -493,7 +475,7 @@ def test_create_all_options(self): verifylist = [ ('type', RULE_TYPE_BANDWIDTH_LIMIT), ('max_kbps', self.new_rule.max_kbps), - ('max_burst_kbits', self.new_rule.max_burst_kbits), + ('max_burst_kbits', self.new_rule.max_burst_kbps), ('egress', True), ('qos_policy', self.new_rule.qos_policy_id), ] @@ -505,7 +487,7 @@ def test_create_all_options(self): self.qos_policy.id, **{ 'max_kbps': self.new_rule.max_kbps, - 'max_burst_kbps': self.new_rule.max_burst_kbits, + 'max_burst_kbps': self.new_rule.max_burst_kbps, 'direction': self.new_rule.direction, }, ) @@ -545,17 +527,13 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_BANDWIDTH, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.network_client.delete_qos_minimum_bandwidth_rule = mock.Mock( return_value=None ) self.network_client.find_qos_minimum_bandwidth_rule = ( - network_fakes.FakeNetworkQosRule.get_qos_rules( - qos_rules=self.new_rule - ) + network_fakes.get_qos_rules(qos_rules=self.new_rule) ) # Get the command object to test @@ -612,17 +590,13 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_PACKET_RATE, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.network_client.delete_qos_minimum_packet_rate_rule = mock.Mock( return_value=None ) self.network_client.find_qos_minimum_packet_rate_rule = ( - network_fakes.FakeNetworkQosRule.get_qos_rules( - qos_rules=self.new_rule - ) + network_fakes.get_qos_rules(qos_rules=self.new_rule) ) # Get the command object to test @@ -679,17 +653,13 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_DSCP_MARKING, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.network_client.delete_qos_dscp_marking_rule = mock.Mock( return_value=None ) self.network_client.find_qos_dscp_marking_rule = ( - network_fakes.FakeNetworkQosRule.get_qos_rules( - qos_rules=self.new_rule - ) + network_fakes.get_qos_rules(qos_rules=self.new_rule) ) # Get the command object to test @@ -746,17 +716,13 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.network_client.delete_qos_bandwidth_limit_rule = mock.Mock( return_value=None ) self.network_client.find_qos_bandwidth_limit_rule = ( - network_fakes.FakeNetworkQosRule.get_qos_rules( - qos_rules=self.new_rule - ) + network_fakes.get_qos_rules(qos_rules=self.new_rule) ) # Get the command object to test @@ -813,9 +779,7 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_BANDWIDTH, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs=attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.network_client.update_qos_minimum_bandwidth_rule = mock.Mock( return_value=None @@ -917,9 +881,7 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_PACKET_RATE, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs=attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.network_client.update_qos_minimum_packet_rate_rule = mock.Mock( return_value=None @@ -1021,9 +983,7 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_DSCP_MARKING, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs=attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.network_client.update_qos_dscp_marking_rule = mock.Mock( return_value=None @@ -1124,21 +1084,14 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs=attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.update_qos_bandwidth_limit_rule = mock.Mock( - return_value=None - ) - self.network_client.find_qos_bandwidth_limit_rule = mock.Mock( - return_value=self.new_rule - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy + self.network_client.update_qos_bandwidth_limit_rule.return_value = None + self.network_client.find_qos_bandwidth_limit_rule.return_value = ( + self.new_rule ) + self.network_client.find_qos_policy.return_value = self.qos_policy - # Get the command object to test self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) def test_set_nothing(self): @@ -1203,23 +1156,23 @@ def test_set_max_burst_kbits_to_zero(self): self._set_max_burst_kbits(max_burst_kbits=0) def _reset_max_burst_kbits(self, max_burst_kbits): - self.new_rule.max_burst_kbits = max_burst_kbits + self.new_rule.max_burst_kbps = max_burst_kbits def _set_max_burst_kbits(self, max_burst_kbits=None): if max_burst_kbits: self.addCleanup( - self._reset_max_burst_kbits, self.new_rule.max_burst_kbits + self._reset_max_burst_kbits, self.new_rule.max_burst_kbps ) - self.new_rule.max_burst_kbits = max_burst_kbits + self.new_rule.max_burst_kbps = max_burst_kbits arglist = [ '--max-burst-kbits', - str(self.new_rule.max_burst_kbits), + str(self.new_rule.max_burst_kbps), self.new_rule.qos_policy_id, self.new_rule.id, ] verifylist = [ - ('max_burst_kbits', self.new_rule.max_burst_kbits), + ('max_burst_kbits', self.new_rule.max_burst_kbps), ('qos_policy', self.new_rule.qos_policy_id), ('id', self.new_rule.id), ] @@ -1228,7 +1181,7 @@ def _set_max_burst_kbits(self, max_burst_kbits=None): result = self.cmd.take_action(parsed_args) attrs = { - 'max_burst_kbps': self.new_rule.max_burst_kbits, + 'max_burst_kbps': self.new_rule.max_burst_kbps, } self.network_client.update_qos_bandwidth_limit_rule.assert_called_with( self.new_rule, self.qos_policy.id, **attrs @@ -1297,43 +1250,36 @@ def test_set_wrong_options(self): class TestListNetworkQosRule(TestNetworkQosRule): def setUp(self): super().setUp() - attrs = { - 'qos_policy_id': self.qos_policy.id, - 'type': RULE_TYPE_MINIMUM_BANDWIDTH, - } - self.new_rule_min_bw = ( - network_fakes.FakeNetworkQosRule.create_one_qos_rule(attrs=attrs) - ) - attrs['type'] = RULE_TYPE_MINIMUM_PACKET_RATE - self.new_rule_min_pps = ( - network_fakes.FakeNetworkQosRule.create_one_qos_rule(attrs=attrs) - ) - attrs['type'] = RULE_TYPE_DSCP_MARKING - self.new_rule_dscp_mark = ( - network_fakes.FakeNetworkQosRule.create_one_qos_rule(attrs=attrs) - ) - attrs['type'] = RULE_TYPE_BANDWIDTH_LIMIT - self.new_rule_max_bw = ( - network_fakes.FakeNetworkQosRule.create_one_qos_rule(attrs=attrs) - ) self.qos_policy.rules = [ - self.new_rule_min_bw, - self.new_rule_min_pps, - self.new_rule_dscp_mark, - self.new_rule_max_bw, + { + 'max_kbps': 1024, + 'max_burst_kbps': 1024, + 'direction': 'egress', + 'id': 'qos-rule-id-' + uuid.uuid4().hex, + 'qos_policy_id': self.qos_policy.id, + 'type': 'bandwidth_limit', + }, + { + 'dscp_mark': 0, + 'id': 'qos-rule-id-' + uuid.uuid4().hex, + 'qos_policy_id': self.qos_policy.id, + 'type': 'dscp_marking', + }, + { + 'min_kbps': 1024, + 'direction': 'egress', + 'id': 'qos-rule-id-' + uuid.uuid4().hex, + 'qos_policy_id': self.qos_policy.id, + 'type': 'minimum_bandwidth', + }, + { + 'min_kpps': 2800, + 'direction': 'egress', + 'id': 'qos-rule-id-' + uuid.uuid4().hex, + 'qos_policy_id': self.qos_policy.id, + 'type': 'minimum_packet_rate', + }, ] - self.network_client.find_qos_minimum_bandwidth_rule = mock.Mock( - return_value=self.new_rule_min_bw - ) - self.network_client.find_qos_minimum_packet_rate_rule = mock.Mock( - return_value=self.new_rule_min_pps - ) - self.network_client.find_qos_dscp_marking_rule = mock.Mock( - return_value=self.new_rule_dscp_mark - ) - self.network_client.find_qos_bandwidth_limit_rule = mock.Mock( - return_value=self.new_rule_max_bw - ) self.columns = ( 'ID', 'QoS Policy ID', @@ -1349,20 +1295,17 @@ def setUp(self): for index in range(len(self.qos_policy.rules)): self.data.append( ( - self.qos_policy.rules[index].id, - self.qos_policy.rules[index].qos_policy_id, - self.qos_policy.rules[index].type, - getattr(self.qos_policy.rules[index], 'max_kbps', ''), - getattr( - self.qos_policy.rules[index], 'max_burst_kbps', '' - ), - getattr(self.qos_policy.rules[index], 'min_kbps', ''), - getattr(self.qos_policy.rules[index], 'min_kpps', ''), - getattr(self.qos_policy.rules[index], 'dscp_mark', ''), - getattr(self.qos_policy.rules[index], 'direction', ''), + self.qos_policy.rules[index]['id'], + self.qos_policy.id, + self.qos_policy.rules[index]['type'], + self.qos_policy.rules[index].get('max_kbps', ''), + self.qos_policy.rules[index].get('max_burst_kbps', ''), + self.qos_policy.rules[index].get('min_kbps', ''), + self.qos_policy.rules[index].get('min_kpps', ''), + self.qos_policy.rules[index].get('dscp_mark', ''), + self.qos_policy.rules[index].get('direction', ''), ) ) - # Get the command object to test self.cmd = network_qos_rule.ListNetworkQosRule(self.app, None) def test_qos_rule_list(self): @@ -1391,16 +1334,13 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_BANDWIDTH, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.columns = ( 'direction', 'id', 'min_kbps', 'project_id', - 'qos_policy_id', 'type', ) self.data = ( @@ -1408,7 +1348,6 @@ def setUp(self): self.new_rule.id, self.new_rule.min_kbps, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) @@ -1459,16 +1398,13 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_PACKET_RATE, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.columns = ( 'direction', 'id', 'min_kpps', 'project_id', - 'qos_policy_id', 'type', ) self.data = ( @@ -1476,7 +1412,6 @@ def setUp(self): self.new_rule.id, self.new_rule.min_kpps, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) @@ -1527,22 +1462,18 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_DSCP_MARKING, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.columns = ( 'dscp_mark', 'id', 'project_id', - 'qos_policy_id', 'type', ) self.data = ( self.new_rule.dscp_mark, self.new_rule.id, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) @@ -1593,26 +1524,22 @@ def setUp(self): 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, } - self.new_rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule( - attrs - ) + self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] self.columns = ( 'direction', 'id', - 'max_burst_kbits', + 'max_burst_kbps', 'max_kbps', 'project_id', - 'qos_policy_id', 'type', ) self.data = ( self.new_rule.direction, self.new_rule.id, - self.new_rule.max_burst_kbits, + self.new_rule.max_burst_kbps, self.new_rule.max_kbps, self.new_rule.project_id, - self.new_rule.qos_policy_id, self.new_rule.type, ) diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py b/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py index 4838df2e8..c0aafa0bc 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py @@ -28,9 +28,8 @@ def setUp(self): class TestShowNetworkQosRuleType(TestNetworkQosRuleType): attrs = {'drivers': [{'name': 'driver 1', 'supported_parameters': []}]} # The QoS policies to show. - qos_rule_type = ( - network_fakes.FakeNetworkQosRuleType.create_one_qos_rule_type(attrs) - ) + qos_rule_type = network_fakes.create_one_qos_rule_type(attrs) + columns = ('drivers', 'rule_type_name') columns = ('drivers', 'rule_type_name') data = [qos_rule_type.drivers, qos_rule_type.type] @@ -76,9 +75,8 @@ def test_show_all_options(self): class TestListNetworkQosRuleType(TestNetworkQosRuleType): # The QoS policies to list up. - qos_rule_types = ( - network_fakes.FakeNetworkQosRuleType.create_qos_rule_types(count=3) - ) + qos_rule_types = network_fakes.create_qos_rule_types(count=3) + columns = ('Type',) data = [] for qos_rule_type in qos_rule_types: diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py index c13b72bb4..29b3147e1 100644 --- a/openstackclient/tests/unit/network/v2/test_network_rbac.py +++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py @@ -34,8 +34,8 @@ def setUp(self): @ddt.ddt class TestCreateNetworkRBAC(TestNetworkRBAC): network_object = network_fakes.create_one_network() - qos_object = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - sg_object = network_fakes.FakeNetworkSecGroup.create_one_security_group() + qos_object = network_fakes.create_one_qos_policy() + sg_object = network_fakes.FakeSecurityGroup.create_one_security_group() as_object = network_fakes.create_one_address_scope() snp_object = network_fakes.FakeSubnetPool.create_one_subnet_pool() ag_object = network_fakes.create_one_address_group() diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 4484ec42e..2127e7885 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -582,7 +582,7 @@ def test_create_port_with_allowed_address_pair(self): self.assertCountEqual(self.data, data) def test_create_port_with_qos(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + qos_policy = network_fakes.create_one_qos_policy() self.network_client.find_qos_policy = mock.Mock( return_value=qos_policy ) @@ -2371,7 +2371,7 @@ def test_set_port_security_disabled(self): ) def test_set_port_with_qos(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + qos_policy = network_fakes.create_one_qos_policy() self.network_client.find_qos_policy = mock.Mock( return_value=qos_policy ) diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 70993639c..286f57726 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -555,9 +555,7 @@ def test_create_with_enable_default_route_ecmp_no_extension(self): def test_create_with_qos_policy(self): _network = network_fakes.create_one_network() self.network_client.find_network = mock.Mock(return_value=_network) - _qos_policy = ( - network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - ) + _qos_policy = network_fakes.create_one_qos_policy() self.network_client.find_qos_policy = mock.Mock( return_value=_qos_policy ) @@ -590,9 +588,7 @@ def test_create_with_qos_policy(self): self.assertCountEqual(self.data, data) def test_create_with_qos_policy_no_external_gateway(self): - _qos_policy = ( - network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - ) + _qos_policy = network_fakes.create_one_qos_policy() self.network_client.find_qos_policy = mock.Mock( return_value=_qos_policy ) @@ -1668,7 +1664,7 @@ def test_set_with_no_tag(self): self._test_set_tags(with_tags=False) def test_set_gateway_ip_qos(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + qos_policy = network_fakes.create_one_qos_policy() self.network_client.find_qos_policy = mock.Mock( return_value=qos_policy ) @@ -1725,7 +1721,7 @@ def test_unset_gateway_ip_qos(self): self.assertIsNone(result) def test_set_unset_gateway_ip_qos(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + qos_policy = network_fakes.create_one_qos_policy() self.network_client.find_qos_policy = mock.Mock( return_value=qos_policy ) @@ -1753,7 +1749,7 @@ def test_set_unset_gateway_ip_qos(self): ) def test_set_gateway_ip_qos_no_gateway(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + qos_policy = network_fakes.create_one_qos_policy() self.network_client.find_qos_policy = mock.Mock( return_value=qos_policy ) @@ -1775,7 +1771,7 @@ def test_set_gateway_ip_qos_no_gateway(self): ) def test_unset_gateway_ip_qos_no_gateway(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + qos_policy = network_fakes.create_one_qos_policy() self.network_client.find_qos_policy = mock.Mock( return_value=qos_policy ) @@ -1932,9 +1928,7 @@ class TestUnsetRouter(TestRouter): def setUp(self): super().setUp() self.fake_network = network_fakes.create_one_network() - self.fake_qos_policy = ( - network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() - ) + self.fake_qos_policy = network_fakes.create_one_qos_policy() self._testrouter = network_fakes.FakeRouter.create_one_router( { 'routes': [ @@ -2102,7 +2096,7 @@ def test_unset_router_qos_policy(self): self.assertIsNone(result) def test_unset_gateway_ip_qos_no_network(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + qos_policy = network_fakes.create_one_qos_policy() self.network_client.find_qos_policy = mock.Mock( return_value=qos_policy ) @@ -2122,7 +2116,7 @@ def test_unset_gateway_ip_qos_no_network(self): ) def test_unset_gateway_ip_qos_no_qos(self): - qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + qos_policy = network_fakes.create_one_qos_policy() self.network_client.find_qos_policy = mock.Mock( return_value=qos_policy ) From d96c81ff7f3eeb94cca83313a5435c0131a9f951 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 3 Apr 2025 16:19:23 +0100 Subject: [PATCH 125/245] Refactor network fakes to sdk properties PART6 Included resources: router security_group security_group_rule Change-Id: I2423fc31d94f85aeefc7b0a205dfb38829417a29 --- openstackclient/network/v2/router.py | 2 +- openstackclient/network/v2/security_group.py | 9 +- .../network/v2/security_group_rule.py | 2 +- .../tests/unit/network/v2/fakes.py | 436 ++++++++---------- .../network/v2/test_floating_ip_network.py | 2 +- .../network/v2/test_l3_conntrack_helper.py | 4 +- .../tests/unit/network/v2/test_ndp_proxy.py | 4 +- .../unit/network/v2/test_network_agent.py | 44 +- .../unit/network/v2/test_network_rbac.py | 2 +- .../tests/unit/network/v2/test_port.py | 28 +- .../tests/unit/network/v2/test_router.py | 106 +++-- .../network/v2/test_security_group_network.py | 52 +-- .../v2/test_security_group_rule_network.py | 72 ++- 13 files changed, 353 insertions(+), 410 deletions(-) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 0f6b622cd..67e3de06f 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -76,7 +76,7 @@ def _get_columns(item): } if hasattr(item, 'interfaces_info'): column_map['interfaces_info'] = 'interfaces_info' - invisible_columns = ['location'] + invisible_columns = ['location', 'tenant_id'] if item.is_ha is None: invisible_columns.append('is_ha') column_map.pop('is_ha') diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index 32b544fce..ddf6c786e 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -89,9 +89,8 @@ def _get_columns(item): # We still support Nova managed security groups, where we have tenant_id. column_map = { 'security_group_rules': 'rules', - 'tenant_id': 'project_id', } - hidden_columns = ['location'] + hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( item, column_map, hidden_columns ) @@ -186,7 +185,8 @@ def take_action_compute(self, client, parsed_args): parsed_args.name, description, ) - display_columns, property_columns = _get_columns(obj) + display_columns = ('description', 'id', 'name', 'project_id', 'rules') + property_columns = ('description', 'id', 'name', 'tenant_id', 'rules') data = utils.get_dict_properties( obj, property_columns, formatters=_formatters_compute ) @@ -420,7 +420,8 @@ def take_action_network(self, client, parsed_args): def take_action_compute(self, client, parsed_args): obj = compute_v2.find_security_group(client, parsed_args.group) - display_columns, property_columns = _get_columns(obj) + display_columns = ('description', 'id', 'name', 'project_id', 'rules') + property_columns = ('description', 'id', 'name', 'tenant_id', 'rules') data = utils.get_dict_properties( obj, property_columns, formatters=_formatters_compute ) diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index 954ee4cbe..4907fe66d 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -30,7 +30,7 @@ def _get_columns(item): - hidden_columns = ['location', 'tenant_id'] + hidden_columns = ['location', 'name', 'tenant_id', 'tags'] return utils.get_osc_show_columns_for_sdk_resource( item, {}, hidden_columns ) diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index 1ae1a253b..d58f6e190 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -46,7 +46,9 @@ from openstack.network.v2 import qos_policy as _qos_policy from openstack.network.v2 import qos_rule_type as _qos_rule_type from openstack.network.v2 import rbac_policy as network_rbac +from openstack.network.v2 import router as _router from openstack.network.v2 import security_group as _security_group +from openstack.network.v2 import security_group_rule as _security_group_rule from openstack.network.v2 import segment as _segment from openstack.network.v2 import service_profile as _service_profile from openstack.network.v2 import trunk as _trunk @@ -132,246 +134,6 @@ def create_one_extension(attrs=None): return extension -class FakeRouter: - """Fake one or more routers.""" - - @staticmethod - def create_one_router(attrs=None): - """Create a fake router. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, name, admin_state_up, - status, project_id - """ - attrs = attrs or {} - - # Set default attributes. - router_attrs = { - 'id': 'router-id-' + uuid.uuid4().hex, - 'name': 'router-name-' + uuid.uuid4().hex, - 'status': 'ACTIVE', - 'admin_state_up': True, - 'description': 'router-description-' + uuid.uuid4().hex, - 'distributed': False, - 'ha': False, - 'project_id': 'project-id-' + uuid.uuid4().hex, - 'routes': [], - 'external_gateway_info': {}, - 'availability_zone_hints': [], - 'availability_zones': [], - 'tags': [], - 'location': 'MUNCHMUNCHMUNCH', - } - - # Overwrite default attributes. - router_attrs.update(attrs) - - router = fakes.FakeResource( - info=copy.deepcopy(router_attrs), loaded=True - ) - - # Set attributes with special mapping in OpenStack SDK. - router.is_admin_state_up = router_attrs['admin_state_up'] - router.is_distributed = router_attrs['distributed'] - router.is_ha = router_attrs['ha'] - - return router - - @staticmethod - def create_routers(attrs=None, count=2): - """Create multiple fake routers. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of routers to fake - :return: - A list of FakeResource objects faking the routers - """ - routers = [] - for i in range(0, count): - routers.append(FakeRouter.create_one_router(attrs)) - - return routers - - @staticmethod - def get_routers(routers=None, count=2): - """Get an iterable Mock object with a list of faked routers. - - If routers list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List routers: - A list of FakeResource objects faking routers - :param int count: - The number of routers to fake - :return: - An iterable Mock object with side_effect set to a list of faked - routers - """ - if routers is None: - routers = FakeRouter.create_routers(count) - return mock.Mock(side_effect=routers) - - -class FakeSecurityGroup: - """Fake one or more security groups.""" - - @staticmethod - def create_one_security_group(attrs=None): - """Create a fake security group. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, name, etc. - """ - attrs = attrs or {} - - # Set default attributes. - security_group_attrs = { - 'id': 'security-group-id-' + uuid.uuid4().hex, - 'name': 'security-group-name-' + uuid.uuid4().hex, - 'description': 'security-group-description-' + uuid.uuid4().hex, - 'stateful': True, - 'project_id': 'project-id-' + uuid.uuid4().hex, - 'security_group_rules': [], - 'tags': [], - 'location': 'MUNCHMUNCHMUNCH', - } - - # Overwrite default attributes. - security_group_attrs.update(attrs) - - security_group = fakes.FakeResource( - info=copy.deepcopy(security_group_attrs), loaded=True - ) - - return security_group - - @staticmethod - def create_security_groups(attrs=None, count=2): - """Create multiple fake security groups. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of security groups to fake - :return: - A list of FakeResource objects faking the security groups - """ - security_groups = [] - for i in range(0, count): - security_groups.append( - FakeSecurityGroup.create_one_security_group(attrs) - ) - - return security_groups - - @staticmethod - def get_security_groups(security_groups=None, count=2): - """Get an iterable Mock object with a list of faked security groups. - - If security groups list is provided, then initialize the Mock object - with the list. Otherwise create one. - - :param List security_groups: - A list of FakeResource objects faking security groups - :param int count: - The number of security groups to fake - :return: - An iterable Mock object with side_effect set to a list of faked - security groups - """ - if security_groups is None: - security_groups = FakeSecurityGroup.create_security_groups(count) - return mock.Mock(side_effect=security_groups) - - -class FakeSecurityGroupRule: - """Fake one or more security group rules.""" - - @staticmethod - def create_one_security_group_rule(attrs=None): - """Create a fake security group rule. - - :param Dictionary attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, etc. - """ - attrs = attrs or {} - - # Set default attributes. - security_group_rule_attrs = { - 'description': 'security-group-rule-description-' - + uuid.uuid4().hex, - 'direction': 'ingress', - 'ether_type': 'IPv4', - 'id': 'security-group-rule-id-' + uuid.uuid4().hex, - 'port_range_max': None, - 'port_range_min': None, - 'protocol': None, - 'remote_group_id': None, - 'remote_address_group_id': None, - 'remote_ip_prefix': '0.0.0.0/0', - 'security_group_id': 'security-group-id-' + uuid.uuid4().hex, - 'project_id': 'project-id-' + uuid.uuid4().hex, - 'location': 'MUNCHMUNCHMUNCH', - } - - # Overwrite default attributes. - security_group_rule_attrs.update(attrs) - - security_group_rule = fakes.FakeResource( - info=copy.deepcopy(security_group_rule_attrs), loaded=True - ) - - return security_group_rule - - @staticmethod - def create_security_group_rules(attrs=None, count=2): - """Create multiple fake security group rules. - - :param Dictionary attrs: - A dictionary with all attributes - :param int count: - The number of security group rules to fake - :return: - A list of FakeResource objects faking the security group rules - """ - security_group_rules = [] - for i in range(0, count): - security_group_rules.append( - FakeSecurityGroupRule.create_one_security_group_rule(attrs) - ) - - return security_group_rules - - @staticmethod - def get_security_group_rules(security_group_rules=None, count=2): - """Get an iterable Mock with a list of faked security group rules. - - If security group rules list is provided, then initialize the Mock - object with the list. Otherwise create one. - - :param List security_group_rules: - A list of FakeResource objects faking security group rules - :param int count: - The number of security group rules to fake - :return: - An iterable Mock object with side_effect set to a list of faked - security group rules - """ - if security_group_rules is None: - security_group_rules = ( - FakeSecurityGroupRule.create_security_group_rules(count) - ) - return mock.Mock(side_effect=security_group_rules) - - class FakeSubnet: """Fake one or more subnets.""" @@ -1631,20 +1393,32 @@ def get_network_rbacs(rbac_policies=None, count=2): def create_one_security_group(attrs=None): - """Create a security group.""" + """Create a fake security group. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A SecurityGroup object, with id, name, etc. + """ attrs = attrs or {} + # Set default attributes. security_group_attrs = { - 'name': 'security-group-name-' + uuid.uuid4().hex, 'id': 'security-group-id-' + uuid.uuid4().hex, - 'project_id': 'project-id-' + uuid.uuid4().hex, + 'name': 'security-group-name-' + uuid.uuid4().hex, 'description': 'security-group-description-' + uuid.uuid4().hex, + 'stateful': True, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'security_group_rules': [], + 'tags': [], 'location': 'MUNCHMUNCHMUNCH', } + # Overwrite default attributes. security_group_attrs.update(attrs) security_group = _security_group.SecurityGroup(**security_group_attrs) + security_group.tenant_id = None # unset deprecated opts return security_group @@ -1652,9 +1426,12 @@ def create_one_security_group(attrs=None): def create_security_groups(attrs=None, count=2): """Create multiple fake security groups. - :param dict attrs: A dictionary with all attributes - :param int count: The number of security groups to fake - :return: A list of fake SecurityGroup objects + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of security groups to fake + :return: + A list of SecurityGroup objects faking the security groups """ security_groups = [] for i in range(0, count): @@ -1663,6 +1440,99 @@ def create_security_groups(attrs=None, count=2): return security_groups +def get_security_groups(security_groups=None, count=2): + """Get an iterable Mock object with a list of faked security groups. + + If security groups list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List security_groups: + A list of SecurityGroup objects faking security groups + :param int count: + The number of security groups to fake + :return: + An iterable Mock object with side_effect set to a list of faked + security groups + """ + if security_groups is None: + security_groups = create_security_groups(count) + return mock.Mock(side_effect=security_groups) + + +def create_one_security_group_rule(attrs=None): + """Create a fake security group rule. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with id, etc. + """ + attrs = attrs or {} + + # Set default attributes. + security_group_rule_attrs = { + 'description': 'security-group-rule-description-' + uuid.uuid4().hex, + 'direction': 'ingress', + 'ether_type': 'IPv4', + 'id': 'security-group-rule-id-' + uuid.uuid4().hex, + 'port_range_max': None, + 'port_range_min': None, + 'protocol': None, + 'remote_group_id': None, + 'remote_address_group_id': None, + 'remote_ip_prefix': '0.0.0.0/0', + 'security_group_id': 'security-group-id-' + uuid.uuid4().hex, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'location': 'MUNCHMUNCHMUNCH', + } + + # Overwrite default attributes. + security_group_rule_attrs.update(attrs) + + security_group_rule = _security_group_rule.SecurityGroupRule( + **security_group_rule_attrs + ) + security_group_rule.tenant_id = None # unset deprecated opts + + return security_group_rule + + +def create_security_group_rules(attrs=None, count=2): + """Create multiple fake security group rules. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of security group rules to fake + :return: + A list of SecurityGroupRule objects faking the security group rules + """ + security_group_rules = [] + for i in range(0, count): + security_group_rules.append(create_one_security_group_rule(attrs)) + + return security_group_rules + + +def get_security_group_rules(security_group_rules=None, count=2): + """Get an iterable Mock with a list of faked security group rules. + + If security group rules list is provided, then initialize the Mock + object with the list. Otherwise create one. + + :param List security_group_rules: + A list of SecurityGroupRule objects faking security group rules + :param int count: + The number of security group rules to fake + :return: + An iterable Mock object with side_effect set to a list of faked + security group rules + """ + if security_group_rules is None: + security_group_rules = create_security_group_rules(count) + return mock.Mock(side_effect=security_group_rules) + + def create_one_service_profile(attrs=None): """Create service profile.""" attrs = attrs or {} @@ -1902,6 +1772,80 @@ def create_qos_rule_types(attrs=None, count=2): return qos_rule_types +def create_one_router(attrs=None): + """Create a fake router. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A Router object, with id, name, admin_state_up, + status, project_id + """ + attrs = attrs or {} + + # Set default attributes. + router_attrs = { + 'id': 'router-id-' + uuid.uuid4().hex, + 'name': 'router-name-' + uuid.uuid4().hex, + 'status': 'ACTIVE', + 'is_admin_state_up': True, + 'description': 'router-description-' + uuid.uuid4().hex, + 'distributed': False, + 'ha': False, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'routes': [], + 'external_gateway_info': {}, + 'availability_zone_hints': [], + 'availability_zones': [], + 'tags': [], + 'location': 'MUNCHMUNCHMUNCH', + } + + # Overwrite default attributes. + router_attrs.update(attrs) + + router = _router.Router(**router_attrs) + router.tenant_id = None # unset deprecated opts + + return router + + +def create_routers(attrs=None, count=2): + """Create multiple fake routers. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of routers to fake + :return: + A list of Router objects faking the routers + """ + routers = [] + for i in range(0, count): + routers.append(create_one_router(attrs)) + + return routers + + +def get_routers(routers=None, count=2): + """Get an iterable Mock object with a list of faked routers. + + If routers list is provided, then initialize the Mock object with the + list. Otherwise create one. + + :param List routers: + A list of Router objects faking routers + :param int count: + The number of routers to fake + :return: + An iterable Mock object with side_effect set to a list of faked + routers + """ + if routers is None: + routers = create_routers(count) + return mock.Mock(side_effect=routers) + + def create_one_local_ip(attrs=None): """Create a fake local ip. diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py index 03d21cffc..d7d0b7799 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py @@ -414,7 +414,7 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): 'id': 'fake_port_id', } ) - fake_router = network_fakes.FakeRouter.create_one_router( + fake_router = network_fakes.create_one_router( { 'id': 'fake_router_id', } diff --git a/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py b/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py index c7aadf0bf..b358024ff 100644 --- a/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +++ b/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py @@ -24,8 +24,8 @@ class TestConntrackHelper(network_fakes.TestNetworkV2): def setUp(self): super().setUp() - self.router = network_fakes.FakeRouter.create_one_router() - self.network_client.find_router = mock.Mock(return_value=self.router) + self.router = network_fakes.create_one_router() + self.network_client.find_router.return_value = self.router class TestCreateL3ConntrackHelper(TestConntrackHelper): diff --git a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py index bcbc0ae7d..13d3f3d99 100644 --- a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py +++ b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py @@ -30,9 +30,7 @@ def setUp(self): # Get a shortcut to the DomainManager Mock self.domains_mock = self.identity_client.domains - self.router = network_fakes.FakeRouter.create_one_router( - {'id': 'fake-router-id'} - ) + self.router = network_fakes.create_one_router({'id': 'fake-router-id'}) self.network_client.find_router = mock.Mock(return_value=self.router) self.port = network_fakes.create_one_port() self.network_client.find_port = mock.Mock(return_value=self.port) diff --git a/openstackclient/tests/unit/network/v2/test_network_agent.py b/openstackclient/tests/unit/network/v2/test_network_agent.py index 15bacae58..bd79950d5 100644 --- a/openstackclient/tests/unit/network/v2/test_network_agent.py +++ b/openstackclient/tests/unit/network/v2/test_network_agent.py @@ -70,7 +70,7 @@ def test_add_network_to_dhcp_agent(self): class TestAddRouterAgent(TestNetworkAgent): - _router = network_fakes.FakeRouter.create_one_router() + _router = network_fakes.create_one_router() _agent = network_fakes.create_one_network_agent() def setUp(self): @@ -219,30 +219,24 @@ class TestListNetworkAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.agents = mock.Mock( - return_value=self.network_agents + + self.network_client.agents.return_value = self.network_agents + self.network_client.routers_hosting_l3_agents.return_value = ( + self.network_agents ) _testagent = network_fakes.create_one_network_agent() - self.network_client.get_agent = mock.Mock(return_value=_testagent) + self.network_client.get_agent.return_value = _testagent + self.network_client.get_agent.return_value = _testagent self._testnetwork = network_fakes.create_one_network() - self.network_client.find_network = mock.Mock( - return_value=self._testnetwork - ) - self.network_client.network_hosting_dhcp_agents = mock.Mock( - return_value=self.network_agents + self.network_client.find_network.return_value = self._testnetwork + self.network_client.network_hosting_dhcp_agents.return_value = ( + self.network_agents ) - self.network_client.get_agent = mock.Mock(return_value=_testagent) - - self._testrouter = network_fakes.FakeRouter.create_one_router() - self.network_client.find_router = mock.Mock( - return_value=self._testrouter - ) - self.network_client.routers_hosting_l3_agents = mock.Mock( - return_value=self.network_agents - ) + self._testrouter = network_fakes.create_one_router() + self.network_client.find_router.return_value = self._testrouter # Get the command object to test self.cmd = network_agent.ListNetworkAgent(self.app, None) @@ -323,15 +317,11 @@ def test_network_agents_list_routers(self): ] verifylist = [('router', self._testrouter.id), ('long', False)] - attrs = { - self._testrouter, - } - parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.network_client.routers_hosting_l3_agents.assert_called_once_with( - *attrs + self._testrouter ) self.assertEqual(self.columns, columns) @@ -345,15 +335,11 @@ def test_network_agents_list_routers_with_long_option(self): ] verifylist = [('router', self._testrouter.id), ('long', True)] - attrs = { - self._testrouter, - } - parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.network_client.routers_hosting_l3_agents.assert_called_once_with( - *attrs + self._testrouter ) # Add a column 'HA State' and corresponding data. @@ -422,7 +408,7 @@ def test_network_from_dhcp_agent(self): class TestRemoveRouterAgent(TestNetworkAgent): - _router = network_fakes.FakeRouter.create_one_router() + _router = network_fakes.create_one_router() _agent = network_fakes.create_one_network_agent() def setUp(self): diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py index 29b3147e1..816e93237 100644 --- a/openstackclient/tests/unit/network/v2/test_network_rbac.py +++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py @@ -35,7 +35,7 @@ def setUp(self): class TestCreateNetworkRBAC(TestNetworkRBAC): network_object = network_fakes.create_one_network() qos_object = network_fakes.create_one_qos_policy() - sg_object = network_fakes.FakeSecurityGroup.create_one_security_group() + sg_object = network_fakes.create_one_security_group() as_object = network_fakes.create_one_address_scope() snp_object = network_fakes.FakeSubnetPool.create_one_subnet_pool() ag_object = network_fakes.create_one_address_group() diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 2127e7885..5e48e311e 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -320,7 +320,7 @@ def test_create_json_binding_profile(self): self.assertCountEqual(self.data, data) def test_create_with_security_group(self): - secgroup = network_fakes.FakeSecurityGroup.create_one_security_group() + secgroup = network_fakes.create_one_security_group() self.network_client.find_security_group = mock.Mock( return_value=secgroup ) @@ -391,8 +391,8 @@ def test_create_port_with_dns_name(self): self.assertCountEqual(self.data, data) def test_create_with_security_groups(self): - sg_1 = network_fakes.FakeSecurityGroup.create_one_security_group() - sg_2 = network_fakes.FakeSecurityGroup.create_one_security_group() + sg_1 = network_fakes.create_one_security_group() + sg_2 = network_fakes.create_one_security_group() self.network_client.find_security_group = mock.Mock( side_effect=[sg_1, sg_2] ) @@ -1325,7 +1325,7 @@ def setUp(self): super().setUp() self.network_client.ports = mock.Mock(return_value=self._ports) - fake_router = network_fakes.FakeRouter.create_one_router( + fake_router = network_fakes.create_one_router( { 'id': 'fake-router-id', } @@ -2096,7 +2096,7 @@ def test_set_port_mixed_binding_profile(self): self.assertIsNone(result) def test_set_port_security_group(self): - sg = network_fakes.FakeSecurityGroup.create_one_security_group() + sg = network_fakes.create_one_security_group() self.network_client.find_security_group = mock.Mock(return_value=sg) arglist = [ '--security-group', @@ -2119,9 +2119,9 @@ def test_set_port_security_group(self): self.assertIsNone(result) def test_set_port_security_group_append(self): - sg_1 = network_fakes.FakeSecurityGroup.create_one_security_group() - sg_2 = network_fakes.FakeSecurityGroup.create_one_security_group() - sg_3 = network_fakes.FakeSecurityGroup.create_one_security_group() + sg_1 = network_fakes.create_one_security_group() + sg_2 = network_fakes.create_one_security_group() + sg_3 = network_fakes.create_one_security_group() self.network_client.find_security_group = mock.Mock( side_effect=[sg_2, sg_3] ) @@ -2172,8 +2172,8 @@ def test_set_port_security_group_clear(self): self.assertIsNone(result) def test_set_port_security_group_replace(self): - sg1 = network_fakes.FakeSecurityGroup.create_one_security_group() - sg2 = network_fakes.FakeSecurityGroup.create_one_security_group() + sg1 = network_fakes.create_one_security_group() + sg2 = network_fakes.create_one_security_group() _testport = network_fakes.create_one_port( {'security_group_ids': [sg1.id]} ) @@ -2821,8 +2821,8 @@ def test_unset_port_binding_profile_not_existent(self): ) def test_unset_security_group(self): - _fake_sg1 = network_fakes.FakeSecurityGroup.create_one_security_group() - _fake_sg2 = network_fakes.FakeSecurityGroup.create_one_security_group() + _fake_sg1 = network_fakes.create_one_security_group() + _fake_sg2 = network_fakes.create_one_security_group() _fake_port = network_fakes.create_one_port( {'security_group_ids': [_fake_sg1.id, _fake_sg2.id]} ) @@ -2849,8 +2849,8 @@ def test_unset_security_group(self): self.assertIsNone(result) def test_unset_port_security_group_not_existent(self): - _fake_sg1 = network_fakes.FakeSecurityGroup.create_one_security_group() - _fake_sg2 = network_fakes.FakeSecurityGroup.create_one_security_group() + _fake_sg1 = network_fakes.create_one_security_group() + _fake_sg2 = network_fakes.create_one_security_group() _fake_port = network_fakes.create_one_port( {'security_group_ids': [_fake_sg1.id]} ) diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 286f57726..353658330 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -34,9 +34,7 @@ class TestAddPortToRouter(TestRouter): '''Add port to Router''' _port = network_fakes.create_one_port() - _router = network_fakes.FakeRouter.create_one_router( - attrs={'port': _port.id} - ) + _router = network_fakes.create_one_router(attrs={'port': _port.id}) def setUp(self): super().setUp() @@ -84,9 +82,7 @@ class TestAddSubnetToRouter(TestRouter): '''Add subnet to Router''' _subnet = network_fakes.FakeSubnet.create_one_subnet() - _router = network_fakes.FakeRouter.create_one_router( - attrs={'subnet': _subnet.id} - ) + _router = network_fakes.create_one_router(attrs={'subnet': _subnet.id}) def setUp(self): super().setUp() @@ -129,38 +125,48 @@ def test_add_subnet_required_options(self): class TestCreateRouter(TestRouter): # The new router created. - new_router = network_fakes.FakeRouter.create_one_router() + new_router = network_fakes.create_one_router() _extensions = {'fake': network_fakes.create_one_extension()} columns = ( 'admin_state_up', 'availability_zone_hints', 'availability_zones', + 'created_at', 'description', 'distributed', + 'enable_ndp_proxy', 'external_gateway_info', + 'flavor_id', 'ha', 'id', 'name', 'project_id', + 'revision_number', 'routes', 'status', 'tags', + 'updated_at', ) data = ( - router.AdminStateColumn(new_router.admin_state_up), + router.AdminStateColumn(new_router.is_admin_state_up), format_columns.ListColumn(new_router.availability_zone_hints), format_columns.ListColumn(new_router.availability_zones), + new_router.created_at, new_router.description, - new_router.distributed, + new_router.is_distributed, + new_router.enable_ndp_proxy, router.RouterInfoColumn(new_router.external_gateway_info), - new_router.ha, + new_router.flavor_id, + new_router.is_ha, new_router.id, new_router.name, new_router.project_id, + new_router.revision_number, router.RoutesColumn(new_router.routes), new_router.status, format_columns.ListColumn(new_router.tags), + new_router.updated_at, ) def setUp(self): @@ -614,14 +620,14 @@ def test_create_with_qos_policy_no_external_gateway(self): class TestDeleteRouter(TestRouter): # The routers to delete. - _routers = network_fakes.FakeRouter.create_routers(count=2) + _routers = network_fakes.create_routers(count=2) def setUp(self): super().setUp() self.network_client.delete_router = mock.Mock(return_value=None) - self.network_client.find_router = network_fakes.FakeRouter.get_routers( + self.network_client.find_router = network_fakes.get_routers( self._routers ) @@ -696,7 +702,7 @@ def test_multi_routers_delete_with_exception(self): class TestListRouter(TestRouter): # The routers going to be listed up. - routers = network_fakes.FakeRouter.create_routers(count=3) + routers = network_fakes.create_routers(count=3) extensions = network_fakes.create_one_extension() columns = ( @@ -727,10 +733,10 @@ class TestListRouter(TestRouter): r.id, r.name, r.status, - router.AdminStateColumn(r.admin_state_up), + router.AdminStateColumn(r.is_admin_state_up), r.project_id, - r.distributed, - r.ha, + r.is_distributed, + r.is_ha, ) ) @@ -813,7 +819,7 @@ def test_router_list_no_options(self): self.assertCountEqual(self.data, list(data)) def test_router_list_no_ha_no_distributed(self): - _routers = network_fakes.FakeRouter.create_routers( + _routers = network_fakes.create_routers( {'ha': None, 'distributed': None}, count=3 ) @@ -1033,9 +1039,7 @@ class TestRemovePortFromRouter(TestRouter): '''Remove port from a Router''' _port = network_fakes.create_one_port() - _router = network_fakes.FakeRouter.create_one_router( - attrs={'port': _port.id} - ) + _router = network_fakes.create_one_router(attrs={'port': _port.id}) def setUp(self): super().setUp() @@ -1080,9 +1084,7 @@ class TestRemoveSubnetFromRouter(TestRouter): '''Remove subnet from Router''' _subnet = network_fakes.FakeSubnet.create_one_subnet() - _router = network_fakes.FakeRouter.create_one_router( - attrs={'subnet': _subnet.id} - ) + _router = network_fakes.create_one_router(attrs={'subnet': _subnet.id}) def setUp(self): super().setUp() @@ -1123,7 +1125,7 @@ def test_remove_subnet_required_options(self): class TestAddExtraRoutesToRouter(TestRouter): - _router = network_fakes.FakeRouter.create_one_router() + _router = network_fakes.create_one_router() def setUp(self): super().setUp() @@ -1212,7 +1214,7 @@ def test_add_multiple_extra_routes(self): class TestRemoveExtraRoutesFromRouter(TestRouter): - _router = network_fakes.FakeRouter.create_one_router() + _router = network_fakes.create_one_router() def setUp(self): super().setUp() @@ -1307,7 +1309,7 @@ class TestSetRouter(TestRouter): _subnet = network_fakes.FakeSubnet.create_one_subnet( attrs={'network_id': _network.id} ) - _router = network_fakes.FakeRouter.create_one_router( + _router = network_fakes.create_one_router( attrs={'routes': [_default_route], 'tags': ['green', 'red']} ) _extensions = {'fake': network_fakes.create_one_extension()} @@ -1454,7 +1456,7 @@ def test_set_no_route(self): self.assertIsNone(result) def test_set_route_overwrite_route(self): - _testrouter = network_fakes.FakeRouter.create_one_router( + _testrouter = network_fakes.create_one_router( {'routes': [{"destination": "10.0.0.2", "nexthop": "1.1.1.1"}]} ) self.network_client.find_router = mock.Mock(return_value=_testrouter) @@ -1753,7 +1755,7 @@ def test_set_gateway_ip_qos_no_gateway(self): self.network_client.find_qos_policy = mock.Mock( return_value=qos_policy ) - router = network_fakes.FakeRouter.create_one_router() + router = network_fakes.create_one_router() self.network_client.find_router = mock.Mock(return_value=router) arglist = [ "--qos-policy", @@ -1775,7 +1777,7 @@ def test_unset_gateway_ip_qos_no_gateway(self): self.network_client.find_qos_policy = mock.Mock( return_value=qos_policy ) - router = network_fakes.FakeRouter.create_one_router() + router = network_fakes.create_one_router() self.network_client.find_router = mock.Mock(return_value=router) arglist = [ "--no-qos-policy", @@ -1793,7 +1795,7 @@ def test_unset_gateway_ip_qos_no_gateway(self): class TestShowRouter(TestRouter): # The router to set. - _router = network_fakes.FakeRouter.create_one_router() + _router = network_fakes.create_one_router() _port = network_fakes.create_one_port( {'device_owner': 'network:router_interface', 'device_id': _router.id} ) @@ -1813,33 +1815,43 @@ class TestShowRouter(TestRouter): 'admin_state_up', 'availability_zone_hints', 'availability_zones', + 'created_at', 'description', 'distributed', + 'enable_ndp_proxy', 'external_gateway_info', + 'flavor_id', 'ha', 'id', 'interfaces_info', 'name', 'project_id', + 'revision_number', 'routes', 'status', 'tags', + 'updated_at', ) data = ( - router.AdminStateColumn(_router.admin_state_up), + router.AdminStateColumn(_router.is_admin_state_up), format_columns.ListColumn(_router.availability_zone_hints), format_columns.ListColumn(_router.availability_zones), + _router.created_at, _router.description, - _router.distributed, + _router.is_distributed, + _router.enable_ndp_proxy, router.RouterInfoColumn(_router.external_gateway_info), - _router.ha, + _router.flavor_id, + _router.is_ha, _router.id, router.RouterInfoColumn(_router.interfaces_info), _router.name, _router.project_id, + _router.revision_number, router.RoutesColumn(_router.routes), _router.status, format_columns.ListColumn(_router.tags), + _router.updated_at, ) def setUp(self): @@ -1884,7 +1896,7 @@ def test_show_all_options(self): self.assertCountEqual(self.data, data) def test_show_no_ha_no_distributed(self): - _router = network_fakes.FakeRouter.create_one_router( + _router = network_fakes.create_one_router( {'ha': None, 'distributed': None} ) @@ -1905,7 +1917,7 @@ def test_show_no_ha_no_distributed(self): self.assertNotIn("is_ha", columns) def test_show_no_extra_route_extension(self): - _router = network_fakes.FakeRouter.create_one_router({'routes': None}) + _router = network_fakes.create_one_router({'routes': None}) arglist = [ _router.name, @@ -1929,7 +1941,7 @@ def setUp(self): super().setUp() self.fake_network = network_fakes.create_one_network() self.fake_qos_policy = network_fakes.create_one_qos_policy() - self._testrouter = network_fakes.FakeRouter.create_one_router( + self._testrouter = network_fakes.create_one_router( { 'routes': [ { @@ -2100,7 +2112,7 @@ def test_unset_gateway_ip_qos_no_network(self): self.network_client.find_qos_policy = mock.Mock( return_value=qos_policy ) - router = network_fakes.FakeRouter.create_one_router() + router = network_fakes.create_one_router() self.network_client.find_router = mock.Mock(return_value=router) arglist = [ "--qos-policy", @@ -2120,7 +2132,7 @@ def test_unset_gateway_ip_qos_no_qos(self): self.network_client.find_qos_policy = mock.Mock( return_value=qos_policy ) - router = network_fakes.FakeRouter.create_one_router( + router = network_fakes.create_one_router( {"external_gateway_info": {"network_id": "fake-id"}} ) self.network_client.find_router = mock.Mock(return_value=router) @@ -2145,7 +2157,7 @@ def setUp(self): self._network = network_fakes.create_one_network() self._networks.append(self._network) - self._router = network_fakes.FakeRouter.create_one_router( + self._router = network_fakes.create_one_router( { 'external_gateway_info': { 'network_id': self._network.id, @@ -2189,16 +2201,21 @@ class TestCreateMultipleGateways(TestGatewayOps): 'admin_state_up', 'availability_zone_hints', 'availability_zones', + 'created_at', 'description', 'distributed', + 'enable_ndp_proxy', 'external_gateway_info', + 'flavor_id', 'ha', 'id', 'name', 'project_id', + 'revision_number', 'routes', 'status', 'tags', + 'updated_at', ) def setUp(self): @@ -2215,19 +2232,24 @@ def setUp(self): ) self._data = ( - router.AdminStateColumn(self._router.admin_state_up), + router.AdminStateColumn(self._router.is_admin_state_up), format_columns.ListColumn(self._router.availability_zone_hints), format_columns.ListColumn(self._router.availability_zones), + self._router.created_at, self._router.description, - self._router.distributed, + self._router.is_distributed, + self._router.enable_ndp_proxy, router.RouterInfoColumn(self._router.external_gateway_info), - self._router.ha, + self._router.flavor_id, + self._router.is_ha, self._router.id, self._router.name, self._router.project_id, + self._router.revision_number, router.RoutesColumn(self._router.routes), self._router.status, format_columns.ListColumn(self._router.tags), + self._router.updated_at, ) self.cmd = router.CreateRouter(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_security_group_network.py b/openstackclient/tests/unit/network/v2/test_security_group_network.py index 9619b2963..62b87642c 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py @@ -36,28 +36,32 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): project = identity_fakes.FakeProject.create_one_project() domain = identity_fakes.FakeDomain.create_one_domain() # The security group to be created. - _security_group = ( - network_fakes.FakeSecurityGroup.create_one_security_group() - ) + _security_group = network_fakes.create_one_security_group() columns = ( + 'created_at', 'description', 'id', 'name', 'project_id', + 'revision_number', 'rules', 'stateful', 'tags', + 'updated_at', ) data = ( + _security_group.created_at, _security_group.description, _security_group.id, _security_group.name, _security_group.project_id, + _security_group.revision_number, security_group.NetworkSecurityGroupRulesColumn([]), _security_group.stateful, _security_group.tags, + _security_group.updated_at, ) def setUp(self): @@ -163,7 +167,7 @@ def _test_create_with_tag(self, add_tags=True): else: self.assertFalse(self.network_client.set_tags.called) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) + self.assertEqual(self.data, data) def test_create_with_tags(self): self._test_create_with_tag(add_tags=True) @@ -174,7 +178,7 @@ def test_create_with_no_tag(self): class TestDeleteSecurityGroupNetwork(TestSecurityGroupNetwork): # The security groups to be deleted. - _security_groups = network_fakes.FakeSecurityGroup.create_security_groups() + _security_groups = network_fakes.create_security_groups() def setUp(self): super().setUp() @@ -184,9 +188,7 @@ def setUp(self): ) self.network_client.find_security_group = ( - network_fakes.FakeSecurityGroup.get_security_groups( - self._security_groups - ) + network_fakes.get_security_groups(self._security_groups) ) # Get the command object to test @@ -264,9 +266,7 @@ def test_multi_security_groups_delete_with_exception(self): class TestListSecurityGroupNetwork(TestSecurityGroupNetwork): # The security group to be listed. - _security_groups = network_fakes.FakeSecurityGroup.create_security_groups( - count=3 - ) + _security_groups = network_fakes.create_security_groups(count=3) columns = ( 'ID', @@ -412,10 +412,8 @@ def test_list_with_tag_options(self): class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork): # The security group to be set. - _security_group = ( - network_fakes.FakeSecurityGroup.create_one_security_group( - attrs={'tags': ['green', 'red']} - ) + _security_group = network_fakes.create_one_security_group( + attrs={'tags': ['green', 'red']} ) def setUp(self): @@ -515,37 +513,39 @@ def test_set_with_no_tag(self): class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork): # The security group rule to be shown with the group. - _security_group_rule = ( - network_fakes.FakeSecurityGroupRule.create_one_security_group_rule() - ) + _security_group_rule = network_fakes.create_one_security_group_rule() # The security group to be shown. - _security_group = ( - network_fakes.FakeSecurityGroup.create_one_security_group( - attrs={'security_group_rules': [_security_group_rule._info]} - ) + _security_group = network_fakes.create_one_security_group( + attrs={'security_group_rules': [dict(_security_group_rule)]} ) columns = ( + 'created_at', 'description', 'id', 'name', 'project_id', + 'revision_number', 'rules', 'stateful', 'tags', + 'updated_at', ) data = ( + _security_group.created_at, _security_group.description, _security_group.id, _security_group.name, _security_group.project_id, + _security_group.revision_number, security_group.NetworkSecurityGroupRulesColumn( - [_security_group_rule._info] + [dict(_security_group_rule)] ), _security_group.stateful, _security_group.tags, + _security_group.updated_at, ) def setUp(self): @@ -583,10 +583,8 @@ def test_show_all_options(self): class TestUnsetSecurityGroupNetwork(TestSecurityGroupNetwork): # The security group to be unset. - _security_group = ( - network_fakes.FakeSecurityGroup.create_one_security_group( - attrs={'tags': ['green', 'red']} - ) + _security_group = network_fakes.create_one_security_group( + attrs={'tags': ['green', 'red']} ) def setUp(self): diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py index c9018db5c..34cf12e01 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py @@ -40,14 +40,13 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): _security_group_rule = None # The security group that will contain the rule created. - _security_group = ( - network_fakes.FakeSecurityGroup.create_one_security_group() - ) + _security_group = network_fakes.create_one_security_group() # The address group to be used in security group rules _address_group = network_fakes.create_one_address_group() expected_columns = ( + 'created_at', 'description', 'direction', 'ether_type', @@ -59,21 +58,22 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): 'remote_address_group_id', 'remote_group_id', 'remote_ip_prefix', + 'revision_number', 'security_group_id', + 'updated_at', ) expected_data = None def _setup_security_group_rule(self, attrs=None): self._security_group_rule = ( - network_fakes.FakeSecurityGroupRule.create_one_security_group_rule( - attrs - ) + network_fakes.create_one_security_group_rule(attrs) ) self.network_client.create_security_group_rule = mock.Mock( return_value=self._security_group_rule ) self.expected_data = ( + self._security_group_rule.created_at, self._security_group_rule.description, self._security_group_rule.direction, self._security_group_rule.ether_type, @@ -85,7 +85,9 @@ def _setup_security_group_rule(self, attrs=None): self._security_group_rule.remote_address_group_id, self._security_group_rule.remote_group_id, self._security_group_rule.remote_ip_prefix, + self._security_group_rule.revision_number, self._security_group_rule.security_group_id, + self._security_group_rule.updated_at, ) def setUp(self): @@ -963,11 +965,7 @@ def test_create_with_description(self): class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): # The security group rules to be deleted. - _security_group_rules = ( - network_fakes.FakeSecurityGroupRule.create_security_group_rules( - count=2 - ) - ) + _security_group_rules = network_fakes.create_security_group_rules(count=2) def setUp(self): super().setUp() @@ -977,9 +975,7 @@ def setUp(self): ) self.network_client.find_security_group_rule = ( - network_fakes.FakeSecurityGroupRule.get_security_group_rules( - self._security_group_rules - ) + network_fakes.get_security_group_rules(self._security_group_rules) ) # Get the command object to test @@ -1057,33 +1053,27 @@ def test_multi_security_group_rules_delete_with_exception(self): class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): # The security group to hold the rules. - _security_group = ( - network_fakes.FakeSecurityGroup.create_one_security_group() - ) + _security_group = network_fakes.create_one_security_group() # The security group rule to be listed. - _security_group_rule_tcp = ( - network_fakes.FakeSecurityGroupRule.create_one_security_group_rule( - { - 'protocol': 'tcp', - 'port_range_max': 80, - 'port_range_min': 80, - 'security_group_id': _security_group.id, - } - ) + _security_group_rule_tcp = network_fakes.create_one_security_group_rule( + { + 'protocol': 'tcp', + 'port_range_max': 80, + 'port_range_min': 80, + 'security_group_id': _security_group.id, + } ) - _security_group_rule_icmp = ( - network_fakes.FakeSecurityGroupRule.create_one_security_group_rule( - { - 'protocol': 'icmp', - 'remote_ip_prefix': '10.0.2.0/24', - 'security_group_id': _security_group.id, - } - ) + _security_group_rule_icmp = network_fakes.create_one_security_group_rule( + { + 'protocol': 'icmp', + 'remote_ip_prefix': '10.0.2.0/24', + 'security_group_id': _security_group.id, + } ) _security_group.security_group_rules = [ - _security_group_rule_tcp._info, - _security_group_rule_icmp._info, + dict(_security_group_rule_tcp), + dict(_security_group_rule_icmp), ] _security_group_rules = [ _security_group_rule_tcp, @@ -1264,11 +1254,10 @@ def test_list_with_wrong_egress(self): class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): # The security group rule to be shown. - _security_group_rule = ( - network_fakes.FakeSecurityGroupRule.create_one_security_group_rule() - ) + _security_group_rule = network_fakes.create_one_security_group_rule() columns = ( + 'created_at', 'description', 'direction', 'ether_type', @@ -1280,10 +1269,13 @@ class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): 'remote_address_group_id', 'remote_group_id', 'remote_ip_prefix', + 'revision_number', 'security_group_id', + 'updated_at', ) data = ( + _security_group_rule.created_at, _security_group_rule.description, _security_group_rule.direction, _security_group_rule.ether_type, @@ -1295,7 +1287,9 @@ class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): _security_group_rule.remote_address_group_id, _security_group_rule.remote_group_id, _security_group_rule.remote_ip_prefix, + _security_group_rule.revision_number, _security_group_rule.security_group_id, + _security_group_rule.updated_at, ) def setUp(self): From c68622402e0bd4f77627c4eb6850b56335934286 Mon Sep 17 00:00:00 2001 From: Douglas Viroel Date: Thu, 9 Jan 2025 15:50:48 -0300 Subject: [PATCH 126/245] Add support for showing scheduler_hints in server details Adds support for a new compute microversion that returns the associated scheduler_hints in ``GET /servers/{server_id}``, ``GET /servers/detail``, ``PUT /servers/{server_id}`` and ``POST /server/{server_id}/action`` (rebuild) responses. Change-Id: Ia5a4e0047b5123f2fb063cfc9ab1f58b2844308f --- openstackclient/compute/v2/server.py | 21 ++++++++++++++++++- .../tests/unit/compute/v2/test_server.py | 8 +++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index dcadca1b6..c70e73b2f 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -184,6 +184,7 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): 'user_data': 'OS-EXT-SRV-ATTR:user_data', 'vm_state': 'OS-EXT-STS:vm_state', 'pinned_availability_zone': 'pinned_availability_zone', + 'scheduler_hints': 'scheduler_hints', } # Some columns returned by openstacksdk should not be shown because they're # either irrelevant or duplicates @@ -204,7 +205,6 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): 'min_count', 'networks', 'personality', - 'scheduler_hints', # aliases 'volumes', # unnecessary @@ -235,6 +235,11 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): info = data + # NOTE(dviroel): microversion 2.100 is now retrieving scheduler_hints + # content from request_spec on detailed responses + if not sdk_utils.supports_microversion(compute_client, '2.100'): + info.pop('scheduler_hints', None) + # Convert the image blob to a name image_info = info.get('image', {}) if image_info and any(image_info.values()): @@ -321,6 +326,11 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): info['OS-EXT-STS:power_state'] ) + if 'scheduler_hints' in info: + info['scheduler_hints'] = format_columns.DictListColumn( + info.pop('scheduler_hints', {}), + ) + return info @@ -2873,12 +2883,14 @@ def take_action(self, parsed_args): 'pinned_availability_zone', 'hypervisor_hostname', 'metadata', + 'scheduler_hints', ) column_headers += ( 'Availability Zone', 'Pinned Availability Zone', 'Host', 'Properties', + 'Scheduler Hints', ) # support for additional columns @@ -2923,6 +2935,12 @@ def take_action(self, parsed_args): if c in ('Properties', "properties"): columns += ('Metadata',) column_headers += ('Properties',) + if c in ( + 'scheduler_hints', + "Scheduler Hints", + ): + columns += ('scheduler_hints',) + column_headers += ('Scheduler Hints',) # remove duplicates column_headers = tuple(dict.fromkeys(column_headers)) @@ -3089,6 +3107,7 @@ def take_action(self, parsed_args): 'metadata': format_columns.DictColumn, 'security_groups_name': format_columns.ListColumn, 'hypervisor_hostname': HostColumn, + 'scheduler_hints': format_columns.DictListColumn, }, ) for s in data diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 789daadf4..72b091f0c 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4603,6 +4603,7 @@ class _TestServerList(TestServer): 'Pinned Availability Zone', 'Host', 'Properties', + 'Scheduler Hints', ) def setUp(self): @@ -4742,6 +4743,7 @@ def test_server_list_long_option(self): getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), + format_columns.DictListColumn(None), ) for s in self.servers ) @@ -4790,6 +4792,8 @@ def test_server_list_column_option(self): 'Host', '-c', 'Properties', + '-c', + 'Scheduler Hints', '--long', ] verifylist = [ @@ -4812,6 +4816,7 @@ def test_server_list_column_option(self): self.assertIn('Pinned Availability Zone', columns) self.assertIn('Host', columns) self.assertIn('Properties', columns) + self.assertIn('Scheduler Hints', columns) self.assertCountEqual(columns, set(columns)) def test_server_list_no_name_lookup_option(self): @@ -5225,6 +5230,7 @@ def test_server_list_long_with_host_status_v216(self): getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), + format_columns.DictListColumn(s.scheduler_hints), ) for s in self.servers ) @@ -5280,6 +5286,7 @@ def test_server_list_long_with_host_status_v216(self): getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), + format_columns.DictListColumn(s.scheduler_hints), s.host_status, ) for s in servers @@ -5317,6 +5324,7 @@ class TestServerListV273(_TestServerList): 'Pinned Availability Zone', 'Host', 'Properties', + 'Scheduler Hints', ) def setUp(self): From c66abfc76fa9bcefa95c10362f7dc9f1cb15bc9a Mon Sep 17 00:00:00 2001 From: Rajesh Tailor Date: Wed, 9 Apr 2025 12:02:58 +0530 Subject: [PATCH 127/245] Workaround for failing tests on openstacksdk change The 'show-instance-action-finish-time' blueprint [1] adds support for showing 'finish_time' for InstanceAction object. This change adds 'finish_time' as hidden column, so it doesn't fail tests. We need to remove this from hidden_column list, once all the changes related to blueprint are merged and show the field only if microversion is >= 2.101 This is a workaround for failing tests on patch [2], as per suggestion from Stephen. [1] https://blueprints.launchpad.net/openstack/?searchtext=show-instance-action-finish-time [2] https://review.opendev.org/c/openstack/openstacksdk/+/930562 Implements: blueprint show-instance-action-finish-time Change-Id: Ib9294a603daed0fdb936be128dfba254b9108799 --- openstackclient/compute/v2/server_event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstackclient/compute/v2/server_event.py b/openstackclient/compute/v2/server_event.py index 3666bfe29..3275df993 100644 --- a/openstackclient/compute/v2/server_event.py +++ b/openstackclient/compute/v2/server_event.py @@ -82,7 +82,7 @@ def machine_readable(self): def _get_server_event_columns(item, client): - hidden_columns = ['name', 'server_id', 'links', 'location'] + hidden_columns = ['name', 'server_id', 'links', 'location', 'finish_time'] if not sdk_utils.supports_microversion(client, '2.58'): # updated_at was introduced in 2.58 From 11495e655ac7d6d8536b7762abd9c8238273ffa9 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 10 Apr 2025 18:21:15 +0100 Subject: [PATCH 128/245] Don't warn about unsupported version with SDK-based commands This doesn't make sense: SDK (and the server) will handle this for us. Change-Id: I31b84e09eca0dc2bc5316d6727620346ae519512 Signed-off-by: Stephen Finucane Closes-bug: #2106760 --- openstackclient/compute/client.py | 5 +++++ openstackclient/image/client.py | 5 +++++ openstackclient/network/client.py | 6 ------ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/openstackclient/compute/client.py b/openstackclient/compute/client.py index 74979bef3..73ce2f87d 100644 --- a/openstackclient/compute/client.py +++ b/openstackclient/compute/client.py @@ -46,3 +46,8 @@ def build_option_parser(parser): % DEFAULT_API_VERSION, ) return parser + + +def check_api_version(check_version): + # SDK supports auto-negotiation for us: always return True + return True diff --git a/openstackclient/image/client.py b/openstackclient/image/client.py index 69a08d82f..f75b3e682 100644 --- a/openstackclient/image/client.py +++ b/openstackclient/image/client.py @@ -46,3 +46,8 @@ def build_option_parser(parser): % DEFAULT_API_VERSION, ) return parser + + +def check_api_version(check_version): + # SDK supports auto-negotiation for us: always return True + return True diff --git a/openstackclient/network/client.py b/openstackclient/network/client.py index faaa54e2b..5165e1ecd 100644 --- a/openstackclient/network/client.py +++ b/openstackclient/network/client.py @@ -27,12 +27,6 @@ def make_client(instance): """Returns a network proxy""" - # NOTE(dtroyer): As of osc-lib 1.8.0 and OpenStackSDK 0.10.0 the - # old Profile interface and separate client creation - # for each API that uses the SDK is unnecessary. This - # callback remains as a remnant of the original plugin - # interface and to avoid the code churn of changing all - # of the existing references. LOG.debug( 'Network client initialized using OpenStack SDK: %s', instance.sdk_connection.network, From d123be08193762ef9be27ebf8044113fca16498a Mon Sep 17 00:00:00 2001 From: melanie witt Date: Sat, 1 Feb 2025 00:44:03 +0000 Subject: [PATCH 129/245] Fix 'openstack keypair list --project ' The --project option of 'openstack keypair list' is supposed to filter keypairs by a project but has not been working and instead returns keypairs from all projects. The reason appears to be because it uses a request for a user list filtered by project but tenant_id/project_id is not a valid filter for GET /users. This fixes the issue by requesting role assignments for the specified project and then requesting keypairs for users with a role in the project. This change depends on a recent openstacksdk bug fix change Ic552dee83d56278d2b866de0cb365a0c394fe26a which fixed the user_id query parameter for the compute /os-keypairs APIs. The bug fix was released in openstacksdk 4.4.0. Closes-Bug: #2096947 Change-Id: Ibb5757766e3040e58d64388b95678fab9b2b6f23 --- openstackclient/compute/v2/keypair.py | 13 ++++-- .../functional/compute/v2/test_keypair.py | 46 +++++++++++++++++-- .../tests/unit/compute/v2/fakes.py | 4 +- .../tests/unit/compute/v2/test_keypair.py | 17 +++++-- requirements.txt | 2 +- 5 files changed, 66 insertions(+), 16 deletions(-) diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index d10939b4f..97dd04f4e 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -300,6 +300,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): compute_client = self.app.client_manager.compute identity_client = self.app.client_manager.identity + identity_sdk_client = self.app.client_manager.sdk_connection.identity kwargs = {} @@ -345,11 +346,17 @@ def take_action(self, parsed_args): parsed_args.project, parsed_args.project_domain, ).id - users = identity_client.users.list(tenant_id=project) + assignments = identity_sdk_client.role_assignments( + scope_project_id=project + ) + user_ids = set() + for assignment in assignments: + if assignment.user: + user_ids.add(assignment.user['id']) data = [] - for user in users: - kwargs['user_id'] = user.id + for user_id in user_ids: + kwargs['user_id'] = user_id data.extend(compute_client.keypairs(**kwargs)) elif parsed_args.user: if not sdk_utils.supports_microversion(compute_client, '2.10'): diff --git a/openstackclient/tests/functional/compute/v2/test_keypair.py b/openstackclient/tests/functional/compute/v2/test_keypair.py index 4b178cb72..2e01b1bed 100644 --- a/openstackclient/tests/functional/compute/v2/test_keypair.py +++ b/openstackclient/tests/functional/compute/v2/test_keypair.py @@ -21,12 +21,18 @@ class KeypairBase(base.TestCase): """Methods for functional tests.""" - def keypair_create(self, name=data_utils.rand_uuid()): + def keypair_create(self, name=data_utils.rand_uuid(), user=None): """Create keypair and add cleanup.""" - raw_output = self.openstack('keypair create ' + name) - self.addCleanup(self.keypair_delete, name, True) + cmd = 'keypair create ' + name + if user is not None: + cmd += ' --user ' + user + raw_output = self.openstack(cmd) + self.addCleanup( + self.keypair_delete, name, ignore_exceptions=True, user=user + ) if not raw_output: self.fail('Keypair has not been created!') + return name def keypair_list(self, params=''): """Return dictionary with list of keypairs.""" @@ -34,10 +40,13 @@ def keypair_list(self, params=''): keypairs = self.parse_show_as_object(raw_output) return keypairs - def keypair_delete(self, name, ignore_exceptions=False): + def keypair_delete(self, name, ignore_exceptions=False, user=None): """Try to delete keypair by name.""" try: - self.openstack('keypair delete ' + name) + cmd = 'keypair delete ' + name + if user is not None: + cmd += ' --user ' + user + self.openstack(cmd) except exceptions.CommandFailed: if not ignore_exceptions: raise @@ -200,3 +209,30 @@ def test_keypair_show(self): items = self.parse_listing(raw_output) self.assert_table_structure(items, HEADERS) self.assertInOutput(self.KPName, raw_output) + + def test_keypair_list_by_project(self): + """Test keypair list by project. + + Test steps: + 1) Create keypair for admin project in setUp + 2) Create a new project + 3) Create a new user + 4) Associate the new user with the new project + 5) Create keypair for the new user + 6) List keypairs by the new project + 7) Check that only the keypair from step 5 is returned + """ + project_name = data_utils.rand_name('TestProject') + self.openstack(f'project create {project_name}') + self.addCleanup(self.openstack, f'project delete {project_name}') + user_name = data_utils.rand_name('TestUser') + self.openstack(f'user create {user_name}') + self.addCleanup(self.openstack, f'user delete {user_name}') + self.openstack( + f'role add --user {user_name} --project {project_name} member' + ) + keypair_name = self.keypair_create(user=user_name) + raw_output = self.openstack(f'keypair list --project {project_name}') + items = self.parse_listing(raw_output) + self.assertEqual(1, len(items)) + self.assertEqual(keypair_name, items[0]['Name']) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 1eff82d11..ef868a8e3 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -33,7 +33,7 @@ from openstack.compute.v2 import volume_attachment as _volume_attachment from openstackclient.tests.unit import fakes -from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit.image.v2 import fakes as image_fakes from openstackclient.tests.unit.network.v2 import fakes as network_fakes from openstackclient.tests.unit import utils @@ -121,10 +121,10 @@ def set_compute_api_version(self, version: str = '2.1'): class TestComputev2( + identity_fakes.FakeClientMixin, network_fakes.FakeClientMixin, image_fakes.FakeClientMixin, volume_fakes.FakeClientMixin, - identity_fakes.FakeClientMixin, FakeClientMixin, utils.TestCommand, ): ... diff --git a/openstackclient/tests/unit/compute/v2/test_keypair.py b/openstackclient/tests/unit/compute/v2/test_keypair.py index 85a4547a6..4eaaf4c9b 100644 --- a/openstackclient/tests/unit/compute/v2/test_keypair.py +++ b/openstackclient/tests/unit/compute/v2/test_keypair.py @@ -18,6 +18,7 @@ from openstack.compute.v2 import keypair as _keypair from openstack.identity.v3 import project as _project +from openstack.identity.v3 import role_assignment as _role_assignment from openstack.identity.v3 import user as _user from openstack.test import fakes as sdk_fakes from osc_lib import exceptions @@ -529,13 +530,17 @@ def test_keypair_list_with_user_pre_v210(self): def test_keypair_list_with_project(self): self.set_compute_api_version('2.35') - projects_mock = self.identity_client.tenants + projects_mock = self.identity_client.projects projects_mock.reset_mock() projects_mock.get.return_value = self._project - users_mock = self.identity_client.users - users_mock.reset_mock() - users_mock.list.return_value = [self._user] + role_assignments_mock = self.identity_sdk_client.role_assignments + role_assignments_mock.reset_mock() + assignment = sdk_fakes.generate_fake_resource( + _role_assignment.RoleAssignment + ) + assignment.user = self._user + role_assignments_mock.return_value = [assignment] arglist = ['--project', self._project.name] verifylist = [('project', self._project.name)] @@ -544,7 +549,9 @@ def test_keypair_list_with_project(self): columns, data = self.cmd.take_action(parsed_args) projects_mock.get.assert_called_with(self._project.name) - users_mock.list.assert_called_with(tenant_id=self._project.id) + role_assignments_mock.assert_called_with( + scope_project_id=self._project.id + ) self.compute_client.keypairs.assert_called_with( user_id=self._user.id, ) diff --git a/requirements.txt b/requirements.txt index c31de42a7..bf2283328 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 cryptography>=2.7 # BSD/Apache-2.0 cliff>=3.5.0 # Apache-2.0 iso8601>=0.1.11 # MIT -openstacksdk>=3.3.0 # Apache-2.0 +openstacksdk>=4.4.0 # Apache-2.0 osc-lib>=2.3.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 python-keystoneclient>=3.22.0 # Apache-2.0 From 25cd1178b31a3d5582ab4ad77518fb63a856fd88 Mon Sep 17 00:00:00 2001 From: Sean Mooney Date: Thu, 10 Apr 2025 13:01:56 +0100 Subject: [PATCH 130/245] Require confirmation to reset server state. This change updates the server set state command to require confirmation before it is applied. The same pattern as project clean is used and a new --auto-approve flag is added to allow skipping the prompt. Operators often use reset state in cases that are incorrect this change updates the warning to be more explicit about when and when not to use it. Change-Id: Iab14739cf6043ad45ad49edff0580e81d75af2fd --- openstackclient/compute/v2/server.py | 45 +++++++++++++++-- .../tests/unit/compute/v2/test_server.py | 49 +++++++++++++++++++ .../confirm-reset-state-24497c8b24990aa7.yaml | 6 +++ 3 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/confirm-reset-state-24497c8b24990aa7.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index a3ca4d07a..3345ecab5 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -4462,15 +4462,27 @@ def get_parser(self, prog_name): '(repeat option to set multiple properties)' ), ) + parser.add_argument( + '--auto-approve', + action='store_true', + help=_( + "Allow server state override without asking for confirmation" + ), + ) parser.add_argument( '--state', metavar='', choices=['active', 'error'], help=_( - 'New server state ' - '**WARNING** This can result in instances that are no longer ' - 'usable and should be used with caution ' - '(admin only)' + 'New server state.' + '**WARNING** Resetting the state is intended to work around ' + 'servers stuck in an intermediate state, such as deleting. ' + 'If the server is in an error state then this is almost ' + 'never the correct command to run and you should prefer hard ' + 'reboot where possible. In particular, if the server is in ' + 'an error state due to a move operation, setting the state ' + 'can result in instances that are no longer usable. Proceed ' + 'with caution. (admin only)' ), ) parser.add_argument( @@ -4505,6 +4517,20 @@ def get_parser(self, prog_name): ) return parser + @staticmethod + def ask_user_yesno(msg): + """Ask user Y/N question + + :param str msg: question text + :return bool: User choice + """ + while True: + answer = getpass.getpass('{} [{}]: '.format(msg, 'y/n')) + if answer in ('y', 'Y', 'yes'): + return True + elif answer in ('n', 'N', 'no'): + return False + def take_action(self, parsed_args): compute_client = self.app.client_manager.compute server = compute_client.find_server( @@ -4555,6 +4581,17 @@ def take_action(self, parsed_args): ) if parsed_args.state: + if not parsed_args.auto_approve: + if not self.ask_user_yesno( + _( + "Resetting the server state can make it much harder " + "to recover a server from an error state. If the " + "server is in error status due to a failed move " + "operation then this is likely not the correct " + "approach to fix the problem. Do you wish to continue?" + ) + ): + return compute_client.reset_server_state(server, state=parsed_args.state) if parsed_args.root_password: diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 39af6af7a..25ee1d65b 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -7935,6 +7935,7 @@ def test_server_set_with_state(self): arglist = [ '--state', 'active', + '--auto-approve', self.server.id, ] verifylist = [ @@ -7955,6 +7956,54 @@ def test_server_set_with_state(self): self.compute_client.add_tag_to_server.assert_not_called() self.assertIsNone(result) + def test_server_set_with_state_prompt_y(self): + arglist = [ + '--state', + 'active', + self.server.id, + ] + verifylist = [ + ('state', 'active'), + ('server', self.server.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + with mock.patch('getpass.getpass', return_value='y'): + result = self.cmd.take_action(parsed_args) + + self.compute_client.reset_server_state.assert_called_once_with( + self.server, state='active' + ) + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() + self.assertIsNone(result) + + def test_server_set_with_state_prompt_n(self): + arglist = [ + '--state', + 'active', + self.server.id, + ] + verifylist = [ + ('state', 'active'), + ('server', self.server.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + with mock.patch('getpass.getpass', return_value='n'): + result = self.cmd.take_action(parsed_args) + + self.compute_client.reset_server_state.assert_not_called() + self.compute_client.update_server.assert_not_called() + self.compute_client.set_server_metadata.assert_not_called() + self.compute_client.change_server_password.assert_not_called() + self.compute_client.clear_server_password.assert_not_called() + self.compute_client.add_tag_to_server.assert_not_called() + self.assertIsNone(result) + def test_server_set_with_invalid_state(self): arglist = [ '--state', diff --git a/releasenotes/notes/confirm-reset-state-24497c8b24990aa7.yaml b/releasenotes/notes/confirm-reset-state-24497c8b24990aa7.yaml new file mode 100644 index 000000000..b381e5a82 --- /dev/null +++ b/releasenotes/notes/confirm-reset-state-24497c8b24990aa7.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + The ``openstack server set`` command has been extended with a new + parameter ``--auto-approve`` and the existing ``--state`` parameter + has been modified to require confirmation before resetting the state. From b0fe724cafa72c224d253c849c025c1b8a838219 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 14 Apr 2025 14:04:34 +0100 Subject: [PATCH 131/245] tests: Remove use of legacy resource helpers This allows us to remove get_servers, create_servers, and create_one_servers. Change-Id: I31a86b6333fdc3da1b54407f077873511260a5df Signed-off-by: Stephen Finucane --- .../tests/unit/compute/v2/fakes.py | 73 ------------ .../unit/compute/v2/test_server_backup.py | 103 ++++++----------- .../unit/compute/v2/test_server_image.py | 105 ++++++------------ 3 files changed, 65 insertions(+), 216 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index ef868a8e3..2ab996e51 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -313,61 +313,6 @@ def create_security_group_rules(attrs=None, count=2): return security_group_rules -def create_one_server(attrs=None, methods=None): - """Create a fake server. - - :param dict attrs: - A dictionary with all attributes - :param dict methods: - A dictionary with all methods - :return: - A FakeResource object, with id, name, metadata, and so on - """ - attrs = attrs or {} - methods = methods or {} - - # Set default attributes. - server_info = { - 'id': 'server-id-' + uuid.uuid4().hex, - 'name': 'server-name-' + uuid.uuid4().hex, - 'metadata': {}, - 'image': { - 'id': 'image-id-' + uuid.uuid4().hex, - }, - 'flavor': { - 'id': 'flavor-id-' + uuid.uuid4().hex, - }, - 'OS-EXT-STS:power_state': 1, - } - - # Overwrite default attributes. - server_info.update(attrs) - - server = fakes.FakeResource( - info=copy.deepcopy(server_info), methods=methods, loaded=True - ) - return server - - -def create_servers(attrs=None, methods=None, count=2): - """Create multiple fake servers. - - :param dict attrs: - A dictionary with all attributes - :param dict methods: - A dictionary with all methods - :param int count: - The number of servers to fake - :return: - A list of FakeResource objects faking the servers - """ - servers = [] - for i in range(0, count): - servers.append(create_one_server(attrs, methods)) - - return servers - - def create_one_sdk_server(attrs=None): """Create a fake server for testing migration to sdk @@ -414,24 +359,6 @@ def create_sdk_servers(attrs=None, count=2): return servers -def get_servers(servers=None, count=2): - """Get an iterable MagicMock object with a list of faked servers. - - If servers list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param list servers: A list of fake openstack.compute.v2.server.Server - objects - :param int count: - The number of servers to fake - :return: An iterable Mock object with side_effect set to a list of faked - servers - """ - if servers is None: - servers = create_servers(count) - return mock.Mock(side_effect=servers) - - def create_one_server_action(attrs=None): """Create a fake server action. diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py index bc1fca2b8..e473c7e1b 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_backup.py +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -22,22 +22,8 @@ from openstackclient.tests.unit.image.v2 import fakes as image_fakes -class TestServerBackup(compute_fakes.TestComputev2): - def setup_servers_mock(self, count): - servers = compute_fakes.create_sdk_servers( - count=count, - ) - self.compute_client.find_server = compute_fakes.get_servers( - servers, - 0, - ) - return servers - - -class TestServerBackupCreate(TestServerBackup): - # Just return whatever Image is testing with these days +class TestServerBackupCreate(compute_fakes.TestComputev2): def image_columns(self, image): - # columnlist = tuple(sorted(image.keys())) columnlist = ( 'id', 'name', @@ -64,42 +50,27 @@ def image_data(self, image): def setUp(self): super().setUp() + self.server = compute_fakes.create_one_sdk_server() + self.compute_client.find_server.return_value = self.server + + self.image = image_fakes.create_one_image( + {'name': self.server.name, 'status': 'active'} + ) + self.image_client.find_image.return_value = self.image + # Get the command object to test self.cmd = server_backup.CreateServerBackup(self.app, None) - def setup_images_mock(self, count, servers=None): - if servers: - images = image_fakes.create_images( - attrs={ - 'name': servers[0].name, - 'status': 'active', - }, - count=count, - ) - else: - images = image_fakes.create_images( - attrs={ - 'status': 'active', - }, - count=count, - ) - - self.image_client.find_image = mock.Mock(side_effect=images) - return images - def test_server_backup_defaults(self): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - arglist = [ - servers[0].id, + self.server.id, ] verifylist = [ ('name', None), ('type', None), ('rotate', None), ('wait', False), - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -109,19 +80,16 @@ def test_server_backup_defaults(self): columns, data = self.cmd.take_action(parsed_args) self.compute_client.backup_server.assert_called_with( - servers[0].id, - servers[0].name, + self.server.id, + self.server.name, '', 1, ) - self.assertEqual(self.image_columns(images[0]), columns) - self.assertCountEqual(self.image_data(images[0]), data) + self.assertEqual(self.image_columns(self.image), columns) + self.assertCountEqual(self.image_data(self.image), data) def test_server_backup_create_options(self): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - arglist = [ '--name', 'image', @@ -129,13 +97,13 @@ def test_server_backup_create_options(self): 'daily', '--rotate', '2', - servers[0].id, + self.server.id, ] verifylist = [ ('name', 'image'), ('type', 'daily'), ('rotate', 2), - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -145,22 +113,18 @@ def test_server_backup_create_options(self): columns, data = self.cmd.take_action(parsed_args) self.compute_client.backup_server.assert_called_with( - servers[0].id, + self.server.id, 'image', 'daily', 2, ) - self.assertEqual(self.image_columns(images[0]), columns) - self.assertCountEqual(self.image_data(images[0]), data) + self.assertEqual(self.image_columns(self.image), columns) + self.assertCountEqual(self.image_data(self.image), data) @mock.patch.object(common_utils, 'wait_for_status', return_value=False) def test_server_backup_wait_fail(self, mock_wait_for_status): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - self.image_client.get_image = mock.Mock( - side_effect=images[0], - ) + self.image_client.get_image.return_value = self.image arglist = [ '--name', @@ -168,13 +132,13 @@ def test_server_backup_wait_fail(self, mock_wait_for_status): '--type', 'daily', '--wait', - servers[0].id, + self.server.id, ] verifylist = [ ('name', 'image'), ('type', 'daily'), ('wait', True), - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -185,23 +149,20 @@ def test_server_backup_wait_fail(self, mock_wait_for_status): ) self.compute_client.backup_server.assert_called_with( - servers[0].id, + self.server.id, 'image', 'daily', 1, ) mock_wait_for_status.assert_called_once_with( - self.image_client.get_image, images[0].id, callback=mock.ANY + self.image_client.get_image, self.image.id, callback=mock.ANY ) @mock.patch.object(common_utils, 'wait_for_status', return_value=True) def test_server_backup_wait_ok(self, mock_wait_for_status): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - self.image_client.get_image = mock.Mock( - side_effect=images[0], + side_effect=self.image, ) arglist = [ @@ -210,13 +171,13 @@ def test_server_backup_wait_ok(self, mock_wait_for_status): '--type', 'daily', '--wait', - servers[0].id, + self.server.id, ] verifylist = [ ('name', 'image'), ('type', 'daily'), ('wait', True), - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -226,15 +187,15 @@ def test_server_backup_wait_ok(self, mock_wait_for_status): columns, data = self.cmd.take_action(parsed_args) self.compute_client.backup_server.assert_called_with( - servers[0].id, + self.server.id, 'image', 'daily', 1, ) mock_wait_for_status.assert_called_once_with( - self.image_client.get_image, images[0].id, callback=mock.ANY + self.image_client.get_image, self.image.id, callback=mock.ANY ) - self.assertEqual(self.image_columns(images[0]), columns) - self.assertCountEqual(self.image_data(images[0]), data) + self.assertEqual(self.image_columns(self.image), columns) + self.assertCountEqual(self.image_data(self.image), data) diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py index 7feb0b22f..f4a56ff13 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_image.py +++ b/openstackclient/tests/unit/compute/v2/test_server_image.py @@ -21,21 +21,8 @@ from openstackclient.tests.unit.image.v2 import fakes as image_fakes -class TestServerImage(compute_fakes.TestComputev2): - def setup_servers_mock(self, count): - servers = compute_fakes.create_sdk_servers( - count=count, - ) - self.compute_client.find_server = compute_fakes.get_servers( - servers, - 0, - ) - return servers - - -class TestServerImageCreate(TestServerImage): +class TestServerImageCreate(compute_fakes.TestComputev2): def image_columns(self, image): - # columnlist = tuple(sorted(image.keys())) columnlist = ( 'id', 'name', @@ -62,41 +49,24 @@ def image_data(self, image): def setUp(self): super().setUp() - # Get the command object to test - self.cmd = server_image.CreateServerImage(self.app, None) + self.server = compute_fakes.create_one_sdk_server() + self.compute_client.find_server.return_value = self.server - def setup_images_mock(self, count, servers=None): - if servers: - images = image_fakes.create_images( - attrs={ - 'name': servers[0].name, - 'status': 'active', - }, - count=count, - ) - else: - images = image_fakes.create_images( - attrs={ - 'status': 'active', - }, - count=count, - ) - - self.image_client.find_image = mock.Mock(side_effect=images) - self.compute_client.create_server_image = mock.Mock( - return_value=images[0], + self.image = image_fakes.create_one_image( + {'name': self.server.name, 'status': 'active'} ) - return images + self.image_client.find_image.return_value = self.image + self.compute_client.create_server_image.return_value = self.image - def test_server_image_create_defaults(self): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) + # Get the command object to test + self.cmd = server_image.CreateServerImage(self.app, None) + def test_server_image_create_defaults(self): arglist = [ - servers[0].id, + self.server.id, ] verifylist = [ - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -106,28 +76,25 @@ def test_server_image_create_defaults(self): columns, data = self.cmd.take_action(parsed_args) self.compute_client.create_server_image.assert_called_with( - servers[0].id, - servers[0].name, + self.server.id, + self.server.name, None, ) - self.assertEqual(self.image_columns(images[0]), columns) - self.assertCountEqual(self.image_data(images[0]), data) + self.assertEqual(self.image_columns(self.image), columns) + self.assertCountEqual(self.image_data(self.image), data) def test_server_image_create_options(self): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - arglist = [ '--name', 'img-nam', '--property', 'key=value', - servers[0].id, + self.server.id, ] verifylist = [ ('name', 'img-nam'), - ('server', servers[0].id), + ('server', self.server.id), ('properties', {'key': 'value'}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -138,26 +105,23 @@ def test_server_image_create_options(self): columns, data = self.cmd.take_action(parsed_args) self.compute_client.create_server_image.assert_called_with( - servers[0].id, + self.server.id, 'img-nam', {'key': 'value'}, ) - self.assertEqual(self.image_columns(images[0]), columns) - self.assertCountEqual(self.image_data(images[0]), data) + self.assertEqual(self.image_columns(self.image), columns) + self.assertCountEqual(self.image_data(self.image), data) @mock.patch.object(common_utils, 'wait_for_status', return_value=False) def test_server_create_image_wait_fail(self, mock_wait_for_status): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - arglist = [ '--wait', - servers[0].id, + self.server.id, ] verifylist = [ ('wait', True), - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -168,27 +132,24 @@ def test_server_create_image_wait_fail(self, mock_wait_for_status): ) self.compute_client.create_server_image.assert_called_with( - servers[0].id, - servers[0].name, + self.server.id, + self.server.name, None, ) mock_wait_for_status.assert_called_once_with( - self.image_client.get_image, images[0].id, callback=mock.ANY + self.image_client.get_image, self.image.id, callback=mock.ANY ) @mock.patch.object(common_utils, 'wait_for_status', return_value=True) def test_server_create_image_wait_ok(self, mock_wait_for_status): - servers = self.setup_servers_mock(count=1) - images = self.setup_images_mock(count=1, servers=servers) - arglist = [ '--wait', - servers[0].id, + self.server.id, ] verifylist = [ ('wait', True), - ('server', servers[0].id), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -198,14 +159,14 @@ def test_server_create_image_wait_ok(self, mock_wait_for_status): columns, data = self.cmd.take_action(parsed_args) self.compute_client.create_server_image.assert_called_with( - servers[0].id, - servers[0].name, + self.server.id, + self.server.name, None, ) mock_wait_for_status.assert_called_once_with( - self.image_client.get_image, images[0].id, callback=mock.ANY + self.image_client.get_image, self.image.id, callback=mock.ANY ) - self.assertEqual(self.image_columns(images[0]), columns) - self.assertCountEqual(self.image_data(images[0]), data) + self.assertEqual(self.image_columns(self.image), columns) + self.assertCountEqual(self.image_data(self.image), data) From 2c878ad2b7fa5555518982c1405a4949ce7153fb Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 14 Apr 2025 13:57:58 +0100 Subject: [PATCH 132/245] tests: Remove dead code None of these fakes are used anymore. Remove them. Change-Id: I06721aa77f93b76b189901bbdc13a9825fe2fc3d Signed-off-by: Stephen Finucane --- .../tests/unit/compute/v2/fakes.py | 109 ------------------ 1 file changed, 109 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 2ab996e51..1ba0f6468 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -40,60 +40,6 @@ from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes -class FakeComputev2Client: - def __init__(self, **kwargs): - self.agents = mock.Mock() - self.agents.resource_class = fakes.FakeResource(None, {}) - - self.images = mock.Mock() - self.images.resource_class = fakes.FakeResource(None, {}) - - self.servers = mock.Mock() - self.servers.resource_class = fakes.FakeResource(None, {}) - - self.services = mock.Mock() - self.services.resource_class = fakes.FakeResource(None, {}) - - self.extensions = mock.Mock() - self.extensions.resource_class = fakes.FakeResource(None, {}) - - self.flavors = mock.Mock() - - self.flavor_access = mock.Mock() - self.flavor_access.resource_class = fakes.FakeResource(None, {}) - - self.usage = mock.Mock() - self.usage.resource_class = fakes.FakeResource(None, {}) - - self.volumes = mock.Mock() - self.volumes.resource_class = fakes.FakeResource(None, {}) - - self.hypervisors = mock.Mock() - self.hypervisors.resource_class = fakes.FakeResource(None, {}) - - self.hypervisors_stats = mock.Mock() - self.hypervisors_stats.resource_class = fakes.FakeResource(None, {}) - - self.keypairs = mock.Mock() - self.keypairs.resource_class = fakes.FakeResource(None, {}) - - self.server_groups = mock.Mock() - self.server_groups.resource_class = fakes.FakeResource(None, {}) - - self.server_migrations = mock.Mock() - self.server_migrations.resource_class = fakes.FakeResource(None, {}) - - self.instance_action = mock.Mock() - self.instance_action.resource_class = fakes.FakeResource(None, {}) - - self.migrations = mock.Mock() - self.migrations.resource_class = fakes.FakeResource(None, {}) - - self.auth_token = kwargs['token'] - - self.management_url = kwargs['endpoint'] - - class FakeClientMixin: def setUp(self): super().setUp() @@ -250,25 +196,6 @@ def create_security_groups(attrs=None, count=2): return security_groups -def get_security_groups(security_groups=None, count=2): - """Get an iterable MagicMock with a list of faked security groups. - - If security groups list is provided, then initialize the Mock object - with the list. Otherwise create one. - - :param List security_groups: - A list of FakeResource objects faking security groups - :param int count: - The number of security groups to fake - :return: - An iterable Mock object with side_effect set to a list of faked - security groups - """ - if security_groups is None: - security_groups = create_security_groups(count) - return mock.Mock(side_effect=security_groups) - - def create_one_security_group_rule(attrs=None): """Create a fake security group rule. @@ -448,23 +375,6 @@ def create_flavors(attrs=None, count=2): return flavors -def get_flavors(flavors=None, count=2): - """Get an iterable MagicMock object with a list of faked flavors. - - If flavors list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param list flavors: A list of fake openstack.compute.v2.flavor.Flavor - objects - :param int count: The number of flavors to fake - :return: An iterable Mock object with side_effect set to a list of faked - flavors - """ - if flavors is None: - flavors = create_flavors(count) - return mock.Mock(side_effect=flavors) - - def create_one_flavor_access(attrs=None): """Create a fake flavor access. @@ -709,25 +619,6 @@ def create_networks(attrs=None, count=2): return networks -def get_networks(networks=None, count=2): - """Get an iterable MagicMock object with a list of faked networks. - - If networks list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List networks: - A list of FakeResource objects faking networks - :param int count: - The number of networks to fake - :return: - An iterable Mock object with side_effect set to a list of faked - networks - """ - if networks is None: - networks = create_networks(count=count) - return mock.Mock(side_effect=networks) - - def create_limits(attrs=None): """Create a fake limits object.""" attrs = attrs or {} From abed52f106ed5136c0b53fc9468e810a58898a67 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 14 Apr 2025 16:04:28 +0100 Subject: [PATCH 133/245] tests: Remove sdk prefix It is no longer necessary. Change-Id: I6bcc14be90be7c93ec4729f241299d55885570fd Signed-off-by: Stephen Finucane --- .../tests/unit/compute/v2/fakes.py | 44 +++++----- .../tests/unit/compute/v2/test_server.py | 80 +++++++++---------- .../unit/compute/v2/test_server_backup.py | 2 +- .../unit/compute/v2/test_server_event.py | 4 +- .../unit/compute/v2/test_server_image.py | 2 +- .../unit/compute/v2/test_server_migration.py | 8 +- .../tests/unit/network/v2/test_port.py | 2 +- .../unit/volume/v3/test_volume_attachment.py | 2 +- 8 files changed, 73 insertions(+), 71 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 1ba0f6468..878d5910c 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -126,7 +126,7 @@ def create_one_extension(attrs=None): """Create a fake extension. :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.extension.Extension object + :return: A fake :class:`~openstack.compute.v2.extension.Extension` object """ attrs = attrs or {} @@ -240,11 +240,11 @@ def create_security_group_rules(attrs=None, count=2): return security_group_rules -def create_one_sdk_server(attrs=None): - """Create a fake server for testing migration to sdk +def create_one_server(attrs=None): + """Create a fake server :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.server.Server object, + :return: A fake :class:`~openstack.compute.v2.server.Server` object, """ attrs = attrs or {} @@ -272,16 +272,16 @@ def create_one_sdk_server(attrs=None): return server -def create_sdk_servers(attrs=None, count=2): - """Create multiple fake servers for testing migration to sdk +def create_servers(attrs=None, count=2): + """Create multiple fake servers :param dict attrs: A dictionary with all attributes :param int count: The number of servers to fake - :return: A list of fake openstack.compute.v2.server.Server objects + :return: A list of fake :class:`openstack.compute.v2.server.Server` objects """ servers = [] for i in range(0, count): - servers.append(create_one_sdk_server(attrs)) + servers.append(create_one_server(attrs)) return servers @@ -290,7 +290,8 @@ def create_one_server_action(attrs=None): """Create a fake server action. :param attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.server_action.ServerAction object + :return: A fake :class:`~openstack.compute.v2.server_action.ServerAction` + object """ attrs = attrs or {} @@ -333,7 +334,7 @@ def create_one_flavor(attrs=None): """Create a fake flavor. :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.flavor.Flavor object + :return: A fake :class:`~openstack.compute.v2.flavor.Flavor` object """ attrs = attrs or {} @@ -366,7 +367,7 @@ def create_flavors(attrs=None, count=2): :param dict attrs: A dictionary with all attributes :param int count: The number of flavors to fake - :return: A list of fake openstack.compute.v2.flavor.Flavor objects + :return: A list of fake :class:`openstack.compute.v2.flavor.Flavor` objects """ flavors = [] for i in range(0, count): @@ -405,8 +406,8 @@ def create_one_availability_zone(attrs=None): """Create a fake AZ. :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.availability_zone.AvailabilityZone - object + :return: A fake + :class:`~openstack.compute.v2.availability_zone.AvailabilityZone` object """ attrs = attrs or {} @@ -684,7 +685,7 @@ def create_one_migration(attrs=None): """Create a fake migration. :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.migration.Migration object + :return: A fake :class:`~openstack.compute.v2.migration.Migration` object """ attrs = attrs or {} @@ -720,7 +721,7 @@ def create_migrations(attrs=None, count=2): :param dict attrs: A dictionary with all attributes :param int count: The number of migrations to fake - :return: A list of fake openstack.compute.v2.migration.Migration objects + :return: A list of fake :class:`openstack.compute.v2.migration.Migration` objects """ migrations = [] for i in range(0, count): @@ -733,7 +734,8 @@ def create_one_server_migration(attrs=None): """Create a fake server migration. :param dict attrs: A dictionary with all attributes - :return A fake openstack.compute.v2.server_migration.ServerMigration object + :return: A fake + :class:`~openstack.compute.v2.server_migration.ServerMigration` object """ attrs = attrs or {} @@ -789,8 +791,8 @@ def create_one_volume_attachment(attrs=None): """Create a fake volume attachment. :param dict attrs: A dictionary with all attributes - :return: A fake openstack.compute.v2.volume_attachment.VolumeAttachment - object + :return: A fake + :class:`~openstack.compute.v2.volume_attachment.VolumeAttachment` object """ attrs = attrs or {} @@ -831,12 +833,12 @@ def create_volume_attachments(attrs=None, count=2): def create_one_server_interface(attrs=None): - """Create a fake SDK ServerInterface. + """Create a fake ServerInterface. :param dict attrs: A dictionary with all attributes :param dict methods: A dictionary with all methods - :return: A fake openstack.compute.v2.server_interface.ServerInterface - object + :return: A fake + :class:`~openstack.compute.v2.server_interface.ServerInterface` object """ attrs = attrs or {} diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 37e009163..34b06968f 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -70,7 +70,7 @@ def setUp(self): self.attrs = {} def setup_sdk_servers_mock(self, count): - servers = compute_fakes.create_sdk_servers( + servers = compute_fakes.create_servers( attrs=self.attrs, count=count, ) @@ -348,7 +348,7 @@ def setUp(self): super().setUp() self.app.client_manager.network_endpoint_enabled = False - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.cmd = server.AddFloatingIP(self.app, None) @@ -404,7 +404,7 @@ class TestServerAddFloatingIPNetwork( def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.network_client.update_ip = mock.Mock(return_value=None) @@ -700,7 +700,7 @@ class TestServerVolume(TestServer): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.volume = volume_fakes.create_one_sdk_volume() @@ -1126,7 +1126,7 @@ class TestServerAddSecurityGroup(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.compute_client.add_security_group_to_server.return_value = None @@ -1295,7 +1295,7 @@ def setUp(self): 'image': self.image, 'flavor': self.flavor, } - self.server = compute_fakes.create_one_sdk_server(attrs=attrs) + self.server = compute_fakes.create_one_server(attrs=attrs) self.compute_client.create_server.return_value = self.server self.compute_client.get_server.return_value = self.server @@ -4382,7 +4382,7 @@ class TestServerDelete(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.compute_client.delete_server.return_value = None @@ -4429,7 +4429,7 @@ def test_server_delete_with_force(self): self.assertIsNone(result) def test_server_delete_multi_servers(self): - servers = compute_fakes.create_sdk_servers(count=3) + servers = compute_fakes.create_servers(count=3) self.compute_client.find_server.return_value = None self.compute_client.find_server.side_effect = servers @@ -5511,7 +5511,7 @@ def test_server_list_v269_with_partial_constructs(self): class TestServerAction(compute_fakes.TestComputev2): def run_method_with_sdk_servers(self, method_name, server_count): - servers = compute_fakes.create_sdk_servers(count=server_count) + servers = compute_fakes.create_servers(count=server_count) self.compute_client.find_server.side_effect = servers arglist = [s.id for s in servers] @@ -5544,7 +5544,7 @@ def test_server_lock_multi_servers(self): def test_server_lock_with_reason(self): self.set_compute_api_version('2.73') - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.compute_client.lock_server.return_value = None @@ -5573,8 +5573,8 @@ def test_server_lock_with_reason(self): def test_server_lock_with_reason_multi_servers(self): self.set_compute_api_version('2.73') - server_a = compute_fakes.create_one_sdk_server() - server_b = compute_fakes.create_one_sdk_server() + server_a = compute_fakes.create_one_server() + server_b = compute_fakes.create_one_server() self.compute_client.find_server.side_effect = [server_a, server_b] self.compute_client.lock_server.return_value = None @@ -5603,7 +5603,7 @@ def test_server_lock_with_reason_multi_servers(self): def test_server_lock_with_reason_pre_v273(self): self.set_compute_api_version('2.72') - server = compute_fakes.create_one_sdk_server() + server = compute_fakes.create_one_server() arglist = [ server.id, @@ -5631,7 +5631,7 @@ class TestServerMigrate(TestServer): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.compute_client.migrate_server.return_value = None self.compute_client.live_migrate_server.return_value = None @@ -6176,7 +6176,7 @@ def setUp(self): 'status': 'ACTIVE', 'image': {'id': self.image.id}, } - self.server = compute_fakes.create_one_sdk_server(attrs=attrs) + self.server = compute_fakes.create_one_server(attrs=attrs) self.compute_client.find_server.return_value = self.server self.compute_client.rebuild_server.return_value = self.server @@ -6911,7 +6911,7 @@ def setUp(self): 'status': 'ACTIVE', 'image': '', } - self.server = compute_fakes.create_one_sdk_server(attrs=attrs) + self.server = compute_fakes.create_one_server(attrs=attrs) self.compute_client.find_server.return_value = self.server self.compute_client.rebuild_server.return_value = self.server @@ -7002,9 +7002,9 @@ def setUp(self): 'networks': {}, 'adminPass': 'passw0rd', } - self.server = compute_fakes.create_one_sdk_server(attrs=attrs) + self.server = compute_fakes.create_one_server(attrs=attrs) attrs['id'] = self.server.id - self.new_server = compute_fakes.create_one_sdk_server(attrs=attrs) + self.new_server = compute_fakes.create_one_server(attrs=attrs) # Return value for utils.find_resource for server. self.compute_client.find_server.return_value = self.server @@ -7146,7 +7146,7 @@ class TestServerRemoveFixedIP(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() # Get the command object to test self.cmd = server.RemoveFixedIP(self.app, None) @@ -7175,7 +7175,7 @@ class TestServerRescue(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.cmd = server.RescueServer(self.app, None) @@ -7255,7 +7255,7 @@ def setUp(self): super().setUp() self.app.client_manager.network_endpoint_enabled = False - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.cmd = server.RemoveFloatingIP(self.app, None) @@ -7417,7 +7417,7 @@ class TestServerRemoveSecurityGroup(TestServer): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.compute_client.remove_security_group_from_server.return_value = ( None @@ -7480,7 +7480,7 @@ class TestServerResize(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.flavor = compute_fakes.create_one_flavor() self.compute_client.find_flavor.return_value = self.flavor @@ -7688,7 +7688,7 @@ class TestServerResizeConfirm(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.compute_client.confirm_server_resize.return_value = None @@ -7720,7 +7720,7 @@ class TestServerMigrateConfirm(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.compute_client.confirm_server_resize.return_value = None @@ -7758,7 +7758,7 @@ class TestServerConfirmMigration(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.compute_client.confirm_server_resize.return_value = None @@ -7789,7 +7789,7 @@ class TestServerResizeRevert(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.compute_client.revert_server_resize.return_value = None @@ -7821,7 +7821,7 @@ class TestServerMigrateRevert(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.compute_client.revert_server_resize.return_value = None @@ -7859,7 +7859,7 @@ class TestServerRevertMigration(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.compute_client.revert_server_resize.return_value = None @@ -7918,7 +7918,7 @@ class TestServerSet(TestServer): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server # Get the command object to test @@ -8300,7 +8300,7 @@ class TestServerShelve(TestServer): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server( + self.server = compute_fakes.create_one_server( attrs={'status': 'ACTIVE'}, ) @@ -8434,7 +8434,7 @@ def setUp(self): self.compute_client.get_server_diagnostics.return_value = { 'test': 'test' } - self.server = compute_fakes.create_one_sdk_server( + self.server = compute_fakes.create_one_server( attrs=server_info, ) self.server.fetch_topology = mock.MagicMock(return_value=self.topology) @@ -8701,7 +8701,7 @@ def setUp(self): ], }, } - self.server = compute_fakes.create_one_sdk_server( + self.server = compute_fakes.create_one_server( attrs=self.attrs, ) self.compute_client.find_server.return_value = self.server @@ -8822,7 +8822,7 @@ def test_server_start_multi_servers(self): self.run_method_with_sdk_servers('start_server', 3) def test_server_start_with_all_projects(self): - server = compute_fakes.create_one_sdk_server() + server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = server arglist = [ @@ -8858,7 +8858,7 @@ def test_server_stop_multi_servers(self): self.run_method_with_sdk_servers('stop_server', 3) def test_server_start_with_all_projects(self): - server = compute_fakes.create_one_sdk_server() + server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = server arglist = [ @@ -8926,7 +8926,7 @@ class TestServerUnrescue(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.cmd = server.UnrescueServer(self.app, None) @@ -8955,7 +8955,7 @@ class TestServerUnset(TestServer): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server # Get the command object to test @@ -9105,7 +9105,7 @@ class TestServerUnshelve(TestServer): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server( + self.server = compute_fakes.create_one_server( attrs={'status': 'SHELVED'}, ) @@ -9427,7 +9427,7 @@ def test_prep_server_detail(self): 'properties': '', 'volumes_attached': [{"id": "6344fe9d-ef20-45b2-91a6"}], } - _server = compute_fakes.create_one_sdk_server(server_info) + _server = compute_fakes.create_one_server(server_info) self.compute_client.get_server.return_value = _server expected = { @@ -9514,7 +9514,7 @@ def test_prep_server_detail_v247(self): 'properties': '', 'volumes_attached': [{"id": "6344fe9d-ef20-45b2-91a6"}], } - _server = compute_fakes.create_one_sdk_server(server_info) + _server = compute_fakes.create_one_server(server_info) self.compute_client.get_server.return_value = _server expected = { diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py index e473c7e1b..0d039d210 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_backup.py +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -50,7 +50,7 @@ def image_data(self, image): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.image = image_fakes.create_one_image( diff --git a/openstackclient/tests/unit/compute/v2/test_server_event.py b/openstackclient/tests/unit/compute/v2/test_server_event.py index a5e5116a1..710a82ea7 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_event.py +++ b/openstackclient/tests/unit/compute/v2/test_server_event.py @@ -22,7 +22,7 @@ class TestListServerEvent(compute_fakes.TestComputev2): - fake_server = compute_fakes.create_one_sdk_server() + fake_server = compute_fakes.create_one_server() fake_event = compute_fakes.create_one_server_action() columns = ( @@ -366,7 +366,7 @@ def test_server_event_list_with_marker_pre_v258(self): class TestShowServerEvent(compute_fakes.TestComputev2): - fake_server = compute_fakes.create_one_sdk_server() + fake_server = compute_fakes.create_one_server() fake_event = compute_fakes.create_one_server_action() columns = ( 'action', diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py index f4a56ff13..726bc81ec 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_image.py +++ b/openstackclient/tests/unit/compute/v2/test_server_image.py @@ -49,7 +49,7 @@ def image_data(self, image): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.image = image_fakes.create_one_image( diff --git a/openstackclient/tests/unit/compute/v2/test_server_migration.py b/openstackclient/tests/unit/compute/v2/test_server_migration.py index 362c9bae6..5016c2572 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_migration.py +++ b/openstackclient/tests/unit/compute/v2/test_server_migration.py @@ -52,7 +52,7 @@ class TestListMigration(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.migrations = compute_fakes.create_migrations(count=3) @@ -694,7 +694,7 @@ class TestServerMigrationShow(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server self.server_migration = compute_fakes.create_one_server_migration() @@ -897,7 +897,7 @@ class TestServerMigrationAbort(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() # Return value for utils.find_resource for server. self.compute_client.find_server.return_value = self.server @@ -1012,7 +1012,7 @@ class TestServerMigrationForceComplete(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_sdk_server() + self.server = compute_fakes.create_one_server() # Return value for utils.find_resource for server. self.compute_client.find_server.return_value = self.server diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 5e48e311e..9221f2fa3 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -1377,7 +1377,7 @@ def test_port_list_router_opt(self): self.assertCountEqual(self.data, list(data)) def test_port_list_with_server_option(self): - fake_server = compute_fakes.create_one_sdk_server() + fake_server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = fake_server arglist = [ diff --git a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py index d4dc469e3..b7838e034 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py @@ -28,7 +28,7 @@ def setUp(self): class TestVolumeAttachmentCreate(TestVolumeAttachment): volume = volume_fakes.create_one_volume() - server = compute_fakes.create_one_sdk_server() + server = compute_fakes.create_one_server() volume_attachment = volume_fakes.create_one_volume_attachment( attrs={'instance': server.id, 'volume_id': volume.id}, ) From 7d6400319679d2ccc3c65d54ad5d79e25000a41e Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 14 Apr 2025 16:24:32 +0100 Subject: [PATCH 134/245] tests: Stop returning FakeResource in compute tests This was still being used in places where we have our own API bindings because SDK does not support the API. Those bindings should be returning dicts, not FakeResource objects. Correct this, and in doing so fix the bug this highlights in our cell-down output. Change-Id: I6647d94fcf5ada8186edbf64c03abd3d8ae7ca56 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 2 +- .../tests/unit/compute/v2/fakes.py | 156 +++++++----------- .../tests/unit/compute/v2/test_flavor.py | 2 +- .../tests/unit/compute/v2/test_server.py | 9 +- .../v2/test_security_group_rule_compute.py | 4 +- 5 files changed, 63 insertions(+), 110 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 34d569bec..4260e7888 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -3004,7 +3004,7 @@ def take_action(self, parsed_args): # infrastructure failure situations. # For those servers with partial constructs we just skip the # processing of the image and flavor information. - if not hasattr(s, 'image') or not hasattr(s, 'flavor'): + if getattr(s, 'status') == 'UNKNOWN': continue if 'id' in s.image and s.image.id is not None: diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 878d5910c..37f93b772 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -13,7 +13,6 @@ # under the License. # -import copy import random import re from unittest import mock @@ -32,7 +31,6 @@ from openstack.compute.v2 import server_migration as _server_migration from openstack.compute.v2 import volume_attachment as _volume_attachment -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit.image.v2 import fakes as image_fakes from openstackclient.tests.unit.network.v2 import fakes as network_fakes @@ -79,16 +77,14 @@ class TestComputev2( def create_one_agent(attrs=None): """Create a fake agent. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with agent_id, os, and so on + :param dict attrs: A dictionary with all attributes + :return: A dicionarty faking the agent """ attrs = attrs or {} # set default attributes. - agent_info = { + agent_attrs = { 'agent_id': 'agent-id-' + uuid.uuid4().hex, 'os': 'agent-os-' + uuid.uuid4().hex, 'architecture': 'agent-architecture', @@ -98,22 +94,20 @@ def create_one_agent(attrs=None): 'hypervisor': 'hypervisor', } + assert not set(attrs) - set(agent_attrs), 'unknown keys' + # Overwrite default attributes. - agent_info.update(attrs) + agent_attrs.update(attrs) - agent = fakes.FakeResource(info=copy.deepcopy(agent_info), loaded=True) - return agent + return agent_attrs def create_agents(attrs=None, count=2): """Create multiple fake agents. - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of agents to fake - :return: - A list of FakeResource objects faking the agents + :param dict attrs: A dictionary with all attributes + :param int count: The number of agents to fake + :return: A list of dictionaries faking the agents """ agents = [] for i in range(0, count): @@ -158,10 +152,8 @@ def create_one_extension(attrs=None): def create_one_security_group(attrs=None): """Create a fake security group. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, name, etc. + :param dict attrs: A dictionary with all attributes + :return: A dictionary faking the security group """ attrs = attrs or {} @@ -174,6 +166,8 @@ def create_one_security_group(attrs=None): 'rules': [], } + assert not set(attrs) - set(security_group_attrs), 'unknown keys' + # Overwrite default attributes. security_group_attrs.update(attrs) return security_group_attrs @@ -182,12 +176,9 @@ def create_one_security_group(attrs=None): def create_security_groups(attrs=None, count=2): """Create multiple fake security groups. - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of security groups to fake - :return: - A list of FakeResource objects faking the security groups + :param dict attrs: A dictionary with all attributes + :param int count: The number of security groups to fake + :return: A list of dictionaries faking the security groups """ security_groups = [] for i in range(0, count): @@ -199,10 +190,8 @@ def create_security_groups(attrs=None, count=2): def create_one_security_group_rule(attrs=None): """Create a fake security group rule. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, etc. + :param dict attrs: A dictionary with all attributes + :return: A dictionary faking the security group rule """ attrs = attrs or {} @@ -217,6 +206,8 @@ def create_one_security_group_rule(attrs=None): 'to_port': 0, } + assert not set(attrs) - set(security_group_rule_attrs), 'unknown keys' + # Overwrite default attributes. security_group_rule_attrs.update(attrs) @@ -226,12 +217,9 @@ def create_one_security_group_rule(attrs=None): def create_security_group_rules(attrs=None, count=2): """Create multiple fake security group rules. - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of security group rules to fake - :return: - A list of FakeResource objects faking the security group rules + :param dict attrs: A dictionary with all attributes + :param int count: The number of security group rules to fake + :return: A list of dictionaries faking the security group rules """ security_group_rules = [] for i in range(0, count): @@ -379,10 +367,8 @@ def create_flavors(attrs=None, count=2): def create_one_flavor_access(attrs=None): """Create a fake flavor access. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with flavor_id, tenat_id + :param dict attrs: A dictionary with all attributes + :return: A dictionary faking the flavor access """ attrs = attrs or {} @@ -392,14 +378,12 @@ def create_one_flavor_access(attrs=None): 'tenant_id': 'tenant-id-' + uuid.uuid4().hex, } + assert not set(attrs) - set(flavor_access_info), 'unknown keys' + # Overwrite default attributes. flavor_access_info.update(attrs) - flavor_access = fakes.FakeResource( - info=copy.deepcopy(flavor_access_info), loaded=True - ) - - return flavor_access + return flavor_access_info def create_one_availability_zone(attrs=None): @@ -454,12 +438,10 @@ def create_availability_zones(attrs=None, count=2): def create_one_floating_ip(attrs=None): - """Create a fake floating ip. + """Create a fake floating IP. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, ip, and so on + :param dict attrs: A dictionary with all attributes + :return: A dictionary faking the floating IP """ attrs = attrs or {} @@ -472,6 +454,8 @@ def create_one_floating_ip(attrs=None): 'pool': 'public', } + assert not set(attrs) - set(floating_ip_attrs), 'unknown keys' + # Overwrite default attributes. floating_ip_attrs.update(attrs) @@ -479,14 +463,11 @@ def create_one_floating_ip(attrs=None): def create_floating_ips(attrs=None, count=2): - """Create multiple fake floating ips. - - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of floating ips to fake - :return: - A list of FakeResource objects faking the floating ips + """Create multiple fake floating IPs. + + :param dict attrs: A dictionary with all attributes + :param int count: The number of floating IPs to fake + :return: A list of dictionaries faking the floating IPs """ floating_ips = [] for i in range(0, count): @@ -494,32 +475,11 @@ def create_floating_ips(attrs=None, count=2): return floating_ips -def get_floating_ips(floating_ips=None, count=2): - """Get an iterable MagicMock object with a list of faked floating ips. - - If floating_ips list is provided, then initialize the Mock object - with the list. Otherwise create one. - - :param List floating_ips: - A list of FakeResource objects faking floating ips - :param int count: - The number of floating ips to fake - :return: - An iterable Mock object with side_effect set to a list of faked - floating ips - """ - if floating_ips is None: - floating_ips = create_floating_ips(count) - return mock.Mock(side_effect=floating_ips) - - def create_one_floating_ip_pool(attrs=None): - """Create a fake floating ip pool. + """Create a fake floating IP pool. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with name, etc + :param dict attrs: A dictionary with all attributes + :return: A dictionary faking the floating IP pool """ if attrs is None: attrs = {} @@ -529,6 +489,8 @@ def create_one_floating_ip_pool(attrs=None): 'name': 'floating-ip-pool-name-' + uuid.uuid4().hex, } + assert not set(attrs) - set(floating_ip_pool_attrs), 'unknown keys' + # Overwrite default attributes. floating_ip_pool_attrs.update(attrs) @@ -536,14 +498,11 @@ def create_one_floating_ip_pool(attrs=None): def create_floating_ip_pools(attrs=None, count=2): - """Create multiple fake floating ip pools. - - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of floating ip pools to fake - :return: - A list of FakeResource objects faking the floating ip pools + """Create multiple fake floating IP pools. + + :param dict attrs: A dictionary with all attributes + :param int count: The number of floating IP pools to fake + :return: A list of dictionaries faking the floating IP pools """ floating_ip_pools = [] for i in range(0, count): @@ -554,10 +513,8 @@ def create_floating_ip_pools(attrs=None, count=2): def create_one_network(attrs=None): """Create a fake network. - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with id, label, cidr and so on + :param dict attrs: A dictionary with all attributes + :return: A dictionary faking the network """ attrs = attrs or {} @@ -597,6 +554,8 @@ def create_one_network(attrs=None): 'vpn_public_port': None, } + assert not set(attrs) - set(network_attrs), 'unknown keys' + # Overwrite default attributes. network_attrs.update(attrs) @@ -606,12 +565,9 @@ def create_one_network(attrs=None): def create_networks(attrs=None, count=2): """Create multiple fake networks. - :param dict attrs: - A dictionary with all attributes - :param int count: - The number of networks to fake - :return: - A list of FakeResource objects faking the networks + :param dict attrs: A dictionary with all attributes + :param int count: The number of networks to fake + :return: A list of dictionaries faking the networks """ networks = [] for i in range(0, count): diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py index 4234fbbd5..fa8d5ad56 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -1052,7 +1052,7 @@ def test_private_flavor_show(self): data_with_project = ( private_flavor.is_disabled, private_flavor.ephemeral, - [self.flavor_access.tenant_id], + [self.flavor_access['tenant_id']], private_flavor.description, private_flavor.disk, private_flavor.id, diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 34b06968f..eb0b934e3 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -21,6 +21,7 @@ import uuid import iso8601 +from openstack.compute.v2 import server as _server from openstack.compute.v2 import server_group as _server_group from openstack import exceptions as sdk_exceptions from openstack.test import fakes as sdk_fakes @@ -5487,9 +5488,7 @@ def test_server_list_v269_with_partial_constructs(self): ], "networks": {}, } - fake_server = compute_fakes.fakes.FakeResource( - info=server_dict, - ) + fake_server = _server.Server(**server_dict) self.servers.append(fake_server) columns, data = self.cmd.take_action(parsed_args) # get the first three servers out since our interest is in the partial @@ -5500,9 +5499,9 @@ def test_server_list_v269_with_partial_constructs(self): partial_server = next(data) expected_row = ( 'server-id-95a56bfc4xxxxxx28d7e418bfd97813a', - '', + None, 'UNKNOWN', - server.AddressesColumn(''), + server.AddressesColumn(None), '', '', ) diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py b/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py index 678ff8a30..9cab52e39 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py @@ -383,7 +383,6 @@ class TestListSecurityGroupRuleCompute(compute_fakes.TestComputev2): _security_group_rule_tcp = compute_fakes.create_one_security_group_rule( { 'ip_protocol': 'tcp', - 'ethertype': 'IPv4', 'from_port': 80, 'to_port': 80, 'group': {'name': _security_group['name']}, @@ -392,7 +391,6 @@ class TestListSecurityGroupRuleCompute(compute_fakes.TestComputev2): _security_group_rule_icmp = compute_fakes.create_one_security_group_rule( { 'ip_protocol': 'icmp', - 'ethertype': 'IPv4', 'from_port': -1, 'to_port': -1, 'ip_range': {'cidr': '10.0.2.0/24'}, @@ -426,7 +424,7 @@ class TestListSecurityGroupRuleCompute(compute_fakes.TestComputev2): expected_rule_with_group = ( rule['id'], rule['ip_protocol'], - rule['ethertype'], + '', # ethertype is a neutron-only thing rule['ip_range'], rule['port_range'], rule['remote_security_group'], From e4d621d24fec9629e4764494200d37e552959be3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 16 Apr 2025 10:59:36 +0100 Subject: [PATCH 135/245] zuul: Remove osc-upload-image, osc-promote-image jobs We are no longer going to publish these images to Dockerhub, given the recent changes to quotas there coupled with the fact that no one appears to be using them [1]. The osc-build-image job is retained to ensure our Dockerfile keeps working. [1] https://lists.openstack.org/archives/list/openstack-discuss@lists.openstack.org/thread/BE7PPQL4DGNDZ2SIMUVSK67I5NF3TFCX/ Change-Id: I9d2ca8f90b8244a09832da673491312095520968 Signed-off-by: Stephen Finucane --- .zuul.yaml | 52 ++-------------------------------------------------- 1 file changed, 2 insertions(+), 50 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index 2654db30a..a320f6d02 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -137,22 +137,6 @@ tox_envlist: functional tox_install_siblings: true -- secret: - name: osc-dockerhub - data: - username: osclientzuul - password: !encrypted/pkcs1-oaep - - LbIZjJiVstRVXMpoLQ3+/JcNB6lKVUWJXXo5+Outf+PKAaO7mNnv8XLiFMKnJ6ftopLyu - hWbX9rA+NddvplLQkf1xxkh7QBBU8PToLr58quI2SENUclt4tpjxbZfZu451kFSNJvNvR - E58cHHpfJZpyRnS2htXmN/Qy24gbV2w7CQxSZD2YhlcrerD8uQ8rWEnlY1wcJEaEGomtS - ZTGxsdK2TsZC2cd4b7TG7+xbl2i+hjADzwSQAgUzlLlwuG71667+IWk4SOZ7OycJTv9NN - ZTak8+CGfiMKdmsxZ1Z8uD7DC+RIklDjMWyly6zuhWzfhOmsmU0CesR50moodRUvbK79p - NZM8u0hBex5cl2EpUEwJL/FSPJXUhDMPoMoTZT/SAuXf25R9eZ9JGrKsIAlmVhpl8ifoE - 8TpPyvIHGS3YelTQjhqOX0wGb9T4ZauQCcI5Ajzy9NuCTyD9xxme9OX1zz7gMACRnVHvz - q7U7Ue90MnmGH6E2SgKjIZhyzy9Efwb7JUvH1Zb3hlrjCjEhwi9MV5FnABTEeXyYwE10s - 3o/KZg2zvdWkVG6x0dEkjpoQaNuaB7T2Na7Sm421n/z3LCzhiQGuTUjENnL6cMEtuA6Pp - BfI5+Qlg7HMwkBXNB73EPfWHzbCR3VNrzGYTy9FvhGud0/cXsuBXgps4WH63ic= - - job: name: osc-build-image parent: opendev-build-docker-image @@ -162,38 +146,10 @@ - python-builder-3.11-bookworm-container-image - python-base-3.11-bookworm-container-image provides: osc-container-image - vars: &osc_image_vars + vars: docker_images: - context: . - repository: osclient/python-openstackclient - -- job: - name: osc-upload-image - parent: opendev-upload-docker-image - description: Build Docker images and upload to Docker Hub. - allowed-projects: openstack/python-openstackclient - requires: - - python-builder-3.11-bookworm-container-image - - python-base-3.11-bookworm-container-image - provides: osc-container-image - secrets: - - name: docker_credentials - secret: osc-dockerhub - pass-to-parent: true - vars: *osc_image_vars - -- job: - name: osc-promote-image - parent: opendev-promote-docker-image - allowed-projects: openstack/python-openstackclient - description: Promote previously uploaded Docker images. - secrets: - - name: docker_credentials - secret: osc-dockerhub - pass-to-parent: true - nodeset: - nodes: [] - vars: *osc_image_vars + tags: [] - project-template: name: osc-tox-unit-tips @@ -226,8 +182,4 @@ branches: ^master$ gate: jobs: - - osc-upload-image - osc-functional-devstack - promote: - jobs: - - osc-promote-image From f4e97d97336b9c1abd9dcffdabcf4b401bce4530 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 16 Apr 2025 11:18:14 +0100 Subject: [PATCH 136/245] Update the docker image to python3.12 Change-Id: I2120ac8d27bcefffa0b414cb74871922ccd2ad80 Signed-off-by: Stephen Finucane --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 420fdb5ea..c0e02dd19 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,12 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM docker.io/opendevorg/python-builder:3.11-bookworm as builder +FROM docker.io/opendevorg/python-builder:3.12-bookworm AS builder COPY . /tmp/src RUN assemble -FROM docker.io/opendevorg/python-base:3.11-bookworm +FROM docker.io/opendevorg/python-base:3.12-bookworm COPY --from=builder /output/ /output RUN /output/install-from-bindep From 22eecc54f846b66f13ad781988078f13b924677f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 16 Apr 2025 11:25:11 +0100 Subject: [PATCH 137/245] Add labels to Dockerfile Change-Id: Ic2c774c4fea263c7b04d20182e3354d9ae93788b Signed-off-by: Stephen Finucane --- Dockerfile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Dockerfile b/Dockerfile index c0e02dd19..6709be751 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,13 @@ RUN assemble FROM docker.io/opendevorg/python-base:3.12-bookworm +LABEL org.opencontainers.image.title="python-openstackclient" +LABEL org.opencontainers.image.description="Client for OpenStack services." +LABEL org.opencontainers.image.licenses="Apache License 2.0" +LABEL org.opencontainers.image.url="https://www.openstack.org/" +LABEL org.opencontainers.image.documentation="https://docs.openstack.org/python-openstackclient/latest/" +LABEL org.opencontainers.image.source="https://opendev.org/openstack/python-openstackclient" + COPY --from=builder /output/ /output RUN /output/install-from-bindep From 6cb5d8cd1476b0415f85033582312887f4b9ea18 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 16 Apr 2025 12:16:29 +0100 Subject: [PATCH 138/245] Update README Restructure things to be a little more helpful. Also add a reference to the Dockerfile we provide and remove an errant header that should have been removed in change Ife108e6ae191641b56e872e4616a3f4ec78281e8. Change-Id: I5f562a99ccee7db485b5d40ef4ea6f2e2e362c13 Signed-off-by: Stephen Finucane --- README.rst | 198 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 148 insertions(+), 50 deletions(-) diff --git a/README.rst b/README.rst index ff1b42c8e..af3837a35 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,3 @@ -======================== -Team and repository tags -======================== - =============== OpenStackClient =============== @@ -10,94 +6,158 @@ OpenStackClient :target: https://pypi.org/project/python-openstackclient/ :alt: Latest Version -OpenStackClient (aka OSC) is a command-line client for OpenStack that brings +OpenStackClient (OSC) is a command-line client for OpenStack that brings the command set for Compute, Identity, Image, Network, Object Store and Block Storage APIs together in a single shell with a uniform command structure. +Support for additional service APIs is provided via plugins. The primary goal is to provide a unified shell command structure and a common language to describe operations in OpenStack. -* `PyPi`_ - package installation -* `Online Documentation`_ -* `Launchpad project`_ - bugs and feature requests -* `Blueprints`_ - feature specifications (historical only) -* `Source`_ -* `Developer`_ - getting started as a developer -* `Contributing`_ - contributing code -* `Testing`_ - testing code -* IRC: #openstack-sdks on OFTC (irc.oftc.net) -* License: Apache 2.0 - -.. _PyPi: https://pypi.org/project/python-openstackclient -.. _Online Documentation: https://docs.openstack.org/python-openstackclient/latest/ -.. _Blueprints: https://blueprints.launchpad.net/python-openstackclient -.. _`Launchpad project`: https://bugs.launchpad.net/python-openstackclient -.. _Source: https://opendev.org/openstack/python-openstackclient -.. _Developer: https://docs.openstack.org/project-team-guide/project-setup/python.html -.. _Contributing: https://docs.openstack.org/infra/manual/developers.html -.. _Testing: https://docs.openstack.org/python-openstackclient/latest/contributor/developing.html#testing -.. _Release Notes: https://docs.openstack.org/releasenotes/python-openstackclient - Getting Started =============== -OpenStack Client can be installed from PyPI using pip:: +OpenStack Client can be installed from PyPI using pip: + +.. code-block:: shell python3 -m pip install python-openstackclient -There are a few variants on getting help. A list of global options and supported -commands is shown with ``--help``:: +You can use ``--help`` or the ``help`` command to get a list of global options +and supported commands: + +.. code-block:: shell openstack --help + openstack help -There is also a ``help`` command that can be used to get help text for a specific -command:: +You can also get help for a specific command: - openstack help +.. code-block:: shell + + openstack server create --help openstack help server create -If you want to make changes to the OpenStackClient for testing and contribution, -make any changes and then run:: +You can add support for additional services by installing their clients. For +example, to add support for the DNS service (designate): + +.. code-block:: shell + + python3 -m pip install python3-designateclient + +A ``Dockerfile`` is provided for your convenience in the repository. You can +use this to build your own container images: + +.. code-block:: shell git clone https://opendev.org/openstack/python-openstackclient cd python-openstackclient - python3 -m pip install -e . + podman build . -t example.com/myuser/openstackclient -Configuration -============= +For more information the available options and commands, refer to the `Users +Guide`__. -The CLI is configured via environment variables and command-line -options as listed in https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html. +.. __: https://docs.openstack.org/python-openstackclient/latest/cli/index.html -Authentication using username/password is most commonly used: +Configuration +============= -- For a local user, your configuration will look like the one below:: +OpenStack Client must be configured with authentication information in order to +communicate with a given OpenStack cloud. This configuration can be achieved +via a ``clouds.yaml`` file, a set of environment variables (often shared via an +``openrc`` file), a set of command-line options, or a combination of all three. +Your cloud provider or deployment tooling will typically provide either a +``clouds.yaml`` file or ``openrc`` file for you. If using a ``clouds.yaml`` +file, OpenStack Client expects to find it in one of the following locations: + +* If set, the path indicated by the ``OS_CLIENT_CONFIG_FILE`` environment + variable +* ``.`` (the current directory) +* ``$HOME/.config/openstack`` +* ``/etc/openstack`` + +The options you should set will depend on the configuration of your cloud and +the authentication mechanism(s) supported. For example, consider a cloud that +supports username/password authentication. Configuration for this cloud using a +``clouds.yaml`` file would look like so: + +.. code-block:: yaml + + clouds: + my-cloud: + auth: + auth_url: '' + project_name: '' + project_domain_name: '' + username: '' + user_domain_name: '' + password: '' # (optional) + region_name: '' + +The corresponding environment variables would look very similar: + +.. code-block:: shell export OS_AUTH_URL= - export OS_IDENTITY_API_VERSION=3 + export OS_REGION_NAME= export OS_PROJECT_NAME= export OS_PROJECT_DOMAIN_NAME= export OS_USERNAME= export OS_USER_DOMAIN_NAME= export OS_PASSWORD= # (optional) - The corresponding command-line options look very similar:: +Likewise, the corresponding command-line options would look very similar: - --os-auth-url - --os-identity-api-version 3 +:: + + openstack + --os-auth-url + --os-region --os-project-name --os-project-domain-name --os-username --os-user-domain-name [--os-password ] -- For a federated user, your configuration will look the so:: +.. note:: + + If a password is not provided above (in plaintext), you will be + interactively prompted to provide one securely. + +Some clouds use federated authentication. If this is the case, your +configuration will be slightly more involved. For example, to configure +username/password authentication for a federated user using a ``clouds.yaml`` +file: + +.. code-block:: yaml + + clouds: + my-cloud: + auth: + auth_url: '' + project_name: '' + project_domain_name: '' + username: '' + user_domain_name: '' + password: '' + identity_provider: '' + client_id: '' + client_secret: '' + openid_scope: '' + protocol: '' + access_token_type: '' + discovery_endpoint: '' + auth_type: 'v3oidcpassword' + region_name: '' + +The corresponding environment variables would look very similar: + +.. code-block:: shell export OS_PROJECT_NAME= export OS_PROJECT_DOMAIN_NAME= export OS_AUTH_URL= export OS_IDENTITY_API_VERSION=3 - export OS_AUTH_PLUGIN=openid export OS_AUTH_TYPE=v3oidcpassword export OS_USERNAME= export OS_PASSWORD= @@ -109,7 +169,9 @@ Authentication using username/password is most commonly used: export OS_ACCESS_TOKEN_TYPE= export OS_DISCOVERY_ENDPOINT= - The corresponding command-line options look very similar:: +Likewise, the corresponding command-line options would look very similar: + +.. code-block:: shell --os-project-name --os-project-domain-name @@ -127,5 +189,41 @@ Authentication using username/password is most commonly used: --os-access-token-type --os-discovery-endpoint -If a password is not provided above (in plaintext), you will be interactively -prompted to provide one securely. +For more information on configuring authentication, including an overview of +the many authentication mechanisms supported, refer to the `Authentication +guide`__. For more information on configuration in general, refer to the +`Configuration guide`__. + +.. __: https://docs.openstack.org/python-openstackclient/latest/cli/authentication.html. +.. __: https://docs.openstack.org/python-openstackclient/latest/configuration/index.html + +Contributing +============ + +You can clone the repository from opendev.org:: + + git clone https://opendev.org/openstack/python-openstackclient + cd python-openstackclient + +OpenStack Client uses the same contributor process as other OpenStack projects. +For information on this process, including help on setting up you Gerrit +account and an overview of the CI process, refer to the `OpenStack Contributors +Guide`__. + +For more information on contributing to OpenStack Client itself, including +guidance on how to design new commands and how to report bugs, refer to the +`Contributors Guide`__. + +.. __: https://docs.openstack.org/python-openstackclient/latest/contributor/index.html +.. __: https://docs.opendev.org/opendev/infra-manual/latest/developers.html + +Links +----- + +* `Issue Tracker `_ +* `Code Review `_ +* `Documentation `_ +* `PyPi `_ +* `Mailing list `_ +* `Release Notes `_ +* `IRC (#openstack-sdks on OFTC (irc.oftc.net)) `_ From 5d730f374b63bbc2121a025638b7abe79f156f53 Mon Sep 17 00:00:00 2001 From: Michael Still Date: Wed, 7 May 2025 19:28:45 +1000 Subject: [PATCH 139/245] Add support for spice-direct console types. This patch adds support for Nova microversion 2.99 which exposes the new spice-direct console type and the pre-existing /os-console-auth-token/ API. +----------+----------------------------------------------------------+ | Field | Value | +----------+----------------------------------------------------------+ | protocol | spice | | type | spice-direct | | url | http://127.0.0.1:13002/nova?token=f78009fb-41ad-... | +----------+----------------------------------------------------------+ +----------------------+--------------------------------------+ | Field | Value | +----------------------+--------------------------------------+ | host | 127.0.0.1 | | instance_uuid | f2477018-aa93-... | | internal_access_path | None | | port | 5900 | | tls_port | 5901 | +----------------------+--------------------------------------+ Change-Id: I2d33646d6ac9b25076d69be76dcef8f5c465cd1b Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/940479 --- .../command-objects/console-connection.rst | 10 +++ openstackclient/compute/v2/console.py | 7 ++ .../compute/v2/console_connection.py | 48 +++++++++++++ .../tests/unit/compute/v2/test_console.py | 19 ++++- .../compute/v2/test_console_connection.py | 72 +++++++++++++++++++ ...e-console-auth-token-1eda2bd62060ccfa.yaml | 6 ++ requirements.txt | 2 +- setup.cfg | 2 + 8 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 doc/source/cli/command-objects/console-connection.rst create mode 100644 openstackclient/compute/v2/console_connection.py create mode 100644 openstackclient/tests/unit/compute/v2/test_console_connection.py create mode 100644 releasenotes/notes/compute-add-validate-console-auth-token-1eda2bd62060ccfa.yaml diff --git a/doc/source/cli/command-objects/console-connection.rst b/doc/source/cli/command-objects/console-connection.rst new file mode 100644 index 000000000..c3358050f --- /dev/null +++ b/doc/source/cli/command-objects/console-connection.rst @@ -0,0 +1,10 @@ +================== +console connection +================== + +Server console connection information + +Compute v2 + +.. autoprogram-cliff:: openstack.compute.v2 + :command: console connection show diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py index 0968dcb01..bcabcd2d8 100644 --- a/openstackclient/compute/v2/console.py +++ b/openstackclient/compute/v2/console.py @@ -106,6 +106,13 @@ def get_parser(self, prog_name): const='spice-html5', help=_("Show SPICE console URL"), ) + type_group.add_argument( + '--spice-direct', + dest='url_type', + action='store_const', + const='spice-direct', + help=_("Show SPICE direct protocol native console URL"), + ) type_group.add_argument( '--rdp', dest='url_type', diff --git a/openstackclient/compute/v2/console_connection.py b/openstackclient/compute/v2/console_connection.py new file mode 100644 index 000000000..d90b2ceb9 --- /dev/null +++ b/openstackclient/compute/v2/console_connection.py @@ -0,0 +1,48 @@ +# 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. +# + +"""Compute v2 Console auth token implementations.""" + +from osc_lib.command import command +from osc_lib import utils + +from openstackclient.i18n import _ + + +def _get_console_connection_columns(item): + column_map: dict[str, str] = {} + hidden_columns = ['id', 'location', 'name'] + return utils.get_osc_show_columns_for_sdk_resource( + item, column_map, hidden_columns + ) + + +class ShowConsoleConnectionInformation(command.ShowOne): + _description = _("Show server's remote console connection information") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'token', + metavar='', + help=_("Nova console token to lookup"), + ) + return parser + + def take_action(self, parsed_args): + compute_client = self.app.client_manager.compute + data = compute_client.validate_console_auth_token(parsed_args.token) + display_columns, columns = _get_console_connection_columns(data) + data = utils.get_dict_properties(data, columns) + + return (display_columns, data) diff --git a/openstackclient/tests/unit/compute/v2/test_console.py b/openstackclient/tests/unit/compute/v2/test_console.py index bcff81993..423290c11 100644 --- a/openstackclient/tests/unit/compute/v2/test_console.py +++ b/openstackclient/tests/unit/compute/v2/test_console.py @@ -157,7 +157,7 @@ def test_console_url_show_with_xvpvnc(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - def test_console_url_show_with_spice(self): + def test_console_url_show_with_spice_html5(self): arglist = [ '--spice', 'foo_vm', @@ -174,6 +174,23 @@ def test_console_url_show_with_spice(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) + def test_console_url_show_with_spice_direct(self): + arglist = [ + '--spice-direct', + 'foo_vm', + ] + verifylist = [ + ('url_type', 'spice-direct'), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.compute_client.create_console.assert_called_once_with( + self._server.id, console_type='spice-direct' + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + def test_console_url_show_with_rdp(self): arglist = [ '--rdp', diff --git a/openstackclient/tests/unit/compute/v2/test_console_connection.py b/openstackclient/tests/unit/compute/v2/test_console_connection.py new file mode 100644 index 000000000..ab9cb0c05 --- /dev/null +++ b/openstackclient/tests/unit/compute/v2/test_console_connection.py @@ -0,0 +1,72 @@ +# 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 openstack.compute.v2 import console_auth_token as _console_auth_token +from openstack.test import fakes as sdk_fakes + +from openstackclient.compute.v2 import console_connection +from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes + + +class TestConsoleTokens(compute_fakes.TestComputev2): + def setUp(self): + super().setUp() + + self._console_auth_token = sdk_fakes.generate_fake_resource( + _console_auth_token.ConsoleAuthToken, + host='127.0.0.1', + instance_uuid=uuid.uuid4().hex, + internal_access_path=None, + port=5900, + tls_port=5901, + ) + self.compute_client.validate_console_auth_token.return_value = ( + self._console_auth_token + ) + + self.columns = ( + 'host', + 'instance_uuid', + 'internal_access_path', + 'port', + 'tls_port', + ) + self.data = ( + self._console_auth_token.host, + self._console_auth_token.instance_uuid, + self._console_auth_token.internal_access_path, + self._console_auth_token.port, + self._console_auth_token.tls_port, + ) + + self.cmd = console_connection.ShowConsoleConnectionInformation( + self.app, None + ) + + def test_console_connection_show(self): + arglist = [ + 'token', + ] + verifylist = [ + ('token', 'token'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute_client.validate_console_auth_token.assert_called_once_with( + 'token' + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/releasenotes/notes/compute-add-validate-console-auth-token-1eda2bd62060ccfa.yaml b/releasenotes/notes/compute-add-validate-console-auth-token-1eda2bd62060ccfa.yaml new file mode 100644 index 000000000..5b03061ef --- /dev/null +++ b/releasenotes/notes/compute-add-validate-console-auth-token-1eda2bd62060ccfa.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add support for the new ``spice-direct`` console type, as well as the + exposing the ability for admins to lookup console connection information + via the new ``console connection show`` command. diff --git a/requirements.txt b/requirements.txt index bf2283328..5e9b33d27 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 cryptography>=2.7 # BSD/Apache-2.0 cliff>=3.5.0 # Apache-2.0 iso8601>=0.1.11 # MIT -openstacksdk>=4.4.0 # Apache-2.0 +openstacksdk>=4.5.0 # Apache-2.0 osc-lib>=2.3.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 python-keystoneclient>=3.22.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index a770b4b62..d822a9bf6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -77,6 +77,8 @@ openstack.compute.v2 = console_log_show = openstackclient.compute.v2.console:ShowConsoleLog console_url_show = openstackclient.compute.v2.console:ShowConsoleURL + console_connection_show = openstackclient.compute.v2.console_connection:ShowConsoleConnectionInformation + flavor_create = openstackclient.compute.v2.flavor:CreateFlavor flavor_delete = openstackclient.compute.v2.flavor:DeleteFlavor flavor_list = openstackclient.compute.v2.flavor:ListFlavor From ce2a253d5ac8603c61f4e868e2126354e1cfe4db Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 7 May 2025 12:38:06 +0100 Subject: [PATCH 140/245] Drop support for Python 3.9 Change-Id: If7d8ce2be7081fdcd609c54a211c91439cddce6b Signed-off-by: Stephen Finucane --- .zuul.yaml | 16 ++++++++-------- .../notes/drop-python-39-fc95c2d17a862e3e.yaml | 4 ++++ setup.cfg | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) create mode 100644 releasenotes/notes/drop-python-39-fc95c2d17a862e3e.yaml diff --git a/.zuul.yaml b/.zuul.yaml index a320f6d02..74ca67d57 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -23,8 +23,8 @@ zuul_work_dir: src/opendev.org/openstack/python-openstackclient - job: - name: osc-tox-py39-tips - parent: openstack-tox-py39 + name: osc-tox-py310-tips + parent: openstack-tox-py310 description: | Run unit tests for OpenStackClient with master branch of important libs. @@ -43,8 +43,8 @@ zuul_work_dir: src/opendev.org/openstack/python-openstackclient - job: - name: osc-tox-py312-tips - parent: openstack-tox-py312 + name: osc-tox-py313-tips + parent: openstack-tox-py313 description: | Run unit tests for OpenStackClient with master branch of important libs. @@ -155,12 +155,12 @@ name: osc-tox-unit-tips check: jobs: - - osc-tox-py39-tips - - osc-tox-py312-tips + - osc-tox-py310-tips + - osc-tox-py313-tips gate: jobs: - - osc-tox-py39-tips - - osc-tox-py312-tips + - osc-tox-py310-tips + - osc-tox-py313-tips - project: templates: diff --git a/releasenotes/notes/drop-python-39-fc95c2d17a862e3e.yaml b/releasenotes/notes/drop-python-39-fc95c2d17a862e3e.yaml new file mode 100644 index 000000000..7d8ca21ff --- /dev/null +++ b/releasenotes/notes/drop-python-39-fc95c2d17a862e3e.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + Support for Python 3.9 has been dropped. diff --git a/setup.cfg b/setup.cfg index a770b4b62..83659ea50 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,7 +7,7 @@ description_content_type = text/x-rst author = OpenStack author_email = openstack-discuss@lists.openstack.org home_page = https://docs.openstack.org/python-openstackclient/latest/ -python_requires = >=3.9 +python_requires = >=3.10 classifier = Environment :: OpenStack Intended Audience :: Information Technology @@ -16,10 +16,10 @@ classifier = Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 3 - Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 + Programming Language :: Python :: 3.13 [files] packages = From 7c7c066096433fc11718938e9d03d3915216bd7a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 7 May 2025 12:39:53 +0100 Subject: [PATCH 141/245] Bump Python version used for linters to 3.10 Change-Id: I693516fc2a08218c50d83a3ab121b51254f97958 Signed-off-by: Stephen Finucane --- .pre-commit-config.yaml | 2 +- openstackclient/compute/v2/server.py | 3 +-- openstackclient/shell.py | 2 +- openstackclient/tests/unit/volume/v2/fakes.py | 3 +-- pyproject.toml | 3 ++- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a8382cf11..09a9172bd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: files: .*\.(yaml|yml)$ args: ['--unsafe'] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.2 + rev: v0.11.8 hooks: - id: ruff args: ['--fix', '--unsafe-fixes'] diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 4260e7888..524eae549 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -21,7 +21,6 @@ import json import logging import os -import typing as ty from cliff import columns as cliff_columns import iso8601 @@ -1874,7 +1873,7 @@ def _match_image(image_api, wanted_properties): # Default to empty list if nothing was specified and let nova # decide the default behavior. - networks: ty.Union[str, list[dict[str, str]], None] = [] + networks: str | list[dict[str, str]] | None = [] if 'auto' in parsed_args.nics or 'none' in parsed_args.nics: if len(parsed_args.nics) > 1: diff --git a/openstackclient/shell.py b/openstackclient/shell.py index 50a767ec3..6bbbb5f7b 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -94,7 +94,7 @@ def _load_plugins(self): # instead. mod_versions = getattr(mod, 'API_VERSIONS', None) if mod_versions is not None and not isinstance( - mod_versions, (dict, tuple) + mod_versions, dict | tuple ): raise TypeError( f'Plugin {mod} has incompatible API_VERSIONS. ' diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index 1c183728c..c2303d388 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -14,7 +14,6 @@ import copy import random -import typing as ty from unittest import mock import uuid @@ -92,7 +91,7 @@ def setUp(self): self.volume_sdk_client = self.app.client_manager.sdk_connection.volume self.set_volume_api_version() # default to the lowest - def set_volume_api_version(self, version: ty.Optional[str] = None): + def set_volume_api_version(self, version: str | None = None): """Set a fake block storage API version. :param version: The fake microversion to "support". This must be None diff --git a/pyproject.toml b/pyproject.toml index 2a532cef6..50092d406 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.mypy] -python_version = "3.9" +python_version = "3.10" show_column_numbers = true show_error_context = true ignore_missing_imports = true @@ -22,6 +22,7 @@ ignore_errors = true [tool.ruff] line-length = 79 +target-version = "py310" [tool.ruff.format] quote-style = "preserve" From 94d17b876226c9380dd2b0cf9295e737cfc6401c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 15 May 2025 15:19:12 +0100 Subject: [PATCH 142/245] identity: Fix listing of applications credentials by user Change-Id: I71f1c4f338694e2b50e71b6907c415bbb6a768fa Signed-off-by: Stephen Finucane Closes-bug: #2107354 --- openstackclient/identity/common.py | 52 ++++++++++++++ .../identity/v3/application_credential.py | 4 +- .../v3/test_application_credential.py | 72 ++++++++++++------- 3 files changed, 101 insertions(+), 27 deletions(-) diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index 65ff8852f..412fd7159 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -189,6 +189,16 @@ def find_domain(identity_client, name_or_id): ) +def find_domain_id_sdk( + identity_client, name_or_id, *, validate_actor_existence=True +): + return _find_sdk_id( + identity_client.find_domain, + name_or_id=name_or_id, + validate_actor_existence=validate_actor_existence, + ) + + def find_group(identity_client, name_or_id, domain_name_or_id=None): if domain_name_or_id is None: return _find_identity_resource( @@ -229,6 +239,32 @@ def find_user(identity_client, name_or_id, domain_name_or_id=None): ) +def find_user_id_sdk( + identity_client, + name_or_id, + domain_name_or_id=None, + *, + validate_actor_existence=True, +): + if domain_name_or_id is None: + return _find_sdk_id( + identity_client.find_user, + name_or_id=name_or_id, + validate_actor_existence=validate_actor_existence, + ) + domain_id = find_domain_id_sdk( + identity_client, + name_or_id=domain_name_or_id, + validate_actor_existence=validate_actor_existence, + ) + return _find_sdk_id( + identity_client.find_user, + name_or_id=name_or_id, + validate_actor_existence=validate_actor_existence, + domain_id=domain_id, + ) + + def _find_identity_resource( identity_client_manager, name_or_id, resource_type, **kwargs ): @@ -269,6 +305,22 @@ def _find_identity_resource( return resource_type(None, {'id': name_or_id, 'name': name_or_id}) +def _find_sdk_id( + find_command, name_or_id, *, validate_actor_existence=True, **kwargs +): + try: + resource = find_command( + name_or_id=name_or_id, ignore_missing=False, **kwargs + ) + except sdk_exceptions.ForbiddenException: + return name_or_id + except sdk_exceptions.ResourceNotFound as exc: + if not validate_actor_existence: + return name_or_id + raise exceptions.CommandError from exc + return resource.id + + def get_immutable_options(parsed_args): options = {} if parsed_args.immutable: diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py index 7bc2b8a19..f0dd5a58f 100644 --- a/openstackclient/identity/v3/application_credential.py +++ b/openstackclient/identity/v3/application_credential.py @@ -269,9 +269,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity if parsed_args.user: - user_id = common.find_user( + user_id = common.find_user_id_sdk( identity_client, parsed_args.user, parsed_args.user_domain - ).id + ) else: conn = self.app.client_manager.sdk_connection user_id = conn.config.get_auth().get_user_id(conn.identity) diff --git a/openstackclient/tests/unit/identity/v3/test_application_credential.py b/openstackclient/tests/unit/identity/v3/test_application_credential.py index b53042ed1..a4ecec5dc 100644 --- a/openstackclient/tests/unit/identity/v3/test_application_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py @@ -24,6 +24,7 @@ application_credential as _application_credential, ) from openstack.identity.v3 import role as _role +from openstack.identity.v3 import user as _user from openstack.test import fakes as sdk_fakes from openstackclient.identity.v3 import application_credential from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes @@ -325,6 +326,31 @@ def setUp(self): self.identity_sdk_client.application_credentials.return_value = [ self.application_credential ] + self.user = sdk_fakes.generate_fake_resource(resource_type=_user.User) + self.identity_sdk_client.find_user.return_value = self.user + + self.columns = ( + 'ID', + 'Name', + 'Description', + 'Project ID', + 'Roles', + 'Unrestricted', + 'Access Rules', + 'Expires At', + ) + self.data = ( + ( + self.application_credential.id, + self.application_credential.name, + self.application_credential.description, + self.application_credential.project_id, + '', + self.application_credential.unrestricted, + self.application_credential.access_rules, + self.application_credential.expires_at, + ), + ) # Get the command object to test self.cmd = application_credential.ListApplicationCredential( @@ -339,39 +365,35 @@ def test_application_credential_list(self): conn = self.app.client_manager.sdk_connection user_id = conn.config.get_auth().get_user_id(conn.identity) - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, tuple(data)) + + self.identity_sdk_client.find_user.assert_not_called() self.identity_sdk_client.application_credentials.assert_called_with( user=user_id ) - collist = ( - 'ID', - 'Name', - 'Description', - 'Project ID', - 'Roles', - 'Unrestricted', - 'Access Rules', - 'Expires At', + def test_application_credential_list_user(self): + arglist = ['--user', self.user.name] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + conn = self.app.client_manager.sdk_connection + conn.config.get_auth().get_user_id(conn.identity) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, tuple(data)) + + self.identity_sdk_client.find_user.assert_called_once_with( + name_or_id=self.user.name, ignore_missing=False ) - self.assertEqual(collist, columns) - datalist = ( - ( - self.application_credential.id, - self.application_credential.name, - self.application_credential.description, - self.application_credential.project_id, - self.application_credential.roles, - self.application_credential.unrestricted, - self.application_credential.access_rules, - self.application_credential.expires_at, - ), + self.identity_sdk_client.application_credentials.assert_called_with( + user=self.user.id ) - self.assertEqual(datalist, tuple(data)) class TestApplicationCredentialShow(identity_fakes.TestIdentityv3): From e26b4479258be9ad8991b8b503578dbb75af828c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 15 May 2025 16:02:28 +0100 Subject: [PATCH 143/245] identity: Add missing user argument Change-Id: Ifd2b32e97d1f5fd426f333da13852a8bb6821f1b Signed-off-by: Stephen Finucane Closes-bug: #2110765 --- openstackclient/identity/v3/user.py | 6 +++++- .../tests/unit/identity/v3/test_user.py | 21 +++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index bbf3f1df5..5be69bc65 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -659,6 +659,8 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) # FIXME(gyee): there are two scenarios: # @@ -701,7 +703,9 @@ def take_action(self, parsed_args): ) identity_client.update_user( - current_password=current_password, password=password + user=user_id, + current_password=current_password, + password=password, ) diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index 91e8b45c3..d9e16f174 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -1686,11 +1686,14 @@ def test_user_password_change(self): # Mock getting user current password. with self._mock_get_password(current_pass): result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) self.identity_sdk_client.update_user.assert_called_with( - current_password=current_pass, password=new_pass + user=user_id, current_password=current_pass, password=new_pass ) - self.assertIsNone(result) def test_user_create_password_prompt(self): current_pass = 'old_pass' @@ -1700,11 +1703,14 @@ def test_user_create_password_prompt(self): # Mock getting user current and new password. with self._mock_get_password(current_pass, new_pass): result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) self.identity_sdk_client.update_user.assert_called_with( - current_password=current_pass, password=new_pass + user=user_id, current_password=current_pass, password=new_pass ) - self.assertIsNone(result) def test_user_password_change_no_prompt(self): current_pass = 'old_pass' @@ -1722,11 +1728,14 @@ def test_user_password_change_no_prompt(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + conn = self.app.client_manager.sdk_connection + user_id = conn.config.get_auth().get_user_id(conn.identity) self.identity_sdk_client.update_user.assert_called_with( - current_password=current_pass, password=new_pass + user=user_id, current_password=current_pass, password=new_pass ) - self.assertIsNone(result) class TestUserShow(identity_fakes.TestIdentityv3): From 32762bcda6a63e2468607b4d8ed7673ff8cb2777 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Sun, 18 May 2025 15:11:20 +0100 Subject: [PATCH 144/245] compute: Fix key used for NIC fixed IP field Change-Id: If099ac0e2663228681e87e2f4821b746c8113ffc Signed-off-by: Stephen Finucane Closes-bug: #2106221 --- openstackclient/compute/v2/server.py | 4 ++-- openstackclient/tests/unit/compute/v2/test_server.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 524eae549..d0892a0a2 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1947,9 +1947,9 @@ def _match_image(image_api, wanted_properties): network['port'] = nic['port-id'] if nic['v4-fixed-ip']: - network['fixed'] = nic['v4-fixed-ip'] + network['fixed_ip'] = nic['v4-fixed-ip'] elif nic['v6-fixed-ip']: - network['fixed'] = nic['v6-fixed-ip'] + network['fixed_ip'] = nic['v6-fixed-ip'] if nic.get('tag'): # tags are optional network['tag'] = nic['tag'] diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index eb0b934e3..5d626cb00 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -1705,7 +1705,7 @@ def find_port(name_or_id, ignore_missing): }, { 'uuid': network_net2.id, - 'fixed': '10.0.0.2', + 'fixed_ip': '10.0.0.2', }, { 'port': port_port1.id, From eea369e73cb5759dae882915022a8a53f4cbf76a Mon Sep 17 00:00:00 2001 From: Chaemin-Lim Date: Mon, 19 May 2025 16:12:06 +0900 Subject: [PATCH 145/245] Fix incorrect warning with --password-prompt option When creating a user with the --password-prompt option, a warning is incorrectly displayed stating that no password was supplied, even though a password was entered. This occurs because the code checks parsed_args.password instead of the password variable that actually stores the prompted password. This patch fixes the issue by checking the 'password' variable instead of 'parsed_args.password' in the warning condition. A test case has been added to verify that no warning is displayed when using --password-prompt and entering a password. Closes-Bug: #2091836 Change-Id: Ib3ddc7e400ee7988f605c00db534bccc3617d982 --- openstackclient/identity/v3/user.py | 2 +- .../tests/unit/identity/v3/test_user.py | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 5be69bc65..14273e8f6 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -289,7 +289,7 @@ def take_action(self, parsed_args): elif parsed_args.password_prompt: password = utils.get_password(self.app.stdin) - if not parsed_args.password: + if not password: LOG.warning( _( "No password was supplied, authentication will fail " diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index d9e16f174..10207141c 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -163,6 +163,53 @@ def test_user_create_password_prompt(self): self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) + def test_user_create_password_prompt_no_warning(self): + arglist = [ + '--password-prompt', + self.user.name, + ] + verifylist = [ + ('password', None), + ('password_prompt', True), + ('enable', False), + ('disable', False), + ('name', self.user.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + import logging + + # Mock the password prompt + mocker = mock.Mock() + mocker.return_value = 'abc123' + + # Use assertLogs to verify no warnings are logged + logger = 'openstackclient.identity.v3.user' + with mock.patch("osc_lib.utils.get_password", mocker): + with self.assertLogs(logger, level='WARNING') as log_ctx: + logging.getLogger(logger).warning( + "Dummy warning for test setup" + ) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(1, len(log_ctx.records)) + self.assertIn( + "Dummy warning for test setup", log_ctx.output[0] + ) + self.assertNotIn( + "No password was supplied", ''.join(log_ctx.output) + ) + + # Set expected values + kwargs = { + 'name': self.user.name, + 'is_enabled': True, + 'password': 'abc123', + } + self.identity_sdk_client.create_user.assert_called_with(**kwargs) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + def test_user_create_email(self): arglist = [ '--email', From a74850d2c4dccd72fc8e59df04848747dda0b70f Mon Sep 17 00:00:00 2001 From: xfrnk2 Date: Sun, 6 Oct 2024 01:20:27 +0900 Subject: [PATCH 146/245] Add a column to all_projects tag of server list cmd Add a Project ID column to the --all-projects tag of server list cmd Differentiate from the basic command, add a column for Project ID to facilitate easier identification. Add test code for the Project ID column of --all-projects tag. Change-Id: I12af2c91f934e7cd268d21cf76dda78646ed2ff4 --- openstackclient/compute/v2/server.py | 4 ++ .../tests/unit/compute/v2/test_server.py | 39 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 524eae549..73411a654 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2853,6 +2853,10 @@ def take_action(self, parsed_args): 'Scheduler Hints', ) + if parsed_args.all_projects: + columns += ('project_id',) + column_headers += ('Project ID',) + # support for additional columns if parsed_args.columns: for c in parsed_args.columns: diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index eb0b934e3..e129918d0 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4594,6 +4594,15 @@ class _TestServerList(TestServer): 'Properties', 'Scheduler Hints', ) + columns_all_projects = ( + 'ID', + 'Name', + 'Status', + 'Networks', + 'Image', + 'Flavor', + 'Project ID', + ) def setUp(self): super().setUp() @@ -4755,6 +4764,36 @@ def test_server_list_long_option(self): self.assertEqual(self.columns_long, columns) self.assertEqual(self.data, tuple(data)) + def test_server_list_all_projects_option(self): + self.data = tuple( + ( + s.id, + s.name, + s.status, + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + s.project_id, + ) + for s in self.servers + ) + arglist = [ + '--all-projects', + ] + verifylist = [ + ('all_projects', True), + ('long', False), + ('deleted', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.image_client.images.assert_called() + self.compute_client.flavors.assert_called() + self.assertEqual(self.columns_all_projects, columns) + self.assertEqual(self.data, tuple(data)) + def test_server_list_column_option(self): arglist = [ '-c', From 00f4cf9c17240691994ceb2ea0d98d66879ac818 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 6 Mar 2025 12:44:54 +0000 Subject: [PATCH 147/245] volume: Migrate 'block storage log level *' to SDK Change-Id: Ic03f65fee197a85518df448c18a0fd2c11d51993 Signed-off-by: Stephen Finucane --- .../volume/v3/test_block_storage_log_level.py | 132 ++++++++---------- .../volume/v3/block_storage_log_level.py | 50 +++---- 2 files changed, 83 insertions(+), 99 deletions(-) diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py b/openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py index 58394e18b..9f27197c5 100644 --- a/openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py +++ b/openstackclient/tests/unit/volume/v3/test_block_storage_log_level.py @@ -10,9 +10,10 @@ # 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 ddt +from openstack.block_storage.v3 import service as _service +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.tests.unit import utils as tests_utils @@ -20,24 +21,17 @@ from openstackclient.volume.v3 import block_storage_log_level as service -class TestService(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - # Get a shortcut to the ServiceManager Mock - self.service_mock = self.volume_client.services - self.service_mock.reset_mock() - - -class TestBlockStorageLogLevelList(TestService): - service_log = volume_fakes.create_service_log_level_entry() - +class TestBlockStorageLogLevelList(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.service_mock.get_log_levels.return_value = [self.service_log] + self.log_level = sdk_fakes.generate_fake_resource( + _service.LogLevel, binary='cinder-scheduler' + ) + self.volume_sdk_client.get_service_log_levels.return_value = [ + self.log_level + ] - # Get the command object to test self.cmd = service.BlockStorageLogLevelList(self.app, None) def test_block_storage_log_level_list(self): @@ -45,16 +39,16 @@ def test_block_storage_log_level_list(self): arglist = [ '--host', - self.service_log.host, + self.log_level.host, '--service', - self.service_log.binary, + self.log_level.binary, '--log-prefix', - self.service_log.prefix, + 'cinder.', ] verifylist = [ - ('host', self.service_log.host), - ('service', self.service_log.binary), - ('log_prefix', self.service_log.prefix), + ('host', self.log_level.host), + ('service', self.log_level.binary), + ('log_prefix', 'cinder.'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -66,40 +60,35 @@ def test_block_storage_log_level_list(self): 'Prefix', 'Level', ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - - datalist = ( + datalist = tuple( ( - self.service_log.binary, - self.service_log.host, - self.service_log.prefix, - self.service_log.level, - ), + self.log_level.binary, + self.log_level.host, + prefix, + level, + ) + for prefix, level in self.log_level.levels.values() ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) - # checking if proper call was made to get log level of services - self.service_mock.get_log_levels.assert_called_with( - server=self.service_log.host, - binary=self.service_log.binary, - prefix=self.service_log.prefix, + self.volume_sdk_client.get_service_log_levels.assert_called_with( + server=self.log_level.host, + binary=self.log_level.binary, + prefix='cinder.', ) def test_block_storage_log_level_list_pre_332(self): arglist = [ '--host', - self.service_log.host, + self.log_level.host, '--service', 'cinder-api', '--log-prefix', 'cinder_test.api.common', ] verifylist = [ - ('host', self.service_log.host), + ('host', self.log_level.host), ('service', 'cinder-api'), ('log_prefix', 'cinder_test.api.common'), ] @@ -117,14 +106,14 @@ def test_block_storage_log_level_list_invalid_service_name(self): arglist = [ '--host', - self.service_log.host, + self.log_level.host, '--service', 'nova-api', '--log-prefix', 'cinder_test.api.common', ] verifylist = [ - ('host', self.service_log.host), + ('host', self.log_level.host), ('service', 'nova-api'), ('log_prefix', 'cinder_test.api.common'), ] @@ -139,13 +128,15 @@ def test_block_storage_log_level_list_invalid_service_name(self): @ddt.ddt -class TestBlockStorageLogLevelSet(TestService): - service_log = volume_fakes.create_service_log_level_entry() - +class TestBlockStorageLogLevelSet(volume_fakes.TestVolume): def setUp(self): super().setUp() - # Get the command object to test + self.log_level = sdk_fakes.generate_fake_resource( + _service.LogLevel, binary='cinder-api' + ) + self.volume_sdk_client.set_service_log_levels.return_value = None + self.cmd = service.BlockStorageLogLevelSet(self.app, None) def test_block_storage_log_level_set(self): @@ -154,45 +145,45 @@ def test_block_storage_log_level_set(self): arglist = [ 'ERROR', '--host', - self.service_log.host, + self.log_level.host, '--service', - self.service_log.binary, + self.log_level.binary, '--log-prefix', - self.service_log.prefix, + 'cinder.api.common', ] verifylist = [ ('level', 'ERROR'), - ('host', self.service_log.host), - ('service', self.service_log.binary), - ('log_prefix', self.service_log.prefix), + ('host', self.log_level.host), + ('service', self.log_level.binary), + ('log_prefix', 'cinder.api.common'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + ret = self.cmd.take_action(parsed_args) - # checking if proper call was made to set log level of services - self.service_mock.set_log_levels.assert_called_with( + self.assertIsNone(ret) + self.volume_sdk_client.set_service_log_levels.assert_called_with( level='ERROR', - server=self.service_log.host, - binary=self.service_log.binary, - prefix=self.service_log.prefix, + server=self.log_level.host, + binary=self.log_level.binary, + prefix='cinder.api.common', ) def test_block_storage_log_level_set_pre_332(self): arglist = [ 'ERROR', '--host', - self.service_log.host, + self.log_level.host, '--service', 'cinder-api', '--log-prefix', - 'cinder_test.api.common', + 'cinder.api.common', ] verifylist = [ ('level', 'ERROR'), - ('host', self.service_log.host), + ('host', self.log_level.host), ('service', 'cinder-api'), - ('log_prefix', 'cinder_test.api.common'), + ('log_prefix', 'cinder.api.common'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -209,7 +200,7 @@ def test_block_storage_log_level_set_invalid_service_name(self): arglist = [ 'ERROR', '--host', - self.service_log.host, + self.log_level.host, '--service', 'nova-api', '--log-prefix', @@ -217,7 +208,7 @@ def test_block_storage_log_level_set_invalid_service_name(self): ] verifylist = [ ('level', 'ERROR'), - ('host', self.service_log.host), + ('host', self.log_level.host), ('service', 'nova-api'), ('log_prefix', 'cinder.api.common'), ] @@ -237,7 +228,7 @@ def test_block_storage_log_level_set_log_level(self, log_level): arglist = [ log_level, '--host', - self.service_log.host, + self.log_level.host, '--service', 'cinder-api', '--log-prefix', @@ -245,7 +236,7 @@ def test_block_storage_log_level_set_log_level(self, log_level): ] verifylist = [ ('level', log_level.upper()), - ('host', self.service_log.host), + ('host', self.log_level.host), ('service', 'cinder-api'), ('log_prefix', 'cinder.api.common'), ] @@ -263,10 +254,9 @@ def test_block_storage_log_level_set_log_level(self, log_level): self.cmd.take_action(parsed_args) - # checking if proper call was made to set log level of services - self.service_mock.set_log_levels.assert_called_with( + self.volume_sdk_client.set_service_log_levels.assert_called_with( level=log_level.upper(), - server=self.service_log.host, - binary=self.service_log.binary, - prefix=self.service_log.prefix, + server=self.log_level.host, + binary=self.log_level.binary, + prefix='cinder.api.common', ) diff --git a/openstackclient/volume/v3/block_storage_log_level.py b/openstackclient/volume/v3/block_storage_log_level.py index 878cf4087..1f8d5b330 100644 --- a/openstackclient/volume/v3/block_storage_log_level.py +++ b/openstackclient/volume/v3/block_storage_log_level.py @@ -14,10 +14,9 @@ """Block Storage Service action implementations""" -from cinderclient import api_versions +from openstack import utils as sdk_utils from osc_lib.command import command from osc_lib import exceptions -from osc_lib import utils from openstackclient.i18n import _ @@ -33,7 +32,7 @@ def get_parser(self, prog_name): parser.add_argument( "--host", metavar="", - default="", + default=None, help=_( "List block storage service log level of specified host " "(name only)" @@ -42,9 +41,9 @@ def get_parser(self, prog_name): parser.add_argument( "--service", metavar="", - default="", + default=None, choices=( - '', + None, '*', 'cinder-api', 'cinder-volume', @@ -59,13 +58,13 @@ def get_parser(self, prog_name): parser.add_argument( "--log-prefix", metavar="", - default="", + default=None, help="Prefix for the log, e.g. 'sqlalchemy'", ) return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume columns = [ "Binary", "Host", @@ -73,29 +72,24 @@ def take_action(self, parsed_args): "Level", ] - if service_client.api_version < api_versions.APIVersion('3.32'): + if not sdk_utils.supports_microversion(volume_client, '3.32'): msg = _( "--os-volume-api-version 3.32 or greater is required to " "support the 'block storage log level list' command" ) raise exceptions.CommandError(msg) - data = service_client.services.get_log_levels( + data = [] + for entry in volume_client.get_service_log_levels( binary=parsed_args.service, server=parsed_args.host, prefix=parsed_args.log_prefix, - ) + ): + entry_levels = sorted(entry.levels.items(), key=lambda x: x[0]) + for prefix, level in entry_levels: + data.append((entry.binary, entry.host, prefix, level)) - return ( - columns, - ( - utils.get_item_properties( - s, - columns, - ) - for s in data - ), - ) + return (columns, data) class BlockStorageLogLevelSet(command.Command): @@ -111,12 +105,12 @@ def get_parser(self, prog_name): metavar="", choices=('INFO', 'WARNING', 'ERROR', 'DEBUG'), type=str.upper, - help=_("Desired log level."), + help=_("Desired log level"), ) parser.add_argument( "--host", metavar="", - default="", + default=None, help=_( "Set block storage service log level of specified host " "(name only)" @@ -125,9 +119,9 @@ def get_parser(self, prog_name): parser.add_argument( "--service", metavar="", - default="", + default=None, choices=( - '', + None, '*', 'cinder-api', 'cinder-volume', @@ -142,22 +136,22 @@ def get_parser(self, prog_name): parser.add_argument( "--log-prefix", metavar="", - default="", + default=None, help="Prefix for the log, e.g. 'sqlalchemy'", ) return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume - if service_client.api_version < api_versions.APIVersion('3.32'): + if not sdk_utils.supports_microversion(volume_client, '3.32'): msg = _( "--os-volume-api-version 3.32 or greater is required to " "support the 'block storage log level set' command" ) raise exceptions.CommandError(msg) - service_client.services.set_log_levels( + volume_client.set_service_log_levels( level=parsed_args.level, binary=parsed_args.service, server=parsed_args.host, From 1ee3ef33d7a805bde562c782c08574dddaa979be Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 6 Mar 2025 12:57:27 +0000 Subject: [PATCH 148/245] volume: Add v3-specific volume service module Ease migration. Change-Id: Ibcdb157ba1bf370c63320d3a1afcf3c400370624 Signed-off-by: Stephen Finucane --- .../tests/unit/volume/v3/test_service.py | 149 ++++++++++++++++++ openstackclient/volume/v3/service.py | 82 +++++++++- setup.cfg | 2 +- 3 files changed, 230 insertions(+), 3 deletions(-) diff --git a/openstackclient/tests/unit/volume/v3/test_service.py b/openstackclient/tests/unit/volume/v3/test_service.py index 333359551..e5aa46758 100644 --- a/openstackclient/tests/unit/volume/v3/test_service.py +++ b/openstackclient/tests/unit/volume/v3/test_service.py @@ -13,6 +13,7 @@ # from cinderclient import api_versions +from osc_lib import exceptions from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes from openstackclient.volume.v3 import service @@ -269,3 +270,151 @@ def test_service_list_with_backend_state(self): # checking if prohibited columns are present in output self.assertNotIn("Disabled Reason", columns) self.assertNotIn(backend_service.disabled_reason, tuple(data)) + + +class TestServiceSet(TestService): + service = volume_fakes.create_one_service() + + def setUp(self): + super().setUp() + + self.service_mock.enable.return_value = self.service + self.service_mock.disable.return_value = self.service + self.service_mock.disable_log_reason.return_value = self.service + + self.cmd = service.SetService(self.app, None) + + def test_service_set_nothing(self): + arglist = [ + self.service.host, + self.service.binary, + ] + verifylist = [ + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.service_mock.enable.assert_not_called() + self.service_mock.disable.assert_not_called() + self.service_mock.disable_log_reason.assert_not_called() + self.assertIsNone(result) + + def test_service_set_enable(self): + arglist = [ + '--enable', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('enable', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.enable.assert_called_with( + self.service.host, self.service.binary + ) + self.service_mock.disable.assert_not_called() + self.service_mock.disable_log_reason.assert_not_called() + self.assertIsNone(result) + + def test_service_set_disable(self): + arglist = [ + '--disable', + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable', True), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.disable.assert_called_with( + self.service.host, self.service.binary + ) + self.service_mock.enable.assert_not_called() + self.service_mock.disable_log_reason.assert_not_called() + self.assertIsNone(result) + + def test_service_set_disable_with_reason(self): + reason = 'earthquake' + arglist = [ + '--disable', + '--disable-reason', + reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable', True), + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.service_mock.disable_log_reason.assert_called_with( + self.service.host, self.service.binary, reason + ) + self.assertIsNone(result) + + def test_service_set_only_with_disable_reason(self): + reason = 'earthquake' + arglist = [ + '--disable-reason', + reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: + self.cmd.take_action(parsed_args) + self.fail("CommandError should be raised.") + except exceptions.CommandError as e: + self.assertEqual( + "Cannot specify option --disable-reason without " + "--disable specified.", + str(e), + ) + + def test_service_set_enable_with_disable_reason(self): + reason = 'earthquake' + arglist = [ + '--enable', + '--disable-reason', + reason, + self.service.host, + self.service.binary, + ] + verifylist = [ + ('enable', True), + ('disable_reason', reason), + ('host', self.service.host), + ('service', self.service.binary), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: + self.cmd.take_action(parsed_args) + self.fail("CommandError should be raised.") + except exceptions.CommandError as e: + self.assertEqual( + "Cannot specify option --disable-reason without " + "--disable specified.", + str(e), + ) diff --git a/openstackclient/volume/v3/service.py b/openstackclient/volume/v3/service.py index fe4db4c11..51055dc7e 100644 --- a/openstackclient/volume/v3/service.py +++ b/openstackclient/volume/v3/service.py @@ -15,12 +15,36 @@ """Service action implementations""" from cinderclient import api_versions +from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils -from openstackclient.volume.v2 import service as service_v2 +from openstackclient.i18n import _ -class ListService(service_v2.ListService): +class ListService(command.Lister): + _description = _("List service command") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "--host", + metavar="", + help=_("List services on specified host (name only)"), + ) + parser.add_argument( + "--service", + metavar="", + help=_("List only specified service (name only)"), + ) + parser.add_argument( + "--long", + action="store_true", + default=False, + help=_("List additional fields in output"), + ) + return parser + def take_action(self, parsed_args): service_client = self.app.client_manager.volume @@ -53,3 +77,57 @@ def take_action(self, parsed_args): for s in data ), ) + + +class SetService(command.Command): + _description = _("Set volume service properties") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument("host", metavar="", help=_("Name of host")) + parser.add_argument( + "service", + metavar="", + help=_("Name of service (Binary name)"), + ) + enabled_group = parser.add_mutually_exclusive_group() + enabled_group.add_argument( + "--enable", action="store_true", help=_("Enable volume service") + ) + enabled_group.add_argument( + "--disable", action="store_true", help=_("Disable volume service") + ) + parser.add_argument( + "--disable-reason", + metavar="", + help=_( + "Reason for disabling the service " + "(should be used with --disable option)" + ), + ) + return parser + + def take_action(self, parsed_args): + if parsed_args.disable_reason and not parsed_args.disable: + msg = _( + "Cannot specify option --disable-reason without " + "--disable specified." + ) + raise exceptions.CommandError(msg) + + service_client = self.app.client_manager.volume + if parsed_args.enable: + service_client.services.enable( + parsed_args.host, parsed_args.service + ) + if parsed_args.disable: + if parsed_args.disable_reason: + service_client.services.disable_log_reason( + parsed_args.host, + parsed_args.service, + parsed_args.disable_reason, + ) + else: + service_client.services.disable( + parsed_args.host, parsed_args.service + ) diff --git a/setup.cfg b/setup.cfg index cd3a107e4..519a371cb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -804,7 +804,7 @@ openstack.volume.v3 = volume_qos_unset = openstackclient.volume.v2.qos_specs:UnsetQos volume_service_list = openstackclient.volume.v3.service:ListService - volume_service_set = openstackclient.volume.v2.service:SetService + volume_service_set = openstackclient.volume.v3.service:SetService volume_transfer_request_accept = openstackclient.volume.v3.volume_transfer_request:AcceptTransferRequest volume_transfer_request_create = openstackclient.volume.v3.volume_transfer_request:CreateTransferRequest From b933330d55e47050a620516c4b26437f5956b808 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 6 Mar 2025 13:07:17 +0000 Subject: [PATCH 149/245] volume: Migrate 'service *' to SDK Change-Id: I81254c6cde8783be371ccdcface5027eb247b1ce Signed-off-by: Stephen Finucane --- .../tests/unit/volume/v2/test_service.py | 148 ++++------ .../tests/unit/volume/v3/test_service.py | 257 +++++++----------- openstackclient/volume/v2/service.py | 79 +++--- openstackclient/volume/v3/service.py | 67 +++-- 4 files changed, 232 insertions(+), 319 deletions(-) diff --git a/openstackclient/tests/unit/volume/v2/test_service.py b/openstackclient/tests/unit/volume/v2/test_service.py index a55398564..e230a39a9 100644 --- a/openstackclient/tests/unit/volume/v2/test_service.py +++ b/openstackclient/tests/unit/volume/v2/test_service.py @@ -12,108 +12,83 @@ # under the License. # +from unittest import mock + +from openstack.block_storage.v2 import service as _service +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes from openstackclient.volume.v2 import service -class TestService(volume_fakes.TestVolume): +class TestServiceList(volume_fakes.TestVolume): def setUp(self): super().setUp() - # Get a shortcut to the ServiceManager Mock - self.service_mock = self.volume_client.services - self.service_mock.reset_mock() - - -class TestServiceList(TestService): - # The service to be listed - services = volume_fakes.create_one_service() + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.volume_sdk_client.services.return_value = [self.service] - def setUp(self): - super().setUp() - - self.service_mock.list.return_value = [self.services] - - # Get the command object to test self.cmd = service.ListService(self.app, None) def test_service_list(self): arglist = [ '--host', - self.services.host, + self.service.host, '--service', - self.services.binary, + self.service.binary, ] verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), + ('host', self.service.host), + ('service', self.service.binary), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - expected_columns = [ + expected_columns = ( 'Binary', 'Host', 'Zone', 'Status', 'State', 'Updated At', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - + ) datalist = ( ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, ), ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list services - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, + self.volume_sdk_client.services.assert_called_with( + host=self.service.host, + binary=self.service.binary, ) - # checking if prohibited columns are present in output - self.assertNotIn("Disabled Reason", columns) - self.assertNotIn(self.services.disabled_reason, tuple(data)) - def test_service_list_with_long_option(self): arglist = [ '--host', - self.services.host, + self.service.host, '--service', - self.services.binary, + self.service.binary, '--long', ] verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), + ('host', self.service.host), + ('service', self.service.binary), ('long', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - expected_columns = [ + expected_columns = ( 'Binary', 'Host', 'Zone', @@ -121,41 +96,34 @@ def test_service_list_with_long_option(self): 'State', 'Updated At', 'Disabled Reason', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - + ) datalist = ( ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, - self.services.disabled_reason, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + self.service.disabled_reason, ), ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) - - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, + self.volume_sdk_client.services.assert_called_with( + host=self.service.host, + binary=self.service.binary, ) -class TestServiceSet(TestService): - service = volume_fakes.create_one_service() - +class TestServiceSet(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.service_mock.enable.return_value = self.service - self.service_mock.disable.return_value = self.service - self.service_mock.disable_log_reason.return_value = self.service + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.service.enable = mock.Mock(autospec=True) + self.service.disable = mock.Mock(autospec=True) + self.volume_sdk_client.find_service.return_value = self.service self.cmd = service.SetService(self.app, None) @@ -171,9 +139,8 @@ def test_service_set_nothing(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.service_mock.enable.assert_not_called() - self.service_mock.disable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() + self.service.enable.assert_not_called() + self.service.disable.assert_not_called() self.assertIsNone(result) def test_service_set_enable(self): @@ -191,11 +158,8 @@ def test_service_set_enable(self): result = self.cmd.take_action(parsed_args) - self.service_mock.enable.assert_called_with( - self.service.host, self.service.binary - ) - self.service_mock.disable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() + self.service.enable.assert_called_with(self.volume_sdk_client) + self.service.disable.assert_not_called() self.assertIsNone(result) def test_service_set_disable(self): @@ -213,11 +177,10 @@ def test_service_set_disable(self): result = self.cmd.take_action(parsed_args) - self.service_mock.disable.assert_called_with( - self.service.host, self.service.binary + self.service.enable.assert_not_called() + self.service.disable.assert_called_with( + self.volume_sdk_client, reason=None ) - self.service_mock.enable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() self.assertIsNone(result) def test_service_set_disable_with_reason(self): @@ -239,8 +202,9 @@ def test_service_set_disable_with_reason(self): result = self.cmd.take_action(parsed_args) - self.service_mock.disable_log_reason.assert_called_with( - self.service.host, self.service.binary, reason + self.service.enable.assert_not_called() + self.service.disable.assert_called_with( + self.volume_sdk_client, reason=reason ) self.assertIsNone(result) @@ -258,6 +222,7 @@ def test_service_set_only_with_disable_reason(self): ('service', self.service.binary), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: self.cmd.take_action(parsed_args) self.fail("CommandError should be raised.") @@ -284,6 +249,7 @@ def test_service_set_enable_with_disable_reason(self): ('service', self.service.binary), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: self.cmd.take_action(parsed_args) self.fail("CommandError should be raised.") diff --git a/openstackclient/tests/unit/volume/v3/test_service.py b/openstackclient/tests/unit/volume/v3/test_service.py index e5aa46758..53027fcb5 100644 --- a/openstackclient/tests/unit/volume/v3/test_service.py +++ b/openstackclient/tests/unit/volume/v3/test_service.py @@ -12,109 +12,83 @@ # under the License. # -from cinderclient import api_versions +from unittest import mock + +from openstack.block_storage.v3 import service as _service +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes from openstackclient.volume.v3 import service -class TestService(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - # Get a shortcut to the ServiceManager Mock - self.service_mock = self.volume_client.services - self.service_mock.reset_mock() - - -class TestServiceList(TestService): - # The service to be listed - services = volume_fakes.create_one_service() - +class TestServiceList(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.service_mock.list.return_value = [self.services] + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.volume_sdk_client.services.return_value = [self.service] - # Get the command object to test self.cmd = service.ListService(self.app, None) def test_service_list(self): arglist = [ '--host', - self.services.host, + self.service.host, '--service', - self.services.binary, + self.service.binary, ] verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), + ('host', self.service.host), + ('service', self.service.binary), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - expected_columns = [ + expected_columns = ( 'Binary', 'Host', 'Zone', 'Status', 'State', 'Updated At', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - + ) datalist = ( ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, ), ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list services - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, + self.volume_sdk_client.services.assert_called_with( + host=self.service.host, + binary=self.service.binary, ) - # checking if prohibited columns are present in output - self.assertNotIn("Disabled Reason", columns) - self.assertNotIn(self.services.disabled_reason, tuple(data)) - def test_service_list_with_long_option(self): arglist = [ '--host', - self.services.host, + self.service.host, '--service', - self.services.binary, + self.service.binary, '--long', ] verifylist = [ - ('host', self.services.host), - ('service', self.services.binary), + ('host', self.service.host), + ('service', self.service.binary), ('long', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - expected_columns = [ + expected_columns = ( 'Binary', 'Host', 'Zone', @@ -122,55 +96,43 @@ def test_service_list_with_long_option(self): 'State', 'Updated At', 'Disabled Reason', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - + ) datalist = ( ( - self.services.binary, - self.services.host, - self.services.zone, - self.services.status, - self.services.state, - self.services.updated_at, - self.services.disabled_reason, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + self.service.disabled_reason, ), ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) - - self.service_mock.list.assert_called_with( - self.services.host, - self.services.binary, + self.volume_sdk_client.services.assert_called_with( + host=self.service.host, + binary=self.service.binary, ) def test_service_list_with_cluster(self): - self.volume_client.api_version = api_versions.APIVersion('3.7') - cluster = {'cluster': 'fake-cluster'} - cluster_service = volume_fakes.create_one_service(attrs=cluster) - self.service_mock.list.return_value = [cluster_service] + self.set_volume_api_version('3.7') arglist = [ '--host', - cluster_service.host, + self.service.host, '--service', - cluster_service.binary, + self.service.binary, ] verifylist = [ - ('host', cluster_service.host), - ('service', cluster_service.binary), + ('host', self.service.host), + ('service', self.service.binary), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - expected_columns = [ + expected_columns = ( 'Binary', 'Host', 'Zone', @@ -178,60 +140,43 @@ def test_service_list_with_cluster(self): 'State', 'Updated At', 'Cluster', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - + ) datalist = ( ( - cluster_service.binary, - cluster_service.host, - cluster_service.zone, - cluster_service.status, - cluster_service.state, - cluster_service.updated_at, - cluster_service.cluster, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + self.service.cluster, ), ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list services - self.service_mock.list.assert_called_with( - cluster_service.host, - cluster_service.binary, + self.volume_sdk_client.services.assert_called_with( + host=self.service.host, + binary=self.service.binary, ) - # checking if prohibited columns are present in output - self.assertNotIn("Disabled Reason", columns) - self.assertNotIn(cluster_service.disabled_reason, tuple(data)) - def test_service_list_with_backend_state(self): - self.volume_client.api_version = api_versions.APIVersion('3.49') - backend_state = {'cluster': 'fake-cluster', 'backend_state': 'up'} - backend_service = volume_fakes.create_one_service(attrs=backend_state) - self.service_mock.list.return_value = [backend_service] + self.set_volume_api_version('3.49') arglist = [ '--host', - backend_service.host, + self.service.host, '--service', - backend_service.binary, + self.service.binary, ] verifylist = [ - ('host', backend_service.host), - ('service', backend_service.binary), + ('host', self.service.host), + ('service', self.service.binary), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class Lister in cliff, abstract method take_action() - # returns a tuple containing the column names and an iterable - # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - expected_columns = [ + expected_columns = ( 'Binary', 'Host', 'Zone', @@ -240,47 +185,35 @@ def test_service_list_with_backend_state(self): 'Updated At', 'Cluster', 'Backend State', - ] - - # confirming if all expected columns are present in the result. - self.assertEqual(expected_columns, columns) - + ) datalist = ( ( - backend_service.binary, - backend_service.host, - backend_service.zone, - backend_service.status, - backend_service.state, - backend_service.updated_at, - backend_service.cluster, - backend_service.backend_state, + self.service.binary, + self.service.host, + self.service.availability_zone, + self.service.status, + self.service.state, + self.service.updated_at, + self.service.cluster, + self.service.backend_state, ), ) - - # confirming if all expected values are present in the result. + self.assertEqual(expected_columns, columns) self.assertEqual(datalist, tuple(data)) - - # checking if proper call was made to list services - self.service_mock.list.assert_called_with( - backend_service.host, - backend_service.binary, + self.volume_sdk_client.services.assert_called_with( + host=self.service.host, + binary=self.service.binary, ) - # checking if prohibited columns are present in output - self.assertNotIn("Disabled Reason", columns) - self.assertNotIn(backend_service.disabled_reason, tuple(data)) - - -class TestServiceSet(TestService): - service = volume_fakes.create_one_service() +class TestServiceSet(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.service_mock.enable.return_value = self.service - self.service_mock.disable.return_value = self.service - self.service_mock.disable_log_reason.return_value = self.service + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.service.enable = mock.Mock(autospec=True) + self.service.disable = mock.Mock(autospec=True) + self.volume_sdk_client.find_service.return_value = self.service self.cmd = service.SetService(self.app, None) @@ -296,9 +229,8 @@ def test_service_set_nothing(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.service_mock.enable.assert_not_called() - self.service_mock.disable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() + self.service.enable.assert_not_called() + self.service.disable.assert_not_called() self.assertIsNone(result) def test_service_set_enable(self): @@ -316,11 +248,8 @@ def test_service_set_enable(self): result = self.cmd.take_action(parsed_args) - self.service_mock.enable.assert_called_with( - self.service.host, self.service.binary - ) - self.service_mock.disable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() + self.service.enable.assert_called_with(self.volume_sdk_client) + self.service.disable.assert_not_called() self.assertIsNone(result) def test_service_set_disable(self): @@ -338,11 +267,10 @@ def test_service_set_disable(self): result = self.cmd.take_action(parsed_args) - self.service_mock.disable.assert_called_with( - self.service.host, self.service.binary + self.service.enable.assert_not_called() + self.service.disable.assert_called_with( + self.volume_sdk_client, reason=None ) - self.service_mock.enable.assert_not_called() - self.service_mock.disable_log_reason.assert_not_called() self.assertIsNone(result) def test_service_set_disable_with_reason(self): @@ -364,8 +292,9 @@ def test_service_set_disable_with_reason(self): result = self.cmd.take_action(parsed_args) - self.service_mock.disable_log_reason.assert_called_with( - self.service.host, self.service.binary, reason + self.service.enable.assert_not_called() + self.service.disable.assert_called_with( + self.volume_sdk_client, reason=reason ) self.assertIsNone(result) @@ -383,6 +312,7 @@ def test_service_set_only_with_disable_reason(self): ('service', self.service.binary), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: self.cmd.take_action(parsed_args) self.fail("CommandError should be raised.") @@ -409,6 +339,7 @@ def test_service_set_enable_with_disable_reason(self): ('service', self.service.binary), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: self.cmd.take_action(parsed_args) self.fail("CommandError should be raised.") diff --git a/openstackclient/volume/v2/service.py b/openstackclient/volume/v2/service.py index 2a33fc0b9..418ecec42 100644 --- a/openstackclient/volume/v2/service.py +++ b/openstackclient/volume/v2/service.py @@ -45,33 +45,34 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume + + columns: tuple[str, ...] = ( + "binary", + "host", + "availability_zone", + "status", + "state", + "updated_at", + ) + column_names: tuple[str, ...] = ( + "Binary", + "Host", + "Zone", + "Status", + "State", + "Updated At", + ) if parsed_args.long: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - "Disabled Reason", - ] - else: - columns = [ - "Binary", - "Host", - "Zone", - "Status", - "State", - "Updated At", - ] - - data = service_client.services.list( - parsed_args.host, parsed_args.service + columns += ("disabled_reason",) + column_names += ("Disabled Reason",) + + data = volume_client.services( + host=parsed_args.host, binary=parsed_args.service ) return ( - columns, + column_names, ( utils.get_item_properties( s, @@ -87,7 +88,11 @@ class SetService(command.Command): def get_parser(self, prog_name): parser = super().get_parser(prog_name) - parser.add_argument("host", metavar="", help=_("Name of host")) + parser.add_argument( + "host", + metavar="", + help=_("Name of host"), + ) parser.add_argument( "service", metavar="", @@ -118,19 +123,17 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - service_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume + + service = volume_client.find_service( + host=parsed_args.host, service=parsed_args.service + ) + if parsed_args.enable: - service_client.services.enable( - parsed_args.host, parsed_args.service - ) + service.enable(volume_client) + if parsed_args.disable: - if parsed_args.disable_reason: - service_client.services.disable_log_reason( - parsed_args.host, - parsed_args.service, - parsed_args.disable_reason, - ) - else: - service_client.services.disable( - parsed_args.host, parsed_args.service - ) + service.disable( + volume_client, + reason=parsed_args.disable_reason, + ) diff --git a/openstackclient/volume/v3/service.py b/openstackclient/volume/v3/service.py index 51055dc7e..fb41b1f47 100644 --- a/openstackclient/volume/v3/service.py +++ b/openstackclient/volume/v3/service.py @@ -14,7 +14,7 @@ """Service action implementations""" -from cinderclient import api_versions +from openstack import utils as sdk_utils from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -46,29 +46,40 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - service_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume - columns = [ + columns: tuple[str, ...] = ( + "binary", + "host", + "availability_zone", + "status", + "state", + "updated_at", + ) + column_names: tuple[str, ...] = ( "Binary", "Host", "Zone", "Status", "State", "Updated At", - ] + ) - if service_client.api_version >= api_versions.APIVersion('3.7'): - columns.append("Cluster") - if service_client.api_version >= api_versions.APIVersion('3.49'): - columns.append("Backend State") + if sdk_utils.supports_microversion(volume_client, '3.7'): + columns += ("cluster",) + column_names += ("Cluster",) + if sdk_utils.supports_microversion(volume_client, '3.49'): + columns += ("backend_state",) + column_names += ("Backend State",) if parsed_args.long: - columns.append("Disabled Reason") + columns += ("disabled_reason",) + column_names += ("Disabled Reason",) - data = service_client.services.list( - parsed_args.host, parsed_args.service + data = volume_client.services( + host=parsed_args.host, binary=parsed_args.service ) return ( - columns, + column_names, ( utils.get_item_properties( s, @@ -84,7 +95,11 @@ class SetService(command.Command): def get_parser(self, prog_name): parser = super().get_parser(prog_name) - parser.add_argument("host", metavar="", help=_("Name of host")) + parser.add_argument( + "host", + metavar="", + help=_("Name of host"), + ) parser.add_argument( "service", metavar="", @@ -115,19 +130,17 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - service_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume + + service = volume_client.find_service( + host=parsed_args.host, service=parsed_args.service + ) + if parsed_args.enable: - service_client.services.enable( - parsed_args.host, parsed_args.service - ) + service.enable(volume_client) + if parsed_args.disable: - if parsed_args.disable_reason: - service_client.services.disable_log_reason( - parsed_args.host, - parsed_args.service, - parsed_args.disable_reason, - ) - else: - service_client.services.disable( - parsed_args.host, parsed_args.service - ) + service.disable( + volume_client, + reason=parsed_args.disable_reason, + ) From 8eb1a183fe82e341812d0895ff14894d1668615c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Mar 2025 17:39:33 +0000 Subject: [PATCH 150/245] volume: Migrate 'backup set', 'backup unset' to SDK Change-Id: Iced346df828faab1ff08a2645ff64f4cfea25cb1 Signed-off-by: Stephen Finucane --- .../unit/volume/v2/test_volume_backup.py | 63 ++++---- .../unit/volume/v3/test_volume_backup.py | 142 +++++++++--------- openstackclient/volume/v2/volume_backup.py | 12 +- openstackclient/volume/v3/volume_backup.py | 43 +++--- 4 files changed, 131 insertions(+), 129 deletions(-) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index faf24899a..b2401afd4 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -13,26 +13,15 @@ from unittest.mock import call +from openstack.block_storage.v2 import backup as _backup +from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes from openstackclient.volume.v2 import volume_backup -class TestBackupLegacy(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - self.backups_mock = self.volume_client.backups - self.backups_mock.reset_mock() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - self.restores_mock = self.volume_client.restores - self.restores_mock.reset_mock() - - class TestBackupCreate(volume_fakes.TestVolume): volume = volume_fakes.create_one_volume() snapshot = volume_fakes.create_one_snapshot() @@ -476,17 +465,15 @@ def test_backup_restore_with_volume_existing(self): ) -class TestBackupSet(TestBackupLegacy): - backup = volume_fakes.create_one_backup( - attrs={'metadata': {'wow': 'cool'}}, - ) - +class TestBackupSet(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.backups_mock.get.return_value = self.backup + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, metadata={'wow': 'cool'} + ) + self.volume_sdk_client.find_backup.return_value = self.backup - # Get the command object to test self.cmd = volume_backup.SetVolumeBackup(self.app, None) def test_backup_set_state(self): @@ -496,26 +483,34 @@ def test_backup_set_state(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.backups_mock.reset_state.assert_called_once_with( - self.backup.id, 'error' - ) self.assertIsNone(result) + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.reset_backup_status.assert_called_with( + self.backup, status='error' + ) + def test_backup_set_state_failed(self): - self.backups_mock.reset_state.side_effect = exceptions.CommandError() + self.volume_sdk_client.reset_backup_status.side_effect = ( + sdk_exceptions.NotFoundException('foo') + ) + arglist = ['--state', 'error', self.backup.id] verifylist = [('state', 'error'), ('backup', self.backup.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - 'One or more of the set operations failed', str(e) - ) - self.backups_mock.reset_state.assert_called_with( - self.backup.id, 'error' + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertEqual('One or more of the set operations failed', str(exc)) + + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.reset_backup_status.assert_called_with( + self.backup, status='error' ) diff --git a/openstackclient/tests/unit/volume/v3/test_volume_backup.py b/openstackclient/tests/unit/volume/v3/test_volume_backup.py index fb0a46508..5069bee56 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_backup.py @@ -13,26 +13,15 @@ from unittest.mock import call +from openstack.block_storage.v3 import backup as _backup +from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes from openstackclient.volume.v3 import volume_backup -class TestBackupLegacy(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - self.backups_mock = self.volume_client.backups - self.backups_mock.reset_mock() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - self.restores_mock = self.volume_client.restores - self.restores_mock.reset_mock() - - class TestBackupCreate(volume_fakes.TestVolume): volume = volume_fakes.create_one_volume() snapshot = volume_fakes.create_one_snapshot() @@ -574,17 +563,15 @@ def test_backup_restore_with_volume_existing(self): ) -class TestBackupSet(TestBackupLegacy): - backup = volume_fakes.create_one_backup( - attrs={'metadata': {'wow': 'cool'}}, - ) - +class TestBackupSet(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.backups_mock.get.return_value = self.backup + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, metadata={'wow': 'cool'} + ) + self.volume_sdk_client.find_backup.return_value = self.backup - # Get the command object to test self.cmd = volume_backup.SetVolumeBackup(self.app, None) def test_backup_set_name(self): @@ -601,14 +588,16 @@ def test_backup_set_name(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns nothing result = self.cmd.take_action(parsed_args) - self.backups_mock.update.assert_called_once_with( - self.backup.id, **{'name': 'new_name'} - ) self.assertIsNone(result) + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.update_backup.assert_called_once_with( + self.backup, name='new_name' + ) + def test_backup_set_name_pre_v39(self): self.set_volume_api_version('3.8') @@ -644,13 +633,14 @@ def test_backup_set_description(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - # Set expected values - kwargs = {'description': 'new_description'} - self.backups_mock.update.assert_called_once_with( - self.backup.id, **kwargs + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.update_backup.assert_called_once_with( + self.backup, description='new_description' ) - self.assertIsNone(result) def test_backup_set_description_pre_v39(self): self.set_volume_api_version('3.8') @@ -679,26 +669,34 @@ def test_backup_set_state(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.backups_mock.reset_state.assert_called_once_with( - self.backup.id, 'error' - ) self.assertIsNone(result) + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.reset_backup_status.assert_called_with( + self.backup, status='error' + ) + def test_backup_set_state_failed(self): - self.backups_mock.reset_state.side_effect = exceptions.CommandError() + self.volume_sdk_client.reset_backup_status.side_effect = ( + sdk_exceptions.NotFoundException('foo') + ) + arglist = ['--state', 'error', self.backup.id] verifylist = [('state', 'error'), ('backup', self.backup.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - 'One or more of the set operations failed', str(e) - ) - self.backups_mock.reset_state.assert_called_with( - self.backup.id, 'error' + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertEqual('One or more of the set operations failed', str(exc)) + + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.reset_backup_status.assert_called_with( + self.backup, status='error' ) def test_backup_set_no_property(self): @@ -715,15 +713,14 @@ def test_backup_set_no_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - # Set expected values - kwargs = { - 'metadata': {}, - } - self.backups_mock.update.assert_called_once_with( - self.backup.id, **kwargs + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.update_backup.assert_called_once_with( + self.backup, metadata={} ) - self.assertIsNone(result) def test_backup_set_no_property_pre_v343(self): self.set_volume_api_version('3.42') @@ -758,15 +755,14 @@ def test_backup_set_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - # Set expected values - kwargs = { - 'metadata': {'wow': 'cool', 'foo': 'bar'}, - } - self.backups_mock.update.assert_called_once_with( - self.backup.id, **kwargs + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.update_backup.assert_called_once_with( + self.backup, metadata={'wow': 'cool', 'foo': 'bar'} ) - self.assertIsNone(result) def test_backup_set_property_pre_v343(self): self.set_volume_api_version('3.42') @@ -788,17 +784,16 @@ def test_backup_set_property_pre_v343(self): self.assertIn("--os-volume-api-version 3.43 or greater", str(exc)) -class TestBackupUnset(TestBackupLegacy): - backup = volume_fakes.create_one_backup( - attrs={'metadata': {'foo': 'bar'}}, - ) - +class TestBackupUnset(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.backups_mock.get.return_value = self.backup + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, metadata={'foo': 'bar', 'wow': 'cool'} + ) + self.volume_sdk_client.find_backup.return_value = self.backup + self.volume_sdk_client.delete_backup_metadata.return_value = None - # Get the command object to test self.cmd = volume_backup.UnsetVolumeBackup(self.app, None) def test_backup_unset_property(self): @@ -816,15 +811,14 @@ def test_backup_unset_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - # Set expected values - kwargs = { - 'metadata': {}, - } - self.backups_mock.update.assert_called_once_with( - self.backup.id, **kwargs + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) + self.volume_sdk_client.delete_backup_metadata.assert_called_once_with( + self.backup, keys=['wow'] ) - self.assertIsNone(result) def test_backup_unset_property_pre_v343(self): self.set_volume_api_version('3.42') @@ -907,7 +901,9 @@ def test_backup_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_sdk_client.find_backup.assert_called_with(self.backup.id) + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index f6746eec8..55cde32f1 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -417,13 +417,19 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - backup = utils.find_resource(volume_client.backups, parsed_args.backup) + volume_client = self.app.client_manager.sdk_connection.volume + + backup = volume_client.find_backup( + parsed_args.backup, + ignore_missing=False, + ) result = 0 if parsed_args.state: try: - volume_client.backups.reset_state(backup.id, parsed_args.state) + volume_client.reset_backup_status( + backup, status=parsed_args.state + ) except Exception as e: LOG.error(_("Failed to set backup state: %s"), e) result += 1 diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py index 9892eadb0..7df46f8e6 100644 --- a/openstackclient/volume/v3/volume_backup.py +++ b/openstackclient/volume/v3/volume_backup.py @@ -18,7 +18,6 @@ import functools import logging -from cinderclient import api_versions from cliff import columns as cliff_columns from openstack import utils as sdk_utils from osc_lib.cli import parseractions @@ -512,13 +511,19 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - backup = utils.find_resource(volume_client.backups, parsed_args.backup) + volume_client = self.app.client_manager.sdk_connection.volume + + backup = volume_client.find_backup( + parsed_args.backup, + ignore_missing=False, + ) result = 0 if parsed_args.state: try: - volume_client.backups.reset_state(backup.id, parsed_args.state) + volume_client.reset_backup_status( + backup, status=parsed_args.state + ) except Exception as e: LOG.error(_("Failed to set backup state: %s"), e) result += 1 @@ -526,7 +531,7 @@ def take_action(self, parsed_args): kwargs = {} if parsed_args.name: - if volume_client.api_version < api_versions.APIVersion('3.9'): + if not sdk_utils.supports_microversion(volume_client, '3.9'): msg = _( '--os-volume-api-version 3.9 or greater is required to ' 'support the --name option' @@ -536,7 +541,7 @@ def take_action(self, parsed_args): kwargs['name'] = parsed_args.name if parsed_args.description: - if volume_client.api_version < api_versions.APIVersion('3.9'): + if not sdk_utils.supports_microversion(volume_client, '3.9'): msg = _( '--os-volume-api-version 3.9 or greater is required to ' 'support the --description option' @@ -546,7 +551,7 @@ def take_action(self, parsed_args): kwargs['description'] = parsed_args.description if parsed_args.no_property: - if volume_client.api_version < api_versions.APIVersion('3.43'): + if not sdk_utils.supports_microversion(volume_client, '3.43'): msg = _( '--os-volume-api-version 3.43 or greater is required to ' 'support the --no-property option' @@ -554,14 +559,14 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) if parsed_args.properties: - if volume_client.api_version < api_versions.APIVersion('3.43'): + if not sdk_utils.supports_microversion(volume_client, '3.43'): msg = _( '--os-volume-api-version 3.43 or greater is required to ' 'support the --property option' ) raise exceptions.CommandError(msg) - if volume_client.api_version >= api_versions.APIVersion('3.43'): + if sdk_utils.supports_microversion(volume_client, '3.43'): metadata = copy.deepcopy(backup.metadata) if parsed_args.no_property: @@ -572,7 +577,7 @@ def take_action(self, parsed_args): if kwargs: try: - volume_client.backups.update(backup.id, **kwargs) + volume_client.update_backup(backup, **kwargs) except Exception as e: LOG.error("Failed to update backup: %s", e) result += 1 @@ -608,16 +613,18 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume - if volume_client.api_version < api_versions.APIVersion('3.43'): + if not sdk_utils.supports_microversion(volume_client, '3.43'): msg = _( '--os-volume-api-version 3.43 or greater is required to ' 'support the --property option' ) raise exceptions.CommandError(msg) - backup = utils.find_resource(volume_client.backups, parsed_args.backup) + backup = volume_client.find_backup( + parsed_args.backup, ignore_missing=False + ) metadata = copy.deepcopy(backup.metadata) for key in parsed_args.properties: @@ -632,11 +639,7 @@ def take_action(self, parsed_args): del metadata[key] - kwargs = { - 'metadata': metadata, - } - - volume_client.backups.update(backup.id, **kwargs) + volume_client.delete_backup_metadata(backup, keys=list(metadata)) class ShowVolumeBackup(command.ShowOne): @@ -653,7 +656,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume - backup = volume_client.find_backup(parsed_args.backup) + backup = volume_client.find_backup( + parsed_args.backup, ignore_missing=False + ) columns: tuple[str, ...] = ( "availability_zone", "container", From a9b9984973f2ae8e9ce79507dfdb306ead9608c4 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 10 Mar 2025 12:28:13 +0000 Subject: [PATCH 151/245] tests: Use SDK mocks for SDK-based commands Change-Id: I7e781875d4467ed097196771144e25cee38ce678 Signed-off-by: Stephen Finucane --- .../unit/volume/v2/test_volume_backup.py | 226 ++++++++------- .../unit/volume/v3/test_volume_backup.py | 259 +++++++++--------- 2 files changed, 241 insertions(+), 244 deletions(-) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index b2401afd4..7d4fba92d 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -14,6 +14,8 @@ from unittest.mock import call from openstack.block_storage.v2 import backup as _backup +from openstack.block_storage.v2 import snapshot as _snapshot +from openstack.block_storage.v2 import volume as _volume from openstack import exceptions as sdk_exceptions from openstack.test import fakes as sdk_fakes from osc_lib import exceptions @@ -23,68 +25,69 @@ class TestBackupCreate(volume_fakes.TestVolume): - volume = volume_fakes.create_one_volume() - snapshot = volume_fakes.create_one_snapshot() - new_backup = volume_fakes.create_one_backup( - attrs={'volume_id': volume.id, 'snapshot_id': snapshot.id} - ) - columns = ( 'id', 'name', 'volume_id', ) - data = ( - new_backup.id, - new_backup.name, - new_backup.volume_id, - ) def setUp(self): super().setUp() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) self.volume_sdk_client.find_volume.return_value = self.volume + self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) self.volume_sdk_client.find_snapshot.return_value = self.snapshot - self.volume_sdk_client.create_backup.return_value = self.new_backup + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, + volume_id=self.volume.id, + snapshot_id=self.snapshot.id, + ) + self.volume_sdk_client.create_backup.return_value = self.backup + + self.data = ( + self.backup.id, + self.backup.name, + self.backup.volume_id, + ) - # Get the command object to test self.cmd = volume_backup.CreateVolumeBackup(self.app, None) def test_backup_create(self): arglist = [ "--name", - self.new_backup.name, + self.backup.name, "--description", - self.new_backup.description, + self.backup.description, "--container", - self.new_backup.container, + self.backup.container, "--force", "--incremental", "--snapshot", - self.new_backup.snapshot_id, - self.new_backup.volume_id, + self.backup.snapshot_id, + self.backup.volume_id, ] verifylist = [ - ("name", self.new_backup.name), - ("description", self.new_backup.description), - ("container", self.new_backup.container), + ("name", self.backup.name), + ("description", self.backup.description), + ("container", self.backup.container), ("force", True), ("incremental", True), - ("snapshot", self.new_backup.snapshot_id), - ("volume", self.new_backup.volume_id), + ("snapshot", self.backup.snapshot_id), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.create_backup.assert_called_with( - volume_id=self.new_backup.volume_id, - container=self.new_backup.container, - name=self.new_backup.name, - description=self.new_backup.description, + volume_id=self.backup.volume_id, + container=self.backup.container, + name=self.backup.name, + description=self.backup.description, force=True, is_incremental=True, - snapshot_id=self.new_backup.snapshot_id, + snapshot_id=self.backup.snapshot_id, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -92,25 +95,25 @@ def test_backup_create(self): def test_backup_create_without_name(self): arglist = [ "--description", - self.new_backup.description, + self.backup.description, "--container", - self.new_backup.container, - self.new_backup.volume_id, + self.backup.container, + self.backup.volume_id, ] verifylist = [ - ("description", self.new_backup.description), - ("container", self.new_backup.container), - ("volume", self.new_backup.volume_id), + ("description", self.backup.description), + ("container", self.backup.container), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.create_backup.assert_called_with( - volume_id=self.new_backup.volume_id, - container=self.new_backup.container, + volume_id=self.backup.volume_id, + container=self.backup.container, name=None, - description=self.new_backup.description, + description=self.backup.description, force=False, is_incremental=False, ) @@ -119,17 +122,13 @@ def test_backup_create_without_name(self): class TestBackupDelete(volume_fakes.TestVolume): - backups = volume_fakes.create_backups(count=2) - def setUp(self): super().setUp() - self.volume_sdk_client.find_backup = volume_fakes.get_backups( - self.backups - ) + self.backups = list(sdk_fakes.generate_fake_resources(_backup.Backup)) + self.volume_sdk_client.find_backup.side_effect = self.backups self.volume_sdk_client.delete_backup.return_value = None - # Get the command object to mock self.cmd = volume_backup.DeleteVolumeBackup(self.app, None) def test_backup_delete(self): @@ -212,11 +211,6 @@ def test_delete_multiple_backups_with_exception(self): class TestBackupList(volume_fakes.TestVolume): - volume = volume_fakes.create_one_volume() - backups = volume_fakes.create_backups( - attrs={'volume_id': volume.name}, count=3 - ) - columns = ( 'ID', 'Name', @@ -232,45 +226,51 @@ class TestBackupList(volume_fakes.TestVolume): 'Container', ) - data = [] - for b in backups: - data.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - b.is_incremental, - b.created_at, - ) - ) - data_long = [] - for b in backups: - data_long.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - b.is_incremental, - b.created_at, - b.availability_zone, - volume_backup.VolumeIdColumn(b.volume_id), - b.container, - ) - ) - def setUp(self): super().setUp() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume self.volume_sdk_client.volumes.return_value = [self.volume] + self.backups = list( + sdk_fakes.generate_fake_resources( + _backup.Backup, + attrs={'volume_id': self.volume.id}, + ) + ) self.volume_sdk_client.backups.return_value = self.backups - self.volume_sdk_client.find_volume.return_value = self.volume self.volume_sdk_client.find_backup.return_value = self.backups[0] - # Get the command to test + self.data = [] + for b in self.backups: + self.data.append( + ( + b.id, + b.name, + b.description, + b.status, + b.size, + b.is_incremental, + b.created_at, + ) + ) + self.data_long = [] + for b in self.backups: + self.data_long.append( + ( + b.id, + b.name, + b.description, + b.status, + b.size, + b.is_incremental, + b.created_at, + b.availability_zone, + volume_backup.VolumeIdColumn(b.volume_id), + b.container, + ) + ) + self.cmd = volume_backup.ListVolumeBackup(self.app, None) def test_backup_list_without_options(self): @@ -348,35 +348,33 @@ def test_backup_list_with_options(self): class TestBackupRestore(volume_fakes.TestVolume): - volume = volume_fakes.create_one_volume() - backup = volume_fakes.create_one_backup( - attrs={'volume_id': volume.id}, - ) - columns = ( "id", "volume_id", "volume_name", ) - data = ( - backup.id, - volume.id, - volume.name, - ) - def setUp(self): super().setUp() - self.volume_sdk_client.find_backup.return_value = self.backup + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) self.volume_sdk_client.find_volume.return_value = self.volume + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, volume_id=self.volume.id + ) + self.volume_sdk_client.find_backup.return_value = self.backup self.volume_sdk_client.restore_backup.return_value = { 'id': self.backup['id'], 'volume_id': self.volume['id'], 'volume_name': self.volume['name'], } - # Get the command object to mock + self.data = ( + self.backup.id, + self.volume.id, + self.volume.name, + ) + self.cmd = volume_backup.RestoreVolumeBackup(self.app, None) def test_backup_restore(self): @@ -515,8 +513,6 @@ def test_backup_set_state_failed(self): class TestBackupShow(volume_fakes.TestVolume): - backup = volume_fakes.create_one_backup() - columns = ( "availability_zone", "container", @@ -535,30 +531,32 @@ class TestBackupShow(volume_fakes.TestVolume): "updated_at", "volume_id", ) - data = ( - backup.availability_zone, - backup.container, - backup.created_at, - backup.data_timestamp, - backup.description, - backup.fail_reason, - backup.has_dependent_backups, - backup.id, - backup.is_incremental, - backup.name, - backup.object_count, - backup.size, - backup.snapshot_id, - backup.status, - backup.updated_at, - backup.volume_id, - ) def setUp(self): super().setUp() + self.backup = sdk_fakes.generate_fake_resource(_backup.Backup) self.volume_sdk_client.find_backup.return_value = self.backup - # Get the command object to test + + self.data = ( + self.backup.availability_zone, + self.backup.container, + self.backup.created_at, + self.backup.data_timestamp, + self.backup.description, + self.backup.fail_reason, + self.backup.has_dependent_backups, + self.backup.id, + self.backup.is_incremental, + self.backup.name, + self.backup.object_count, + self.backup.size, + self.backup.snapshot_id, + self.backup.status, + self.backup.updated_at, + self.backup.volume_id, + ) + self.cmd = volume_backup.ShowVolumeBackup(self.app, None) def test_backup_show(self): diff --git a/openstackclient/tests/unit/volume/v3/test_volume_backup.py b/openstackclient/tests/unit/volume/v3/test_volume_backup.py index 5069bee56..5be0985a2 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_backup.py @@ -11,9 +11,11 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest.mock import call +from unittest import mock from openstack.block_storage.v3 import backup as _backup +from openstack.block_storage.v3 import snapshot as _snapshot +from openstack.block_storage.v3 import volume as _volume from openstack import exceptions as sdk_exceptions from openstack.test import fakes as sdk_fakes from osc_lib import exceptions @@ -23,68 +25,69 @@ class TestBackupCreate(volume_fakes.TestVolume): - volume = volume_fakes.create_one_volume() - snapshot = volume_fakes.create_one_snapshot() - new_backup = volume_fakes.create_one_backup( - attrs={'volume_id': volume.id, 'snapshot_id': snapshot.id} - ) - columns = ( 'id', 'name', 'volume_id', ) - data = ( - new_backup.id, - new_backup.name, - new_backup.volume_id, - ) def setUp(self): super().setUp() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) self.volume_sdk_client.find_volume.return_value = self.volume + self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) self.volume_sdk_client.find_snapshot.return_value = self.snapshot - self.volume_sdk_client.create_backup.return_value = self.new_backup + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, + volume_id=self.volume.id, + snapshot_id=self.snapshot.id, + ) + self.volume_sdk_client.create_backup.return_value = self.backup + + self.data = ( + self.backup.id, + self.backup.name, + self.backup.volume_id, + ) - # Get the command object to test self.cmd = volume_backup.CreateVolumeBackup(self.app, None) def test_backup_create(self): arglist = [ "--name", - self.new_backup.name, + self.backup.name, "--description", - self.new_backup.description, + self.backup.description, "--container", - self.new_backup.container, + self.backup.container, "--force", "--incremental", "--snapshot", - self.new_backup.snapshot_id, - self.new_backup.volume_id, + self.backup.snapshot_id, + self.backup.volume_id, ] verifylist = [ - ("name", self.new_backup.name), - ("description", self.new_backup.description), - ("container", self.new_backup.container), + ("name", self.backup.name), + ("description", self.backup.description), + ("container", self.backup.container), ("force", True), ("incremental", True), - ("snapshot", self.new_backup.snapshot_id), - ("volume", self.new_backup.volume_id), + ("snapshot", self.backup.snapshot_id), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.create_backup.assert_called_with( - volume_id=self.new_backup.volume_id, - container=self.new_backup.container, - name=self.new_backup.name, - description=self.new_backup.description, + volume_id=self.backup.volume_id, + container=self.backup.container, + name=self.backup.name, + description=self.backup.description, force=True, is_incremental=True, - snapshot_id=self.new_backup.snapshot_id, + snapshot_id=self.backup.snapshot_id, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) @@ -97,18 +100,18 @@ def test_backup_create_with_properties(self): "foo=bar", "--property", "wow=much-cool", - self.new_backup.volume_id, + self.backup.volume_id, ] verifylist = [ ("properties", {"foo": "bar", "wow": "much-cool"}), - ("volume", self.new_backup.volume_id), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.create_backup.assert_called_with( - volume_id=self.new_backup.volume_id, + volume_id=self.backup.volume_id, container=None, name=None, description=None, @@ -127,11 +130,11 @@ def test_backup_create_with_properties_pre_v343(self): "foo=bar", "--property", "wow=much-cool", - self.new_backup.volume_id, + self.backup.volume_id, ] verifylist = [ ("properties", {"foo": "bar", "wow": "much-cool"}), - ("volume", self.new_backup.volume_id), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -146,18 +149,18 @@ def test_backup_create_with_availability_zone(self): arglist = [ "--availability-zone", "my-az", - self.new_backup.volume_id, + self.backup.volume_id, ] verifylist = [ ("availability_zone", "my-az"), - ("volume", self.new_backup.volume_id), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.create_backup.assert_called_with( - volume_id=self.new_backup.volume_id, + volume_id=self.backup.volume_id, container=None, name=None, description=None, @@ -174,11 +177,11 @@ def test_backup_create_with_availability_zone_pre_v351(self): arglist = [ "--availability-zone", "my-az", - self.new_backup.volume_id, + self.backup.volume_id, ] verifylist = [ ("availability_zone", "my-az"), - ("volume", self.new_backup.volume_id), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -190,25 +193,25 @@ def test_backup_create_with_availability_zone_pre_v351(self): def test_backup_create_without_name(self): arglist = [ "--description", - self.new_backup.description, + self.backup.description, "--container", - self.new_backup.container, - self.new_backup.volume_id, + self.backup.container, + self.backup.volume_id, ] verifylist = [ - ("description", self.new_backup.description), - ("container", self.new_backup.container), - ("volume", self.new_backup.volume_id), + ("description", self.backup.description), + ("container", self.backup.container), + ("volume", self.backup.volume_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volume_sdk_client.create_backup.assert_called_with( - volume_id=self.new_backup.volume_id, - container=self.new_backup.container, + volume_id=self.backup.volume_id, + container=self.backup.container, name=None, - description=self.new_backup.description, + description=self.backup.description, force=False, is_incremental=False, ) @@ -217,17 +220,13 @@ def test_backup_create_without_name(self): class TestBackupDelete(volume_fakes.TestVolume): - backups = volume_fakes.create_backups(count=2) - def setUp(self): super().setUp() - self.volume_sdk_client.find_backup = volume_fakes.get_backups( - self.backups - ) + self.backups = list(sdk_fakes.generate_fake_resources(_backup.Backup)) + self.volume_sdk_client.find_backup.side_effect = self.backups self.volume_sdk_client.delete_backup.return_value = None - # Get the command object to mock self.cmd = volume_backup.DeleteVolumeBackup(self.app, None) def test_backup_delete(self): @@ -270,7 +269,7 @@ def test_delete_multiple_backups(self): calls = [] for b in self.backups: - calls.append(call(b.id, ignore_missing=False, force=False)) + calls.append(mock.call(b.id, ignore_missing=False, force=False)) self.volume_sdk_client.delete_backup.assert_has_calls(calls) self.assertIsNone(result) @@ -310,11 +309,6 @@ def test_delete_multiple_backups_with_exception(self): class TestBackupList(volume_fakes.TestVolume): - volume = volume_fakes.create_one_volume() - backups = volume_fakes.create_backups( - attrs={'volume_id': volume.name}, count=3 - ) - columns = ( 'ID', 'Name', @@ -330,45 +324,51 @@ class TestBackupList(volume_fakes.TestVolume): 'Container', ) - data = [] - for b in backups: - data.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - b.is_incremental, - b.created_at, - ) - ) - data_long = [] - for b in backups: - data_long.append( - ( - b.id, - b.name, - b.description, - b.status, - b.size, - b.is_incremental, - b.created_at, - b.availability_zone, - volume_backup.VolumeIdColumn(b.volume_id), - b.container, - ) - ) - def setUp(self): super().setUp() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume self.volume_sdk_client.volumes.return_value = [self.volume] + self.backups = list( + sdk_fakes.generate_fake_resources( + _backup.Backup, + attrs={'volume_id': self.volume.id}, + ) + ) self.volume_sdk_client.backups.return_value = self.backups - self.volume_sdk_client.find_volume.return_value = self.volume self.volume_sdk_client.find_backup.return_value = self.backups[0] - # Get the command to test + self.data = [] + for b in self.backups: + self.data.append( + ( + b.id, + b.name, + b.description, + b.status, + b.size, + b.is_incremental, + b.created_at, + ) + ) + self.data_long = [] + for b in self.backups: + self.data_long.append( + ( + b.id, + b.name, + b.description, + b.status, + b.size, + b.is_incremental, + b.created_at, + b.availability_zone, + volume_backup.VolumeIdColumn(b.volume_id), + b.container, + ) + ) + self.cmd = volume_backup.ListVolumeBackup(self.app, None) def test_backup_list_without_options(self): @@ -446,35 +446,34 @@ def test_backup_list_with_options(self): class TestBackupRestore(volume_fakes.TestVolume): - volume = volume_fakes.create_one_volume() - backup = volume_fakes.create_one_backup( - attrs={'volume_id': volume.id}, - ) - columns = ( "id", "volume_id", "volume_name", ) - data = ( - backup.id, - volume.id, - volume.name, - ) - def setUp(self): super().setUp() - self.volume_sdk_client.find_backup.return_value = self.backup + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) self.volume_sdk_client.find_volume.return_value = self.volume + self.backup = sdk_fakes.generate_fake_resource( + _backup.Backup, volume_id=self.volume.id + ) + self.volume_sdk_client.find_backup.return_value = self.backup + self.volume_sdk_client.create_backup.return_value = self.backup self.volume_sdk_client.restore_backup.return_value = { 'id': self.backup['id'], 'volume_id': self.volume['id'], 'volume_name': self.volume['name'], } - # Get the command object to mock + self.data = ( + self.backup.id, + self.volume.id, + self.volume.name, + ) + self.cmd = volume_backup.RestoreVolumeBackup(self.app, None) def test_backup_restore(self): @@ -841,8 +840,6 @@ def test_backup_unset_property_pre_v343(self): class TestBackupShow(volume_fakes.TestVolume): - backup = volume_fakes.create_one_backup() - columns = ( "availability_zone", "container", @@ -865,34 +862,36 @@ class TestBackupShow(volume_fakes.TestVolume): "user_id", "volume_id", ) - data = ( - backup.availability_zone, - backup.container, - backup.created_at, - backup.data_timestamp, - backup.description, - backup.encryption_key_id, - backup.fail_reason, - backup.has_dependent_backups, - backup.id, - backup.is_incremental, - backup.metadata, - backup.name, - backup.object_count, - backup.project_id, - backup.size, - backup.snapshot_id, - backup.status, - backup.updated_at, - backup.user_id, - backup.volume_id, - ) def setUp(self): super().setUp() + self.backup = sdk_fakes.generate_fake_resource(_backup.Backup) self.volume_sdk_client.find_backup.return_value = self.backup - # Get the command object to test + + self.data = ( + self.backup.availability_zone, + self.backup.container, + self.backup.created_at, + self.backup.data_timestamp, + self.backup.description, + self.backup.encryption_key_id, + self.backup.fail_reason, + self.backup.has_dependent_backups, + self.backup.id, + self.backup.is_incremental, + self.backup.metadata, + self.backup.name, + self.backup.object_count, + self.backup.project_id, + self.backup.size, + self.backup.snapshot_id, + self.backup.status, + self.backup.updated_at, + self.backup.user_id, + self.backup.volume_id, + ) + self.cmd = volume_backup.ShowVolumeBackup(self.app, None) def test_backup_show(self): From e1ff450e342e031d469028c828a2da4f6681914c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 10 Mar 2025 12:01:54 +0000 Subject: [PATCH 152/245] volume: Add v3-specific volume snapshot module Change-Id: I23026abbb909c082fbc0fe0c9b2efcc89f4d464a Signed-off-by: Stephen Finucane --- .../unit/volume/v3/test_volume_snapshot.py | 629 ++++++++++++++++++ openstackclient/volume/v3/volume_snapshot.py | 440 ++++++++++++ setup.cfg | 10 +- 3 files changed, 1074 insertions(+), 5 deletions(-) diff --git a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py index 4c990d840..cc0381a8c 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py @@ -13,9 +13,12 @@ from unittest import mock +from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib import utils +from openstackclient.tests.unit.identity.v3 import fakes as project_fakes +from openstackclient.tests.unit import utils as test_utils from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes_v3 from openstackclient.volume.v3 import volume_snapshot @@ -27,10 +30,163 @@ def setUp(self): self.snapshots_mock = self.volume_client.volume_snapshots self.snapshots_mock.reset_mock() + self.volumes_mock = self.volume_client.volumes + self.volumes_mock.reset_mock() + self.project_mock = self.identity_client.projects + self.project_mock.reset_mock() self.volume_sdk_client.unmanage_snapshot.return_value = None +class TestVolumeSnapshotCreate(TestVolumeSnapshot): + columns = ( + 'created_at', + 'description', + 'id', + 'name', + 'properties', + 'size', + 'status', + 'volume_id', + ) + + def setUp(self): + super().setUp() + + self.volume = volume_fakes.create_one_volume() + self.new_snapshot = volume_fakes.create_one_snapshot( + attrs={'volume_id': self.volume.id} + ) + + self.data = ( + self.new_snapshot.created_at, + self.new_snapshot.description, + self.new_snapshot.id, + self.new_snapshot.name, + format_columns.DictColumn(self.new_snapshot.metadata), + self.new_snapshot.size, + self.new_snapshot.status, + self.new_snapshot.volume_id, + ) + + self.volumes_mock.get.return_value = self.volume + self.snapshots_mock.create.return_value = self.new_snapshot + self.snapshots_mock.manage.return_value = self.new_snapshot + # Get the command object to test + self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None) + + def test_snapshot_create(self): + arglist = [ + "--volume", + self.new_snapshot.volume_id, + "--description", + self.new_snapshot.description, + "--force", + '--property', + 'Alpha=a', + '--property', + 'Beta=b', + self.new_snapshot.name, + ] + verifylist = [ + ("volume", self.new_snapshot.volume_id), + ("description", self.new_snapshot.description), + ("force", True), + ('property', {'Alpha': 'a', 'Beta': 'b'}), + ("snapshot_name", self.new_snapshot.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.create.assert_called_with( + self.new_snapshot.volume_id, + force=True, + name=self.new_snapshot.name, + description=self.new_snapshot.description, + metadata={'Alpha': 'a', 'Beta': 'b'}, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_snapshot_create_without_name(self): + arglist = [ + "--volume", + self.new_snapshot.volume_id, + ] + verifylist = [ + ("volume", self.new_snapshot.volume_id), + ] + self.assertRaises( + test_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist, + ) + + def test_snapshot_create_without_volume(self): + arglist = [ + "--description", + self.new_snapshot.description, + "--force", + self.new_snapshot.name, + ] + verifylist = [ + ("description", self.new_snapshot.description), + ("force", True), + ("snapshot_name", self.new_snapshot.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volumes_mock.get.assert_called_once_with(self.new_snapshot.name) + self.snapshots_mock.create.assert_called_once_with( + self.new_snapshot.volume_id, + force=True, + name=self.new_snapshot.name, + description=self.new_snapshot.description, + metadata=None, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_snapshot_create_with_remote_source(self): + arglist = [ + '--remote-source', + 'source-name=test_source_name', + '--remote-source', + 'source-id=test_source_id', + '--volume', + self.new_snapshot.volume_id, + self.new_snapshot.name, + ] + ref_dict = { + 'source-name': 'test_source_name', + 'source-id': 'test_source_id', + } + verifylist = [ + ('remote_source', ref_dict), + ('volume', self.new_snapshot.volume_id), + ("snapshot_name", self.new_snapshot.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.manage.assert_called_with( + volume_id=self.new_snapshot.volume_id, + ref=ref_dict, + name=self.new_snapshot.name, + description=None, + metadata=None, + ) + self.snapshots_mock.create.assert_not_called() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + class TestVolumeSnapshotDelete(TestVolumeSnapshot): snapshots = volume_fakes.create_snapshots(count=2) @@ -158,3 +314,476 @@ def test_delete_multiple_snapshots_remote(self): calls.append(mock.call(s.id)) self.volume_sdk_client.unmanage_snapshot.assert_has_calls(calls) self.assertIsNone(result) + + +class TestVolumeSnapshotList(TestVolumeSnapshot): + volume = volume_fakes.create_one_volume() + project = project_fakes.FakeProject.create_one_project() + snapshots = volume_fakes.create_snapshots( + attrs={'volume_id': volume.name}, count=3 + ) + + columns = ["ID", "Name", "Description", "Status", "Size"] + columns_long = columns + ["Created At", "Volume", "Properties"] + + data = [] + for s in snapshots: + data.append( + ( + s.id, + s.name, + s.description, + s.status, + s.size, + ) + ) + data_long = [] + for s in snapshots: + data_long.append( + ( + s.id, + s.name, + s.description, + s.status, + s.size, + s.created_at, + volume_snapshot.VolumeIdColumn( + s.volume_id, volume_cache={volume.id: volume} + ), + format_columns.DictColumn(s.metadata), + ) + ) + + def setUp(self): + super().setUp() + + self.volumes_mock.list.return_value = [self.volume] + self.volumes_mock.get.return_value = self.volume + self.project_mock.get.return_value = self.project + self.snapshots_mock.list.return_value = self.snapshots + # Get the command to test + self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None) + + def test_snapshot_list_without_options(self): + arglist = [] + verifylist = [('all_projects', False), ('long', False)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.list.assert_called_once_with( + limit=None, + marker=None, + search_opts={ + 'all_tenants': False, + 'name': None, + 'status': None, + 'project_id': None, + 'volume_id': None, + }, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_snapshot_list_with_options(self): + arglist = [ + "--long", + "--limit", + "2", + "--project", + self.project.id, + "--marker", + self.snapshots[0].id, + ] + verifylist = [ + ("long", True), + ("limit", 2), + ("project", self.project.id), + ("marker", self.snapshots[0].id), + ('all_projects', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.list.assert_called_once_with( + limit=2, + marker=self.snapshots[0].id, + search_opts={ + 'all_tenants': True, + 'project_id': self.project.id, + 'name': None, + 'status': None, + 'volume_id': None, + }, + ) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + def test_snapshot_list_all_projects(self): + arglist = [ + '--all-projects', + ] + verifylist = [('long', False), ('all_projects', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.list.assert_called_once_with( + limit=None, + marker=None, + search_opts={ + 'all_tenants': True, + 'name': None, + 'status': None, + 'project_id': None, + 'volume_id': None, + }, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_snapshot_list_name_option(self): + arglist = [ + '--name', + self.snapshots[0].name, + ] + verifylist = [ + ('all_projects', False), + ('long', False), + ('name', self.snapshots[0].name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.list.assert_called_once_with( + limit=None, + marker=None, + search_opts={ + 'all_tenants': False, + 'name': self.snapshots[0].name, + 'status': None, + 'project_id': None, + 'volume_id': None, + }, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_snapshot_list_status_option(self): + arglist = [ + '--status', + self.snapshots[0].status, + ] + verifylist = [ + ('all_projects', False), + ('long', False), + ('status', self.snapshots[0].status), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.list.assert_called_once_with( + limit=None, + marker=None, + search_opts={ + 'all_tenants': False, + 'name': None, + 'status': self.snapshots[0].status, + 'project_id': None, + 'volume_id': None, + }, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_snapshot_list_volumeid_option(self): + arglist = [ + '--volume', + self.volume.id, + ] + verifylist = [ + ('all_projects', False), + ('long', False), + ('volume', self.volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.snapshots_mock.list.assert_called_once_with( + limit=None, + marker=None, + search_opts={ + 'all_tenants': False, + 'name': None, + 'status': None, + 'project_id': None, + 'volume_id': self.volume.id, + }, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_snapshot_list_negative_limit(self): + arglist = [ + "--limit", + "-2", + ] + verifylist = [ + ("limit", -2), + ] + self.assertRaises( + test_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist, + ) + + +class TestVolumeSnapshotSet(TestVolumeSnapshot): + snapshot = volume_fakes.create_one_snapshot() + + def setUp(self): + super().setUp() + + self.snapshots_mock.get.return_value = self.snapshot + self.snapshots_mock.set_metadata.return_value = None + self.snapshots_mock.update.return_value = None + # Get the command object to mock + self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None) + + def test_snapshot_set_no_option(self): + arglist = [ + self.snapshot.id, + ] + verifylist = [ + ("snapshot", self.snapshot.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot) + self.assertNotCalled(self.snapshots_mock.reset_state) + self.assertNotCalled(self.snapshots_mock.update) + self.assertNotCalled(self.snapshots_mock.set_metadata) + self.assertIsNone(result) + + def test_snapshot_set_name_and_property(self): + arglist = [ + "--name", + "new_snapshot", + "--property", + "x=y", + "--property", + "foo=foo", + self.snapshot.id, + ] + new_property = {"x": "y", "foo": "foo"} + verifylist = [ + ("name", "new_snapshot"), + ("property", new_property), + ("snapshot", self.snapshot.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + "name": "new_snapshot", + } + self.snapshots_mock.update.assert_called_with( + self.snapshot.id, **kwargs + ) + self.snapshots_mock.set_metadata.assert_called_with( + self.snapshot.id, new_property + ) + self.assertIsNone(result) + + def test_snapshot_set_with_no_property(self): + arglist = [ + "--no-property", + self.snapshot.id, + ] + verifylist = [ + ("no_property", True), + ("snapshot", self.snapshot.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot) + self.assertNotCalled(self.snapshots_mock.reset_state) + self.assertNotCalled(self.snapshots_mock.update) + self.assertNotCalled(self.snapshots_mock.set_metadata) + self.snapshots_mock.delete_metadata.assert_called_with( + self.snapshot.id, ["foo"] + ) + self.assertIsNone(result) + + def test_snapshot_set_with_no_property_and_property(self): + arglist = [ + "--no-property", + "--property", + "foo_1=bar_1", + self.snapshot.id, + ] + verifylist = [ + ("no_property", True), + ("property", {"foo_1": "bar_1"}), + ("snapshot", self.snapshot.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot) + self.assertNotCalled(self.snapshots_mock.reset_state) + self.assertNotCalled(self.snapshots_mock.update) + self.snapshots_mock.delete_metadata.assert_called_with( + self.snapshot.id, ["foo"] + ) + self.snapshots_mock.set_metadata.assert_called_once_with( + self.snapshot.id, {"foo_1": "bar_1"} + ) + self.assertIsNone(result) + + def test_snapshot_set_state_to_error(self): + arglist = ["--state", "error", self.snapshot.id] + verifylist = [("state", "error"), ("snapshot", self.snapshot.id)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.snapshots_mock.reset_state.assert_called_with( + self.snapshot.id, "error" + ) + self.assertIsNone(result) + + def test_volume_set_state_failed(self): + self.snapshots_mock.reset_state.side_effect = exceptions.CommandError() + arglist = ['--state', 'error', self.snapshot.id] + verifylist = [('state', 'error'), ('snapshot', self.snapshot.id)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual( + 'One or more of the set operations failed', str(e) + ) + self.snapshots_mock.reset_state.assert_called_once_with( + self.snapshot.id, 'error' + ) + + def test_volume_set_name_and_state_failed(self): + self.snapshots_mock.reset_state.side_effect = exceptions.CommandError() + arglist = [ + '--state', + 'error', + "--name", + "new_snapshot", + self.snapshot.id, + ] + verifylist = [ + ('state', 'error'), + ("name", "new_snapshot"), + ('snapshot', self.snapshot.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual( + 'One or more of the set operations failed', str(e) + ) + kwargs = { + "name": "new_snapshot", + } + self.snapshots_mock.update.assert_called_once_with( + self.snapshot.id, **kwargs + ) + self.snapshots_mock.reset_state.assert_called_once_with( + self.snapshot.id, 'error' + ) + + +class TestVolumeSnapshotShow(TestVolumeSnapshot): + columns = ( + 'created_at', + 'description', + 'id', + 'name', + 'properties', + 'size', + 'status', + 'volume_id', + ) + + def setUp(self): + super().setUp() + + self.snapshot = volume_fakes.create_one_snapshot() + + self.data = ( + self.snapshot.created_at, + self.snapshot.description, + self.snapshot.id, + self.snapshot.name, + format_columns.DictColumn(self.snapshot.metadata), + self.snapshot.size, + self.snapshot.status, + self.snapshot.volume_id, + ) + + self.snapshots_mock.get.return_value = self.snapshot + # Get the command object to test + self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None) + + def test_snapshot_show(self): + arglist = [self.snapshot.id] + verifylist = [("snapshot", self.snapshot.id)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.snapshots_mock.get.assert_called_with(self.snapshot.id) + + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + +class TestVolumeSnapshotUnset(TestVolumeSnapshot): + snapshot = volume_fakes.create_one_snapshot() + + def setUp(self): + super().setUp() + + self.snapshots_mock.get.return_value = self.snapshot + self.snapshots_mock.delete_metadata.return_value = None + # Get the command object to mock + self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None) + + def test_snapshot_unset(self): + arglist = [ + "--property", + "foo", + self.snapshot.id, + ] + verifylist = [ + ("property", ["foo"]), + ("snapshot", self.snapshot.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.snapshots_mock.delete_metadata.assert_called_with( + self.snapshot.id, ["foo"] + ) + self.assertIsNone(result) diff --git a/openstackclient/volume/v3/volume_snapshot.py b/openstackclient/volume/v3/volume_snapshot.py index bb7f5a660..b0b1c0f55 100644 --- a/openstackclient/volume/v3/volume_snapshot.py +++ b/openstackclient/volume/v3/volume_snapshot.py @@ -14,17 +14,144 @@ """Volume v3 snapshot action implementations""" +import copy +import functools import logging +from cliff import columns as cliff_columns +from osc_lib.cli import format_columns +from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import pagination from openstackclient.i18n import _ +from openstackclient.identity import common as identity_common LOG = logging.getLogger(__name__) +class VolumeIdColumn(cliff_columns.FormattableColumn): + """Formattable column for volume ID column. + + Unlike the parent FormattableColumn class, the initializer of the + class takes volume_cache as the second argument. + osc_lib.utils.get_item_properties instantiate cliff FormattableColumn + object with a single parameter "column value", so you need to pass + a partially initialized class like + ``functools.partial(VolumeIdColumn, volume_cache)``. + """ + + def __init__(self, value, volume_cache=None): + super().__init__(value) + self._volume_cache = volume_cache or {} + + def human_readable(self): + """Return a volume name if available + + :rtype: either the volume ID or name + """ + volume_id = self._value + volume = volume_id + if volume_id in self._volume_cache.keys(): + volume = self._volume_cache[volume_id].name + return volume + + +class CreateVolumeSnapshot(command.ShowOne): + _description = _("Create new volume snapshot") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "snapshot_name", + metavar="", + help=_("Name of the new snapshot"), + ) + parser.add_argument( + "--volume", + metavar="", + help=_( + "Volume to snapshot (name or ID) (default is )" + ), + ) + parser.add_argument( + "--description", + metavar="", + help=_("Description of the snapshot"), + ) + parser.add_argument( + "--force", + action="store_true", + default=False, + help=_( + "Create a snapshot attached to an instance. Default is False" + ), + ) + parser.add_argument( + "--property", + metavar="", + action=parseractions.KeyValueAction, + help=_( + "Set a property to this snapshot " + "(repeat option to set multiple properties)" + ), + ) + parser.add_argument( + "--remote-source", + metavar="", + action=parseractions.KeyValueAction, + help=_( + "The attribute(s) of the existing remote volume snapshot " + "(admin required) (repeat option to specify multiple " + "attributes) e.g.: '--remote-source source-name=test_name " + "--remote-source source-id=test_id'" + ), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + volume = parsed_args.volume + if not parsed_args.volume: + volume = parsed_args.snapshot_name + volume_id = utils.find_resource(volume_client.volumes, volume).id + if parsed_args.remote_source: + # Create a new snapshot from an existing remote snapshot source + if parsed_args.force: + msg = _( + "'--force' option will not work when you create " + "new volume snapshot from an existing remote " + "volume snapshot" + ) + LOG.warning(msg) + snapshot = volume_client.volume_snapshots.manage( + volume_id=volume_id, + ref=parsed_args.remote_source, + name=parsed_args.snapshot_name, + description=parsed_args.description, + metadata=parsed_args.property, + ) + else: + # create a new snapshot from scratch + snapshot = volume_client.volume_snapshots.create( + volume_id, + force=parsed_args.force, + name=parsed_args.snapshot_name, + description=parsed_args.description, + metadata=parsed_args.property, + ) + snapshot._info.update( + { + 'properties': format_columns.DictColumn( + snapshot._info.pop('metadata') + ) + } + ) + return zip(*sorted(snapshot._info.items())) + + class DeleteVolumeSnapshot(command.Command): _description = _("Delete volume snapshot(s)") @@ -96,3 +223,316 @@ def take_action(self, parsed_args): 'total': total, } raise exceptions.CommandError(msg) + + +class ListVolumeSnapshot(command.Lister): + _description = _("List volume snapshots") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + '--all-projects', + action='store_true', + default=False, + help=_('Include all projects (admin only)'), + ) + parser.add_argument( + '--project', + metavar='', + help=_('Filter results by project (name or ID) (admin only)'), + ) + identity_common.add_project_domain_option_to_parser(parser) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=_('List additional fields in output'), + ) + parser.add_argument( + '--name', + metavar='', + default=None, + help=_('Filters results by a name.'), + ) + parser.add_argument( + '--status', + metavar='', + choices=[ + 'available', + 'error', + 'creating', + 'deleting', + 'error_deleting', + ], + help=_( + "Filters results by a status. " + "('available', 'error', 'creating', 'deleting'" + " or 'error_deleting')" + ), + ) + parser.add_argument( + '--volume', + metavar='', + default=None, + help=_('Filters results by a volume (name or ID).'), + ) + pagination.add_marker_pagination_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + identity_client = self.app.client_manager.identity + + if parsed_args.long: + columns = [ + 'ID', + 'Name', + 'Description', + 'Status', + 'Size', + 'Created At', + 'Volume ID', + 'Metadata', + ] + column_headers = copy.deepcopy(columns) + column_headers[6] = 'Volume' + column_headers[7] = 'Properties' + else: + columns = ['ID', 'Name', 'Description', 'Status', 'Size'] + column_headers = copy.deepcopy(columns) + + # Cache the volume list + volume_cache = {} + try: + for s in volume_client.volumes.list(): + volume_cache[s.id] = s + except Exception: # noqa: S110 + # Just forget it if there's any trouble + pass + _VolumeIdColumn = functools.partial( + VolumeIdColumn, volume_cache=volume_cache + ) + + volume_id = None + if parsed_args.volume: + volume_id = utils.find_resource( + volume_client.volumes, parsed_args.volume + ).id + + project_id = None + if parsed_args.project: + project_id = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + + # set value of 'all_tenants' when using project option + all_projects = ( + True if parsed_args.project else parsed_args.all_projects + ) + + search_opts = { + 'all_tenants': all_projects, + 'project_id': project_id, + 'name': parsed_args.name, + 'status': parsed_args.status, + 'volume_id': volume_id, + } + + data = volume_client.volume_snapshots.list( + search_opts=search_opts, + marker=parsed_args.marker, + limit=parsed_args.limit, + ) + return ( + column_headers, + ( + utils.get_item_properties( + s, + columns, + formatters={ + 'Metadata': format_columns.DictColumn, + 'Volume ID': _VolumeIdColumn, + }, + ) + for s in data + ), + ) + + +class SetVolumeSnapshot(command.Command): + _description = _("Set volume snapshot properties") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'snapshot', + metavar='', + help=_('Snapshot to modify (name or ID)'), + ) + parser.add_argument( + '--name', metavar='', help=_('New snapshot name') + ) + parser.add_argument( + '--description', + metavar='', + help=_('New snapshot description'), + ) + parser.add_argument( + "--no-property", + dest="no_property", + action="store_true", + help=_( + "Remove all properties from " + "(specify both --no-property and --property to " + "remove the current properties before setting " + "new properties.)" + ), + ) + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + help=_( + 'Property to add/change for this snapshot ' + '(repeat option to set multiple properties)' + ), + ) + parser.add_argument( + '--state', + metavar='', + choices=[ + 'available', + 'error', + 'creating', + 'deleting', + 'error_deleting', + ], + help=_( + 'New snapshot state. ("available", "error", "creating", ' + '"deleting", or "error_deleting") (admin only) ' + '(This option simply changes the state of the snapshot ' + 'in the database with no regard to actual status, ' + 'exercise caution when using)' + ), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + snapshot = utils.find_resource( + volume_client.volume_snapshots, parsed_args.snapshot + ) + + result = 0 + if parsed_args.no_property: + try: + key_list = snapshot.metadata.keys() + volume_client.volume_snapshots.delete_metadata( + snapshot.id, + list(key_list), + ) + except Exception as e: + LOG.error(_("Failed to clean snapshot properties: %s"), e) + result += 1 + + if parsed_args.property: + try: + volume_client.volume_snapshots.set_metadata( + snapshot.id, parsed_args.property + ) + except Exception as e: + LOG.error(_("Failed to set snapshot property: %s"), e) + result += 1 + + if parsed_args.state: + try: + volume_client.volume_snapshots.reset_state( + snapshot.id, parsed_args.state + ) + except Exception as e: + LOG.error(_("Failed to set snapshot state: %s"), e) + result += 1 + + kwargs = {} + if parsed_args.name: + kwargs['name'] = parsed_args.name + if parsed_args.description: + kwargs['description'] = parsed_args.description + if kwargs: + try: + volume_client.volume_snapshots.update(snapshot.id, **kwargs) + except Exception as e: + LOG.error( + _("Failed to update snapshot name or description: %s"), + e, + ) + result += 1 + + if result > 0: + raise exceptions.CommandError( + _("One or more of the set operations failed") + ) + + +class ShowVolumeSnapshot(command.ShowOne): + _description = _("Display volume snapshot details") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "snapshot", + metavar="", + help=_("Snapshot to display (name or ID)"), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + snapshot = utils.find_resource( + volume_client.volume_snapshots, parsed_args.snapshot + ) + snapshot._info.update( + { + 'properties': format_columns.DictColumn( + snapshot._info.pop('metadata') + ) + } + ) + return zip(*sorted(snapshot._info.items())) + + +class UnsetVolumeSnapshot(command.Command): + _description = _("Unset volume snapshot properties") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'snapshot', + metavar='', + help=_('Snapshot to modify (name or ID)'), + ) + parser.add_argument( + '--property', + metavar='', + action='append', + default=[], + help=_( + 'Property to remove from snapshot ' + '(repeat option to remove multiple properties)' + ), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + snapshot = utils.find_resource( + volume_client.volume_snapshots, parsed_args.snapshot + ) + + if parsed_args.property: + volume_client.volume_snapshots.delete_metadata( + snapshot.id, + parsed_args.property, + ) diff --git a/setup.cfg b/setup.cfg index 519a371cb..5c6449d5f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -780,12 +780,12 @@ openstack.volume.v3 = block_storage_resource_filter_list = openstackclient.volume.v3.block_storage_resource_filter:ListBlockStorageResourceFilter block_storage_resource_filter_show = openstackclient.volume.v3.block_storage_resource_filter:ShowBlockStorageResourceFilter - volume_snapshot_create = openstackclient.volume.v2.volume_snapshot:CreateVolumeSnapshot + volume_snapshot_create = openstackclient.volume.v3.volume_snapshot:CreateVolumeSnapshot volume_snapshot_delete = openstackclient.volume.v3.volume_snapshot:DeleteVolumeSnapshot - volume_snapshot_list = openstackclient.volume.v2.volume_snapshot:ListVolumeSnapshot - volume_snapshot_set = openstackclient.volume.v2.volume_snapshot:SetVolumeSnapshot - volume_snapshot_show = openstackclient.volume.v2.volume_snapshot:ShowVolumeSnapshot - volume_snapshot_unset = openstackclient.volume.v2.volume_snapshot:UnsetVolumeSnapshot + volume_snapshot_list = openstackclient.volume.v3.volume_snapshot:ListVolumeSnapshot + volume_snapshot_set = openstackclient.volume.v3.volume_snapshot:SetVolumeSnapshot + volume_snapshot_show = openstackclient.volume.v3.volume_snapshot:ShowVolumeSnapshot + volume_snapshot_unset = openstackclient.volume.v3.volume_snapshot:UnsetVolumeSnapshot volume_type_create = openstackclient.volume.v3.volume_type:CreateVolumeType volume_type_delete = openstackclient.volume.v3.volume_type:DeleteVolumeType From fc42f12eb212f6390cc7a80ce57531b1b80d32b6 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 10 Mar 2025 11:50:55 +0000 Subject: [PATCH 153/245] volume: Migrate 'snapshot delete' to SDK Change-Id: Iba89d521ec17a642c5905b0cff908b5a4a9dafd0 Signed-off-by: Stephen Finucane --- .../unit/volume/v2/test_volume_snapshot.py | 88 ++++++++------- .../unit/volume/v3/test_volume_snapshot.py | 102 ++++++++++-------- openstackclient/volume/v2/volume_snapshot.py | 14 +-- openstackclient/volume/v3/volume_snapshot.py | 18 ++-- 4 files changed, 124 insertions(+), 98 deletions(-) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py index 6e7e02ae0..fc26e53f3 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py @@ -13,9 +13,11 @@ from unittest import mock +from openstack.block_storage.v2 import snapshot as _snapshot +from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions -from osc_lib import utils from openstackclient.tests.unit.identity.v3 import fakes as project_fakes from openstackclient.tests.unit import utils as test_utils @@ -184,16 +186,16 @@ def test_snapshot_create_with_remote_source(self): self.assertEqual(self.data, data) -class TestVolumeSnapshotDelete(TestVolumeSnapshot): - snapshots = volume_fakes.create_snapshots(count=2) - +class TestVolumeSnapshotDelete(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.snapshots_mock.get = volume_fakes.get_snapshots(self.snapshots) - self.snapshots_mock.delete.return_value = None + self.snapshots = list( + sdk_fakes.generate_fake_resources(_snapshot.Snapshot) + ) + self.volume_sdk_client.find_snapshot.side_effect = self.snapshots + self.volume_sdk_client.delete_snapshot.return_value = None - # Get the command object to mock self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None) def test_snapshot_delete(self): @@ -202,11 +204,14 @@ def test_snapshot_delete(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.snapshots_mock.delete.assert_called_with( - self.snapshots[0].id, False + self.volume_sdk_client.find_snapshot.assert_called_once_with( + self.snapshots[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_snapshot.assert_called_once_with( + self.snapshots[0].id, force=False ) - self.assertIsNone(result) def test_snapshot_delete_with_force(self): arglist = ['--force', self.snapshots[0].id] @@ -214,11 +219,14 @@ def test_snapshot_delete_with_force(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.snapshots_mock.delete.assert_called_with( - self.snapshots[0].id, True + self.volume_sdk_client.find_snapshot.assert_called_once_with( + self.snapshots[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_snapshot.assert_called_once_with( + self.snapshots[0].id, force=True ) - self.assertIsNone(result) def test_delete_multiple_snapshots(self): arglist = [] @@ -227,17 +235,24 @@ def test_delete_multiple_snapshots(self): verifylist = [ ('snapshots', arglist), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - calls = [] - for s in self.snapshots: - calls.append(mock.call(s.id, False)) - self.snapshots_mock.delete.assert_has_calls(calls) + result = self.cmd.take_action(parsed_args) self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_has_calls( + [mock.call(x.id, ignore_missing=False) for x in self.snapshots] + ) + self.volume_sdk_client.delete_snapshot.assert_has_calls( + [mock.call(x.id, force=False) for x in self.snapshots] + ) + def test_delete_multiple_snapshots_with_exception(self): + self.volume_sdk_client.find_snapshot.side_effect = [ + self.snapshots[0], + sdk_exceptions.NotFoundException(), + ] + arglist = [ self.snapshots[0].id, 'unexist_snapshot', @@ -248,25 +263,24 @@ def test_delete_multiple_snapshots_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - find_mock_result = [self.snapshots[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 snapshots failed to delete.', str(e)) - - find_mock.assert_any_call( - self.snapshots_mock, self.snapshots[0].id - ) - find_mock.assert_any_call(self.snapshots_mock, 'unexist_snapshot') + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual('1 of 2 snapshots failed to delete.', str(exc)) - self.assertEqual(2, find_mock.call_count) - self.snapshots_mock.delete.assert_called_once_with( - self.snapshots[0].id, False - ) + self.volume_sdk_client.find_snapshot.assert_has_calls( + [ + mock.call(self.snapshots[0].id, ignore_missing=False), + mock.call('unexist_snapshot', ignore_missing=False), + ] + ) + self.volume_sdk_client.delete_snapshot.assert_has_calls( + [ + mock.call(self.snapshots[0].id, force=False), + ] + ) class TestVolumeSnapshotList(TestVolumeSnapshot): diff --git a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py index cc0381a8c..2fecf631d 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py @@ -13,9 +13,11 @@ from unittest import mock +from openstack.block_storage.v3 import snapshot as _snapshot +from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions -from osc_lib import utils from openstackclient.tests.unit.identity.v3 import fakes as project_fakes from openstackclient.tests.unit import utils as test_utils @@ -187,16 +189,17 @@ def test_snapshot_create_with_remote_source(self): self.assertEqual(self.data, data) -class TestVolumeSnapshotDelete(TestVolumeSnapshot): - snapshots = volume_fakes.create_snapshots(count=2) - +class TestVolumeSnapshotDelete(volume_fakes_v3.TestVolume): def setUp(self): super().setUp() - self.snapshots_mock.get = volume_fakes.get_snapshots(self.snapshots) - self.snapshots_mock.delete.return_value = None + self.snapshots = list( + sdk_fakes.generate_fake_resources(_snapshot.Snapshot) + ) + self.volume_sdk_client.find_snapshot.side_effect = self.snapshots + self.volume_sdk_client.delete_snapshot.return_value = None + self.volume_sdk_client.unmanage_snapshot.return_value = None - # Get the command object to mock self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None) def test_snapshot_delete(self): @@ -205,11 +208,14 @@ def test_snapshot_delete(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.snapshots_mock.delete.assert_called_with( - self.snapshots[0].id, False + self.volume_sdk_client.find_snapshot.assert_called_once_with( + self.snapshots[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_snapshot.assert_called_once_with( + self.snapshots[0].id, force=False ) - self.assertIsNone(result) def test_snapshot_delete_with_force(self): arglist = ['--force', self.snapshots[0].id] @@ -217,11 +223,14 @@ def test_snapshot_delete_with_force(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.snapshots_mock.delete.assert_called_with( - self.snapshots[0].id, True + self.volume_sdk_client.find_snapshot.assert_called_once_with( + self.snapshots[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_snapshot.assert_called_once_with( + self.snapshots[0].id, force=True ) - self.assertIsNone(result) def test_delete_multiple_snapshots(self): arglist = [] @@ -230,17 +239,24 @@ def test_delete_multiple_snapshots(self): verifylist = [ ('snapshots', arglist), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - calls = [] - for s in self.snapshots: - calls.append(mock.call(s.id, False)) - self.snapshots_mock.delete.assert_has_calls(calls) + result = self.cmd.take_action(parsed_args) self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_has_calls( + [mock.call(x.id, ignore_missing=False) for x in self.snapshots] + ) + self.volume_sdk_client.delete_snapshot.assert_has_calls( + [mock.call(x.id, force=False) for x in self.snapshots] + ) + def test_delete_multiple_snapshots_with_exception(self): + self.volume_sdk_client.find_snapshot.side_effect = [ + self.snapshots[0], + sdk_exceptions.NotFoundException(), + ] + arglist = [ self.snapshots[0].id, 'unexist_snapshot', @@ -251,25 +267,24 @@ def test_delete_multiple_snapshots_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - find_mock_result = [self.snapshots[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 snapshots failed to delete.', str(e)) - - find_mock.assert_any_call( - self.snapshots_mock, self.snapshots[0].id - ) - find_mock.assert_any_call(self.snapshots_mock, 'unexist_snapshot') + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual('1 of 2 snapshots failed to delete.', str(exc)) - self.assertEqual(2, find_mock.call_count) - self.snapshots_mock.delete.assert_called_once_with( - self.snapshots[0].id, False - ) + self.volume_sdk_client.find_snapshot.assert_has_calls( + [ + mock.call(self.snapshots[0].id, ignore_missing=False), + mock.call('unexist_snapshot', ignore_missing=False), + ] + ) + self.volume_sdk_client.delete_snapshot.assert_has_calls( + [ + mock.call(self.snapshots[0].id, force=False), + ] + ) def test_snapshot_delete_remote(self): arglist = ['--remote', self.snapshots[0].id] @@ -277,11 +292,11 @@ def test_snapshot_delete_remote(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) self.volume_sdk_client.unmanage_snapshot.assert_called_with( self.snapshots[0].id ) - self.assertIsNone(result) def test_snapshot_delete_with_remote_force(self): arglist = ['--remote', '--force', self.snapshots[0].id] @@ -305,16 +320,15 @@ def test_delete_multiple_snapshots_remote(self): for s in self.snapshots: arglist.append(s.id) verifylist = [('remote', True), ('snapshots', arglist[1:])] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - calls = [] - for s in self.snapshots: - calls.append(mock.call(s.id)) - self.volume_sdk_client.unmanage_snapshot.assert_has_calls(calls) + result = self.cmd.take_action(parsed_args) self.assertIsNone(result) + self.volume_sdk_client.unmanage_snapshot.assert_has_calls( + [mock.call(s.id) for s in self.snapshots] + ) + class TestVolumeSnapshotList(TestVolumeSnapshot): volume = volume_fakes.create_one_volume() diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index e8d5484a2..1c607a201 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -175,16 +175,16 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume result = 0 - for i in parsed_args.snapshots: + for snapshot in parsed_args.snapshots: try: - snapshot_id = utils.find_resource( - volume_client.volume_snapshots, i + snapshot_id = volume_client.find_snapshot( + snapshot, ignore_missing=False ).id - volume_client.volume_snapshots.delete( - snapshot_id, parsed_args.force + volume_client.delete_snapshot( + snapshot_id, force=parsed_args.force ) except Exception as e: result += 1 @@ -193,7 +193,7 @@ def take_action(self, parsed_args): "Failed to delete snapshot with " "name or ID '%(snapshot)s': %(e)s" ) - % {'snapshot': i, 'e': e} + % {'snapshot': snapshot, 'e': e} ) if result > 0: diff --git a/openstackclient/volume/v3/volume_snapshot.py b/openstackclient/volume/v3/volume_snapshot.py index b0b1c0f55..a25aa7e62 100644 --- a/openstackclient/volume/v3/volume_snapshot.py +++ b/openstackclient/volume/v3/volume_snapshot.py @@ -182,9 +182,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_client_sdk = self.app.client_manager.sdk_connection.volume - + volume_client = self.app.client_manager.sdk_connection.volume result = 0 if parsed_args.remote: @@ -195,16 +193,16 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - for i in parsed_args.snapshots: + for snapshot in parsed_args.snapshots: try: - snapshot_id = utils.find_resource( - volume_client.volume_snapshots, i + snapshot_id = volume_client.find_snapshot( + snapshot, ignore_missing=False ).id if parsed_args.remote: - volume_client_sdk.unmanage_snapshot(snapshot_id) + volume_client.unmanage_snapshot(snapshot_id) else: - volume_client.volume_snapshots.delete( - snapshot_id, parsed_args.force + volume_client.delete_snapshot( + snapshot_id, force=parsed_args.force ) except Exception as e: result += 1 @@ -213,7 +211,7 @@ def take_action(self, parsed_args): "Failed to delete snapshot with " "name or ID '%(snapshot)s': %(e)s" ) - % {'snapshot': i, 'e': e} + % {'snapshot': snapshot, 'e': e} ) if result > 0: From e0020aec6a095a3dd42ce5dfe99301da292a8d75 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 10 Mar 2025 16:54:04 +0000 Subject: [PATCH 154/245] volume: Migrate 'snapshot create' to SDK Change-Id: I0c2811b8518c41658803a7b2053f0f5d5114ed67 Signed-off-by: Stephen Finucane --- .../unit/volume/v2/test_volume_snapshot.py | 112 ++++++++++-------- .../unit/volume/v3/test_volume_snapshot.py | 104 ++++++++-------- openstackclient/volume/v2/volume_snapshot.py | 67 ++++++++--- openstackclient/volume/v3/volume_snapshot.py | 69 ++++++++--- 4 files changed, 221 insertions(+), 131 deletions(-) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py index fc26e53f3..dc605be59 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py @@ -14,6 +14,7 @@ from unittest import mock from openstack.block_storage.v2 import snapshot as _snapshot +from openstack.block_storage.v3 import volume as _volume from openstack import exceptions as sdk_exceptions from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns @@ -37,7 +38,7 @@ def setUp(self): self.project_mock.reset_mock() -class TestVolumeSnapshotCreate(TestVolumeSnapshot): +class TestVolumeSnapshotCreate(volume_fakes.TestVolume): columns = ( 'created_at', 'description', @@ -52,69 +53,71 @@ class TestVolumeSnapshotCreate(TestVolumeSnapshot): def setUp(self): super().setUp() - self.volume = volume_fakes.create_one_volume() - self.new_snapshot = volume_fakes.create_one_snapshot( - attrs={'volume_id': self.volume.id} + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume + self.snapshot = sdk_fakes.generate_fake_resource( + _snapshot.Snapshot, volume_id=self.volume.id ) + self.volume_sdk_client.create_snapshot.return_value = self.snapshot + self.volume_sdk_client.manage_snapshot.return_value = self.snapshot self.data = ( - self.new_snapshot.created_at, - self.new_snapshot.description, - self.new_snapshot.id, - self.new_snapshot.name, - format_columns.DictColumn(self.new_snapshot.metadata), - self.new_snapshot.size, - self.new_snapshot.status, - self.new_snapshot.volume_id, + self.snapshot.created_at, + self.snapshot.description, + self.snapshot.id, + self.snapshot.name, + format_columns.DictColumn(self.snapshot.metadata), + self.snapshot.size, + self.snapshot.status, + self.snapshot.volume_id, ) - self.volumes_mock.get.return_value = self.volume - self.snapshots_mock.create.return_value = self.new_snapshot - self.snapshots_mock.manage.return_value = self.new_snapshot - # Get the command object to test self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None) def test_snapshot_create(self): arglist = [ "--volume", - self.new_snapshot.volume_id, + self.snapshot.volume_id, "--description", - self.new_snapshot.description, + self.snapshot.description, "--force", '--property', 'Alpha=a', '--property', 'Beta=b', - self.new_snapshot.name, + self.snapshot.name, ] verifylist = [ - ("volume", self.new_snapshot.volume_id), - ("description", self.new_snapshot.description), + ("volume", self.snapshot.volume_id), + ("description", self.snapshot.description), ("force", True), - ('property', {'Alpha': 'a', 'Beta': 'b'}), - ("snapshot_name", self.new_snapshot.name), + ('properties', {'Alpha': 'a', 'Beta': 'b'}), + ("snapshot_name", self.snapshot.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.create.assert_called_with( - self.new_snapshot.volume_id, + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + self.volume_sdk_client.find_volume.assert_called_once_with( + self.snapshot.volume_id, ignore_missing=False + ) + self.volume_sdk_client.create_snapshot.assert_called_with( + volume_id=self.snapshot.volume_id, force=True, - name=self.new_snapshot.name, - description=self.new_snapshot.description, + name=self.snapshot.name, + description=self.snapshot.description, metadata={'Alpha': 'a', 'Beta': 'b'}, ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) def test_snapshot_create_without_name(self): arglist = [ "--volume", - self.new_snapshot.volume_id, + self.snapshot.volume_id, ] verifylist = [ - ("volume", self.new_snapshot.volume_id), + ("volume", self.snapshot.volume_id), ] self.assertRaises( test_utils.ParserException, @@ -127,29 +130,31 @@ def test_snapshot_create_without_name(self): def test_snapshot_create_without_volume(self): arglist = [ "--description", - self.new_snapshot.description, + self.snapshot.description, "--force", - self.new_snapshot.name, + self.snapshot.name, ] verifylist = [ - ("description", self.new_snapshot.description), + ("description", self.snapshot.description), ("force", True), - ("snapshot_name", self.new_snapshot.name), + ("snapshot_name", self.snapshot.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_once_with(self.new_snapshot.name) - self.snapshots_mock.create.assert_called_once_with( - self.new_snapshot.volume_id, + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + self.volume_sdk_client.find_volume.assert_called_once_with( + self.snapshot.name, ignore_missing=False + ) + self.volume_sdk_client.create_snapshot.assert_called_once_with( + volume_id=self.snapshot.volume_id, force=True, - name=self.new_snapshot.name, - description=self.new_snapshot.description, + name=self.snapshot.name, + description=self.snapshot.description, metadata=None, ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) def test_snapshot_create_with_remote_source(self): arglist = [ @@ -158,8 +163,8 @@ def test_snapshot_create_with_remote_source(self): '--remote-source', 'source-id=test_source_id', '--volume', - self.new_snapshot.volume_id, - self.new_snapshot.name, + self.snapshot.volume_id, + self.snapshot.name, ] ref_dict = { 'source-name': 'test_source_name', @@ -167,23 +172,26 @@ def test_snapshot_create_with_remote_source(self): } verifylist = [ ('remote_source', ref_dict), - ('volume', self.new_snapshot.volume_id), - ("snapshot_name", self.new_snapshot.name), + ('volume', self.snapshot.volume_id), + ("snapshot_name", self.snapshot.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.manage.assert_called_with( - volume_id=self.new_snapshot.volume_id, + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + self.volume_sdk_client.find_volume.assert_called_once_with( + self.snapshot.volume_id, ignore_missing=False + ) + self.volume_sdk_client.manage_snapshot.assert_called_with( + volume_id=self.snapshot.volume_id, ref=ref_dict, - name=self.new_snapshot.name, + name=self.snapshot.name, description=None, metadata=None, ) - self.snapshots_mock.create.assert_not_called() - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + self.volume_sdk_client.create_snapshot.assert_not_called() class TestVolumeSnapshotDelete(volume_fakes.TestVolume): diff --git a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py index 2fecf631d..26c993220 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py @@ -14,6 +14,7 @@ from unittest import mock from openstack.block_storage.v3 import snapshot as _snapshot +from openstack.block_storage.v3 import volume as _volume from openstack import exceptions as sdk_exceptions from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns @@ -40,7 +41,7 @@ def setUp(self): self.volume_sdk_client.unmanage_snapshot.return_value = None -class TestVolumeSnapshotCreate(TestVolumeSnapshot): +class TestVolumeSnapshotCreate(volume_fakes_v3.TestVolume): columns = ( 'created_at', 'description', @@ -55,57 +56,59 @@ class TestVolumeSnapshotCreate(TestVolumeSnapshot): def setUp(self): super().setUp() - self.volume = volume_fakes.create_one_volume() - self.new_snapshot = volume_fakes.create_one_snapshot( - attrs={'volume_id': self.volume.id} + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume + self.snapshot = sdk_fakes.generate_fake_resource( + _snapshot.Snapshot, volume_id=self.volume.id ) + self.volume_sdk_client.create_snapshot.return_value = self.snapshot + self.volume_sdk_client.manage_snapshot.return_value = self.snapshot self.data = ( - self.new_snapshot.created_at, - self.new_snapshot.description, - self.new_snapshot.id, - self.new_snapshot.name, - format_columns.DictColumn(self.new_snapshot.metadata), - self.new_snapshot.size, - self.new_snapshot.status, - self.new_snapshot.volume_id, + self.snapshot.created_at, + self.snapshot.description, + self.snapshot.id, + self.snapshot.name, + format_columns.DictColumn(self.snapshot.metadata), + self.snapshot.size, + self.snapshot.status, + self.snapshot.volume_id, ) - self.volumes_mock.get.return_value = self.volume - self.snapshots_mock.create.return_value = self.new_snapshot - self.snapshots_mock.manage.return_value = self.new_snapshot - # Get the command object to test self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None) def test_snapshot_create(self): arglist = [ "--volume", - self.new_snapshot.volume_id, + self.snapshot.volume_id, "--description", - self.new_snapshot.description, + self.snapshot.description, "--force", '--property', 'Alpha=a', '--property', 'Beta=b', - self.new_snapshot.name, + self.snapshot.name, ] verifylist = [ - ("volume", self.new_snapshot.volume_id), - ("description", self.new_snapshot.description), + ("volume", self.snapshot.volume_id), + ("description", self.snapshot.description), ("force", True), - ('property', {'Alpha': 'a', 'Beta': 'b'}), - ("snapshot_name", self.new_snapshot.name), + ('properties', {'Alpha': 'a', 'Beta': 'b'}), + ("snapshot_name", self.snapshot.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.create.assert_called_with( - self.new_snapshot.volume_id, + self.volume_sdk_client.find_volume.assert_called_once_with( + self.snapshot.volume_id, ignore_missing=False + ) + self.volume_sdk_client.create_snapshot.assert_called_with( + volume_id=self.snapshot.volume_id, force=True, - name=self.new_snapshot.name, - description=self.new_snapshot.description, + name=self.snapshot.name, + description=self.snapshot.description, metadata={'Alpha': 'a', 'Beta': 'b'}, ) self.assertEqual(self.columns, columns) @@ -114,10 +117,10 @@ def test_snapshot_create(self): def test_snapshot_create_without_name(self): arglist = [ "--volume", - self.new_snapshot.volume_id, + self.snapshot.volume_id, ] verifylist = [ - ("volume", self.new_snapshot.volume_id), + ("volume", self.snapshot.volume_id), ] self.assertRaises( test_utils.ParserException, @@ -130,25 +133,27 @@ def test_snapshot_create_without_name(self): def test_snapshot_create_without_volume(self): arglist = [ "--description", - self.new_snapshot.description, + self.snapshot.description, "--force", - self.new_snapshot.name, + self.snapshot.name, ] verifylist = [ - ("description", self.new_snapshot.description), + ("description", self.snapshot.description), ("force", True), - ("snapshot_name", self.new_snapshot.name), + ("snapshot_name", self.snapshot.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_once_with(self.new_snapshot.name) - self.snapshots_mock.create.assert_called_once_with( - self.new_snapshot.volume_id, + self.volume_sdk_client.find_volume.assert_called_once_with( + self.snapshot.name, ignore_missing=False + ) + self.volume_sdk_client.create_snapshot.assert_called_with( + volume_id=self.snapshot.volume_id, force=True, - name=self.new_snapshot.name, - description=self.new_snapshot.description, + name=self.snapshot.name, + description=self.snapshot.description, metadata=None, ) self.assertEqual(self.columns, columns) @@ -161,8 +166,8 @@ def test_snapshot_create_with_remote_source(self): '--remote-source', 'source-id=test_source_id', '--volume', - self.new_snapshot.volume_id, - self.new_snapshot.name, + self.snapshot.volume_id, + self.snapshot.name, ] ref_dict = { 'source-name': 'test_source_name', @@ -170,23 +175,26 @@ def test_snapshot_create_with_remote_source(self): } verifylist = [ ('remote_source', ref_dict), - ('volume', self.new_snapshot.volume_id), - ("snapshot_name", self.new_snapshot.name), + ('volume', self.snapshot.volume_id), + ("snapshot_name", self.snapshot.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.manage.assert_called_with( - volume_id=self.new_snapshot.volume_id, + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + self.volume_sdk_client.find_volume.assert_called_once_with( + self.snapshot.volume_id, ignore_missing=False + ) + self.volume_sdk_client.manage_snapshot.assert_called_with( + volume_id=self.snapshot.volume_id, ref=ref_dict, - name=self.new_snapshot.name, + name=self.snapshot.name, description=None, metadata=None, ) - self.snapshots_mock.create.assert_not_called() - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + self.volume_sdk_client.create_snapshot.assert_not_called() class TestVolumeSnapshotDelete(volume_fakes_v3.TestVolume): diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index 1c607a201..4efc5f0e6 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -17,8 +17,10 @@ import copy import functools import logging +import typing as ty from cliff import columns as cliff_columns +from openstack.block_storage.v2 import snapshot as _snapshot from osc_lib.cli import format_columns from osc_lib.cli import parseractions from osc_lib.command import command @@ -60,6 +62,42 @@ def human_readable(self): return volume +def _format_snapshot(snapshot: _snapshot.Snapshot) -> dict[str, ty.Any]: + # Some columns returned by openstacksdk should not be shown because they're + # either irrelevant or duplicates + ignored_columns = { + # computed columns + 'location', + # create-only columns + 'consumes_quota', + 'force', + 'group_snapshot_id', + # ignored columns + 'os-extended-snapshot-attributes:progress', + 'os-extended-snapshot-attributes:project_id', + 'updated_at', + 'user_id', + # unnecessary columns + 'links', + } + + info = snapshot.to_dict(original_names=True) + data = {} + for key, value in info.items(): + if key in ignored_columns: + continue + + data[key] = value + + data.update( + { + 'properties': format_columns.DictColumn(data.pop('metadata')), + } + ) + + return data + + class CreateVolumeSnapshot(command.ShowOne): _description = _("Create new volume snapshot") @@ -94,6 +132,7 @@ def get_parser(self, prog_name): "--property", metavar="", action=parseractions.KeyValueAction, + dest="properties", help=_( "Set a property to this snapshot " "(repeat option to set multiple properties)" @@ -113,11 +152,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume + volume = parsed_args.volume if not parsed_args.volume: volume = parsed_args.snapshot_name - volume_id = utils.find_resource(volume_client.volumes, volume).id + volume_id = volume_client.find_volume(volume, ignore_missing=False).id + if parsed_args.remote_source: # Create a new snapshot from an existing remote snapshot source if parsed_args.force: @@ -127,30 +168,26 @@ def take_action(self, parsed_args): "volume snapshot" ) LOG.warning(msg) - snapshot = volume_client.volume_snapshots.manage( + + snapshot = volume_client.manage_snapshot( volume_id=volume_id, ref=parsed_args.remote_source, name=parsed_args.snapshot_name, description=parsed_args.description, - metadata=parsed_args.property, + metadata=parsed_args.properties, ) else: # create a new snapshot from scratch - snapshot = volume_client.volume_snapshots.create( - volume_id, + snapshot = volume_client.create_snapshot( + volume_id=volume_id, force=parsed_args.force, name=parsed_args.snapshot_name, description=parsed_args.description, - metadata=parsed_args.property, + metadata=parsed_args.properties, ) - snapshot._info.update( - { - 'properties': format_columns.DictColumn( - snapshot._info.pop('metadata') - ) - } - ) - return zip(*sorted(snapshot._info.items())) + + data = _format_snapshot(snapshot) + return zip(*sorted(data.items())) class DeleteVolumeSnapshot(command.Command): diff --git a/openstackclient/volume/v3/volume_snapshot.py b/openstackclient/volume/v3/volume_snapshot.py index a25aa7e62..5b09c9a9b 100644 --- a/openstackclient/volume/v3/volume_snapshot.py +++ b/openstackclient/volume/v3/volume_snapshot.py @@ -17,8 +17,10 @@ import copy import functools import logging +import typing as ty from cliff import columns as cliff_columns +from openstack.block_storage.v3 import snapshot as _snapshot from osc_lib.cli import format_columns from osc_lib.cli import parseractions from osc_lib.command import command @@ -59,6 +61,42 @@ def human_readable(self): return volume +def _format_snapshot(snapshot: _snapshot.Snapshot) -> dict[str, ty.Any]: + # Some columns returned by openstacksdk should not be shown because they're + # either irrelevant or duplicates + ignored_columns = { + # computed columns + 'location', + # create-only columns + 'consumes_quota', + 'force', + 'group_snapshot_id', + # ignored columns + 'os-extended-snapshot-attributes:progress', + 'os-extended-snapshot-attributes:project_id', + 'updated_at', + 'user_id', + # unnecessary columns + 'links', + } + + info = snapshot.to_dict(original_names=True) + data = {} + for key, value in info.items(): + if key in ignored_columns: + continue + + data[key] = value + + data.update( + { + 'properties': format_columns.DictColumn(data.pop('metadata')), + } + ) + + return data + + class CreateVolumeSnapshot(command.ShowOne): _description = _("Create new volume snapshot") @@ -92,6 +130,7 @@ def get_parser(self, prog_name): parser.add_argument( "--property", metavar="", + dest='properties', action=parseractions.KeyValueAction, help=_( "Set a property to this snapshot " @@ -112,11 +151,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume + volume = parsed_args.volume if not parsed_args.volume: volume = parsed_args.snapshot_name - volume_id = utils.find_resource(volume_client.volumes, volume).id + volume_id = volume_client.find_volume(volume, ignore_missing=False).id + if parsed_args.remote_source: # Create a new snapshot from an existing remote snapshot source if parsed_args.force: @@ -126,30 +167,26 @@ def take_action(self, parsed_args): "volume snapshot" ) LOG.warning(msg) - snapshot = volume_client.volume_snapshots.manage( + + snapshot = volume_client.manage_snapshot( volume_id=volume_id, ref=parsed_args.remote_source, name=parsed_args.snapshot_name, description=parsed_args.description, - metadata=parsed_args.property, + metadata=parsed_args.properties, ) else: - # create a new snapshot from scratch - snapshot = volume_client.volume_snapshots.create( - volume_id, + # Create a new snapshot from scratch + snapshot = volume_client.create_snapshot( + volume_id=volume_id, force=parsed_args.force, name=parsed_args.snapshot_name, description=parsed_args.description, - metadata=parsed_args.property, + metadata=parsed_args.properties, ) - snapshot._info.update( - { - 'properties': format_columns.DictColumn( - snapshot._info.pop('metadata') - ) - } - ) - return zip(*sorted(snapshot._info.items())) + + data = _format_snapshot(snapshot) + return zip(*sorted(data.items())) class DeleteVolumeSnapshot(command.Command): From 3c6fa42642dfa36be7f7f0a2a1158454cebfd2eb Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 27 Mar 2025 23:08:22 +0000 Subject: [PATCH 155/245] volume: Migrate 'snapshot set', 'snapshot unset' to SDK Change-Id: Id34d460c8c5656bf43f48717b13a002508562e4e Signed-off-by: Stephen Finucane --- .../unit/volume/v2/test_volume_snapshot.py | 152 ++++++++--------- .../unit/volume/v3/test_volume_snapshot.py | 154 +++++++++--------- openstackclient/volume/v2/volume_snapshot.py | 39 ++--- openstackclient/volume/v3/volume_snapshot.py | 39 ++--- 4 files changed, 199 insertions(+), 185 deletions(-) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py index dc605be59..9f74c2115 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py @@ -519,16 +519,18 @@ def test_snapshot_list_negative_limit(self): ) -class TestVolumeSnapshotSet(TestVolumeSnapshot): - snapshot = volume_fakes.create_one_snapshot() - +class TestVolumeSnapshotSet(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.snapshots_mock.get.return_value = self.snapshot - self.snapshots_mock.set_metadata.return_value = None - self.snapshots_mock.update.return_value = None - # Get the command object to mock + self.snapshot = sdk_fakes.generate_fake_resource( + _snapshot.Snapshot, metadata={'foo': 'bar'} + ) + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.volume_sdk_client.delete_snapshot_metadata.return_value = None + self.volume_sdk_client.set_snapshot_metadata.return_value = None + self.volume_sdk_client.update_snapshot.return_value = None + self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None) def test_snapshot_set_no_option(self): @@ -541,11 +543,14 @@ def test_snapshot_set_no_option(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot) - self.assertNotCalled(self.snapshots_mock.reset_state) - self.assertNotCalled(self.snapshots_mock.update) - self.assertNotCalled(self.snapshots_mock.set_metadata) + self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + parsed_args.snapshot, ignore_missing=False + ) + self.volume_sdk_client.reset_snapshot_status.assert_not_called() + self.volume_sdk_client.update_snapshot.assert_not_called() + self.volume_sdk_client.set_snapshot_metadata.assert_not_called() def test_snapshot_set_name_and_property(self): arglist = [ @@ -557,26 +562,22 @@ def test_snapshot_set_name_and_property(self): "foo=foo", self.snapshot.id, ] - new_property = {"x": "y", "foo": "foo"} verifylist = [ ("name", "new_snapshot"), - ("property", new_property), + ("properties", {"x": "y", "foo": "foo"}), ("snapshot", self.snapshot.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - kwargs = { - "name": "new_snapshot", - } - self.snapshots_mock.update.assert_called_with( - self.snapshot.id, **kwargs + self.assertIsNone(result) + self.volume_sdk_client.update_snapshot.assert_called_with( + self.snapshot.id, name="new_snapshot" ) - self.snapshots_mock.set_metadata.assert_called_with( - self.snapshot.id, new_property + self.volume_sdk_client.set_snapshot_metadata.assert_called_with( + self.snapshot.id, x="y", foo="foo" ) - self.assertIsNone(result) def test_snapshot_set_with_no_property(self): arglist = [ @@ -590,14 +591,17 @@ def test_snapshot_set_with_no_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot) - self.assertNotCalled(self.snapshots_mock.reset_state) - self.assertNotCalled(self.snapshots_mock.update) - self.assertNotCalled(self.snapshots_mock.set_metadata) - self.snapshots_mock.delete_metadata.assert_called_with( - self.snapshot.id, ["foo"] - ) + self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + parsed_args.snapshot, ignore_missing=False + ) + self.volume_sdk_client.reset_snapshot_status.assert_not_called() + self.volume_sdk_client.update_snapshot.assert_not_called() + self.volume_sdk_client.set_snapshot_metadata.assert_not_called() + self.volume_sdk_client.delete_snapshot_metadata.assert_called_with( + self.snapshot.id, keys=["foo"] + ) def test_snapshot_set_with_no_property_and_property(self): arglist = [ @@ -608,22 +612,26 @@ def test_snapshot_set_with_no_property_and_property(self): ] verifylist = [ ("no_property", True), - ("property", {"foo_1": "bar_1"}), + ("properties", {"foo_1": "bar_1"}), ("snapshot", self.snapshot.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot) - self.assertNotCalled(self.snapshots_mock.reset_state) - self.assertNotCalled(self.snapshots_mock.update) - self.snapshots_mock.delete_metadata.assert_called_with( - self.snapshot.id, ["foo"] + + self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + parsed_args.snapshot, ignore_missing=False ) - self.snapshots_mock.set_metadata.assert_called_once_with( - self.snapshot.id, {"foo_1": "bar_1"} + self.volume_sdk_client.reset_snapshot_status.assert_not_called() + self.volume_sdk_client.update_snapshot.assert_not_called() + self.volume_sdk_client.delete_snapshot_metadata.assert_called_with( + self.snapshot.id, keys=["foo"] + ) + self.volume_sdk_client.set_snapshot_metadata.assert_called_once_with( + self.snapshot.id, + foo_1="bar_1", ) - self.assertIsNone(result) def test_snapshot_set_state_to_error(self): arglist = ["--state", "error", self.snapshot.id] @@ -632,30 +640,32 @@ def test_snapshot_set_state_to_error(self): result = self.cmd.take_action(parsed_args) - self.snapshots_mock.reset_state.assert_called_with( + self.assertIsNone(result) + self.volume_sdk_client.reset_snapshot_status.assert_called_with( self.snapshot.id, "error" ) - self.assertIsNone(result) def test_volume_set_state_failed(self): - self.snapshots_mock.reset_state.side_effect = exceptions.CommandError() + self.volume_sdk_client.reset_snapshot_status.side_effect = ( + exceptions.CommandError() + ) arglist = ['--state', 'error', self.snapshot.id] verifylist = [('state', 'error'), ('snapshot', self.snapshot.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - 'One or more of the set operations failed', str(e) - ) - self.snapshots_mock.reset_state.assert_called_once_with( + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertEqual('One or more of the set operations failed', str(exc)) + self.volume_sdk_client.reset_snapshot_status.assert_called_once_with( self.snapshot.id, 'error' ) def test_volume_set_name_and_state_failed(self): - self.snapshots_mock.reset_state.side_effect = exceptions.CommandError() + self.volume_sdk_client.reset_snapshot_status.side_effect = ( + exceptions.CommandError() + ) arglist = [ '--state', 'error', @@ -668,22 +678,19 @@ def test_volume_set_name_and_state_failed(self): ("name", "new_snapshot"), ('snapshot', self.snapshot.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - 'One or more of the set operations failed', str(e) - ) - kwargs = { - "name": "new_snapshot", - } - self.snapshots_mock.update.assert_called_once_with( - self.snapshot.id, **kwargs + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + self.assertEqual('One or more of the set operations failed', str(exc)) + self.volume_sdk_client.update_snapshot.assert_called_once_with( + self.snapshot.id, name="new_snapshot" ) - self.snapshots_mock.reset_state.assert_called_once_with( + self.volume_sdk_client.reset_snapshot_status.assert_called_once_with( self.snapshot.id, 'error' ) @@ -732,15 +739,14 @@ def test_snapshot_show(self): self.assertCountEqual(self.data, data) -class TestVolumeSnapshotUnset(TestVolumeSnapshot): - snapshot = volume_fakes.create_one_snapshot() - +class TestVolumeSnapshotUnset(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.snapshots_mock.get.return_value = self.snapshot - self.snapshots_mock.delete_metadata.return_value = None - # Get the command object to mock + self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.volume_sdk_client.delete_snapshot_metadata.return_value = None + self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None) def test_snapshot_unset(self): @@ -750,7 +756,7 @@ def test_snapshot_unset(self): self.snapshot.id, ] verifylist = [ - ("property", ["foo"]), + ("properties", ["foo"]), ("snapshot", self.snapshot.id), ] @@ -758,7 +764,7 @@ def test_snapshot_unset(self): result = self.cmd.take_action(parsed_args) - self.snapshots_mock.delete_metadata.assert_called_with( - self.snapshot.id, ["foo"] - ) self.assertIsNone(result) + self.volume_sdk_client.delete_snapshot_metadata.assert_called_with( + self.snapshot.id, keys=["foo"] + ) diff --git a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py index 26c993220..0f10a1e40 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py @@ -566,15 +566,16 @@ def test_snapshot_list_negative_limit(self): ) -class TestVolumeSnapshotSet(TestVolumeSnapshot): - snapshot = volume_fakes.create_one_snapshot() - +class TestVolumeSnapshotSet(volume_fakes_v3.TestVolume): def setUp(self): super().setUp() - self.snapshots_mock.get.return_value = self.snapshot - self.snapshots_mock.set_metadata.return_value = None - self.snapshots_mock.update.return_value = None + self.snapshot = sdk_fakes.generate_fake_resource( + _snapshot.Snapshot, metadata={'foo': 'bar'} + ) + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.volume_sdk_client.set_snapshot_metadata.return_value = None + self.volume_sdk_client.update_snapshot.return_value = None # Get the command object to mock self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None) @@ -588,11 +589,14 @@ def test_snapshot_set_no_option(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot) - self.assertNotCalled(self.snapshots_mock.reset_state) - self.assertNotCalled(self.snapshots_mock.update) - self.assertNotCalled(self.snapshots_mock.set_metadata) + self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + parsed_args.snapshot, ignore_missing=False + ) + self.volume_sdk_client.reset_snapshot_status.assert_not_called() + self.volume_sdk_client.update_snapshot.assert_not_called() + self.volume_sdk_client.set_snapshot_metadata.assert_not_called() def test_snapshot_set_name_and_property(self): arglist = [ @@ -604,26 +608,22 @@ def test_snapshot_set_name_and_property(self): "foo=foo", self.snapshot.id, ] - new_property = {"x": "y", "foo": "foo"} verifylist = [ ("name", "new_snapshot"), - ("property", new_property), + ("properties", {"x": "y", "foo": "foo"}), ("snapshot", self.snapshot.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - kwargs = { - "name": "new_snapshot", - } - self.snapshots_mock.update.assert_called_with( - self.snapshot.id, **kwargs + self.assertIsNone(result) + self.volume_sdk_client.update_snapshot.assert_called_with( + self.snapshot.id, name="new_snapshot" ) - self.snapshots_mock.set_metadata.assert_called_with( - self.snapshot.id, new_property + self.volume_sdk_client.set_snapshot_metadata.assert_called_with( + self.snapshot.id, x="y", foo="foo" ) - self.assertIsNone(result) def test_snapshot_set_with_no_property(self): arglist = [ @@ -637,14 +637,17 @@ def test_snapshot_set_with_no_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot) - self.assertNotCalled(self.snapshots_mock.reset_state) - self.assertNotCalled(self.snapshots_mock.update) - self.assertNotCalled(self.snapshots_mock.set_metadata) - self.snapshots_mock.delete_metadata.assert_called_with( - self.snapshot.id, ["foo"] - ) + self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + parsed_args.snapshot, ignore_missing=False + ) + self.volume_sdk_client.reset_snapshot_status.assert_not_called() + self.volume_sdk_client.update_snapshot.assert_not_called() + self.volume_sdk_client.set_snapshot_metadata.assert_not_called() + self.volume_sdk_client.delete_snapshot_metadata.assert_called_with( + self.snapshot.id, keys=["foo"] + ) def test_snapshot_set_with_no_property_and_property(self): arglist = [ @@ -655,22 +658,26 @@ def test_snapshot_set_with_no_property_and_property(self): ] verifylist = [ ("no_property", True), - ("property", {"foo_1": "bar_1"}), + ("properties", {"foo_1": "bar_1"}), ("snapshot", self.snapshot.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.snapshots_mock.get.assert_called_once_with(parsed_args.snapshot) - self.assertNotCalled(self.snapshots_mock.reset_state) - self.assertNotCalled(self.snapshots_mock.update) - self.snapshots_mock.delete_metadata.assert_called_with( - self.snapshot.id, ["foo"] + + self.assertIsNone(result) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + parsed_args.snapshot, ignore_missing=False ) - self.snapshots_mock.set_metadata.assert_called_once_with( - self.snapshot.id, {"foo_1": "bar_1"} + self.volume_sdk_client.reset_snapshot_status.assert_not_called() + self.volume_sdk_client.update_snapshot.assert_not_called() + self.volume_sdk_client.delete_snapshot_metadata.assert_called_with( + self.snapshot.id, keys=["foo"] + ) + self.volume_sdk_client.set_snapshot_metadata.assert_called_once_with( + self.snapshot.id, + foo_1="bar_1", ) - self.assertIsNone(result) def test_snapshot_set_state_to_error(self): arglist = ["--state", "error", self.snapshot.id] @@ -679,30 +686,34 @@ def test_snapshot_set_state_to_error(self): result = self.cmd.take_action(parsed_args) - self.snapshots_mock.reset_state.assert_called_with( + self.assertIsNone(result) + self.volume_sdk_client.reset_snapshot_status.assert_called_with( self.snapshot.id, "error" ) - self.assertIsNone(result) def test_volume_set_state_failed(self): - self.snapshots_mock.reset_state.side_effect = exceptions.CommandError() + self.volume_sdk_client.reset_snapshot_status.side_effect = ( + exceptions.CommandError() + ) arglist = ['--state', 'error', self.snapshot.id] verifylist = [('state', 'error'), ('snapshot', self.snapshot.id)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - 'One or more of the set operations failed', str(e) - ) - self.snapshots_mock.reset_state.assert_called_once_with( + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + self.assertEqual('One or more of the set operations failed', str(exc)) + self.volume_sdk_client.reset_snapshot_status.assert_called_once_with( self.snapshot.id, 'error' ) def test_volume_set_name_and_state_failed(self): - self.snapshots_mock.reset_state.side_effect = exceptions.CommandError() + self.volume_sdk_client.reset_snapshot_status.side_effect = ( + exceptions.CommandError() + ) arglist = [ '--state', 'error', @@ -715,22 +726,19 @@ def test_volume_set_name_and_state_failed(self): ("name", "new_snapshot"), ('snapshot', self.snapshot.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - try: - self.cmd.take_action(parsed_args) - self.fail('CommandError should be raised.') - except exceptions.CommandError as e: - self.assertEqual( - 'One or more of the set operations failed', str(e) - ) - kwargs = { - "name": "new_snapshot", - } - self.snapshots_mock.update.assert_called_once_with( - self.snapshot.id, **kwargs + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, ) - self.snapshots_mock.reset_state.assert_called_once_with( + + self.assertEqual('One or more of the set operations failed', str(exc)) + self.volume_sdk_client.update_snapshot.assert_called_once_with( + self.snapshot.id, name="new_snapshot" + ) + self.volume_sdk_client.reset_snapshot_status.assert_called_once_with( self.snapshot.id, 'error' ) @@ -779,15 +787,14 @@ def test_snapshot_show(self): self.assertCountEqual(self.data, data) -class TestVolumeSnapshotUnset(TestVolumeSnapshot): - snapshot = volume_fakes.create_one_snapshot() - +class TestVolumeSnapshotUnset(volume_fakes_v3.TestVolume): def setUp(self): super().setUp() - self.snapshots_mock.get.return_value = self.snapshot - self.snapshots_mock.delete_metadata.return_value = None - # Get the command object to mock + self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.volume_sdk_client.delete_snapshot_metadata.return_value = None + self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None) def test_snapshot_unset(self): @@ -797,15 +804,14 @@ def test_snapshot_unset(self): self.snapshot.id, ] verifylist = [ - ("property", ["foo"]), + ("properties", ["foo"]), ("snapshot", self.snapshot.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.snapshots_mock.delete_metadata.assert_called_with( - self.snapshot.id, ["foo"] - ) self.assertIsNone(result) + self.volume_sdk_client.delete_snapshot_metadata.assert_called_with( + self.snapshot.id, keys=["foo"] + ) diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index 4efc5f0e6..51ba07ecb 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -411,6 +411,7 @@ def get_parser(self, prog_name): '--property', metavar='', action=parseractions.KeyValueAction, + dest='properties', help=_( 'Property to add/change for this snapshot ' '(repeat option to set multiple properties)' @@ -437,27 +438,26 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot + volume_client = self.app.client_manager.sdk_connection.volume + + snapshot = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False ) result = 0 if parsed_args.no_property: try: - key_list = snapshot.metadata.keys() - volume_client.volume_snapshots.delete_metadata( - snapshot.id, - list(key_list), + volume_client.delete_snapshot_metadata( + snapshot.id, keys=list(snapshot.metadata) ) except Exception as e: LOG.error(_("Failed to clean snapshot properties: %s"), e) result += 1 - if parsed_args.property: + if parsed_args.properties: try: - volume_client.volume_snapshots.set_metadata( - snapshot.id, parsed_args.property + volume_client.set_snapshot_metadata( + snapshot.id, **parsed_args.properties ) except Exception as e: LOG.error(_("Failed to set snapshot property: %s"), e) @@ -465,7 +465,7 @@ def take_action(self, parsed_args): if parsed_args.state: try: - volume_client.volume_snapshots.reset_state( + volume_client.reset_snapshot_status( snapshot.id, parsed_args.state ) except Exception as e: @@ -479,7 +479,7 @@ def take_action(self, parsed_args): kwargs['description'] = parsed_args.description if kwargs: try: - volume_client.volume_snapshots.update(snapshot.id, **kwargs) + volume_client.update_snapshot(snapshot.id, **kwargs) except Exception as e: LOG.error( _("Failed to update snapshot name or description: %s"), @@ -535,6 +535,7 @@ def get_parser(self, prog_name): metavar='', action='append', default=[], + dest='properties', help=_( 'Property to remove from snapshot ' '(repeat option to remove multiple properties)' @@ -543,13 +544,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot + volume_client = self.app.client_manager.sdk_connection.volume + + snapshot = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False ) - if parsed_args.property: - volume_client.volume_snapshots.delete_metadata( - snapshot.id, - parsed_args.property, + if parsed_args.properties: + volume_client.delete_snapshot_metadata( + snapshot.id, keys=parsed_args.properties ) diff --git a/openstackclient/volume/v3/volume_snapshot.py b/openstackclient/volume/v3/volume_snapshot.py index 5b09c9a9b..213839598 100644 --- a/openstackclient/volume/v3/volume_snapshot.py +++ b/openstackclient/volume/v3/volume_snapshot.py @@ -429,6 +429,7 @@ def get_parser(self, prog_name): '--property', metavar='', action=parseractions.KeyValueAction, + dest='properties', help=_( 'Property to add/change for this snapshot ' '(repeat option to set multiple properties)' @@ -455,27 +456,26 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot + volume_client = self.app.client_manager.sdk_connection.volume + + snapshot = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False ) result = 0 if parsed_args.no_property: try: - key_list = snapshot.metadata.keys() - volume_client.volume_snapshots.delete_metadata( - snapshot.id, - list(key_list), + volume_client.delete_snapshot_metadata( + snapshot.id, keys=list(snapshot.metadata) ) except Exception as e: LOG.error(_("Failed to clean snapshot properties: %s"), e) result += 1 - if parsed_args.property: + if parsed_args.properties: try: - volume_client.volume_snapshots.set_metadata( - snapshot.id, parsed_args.property + volume_client.set_snapshot_metadata( + snapshot.id, **parsed_args.properties ) except Exception as e: LOG.error(_("Failed to set snapshot property: %s"), e) @@ -483,7 +483,7 @@ def take_action(self, parsed_args): if parsed_args.state: try: - volume_client.volume_snapshots.reset_state( + volume_client.reset_snapshot_status( snapshot.id, parsed_args.state ) except Exception as e: @@ -497,7 +497,7 @@ def take_action(self, parsed_args): kwargs['description'] = parsed_args.description if kwargs: try: - volume_client.volume_snapshots.update(snapshot.id, **kwargs) + volume_client.update_snapshot(snapshot.id, **kwargs) except Exception as e: LOG.error( _("Failed to update snapshot name or description: %s"), @@ -551,6 +551,7 @@ def get_parser(self, prog_name): parser.add_argument( '--property', metavar='', + dest='properties', action='append', default=[], help=_( @@ -561,13 +562,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot + volume_client = self.app.client_manager.sdk_connection.volume + + snapshot = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False ) - if parsed_args.property: - volume_client.volume_snapshots.delete_metadata( - snapshot.id, - parsed_args.property, + if parsed_args.properties: + volume_client.delete_snapshot_metadata( + snapshot.id, keys=parsed_args.properties ) From 267a29d594214b9891acd56c59186c431f048543 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 28 Mar 2025 12:27:18 +0000 Subject: [PATCH 156/245] volume: Migrate 'snapshot show', 'snapshot list' to SDK Change-Id: I40de24012363f496e46c3dddc31a3e2563ccf443 Signed-off-by: Stephen Finucane --- .../unit/volume/v2/test_volume_snapshot.py | 218 +++++++-------- .../unit/volume/v3/test_volume_snapshot.py | 252 ++++++++---------- openstackclient/volume/v2/volume_snapshot.py | 85 +++--- openstackclient/volume/v3/volume_snapshot.py | 85 +++--- 4 files changed, 299 insertions(+), 341 deletions(-) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py index 9f74c2115..0df379bb2 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py @@ -26,18 +26,6 @@ from openstackclient.volume.v2 import volume_snapshot -class TestVolumeSnapshot(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - self.project_mock = self.identity_client.projects - self.project_mock.reset_mock() - - class TestVolumeSnapshotCreate(volume_fakes.TestVolume): columns = ( 'created_at', @@ -291,52 +279,57 @@ def test_delete_multiple_snapshots_with_exception(self): ) -class TestVolumeSnapshotList(TestVolumeSnapshot): - volume = volume_fakes.create_one_volume() - project = project_fakes.FakeProject.create_one_project() - snapshots = volume_fakes.create_snapshots( - attrs={'volume_id': volume.name}, count=3 - ) +class TestVolumeSnapshotList(volume_fakes.TestVolume): + def setUp(self): + super().setUp() - columns = ["ID", "Name", "Description", "Status", "Size"] - columns_long = columns + ["Created At", "Volume", "Properties"] - - data = [] - for s in snapshots: - data.append( - ( - s.id, - s.name, - s.description, - s.status, - s.size, + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.snapshots = list( + sdk_fakes.generate_fake_resources( + _snapshot.Snapshot, attrs={'volume_id': self.volume.name} ) ) - data_long = [] - for s in snapshots: - data_long.append( - ( - s.id, - s.name, - s.description, - s.status, - s.size, - s.created_at, - volume_snapshot.VolumeIdColumn( - s.volume_id, volume_cache={volume.id: volume} - ), - format_columns.DictColumn(s.metadata), - ) + self.project = project_fakes.FakeProject.create_one_project() + self.volume_sdk_client.volumes.return_value = [self.volume] + self.volume_sdk_client.find_volume.return_value = self.volume + self.volume_sdk_client.snapshots.return_value = self.snapshots + self.project_mock = self.identity_client.projects + self.project_mock.get.return_value = self.project + + self.columns = ("ID", "Name", "Description", "Status", "Size") + self.columns_long = self.columns + ( + "Created At", + "Volume", + "Properties", ) - def setUp(self): - super().setUp() + self.data = [] + self.data_long = [] + for s in self.snapshots: + self.data.append( + ( + s.id, + s.name, + s.description, + s.status, + s.size, + ) + ) + self.data_long.append( + ( + s.id, + s.name, + s.description, + s.status, + s.size, + s.created_at, + volume_snapshot.VolumeIdColumn( + s.volume_id, volume_cache={self.volume.id: self.volume} + ), + format_columns.DictColumn(s.metadata), + ) + ) - self.volumes_mock.list.return_value = [self.volume] - self.volumes_mock.get.return_value = self.volume - self.project_mock.get.return_value = self.project - self.snapshots_mock.list.return_value = self.snapshots - # Get the command to test self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None) def test_snapshot_list_without_options(self): @@ -346,16 +339,14 @@ def test_snapshot_list_without_options(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': False, - 'name': None, - 'status': None, - 'project_id': None, - 'volume_id': None, - }, + all_projects=False, + name=None, + status=None, + project_id=None, + volume_id=None, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -381,16 +372,14 @@ def test_snapshot_list_with_options(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=2, marker=self.snapshots[0].id, - search_opts={ - 'all_tenants': True, - 'project_id': self.project.id, - 'name': None, - 'status': None, - 'volume_id': None, - }, + all_projects=True, + project_id=self.project.id, + name=None, + status=None, + volume_id=None, ) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, list(data)) @@ -404,16 +393,14 @@ def test_snapshot_list_all_projects(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': True, - 'name': None, - 'status': None, - 'project_id': None, - 'volume_id': None, - }, + all_projects=True, + name=None, + status=None, + project_id=None, + volume_id=None, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -432,16 +419,14 @@ def test_snapshot_list_name_option(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': False, - 'name': self.snapshots[0].name, - 'status': None, - 'project_id': None, - 'volume_id': None, - }, + all_projects=False, + name=self.snapshots[0].name, + status=None, + project_id=None, + volume_id=None, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -449,27 +434,25 @@ def test_snapshot_list_name_option(self): def test_snapshot_list_status_option(self): arglist = [ '--status', - self.snapshots[0].status, + 'available', ] verifylist = [ ('all_projects', False), ('long', False), - ('status', self.snapshots[0].status), + ('status', 'available'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': False, - 'name': None, - 'status': self.snapshots[0].status, - 'project_id': None, - 'volume_id': None, - }, + all_projects=False, + name=None, + status='available', + project_id=None, + volume_id=None, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -488,16 +471,14 @@ def test_snapshot_list_volumeid_option(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': False, - 'name': None, - 'status': None, - 'project_id': None, - 'volume_id': self.volume.id, - }, + all_projects=False, + name=None, + status=None, + project_id=None, + volume_id=self.volume.id, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -695,23 +676,22 @@ def test_volume_set_name_and_state_failed(self): ) -class TestVolumeSnapshotShow(TestVolumeSnapshot): - columns = ( - 'created_at', - 'description', - 'id', - 'name', - 'properties', - 'size', - 'status', - 'volume_id', - ) - +class TestVolumeSnapshotShow(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.snapshot = volume_fakes.create_one_snapshot() + self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) + self.columns = ( + 'created_at', + 'description', + 'id', + 'name', + 'properties', + 'size', + 'status', + 'volume_id', + ) self.data = ( self.snapshot.created_at, self.snapshot.description, @@ -723,8 +703,8 @@ def setUp(self): self.snapshot.volume_id, ) - self.snapshots_mock.get.return_value = self.snapshot - # Get the command object to test + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None) def test_snapshot_show(self): @@ -733,7 +713,9 @@ def test_snapshot_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.get.assert_called_with(self.snapshot.id) + self.volume_sdk_client.find_snapshot.assert_called_with( + self.snapshot.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py index 0f10a1e40..85613603d 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_snapshot.py @@ -22,37 +22,11 @@ from openstackclient.tests.unit.identity.v3 import fakes as project_fakes from openstackclient.tests.unit import utils as test_utils -from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes -from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes_v3 +from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes from openstackclient.volume.v3 import volume_snapshot -class TestVolumeSnapshot(volume_fakes_v3.TestVolume): - def setUp(self): - super().setUp() - - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - self.project_mock = self.identity_client.projects - self.project_mock.reset_mock() - - self.volume_sdk_client.unmanage_snapshot.return_value = None - - -class TestVolumeSnapshotCreate(volume_fakes_v3.TestVolume): - columns = ( - 'created_at', - 'description', - 'id', - 'name', - 'properties', - 'size', - 'status', - 'volume_id', - ) - +class TestVolumeSnapshotCreate(volume_fakes.TestVolume): def setUp(self): super().setUp() @@ -64,6 +38,16 @@ def setUp(self): self.volume_sdk_client.create_snapshot.return_value = self.snapshot self.volume_sdk_client.manage_snapshot.return_value = self.snapshot + self.columns = ( + 'created_at', + 'description', + 'id', + 'name', + 'properties', + 'size', + 'status', + 'volume_id', + ) self.data = ( self.snapshot.created_at, self.snapshot.description, @@ -197,7 +181,7 @@ def test_snapshot_create_with_remote_source(self): self.volume_sdk_client.create_snapshot.assert_not_called() -class TestVolumeSnapshotDelete(volume_fakes_v3.TestVolume): +class TestVolumeSnapshotDelete(volume_fakes.TestVolume): def setUp(self): super().setUp() @@ -338,52 +322,57 @@ def test_delete_multiple_snapshots_remote(self): ) -class TestVolumeSnapshotList(TestVolumeSnapshot): - volume = volume_fakes.create_one_volume() - project = project_fakes.FakeProject.create_one_project() - snapshots = volume_fakes.create_snapshots( - attrs={'volume_id': volume.name}, count=3 - ) - - columns = ["ID", "Name", "Description", "Status", "Size"] - columns_long = columns + ["Created At", "Volume", "Properties"] +class TestVolumeSnapshotList(volume_fakes.TestVolume): + def setUp(self): + super().setUp() - data = [] - for s in snapshots: - data.append( - ( - s.id, - s.name, - s.description, - s.status, - s.size, + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.snapshots = list( + sdk_fakes.generate_fake_resources( + _snapshot.Snapshot, attrs={'volume_id': self.volume.name} ) ) - data_long = [] - for s in snapshots: - data_long.append( - ( - s.id, - s.name, - s.description, - s.status, - s.size, - s.created_at, - volume_snapshot.VolumeIdColumn( - s.volume_id, volume_cache={volume.id: volume} - ), - format_columns.DictColumn(s.metadata), - ) + self.project = project_fakes.FakeProject.create_one_project() + self.volume_sdk_client.volumes.return_value = [self.volume] + self.volume_sdk_client.find_volume.return_value = self.volume + self.volume_sdk_client.snapshots.return_value = self.snapshots + self.project_mock = self.identity_client.projects + self.project_mock.get.return_value = self.project + + self.columns = ("ID", "Name", "Description", "Status", "Size") + self.columns_long = self.columns + ( + "Created At", + "Volume", + "Properties", ) - def setUp(self): - super().setUp() + self.data = [] + self.data_long = [] + for s in self.snapshots: + self.data.append( + ( + s.id, + s.name, + s.description, + s.status, + s.size, + ) + ) + self.data_long.append( + ( + s.id, + s.name, + s.description, + s.status, + s.size, + s.created_at, + volume_snapshot.VolumeIdColumn( + s.volume_id, volume_cache={self.volume.id: self.volume} + ), + format_columns.DictColumn(s.metadata), + ) + ) - self.volumes_mock.list.return_value = [self.volume] - self.volumes_mock.get.return_value = self.volume - self.project_mock.get.return_value = self.project - self.snapshots_mock.list.return_value = self.snapshots - # Get the command to test self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None) def test_snapshot_list_without_options(self): @@ -393,16 +382,14 @@ def test_snapshot_list_without_options(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': False, - 'name': None, - 'status': None, - 'project_id': None, - 'volume_id': None, - }, + all_projects=False, + name=None, + status=None, + project_id=None, + volume_id=None, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -428,16 +415,14 @@ def test_snapshot_list_with_options(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=2, marker=self.snapshots[0].id, - search_opts={ - 'all_tenants': True, - 'project_id': self.project.id, - 'name': None, - 'status': None, - 'volume_id': None, - }, + all_projects=True, + project_id=self.project.id, + name=None, + status=None, + volume_id=None, ) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, list(data)) @@ -451,16 +436,14 @@ def test_snapshot_list_all_projects(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': True, - 'name': None, - 'status': None, - 'project_id': None, - 'volume_id': None, - }, + all_projects=True, + name=None, + status=None, + project_id=None, + volume_id=None, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -479,16 +462,14 @@ def test_snapshot_list_name_option(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': False, - 'name': self.snapshots[0].name, - 'status': None, - 'project_id': None, - 'volume_id': None, - }, + all_projects=False, + name=self.snapshots[0].name, + status=None, + project_id=None, + volume_id=None, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -496,27 +477,25 @@ def test_snapshot_list_name_option(self): def test_snapshot_list_status_option(self): arglist = [ '--status', - self.snapshots[0].status, + 'available', ] verifylist = [ ('all_projects', False), ('long', False), - ('status', self.snapshots[0].status), + ('status', 'available'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': False, - 'name': None, - 'status': self.snapshots[0].status, - 'project_id': None, - 'volume_id': None, - }, + all_projects=False, + name=None, + status='available', + project_id=None, + volume_id=None, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -535,16 +514,14 @@ def test_snapshot_list_volumeid_option(self): columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.list.assert_called_once_with( + self.volume_sdk_client.snapshots.assert_called_once_with( limit=None, marker=None, - search_opts={ - 'all_tenants': False, - 'name': None, - 'status': None, - 'project_id': None, - 'volume_id': self.volume.id, - }, + all_projects=False, + name=None, + status=None, + project_id=None, + volume_id=self.volume.id, ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -566,7 +543,7 @@ def test_snapshot_list_negative_limit(self): ) -class TestVolumeSnapshotSet(volume_fakes_v3.TestVolume): +class TestVolumeSnapshotSet(volume_fakes.TestVolume): def setUp(self): super().setUp() @@ -743,23 +720,22 @@ def test_volume_set_name_and_state_failed(self): ) -class TestVolumeSnapshotShow(TestVolumeSnapshot): - columns = ( - 'created_at', - 'description', - 'id', - 'name', - 'properties', - 'size', - 'status', - 'volume_id', - ) - +class TestVolumeSnapshotShow(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.snapshot = volume_fakes.create_one_snapshot() + self.snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) + self.columns = ( + 'created_at', + 'description', + 'id', + 'name', + 'properties', + 'size', + 'status', + 'volume_id', + ) self.data = ( self.snapshot.created_at, self.snapshot.description, @@ -771,8 +747,8 @@ def setUp(self): self.snapshot.volume_id, ) - self.snapshots_mock.get.return_value = self.snapshot - # Get the command object to test + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None) def test_snapshot_show(self): @@ -781,13 +757,15 @@ def test_snapshot_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.snapshots_mock.get.assert_called_with(self.snapshot.id) + self.volume_sdk_client.find_snapshot.assert_called_with( + self.snapshot.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) -class TestVolumeSnapshotUnset(volume_fakes_v3.TestVolume): +class TestVolumeSnapshotUnset(volume_fakes.TestVolume): def setUp(self): super().setUp() diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index 51ba07ecb..fb774611f 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -14,7 +14,6 @@ """Volume v2 snapshot action implementations""" -import copy import functools import logging import typing as ty @@ -297,31 +296,39 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume identity_client = self.app.client_manager.identity + columns: tuple[str, ...] = ( + 'id', + 'name', + 'description', + 'status', + 'size', + ) + column_headers: tuple[str, ...] = ( + 'ID', + 'Name', + 'Description', + 'Status', + 'Size', + ) if parsed_args.long: - columns = [ - 'ID', - 'Name', - 'Description', - 'Status', - 'Size', + columns += ( + 'created_at', + 'volume_id', + 'metadata', + ) + column_headers += ( 'Created At', - 'Volume ID', - 'Metadata', - ] - column_headers = copy.deepcopy(columns) - column_headers[6] = 'Volume' - column_headers[7] = 'Properties' - else: - columns = ['ID', 'Name', 'Description', 'Status', 'Size'] - column_headers = copy.deepcopy(columns) + 'Volume', + 'Properties', + ) # Cache the volume list volume_cache = {} try: - for s in volume_client.volumes.list(): + for s in volume_client.volumes(): volume_cache[s.id] = s except Exception: # noqa: S110 # Just forget it if there's any trouble @@ -332,8 +339,8 @@ def take_action(self, parsed_args): volume_id = None if parsed_args.volume: - volume_id = utils.find_resource( - volume_client.volumes, parsed_args.volume + volume_id = volume_client.find_volume( + parsed_args.volume, ignore_missing=False ).id project_id = None @@ -349,18 +356,14 @@ def take_action(self, parsed_args): True if parsed_args.project else parsed_args.all_projects ) - search_opts = { - 'all_tenants': all_projects, - 'project_id': project_id, - 'name': parsed_args.name, - 'status': parsed_args.status, - 'volume_id': volume_id, - } - - data = volume_client.volume_snapshots.list( - search_opts=search_opts, + data = volume_client.snapshots( marker=parsed_args.marker, limit=parsed_args.limit, + all_projects=all_projects, + project_id=project_id, + name=parsed_args.name, + status=parsed_args.status, + volume_id=volume_id, ) return ( column_headers, @@ -369,8 +372,8 @@ def take_action(self, parsed_args): s, columns, formatters={ - 'Metadata': format_columns.DictColumn, - 'Volume ID': _VolumeIdColumn, + 'metadata': format_columns.DictColumn, + 'volume_id': _VolumeIdColumn, }, ) for s in data @@ -506,18 +509,14 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot - ) - snapshot._info.update( - { - 'properties': format_columns.DictColumn( - snapshot._info.pop('metadata') - ) - } + volume_client = self.app.client_manager.sdk_connection.volume + + snapshot = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False ) - return zip(*sorted(snapshot._info.items())) + + data = _format_snapshot(snapshot) + return zip(*sorted(data.items())) class UnsetVolumeSnapshot(command.Command): diff --git a/openstackclient/volume/v3/volume_snapshot.py b/openstackclient/volume/v3/volume_snapshot.py index 213839598..9cbc198b7 100644 --- a/openstackclient/volume/v3/volume_snapshot.py +++ b/openstackclient/volume/v3/volume_snapshot.py @@ -14,7 +14,6 @@ """Volume v3 snapshot action implementations""" -import copy import functools import logging import typing as ty @@ -315,31 +314,39 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume identity_client = self.app.client_manager.identity + columns: tuple[str, ...] = ( + 'id', + 'name', + 'description', + 'status', + 'size', + ) + column_headers: tuple[str, ...] = ( + 'ID', + 'Name', + 'Description', + 'Status', + 'Size', + ) if parsed_args.long: - columns = [ - 'ID', - 'Name', - 'Description', - 'Status', - 'Size', + columns += ( + 'created_at', + 'volume_id', + 'metadata', + ) + column_headers += ( 'Created At', - 'Volume ID', - 'Metadata', - ] - column_headers = copy.deepcopy(columns) - column_headers[6] = 'Volume' - column_headers[7] = 'Properties' - else: - columns = ['ID', 'Name', 'Description', 'Status', 'Size'] - column_headers = copy.deepcopy(columns) + 'Volume', + 'Properties', + ) # Cache the volume list volume_cache = {} try: - for s in volume_client.volumes.list(): + for s in volume_client.volumes(): volume_cache[s.id] = s except Exception: # noqa: S110 # Just forget it if there's any trouble @@ -350,8 +357,8 @@ def take_action(self, parsed_args): volume_id = None if parsed_args.volume: - volume_id = utils.find_resource( - volume_client.volumes, parsed_args.volume + volume_id = volume_client.find_volume( + parsed_args.volume, ignore_missing=False ).id project_id = None @@ -367,18 +374,14 @@ def take_action(self, parsed_args): True if parsed_args.project else parsed_args.all_projects ) - search_opts = { - 'all_tenants': all_projects, - 'project_id': project_id, - 'name': parsed_args.name, - 'status': parsed_args.status, - 'volume_id': volume_id, - } - - data = volume_client.volume_snapshots.list( - search_opts=search_opts, + data = volume_client.snapshots( marker=parsed_args.marker, limit=parsed_args.limit, + all_projects=all_projects, + project_id=project_id, + name=parsed_args.name, + status=parsed_args.status, + volume_id=volume_id, ) return ( column_headers, @@ -387,8 +390,8 @@ def take_action(self, parsed_args): s, columns, formatters={ - 'Metadata': format_columns.DictColumn, - 'Volume ID': _VolumeIdColumn, + 'metadata': format_columns.DictColumn, + 'volume_id': _VolumeIdColumn, }, ) for s in data @@ -524,18 +527,14 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - snapshot = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot - ) - snapshot._info.update( - { - 'properties': format_columns.DictColumn( - snapshot._info.pop('metadata') - ) - } + volume_client = self.app.client_manager.sdk_connection.volume + + snapshot = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False ) - return zip(*sorted(snapshot._info.items())) + + data = _format_snapshot(snapshot) + return zip(*sorted(data.items())) class UnsetVolumeSnapshot(command.Command): From 03aa172fd0c1e9a02c41166cb8c92e5a241e7dba Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Mar 2025 14:01:19 +0000 Subject: [PATCH 157/245] volume: Split v2, v3 create, delete commands Change-Id: I42616b9586fede3b775bc0fdbba73df90b555d46 Signed-off-by: Stephen Finucane --- openstackclient/volume/v2/volume.py | 8 +- openstackclient/volume/v3/volume.py | 139 ++++++++++++++++++++++++++-- 2 files changed, 135 insertions(+), 12 deletions(-) diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 193e82f01..1b5e4666c 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -107,7 +107,7 @@ def _check_size_arg(args): ) raise exceptions.CommandError(msg) - def _get_parser(self, prog_name): + def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( "name", @@ -216,14 +216,10 @@ def _get_parser(self, prog_name): default=None, help=_("Set volume to read-write access mode (default)"), ) - return parser, source_group - - def get_parser(self, prog_name): - parser, _ = self._get_parser(prog_name) return parser def take_action(self, parsed_args): - CreateVolume._check_size_arg(parsed_args) + self._check_size_arg(parsed_args) # size is validated in the above call to # _check_size_arg where we check that size # should be passed if we are not creating a diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 64aa5f076..653ab64b2 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -31,7 +31,6 @@ from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common -from openstackclient.volume.v2 import volume as volume_v2 LOG = logging.getLogger(__name__) @@ -91,7 +90,7 @@ def human_readable(self): return msg -class CreateVolume(volume_v2.CreateVolume): +class CreateVolume(command.ShowOne): _description = _("Create new volume") @staticmethod @@ -117,8 +116,48 @@ def _check_size_arg(args): raise exceptions.CommandError(msg) def get_parser(self, prog_name): - parser, source_group = self._get_parser(prog_name) - + parser = super().get_parser(prog_name) + parser.add_argument( + "name", + metavar="", + nargs="?", + help=_("Volume name"), + ) + parser.add_argument( + "--size", + metavar="", + type=int, + help=_( + "Volume size in GB (required unless --snapshot or " + "--source specified)" + ), + ) + parser.add_argument( + "--type", + metavar="", + help=_("Set the type of volume"), + ) + source_group = parser.add_mutually_exclusive_group() + source_group.add_argument( + "--image", + metavar="", + help=_("Use as source of volume (name or ID)"), + ) + source_group.add_argument( + "--snapshot", + metavar="", + help=_("Use as source of volume (name or ID)"), + ) + source_group.add_argument( + "--source", + metavar="", + help=_("Volume to clone (name or ID)"), + ) + source_group.add_argument( + "--source-replicated", + metavar="", + help=argparse.SUPPRESS, + ) source_group.add_argument( "--backup", metavar="", @@ -138,6 +177,72 @@ def get_parser(self, prog_name): "--remote-source source-id=test_id')" ), ) + parser.add_argument( + "--description", + metavar="", + help=_("Volume description"), + ) + parser.add_argument( + "--availability-zone", + metavar="", + help=_("Create volume in "), + ) + parser.add_argument( + "--consistency-group", + metavar="consistency-group>", + help=_("Consistency group where the new volume belongs to"), + ) + parser.add_argument( + "--property", + metavar="", + action=parseractions.KeyValueAction, + dest="properties", + help=_( + "Set a property to this volume " + "(repeat option to set multiple properties)" + ), + ) + parser.add_argument( + "--hint", + metavar="", + action=KeyValueHintAction, + help=_( + "Arbitrary scheduler hint key-value pairs to help creating " + "a volume. Repeat the option to set multiple hints. " + "'same_host' and 'different_host' get values appended when " + "repeated, all other keys take the last given value" + ), + ) + bootable_group = parser.add_mutually_exclusive_group() + bootable_group.add_argument( + "--bootable", + action="store_true", + dest="bootable", + default=None, + help=_("Mark volume as bootable"), + ) + bootable_group.add_argument( + "--non-bootable", + action="store_false", + dest="bootable", + default=None, + help=_("Mark volume as non-bootable (default)"), + ) + readonly_group = parser.add_mutually_exclusive_group() + readonly_group.add_argument( + "--read-only", + action="store_true", + dest="read_only", + default=None, + help=_("Set volume to read-only access mode"), + ) + readonly_group.add_argument( + "--read-write", + action="store_false", + dest="read_only", + default=None, + help=_("Set volume to read-write access mode (default)"), + ) parser.add_argument( "--host", metavar="", @@ -160,7 +265,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - CreateVolume._check_size_arg(parsed_args) + self._check_size_arg(parsed_args) # size is validated in the above call to # _check_size_arg where we check that size # should be passed if we are not creating a @@ -351,11 +456,33 @@ def take_action(self, parsed_args): return zip(*sorted(volume._info.items())) -class DeleteVolume(volume_v2.DeleteVolume): +class DeleteVolume(command.Command): _description = _("Delete volume(s)") def get_parser(self, prog_name): parser = super().get_parser(prog_name) + parser.add_argument( + "volumes", + metavar="", + nargs="+", + help=_("Volume(s) to delete (name or ID)"), + ) + group = parser.add_mutually_exclusive_group() + group.add_argument( + "--force", + action="store_true", + help=_( + "Attempt forced removal of volume(s), regardless of state " + "(defaults to False)" + ), + ) + group.add_argument( + "--purge", + action="store_true", + help=_( + "Remove any snapshots along with volume(s) (defaults to False)" + ), + ) parser.add_argument( '--remote', action='store_true', From 125133d056eca9c5dbbf58a3698eb133ae1955a6 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 19 May 2025 13:07:46 +0100 Subject: [PATCH 158/245] volume: Temporarily ignore new volume columns Change-Id: I4296766a1576c64eb3927cb0557aef5346b2d6f6 Signed-off-by: Stephen Finucane --- openstackclient/volume/v3/volume.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 653ab64b2..6786f9967 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -339,7 +339,21 @@ def take_action(self, parsed_args): metadata=parsed_args.properties, bootable=parsed_args.bootable, ) - return zip(*sorted(volume.items())) + data = {} + for key, value in volume.to_dict().items(): + # FIXME(stephenfin): Stop ignoring these once we bump SDK + # https://review.opendev.org/c/openstack/openstacksdk/+/945836/ + if key in ( + 'cluster_name', + 'consumes_quota', + 'encryption_key_id', + 'service_uuid', + 'shared_targets', + 'volume_type_id', + ): + continue + data[key] = value + return zip(*sorted(data.items())) source_volume = None if parsed_args.source: From 082aca89d6168b5dabb315ac54ef5bbf776bd6e3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Mar 2025 11:24:18 +0000 Subject: [PATCH 159/245] volume: Migrate 'volume delete' to SDK Change-Id: Ia7d2bfb14945cb5c185daa820f699a4cfe4a3e7f Signed-off-by: Stephen Finucane --- .../tests/unit/volume/v2/test_volume.py | 119 +++++++------- .../tests/unit/volume/v3/test_volume.py | 154 ++++++++++-------- openstackclient/volume/v2/volume.py | 21 +-- openstackclient/volume/v3/volume.py | 21 +-- 4 files changed, 171 insertions(+), 144 deletions(-) diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index 5df96f27d..ea43e78e2 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -13,6 +13,9 @@ from unittest import mock +from openstack.block_storage.v2 import volume as _volume +from openstack import exceptions as sdk_exceptions +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib import utils @@ -46,12 +49,6 @@ def setUp(self): self.consistencygroups_mock = self.volume_client.consistencygroups self.consistencygroups_mock.reset_mock() - def setup_volumes_mock(self, count): - volumes = volume_fakes.create_volumes(count=count) - - self.volumes_mock.get = volume_fakes.get_volumes(volumes, 0) - return volumes - class TestVolumeCreate(TestVolume): project = identity_fakes.FakeProject.create_one_project() @@ -662,37 +659,37 @@ def test_volume_create_hints(self): self.assertCountEqual(self.datalist, data) -class TestVolumeDelete(TestVolume): +class TestVolumeDelete(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volumes_mock.delete.return_value = None + self.volumes = list(sdk_fakes.generate_fake_resources(_volume.Volume)) + self.volume_sdk_client.find_volume.side_effect = self.volumes + self.volume_sdk_client.delete_volume.return_value = None - # Get the command object to mock self.cmd = volume.DeleteVolume(self.app, None) def test_volume_delete_one_volume(self): - volumes = self.setup_volumes_mock(count=1) - - arglist = [volumes[0].id] + arglist = [self.volumes[0].id] verifylist = [ ("force", False), ("purge", False), - ("volumes", [volumes[0].id]), + ("volumes", [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.volumes_mock.delete.assert_called_once_with( - volumes[0].id, cascade=False + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_called_once_with( + self.volumes[0].id, cascade=False, force=False ) - self.assertIsNone(result) def test_volume_delete_multi_volumes(self): - volumes = self.setup_volumes_mock(count=3) - - arglist = [v.id for v in volumes] + arglist = [v.id for v in self.volumes] verifylist = [ ('force', False), ('purge', False), @@ -701,83 +698,95 @@ def test_volume_delete_multi_volumes(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - - calls = [mock.call(v.id, cascade=False) for v in volumes] - self.volumes_mock.delete.assert_has_calls(calls) self.assertIsNone(result) + self.volume_sdk_client.find_volume.assert_has_calls( + [mock.call(v.id, ignore_missing=False) for v in self.volumes] + ) + self.volume_sdk_client.delete_volume.assert_has_calls( + [mock.call(v.id, cascade=False, force=False) for v in self.volumes] + ) + def test_volume_delete_multi_volumes_with_exception(self): - volumes = self.setup_volumes_mock(count=2) + self.volume_sdk_client.find_volume.side_effect = [ + self.volumes[0], + sdk_exceptions.NotFoundException(), + ] arglist = [ - volumes[0].id, + self.volumes[0].id, 'unexist_volume', ] verifylist = [ ('force', False), ('purge', False), - ('volumes', arglist), + ('volumes', [self.volumes[0].id, 'unexist_volume']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - find_mock_result = [volumes[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 volumes failed to delete.', str(e)) - - find_mock.assert_any_call(self.volumes_mock, volumes[0].id) - find_mock.assert_any_call(self.volumes_mock, 'unexist_volume') - - self.assertEqual(2, find_mock.call_count) - self.volumes_mock.delete.assert_called_once_with( - volumes[0].id, cascade=False - ) + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual('1 of 2 volumes failed to delete.', str(exc)) - def test_volume_delete_with_purge(self): - volumes = self.setup_volumes_mock(count=1) + self.volume_sdk_client.find_volume.assert_has_calls( + [ + mock.call(self.volumes[0].id, ignore_missing=False), + mock.call('unexist_volume', ignore_missing=False), + ] + ) + self.volume_sdk_client.delete_volume.assert_has_calls( + [ + mock.call(self.volumes[0].id, cascade=False, force=False), + ] + ) + def test_volume_delete_with_purge(self): arglist = [ '--purge', - volumes[0].id, + self.volumes[0].id, ] verifylist = [ ('force', False), ('purge', True), - ('volumes', [volumes[0].id]), + ('volumes', [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.volumes_mock.delete.assert_called_once_with( - volumes[0].id, cascade=True + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_called_once_with( + self.volumes[0].id, cascade=True, force=False ) - self.assertIsNone(result) def test_volume_delete_with_force(self): - volumes = self.setup_volumes_mock(count=1) - arglist = [ '--force', - volumes[0].id, + self.volumes[0].id, ] verifylist = [ ('force', True), ('purge', False), - ('volumes', [volumes[0].id]), + ('volumes', [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - - self.volumes_mock.force_delete.assert_called_once_with(volumes[0].id) self.assertIsNone(result) + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_called_once_with( + self.volumes[0].id, cascade=False, force=True + ) + class TestVolumeList(TestVolume): project = identity_fakes.FakeProject.create_one_project() diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index 6bf43d73f..359e3d06a 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -17,6 +17,7 @@ from openstack.block_storage.v3 import block_storage_summary as _summary from openstack.block_storage.v3 import snapshot as _snapshot from openstack.block_storage.v3 import volume as _volume +from openstack import exceptions as sdk_exceptions from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions @@ -954,16 +955,17 @@ def test_volume_create_host_no_remote_source(self): ) -class TestVolumeDeleteLegacy(volume_fakes.TestVolume): +class TestVolumeDelete(volume_fakes.TestVolume): def setUp(self): super().setUp() self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() - self.volumes_mock.delete.return_value = None - self.volumes = volume_fakes.create_volumes(count=1) - self.volumes_mock.get = volume_fakes.get_volumes(self.volumes, 0) + self.volumes = list(sdk_fakes.generate_fake_resources(_volume.Volume)) + self.volume_sdk_client.find_volume.side_effect = self.volumes + self.volume_sdk_client.delete_volume.return_value = None + self.volume_sdk_client.unmanage_volume.return_value = None # Get the command object to mock self.cmd = volume.DeleteVolume(self.app, None) @@ -978,11 +980,14 @@ def test_volume_delete_one_volume(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.volumes_mock.delete.assert_called_once_with( - self.volumes[0].id, cascade=False + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_called_once_with( + self.volumes[0].id, cascade=False, force=False ) - self.assertIsNone(result) def test_volume_delete_multi_volumes(self): arglist = [v.id for v in self.volumes] @@ -994,12 +999,21 @@ def test_volume_delete_multi_volumes(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - - calls = [mock.call(v.id, cascade=False) for v in self.volumes] - self.volumes_mock.delete.assert_has_calls(calls) self.assertIsNone(result) + self.volume_sdk_client.find_volume.assert_has_calls( + [mock.call(v.id, ignore_missing=False) for v in self.volumes] + ) + self.volume_sdk_client.delete_volume.assert_has_calls( + [mock.call(v.id, cascade=False, force=False) for v in self.volumes] + ) + def test_volume_delete_multi_volumes_with_exception(self): + self.volume_sdk_client.find_volume.side_effect = [ + self.volumes[0], + sdk_exceptions.NotFoundException(), + ] + arglist = [ self.volumes[0].id, 'unexist_volume', @@ -1007,27 +1021,28 @@ def test_volume_delete_multi_volumes_with_exception(self): verifylist = [ ('force', False), ('purge', False), - ('volumes', arglist), + ('volumes', [self.volumes[0].id, 'unexist_volume']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - find_mock_result = [self.volumes[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 volumes failed to delete.', str(e)) - - find_mock.assert_any_call(self.volumes_mock, self.volumes[0].id) - find_mock.assert_any_call(self.volumes_mock, 'unexist_volume') - - self.assertEqual(2, find_mock.call_count) - self.volumes_mock.delete.assert_called_once_with( - self.volumes[0].id, cascade=False - ) + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual('1 of 2 volumes failed to delete.', str(exc)) + + self.volume_sdk_client.find_volume.assert_has_calls( + [ + mock.call(self.volumes[0].id, ignore_missing=False), + mock.call('unexist_volume', ignore_missing=False), + ] + ) + self.volume_sdk_client.delete_volume.assert_has_calls( + [ + mock.call(self.volumes[0].id, cascade=False, force=False), + ] + ) def test_volume_delete_with_purge(self): arglist = [ @@ -1042,11 +1057,14 @@ def test_volume_delete_with_purge(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) - self.volumes_mock.delete.assert_called_once_with( - self.volumes[0].id, cascade=True + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_called_once_with( + self.volumes[0].id, cascade=True, force=False ) - self.assertIsNone(result) def test_volume_delete_with_force(self): arglist = [ @@ -1061,49 +1079,38 @@ def test_volume_delete_with_force(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - - self.volumes_mock.force_delete.assert_called_once_with( - self.volumes[0].id - ) self.assertIsNone(result) - -class TestVolumeDelete(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - self.volume_sdk_client.unmanage_volume.return_value = None - - # Get the command object to mock - self.cmd = volume.DeleteVolume(self.app, None) + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_called_once_with( + self.volumes[0].id, cascade=False, force=True + ) def test_volume_delete_remote(self): - vol = sdk_fakes.generate_fake_resource(_volume.Volume, **{'size': 1}) - self.volumes_mock.get.return_value = vol - - arglist = ['--remote', vol.id] + arglist = ['--remote', self.volumes[0].id] verifylist = [ ("remote", True), ("force", False), ("purge", False), - ("volumes", [vol.id]), + ("volumes", [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - - self.volume_sdk_client.unmanage_volume.assert_called_once_with(vol.id) self.assertIsNone(result) - def test_volume_delete_multi_volumes_remote(self): - volumes = sdk_fakes.generate_fake_resources( - _volume.Volume, count=3, attrs={'size': 1} + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volumes[0].id, ignore_missing=False + ) + self.volume_sdk_client.delete_volume.assert_not_called() + self.volume_sdk_client.unmanage_volume.assert_called_once_with( + self.volumes[0].id ) - arglist = ['--remote'] - arglist += [v.id for v in volumes] + def test_volume_delete_multi_volumes_remote(self): + arglist = ['--remote'] + [v.id for v in self.volumes] verifylist = [ ('remote', True), ('force', False), @@ -1113,24 +1120,27 @@ def test_volume_delete_multi_volumes_remote(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - - calls = [mock.call(v.id) for v in volumes] - self.volume_sdk_client.unmanage_volume.assert_has_calls(calls) self.assertIsNone(result) - def test_volume_delete_remote_with_purge(self): - vol = sdk_fakes.generate_fake_resource(_volume.Volume, **{'size': 1}) + self.volume_sdk_client.find_volume.assert_has_calls( + [mock.call(v.id, ignore_missing=False) for v in self.volumes] + ) + self.volume_sdk_client.delete_volume.assert_not_called() + self.volume_sdk_client.unmanage_volume.assert_has_calls( + [mock.call(v.id) for v in self.volumes] + ) + def test_volume_delete_remote_with_purge(self): arglist = [ '--remote', '--purge', - vol.id, + self.volumes[0].id, ] verifylist = [ ('remote', True), ('force', False), ('purge', True), - ('volumes', [vol.id]), + ('volumes', [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1143,19 +1153,21 @@ def test_volume_delete_remote_with_purge(self): str(exc), ) - def test_volume_delete_remote_with_force(self): - vol = sdk_fakes.generate_fake_resource(_volume.Volume, **{'size': 1}) + self.volume_sdk_client.find_volume.assert_not_called() + self.volume_sdk_client.delete_volume.assert_not_called() + self.volume_sdk_client.unmanage_volume.assert_not_called() + def test_volume_delete_remote_with_force(self): arglist = [ '--remote', '--force', - vol.id, + self.volumes[0].id, ] verifylist = [ ('remote', True), ('force', True), ('purge', False), - ('volumes', [vol.id]), + ('volumes', [self.volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1168,6 +1180,10 @@ def test_volume_delete_remote_with_force(self): str(exc), ) + self.volume_sdk_client.find_volume.assert_not_called() + self.volume_sdk_client.delete_volume.assert_not_called() + self.volume_sdk_client.unmanage_volume.assert_not_called() + class TestVolumeList(volume_fakes.TestVolume): project = identity_fakes.FakeProject.create_one_project() diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 1b5e4666c..ad1fbe1ac 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -364,18 +364,19 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume result = 0 - for i in parsed_args.volumes: + for volume in parsed_args.volumes: try: - volume_obj = utils.find_resource(volume_client.volumes, i) - if parsed_args.force: - volume_client.volumes.force_delete(volume_obj.id) - else: - volume_client.volumes.delete( - volume_obj.id, cascade=parsed_args.purge - ) + volume_obj = volume_client.find_volume( + volume, ignore_missing=False + ) + volume_client.delete_volume( + volume_obj.id, + force=parsed_args.force, + cascade=parsed_args.purge, + ) except Exception as e: result += 1 LOG.error( @@ -383,7 +384,7 @@ def take_action(self, parsed_args): "Failed to delete volume with " "name or ID '%(volume)s': %(e)s" ), - {'volume': i, 'e': e}, + {'volume': volume, 'e': e}, ) if result > 0: diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 6786f9967..6852944ec 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -505,8 +505,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - volume_client_sdk = self.app.client_manager.sdk_connection.volume + volume_client = self.app.client_manager.sdk_connection.volume result = 0 if parsed_args.remote and (parsed_args.force or parsed_args.purge): @@ -516,16 +515,18 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - for i in parsed_args.volumes: + for volume in parsed_args.volumes: try: - volume_obj = utils.find_resource(volume_client.volumes, i) + volume_obj = volume_client.find_volume( + volume, ignore_missing=False + ) if parsed_args.remote: - volume_client_sdk.unmanage_volume(volume_obj.id) - elif parsed_args.force: - volume_client.volumes.force_delete(volume_obj.id) + volume_client.unmanage_volume(volume_obj.id) else: - volume_client.volumes.delete( - volume_obj.id, cascade=parsed_args.purge + volume_client.delete_volume( + volume_obj.id, + force=parsed_args.force, + cascade=parsed_args.purge, ) except Exception as e: result += 1 @@ -534,7 +535,7 @@ def take_action(self, parsed_args): "Failed to delete volume with " "name or ID '%(volume)s': %(e)s" ), - {'volume': i, 'e': e}, + {'volume': volume, 'e': e}, ) if result > 0: From 01c1b1e36fd712cc6d3367ca23525e7e0b3e9f4d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 19 May 2025 14:07:01 +0100 Subject: [PATCH 160/245] network: Allow multiple FIP filter opts This is allowed by the neutron API. Allow it in OSC. Change-Id: I7642ecd686d11c5af9e11cc80896243e853e33f3 Signed-off-by: Stephen Finucane --- openstackclient/network/v2/floating_ip.py | 87 ++++++++++++------- .../network/v2/test_floating_ip_network.py | 12 +-- .../fip-filter-opts-a847f8743fef467f.yaml | 5 ++ 3 files changed, 69 insertions(+), 35 deletions(-) create mode 100644 releasenotes/notes/fip-filter-opts-a847f8743fef467f.yaml diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index ea1281177..0b2f7fa98 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -243,18 +243,26 @@ def update_parser_network(self, parser): parser.add_argument( '--network', metavar='', + dest='networks', + action='append', help=self.enhance_help_neutron( _( - "List floating IP(s) according to " - "given network (name or ID)" + "List floating IP(s) according to given network " + "(name or ID) " + "(repeat option to fiter on multiple networks)" ) ), ) parser.add_argument( '--port', metavar='', + dest='ports', + action='append', help=self.enhance_help_neutron( - _("List floating IP(s) according to given port (name or ID)") + _( + "List floating IP(s) according to given port (name or ID) " + "(repeat option to fiter on multiple ports)" + ) ), ) parser.add_argument( @@ -271,14 +279,6 @@ def update_parser_network(self, parser): _("List floating IP(s) according to given floating IP address") ), ) - parser.add_argument( - '--long', - action='store_true', - default=False, - help=self.enhance_help_neutron( - _("List additional fields in output") - ), - ) parser.add_argument( '--status', metavar='', @@ -295,8 +295,8 @@ def update_parser_network(self, parser): metavar='', help=self.enhance_help_neutron( _( - "List floating IP(s) according to given project (name or " - "ID)" + "List floating IP(s) according to given project " + "(name or ID) " ) ), ) @@ -304,13 +304,27 @@ def update_parser_network(self, parser): parser.add_argument( '--router', metavar='', + dest='routers', + action='append', help=self.enhance_help_neutron( - _("List floating IP(s) according to given router (name or ID)") + _( + "List floating IP(s) according to given router " + "(name or ID) " + "(repeat option to fiter on multiple routers)" + ) ), ) _tag.add_tag_filtering_option_to_parser( parser, _('floating IP'), enhance_help=self.enhance_help_neutron ) + parser.add_argument( + '--long', + action='store_true', + default=False, + help=self.enhance_help_neutron( + _("List additional fields in output") + ), + ) return parser @@ -354,22 +368,33 @@ def take_action_network(self, client, parsed_args): query = {} - if parsed_args.network is not None: - network = network_client.find_network( - parsed_args.network, ignore_missing=False - ) - query['floating_network_id'] = network.id - if parsed_args.port is not None: - port = network_client.find_port( - parsed_args.port, ignore_missing=False - ) - query['port_id'] = port.id + if parsed_args.networks is not None: + network_ids = [] + for network in parsed_args.networks: + network_id = network_client.find_network( + network, ignore_missing=False + ).id + network_ids.append(network_id) + query['floating_network_id'] = network_ids + + if parsed_args.ports is not None: + port_ids = [] + for port in parsed_args.ports: + port_id = network_client.find_port( + port, ignore_missing=False + ).id + port_ids.append(port_id) + query['port_id'] = port_ids + if parsed_args.fixed_ip_address is not None: query['fixed_ip_address'] = parsed_args.fixed_ip_address + if parsed_args.floating_ip_address is not None: query['floating_ip_address'] = parsed_args.floating_ip_address + if parsed_args.status: query['status'] = parsed_args.status + if parsed_args.project is not None: project = identity_common.find_project( identity_client, @@ -377,11 +402,15 @@ def take_action_network(self, client, parsed_args): parsed_args.project_domain, ) query['project_id'] = project.id - if parsed_args.router is not None: - router = network_client.find_router( - parsed_args.router, ignore_missing=False - ) - query['router_id'] = router.id + + if parsed_args.routers is not None: + router_ids = [] + for router in parsed_args.routers: + router_id = network_client.find_router( + router, ignore_missing=False + ).id + router_ids.append(router_id) + query['router_id'] = router_ids _tag.get_tag_filtering_args(parsed_args, query) diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py index d7d0b7799..605f4d99a 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py @@ -499,7 +499,7 @@ def test_floating_ip_list_network(self): 'fake_network_id', ] verifylist = [ - ('network', 'fake_network_id'), + ('networks', ['fake_network_id']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -507,7 +507,7 @@ def test_floating_ip_list_network(self): self.network_client.ips.assert_called_once_with( **{ - 'floating_network_id': 'fake_network_id', + 'floating_network_id': ['fake_network_id'], } ) self.assertEqual(self.columns, columns) @@ -519,7 +519,7 @@ def test_floating_ip_list_port(self): 'fake_port_id', ] verifylist = [ - ('port', 'fake_port_id'), + ('ports', ['fake_port_id']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -527,7 +527,7 @@ def test_floating_ip_list_port(self): self.network_client.ips.assert_called_once_with( **{ - 'port_id': 'fake_port_id', + 'port_id': ['fake_port_id'], } ) self.assertEqual(self.columns, columns) @@ -660,7 +660,7 @@ def test_floating_ip_list_router(self): '--long', ] verifylist = [ - ('router', 'fake_router_id'), + ('routers', ['fake_router_id']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -668,7 +668,7 @@ def test_floating_ip_list_router(self): self.network_client.ips.assert_called_once_with( **{ - 'router_id': 'fake_router_id', + 'router_id': ['fake_router_id'], } ) self.assertEqual(self.columns_long, columns) diff --git a/releasenotes/notes/fip-filter-opts-a847f8743fef467f.yaml b/releasenotes/notes/fip-filter-opts-a847f8743fef467f.yaml new file mode 100644 index 000000000..997075c27 --- /dev/null +++ b/releasenotes/notes/fip-filter-opts-a847f8743fef467f.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + The ``--network``, ``--port``, and ``--router`` options of the ``floating + ip list`` command can now be specified multiple times. From f1cd38aabfeddfdf4c2b4eb7cf17a9a62255baa8 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 15 May 2025 15:48:34 +0100 Subject: [PATCH 161/245] identity: Normalise output of application credentials commands cliff is now smarter (I9155763eee15e19eab23b48989dfcc19ea2c5d34), so we can effectively revert change I6b4f1b793dc383856bfdf9a01514381be3cd2bf1. We bump the dependencies to ensure this. Change-Id: I2af19043fd66b5be0826a774baeabeac7110a4aa Signed-off-by: Stephen Finucane --- .../identity/v3/application_credential.py | 171 +++++++++--------- .../identity/v3/test_access_rule.py | 2 +- .../v3/test_application_credential.py | 14 +- .../v3/test_application_credential.py | 88 ++++----- requirements.txt | 2 +- 5 files changed, 142 insertions(+), 135 deletions(-) diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py index f0dd5a58f..4c4cd3f5a 100644 --- a/openstackclient/identity/v3/application_credential.py +++ b/openstackclient/identity/v3/application_credential.py @@ -20,6 +20,7 @@ import logging import uuid +from cliff import columns as cliff_columns from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -27,10 +28,84 @@ from openstackclient.i18n import _ from openstackclient.identity import common - LOG = logging.getLogger(__name__) +class RolesColumn(cliff_columns.FormattableColumn): + """Generate a formatted string of role names.""" + + def human_readable(self): + return utils.format_list(r['name'] for r in self._value) + + +def _format_application_credential( + application_credential, *, include_secret=False +): + column_headers: tuple[str, ...] = ( + 'ID', + 'Name', + 'Description', + 'Project ID', + 'Roles', + 'Unrestricted', + 'Access Rules', + 'Expires At', + ) + columns: tuple[str, ...] = ( + 'id', + 'name', + 'description', + 'project_id', + 'roles', + 'unrestricted', + 'access_rules', + 'expires_at', + ) + if include_secret: + column_headers += ('Secret',) + columns += ('secret',) + + return ( + column_headers, + utils.get_item_properties( + application_credential, columns, formatters={'roles': RolesColumn} + ), + ) + + +def _format_application_credentials(application_credentials): + column_headers = ( + 'ID', + 'Name', + 'Description', + 'Project ID', + 'Roles', + 'Unrestricted', + 'Access Rules', + 'Expires At', + ) + columns = ( + 'id', + 'name', + 'description', + 'project_id', + 'roles', + 'unrestricted', + 'access_rules', + 'expires_at', + ) + + return ( + column_headers, + ( + utils.get_item_properties( + x, columns, formatters={'roles': RolesColumn} + ) + for x in application_credentials + ), + ) + + # TODO(stephenfin): Move this to osc_lib since it's useful elsewhere def is_uuid_like(value) -> bool: """Returns validation of a value as a UUID. @@ -38,9 +113,6 @@ def is_uuid_like(value) -> bool: :param val: Value to verify :type val: string :returns: bool - - .. versionchanged:: 1.1.1 - Support non-lowercase UUIDs. """ try: formatted_value = ( @@ -179,31 +251,8 @@ def take_action(self, parsed_args): access_rules=access_rules, ) - # Format roles into something sensible - if application_credential['roles']: - roles = application_credential['roles'] - msg = ' '.join(r['name'] for r in roles) - application_credential['roles'] = msg - - columns = ( - 'id', - 'name', - 'description', - 'project_id', - 'roles', - 'unrestricted', - 'access_rules', - 'expires_at', - 'secret', - ) - return ( - columns, - ( - utils.get_dict_properties( - application_credential, - columns, - ) - ), + return _format_application_credential( + application_credential, include_secret=True ) @@ -252,6 +301,8 @@ def take_action(self, parsed_args): ) % {'errors': errors, 'total': total} raise exceptions.CommandError(msg) + return None + class ListApplicationCredential(command.Lister): _description = _("List application credentials") @@ -276,39 +327,12 @@ def take_action(self, parsed_args): conn = self.app.client_manager.sdk_connection user_id = conn.config.get_auth().get_user_id(conn.identity) - data = identity_client.application_credentials(user=user_id) - - data_formatted = [] - for ac in data: - # Format roles into something sensible - roles = ac['roles'] - msg = ' '.join(r['name'] for r in roles) - ac['roles'] = msg - - data_formatted.append(ac) - - columns = ( - 'ID', - 'Name', - 'Description', - 'Project ID', - 'Roles', - 'Unrestricted', - 'Access Rules', - 'Expires At', - ) - return ( - columns, - ( - utils.get_item_properties( - s, - columns, - formatters={}, - ) - for s in data_formatted - ), + application_credentials = identity_client.application_credentials( + user=user_id ) + return _format_application_credentials(application_credentials) + class ShowApplicationCredential(command.ShowOne): _description = _("Display application credential details") @@ -327,31 +351,8 @@ def take_action(self, parsed_args): conn = self.app.client_manager.sdk_connection user_id = conn.config.get_auth().get_user_id(conn.identity) - app_cred = identity_client.find_application_credential( + application_credential = identity_client.find_application_credential( user_id, parsed_args.application_credential ) - # Format roles into something sensible - roles = app_cred['roles'] - msg = ' '.join(r['name'] for r in roles) - app_cred['roles'] = msg - - columns = ( - 'id', - 'name', - 'description', - 'project_id', - 'roles', - 'unrestricted', - 'access_rules', - 'expires_at', - ) - return ( - columns, - ( - utils.get_dict_properties( - app_cred, - columns, - ) - ), - ) + return _format_application_credential(application_credential) diff --git a/openstackclient/tests/functional/identity/v3/test_access_rule.py b/openstackclient/tests/functional/identity/v3/test_access_rule.py index c8436c834..4f7225801 100644 --- a/openstackclient/tests/functional/identity/v3/test_access_rule.py +++ b/openstackclient/tests/functional/identity/v3/test_access_rule.py @@ -62,7 +62,7 @@ def setUp(self): items = self.parse_show_as_object(raw_output) self.access_rule_ids = [ - x['id'] for x in ast.literal_eval(items['access_rules']) + x['id'] for x in ast.literal_eval(items['Access Rules']) ] self.addCleanup( self.openstack, diff --git a/openstackclient/tests/functional/identity/v3/test_application_credential.py b/openstackclient/tests/functional/identity/v3/test_application_credential.py index a9c2286d5..20315c4e7 100644 --- a/openstackclient/tests/functional/identity/v3/test_application_credential.py +++ b/openstackclient/tests/functional/identity/v3/test_application_credential.py @@ -21,13 +21,13 @@ class ApplicationCredentialTests(common.IdentityTests): APPLICATION_CREDENTIAL_FIELDS = [ - 'id', - 'name', - 'project_id', - 'description', - 'roles', - 'expires_at', - 'unrestricted', + 'ID', + 'Name', + 'Project ID', + 'Description', + 'Roles', + 'Expires At', + 'Unrestricted', ] APPLICATION_CREDENTIAL_LIST_HEADERS = [ 'ID', diff --git a/openstackclient/tests/unit/identity/v3/test_application_credential.py b/openstackclient/tests/unit/identity/v3/test_application_credential.py index a4ecec5dc..a5fda7c4f 100644 --- a/openstackclient/tests/unit/identity/v3/test_application_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py @@ -31,18 +31,6 @@ class TestApplicationCredentialCreate(identity_fakes.TestIdentityv3): - columns = ( - 'id', - 'name', - 'description', - 'project_id', - 'roles', - 'unrestricted', - 'access_rules', - 'expires_at', - 'secret', - ) - def setUp(self): super().setUp() @@ -52,12 +40,25 @@ def setUp(self): roles=[], ) - self.datalist = ( + self.columns = ( + 'ID', + 'Name', + 'Description', + 'Project ID', + 'Roles', + 'Unrestricted', + 'Access Rules', + 'Expires At', + 'Secret', + ) + self.data = ( self.application_credential.id, self.application_credential.name, self.application_credential.description, self.application_credential.project_id, - self.application_credential.roles, + application_credential.RolesColumn( + self.application_credential.roles + ), self.application_credential.unrestricted, self.application_credential.access_rules, self.application_credential.expires_at, @@ -101,7 +102,7 @@ def test_application_credential_create_basic(self): ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + self.assertEqual(self.data, data) def test_application_credential_create_with_options(self): name = self.application_credential.name @@ -147,7 +148,7 @@ def test_application_credential_create_with_options(self): ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + self.assertEqual(self.data, data) def test_application_credential_create_with_access_rules_string(self): name = self.application_credential.name @@ -191,7 +192,7 @@ def test_application_credential_create_with_access_rules_string(self): ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + self.assertEqual(self.data, data) @mock.patch('openstackclient.identity.v3.application_credential.json.load') @mock.patch('openstackclient.identity.v3.application_credential.open') @@ -231,7 +232,7 @@ def test_application_credential_create_with_access_rules_file( ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, data) + self.assertEqual(self.data, data) class TestApplicationCredentialDelete(identity_fakes.TestIdentityv3): @@ -345,7 +346,9 @@ def setUp(self): self.application_credential.name, self.application_credential.description, self.application_credential.project_id, - '', + application_credential.RolesColumn( + self.application_credential.roles + ), self.application_credential.unrestricted, self.application_credential.access_rules, self.application_credential.expires_at, @@ -408,6 +411,29 @@ def setUp(self): self.application_credential ) + self.columns = ( + 'ID', + 'Name', + 'Description', + 'Project ID', + 'Roles', + 'Unrestricted', + 'Access Rules', + 'Expires At', + ) + self.data = ( + self.application_credential.id, + self.application_credential.name, + self.application_credential.description, + self.application_credential.project_id, + application_credential.RolesColumn( + self.application_credential.roles + ), + self.application_credential.unrestricted, + self.application_credential.access_rules, + self.application_credential.expires_at, + ) + # Get the command object to test self.cmd = application_credential.ShowApplicationCredential( self.app, None @@ -434,25 +460,5 @@ def test_application_credential_show(self): user_id, self.application_credential.id ) - collist = ( - 'id', - 'name', - 'description', - 'project_id', - 'roles', - 'unrestricted', - 'access_rules', - 'expires_at', - ) - self.assertEqual(collist, columns) - datalist = ( - self.application_credential.id, - self.application_credential.name, - self.application_credential.description, - self.application_credential.project_id, - self.application_credential.roles, - self.application_credential.unrestricted, - self.application_credential.access_rules, - self.application_credential.expires_at, - ) - self.assertEqual(datalist, data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/requirements.txt b/requirements.txt index 5e9b33d27..2fe51f5f0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 cryptography>=2.7 # BSD/Apache-2.0 -cliff>=3.5.0 # Apache-2.0 +cliff>=4.8.0 # Apache-2.0 iso8601>=0.1.11 # MIT openstacksdk>=4.5.0 # Apache-2.0 osc-lib>=2.3.0 # Apache-2.0 From 444a1df705c410a2d2e9b44bd0873858698bea48 Mon Sep 17 00:00:00 2001 From: djp Date: Mon, 19 May 2025 22:26:32 +0900 Subject: [PATCH 162/245] tests: Simplify mocking in image tests Replace assignment of `Mock` objects to methods that are already mocked in tests/unit/v1/test_image.py, tests/unit/v2/test_image.py story: 2011459 task: 52210 Change-Id: I0a4644c27066c26cf0ee0f3613c174f141fe94ed --- .../tests/unit/image/v1/test_image.py | 17 ++++++++--------- .../tests/unit/image/v2/test_image.py | 6 +++--- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py index 971e969c7..11d083020 100644 --- a/openstackclient/tests/unit/image/v1/test_image.py +++ b/openstackclient/tests/unit/image/v1/test_image.py @@ -54,9 +54,9 @@ class TestImageCreate(image_fakes.TestImagev1): def setUp(self): super().setUp() - self.image_client.create_image = mock.Mock(return_value=self.new_image) - self.image_client.find_image = mock.Mock(return_value=self.new_image) - self.image_client.update_image = mock.Mock(return_image=self.new_image) + self.image_client.create_image.return_value = self.new_image + self.image_client.find_image.return_value = self.new_image + self.image_client.update_image.return_value = self.new_image # Get the command object to test self.cmd = image.CreateImage(self.app, None) @@ -212,8 +212,8 @@ def setUp(self): super().setUp() # This is the return value for utils.find_resource() - self.image_client.find_image = mock.Mock(return_value=self._image) - self.image_client.delete_image = mock.Mock(return_value=None) + self.image_client.find_image.return_value = self._image + self.image_client.delete_image.return_value = None # Get the command object to test self.cmd = image.DeleteImage(self.app, None) @@ -261,7 +261,6 @@ class TestImageList(image_fakes.TestImagev1): def setUp(self): super().setUp() - self.image_client.images = mock.Mock() self.image_client.images.side_effect = [ [self._image], [], @@ -441,8 +440,8 @@ def setUp(self): super().setUp() # This is the return value for utils.find_resource() - self.image_client.find_image = mock.Mock(return_value=self._image) - self.image_client.update_image = mock.Mock(return_value=self._image) + self.image_client.find_image.return_value = self._image + self.image_client.update_image.return_value = self._image # Get the command object to test self.cmd = image.SetImage(self.app, None) @@ -712,7 +711,7 @@ class TestImageShow(image_fakes.TestImagev1): def setUp(self): super().setUp() - self.image_client.find_image = mock.Mock(return_value=self._image) + self.image_client.find_image.return_value = self._image # Get the command object to test self.cmd = image.ShowImage(self.app, None) diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index 9b44c1578..437c8a1e5 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -914,7 +914,7 @@ def test_image_list_limit_option(self): self.assertEqual(ret_limit, len(tuple(data))) def test_image_list_project_option(self): - self.image_client.find_image = mock.Mock(return_value=self._image) + self.image_client.find_image.return_value = self._image arglist = [ '--project', 'nova', @@ -931,7 +931,7 @@ def test_image_list_project_option(self): @mock.patch('osc_lib.utils.find_resource') def test_image_list_marker_option(self, fr_mock): - self.image_client.find_image = mock.Mock(return_value=self._image) + self.image_client.find_image.return_value = self._image arglist = [ '--marker', @@ -1721,7 +1721,7 @@ class TestImageShow(TestImage): def setUp(self): super().setUp() - self.image_client.find_image = mock.Mock(return_value=self._data) + self.image_client.find_image.return_value = self._data # Get the command object to test self.cmd = _image.ShowImage(self.app, None) From a2be1b014e020e9effb1a72e45781e47236b9606 Mon Sep 17 00:00:00 2001 From: 0weng Date: Mon, 28 Apr 2025 16:39:03 -0700 Subject: [PATCH 163/245] Identity: Migrate 'group' commands to SDK Change-Id: I5a477426318d77021c0430efa1d1f9a7b1ee2633 --- openstackclient/identity/common.py | 27 + openstackclient/identity/v3/group.py | 181 +++--- .../tests/unit/identity/v3/test_group.py | 555 +++++++++++------- ...migrate-group-to-sdk-59beef31a7c40bbb.yaml | 4 + 4 files changed, 497 insertions(+), 270 deletions(-) create mode 100644 releasenotes/notes/migrate-group-to-sdk-59beef31a7c40bbb.yaml diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index 412fd7159..67976d654 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -214,6 +214,33 @@ def find_group(identity_client, name_or_id, domain_name_or_id=None): ) +def find_group_id_sdk( + identity_client, + name_or_id, + domain_name_or_id=None, + *, + validate_actor_existence=True, +): + if domain_name_or_id is None: + return _find_sdk_id( + identity_client.find_group, + name_or_id=name_or_id, + validate_actor_existence=validate_actor_existence, + ) + + domain_id = find_domain_id_sdk( + identity_client, + name_or_id=domain_name_or_id, + validate_actor_existence=validate_actor_existence, + ) + return _find_sdk_id( + identity_client.find_group, + name_or_id=name_or_id, + validate_actor_existence=validate_actor_existence, + domain_id=domain_id, + ) + + def find_project(identity_client, name_or_id, domain_name_or_id=None): if domain_name_or_id is None: return _find_identity_resource( diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index d0112064b..92980acc6 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -17,7 +17,7 @@ import logging -from keystoneauth1 import exceptions as ks_exc +from openstack import exceptions as sdk_exc from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -29,6 +29,25 @@ LOG = logging.getLogger(__name__) +def _format_group(group): + columns = ( + 'description', + 'domain_id', + 'id', + 'name', + ) + column_headers = ( + 'description', + 'domain_id', + 'id', + 'name', + ) + return ( + column_headers, + utils.get_item_properties(group, columns), + ) + + class AddUserToGroup(command.Command): _description = _("Add user to group") @@ -53,19 +72,19 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - group_id = common.find_group( + group_id = common.find_group_id_sdk( identity_client, parsed_args.group, parsed_args.group_domain - ).id + ) result = 0 for i in parsed_args.user: try: - user_id = common.find_user( + user_id = common.find_user_id_sdk( identity_client, i, parsed_args.user_domain - ).id - identity_client.users.add_to_group(user_id, group_id) + ) + identity_client.add_user_to_group(user_id, group_id) except Exception as e: result += 1 msg = _("%(user)s not added to group %(group)s: %(e)s") % { @@ -109,32 +128,41 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - user_id = common.find_user( - identity_client, parsed_args.user, parsed_args.user_domain - ).id - group_id = common.find_group( - identity_client, parsed_args.group, parsed_args.group_domain - ).id + user_id = common.find_user_id_sdk( + identity_client, + parsed_args.user, + parsed_args.user_domain, + validate_actor_existence=False, + ) + group_id = common.find_group_id_sdk( + identity_client, + parsed_args.group, + parsed_args.group_domain, + validate_actor_existence=False, + ) + user_in_group = False try: - identity_client.users.check_in_group(user_id, group_id) - except ks_exc.http.HTTPClientError as e: - if e.http_status == 403 or e.http_status == 404: - msg = _("%(user)s not in group %(group)s\n") % { - 'user': parsed_args.user, - 'group': parsed_args.group, - } - self.app.stderr.write(msg) - else: - raise e - else: + user_in_group = identity_client.check_user_in_group( + user_id, group_id + ) + except sdk_exc.ForbiddenException: + # Assume False if forbidden + pass + if user_in_group: msg = _("%(user)s in group %(group)s\n") % { 'user': parsed_args.user, 'group': parsed_args.group, } self.app.stdout.write(msg) + else: + msg = _("%(user)s not in group %(group)s\n") % { + 'user': parsed_args.user, + 'group': parsed_args.group, + } + self.app.stderr.write(msg) class CreateGroup(command.ShowOne): @@ -165,29 +193,33 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - domain = None + kwargs = {} + if parsed_args.name: + kwargs['name'] = parsed_args.name + if parsed_args.description: + kwargs['description'] = parsed_args.description if parsed_args.domain: - domain = common.find_domain(identity_client, parsed_args.domain).id + kwargs['domain_id'] = common.find_domain_id_sdk( + identity_client, parsed_args.domain + ) try: - group = identity_client.groups.create( - name=parsed_args.name, - domain=domain, - description=parsed_args.description, - ) - except ks_exc.Conflict: + group = identity_client.create_group(**kwargs) + except sdk_exc.ConflictException: if parsed_args.or_show: - group = utils.find_resource( - identity_client.groups, parsed_args.name, domain_id=domain - ) + if parsed_args.domain: + group = identity_client.find_group( + parsed_args.name, domain_id=parsed_args.domain + ) + else: + group = identity_client.find_group(parsed_args.name) LOG.info(_('Returning existing group %s'), group.name) else: raise - group._info.pop('links') - return zip(*sorted(group._info.items())) + return _format_group(group) class DeleteGroup(command.Command): @@ -209,15 +241,15 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity errors = 0 for group in parsed_args.groups: try: - group_obj = common.find_group( + group_id = common.find_group_id_sdk( identity_client, group, parsed_args.domain ) - identity_client.groups.delete(group_obj.id) + identity_client.delete_group(group_id) except Exception as e: errors += 1 LOG.error( @@ -262,29 +294,37 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity domain = None if parsed_args.domain: - domain = common.find_domain(identity_client, parsed_args.domain).id + domain = common.find_domain_id_sdk( + identity_client, parsed_args.domain + ) + data = [] if parsed_args.user: - user = common.find_user( + user = common.find_user_id_sdk( identity_client, parsed_args.user, parsed_args.user_domain, - ).id + ) + if domain: + # NOTE(0weng): The API doesn't actually support filtering additionally by domain_id, + # so this doesn't really do anything. + data = identity_client.user_groups(user, domain_id=domain) + else: + data = identity_client.user_groups(user) else: - user = None + if domain: + data = identity_client.groups(domain_id=domain) + else: + data = identity_client.groups() # List groups columns: tuple[str, ...] = ('ID', 'Name') if parsed_args.long: columns += ('Domain ID', 'Description') - data = identity_client.groups.list( - domain=domain, - user=user, - ) return ( columns, @@ -323,19 +363,19 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - group_id = common.find_group( + group_id = common.find_group_id_sdk( identity_client, parsed_args.group, parsed_args.group_domain - ).id + ) result = 0 for i in parsed_args.user: try: - user_id = common.find_user( + user_id = common.find_user_id_sdk( identity_client, i, parsed_args.user_domain - ).id - identity_client.users.remove_from_group(user_id, group_id) + ) + identity_client.remove_user_from_group(user_id, group_id) except Exception as e: result += 1 msg = _("%(user)s not removed from group %(group)s: %(e)s") % { @@ -387,8 +427,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - group = common.find_group( + identity_client = self.app.client_manager.sdk_connection.identity + group = common.find_group_id_sdk( identity_client, parsed_args.group, parsed_args.domain ) kwargs = {} @@ -397,7 +437,7 @@ def take_action(self, parsed_args): if parsed_args.description: kwargs['description'] = parsed_args.description - identity_client.groups.update(group.id, **kwargs) + identity_client.update_group(group, **kwargs) class ShowGroup(command.ShowOne): @@ -418,13 +458,18 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity + identity_client = self.app.client_manager.sdk_connection.identity - group = common.find_group( - identity_client, - parsed_args.group, - domain_name_or_id=parsed_args.domain, - ) + if parsed_args.domain: + domain = common.find_domain_id_sdk( + identity_client, parsed_args.domain + ) + group = identity_client.find_group( + parsed_args.group, domain_id=domain, ignore_missing=False + ) + else: + group = identity_client.find_group( + parsed_args.group, ignore_missing=False + ) - group._info.pop('links') - return zip(*sorted(group._info.items())) + return _format_group(group) diff --git a/openstackclient/tests/unit/identity/v3/test_group.py b/openstackclient/tests/unit/identity/v3/test_group.py index bd983c618..d212e4565 100644 --- a/openstackclient/tests/unit/identity/v3/test_group.py +++ b/openstackclient/tests/unit/identity/v3/test_group.py @@ -14,45 +14,33 @@ from unittest import mock from unittest.mock import call -from keystoneauth1 import exceptions as ks_exc +from openstack import exceptions as sdk_exc +from openstack.identity.v3 import domain as _domain +from openstack.identity.v3 import group as _group +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions -from osc_lib import utils from openstackclient.identity.v3 import group from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestGroup(identity_fakes.TestIdentityv3): +class TestGroupAddUser(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - # Get a shortcut to the DomainManager Mock - self.domains_mock = self.identity_client.domains - self.domains_mock.reset_mock() - - # Get a shortcut to the GroupManager Mock - self.groups_mock = self.identity_client.groups - self.groups_mock.reset_mock() - - # Get a shortcut to the UserManager Mock - self.users_mock = self.identity_client.users - self.users_mock.reset_mock() - - -class TestGroupAddUser(TestGroup): - _group = identity_fakes.FakeGroup.create_one_group() - users = identity_fakes.FakeUser.create_users(count=2) - - def setUp(self): - super().setUp() + self._group = sdk_fakes.generate_fake_resource(_group.Group) + self.users = tuple( + sdk_fakes.generate_fake_resources(_user.User, count=2) + ) - self.groups_mock.get.return_value = self._group - self.users_mock.get = identity_fakes.FakeUser.get_users(self.users) - self.users_mock.add_to_group.return_value = None + self.identity_sdk_client.find_group.return_value = self._group + self.identity_sdk_client.add_user_to_group.return_value = None self.cmd = group.AddUserToGroup(self.app, None) def test_group_add_user(self): + self.identity_sdk_client.find_user.return_value = self.users[0] arglist = [ self._group.name, self.users[0].name, @@ -64,12 +52,16 @@ def test_group_add_user(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.users_mock.add_to_group.assert_called_once_with( + self.identity_sdk_client.add_user_to_group.assert_called_once_with( self.users[0].id, self._group.id ) self.assertIsNone(result) def test_group_add_multi_users(self): + self.identity_sdk_client.find_user.side_effect = [ + self.users[0], + self.users[1], + ] arglist = [ self._group.name, self.users[0].name, @@ -86,13 +78,13 @@ def test_group_add_multi_users(self): call(self.users[0].id, self._group.id), call(self.users[1].id, self._group.id), ] - self.users_mock.add_to_group.assert_has_calls(calls) + self.identity_sdk_client.add_user_to_group.assert_has_calls(calls) self.assertIsNone(result) @mock.patch.object(group.LOG, 'error') def test_group_add_user_with_error(self, mock_error): - self.users_mock.add_to_group.side_effect = [ - exceptions.CommandError(), + self.identity_sdk_client.add_user_to_group.side_effect = [ + sdk_exc.ResourceNotFound, None, ] arglist = [ @@ -111,20 +103,20 @@ def test_group_add_user_with_error(self, mock_error): except exceptions.CommandError as e: msg = f"1 of 2 users not added to group {self._group.name}." self.assertEqual(msg, str(e)) - msg = f"{self.users[0].name} not added to group {self._group.name}: " + msg = f"{self.users[0].name} not added to group {self._group.name}: {str(sdk_exc.ResourceNotFound())}" mock_error.assert_called_once_with(msg) -class TestGroupCheckUser(TestGroup): - group = identity_fakes.FakeGroup.create_one_group() - user = identity_fakes.FakeUser.create_one_user() - +class TestGroupCheckUser(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.groups_mock.get.return_value = self.group - self.users_mock.get.return_value = self.user - self.users_mock.check_in_group.return_value = None + self.group = sdk_fakes.generate_fake_resource(_group.Group) + self.user = sdk_fakes.generate_fake_resource(_user.User) + + self.identity_sdk_client.find_group.return_value = self.group + self.identity_sdk_client.find_user.return_value = self.user + self.identity_sdk_client.check_user_in_group.return_value = True self.cmd = group.CheckUserInGroup(self.app, None) @@ -140,16 +132,15 @@ def test_group_check_user(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.users_mock.check_in_group.assert_called_once_with( + self.identity_sdk_client.check_user_in_group.assert_called_once_with( self.user.id, self.group.id ) self.assertIsNone(result) def test_group_check_user_server_error(self): - def server_error(*args): - raise ks_exc.http.InternalServerError - - self.users_mock.check_in_group.side_effect = server_error + self.identity_sdk_client.check_user_in_group.side_effect = ( + sdk_exc.SDKException + ) arglist = [ self.group.name, self.user.name, @@ -161,12 +152,12 @@ def server_error(*args): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( - ks_exc.http.InternalServerError, self.cmd.take_action, parsed_args + sdk_exc.SDKException, self.cmd.take_action, parsed_args ) -class TestGroupCreate(TestGroup): - domain = identity_fakes.FakeDomain.create_one_domain() +class TestGroupCreate(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) columns = ( 'description', @@ -177,23 +168,20 @@ class TestGroupCreate(TestGroup): def setUp(self): super().setUp() - self.group = identity_fakes.FakeGroup.create_one_group( - attrs={'domain_id': self.domain.id} + self.group = sdk_fakes.generate_fake_resource( + _group.Group, description=None, domain_id=None ) - self.data = ( - self.group.description, - self.group.domain_id, - self.group.id, - self.group.name, + self.group_with_options = sdk_fakes.generate_fake_resource( + _group.Group, domain_id=self.domain.id ) - self.groups_mock.create.return_value = self.group - self.groups_mock.get.return_value = self.group - self.domains_mock.get.return_value = self.domain + self.identity_sdk_client.find_group.return_value = self.group + self.identity_sdk_client.find_domain.return_value = self.domain self.cmd = group.CreateGroup(self.app, None) def test_group_create(self): + self.identity_sdk_client.create_group.return_value = self.group arglist = [ self.group.name, ] @@ -203,40 +191,56 @@ def test_group_create(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.groups_mock.create.assert_called_once_with( + self.identity_sdk_client.create_group.assert_called_once_with( name=self.group.name, - domain=None, - description=None, ) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + datalist = ( + self.group.description, + None, + self.group.id, + self.group.name, + ) + self.assertEqual(datalist, data) def test_group_create_with_options(self): + self.identity_sdk_client.create_group.return_value = ( + self.group_with_options + ) arglist = [ '--domain', self.domain.name, '--description', - self.group.description, - self.group.name, + self.group_with_options.description, + self.group_with_options.name, ] verifylist = [ ('domain', self.domain.name), - ('description', self.group.description), - ('name', self.group.name), + ('description', self.group_with_options.description), + ('name', self.group_with_options.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.groups_mock.create.assert_called_once_with( - name=self.group.name, - domain=self.domain.id, - description=self.group.description, + self.identity_sdk_client.create_group.assert_called_once_with( + name=self.group_with_options.name, + domain_id=self.domain.id, + description=self.group_with_options.description, ) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + datalist = ( + self.group_with_options.description, + self.domain.id, + self.group_with_options.id, + self.group_with_options.name, + ) + self.assertEqual(datalist, data) def test_group_create_or_show(self): - self.groups_mock.create.side_effect = ks_exc.Conflict() + self.identity_sdk_client.find_group.return_value = self.group + self.identity_sdk_client.create_group.side_effect = ( + sdk_exc.ConflictException + ) arglist = [ '--or-show', self.group.name, @@ -248,46 +252,97 @@ def test_group_create_or_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.groups_mock.get.assert_called_once_with(self.group.name) + self.identity_sdk_client.find_group.assert_called_once_with( + self.group.name + ) + self.assertEqual(self.columns, columns) + datalist = ( + self.group.description, + None, + self.group.id, + self.group.name, + ) + self.assertEqual(datalist, data) + + def test_group_create_or_show_with_domain(self): + self.identity_sdk_client.find_group.return_value = ( + self.group_with_options + ) + self.identity_sdk_client.create_group.side_effect = ( + sdk_exc.ConflictException + ) + arglist = [ + '--or-show', + self.group_with_options.name, + '--domain', + self.domain.id, + ] + verifylist = [ + ('or_show', True), + ('name', self.group_with_options.name), + ('domain', self.domain.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.find_group.assert_called_once_with( + self.group_with_options.name, domain_id=self.domain.id + ) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + datalist = ( + self.group_with_options.description, + self.domain.id, + self.group_with_options.id, + self.group_with_options.name, + ) + self.assertEqual(datalist, data) -class TestGroupDelete(TestGroup): - domain = identity_fakes.FakeDomain.create_one_domain() - groups = identity_fakes.FakeGroup.create_groups( - attrs={'domain_id': domain.id}, count=2 - ) +class TestGroupDelete(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) def setUp(self): super().setUp() - self.groups_mock.get = identity_fakes.FakeGroup.get_groups(self.groups) - self.groups_mock.delete.return_value = None - self.domains_mock.get.return_value = self.domain + self.group = sdk_fakes.generate_fake_resource( + _group.Group, + domain_id=None, + ) + self.group_with_domain = sdk_fakes.generate_fake_resource( + _group.Group, + name=self.group.name, + domain_id=self.domain.id, + ) + self.identity_sdk_client.delete_group.return_value = None + self.identity_sdk_client.find_domain.return_value = self.domain self.cmd = group.DeleteGroup(self.app, None) def test_group_delete(self): + self.identity_sdk_client.find_group.return_value = self.group arglist = [ - self.groups[0].id, + self.group.id, ] verifylist = [ - ('groups', [self.groups[0].id]), + ('groups', [self.group.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.groups_mock.get.assert_called_once_with(self.groups[0].id) - self.groups_mock.delete.assert_called_once_with(self.groups[0].id) + self.identity_sdk_client.find_group.assert_called_once_with( + name_or_id=self.group.id, ignore_missing=False + ) + self.identity_sdk_client.delete_group.assert_called_once_with( + self.group.id + ) self.assertIsNone(result) def test_group_multi_delete(self): - arglist = [] - verifylist = [] - - for g in self.groups: - arglist.append(g.id) + self.identity_sdk_client.find_group.side_effect = [ + self.group, + self.group_with_domain, + ] + arglist = [self.group.id, self.group_with_domain.id] verifylist = [ ('groups', arglist), ] @@ -295,39 +350,50 @@ def test_group_multi_delete(self): result = self.cmd.take_action(parsed_args) - calls = [] - for g in self.groups: - calls.append(call(g.id)) - self.groups_mock.delete.assert_has_calls(calls) + self.identity_sdk_client.delete_group.assert_has_calls( + [mock.call(self.group.id), mock.call(self.group_with_domain.id)] + ) self.assertIsNone(result) def test_group_delete_with_domain(self): - get_mock_result = [exceptions.CommandError, self.groups[0]] - self.groups_mock.get = mock.Mock(side_effect=get_mock_result) + self.identity_sdk_client.find_domain.side_effect = [ + sdk_exc.ForbiddenException + ] + self.identity_sdk_client.find_group.return_value = ( + self.group_with_domain + ) arglist = [ '--domain', - self.domain.id, - self.groups[0].id, + self.group_with_domain.domain_id, + self.group_with_domain.name, ] verifylist = [ - ('domain', self.groups[0].domain_id), - ('groups', [self.groups[0].id]), + ('domain', self.domain.id), + ('groups', [self.group_with_domain.name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.groups_mock.get.assert_any_call( - self.groups[0].id, domain_id=self.domain.id + self.identity_sdk_client.find_group.assert_called_with( + name_or_id=self.group_with_domain.name, + ignore_missing=False, + domain_id=self.domain.id, + ) + self.identity_sdk_client.delete_group.assert_called_once_with( + self.group_with_domain.id ) - self.groups_mock.delete.assert_called_once_with(self.groups[0].id) self.assertIsNone(result) - @mock.patch.object(utils, 'find_resource') - def test_delete_multi_groups_with_exception(self, find_mock): - find_mock.side_effect = [self.groups[0], exceptions.CommandError] + def test_delete_multi_groups_with_exception(self): + self.identity_sdk_client.find_group.side_effect = [ + self.group, + self.group_with_domain, + exceptions.CommandError, + ] arglist = [ - self.groups[0].id, + self.group.id, + self.group_with_domain.id, 'unexist_group', ] verifylist = [ @@ -339,45 +405,57 @@ def test_delete_multi_groups_with_exception(self, find_mock): self.cmd.take_action(parsed_args) self.fail('CommandError should be raised.') except exceptions.CommandError as e: - self.assertEqual('1 of 2 groups failed to delete.', str(e)) - - find_mock.assert_any_call(self.groups_mock, self.groups[0].id) - find_mock.assert_any_call(self.groups_mock, 'unexist_group') + self.assertEqual('1 of 3 groups failed to delete.', str(e)) + + self.identity_sdk_client.find_group.assert_has_calls( + [ + mock.call(name_or_id=self.group.id, ignore_missing=False), + mock.call( + name_or_id=self.group_with_domain.id, ignore_missing=False + ), + mock.call(name_or_id='unexist_group', ignore_missing=False), + ] + ) - self.assertEqual(2, find_mock.call_count) - self.groups_mock.delete.assert_called_once_with(self.groups[0].id) + self.assertEqual(3, self.identity_sdk_client.find_group.call_count) + self.identity_sdk_client.delete_group.assert_has_calls( + [ + mock.call(self.group.id), + mock.call(self.group_with_domain.id), + ] + ) -class TestGroupList(TestGroup): - domain = identity_fakes.FakeDomain.create_one_domain() - group = identity_fakes.FakeGroup.create_one_group() - user = identity_fakes.FakeUser.create_one_user() +class TestGroupList(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) columns = ( 'ID', 'Name', ) - datalist = ( - ( - group.id, - group.name, - ), - ) def setUp(self): super().setUp() - self.groups_mock.get.return_value = self.group - self.groups_mock.list.return_value = [self.group] - - self.domains_mock.get.return_value = self.domain + self.group = sdk_fakes.generate_fake_resource( + _group.Group, description=None, domain_id=None + ) + self.group_with_domain = sdk_fakes.generate_fake_resource( + _group.Group, domain_id=self.domain.id + ) + self.user = sdk_fakes.generate_fake_resource(_user.User) - self.users_mock.get.return_value = self.user + self.identity_sdk_client.find_user.return_value = self.user + self.identity_sdk_client.find_domain.return_value = self.domain # Get the command object to test self.cmd = group.ListGroup(self.app, None) def test_group_list_no_options(self): + self.identity_sdk_client.groups.return_value = [ + self.group, + self.group_with_domain, + ] arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -387,18 +465,23 @@ def test_group_list_no_options(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'domain': None, - 'user': None, - } - - self.groups_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.groups.assert_called_with() self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, tuple(data)) + datalist = ( + ( + self.group.id, + self.group.name, + ), + ( + self.group_with_domain.id, + self.group_with_domain.name, + ), + ) + self.assertEqual(datalist, tuple(data)) def test_group_list_domain(self): + self.identity_sdk_client.groups.return_value = [self.group_with_domain] arglist = [ '--domain', self.domain.id, @@ -415,16 +498,17 @@ def test_group_list_domain(self): # Set expected values kwargs = { - 'domain': self.domain.id, - 'user': None, + 'domain_id': self.domain.id, } - self.groups_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.groups.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, tuple(data)) + datalist = ((self.group_with_domain.id, self.group_with_domain.name),) + self.assertEqual(datalist, tuple(data)) def test_group_list_user(self): + self.identity_sdk_client.user_groups.return_value = [self.group] arglist = [ '--user', self.user.name, @@ -439,18 +523,53 @@ def test_group_list_user(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) + self.identity_sdk_client.user_groups.assert_called_with(self.user.id) + + self.assertEqual(self.columns, columns) + + datalist = ((self.group.id, self.group.name),) + self.assertEqual(datalist, tuple(data)) + + def test_group_list_user_domain(self): + self.identity_sdk_client.user_groups.return_value = [ + self.group_with_domain + ] + arglist = [ + '--user', + self.user.name, + '--domain', + self.domain.name, + ] + verifylist = [ + ('user', self.user.name), + ('domain', self.domain.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + # Set expected values kwargs = { - 'domain': None, - 'user': self.user.id, + 'domain_id': self.domain.id, } - self.groups_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.user_groups.assert_called_with( + self.user.id, **kwargs + ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist, tuple(data)) + + datalist = ((self.group_with_domain.id, self.group_with_domain.name),) + self.assertEqual(datalist, tuple(data)) def test_group_list_long(self): + self.identity_sdk_client.groups.return_value = [ + self.group, + self.group_with_domain, + ] arglist = [ '--long', ] @@ -464,15 +583,9 @@ def test_group_list_long(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'domain': None, - 'user': None, - } - - self.groups_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.groups.assert_called_with() - columns = self.columns + ( + long_columns = self.columns + ( 'Domain ID', 'Description', ) @@ -483,25 +596,33 @@ def test_group_list_long(self): self.group.domain_id, self.group.description, ), + ( + self.group_with_domain.id, + self.group_with_domain.name, + self.group_with_domain.domain_id, + self.group_with_domain.description, + ), ) - self.assertEqual(columns, columns) + self.assertEqual(long_columns, columns) self.assertEqual(datalist, tuple(data)) -class TestGroupRemoveUser(TestGroup): - _group = identity_fakes.FakeGroup.create_one_group() - users = identity_fakes.FakeUser.create_users(count=2) - +class TestGroupRemoveUser(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.groups_mock.get.return_value = self._group - self.users_mock.get = identity_fakes.FakeUser.get_users(self.users) - self.users_mock.remove_from_group.return_value = None + self._group = sdk_fakes.generate_fake_resource(_group.Group) + self.users = tuple( + sdk_fakes.generate_fake_resources(_user.User, count=2) + ) + + self.identity_sdk_client.find_group.return_value = self._group + self.identity_sdk_client.remove_user_from_group.return_value = None self.cmd = group.RemoveUserFromGroup(self.app, None) def test_group_remove_user(self): + self.identity_sdk_client.find_user.return_value = self.users[0] arglist = [ self._group.id, self.users[0].id, @@ -513,12 +634,16 @@ def test_group_remove_user(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.users_mock.remove_from_group.assert_called_once_with( + self.identity_sdk_client.remove_user_from_group.assert_called_once_with( self.users[0].id, self._group.id ) self.assertIsNone(result) def test_group_remove_multi_users(self): + self.identity_sdk_client.find_user.side_effect = [ + self.users[0], + self.users[1], + ] arglist = [ self._group.name, self.users[0].name, @@ -535,13 +660,13 @@ def test_group_remove_multi_users(self): call(self.users[0].id, self._group.id), call(self.users[1].id, self._group.id), ] - self.users_mock.remove_from_group.assert_has_calls(calls) + self.identity_sdk_client.remove_user_from_group.assert_has_calls(calls) self.assertIsNone(result) @mock.patch.object(group.LOG, 'error') def test_group_remove_user_with_error(self, mock_error): - self.users_mock.remove_from_group.side_effect = [ - exceptions.CommandError(), + self.identity_sdk_client.remove_user_from_group.side_effect = [ + sdk_exc.ResourceNotFound(), None, ] arglist = [ @@ -560,26 +685,29 @@ def test_group_remove_user_with_error(self, mock_error): except exceptions.CommandError as e: msg = f"1 of 2 users not removed from group {self._group.id}." self.assertEqual(msg, str(e)) - msg = f"{self.users[0].id} not removed from group {self._group.id}: " + msg = f"{self.users[0].id} not removed from group {self._group.id}: {str(sdk_exc.ResourceNotFound())}" mock_error.assert_called_once_with(msg) -class TestGroupSet(TestGroup): - domain = identity_fakes.FakeDomain.create_one_domain() - group = identity_fakes.FakeGroup.create_one_group( - attrs={'domain_id': domain.id} - ) +class TestGroupSet(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) def setUp(self): super().setUp() + self.group = sdk_fakes.generate_fake_resource( + _group.Group, domain_id=self.domain.id + ) + self.group_with_domain = sdk_fakes.generate_fake_resource( + _group.Group, name=self.group.name, domain_id=self.domain.id + ) - self.groups_mock.get.return_value = self.group - self.domains_mock.get.return_value = self.domain - self.groups_mock.update.return_value = None + self.identity_sdk_client.find_group.return_value = self.group + self.identity_sdk_client.find_domain.return_value = self.domain self.cmd = group.SetGroup(self.app, None) def test_group_set_nothing(self): + self.identity_sdk_client.update_group.return_value = self.group arglist = [ self.group.id, ] @@ -589,10 +717,13 @@ def test_group_set_nothing(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.groups_mock.update.assert_called_once_with(self.group.id) + self.identity_sdk_client.update_group.assert_called_once_with( + self.group.id + ) self.assertIsNone(result) def test_group_set_name_and_description(self): + self.identity_sdk_client.update_group.return_value = self.group arglist = [ '--name', 'new_name', @@ -612,36 +743,43 @@ def test_group_set_name_and_description(self): 'name': 'new_name', 'description': 'new_description', } - self.groups_mock.update.assert_called_once_with( + self.identity_sdk_client.update_group.assert_called_once_with( self.group.id, **kwargs ) self.assertIsNone(result) def test_group_set_with_domain(self): - get_mock_result = [exceptions.CommandError, self.group] - self.groups_mock.get = mock.Mock(side_effect=get_mock_result) - + self.identity_sdk_client.find_domain.side_effect = [ + sdk_exc.ForbiddenException + ] + self.identity_sdk_client.find_group.return_value = ( + self.group_with_domain + ) arglist = [ '--domain', self.domain.id, - self.group.id, + self.group_with_domain.name, ] verifylist = [ ('domain', self.domain.id), - ('group', self.group.id), + ('group', self.group_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.groups_mock.get.assert_any_call( - self.group.id, domain_id=self.domain.id + self.identity_sdk_client.find_group.assert_called_once_with( + name_or_id=self.group_with_domain.name, + ignore_missing=False, + domain_id=self.domain.id, + ) + self.identity_sdk_client.update_group.assert_called_once_with( + self.group_with_domain.id ) - self.groups_mock.update.assert_called_once_with(self.group.id) self.assertIsNone(result) -class TestGroupShow(TestGroup): - domain = identity_fakes.FakeDomain.create_one_domain() +class TestGroupShow(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) columns = ( 'description', @@ -652,22 +790,19 @@ class TestGroupShow(TestGroup): def setUp(self): super().setUp() - self.group = identity_fakes.FakeGroup.create_one_group( - attrs={'domain_id': self.domain.id} + self.group = sdk_fakes.generate_fake_resource( + _group.Group, description=None, domain_id=None ) - self.data = ( - self.group.description, - self.group.domain_id, - self.group.id, - self.group.name, + self.group_with_domain = sdk_fakes.generate_fake_resource( + _group.Group, name=self.group.name, domain_id=self.domain.id ) - self.groups_mock.get.return_value = self.group - self.domains_mock.get.return_value = self.domain + self.identity_sdk_client.find_domain.return_value = self.domain self.cmd = group.ShowGroup(self.app, None) def test_group_show(self): + self.identity_sdk_client.find_group.return_value = self.group arglist = [ self.group.id, ] @@ -677,28 +812,44 @@ def test_group_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.groups_mock.get.assert_called_once_with(self.group.id) + self.identity_sdk_client.find_group.assert_called_once_with( + self.group.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + datalist = ( + None, + None, + self.group.id, + self.group.name, + ) + self.assertEqual(datalist, data) def test_group_show_with_domain(self): - get_mock_result = [exceptions.CommandError, self.group] - self.groups_mock.get = mock.Mock(side_effect=get_mock_result) - + self.identity_sdk_client.find_group.return_value = ( + self.group_with_domain + ) arglist = [ '--domain', self.domain.id, - self.group.id, + self.group_with_domain.name, ] verifylist = [ ('domain', self.domain.id), - ('group', self.group.id), + ('group', self.group_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.groups_mock.get.assert_any_call( - self.group.id, domain_id=self.domain.id + self.identity_sdk_client.find_group.assert_called_once_with( + self.group_with_domain.name, + domain_id=self.domain.id, + ignore_missing=False, ) self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) + datalist = ( + self.group_with_domain.description, + self.domain.id, + self.group_with_domain.id, + self.group_with_domain.name, + ) + self.assertEqual(datalist, data) diff --git a/releasenotes/notes/migrate-group-to-sdk-59beef31a7c40bbb.yaml b/releasenotes/notes/migrate-group-to-sdk-59beef31a7c40bbb.yaml new file mode 100644 index 000000000..3cbb9b3dd --- /dev/null +++ b/releasenotes/notes/migrate-group-to-sdk-59beef31a7c40bbb.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + Migrate ``group`` commands from keystoneclient to SDK. From 9bcb1c5c0076b9eaec3bc8b922209eadfa66a60e Mon Sep 17 00:00:00 2001 From: Chaemin-Lim Date: Tue, 20 May 2025 23:46:39 +0900 Subject: [PATCH 164/245] Fix missing 'options' field in 'user show' command This patch fixes a bug where the 'options' field was missing from the output of the 'openstack user show' command since v7.0.0. The issue was caused by the 'options' field not being included in the column list in the _format_user function. This field is important as it contains various user settings such as multi-factor authentication configurations and password policy exemptions. This patch: 1. Adds 'options' field to the column list in _format_user function 2. Updates all affected unit tests to include this field 3. Uses getattr() to safely handle cases where the options field may be absent Without this fix, users cannot see important options like multi-factor authentication settings through the CLI, which could lead to security configuration issues being overlooked. Closes-Bug: #2084946 Change-Id: I4319268ad4310e6164eb8e65664d73f9b32cdd78 --- openstackclient/identity/v3/user.py | 2 ++ openstackclient/tests/unit/identity/v3/test_user.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 506a83476..fee7dbe33 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -41,6 +41,7 @@ def _format_user(user): 'name', 'description', 'password_expires_at', + 'options', ) column_headers = ( 'default_project_id', @@ -51,6 +52,7 @@ def _format_user(user): 'name', 'description', 'password_expires_at', + 'options', ) return ( column_headers, diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index e8f54e818..1236b86e2 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -44,6 +44,7 @@ class TestUserCreate(identity_fakes.TestIdentityv3): 'name', 'description', 'password_expires_at', + 'options', ) def setUp(self): @@ -63,6 +64,7 @@ def setUp(self): self.user.name, self.user.description, self.user.password_expires_at, + getattr(self.user, 'options', {}), ) self.identity_sdk_client.find_domain.return_value = self.domain @@ -279,6 +281,7 @@ def test_user_create_project(self): self.user.name, self.user.description, self.user.password_expires_at, + getattr(self.user, 'options', {}), ) self.assertEqual(datalist, data) @@ -326,6 +329,7 @@ def test_user_create_project_domain(self): self.user.name, self.user.description, self.user.password_expires_at, + getattr(self.user, 'options', {}), ) self.assertEqual(datalist, data) @@ -1853,6 +1857,7 @@ def test_user_show(self): 'name', 'description', 'password_expires_at', + 'options', ) self.assertEqual(collist, columns) datalist = ( @@ -1864,6 +1869,7 @@ def test_user_show(self): self.user.name, self.user.description, self.user.password_expires_at, + getattr(self.user, 'options', {}), ) self.assertEqual(datalist, data) From 3909e933011d8bf800c967f4be044e140a055bf4 Mon Sep 17 00:00:00 2001 From: waf Date: Wed, 21 May 2025 16:52:24 +0900 Subject: [PATCH 165/245] evacuate: respect original SHUTOFF state in --wait completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running `openstack server evacuate --wait`, the command would hang indefinitely if the instance was originally in SHUTOFF state, because only “ACTIVE” was treated as a successful completion. We now capture the server’s status before evacuation and dynamically include “SHUTOFF” in the `success_status` list if the instance was already shut off. This ensures that a shutoff instance is accepted as a valid completion without requiring manual intervention. Unit tests have been added and updated to cover both: - pre-evacuation ACTIVE → success_status=['active'] - pre-evacuation SHUTOFF → success_status=['active','shutoff'] Closes-Bug: #2103426 Change-Id: I86ad1cd173a144b16fde1dbac87819fab2d7a50a --- openstackclient/compute/v2/server.py | 6 ++++ .../tests/unit/compute/v2/test_server.py | 28 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 4260e7888..cafd672f3 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -3879,9 +3879,15 @@ def _show_progress(progress): compute_client.evacuate_server(server, **kwargs) if parsed_args.wait: + orig_status = server.status + success = ['ACTIVE'] + if orig_status == 'SHUTOFF': + success.append('SHUTOFF') + if utils.wait_for_status( compute_client.get_server, server.id, + success_status=success, callback=_show_progress, ): self.app.stdout.write(_('Complete\n')) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index eb0b934e3..8ee61d17b 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -7000,6 +7000,7 @@ def setUp(self): 'image': self.image, 'networks': {}, 'adminPass': 'passw0rd', + 'status': 'ACTIVE', } self.server = compute_fakes.create_one_server(attrs=attrs) attrs['id'] = self.server.id @@ -7137,6 +7138,33 @@ def test_evacuate_with_wait_ok(self, mock_wait_for_status): mock_wait_for_status.assert_called_once_with( self.compute_client.get_server, self.server.id, + success_status=['ACTIVE'], + callback=mock.ANY, + ) + + @mock.patch.object(common_utils, 'wait_for_status', return_value=True) + def test_evacuate_with_wait_ok_shutoff(self, mock_wait_for_status): + self.server.status = 'SHUTOFF' + self.compute_client.get_server.return_value = self.server + + args = [ + self.server.id, + '--wait', + ] + verify_args = [ + ('server', self.server.id), + ('wait', True), + ] + evac_args = { + 'host': None, + 'on_shared_storage': False, + 'admin_pass': None, + } + self._test_evacuate(args, verify_args, evac_args) + mock_wait_for_status.assert_called_once_with( + self.compute_client.get_server, + self.server.id, + success_status=['ACTIVE', 'SHUTOFF'], callback=mock.ANY, ) From 1c70e264a40e3f01bc6e135be6f981d28a68566d Mon Sep 17 00:00:00 2001 From: hongp Date: Fri, 23 May 2025 09:47:18 +0900 Subject: [PATCH 166/245] bug fix volume group show command. There is no "show" function in the "group" class of python-cinderclient. Instead, there is a "get" function, so I fixed it. https://opendev.org/openstack/python-cinderclient/src/commit/0125495f92ecd0248a204974a96b7403dc160fbe/cinderclient/v3/groups.py#L134 Closes-Bug: #2111539 Change-Id: If61d8cf115c9ff04d172d7344565e693774e7a21 --- openstackclient/volume/v3/volume_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstackclient/volume/v3/volume_group.py b/openstackclient/volume/v3/volume_group.py index f943b8030..8433e5736 100644 --- a/openstackclient/volume/v3/volume_group.py +++ b/openstackclient/volume/v3/volume_group.py @@ -552,7 +552,7 @@ def take_action(self, parsed_args): parsed_args.group, ) - group = volume_client.groups.show(group.id, **kwargs) + group = volume_client.groups.get(group.id, **kwargs) if parsed_args.show_replication_targets: replication_targets = ( From 2f03c3ea3ca27cb0352904ad81562944b627ac68 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 26 May 2025 16:02:21 +0100 Subject: [PATCH 167/245] identity: Remove unnecessary helper Make better use of argparse and eliminate the need for a helper in the process. Change-Id: Ibdc9b4bfbb4d532ddb05bce9b49bcf0580cce76d Signed-off-by: Stephen Finucane --- openstackclient/identity/common.py | 21 ++--- openstackclient/identity/v3/domain.py | 11 ++- openstackclient/identity/v3/project.py | 45 +++++----- openstackclient/identity/v3/role.py | 9 +- .../tests/unit/identity/v3/test_domain.py | 4 +- .../tests/unit/identity/v3/test_project.py | 83 +++++++------------ .../tests/unit/identity/v3/test_role.py | 10 +-- 7 files changed, 79 insertions(+), 104 deletions(-) diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index 67976d654..c6e6cfca2 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -348,15 +348,6 @@ def _find_sdk_id( return resource.id -def get_immutable_options(parsed_args): - options = {} - if parsed_args.immutable: - options['immutable'] = True - if parsed_args.no_immutable: - options['immutable'] = False - return options - - def add_user_domain_option_to_parser(parser): parser.add_argument( '--user-domain', @@ -419,17 +410,21 @@ def add_inherited_option_to_parser(parser): def add_resource_option_to_parser(parser): - enable_group = parser.add_mutually_exclusive_group() - enable_group.add_argument( + immutable_group = parser.add_mutually_exclusive_group() + immutable_group.add_argument( '--immutable', action='store_true', + dest='immutable', + default=None, help=_( 'Make resource immutable. An immutable project may not ' 'be deleted or modified except to remove the immutable flag' ), ) - enable_group.add_argument( + immutable_group.add_argument( '--no-immutable', - action='store_true', + action='store_false', + dest='immutable', + default=None, help=_('Make resource mutable (default)'), ) diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index 54c24d46c..536243a70 100644 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -94,7 +94,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity - options = common.get_immutable_options(parsed_args) + options = {} + if parsed_args.immutable is not None: + options['immutable'] = parsed_args.immutable try: domain = identity_client.create_domain( @@ -242,13 +244,10 @@ def take_action(self, parsed_args): kwargs['name'] = parsed_args.name if parsed_args.description: kwargs['description'] = parsed_args.description - if parsed_args.is_enabled is not None: kwargs['is_enabled'] = parsed_args.is_enabled - - options = common.get_immutable_options(parsed_args) - if options: - kwargs['options'] = options + if parsed_args.immutable is not None: + kwargs['options'] = {'immutable': parsed_args.immutable} identity_client.update_domain(domain.id, **kwargs) diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 6446f98b2..383215461 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -59,17 +59,22 @@ def get_parser(self, prog_name): enable_group.add_argument( '--enable', action='store_true', + dest='enabled', + default=True, help=_('Enable project'), ) enable_group.add_argument( '--disable', - action='store_true', + action='store_false', + dest='enabled', + default=True, help=_('Disable project'), ) parser.add_argument( '--property', metavar='', action=parseractions.KeyValueAction, + dest='properties', help=_( 'Add a property to ' '(repeat option to set multiple properties)' @@ -98,15 +103,9 @@ def take_action(self, parsed_args): parsed_args.parent, ).id - enabled = True - if parsed_args.disable: - enabled = False - - options = common.get_immutable_options(parsed_args) - kwargs = {} - if parsed_args.property: - kwargs = parsed_args.property.copy() + if parsed_args.properties: + kwargs = parsed_args.properties.copy() if 'is_domain' in kwargs.keys(): if kwargs['is_domain'].lower() == "true": kwargs['is_domain'] = True @@ -117,13 +116,17 @@ def take_action(self, parsed_args): kwargs['tags'] = list(set(parsed_args.tags)) + options = {} + if parsed_args.immutable is not None: + options['immutable'] = parsed_args.immutable + try: project = identity_client.projects.create( name=parsed_args.name, domain=domain, parent=parent, description=parsed_args.description, - enabled=enabled, + enabled=parsed_args.enabled, options=options, **kwargs, ) @@ -356,16 +359,21 @@ def get_parser(self, prog_name): enable_group.add_argument( '--enable', action='store_true', + dest='enabled', + default=None, help=_('Enable project'), ) enable_group.add_argument( '--disable', - action='store_true', + action='store_false', + dest='enabled', + default=None, help=_('Disable project'), ) parser.add_argument( '--property', metavar='', + dest='properties', action=parseractions.KeyValueAction, help=_( 'Set a property on ' @@ -388,15 +396,12 @@ def take_action(self, parsed_args): kwargs['name'] = parsed_args.name if parsed_args.description: kwargs['description'] = parsed_args.description - if parsed_args.enable: - kwargs['enabled'] = True - if parsed_args.disable: - kwargs['enabled'] = False - options = common.get_immutable_options(parsed_args) - if options: - kwargs['options'] = options - if parsed_args.property: - kwargs.update(parsed_args.property) + if parsed_args.enabled is not None: + kwargs['enabled'] = parsed_args.enabled + if parsed_args.immutable is not None: + kwargs['options'] = {'immutable': parsed_args.immutable} + if parsed_args.properties: + kwargs.update(parsed_args.properties) tag.update_tags_in_args(parsed_args, project, kwargs) identity_client.projects.update(project.id, **kwargs) diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index 61e30e5e0..3301c3f79 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -334,9 +334,12 @@ def take_action(self, parsed_args): if parsed_args.name: create_kwargs['name'] = parsed_args.name + if parsed_args.description: create_kwargs['description'] = parsed_args.description - create_kwargs['options'] = common.get_immutable_options(parsed_args) + + if parsed_args.immutable is not None: + create_kwargs['options'] = {"immutable": parsed_args.immutable} try: role = identity_client.create_role(**create_kwargs) @@ -585,7 +588,9 @@ def take_action(self, parsed_args): ) update_kwargs["domain_id"] = domain_id - update_kwargs["options"] = common.get_immutable_options(parsed_args) + if parsed_args.immutable is not None: + update_kwargs["options"] = {"immutable": parsed_args.immutable} + role = _find_sdk_id( identity_client.find_role, name_or_id=parsed_args.role, diff --git a/openstackclient/tests/unit/identity/v3/test_domain.py b/openstackclient/tests/unit/identity/v3/test_domain.py index 079170739..abe8076af 100644 --- a/openstackclient/tests/unit/identity/v3/test_domain.py +++ b/openstackclient/tests/unit/identity/v3/test_domain.py @@ -186,7 +186,7 @@ def test_domain_create_with_no_immutable(self): self.domain.name, ] verifylist = [ - ('no_immutable', True), + ('immutable', False), ('name', self.domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -461,7 +461,7 @@ def test_domain_set_no_immutable_option(self): self.domain.id, ] verifylist = [ - ('no_immutable', True), + ('immutable', False), ('domain', self.domain.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py index 7788cf02d..024b74f65 100644 --- a/openstackclient/tests/unit/identity/v3/test_project.py +++ b/openstackclient/tests/unit/identity/v3/test_project.py @@ -77,8 +77,7 @@ def test_project_create_no_options(self): ] verifylist = [ ('parent', None), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('tags', []), ] @@ -134,8 +133,7 @@ def test_project_create_description(self): ] verifylist = [ ('description', 'new desc'), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', []), @@ -172,8 +170,7 @@ def test_project_create_domain(self): ] verifylist = [ ('domain', self.project.domain_id), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', []), @@ -210,8 +207,7 @@ def test_project_create_domain_no_perms(self): ] verifylist = [ ('domain', self.project.domain_id), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', []), @@ -243,8 +239,7 @@ def test_project_create_enable(self): self.project.name, ] verifylist = [ - ('enable', True), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', []), @@ -279,8 +274,7 @@ def test_project_create_disable(self): self.project.name, ] verifylist = [ - ('enable', False), - ('disable', True), + ('enabled', False), ('name', self.project.name), ('parent', None), ] @@ -317,7 +311,7 @@ def test_project_create_property(self): self.project.name, ] verifylist = [ - ('property', {'fee': 'fi', 'fo': 'fum'}), + ('properties', {'fee': 'fi', 'fo': 'fum'}), ('name', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -354,11 +348,10 @@ def test_project_create_is_domain_false_property(self): ] verifylist = [ ('parent', None), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('tags', []), - ('property', {'is_domain': 'false'}), + ('properties', {'is_domain': 'false'}), ('name', self.project.name), ] @@ -393,11 +386,10 @@ def test_project_create_is_domain_true_property(self): ] verifylist = [ ('parent', None), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('tags', []), - ('property', {'is_domain': 'true'}), + ('properties', {'is_domain': 'true'}), ('name', self.project.name), ] @@ -432,11 +424,10 @@ def test_project_create_is_domain_none_property(self): ] verifylist = [ ('parent', None), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('tags', []), - ('property', {'is_domain': 'none'}), + ('properties', {'is_domain': 'none'}), ('name', self.project.name), ] @@ -481,8 +472,7 @@ def test_project_create_parent(self): verifylist = [ ('domain', self.project.domain_id), ('parent', self.parent.name), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('tags', []), ] @@ -544,8 +534,7 @@ def test_project_create_invalid_parent(self): verifylist = [ ('domain', self.project.domain_id), ('parent', 'invalid'), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -566,8 +555,7 @@ def test_project_create_with_tags(self): ] verifylist = [ ('domain', self.project.domain_id), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', ['foo']), @@ -602,8 +590,7 @@ def test_project_create_with_immutable_option(self): verifylist = [ ('immutable', True), ('description', None), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', []), @@ -638,10 +625,9 @@ def test_project_create_with_no_immutable_option(self): self.project.name, ] verifylist = [ - ('no_immutable', True), + ('immutable', False), ('description', None), - ('enable', False), - ('disable', False), + ('enabled', True), ('name', self.project.name), ('parent', None), ('tags', []), @@ -981,8 +967,7 @@ def test_project_set_no_options(self): ] verifylist = [ ('project', self.project.name), - ('enable', False), - ('disable', False), + ('enabled', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1001,8 +986,7 @@ def test_project_set_name(self): verifylist = [ ('name', 'qwerty'), ('domain', self.project.domain_id), - ('enable', False), - ('disable', False), + ('enabled', None), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1029,8 +1013,7 @@ def test_project_set_description(self): verifylist = [ ('domain', self.project.domain_id), ('description', 'new desc'), - ('enable', False), - ('disable', False), + ('enabled', None), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1053,8 +1036,7 @@ def test_project_set_enable(self): ] verifylist = [ ('domain', self.project.domain_id), - ('enable', True), - ('disable', False), + ('enabled', True), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1077,8 +1059,7 @@ def test_project_set_disable(self): ] verifylist = [ ('domain', self.project.domain_id), - ('enable', False), - ('disable', True), + ('enabled', False), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1104,7 +1085,7 @@ def test_project_set_property(self): ] verifylist = [ ('domain', self.project.domain_id), - ('property', {'fee': 'fi', 'fo': 'fum'}), + ('properties', {'fee': 'fi', 'fo': 'fum'}), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1132,8 +1113,7 @@ def test_project_set_tags(self): verifylist = [ ('name', 'qwerty'), ('domain', self.project.domain_id), - ('enable', False), - ('disable', False), + ('enabled', None), ('project', self.project.name), ('tags', ['foo']), ] @@ -1160,8 +1140,7 @@ def test_project_remove_tags(self): self.project.name, ] verifylist = [ - ('enable', False), - ('disable', False), + ('enabled', None), ('project', self.project.name), ('remove_tag', ['tag1', 'tag2']), ] @@ -1183,8 +1162,7 @@ def test_project_set_with_immutable_option(self): verifylist = [ ('domain', self.project.domain_id), ('immutable', True), - ('enable', False), - ('disable', False), + ('enabled', None), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1207,9 +1185,8 @@ def test_project_set_with_no_immutable_option(self): ] verifylist = [ ('domain', self.project.domain_id), - ('no_immutable', True), - ('enable', False), - ('disable', False), + ('immutable', False), + ('enabled', None), ('project', self.project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/unit/identity/v3/test_role.py b/openstackclient/tests/unit/identity/v3/test_role.py index e4a27ed11..3ed1d447a 100644 --- a/openstackclient/tests/unit/identity/v3/test_role.py +++ b/openstackclient/tests/unit/identity/v3/test_role.py @@ -494,7 +494,6 @@ def test_role_create_no_options(self): # Set expected values kwargs = { 'name': self.role.name, - 'options': {}, } self.identity_sdk_client.create_role.assert_called_with(**kwargs) @@ -533,7 +532,6 @@ def test_role_create_with_domain(self): kwargs = { 'domain_id': self.domain.id, 'name': self.role_with_domain.name, - 'options': {}, } self.identity_sdk_client.create_role.assert_called_with(**kwargs) @@ -572,7 +570,6 @@ def test_role_create_with_description(self): kwargs = { 'name': self.role_with_description.name, 'description': self.role_with_description.description, - 'options': {}, } self.identity_sdk_client.create_role.assert_called_with(**kwargs) @@ -629,7 +626,7 @@ def test_role_create_with_no_immutable_option(self): self.role.name, ] verifylist = [ - ('no_immutable', True), + ('immutable', False), ('name', self.role.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1437,7 +1434,6 @@ def test_role_set_no_options(self): kwargs = { 'name': 'over', 'role': self.role.id, - 'options': {}, } self.identity_sdk_client.update_role.assert_called_with(**kwargs) self.assertIsNone(result) @@ -1472,7 +1468,6 @@ def test_role_set_domain_role(self): 'name': 'over', 'role': self.role_with_domain.id, 'domain_id': self.domain2.id, - 'options': {}, } self.identity_sdk_client.update_role.assert_called_with(**kwargs) self.assertIsNone(result) @@ -1501,7 +1496,6 @@ def test_role_set_description(self): 'name': 'over', 'description': 'role description', 'role': self.role_with_domain.id, - 'options': {}, } self.identity_sdk_client.update_role.assert_called_with(**kwargs) self.assertIsNone(result) @@ -1544,7 +1538,7 @@ def test_role_set_with_no_immutable(self): ] verifylist = [ ('name', 'over'), - ('no_immutable', True), + ('immutable', False), ('role', self.role_with_domain.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) From 5f602475cdbf4eee91a451caffe3430ef4521bab Mon Sep 17 00:00:00 2001 From: elajkat Date: Mon, 26 May 2025 14:06:45 +0200 Subject: [PATCH 168/245] Security-groups: Temporarily ignore is_shared [1] introduces is_shared field to SDK security-groups. Till that is merged and released temporary skip the new field. [1]: https://review.opendev.org/c/openstack/openstacksdk/+/950305 Related-Bug: #1999774 Change-Id: I71cdf96460bbb21ee61105ef9ccc23170b0b5460 --- openstackclient/network/v2/security_group.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index ddf6c786e..ceca341f8 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -90,7 +90,10 @@ def _get_columns(item): column_map = { 'security_group_rules': 'rules', } - hidden_columns = ['location', 'tenant_id'] + # FIXME(lajoskatona): Stop hiding is_shared when + # https://review.opendev.org/c/openstack/openstacksdk/+/950305 + # is released and SDK version is bumped + hidden_columns = ['location', 'tenant_id', 'is_shared'] return utils.get_osc_show_columns_for_sdk_resource( item, column_map, hidden_columns ) From 2e301857af7662520576133bbcde5ea9fa153a56 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 29 May 2025 19:08:18 +0100 Subject: [PATCH 169/245] docs: Add note about scoping on tokens Change-Id: I4df74eaa1aa82fb8666bc1e6728b55a3e81bc76a Signed-off-by: Stephen Finucane --- doc/source/cli/authentication.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/source/cli/authentication.rst b/doc/source/cli/authentication.rst index 8ed318f52..8c09fc364 100644 --- a/doc/source/cli/authentication.rst +++ b/doc/source/cli/authentication.rst @@ -295,6 +295,15 @@ or, using environment variables: $ TOKEN=$(openstack token issue -f value -c id) +.. note:: + + The above examples assume you require a project-scoped token. You can omit + the project-related configuration if your user has a default project ID set. + Conversely, if requesting domain-scoped or system-scoped, you should update + these examples accordingly. If the user does not have a default project + configured and no scoping information is provided, the resulting token will + be unscoped. + ``v3totp`` ~~~~~~~~~~ From 9ad18c4967b39cf208c41a139103e2177fd0137f Mon Sep 17 00:00:00 2001 From: djp Date: Wed, 28 May 2025 00:35:28 +0900 Subject: [PATCH 170/245] Fix openstack image import --method web-download --uri 'invalid value' although python-openstackclient run command(image import) with invalid uri, but the request succeeds. Fixed it to throw an exception when requesting with an invalid URI. unit test added. the test cover --uri 'invalid value' Task: 52251 Story: 2011468 Closes-Bug: 2111777 Change-Id: I62cd8cdf054b6a5e07d664a543b0923ce5f20f83 --- openstackclient/image/v2/image.py | 7 +++++ .../tests/unit/image/v2/test_image.py | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 7dbd1609f..6c7ca740e 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -22,6 +22,7 @@ import os import sys import typing as ty +import urllib.parse from openstack import exceptions as sdk_exceptions from openstack.image import image_signer @@ -1744,6 +1745,12 @@ def take_action(self, parsed_args): "'--method=web-download'" ) raise exceptions.CommandError(msg) + _parsed = urllib.parse.urlparse(parsed_args.uri) + if not all({_parsed.scheme, _parsed.netloc}): + msg = _("'%(uri)s' is not a valid url") + raise exceptions.CommandError( + msg % {'uri': parsed_args.uri}, + ) else: if parsed_args.uri: msg = _( diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index 437c8a1e5..95d384c24 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -2039,6 +2039,36 @@ def test_import_image__web_download_invalid_options(self): self.image_client.import_image.assert_not_called() + def test_import_image__web_download_invalid_url(self): + arglist = [ + self.image.name, + '--method', + 'web-download', + '--uri', + 'invalid:1234', + ] + + verifylist = [ + ('image', self.image.name), + ('import_method', 'web-download'), + ('uri', 'invalid:1234'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + self.assertIn( + "'invalid:1234' is not a valid url", + str(exc), + ) + + self.image_client.import_image.assert_not_called() + def test_import_image__web_download_invalid_image_state(self): self.image.status = 'uploading' # != 'queued' arglist = [ From edb17881d0c3ec631ed294f3b81689cf357672b9 Mon Sep 17 00:00:00 2001 From: psnew14 Date: Sun, 1 Jun 2025 22:50:55 +0900 Subject: [PATCH 171/245] Remove leading empty line from server create with --wait The "openstack server create" with "--wait" args was priting an extra empty line character before the server ID. This commit removes the extra empty line. story: 2010947 task: 48984 Change-Id: Ib5ba1c9f23e7655ddfae0e5b644ed167ecd6485e --- openstackclient/compute/v2/server.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index ac29650f2..e14392a53 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2129,13 +2129,11 @@ def _match_image(image_api, wanted_properties): f.close() if parsed_args.wait: - if utils.wait_for_status( + if not utils.wait_for_status( compute_client.get_server, server.id, callback=_show_progress, ): - self.app.stdout.write('\n') - else: msg = _('Error creating server: %s') % parsed_args.server_name raise exceptions.CommandError(msg) From ea85c7aa4de0b04697b3ac2e2aad2e163f18b279 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Wed, 18 Jun 2025 18:15:01 +0900 Subject: [PATCH 172/245] Replace deprecated datetime.utcfromtimestamp It was deprecated in Python 3.12 in favor of datetime.fromtimestamp[1]. [1] https://docs.python.org/3/library/datetime.html#datetime.datetime.utcfromtimestamp Change-Id: Ia805157eaecac0c61d4c5f88daa430ec6d69a9d4 --- openstackclient/image/v2/cache.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/openstackclient/image/v2/cache.py b/openstackclient/image/v2/cache.py index 33a2f4fad..9fbce84e4 100644 --- a/openstackclient/image/v2/cache.py +++ b/openstackclient/image/v2/cache.py @@ -37,14 +37,18 @@ def _format_image_cache(cached_images): image_obj = copy.deepcopy(image) image_obj['state'] = 'cached' image_obj['last_accessed'] = ( - datetime.datetime.utcfromtimestamp( - image['last_accessed'] - ).isoformat() + datetime.datetime.fromtimestamp( + image['last_accessed'], tz=datetime.timezone.utc + ) + .replace(tzinfo=None) + .isoformat() ) image_obj['last_modified'] = ( - datetime.datetime.utcfromtimestamp( - image['last_modified'] - ).isoformat() + datetime.datetime.fromtimestamp( + image['last_modified'], tz=datetime.timezone.utc + ) + .replace(tzinfo=None) + .isoformat() ) image_list.append(image_obj) elif item == "queued_images": From 1b4fe6fac10cabfc54c990e19530f7fc10b93377 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 27 Jun 2025 08:59:50 +0100 Subject: [PATCH 173/245] Migrate setup configuration to pyproject.toml Change-Id: If7a6252ce751e875881654965762f6f514a15342 Signed-off-by: Stephen Finucane --- pyproject.toml | 720 +++++++++++++++++++++++++++++++++++++++++++ setup.cfg | 819 ------------------------------------------------- 2 files changed, 720 insertions(+), 819 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 50092d406..404bba04d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,723 @@ +[build-system] +requires = ["pbr>=6.1.1"] +build-backend = "pbr.build" + +[project] +name = "python-openstackclient" +description = "OpenStack Command-line Client" +authors = [ + {name = "OpenStack", email = "openstack-discuss@lists.openstack.org"}, +] +readme = {file = "README.rst", content-type = "text/x-rst"} +license = {text = "Apache-2.0"} +dynamic = ["version", "dependencies"] +# dependencies = [ ] +requires-python = ">=3.10" +classifiers = [ + "Environment :: OpenStack", + "Intended Audience :: Information Technology", + "Intended Audience :: System Administrators", + "License :: OSI Approved :: Apache Software License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] + +# [project.optional-dependencies] +# test = [ +# ] + +[project.urls] +Homepage = "https://docs.openstack.org/python-openstackclient/" +Repository = "https://opendev.org/openstack/python-openstackclient/" + +[project.scripts] +openstack = "openstackclient.shell:main" + +[project.entry-points."openstack.cli"] +command_list = "openstackclient.common.module:ListCommand" +module_list = "openstackclient.common.module:ListModule" + +[project.entry-points."openstack.cli.base"] +compute = "openstackclient.compute.client" +identity = "openstackclient.identity.client" +image = "openstackclient.image.client" +network = "openstackclient.network.client" +object_store = "openstackclient.object.client" +volume = "openstackclient.volume.client" + +[project.entry-points."openstack.common"] +availability_zone_list = "openstackclient.common.availability_zone:ListAvailabilityZone" +configuration_show = "openstackclient.common.configuration:ShowConfiguration" +extension_list = "openstackclient.common.extension:ListExtension" +extension_show = "openstackclient.common.extension:ShowExtension" +limits_show = "openstackclient.common.limits:ShowLimits" +project_cleanup = "openstackclient.common.project_cleanup:ProjectCleanup" +quota_list = "openstackclient.common.quota:ListQuota" +quota_set = "openstackclient.common.quota:SetQuota" +quota_show = "openstackclient.common.quota:ShowQuota" +quota_delete = "openstackclient.common.quota:DeleteQuota" +versions_show = "openstackclient.common.versions:ShowVersions" + +[project.entry-points."openstack.compute.v2"] +aggregate_add_host = "openstackclient.compute.v2.aggregate:AddAggregateHost" +aggregate_create = "openstackclient.compute.v2.aggregate:CreateAggregate" +aggregate_delete = "openstackclient.compute.v2.aggregate:DeleteAggregate" +aggregate_list = "openstackclient.compute.v2.aggregate:ListAggregate" +aggregate_remove_host = "openstackclient.compute.v2.aggregate:RemoveAggregateHost" +aggregate_set = "openstackclient.compute.v2.aggregate:SetAggregate" +aggregate_show = "openstackclient.compute.v2.aggregate:ShowAggregate" +aggregate_unset = "openstackclient.compute.v2.aggregate:UnsetAggregate" +aggregate_cache_image = "openstackclient.compute.v2.aggregate:CacheImageForAggregate" +compute_agent_create = "openstackclient.compute.v2.agent:CreateAgent" +compute_agent_delete = "openstackclient.compute.v2.agent:DeleteAgent" +compute_agent_list = "openstackclient.compute.v2.agent:ListAgent" +compute_agent_set = "openstackclient.compute.v2.agent:SetAgent" +compute_service_delete = "openstackclient.compute.v2.service:DeleteService" +compute_service_list = "openstackclient.compute.v2.service:ListService" +compute_service_set = "openstackclient.compute.v2.service:SetService" +console_log_show = "openstackclient.compute.v2.console:ShowConsoleLog" +console_url_show = "openstackclient.compute.v2.console:ShowConsoleURL" +console_connection_show = "openstackclient.compute.v2.console_connection:ShowConsoleConnectionInformation" +flavor_create = "openstackclient.compute.v2.flavor:CreateFlavor" +flavor_delete = "openstackclient.compute.v2.flavor:DeleteFlavor" +flavor_list = "openstackclient.compute.v2.flavor:ListFlavor" +flavor_show = "openstackclient.compute.v2.flavor:ShowFlavor" +flavor_set = "openstackclient.compute.v2.flavor:SetFlavor" +flavor_unset = "openstackclient.compute.v2.flavor:UnsetFlavor" +host_list = "openstackclient.compute.v2.host:ListHost" +host_set = "openstackclient.compute.v2.host:SetHost" +host_show = "openstackclient.compute.v2.host:ShowHost" +hypervisor_list = "openstackclient.compute.v2.hypervisor:ListHypervisor" +hypervisor_show = "openstackclient.compute.v2.hypervisor:ShowHypervisor" +hypervisor_stats_show = "openstackclient.compute.v2.hypervisor_stats:ShowHypervisorStats" +keypair_create = "openstackclient.compute.v2.keypair:CreateKeypair" +keypair_delete = "openstackclient.compute.v2.keypair:DeleteKeypair" +keypair_list = "openstackclient.compute.v2.keypair:ListKeypair" +keypair_show = "openstackclient.compute.v2.keypair:ShowKeypair" +server_add_fixed_ip = "openstackclient.compute.v2.server:AddFixedIP" +server_add_floating_ip = "openstackclient.compute.v2.server:AddFloatingIP" +server_add_port = "openstackclient.compute.v2.server:AddPort" +server_add_network = "openstackclient.compute.v2.server:AddNetwork" +server_add_security_group = "openstackclient.compute.v2.server:AddServerSecurityGroup" +server_add_volume = "openstackclient.compute.v2.server:AddServerVolume" +server_create = "openstackclient.compute.v2.server:CreateServer" +server_delete = "openstackclient.compute.v2.server:DeleteServer" +server_dump_create = "openstackclient.compute.v2.server:CreateServerDump" +server_evacuate = "openstackclient.compute.v2.server:EvacuateServer" +server_list = "openstackclient.compute.v2.server:ListServer" +server_lock = "openstackclient.compute.v2.server:LockServer" +server_migrate = "openstackclient.compute.v2.server:MigrateServer" +server_migrate_confirm = "openstackclient.compute.v2.server:MigrateConfirm" +server_migrate_revert = "openstackclient.compute.v2.server:MigrateRevert" +server_migration_confirm = "openstackclient.compute.v2.server:ConfirmMigration" +server_migration_revert = "openstackclient.compute.v2.server:RevertMigration" +server_pause = "openstackclient.compute.v2.server:PauseServer" +server_reboot = "openstackclient.compute.v2.server:RebootServer" +server_rebuild = "openstackclient.compute.v2.server:RebuildServer" +server_remove_fixed_ip = "openstackclient.compute.v2.server:RemoveFixedIP" +server_remove_floating_ip = "openstackclient.compute.v2.server:RemoveFloatingIP" +server_remove_port = "openstackclient.compute.v2.server:RemovePort" +server_remove_network = "openstackclient.compute.v2.server:RemoveNetwork" +server_remove_security_group = "openstackclient.compute.v2.server:RemoveServerSecurityGroup" +server_remove_volume = "openstackclient.compute.v2.server:RemoveServerVolume" +server_rescue = "openstackclient.compute.v2.server:RescueServer" +server_resize = "openstackclient.compute.v2.server:ResizeServer" +server_resize_confirm = "openstackclient.compute.v2.server:ResizeConfirm" +server_resize_revert = "openstackclient.compute.v2.server:ResizeRevert" +server_restore = "openstackclient.compute.v2.server:RestoreServer" +server_resume = "openstackclient.compute.v2.server:ResumeServer" +server_set = "openstackclient.compute.v2.server:SetServer" +server_shelve = "openstackclient.compute.v2.server:ShelveServer" +server_show = "openstackclient.compute.v2.server:ShowServer" +server_ssh = "openstackclient.compute.v2.server:SshServer" +server_start = "openstackclient.compute.v2.server:StartServer" +server_stop = "openstackclient.compute.v2.server:StopServer" +server_suspend = "openstackclient.compute.v2.server:SuspendServer" +server_unlock = "openstackclient.compute.v2.server:UnlockServer" +server_unpause = "openstackclient.compute.v2.server:UnpauseServer" +server_unrescue = "openstackclient.compute.v2.server:UnrescueServer" +server_unset = "openstackclient.compute.v2.server:UnsetServer" +server_unshelve = "openstackclient.compute.v2.server:UnshelveServer" +server_backup_create = "openstackclient.compute.v2.server_backup:CreateServerBackup" +server_event_list = "openstackclient.compute.v2.server_event:ListServerEvent" +server_event_show = "openstackclient.compute.v2.server_event:ShowServerEvent" +server_group_create = "openstackclient.compute.v2.server_group:CreateServerGroup" +server_group_delete = "openstackclient.compute.v2.server_group:DeleteServerGroup" +server_group_list = "openstackclient.compute.v2.server_group:ListServerGroup" +server_group_show = "openstackclient.compute.v2.server_group:ShowServerGroup" +server_image_create = "openstackclient.compute.v2.server_image:CreateServerImage" +server_migration_abort = "openstackclient.compute.v2.server_migration:AbortMigration" +server_migration_force_complete = "openstackclient.compute.v2.server_migration:ForceCompleteMigration" +server_migration_list = "openstackclient.compute.v2.server_migration:ListMigration" +server_migration_show = "openstackclient.compute.v2.server_migration:ShowMigration" +server_volume_list = "openstackclient.compute.v2.server_volume:ListServerVolume" +server_volume_set = "openstackclient.compute.v2.server_volume:SetServerVolume" +server_volume_update = "openstackclient.compute.v2.server_volume:UpdateServerVolume" +usage_list = "openstackclient.compute.v2.usage:ListUsage" +usage_show = "openstackclient.compute.v2.usage:ShowUsage" + +[project.entry-points."openstack.identity.v2"] +catalog_list = "openstackclient.identity.v2_0.catalog:ListCatalog" +catalog_show = "openstackclient.identity.v2_0.catalog:ShowCatalog" +ec2_credentials_create = "openstackclient.identity.v2_0.ec2creds:CreateEC2Creds" +ec2_credentials_delete = "openstackclient.identity.v2_0.ec2creds:DeleteEC2Creds" +ec2_credentials_list = "openstackclient.identity.v2_0.ec2creds:ListEC2Creds" +ec2_credentials_show = "openstackclient.identity.v2_0.ec2creds:ShowEC2Creds" +endpoint_create = "openstackclient.identity.v2_0.endpoint:CreateEndpoint" +endpoint_delete = "openstackclient.identity.v2_0.endpoint:DeleteEndpoint" +endpoint_list = "openstackclient.identity.v2_0.endpoint:ListEndpoint" +endpoint_show = "openstackclient.identity.v2_0.endpoint:ShowEndpoint" +project_create = "openstackclient.identity.v2_0.project:CreateProject" +project_delete = "openstackclient.identity.v2_0.project:DeleteProject" +project_list = "openstackclient.identity.v2_0.project:ListProject" +project_set = "openstackclient.identity.v2_0.project:SetProject" +project_show = "openstackclient.identity.v2_0.project:ShowProject" +project_unset = "openstackclient.identity.v2_0.project:UnsetProject" +role_add = "openstackclient.identity.v2_0.role:AddRole" +role_create = "openstackclient.identity.v2_0.role:CreateRole" +role_delete = "openstackclient.identity.v2_0.role:DeleteRole" +role_list = "openstackclient.identity.v2_0.role:ListRole" +role_remove = "openstackclient.identity.v2_0.role:RemoveRole" +role_show = "openstackclient.identity.v2_0.role:ShowRole" +role_assignment_list = "openstackclient.identity.v2_0.role_assignment:ListRoleAssignment" +service_create = "openstackclient.identity.v2_0.service:CreateService" +service_delete = "openstackclient.identity.v2_0.service:DeleteService" +service_list = "openstackclient.identity.v2_0.service:ListService" +service_show = "openstackclient.identity.v2_0.service:ShowService" +token_issue = "openstackclient.identity.v2_0.token:IssueToken" +token_revoke = "openstackclient.identity.v2_0.token:RevokeToken" +user_create = "openstackclient.identity.v2_0.user:CreateUser" +user_delete = "openstackclient.identity.v2_0.user:DeleteUser" +user_list = "openstackclient.identity.v2_0.user:ListUser" +user_set = "openstackclient.identity.v2_0.user:SetUser" +user_show = "openstackclient.identity.v2_0.user:ShowUser" + +[project.entry-points."openstack.identity.v3"] +access_token_create = "openstackclient.identity.v3.token:CreateAccessToken" +access_rule_delete = "openstackclient.identity.v3.access_rule:DeleteAccessRule" +access_rule_list = "openstackclient.identity.v3.access_rule:ListAccessRule" +access_rule_show = "openstackclient.identity.v3.access_rule:ShowAccessRule" +application_credential_create = "openstackclient.identity.v3.application_credential:CreateApplicationCredential" +application_credential_delete = "openstackclient.identity.v3.application_credential:DeleteApplicationCredential" +application_credential_list = "openstackclient.identity.v3.application_credential:ListApplicationCredential" +application_credential_show = "openstackclient.identity.v3.application_credential:ShowApplicationCredential" +catalog_list = "openstackclient.identity.v3.catalog:ListCatalog" +catalog_show = "openstackclient.identity.v3.catalog:ShowCatalog" +consumer_create = "openstackclient.identity.v3.consumer:CreateConsumer" +consumer_delete = "openstackclient.identity.v3.consumer:DeleteConsumer" +consumer_list = "openstackclient.identity.v3.consumer:ListConsumer" +consumer_set = "openstackclient.identity.v3.consumer:SetConsumer" +consumer_show = "openstackclient.identity.v3.consumer:ShowConsumer" +credential_create = "openstackclient.identity.v3.credential:CreateCredential" +credential_delete = "openstackclient.identity.v3.credential:DeleteCredential" +credential_list = "openstackclient.identity.v3.credential:ListCredential" +credential_set = "openstackclient.identity.v3.credential:SetCredential" +credential_show = "openstackclient.identity.v3.credential:ShowCredential" +domain_create = "openstackclient.identity.v3.domain:CreateDomain" +domain_delete = "openstackclient.identity.v3.domain:DeleteDomain" +domain_list = "openstackclient.identity.v3.domain:ListDomain" +domain_set = "openstackclient.identity.v3.domain:SetDomain" +domain_show = "openstackclient.identity.v3.domain:ShowDomain" +ec2_credentials_create = "openstackclient.identity.v3.ec2creds:CreateEC2Creds" +ec2_credentials_delete = "openstackclient.identity.v3.ec2creds:DeleteEC2Creds" +ec2_credentials_list = "openstackclient.identity.v3.ec2creds:ListEC2Creds" +ec2_credentials_show = "openstackclient.identity.v3.ec2creds:ShowEC2Creds" +endpoint_add_project = "openstackclient.identity.v3.endpoint:AddProjectToEndpoint" +endpoint_create = "openstackclient.identity.v3.endpoint:CreateEndpoint" +endpoint_delete = "openstackclient.identity.v3.endpoint:DeleteEndpoint" +endpoint_list = "openstackclient.identity.v3.endpoint:ListEndpoint" +endpoint_remove_project = "openstackclient.identity.v3.endpoint:RemoveProjectFromEndpoint" +endpoint_set = "openstackclient.identity.v3.endpoint:SetEndpoint" +endpoint_show = "openstackclient.identity.v3.endpoint:ShowEndpoint" +endpoint_group_add_project = "openstackclient.identity.v3.endpoint_group:AddProjectToEndpointGroup" +endpoint_group_create = "openstackclient.identity.v3.endpoint_group:CreateEndpointGroup" +endpoint_group_delete = "openstackclient.identity.v3.endpoint_group:DeleteEndpointGroup" +endpoint_group_list = "openstackclient.identity.v3.endpoint_group:ListEndpointGroup" +endpoint_group_remove_project = "openstackclient.identity.v3.endpoint_group:RemoveProjectFromEndpointGroup" +endpoint_group_set = "openstackclient.identity.v3.endpoint_group:SetEndpointGroup" +endpoint_group_show = "openstackclient.identity.v3.endpoint_group:ShowEndpointGroup" +federation_domain_list = "openstackclient.identity.v3.unscoped_saml:ListAccessibleDomains" +federation_project_list = "openstackclient.identity.v3.unscoped_saml:ListAccessibleProjects" +federation_protocol_create = "openstackclient.identity.v3.federation_protocol:CreateProtocol" +federation_protocol_delete = "openstackclient.identity.v3.federation_protocol:DeleteProtocol" +federation_protocol_list = "openstackclient.identity.v3.federation_protocol:ListProtocols" +federation_protocol_set = "openstackclient.identity.v3.federation_protocol:SetProtocol" +federation_protocol_show = "openstackclient.identity.v3.federation_protocol:ShowProtocol" +group_add_user = "openstackclient.identity.v3.group:AddUserToGroup" +group_contains_user = "openstackclient.identity.v3.group:CheckUserInGroup" +group_create = "openstackclient.identity.v3.group:CreateGroup" +group_delete = "openstackclient.identity.v3.group:DeleteGroup" +group_list = "openstackclient.identity.v3.group:ListGroup" +group_remove_user = "openstackclient.identity.v3.group:RemoveUserFromGroup" +group_set = "openstackclient.identity.v3.group:SetGroup" +group_show = "openstackclient.identity.v3.group:ShowGroup" +identity_provider_create = "openstackclient.identity.v3.identity_provider:CreateIdentityProvider" +identity_provider_delete = "openstackclient.identity.v3.identity_provider:DeleteIdentityProvider" +identity_provider_list = "openstackclient.identity.v3.identity_provider:ListIdentityProvider" +identity_provider_set = "openstackclient.identity.v3.identity_provider:SetIdentityProvider" +identity_provider_show = "openstackclient.identity.v3.identity_provider:ShowIdentityProvider" +implied_role_create = "openstackclient.identity.v3.implied_role:CreateImpliedRole" +implied_role_delete = "openstackclient.identity.v3.implied_role:DeleteImpliedRole" +implied_role_list = "openstackclient.identity.v3.implied_role:ListImpliedRole" +limit_create = "openstackclient.identity.v3.limit:CreateLimit" +limit_delete = "openstackclient.identity.v3.limit:DeleteLimit" +limit_list = "openstackclient.identity.v3.limit:ListLimit" +limit_set = "openstackclient.identity.v3.limit:SetLimit" +limit_show = "openstackclient.identity.v3.limit:ShowLimit" +mapping_create = "openstackclient.identity.v3.mapping:CreateMapping" +mapping_delete = "openstackclient.identity.v3.mapping:DeleteMapping" +mapping_list = "openstackclient.identity.v3.mapping:ListMapping" +mapping_set = "openstackclient.identity.v3.mapping:SetMapping" +mapping_show = "openstackclient.identity.v3.mapping:ShowMapping" +policy_create = "openstackclient.identity.v3.policy:CreatePolicy" +policy_delete = "openstackclient.identity.v3.policy:DeletePolicy" +policy_list = "openstackclient.identity.v3.policy:ListPolicy" +policy_set = "openstackclient.identity.v3.policy:SetPolicy" +policy_show = "openstackclient.identity.v3.policy:ShowPolicy" +project_create = "openstackclient.identity.v3.project:CreateProject" +project_delete = "openstackclient.identity.v3.project:DeleteProject" +project_list = "openstackclient.identity.v3.project:ListProject" +project_set = "openstackclient.identity.v3.project:SetProject" +project_show = "openstackclient.identity.v3.project:ShowProject" +region_create = "openstackclient.identity.v3.region:CreateRegion" +region_delete = "openstackclient.identity.v3.region:DeleteRegion" +region_list = "openstackclient.identity.v3.region:ListRegion" +region_set = "openstackclient.identity.v3.region:SetRegion" +region_show = "openstackclient.identity.v3.region:ShowRegion" +registered_limit_create = "openstackclient.identity.v3.registered_limit:CreateRegisteredLimit" +registered_limit_delete = "openstackclient.identity.v3.registered_limit:DeleteRegisteredLimit" +registered_limit_list = "openstackclient.identity.v3.registered_limit:ListRegisteredLimit" +registered_limit_set = "openstackclient.identity.v3.registered_limit:SetRegisteredLimit" +registered_limit_show = "openstackclient.identity.v3.registered_limit:ShowRegisteredLimit" +request_token_authorize = "openstackclient.identity.v3.token:AuthorizeRequestToken" +request_token_create = "openstackclient.identity.v3.token:CreateRequestToken" +role_add = "openstackclient.identity.v3.role:AddRole" +role_create = "openstackclient.identity.v3.role:CreateRole" +role_delete = "openstackclient.identity.v3.role:DeleteRole" +role_list = "openstackclient.identity.v3.role:ListRole" +role_remove = "openstackclient.identity.v3.role:RemoveRole" +role_show = "openstackclient.identity.v3.role:ShowRole" +role_set = "openstackclient.identity.v3.role:SetRole" +role_assignment_list = "openstackclient.identity.v3.role_assignment:ListRoleAssignment" +service_create = "openstackclient.identity.v3.service:CreateService" +service_delete = "openstackclient.identity.v3.service:DeleteService" +service_list = "openstackclient.identity.v3.service:ListService" +service_show = "openstackclient.identity.v3.service:ShowService" +service_set = "openstackclient.identity.v3.service:SetService" +service_provider_create = "openstackclient.identity.v3.service_provider:CreateServiceProvider" +service_provider_delete = "openstackclient.identity.v3.service_provider:DeleteServiceProvider" +service_provider_list = "openstackclient.identity.v3.service_provider:ListServiceProvider" +service_provider_set = "openstackclient.identity.v3.service_provider:SetServiceProvider" +service_provider_show = "openstackclient.identity.v3.service_provider:ShowServiceProvider" +token_issue = "openstackclient.identity.v3.token:IssueToken" +token_revoke = "openstackclient.identity.v3.token:RevokeToken" +trust_create = "openstackclient.identity.v3.trust:CreateTrust" +trust_delete = "openstackclient.identity.v3.trust:DeleteTrust" +trust_list = "openstackclient.identity.v3.trust:ListTrust" +trust_show = "openstackclient.identity.v3.trust:ShowTrust" +user_create = "openstackclient.identity.v3.user:CreateUser" +user_delete = "openstackclient.identity.v3.user:DeleteUser" +user_list = "openstackclient.identity.v3.user:ListUser" +user_set = "openstackclient.identity.v3.user:SetUser" +user_password_set = "openstackclient.identity.v3.user:SetPasswordUser" +user_show = "openstackclient.identity.v3.user:ShowUser" + +[project.entry-points."openstack.image.v1"] +image_create = "openstackclient.image.v1.image:CreateImage" +image_delete = "openstackclient.image.v1.image:DeleteImage" +image_list = "openstackclient.image.v1.image:ListImage" +image_save = "openstackclient.image.v1.image:SaveImage" +image_set = "openstackclient.image.v1.image:SetImage" +image_show = "openstackclient.image.v1.image:ShowImage" + +[project.entry-points."openstack.image.v2"] +image_add_project = "openstackclient.image.v2.image:AddProjectToImage" +image_create = "openstackclient.image.v2.image:CreateImage" +image_delete = "openstackclient.image.v2.image:DeleteImage" +image_list = "openstackclient.image.v2.image:ListImage" +image_member_list = "openstackclient.image.v2.image:ListImageProjects" +image_remove_project = "openstackclient.image.v2.image:RemoveProjectImage" +image_member_get = "openstackclient.image.v2.image:ShowProjectImage" +image_save = "openstackclient.image.v2.image:SaveImage" +image_show = "openstackclient.image.v2.image:ShowImage" +image_set = "openstackclient.image.v2.image:SetImage" +image_unset = "openstackclient.image.v2.image:UnsetImage" +image_stage = "openstackclient.image.v2.image:StageImage" +image_task_show = "openstackclient.image.v2.task:ShowTask" +image_task_list = "openstackclient.image.v2.task:ListTask" +image_import_info = "openstackclient.image.v2.info:ImportInfo" +image_import = "openstackclient.image.v2.image:ImportImage" +image_stores_list = "openstackclient.image.v2.image:StoresInfo" +image_metadef_namespace_create = "openstackclient.image.v2.metadef_namespaces:CreateMetadefNamespace" +image_metadef_namespace_delete = "openstackclient.image.v2.metadef_namespaces:DeleteMetadefNamespace" +image_metadef_namespace_list = "openstackclient.image.v2.metadef_namespaces:ListMetadefNamespace" +image_metadef_namespace_set = "openstackclient.image.v2.metadef_namespaces:SetMetadefNamespace" +image_metadef_namespace_show = "openstackclient.image.v2.metadef_namespaces:ShowMetadefNamespace" +image_metadef_object_create = "openstackclient.image.v2.metadef_objects:CreateMetadefObjects" +image_metadef_object_show = "openstackclient.image.v2.metadef_objects:ShowMetadefObjects" +image_metadef_object_list = "openstackclient.image.v2.metadef_objects:ListMetadefObjects" +image_metadef_object_delete = "openstackclient.image.v2.metadef_objects:DeleteMetadefObject" +image_metadef_object_update = "openstackclient.image.v2.metadef_objects:SetMetadefObject" +image_metadef_object_property_show = "openstackclient.image.v2.metadef_objects:ShowMetadefObjectProperty" +image_metadef_property_create = "openstackclient.image.v2.metadef_properties:CreateMetadefProperty" +image_metadef_property_delete = "openstackclient.image.v2.metadef_properties:DeleteMetadefProperty" +image_metadef_property_list = "openstackclient.image.v2.metadef_properties:ListMetadefProperties" +image_metadef_property_set = "openstackclient.image.v2.metadef_properties:SetMetadefProperty" +image_metadef_property_show = "openstackclient.image.v2.metadef_properties:ShowMetadefProperty" +image_metadef_resource_type_list = "openstackclient.image.v2.metadef_resource_types:ListMetadefResourceTypes" +image_metadef_resource_type_association_create = "openstackclient.image.v2.metadef_resource_type_association:CreateMetadefResourceTypeAssociation" +image_metadef_resource_type_association_delete = "openstackclient.image.v2.metadef_resource_type_association:DeleteMetadefResourceTypeAssociation" +image_metadef_resource_type_association_list = "openstackclient.image.v2.metadef_resource_type_association:ListMetadefResourceTypeAssociations" +cached_image_list = "openstackclient.image.v2.cache:ListCachedImage" +cached_image_queue = "openstackclient.image.v2.cache:QueueCachedImage" +cached_image_delete = "openstackclient.image.v2.cache:DeleteCachedImage" +cached_image_clear = "openstackclient.image.v2.cache:ClearCachedImage" + +[project.entry-points."openstack.network.v2"] +address_group_create = "openstackclient.network.v2.address_group:CreateAddressGroup" +address_group_delete = "openstackclient.network.v2.address_group:DeleteAddressGroup" +address_group_list = "openstackclient.network.v2.address_group:ListAddressGroup" +address_group_set = "openstackclient.network.v2.address_group:SetAddressGroup" +address_group_show = "openstackclient.network.v2.address_group:ShowAddressGroup" +address_group_unset = "openstackclient.network.v2.address_group:UnsetAddressGroup" +address_scope_create = "openstackclient.network.v2.address_scope:CreateAddressScope" +address_scope_delete = "openstackclient.network.v2.address_scope:DeleteAddressScope" +address_scope_list = "openstackclient.network.v2.address_scope:ListAddressScope" +address_scope_set = "openstackclient.network.v2.address_scope:SetAddressScope" +address_scope_show = "openstackclient.network.v2.address_scope:ShowAddressScope" +floating_ip_create = "openstackclient.network.v2.floating_ip:CreateFloatingIP" +floating_ip_delete = "openstackclient.network.v2.floating_ip:DeleteFloatingIP" +floating_ip_list = "openstackclient.network.v2.floating_ip:ListFloatingIP" +floating_ip_set = "openstackclient.network.v2.floating_ip:SetFloatingIP" +floating_ip_show = "openstackclient.network.v2.floating_ip:ShowFloatingIP" +floating_ip_unset = "openstackclient.network.v2.floating_ip:UnsetFloatingIP" +floating_ip_pool_list = "openstackclient.network.v2.floating_ip_pool:ListFloatingIPPool" +floating_ip_port_forwarding_create = "openstackclient.network.v2.floating_ip_port_forwarding:CreateFloatingIPPortForwarding" +floating_ip_port_forwarding_delete = "openstackclient.network.v2.floating_ip_port_forwarding:DeleteFloatingIPPortForwarding" +floating_ip_port_forwarding_list = "openstackclient.network.v2.floating_ip_port_forwarding:ListFloatingIPPortForwarding" +floating_ip_port_forwarding_set = "openstackclient.network.v2.floating_ip_port_forwarding:SetFloatingIPPortForwarding" +floating_ip_port_forwarding_show = "openstackclient.network.v2.floating_ip_port_forwarding:ShowFloatingIPPortForwarding" +ip_availability_list = "openstackclient.network.v2.ip_availability:ListIPAvailability" +ip_availability_show = "openstackclient.network.v2.ip_availability:ShowIPAvailability" +local_ip_create = "openstackclient.network.v2.local_ip:CreateLocalIP" +local_ip_delete = "openstackclient.network.v2.local_ip:DeleteLocalIP" +local_ip_list = "openstackclient.network.v2.local_ip:ListLocalIP" +local_ip_set = "openstackclient.network.v2.local_ip:SetLocalIP" +local_ip_show = "openstackclient.network.v2.local_ip:ShowLocalIP" +local_ip_association_create = "openstackclient.network.v2.local_ip_association:CreateLocalIPAssociation" +local_ip_association_delete = "openstackclient.network.v2.local_ip_association:DeleteLocalIPAssociation" +local_ip_association_list = "openstackclient.network.v2.local_ip_association:ListLocalIPAssociation" +network_agent_add_network = "openstackclient.network.v2.network_agent:AddNetworkToAgent" +network_agent_add_router = "openstackclient.network.v2.network_agent:AddRouterToAgent" +network_agent_delete = "openstackclient.network.v2.network_agent:DeleteNetworkAgent" +network_agent_list = "openstackclient.network.v2.network_agent:ListNetworkAgent" +network_agent_remove_network = "openstackclient.network.v2.network_agent:RemoveNetworkFromAgent" +network_agent_remove_router = "openstackclient.network.v2.network_agent:RemoveRouterFromAgent" +network_agent_set = "openstackclient.network.v2.network_agent:SetNetworkAgent" +network_agent_show = "openstackclient.network.v2.network_agent:ShowNetworkAgent" +network_auto_allocated_topology_create = "openstackclient.network.v2.network_auto_allocated_topology:CreateAutoAllocatedTopology" +network_auto_allocated_topology_delete = "openstackclient.network.v2.network_auto_allocated_topology:DeleteAutoAllocatedTopology" +network_flavor_add_profile = "openstackclient.network.v2.network_flavor:AddNetworkFlavorToProfile" +network_flavor_create = "openstackclient.network.v2.network_flavor:CreateNetworkFlavor" +network_flavor_delete = "openstackclient.network.v2.network_flavor:DeleteNetworkFlavor" +network_flavor_list = "openstackclient.network.v2.network_flavor:ListNetworkFlavor" +network_flavor_remove_profile = "openstackclient.network.v2.network_flavor:RemoveNetworkFlavorFromProfile" +network_flavor_set = "openstackclient.network.v2.network_flavor:SetNetworkFlavor" +network_flavor_show = "openstackclient.network.v2.network_flavor:ShowNetworkFlavor" +network_flavor_profile_create = "openstackclient.network.v2.network_flavor_profile:CreateNetworkFlavorProfile" +network_flavor_profile_delete = "openstackclient.network.v2.network_flavor_profile:DeleteNetworkFlavorProfile" +network_flavor_profile_list = "openstackclient.network.v2.network_flavor_profile:ListNetworkFlavorProfile" +network_flavor_profile_set = "openstackclient.network.v2.network_flavor_profile:SetNetworkFlavorProfile" +network_flavor_profile_show = "openstackclient.network.v2.network_flavor_profile:ShowNetworkFlavorProfile" +network_create = "openstackclient.network.v2.network:CreateNetwork" +network_delete = "openstackclient.network.v2.network:DeleteNetwork" +network_list = "openstackclient.network.v2.network:ListNetwork" +network_set = "openstackclient.network.v2.network:SetNetwork" +network_show = "openstackclient.network.v2.network:ShowNetwork" +network_unset = "openstackclient.network.v2.network:UnsetNetwork" +network_l3_conntrack_helper_create = "openstackclient.network.v2.l3_conntrack_helper:CreateConntrackHelper" +network_l3_conntrack_helper_delete = "openstackclient.network.v2.l3_conntrack_helper:DeleteConntrackHelper" +network_l3_conntrack_helper_list = "openstackclient.network.v2.l3_conntrack_helper:ListConntrackHelper" +network_l3_conntrack_helper_set = "openstackclient.network.v2.l3_conntrack_helper:SetConntrackHelper" +network_l3_conntrack_helper_show = "openstackclient.network.v2.l3_conntrack_helper:ShowConntrackHelper" +network_meter_create = "openstackclient.network.v2.network_meter:CreateMeter" +network_meter_delete = "openstackclient.network.v2.network_meter:DeleteMeter" +network_meter_list = "openstackclient.network.v2.network_meter:ListMeter" +network_meter_show = "openstackclient.network.v2.network_meter:ShowMeter" +network_meter_rule_create = "openstackclient.network.v2.network_meter_rule:CreateMeterRule" +network_meter_rule_delete = "openstackclient.network.v2.network_meter_rule:DeleteMeterRule" +network_meter_rule_list = "openstackclient.network.v2.network_meter_rule:ListMeterRule" +network_meter_rule_show = "openstackclient.network.v2.network_meter_rule:ShowMeterRule" +network_qos_policy_create = "openstackclient.network.v2.network_qos_policy:CreateNetworkQosPolicy" +network_qos_policy_delete = "openstackclient.network.v2.network_qos_policy:DeleteNetworkQosPolicy" +network_qos_policy_list = "openstackclient.network.v2.network_qos_policy:ListNetworkQosPolicy" +network_qos_policy_set = "openstackclient.network.v2.network_qos_policy:SetNetworkQosPolicy" +network_qos_policy_show = "openstackclient.network.v2.network_qos_policy:ShowNetworkQosPolicy" +network_qos_rule_create = "openstackclient.network.v2.network_qos_rule:CreateNetworkQosRule" +network_qos_rule_delete = "openstackclient.network.v2.network_qos_rule:DeleteNetworkQosRule" +network_qos_rule_list = "openstackclient.network.v2.network_qos_rule:ListNetworkQosRule" +network_qos_rule_set = "openstackclient.network.v2.network_qos_rule:SetNetworkQosRule" +network_qos_rule_show = "openstackclient.network.v2.network_qos_rule:ShowNetworkQosRule" +network_qos_rule_type_list = "openstackclient.network.v2.network_qos_rule_type:ListNetworkQosRuleType" +network_qos_rule_type_show = "openstackclient.network.v2.network_qos_rule_type:ShowNetworkQosRuleType" +network_rbac_create = "openstackclient.network.v2.network_rbac:CreateNetworkRBAC" +network_rbac_delete = "openstackclient.network.v2.network_rbac:DeleteNetworkRBAC" +network_rbac_list = "openstackclient.network.v2.network_rbac:ListNetworkRBAC" +network_rbac_set = "openstackclient.network.v2.network_rbac:SetNetworkRBAC" +network_rbac_show = "openstackclient.network.v2.network_rbac:ShowNetworkRBAC" +network_segment_create = "openstackclient.network.v2.network_segment:CreateNetworkSegment" +network_segment_delete = "openstackclient.network.v2.network_segment:DeleteNetworkSegment" +network_segment_list = "openstackclient.network.v2.network_segment:ListNetworkSegment" +network_segment_set = "openstackclient.network.v2.network_segment:SetNetworkSegment" +network_segment_show = "openstackclient.network.v2.network_segment:ShowNetworkSegment" +network_segment_range_create = "openstackclient.network.v2.network_segment_range:CreateNetworkSegmentRange" +network_segment_range_delete = "openstackclient.network.v2.network_segment_range:DeleteNetworkSegmentRange" +network_segment_range_list = "openstackclient.network.v2.network_segment_range:ListNetworkSegmentRange" +network_segment_range_set = "openstackclient.network.v2.network_segment_range:SetNetworkSegmentRange" +network_segment_range_show = "openstackclient.network.v2.network_segment_range:ShowNetworkSegmentRange" +network_service_provider_list = "openstackclient.network.v2.network_service_provider:ListNetworkServiceProvider" +network_subport_list = "openstackclient.network.v2.network_trunk:ListNetworkSubport" +network_trunk_create = "openstackclient.network.v2.network_trunk:CreateNetworkTrunk" +network_trunk_delete = "openstackclient.network.v2.network_trunk:DeleteNetworkTrunk" +network_trunk_list = "openstackclient.network.v2.network_trunk:ListNetworkTrunk" +network_trunk_set = "openstackclient.network.v2.network_trunk:SetNetworkTrunk" +network_trunk_show = "openstackclient.network.v2.network_trunk:ShowNetworkTrunk" +network_trunk_unset = "openstackclient.network.v2.network_trunk:UnsetNetworkTrunk" +port_create = "openstackclient.network.v2.port:CreatePort" +port_delete = "openstackclient.network.v2.port:DeletePort" +port_list = "openstackclient.network.v2.port:ListPort" +port_set = "openstackclient.network.v2.port:SetPort" +port_show = "openstackclient.network.v2.port:ShowPort" +port_unset = "openstackclient.network.v2.port:UnsetPort" +router_add_gateway = "openstackclient.network.v2.router:AddGatewayToRouter" +router_add_port = "openstackclient.network.v2.router:AddPortToRouter" +router_add_route = "openstackclient.network.v2.router:AddExtraRoutesToRouter" +router_add_subnet = "openstackclient.network.v2.router:AddSubnetToRouter" +router_create = "openstackclient.network.v2.router:CreateRouter" +router_delete = "openstackclient.network.v2.router:DeleteRouter" +router_list = "openstackclient.network.v2.router:ListRouter" +router_remove_gateway = "openstackclient.network.v2.router:RemoveGatewayFromRouter" +router_remove_port = "openstackclient.network.v2.router:RemovePortFromRouter" +router_remove_route = "openstackclient.network.v2.router:RemoveExtraRoutesFromRouter" +router_remove_subnet = "openstackclient.network.v2.router:RemoveSubnetFromRouter" +router_set = "openstackclient.network.v2.router:SetRouter" +router_show = "openstackclient.network.v2.router:ShowRouter" +router_unset = "openstackclient.network.v2.router:UnsetRouter" +router_ndp_proxy_create = "openstackclient.network.v2.ndp_proxy:CreateNDPProxy" +router_ndp_proxy_delete = "openstackclient.network.v2.ndp_proxy:DeleteNDPProxy" +router_ndp_proxy_list = "openstackclient.network.v2.ndp_proxy:ListNDPProxy" +router_ndp_proxy_set = "openstackclient.network.v2.ndp_proxy:SetNDPProxy" +router_ndp_proxy_show = "openstackclient.network.v2.ndp_proxy:ShowNDPProxy" +security_group_create = "openstackclient.network.v2.security_group:CreateSecurityGroup" +security_group_delete = "openstackclient.network.v2.security_group:DeleteSecurityGroup" +security_group_list = "openstackclient.network.v2.security_group:ListSecurityGroup" +security_group_set = "openstackclient.network.v2.security_group:SetSecurityGroup" +security_group_show = "openstackclient.network.v2.security_group:ShowSecurityGroup" +security_group_unset = "openstackclient.network.v2.security_group:UnsetSecurityGroup" +security_group_rule_create = "openstackclient.network.v2.security_group_rule:CreateSecurityGroupRule" +security_group_rule_delete = "openstackclient.network.v2.security_group_rule:DeleteSecurityGroupRule" +security_group_rule_list = "openstackclient.network.v2.security_group_rule:ListSecurityGroupRule" +security_group_rule_show = "openstackclient.network.v2.security_group_rule:ShowSecurityGroupRule" +default_security_group_rule_create = "openstackclient.network.v2.default_security_group_rule:CreateDefaultSecurityGroupRule" +default_security_group_rule_delete = "openstackclient.network.v2.default_security_group_rule:DeleteDefaultSecurityGroupRule" +default_security_group_rule_list = "openstackclient.network.v2.default_security_group_rule:ListDefaultSecurityGroupRule" +default_security_group_rule_show = "openstackclient.network.v2.default_security_group_rule:ShowDefaultSecurityGroupRule" +subnet_create = "openstackclient.network.v2.subnet:CreateSubnet" +subnet_delete = "openstackclient.network.v2.subnet:DeleteSubnet" +subnet_list = "openstackclient.network.v2.subnet:ListSubnet" +subnet_set = "openstackclient.network.v2.subnet:SetSubnet" +subnet_show = "openstackclient.network.v2.subnet:ShowSubnet" +subnet_unset = "openstackclient.network.v2.subnet:UnsetSubnet" +subnet_pool_create = "openstackclient.network.v2.subnet_pool:CreateSubnetPool" +subnet_pool_delete = "openstackclient.network.v2.subnet_pool:DeleteSubnetPool" +subnet_pool_list = "openstackclient.network.v2.subnet_pool:ListSubnetPool" +subnet_pool_set = "openstackclient.network.v2.subnet_pool:SetSubnetPool" +subnet_pool_show = "openstackclient.network.v2.subnet_pool:ShowSubnetPool" +subnet_pool_unset = "openstackclient.network.v2.subnet_pool:UnsetSubnetPool" + +[project.entry-points."openstack.object_store.v1"] +object_store_account_set = "openstackclient.object.v1.account:SetAccount" +object_store_account_show = "openstackclient.object.v1.account:ShowAccount" +object_store_account_unset = "openstackclient.object.v1.account:UnsetAccount" +container_create = "openstackclient.object.v1.container:CreateContainer" +container_delete = "openstackclient.object.v1.container:DeleteContainer" +container_list = "openstackclient.object.v1.container:ListContainer" +container_save = "openstackclient.object.v1.container:SaveContainer" +container_set = "openstackclient.object.v1.container:SetContainer" +container_show = "openstackclient.object.v1.container:ShowContainer" +container_unset = "openstackclient.object.v1.container:UnsetContainer" +object_create = "openstackclient.object.v1.object:CreateObject" +object_delete = "openstackclient.object.v1.object:DeleteObject" +object_list = "openstackclient.object.v1.object:ListObject" +object_save = "openstackclient.object.v1.object:SaveObject" +object_set = "openstackclient.object.v1.object:SetObject" +object_show = "openstackclient.object.v1.object:ShowObject" +object_unset = "openstackclient.object.v1.object:UnsetObject" + +[project.entry-points."openstack.volume.v2"] +consistency_group_add_volume = "openstackclient.volume.v2.consistency_group:AddVolumeToConsistencyGroup" +consistency_group_create = "openstackclient.volume.v2.consistency_group:CreateConsistencyGroup" +consistency_group_delete = "openstackclient.volume.v2.consistency_group:DeleteConsistencyGroup" +consistency_group_list = "openstackclient.volume.v2.consistency_group:ListConsistencyGroup" +consistency_group_remove_volume = "openstackclient.volume.v2.consistency_group:RemoveVolumeFromConsistencyGroup" +consistency_group_set = "openstackclient.volume.v2.consistency_group:SetConsistencyGroup" +consistency_group_show = "openstackclient.volume.v2.consistency_group:ShowConsistencyGroup" +consistency_group_snapshot_create = "openstackclient.volume.v2.consistency_group_snapshot:CreateConsistencyGroupSnapshot" +consistency_group_snapshot_delete = "openstackclient.volume.v2.consistency_group_snapshot:DeleteConsistencyGroupSnapshot" +consistency_group_snapshot_list = "openstackclient.volume.v2.consistency_group_snapshot:ListConsistencyGroupSnapshot" +consistency_group_snapshot_show = "openstackclient.volume.v2.consistency_group_snapshot:ShowConsistencyGroupSnapshot" +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" +volume_backup_create = "openstackclient.volume.v2.volume_backup:CreateVolumeBackup" +volume_backup_delete = "openstackclient.volume.v2.volume_backup:DeleteVolumeBackup" +volume_backup_list = "openstackclient.volume.v2.volume_backup:ListVolumeBackup" +volume_backup_restore = "openstackclient.volume.v2.volume_backup:RestoreVolumeBackup" +volume_backup_set = "openstackclient.volume.v2.volume_backup:SetVolumeBackup" +volume_backup_show = "openstackclient.volume.v2.volume_backup:ShowVolumeBackup" +volume_backup_record_export = "openstackclient.volume.v2.backup_record:ExportBackupRecord" +volume_backup_record_import = "openstackclient.volume.v2.backup_record:ImportBackupRecord" +volume_backend_capability_show = "openstackclient.volume.v2.volume_backend:ShowCapability" +volume_backend_pool_list = "openstackclient.volume.v2.volume_backend:ListPool" +volume_host_failover = "openstackclient.volume.v2.volume_host:FailoverVolumeHost" +volume_host_set = "openstackclient.volume.v2.volume_host:SetVolumeHost" +volume_snapshot_create = "openstackclient.volume.v2.volume_snapshot:CreateVolumeSnapshot" +volume_snapshot_delete = "openstackclient.volume.v2.volume_snapshot:DeleteVolumeSnapshot" +volume_snapshot_list = "openstackclient.volume.v2.volume_snapshot:ListVolumeSnapshot" +volume_snapshot_set = "openstackclient.volume.v2.volume_snapshot:SetVolumeSnapshot" +volume_snapshot_show = "openstackclient.volume.v2.volume_snapshot:ShowVolumeSnapshot" +volume_snapshot_unset = "openstackclient.volume.v2.volume_snapshot:UnsetVolumeSnapshot" +volume_type_create = "openstackclient.volume.v2.volume_type:CreateVolumeType" +volume_type_delete = "openstackclient.volume.v2.volume_type:DeleteVolumeType" +volume_type_list = "openstackclient.volume.v2.volume_type:ListVolumeType" +volume_type_set = "openstackclient.volume.v2.volume_type:SetVolumeType" +volume_type_show = "openstackclient.volume.v2.volume_type:ShowVolumeType" +volume_type_unset = "openstackclient.volume.v2.volume_type:UnsetVolumeType" +volume_qos_associate = "openstackclient.volume.v2.qos_specs:AssociateQos" +volume_qos_create = "openstackclient.volume.v2.qos_specs:CreateQos" +volume_qos_delete = "openstackclient.volume.v2.qos_specs:DeleteQos" +volume_qos_disassociate = "openstackclient.volume.v2.qos_specs:DisassociateQos" +volume_qos_list = "openstackclient.volume.v2.qos_specs:ListQos" +volume_qos_set = "openstackclient.volume.v2.qos_specs:SetQos" +volume_qos_show = "openstackclient.volume.v2.qos_specs:ShowQos" +volume_qos_unset = "openstackclient.volume.v2.qos_specs:UnsetQos" +volume_service_list = "openstackclient.volume.v2.service:ListService" +volume_service_set = "openstackclient.volume.v2.service:SetService" +volume_transfer_request_accept = "openstackclient.volume.v2.volume_transfer_request:AcceptTransferRequest" +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:ListTransferRequest" +volume_transfer_request_show = "openstackclient.volume.v2.volume_transfer_request:ShowTransferRequest" + +[project.entry-points."openstack.volume.v3"] +block_storage_log_level_list = "openstackclient.volume.v3.block_storage_log_level:BlockStorageLogLevelList" +block_storage_log_level_set = "openstackclient.volume.v3.block_storage_log_level:BlockStorageLogLevelSet" +block_storage_cleanup = "openstackclient.volume.v3.block_storage_cleanup:BlockStorageCleanup" +block_storage_volume_manageable_list = "openstackclient.volume.v3.block_storage_manage:BlockStorageManageVolumes" +block_storage_snapshot_manageable_list = "openstackclient.volume.v3.block_storage_manage:BlockStorageManageSnapshots" +consistency_group_add_volume = "openstackclient.volume.v2.consistency_group:AddVolumeToConsistencyGroup" +consistency_group_create = "openstackclient.volume.v2.consistency_group:CreateConsistencyGroup" +consistency_group_delete = "openstackclient.volume.v2.consistency_group:DeleteConsistencyGroup" +consistency_group_list = "openstackclient.volume.v2.consistency_group:ListConsistencyGroup" +consistency_group_remove_volume = "openstackclient.volume.v2.consistency_group:RemoveVolumeFromConsistencyGroup" +consistency_group_set = "openstackclient.volume.v2.consistency_group:SetConsistencyGroup" +consistency_group_show = "openstackclient.volume.v2.consistency_group:ShowConsistencyGroup" +consistency_group_snapshot_create = "openstackclient.volume.v2.consistency_group_snapshot:CreateConsistencyGroupSnapshot" +consistency_group_snapshot_delete = "openstackclient.volume.v2.consistency_group_snapshot:DeleteConsistencyGroupSnapshot" +consistency_group_snapshot_list = "openstackclient.volume.v2.consistency_group_snapshot:ListConsistencyGroupSnapshot" +consistency_group_snapshot_show = "openstackclient.volume.v2.consistency_group_snapshot:ShowConsistencyGroupSnapshot" +volume_create = "openstackclient.volume.v3.volume:CreateVolume" +volume_delete = "openstackclient.volume.v3.volume:DeleteVolume" +volume_list = "openstackclient.volume.v3.volume:ListVolume" +volume_migrate = "openstackclient.volume.v3.volume:MigrateVolume" +volume_set = "openstackclient.volume.v3.volume:SetVolume" +volume_show = "openstackclient.volume.v3.volume:ShowVolume" +volume_unset = "openstackclient.volume.v3.volume:UnsetVolume" +volume_attachment_create = "openstackclient.volume.v3.volume_attachment:CreateVolumeAttachment" +volume_attachment_delete = "openstackclient.volume.v3.volume_attachment:DeleteVolumeAttachment" +volume_attachment_list = "openstackclient.volume.v3.volume_attachment:ListVolumeAttachment" +volume_attachment_complete = "openstackclient.volume.v3.volume_attachment:CompleteVolumeAttachment" +volume_attachment_set = "openstackclient.volume.v3.volume_attachment:SetVolumeAttachment" +volume_attachment_show = "openstackclient.volume.v3.volume_attachment:ShowVolumeAttachment" +volume_backup_create = "openstackclient.volume.v3.volume_backup:CreateVolumeBackup" +volume_backup_delete = "openstackclient.volume.v3.volume_backup:DeleteVolumeBackup" +volume_backup_list = "openstackclient.volume.v3.volume_backup:ListVolumeBackup" +volume_backup_restore = "openstackclient.volume.v3.volume_backup:RestoreVolumeBackup" +volume_backup_set = "openstackclient.volume.v3.volume_backup:SetVolumeBackup" +volume_backup_unset = "openstackclient.volume.v3.volume_backup:UnsetVolumeBackup" +volume_backup_show = "openstackclient.volume.v3.volume_backup:ShowVolumeBackup" +volume_backend_capability_show = "openstackclient.volume.v2.volume_backend:ShowCapability" +volume_backend_pool_list = "openstackclient.volume.v2.volume_backend:ListPool" +volume_backup_record_export = "openstackclient.volume.v2.backup_record:ExportBackupRecord" +volume_backup_record_import = "openstackclient.volume.v2.backup_record:ImportBackupRecord" +volume_group_create = "openstackclient.volume.v3.volume_group:CreateVolumeGroup" +volume_group_delete = "openstackclient.volume.v3.volume_group:DeleteVolumeGroup" +volume_group_list = "openstackclient.volume.v3.volume_group:ListVolumeGroup" +volume_group_failover = "openstackclient.volume.v3.volume_group:FailoverVolumeGroup" +volume_group_set = "openstackclient.volume.v3.volume_group:SetVolumeGroup" +volume_group_show = "openstackclient.volume.v3.volume_group:ShowVolumeGroup" +volume_group_snapshot_create = "openstackclient.volume.v3.volume_group_snapshot:CreateVolumeGroupSnapshot" +volume_group_snapshot_delete = "openstackclient.volume.v3.volume_group_snapshot:DeleteVolumeGroupSnapshot" +volume_group_snapshot_list = "openstackclient.volume.v3.volume_group_snapshot:ListVolumeGroupSnapshot" +volume_group_snapshot_show = "openstackclient.volume.v3.volume_group_snapshot:ShowVolumeGroupSnapshot" +volume_group_type_create = "openstackclient.volume.v3.volume_group_type:CreateVolumeGroupType" +volume_group_type_delete = "openstackclient.volume.v3.volume_group_type:DeleteVolumeGroupType" +volume_group_type_list = "openstackclient.volume.v3.volume_group_type:ListVolumeGroupType" +volume_group_type_set = "openstackclient.volume.v3.volume_group_type:SetVolumeGroupType" +volume_group_type_show = "openstackclient.volume.v3.volume_group_type:ShowVolumeGroupType" +volume_host_set = "openstackclient.volume.v2.volume_host:SetVolumeHost" +volume_message_delete = "openstackclient.volume.v3.volume_message:DeleteMessage" +volume_message_list = "openstackclient.volume.v3.volume_message:ListMessages" +volume_message_show = "openstackclient.volume.v3.volume_message:ShowMessage" +block_storage_cluster_list = "openstackclient.volume.v3.block_storage_cluster:ListBlockStorageCluster" +block_storage_cluster_set = "openstackclient.volume.v3.block_storage_cluster:SetBlockStorageCluster" +block_storage_cluster_show = "openstackclient.volume.v3.block_storage_cluster:ShowBlockStorageCluster" +block_storage_resource_filter_list = "openstackclient.volume.v3.block_storage_resource_filter:ListBlockStorageResourceFilter" +block_storage_resource_filter_show = "openstackclient.volume.v3.block_storage_resource_filter:ShowBlockStorageResourceFilter" +volume_snapshot_create = "openstackclient.volume.v3.volume_snapshot:CreateVolumeSnapshot" +volume_snapshot_delete = "openstackclient.volume.v3.volume_snapshot:DeleteVolumeSnapshot" +volume_snapshot_list = "openstackclient.volume.v3.volume_snapshot:ListVolumeSnapshot" +volume_snapshot_set = "openstackclient.volume.v3.volume_snapshot:SetVolumeSnapshot" +volume_snapshot_show = "openstackclient.volume.v3.volume_snapshot:ShowVolumeSnapshot" +volume_snapshot_unset = "openstackclient.volume.v3.volume_snapshot:UnsetVolumeSnapshot" +volume_type_create = "openstackclient.volume.v3.volume_type:CreateVolumeType" +volume_type_delete = "openstackclient.volume.v3.volume_type:DeleteVolumeType" +volume_type_list = "openstackclient.volume.v3.volume_type:ListVolumeType" +volume_type_set = "openstackclient.volume.v3.volume_type:SetVolumeType" +volume_type_show = "openstackclient.volume.v3.volume_type:ShowVolumeType" +volume_type_unset = "openstackclient.volume.v3.volume_type:UnsetVolumeType" +volume_qos_associate = "openstackclient.volume.v2.qos_specs:AssociateQos" +volume_qos_create = "openstackclient.volume.v2.qos_specs:CreateQos" +volume_qos_delete = "openstackclient.volume.v2.qos_specs:DeleteQos" +volume_qos_disassociate = "openstackclient.volume.v2.qos_specs:DisassociateQos" +volume_qos_list = "openstackclient.volume.v2.qos_specs:ListQos" +volume_qos_set = "openstackclient.volume.v2.qos_specs:SetQos" +volume_qos_show = "openstackclient.volume.v2.qos_specs:ShowQos" +volume_qos_unset = "openstackclient.volume.v2.qos_specs:UnsetQos" +volume_service_list = "openstackclient.volume.v3.service:ListService" +volume_service_set = "openstackclient.volume.v3.service:SetService" +volume_transfer_request_accept = "openstackclient.volume.v3.volume_transfer_request:AcceptTransferRequest" +volume_transfer_request_create = "openstackclient.volume.v3.volume_transfer_request:CreateTransferRequest" +volume_transfer_request_delete = "openstackclient.volume.v3.volume_transfer_request:DeleteTransferRequest" +volume_transfer_request_list = "openstackclient.volume.v3.volume_transfer_request:ListTransferRequest" +volume_transfer_request_show = "openstackclient.volume.v3.volume_transfer_request:ShowTransferRequest" +volume_summary = "openstackclient.volume.v3.volume:VolumeSummary" +volume_revert = "openstackclient.volume.v3.volume:VolumeRevertToSnapshot" + +[tool.setuptools] +packages = [ + "openstackclient" +] + [tool.mypy] python_version = "3.10" show_column_numbers = true diff --git a/setup.cfg b/setup.cfg index 5c6449d5f..28a5b7809 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,821 +1,2 @@ [metadata] name = python-openstackclient -summary = OpenStack Command-line Client -description_file = - README.rst -description_content_type = text/x-rst -author = OpenStack -author_email = openstack-discuss@lists.openstack.org -home_page = https://docs.openstack.org/python-openstackclient/latest/ -python_requires = >=3.10 -classifier = - Environment :: OpenStack - Intended Audience :: Information Technology - Intended Audience :: System Administrators - License :: OSI Approved :: Apache Software License - Operating System :: POSIX :: Linux - Programming Language :: Python - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3.12 - Programming Language :: Python :: 3.13 - -[files] -packages = - openstackclient - -[entry_points] -console_scripts = - openstack = openstackclient.shell:main - -openstack.cli = - command_list = openstackclient.common.module:ListCommand - module_list = openstackclient.common.module:ListModule - -openstack.cli.base = - compute = openstackclient.compute.client - identity = openstackclient.identity.client - image = openstackclient.image.client - network = openstackclient.network.client - object_store = openstackclient.object.client - volume = openstackclient.volume.client - -openstack.common = - availability_zone_list = openstackclient.common.availability_zone:ListAvailabilityZone - configuration_show = openstackclient.common.configuration:ShowConfiguration - extension_list = openstackclient.common.extension:ListExtension - extension_show = openstackclient.common.extension:ShowExtension - limits_show = openstackclient.common.limits:ShowLimits - project_cleanup = openstackclient.common.project_cleanup:ProjectCleanup - quota_list = openstackclient.common.quota:ListQuota - quota_set = openstackclient.common.quota:SetQuota - quota_show = openstackclient.common.quota:ShowQuota - quota_delete = openstackclient.common.quota:DeleteQuota - versions_show = openstackclient.common.versions:ShowVersions - -openstack.compute.v2 = - compute_agent_create = openstackclient.compute.v2.agent:CreateAgent - compute_agent_delete = openstackclient.compute.v2.agent:DeleteAgent - compute_agent_list = openstackclient.compute.v2.agent:ListAgent - compute_agent_set = openstackclient.compute.v2.agent:SetAgent - - aggregate_add_host = openstackclient.compute.v2.aggregate:AddAggregateHost - aggregate_create = openstackclient.compute.v2.aggregate:CreateAggregate - aggregate_delete = openstackclient.compute.v2.aggregate:DeleteAggregate - aggregate_list = openstackclient.compute.v2.aggregate:ListAggregate - aggregate_remove_host = openstackclient.compute.v2.aggregate:RemoveAggregateHost - aggregate_set = openstackclient.compute.v2.aggregate:SetAggregate - aggregate_show = openstackclient.compute.v2.aggregate:ShowAggregate - aggregate_unset = openstackclient.compute.v2.aggregate:UnsetAggregate - aggregate_cache_image = openstackclient.compute.v2.aggregate:CacheImageForAggregate - - compute_service_delete = openstackclient.compute.v2.service:DeleteService - compute_service_list = openstackclient.compute.v2.service:ListService - compute_service_set = openstackclient.compute.v2.service:SetService - - console_log_show = openstackclient.compute.v2.console:ShowConsoleLog - console_url_show = openstackclient.compute.v2.console:ShowConsoleURL - - console_connection_show = openstackclient.compute.v2.console_connection:ShowConsoleConnectionInformation - - flavor_create = openstackclient.compute.v2.flavor:CreateFlavor - flavor_delete = openstackclient.compute.v2.flavor:DeleteFlavor - flavor_list = openstackclient.compute.v2.flavor:ListFlavor - flavor_show = openstackclient.compute.v2.flavor:ShowFlavor - flavor_set = openstackclient.compute.v2.flavor:SetFlavor - flavor_unset = openstackclient.compute.v2.flavor:UnsetFlavor - - host_list = openstackclient.compute.v2.host:ListHost - host_set = openstackclient.compute.v2.host:SetHost - host_show = openstackclient.compute.v2.host:ShowHost - - hypervisor_list = openstackclient.compute.v2.hypervisor:ListHypervisor - hypervisor_show = openstackclient.compute.v2.hypervisor:ShowHypervisor - - hypervisor_stats_show = openstackclient.compute.v2.hypervisor_stats:ShowHypervisorStats - - keypair_create = openstackclient.compute.v2.keypair:CreateKeypair - keypair_delete = openstackclient.compute.v2.keypair:DeleteKeypair - keypair_list = openstackclient.compute.v2.keypair:ListKeypair - keypair_show = openstackclient.compute.v2.keypair:ShowKeypair - - server_add_fixed_ip = openstackclient.compute.v2.server:AddFixedIP - server_add_floating_ip = openstackclient.compute.v2.server:AddFloatingIP - server_add_port = openstackclient.compute.v2.server:AddPort - server_add_network = openstackclient.compute.v2.server:AddNetwork - server_add_security_group = openstackclient.compute.v2.server:AddServerSecurityGroup - server_add_volume = openstackclient.compute.v2.server:AddServerVolume - server_create = openstackclient.compute.v2.server:CreateServer - server_delete = openstackclient.compute.v2.server:DeleteServer - server_dump_create = openstackclient.compute.v2.server:CreateServerDump - server_evacuate = openstackclient.compute.v2.server:EvacuateServer - server_list = openstackclient.compute.v2.server:ListServer - server_lock = openstackclient.compute.v2.server:LockServer - server_migrate = openstackclient.compute.v2.server:MigrateServer - server_migrate_confirm = openstackclient.compute.v2.server:MigrateConfirm - server_migrate_revert = openstackclient.compute.v2.server:MigrateRevert - server_migration_confirm = openstackclient.compute.v2.server:ConfirmMigration - server_migration_revert = openstackclient.compute.v2.server:RevertMigration - server_pause = openstackclient.compute.v2.server:PauseServer - server_reboot = openstackclient.compute.v2.server:RebootServer - server_rebuild = openstackclient.compute.v2.server:RebuildServer - server_remove_fixed_ip = openstackclient.compute.v2.server:RemoveFixedIP - server_remove_floating_ip = openstackclient.compute.v2.server:RemoveFloatingIP - server_remove_port = openstackclient.compute.v2.server:RemovePort - server_remove_network = openstackclient.compute.v2.server:RemoveNetwork - server_remove_security_group = openstackclient.compute.v2.server:RemoveServerSecurityGroup - server_remove_volume = openstackclient.compute.v2.server:RemoveServerVolume - server_rescue = openstackclient.compute.v2.server:RescueServer - server_resize = openstackclient.compute.v2.server:ResizeServer - server_resize_confirm = openstackclient.compute.v2.server:ResizeConfirm - server_resize_revert = openstackclient.compute.v2.server:ResizeRevert - server_restore = openstackclient.compute.v2.server:RestoreServer - server_resume = openstackclient.compute.v2.server:ResumeServer - server_set = openstackclient.compute.v2.server:SetServer - server_shelve = openstackclient.compute.v2.server:ShelveServer - server_show = openstackclient.compute.v2.server:ShowServer - server_ssh = openstackclient.compute.v2.server:SshServer - server_start = openstackclient.compute.v2.server:StartServer - server_stop = openstackclient.compute.v2.server:StopServer - server_suspend = openstackclient.compute.v2.server:SuspendServer - server_unlock = openstackclient.compute.v2.server:UnlockServer - server_unpause = openstackclient.compute.v2.server:UnpauseServer - server_unrescue = openstackclient.compute.v2.server:UnrescueServer - server_unset = openstackclient.compute.v2.server:UnsetServer - server_unshelve = openstackclient.compute.v2.server:UnshelveServer - - server_backup_create = openstackclient.compute.v2.server_backup:CreateServerBackup - - server_event_list = openstackclient.compute.v2.server_event:ListServerEvent - server_event_show = openstackclient.compute.v2.server_event:ShowServerEvent - - server_group_create = openstackclient.compute.v2.server_group:CreateServerGroup - server_group_delete = openstackclient.compute.v2.server_group:DeleteServerGroup - server_group_list = openstackclient.compute.v2.server_group:ListServerGroup - server_group_show = openstackclient.compute.v2.server_group:ShowServerGroup - - server_image_create = openstackclient.compute.v2.server_image:CreateServerImage - - server_migration_abort = openstackclient.compute.v2.server_migration:AbortMigration - server_migration_force_complete = openstackclient.compute.v2.server_migration:ForceCompleteMigration - server_migration_list = openstackclient.compute.v2.server_migration:ListMigration - server_migration_show = openstackclient.compute.v2.server_migration:ShowMigration - - server_volume_list = openstackclient.compute.v2.server_volume:ListServerVolume - server_volume_set = openstackclient.compute.v2.server_volume:SetServerVolume - server_volume_update = openstackclient.compute.v2.server_volume:UpdateServerVolume - - usage_list = openstackclient.compute.v2.usage:ListUsage - usage_show = openstackclient.compute.v2.usage:ShowUsage - -openstack.identity.v2 = - catalog_list = openstackclient.identity.v2_0.catalog:ListCatalog - catalog_show = openstackclient.identity.v2_0.catalog:ShowCatalog - - ec2_credentials_create = openstackclient.identity.v2_0.ec2creds:CreateEC2Creds - ec2_credentials_delete = openstackclient.identity.v2_0.ec2creds:DeleteEC2Creds - ec2_credentials_list = openstackclient.identity.v2_0.ec2creds:ListEC2Creds - ec2_credentials_show = openstackclient.identity.v2_0.ec2creds:ShowEC2Creds - - endpoint_create = openstackclient.identity.v2_0.endpoint:CreateEndpoint - endpoint_delete = openstackclient.identity.v2_0.endpoint:DeleteEndpoint - endpoint_list = openstackclient.identity.v2_0.endpoint:ListEndpoint - endpoint_show = openstackclient.identity.v2_0.endpoint:ShowEndpoint - - project_create = openstackclient.identity.v2_0.project:CreateProject - project_delete = openstackclient.identity.v2_0.project:DeleteProject - project_list = openstackclient.identity.v2_0.project:ListProject - project_set = openstackclient.identity.v2_0.project:SetProject - project_show = openstackclient.identity.v2_0.project:ShowProject - project_unset = openstackclient.identity.v2_0.project:UnsetProject - - role_add = openstackclient.identity.v2_0.role:AddRole - role_create = openstackclient.identity.v2_0.role:CreateRole - role_delete = openstackclient.identity.v2_0.role:DeleteRole - role_list = openstackclient.identity.v2_0.role:ListRole - role_remove = openstackclient.identity.v2_0.role:RemoveRole - role_show = openstackclient.identity.v2_0.role:ShowRole - role_assignment_list = openstackclient.identity.v2_0.role_assignment:ListRoleAssignment - - service_create = openstackclient.identity.v2_0.service:CreateService - service_delete = openstackclient.identity.v2_0.service:DeleteService - service_list = openstackclient.identity.v2_0.service:ListService - service_show = openstackclient.identity.v2_0.service:ShowService - - token_issue = openstackclient.identity.v2_0.token:IssueToken - token_revoke = openstackclient.identity.v2_0.token:RevokeToken - - user_create = openstackclient.identity.v2_0.user:CreateUser - user_delete = openstackclient.identity.v2_0.user:DeleteUser - user_list = openstackclient.identity.v2_0.user:ListUser - user_set = openstackclient.identity.v2_0.user:SetUser - user_show = openstackclient.identity.v2_0.user:ShowUser - -openstack.identity.v3 = - access_token_create = openstackclient.identity.v3.token:CreateAccessToken - - access_rule_delete = openstackclient.identity.v3.access_rule:DeleteAccessRule - access_rule_list = openstackclient.identity.v3.access_rule:ListAccessRule - access_rule_show = openstackclient.identity.v3.access_rule:ShowAccessRule - - application_credential_create = openstackclient.identity.v3.application_credential:CreateApplicationCredential - application_credential_delete = openstackclient.identity.v3.application_credential:DeleteApplicationCredential - application_credential_list = openstackclient.identity.v3.application_credential:ListApplicationCredential - application_credential_show = openstackclient.identity.v3.application_credential:ShowApplicationCredential - - catalog_list = openstackclient.identity.v3.catalog:ListCatalog - catalog_show = openstackclient.identity.v3.catalog:ShowCatalog - - consumer_create = openstackclient.identity.v3.consumer:CreateConsumer - consumer_delete = openstackclient.identity.v3.consumer:DeleteConsumer - consumer_list = openstackclient.identity.v3.consumer:ListConsumer - consumer_set = openstackclient.identity.v3.consumer:SetConsumer - consumer_show = openstackclient.identity.v3.consumer:ShowConsumer - - credential_create = openstackclient.identity.v3.credential:CreateCredential - credential_delete = openstackclient.identity.v3.credential:DeleteCredential - credential_list = openstackclient.identity.v3.credential:ListCredential - credential_set = openstackclient.identity.v3.credential:SetCredential - credential_show = openstackclient.identity.v3.credential:ShowCredential - - domain_create = openstackclient.identity.v3.domain:CreateDomain - domain_delete = openstackclient.identity.v3.domain:DeleteDomain - domain_list = openstackclient.identity.v3.domain:ListDomain - domain_set = openstackclient.identity.v3.domain:SetDomain - domain_show = openstackclient.identity.v3.domain:ShowDomain - - ec2_credentials_create = openstackclient.identity.v3.ec2creds:CreateEC2Creds - ec2_credentials_delete = openstackclient.identity.v3.ec2creds:DeleteEC2Creds - ec2_credentials_list = openstackclient.identity.v3.ec2creds:ListEC2Creds - ec2_credentials_show = openstackclient.identity.v3.ec2creds:ShowEC2Creds - - endpoint_add_project = openstackclient.identity.v3.endpoint:AddProjectToEndpoint - endpoint_create = openstackclient.identity.v3.endpoint:CreateEndpoint - endpoint_delete = openstackclient.identity.v3.endpoint:DeleteEndpoint - endpoint_list = openstackclient.identity.v3.endpoint:ListEndpoint - endpoint_remove_project = openstackclient.identity.v3.endpoint:RemoveProjectFromEndpoint - endpoint_set = openstackclient.identity.v3.endpoint:SetEndpoint - endpoint_show = openstackclient.identity.v3.endpoint:ShowEndpoint - - endpoint_group_add_project = openstackclient.identity.v3.endpoint_group:AddProjectToEndpointGroup - endpoint_group_create = openstackclient.identity.v3.endpoint_group:CreateEndpointGroup - endpoint_group_delete = openstackclient.identity.v3.endpoint_group:DeleteEndpointGroup - endpoint_group_list = openstackclient.identity.v3.endpoint_group:ListEndpointGroup - endpoint_group_remove_project = openstackclient.identity.v3.endpoint_group:RemoveProjectFromEndpointGroup - endpoint_group_set = openstackclient.identity.v3.endpoint_group:SetEndpointGroup - endpoint_group_show = openstackclient.identity.v3.endpoint_group:ShowEndpointGroup - - federation_domain_list = openstackclient.identity.v3.unscoped_saml:ListAccessibleDomains - federation_project_list = openstackclient.identity.v3.unscoped_saml:ListAccessibleProjects - - federation_protocol_create = openstackclient.identity.v3.federation_protocol:CreateProtocol - federation_protocol_delete = openstackclient.identity.v3.federation_protocol:DeleteProtocol - federation_protocol_list = openstackclient.identity.v3.federation_protocol:ListProtocols - federation_protocol_set = openstackclient.identity.v3.federation_protocol:SetProtocol - federation_protocol_show = openstackclient.identity.v3.federation_protocol:ShowProtocol - - group_add_user = openstackclient.identity.v3.group:AddUserToGroup - group_contains_user = openstackclient.identity.v3.group:CheckUserInGroup - group_create = openstackclient.identity.v3.group:CreateGroup - group_delete = openstackclient.identity.v3.group:DeleteGroup - group_list = openstackclient.identity.v3.group:ListGroup - group_remove_user = openstackclient.identity.v3.group:RemoveUserFromGroup - group_set = openstackclient.identity.v3.group:SetGroup - group_show = openstackclient.identity.v3.group:ShowGroup - - identity_provider_create = openstackclient.identity.v3.identity_provider:CreateIdentityProvider - identity_provider_delete = openstackclient.identity.v3.identity_provider:DeleteIdentityProvider - identity_provider_list = openstackclient.identity.v3.identity_provider:ListIdentityProvider - identity_provider_set = openstackclient.identity.v3.identity_provider:SetIdentityProvider - identity_provider_show = openstackclient.identity.v3.identity_provider:ShowIdentityProvider - - implied_role_create = openstackclient.identity.v3.implied_role:CreateImpliedRole - implied_role_delete = openstackclient.identity.v3.implied_role:DeleteImpliedRole - implied_role_list = openstackclient.identity.v3.implied_role:ListImpliedRole - - limit_create = openstackclient.identity.v3.limit:CreateLimit - limit_delete = openstackclient.identity.v3.limit:DeleteLimit - limit_list = openstackclient.identity.v3.limit:ListLimit - limit_set = openstackclient.identity.v3.limit:SetLimit - limit_show = openstackclient.identity.v3.limit:ShowLimit - - mapping_create = openstackclient.identity.v3.mapping:CreateMapping - mapping_delete = openstackclient.identity.v3.mapping:DeleteMapping - mapping_list = openstackclient.identity.v3.mapping:ListMapping - mapping_set = openstackclient.identity.v3.mapping:SetMapping - mapping_show = openstackclient.identity.v3.mapping:ShowMapping - - policy_create = openstackclient.identity.v3.policy:CreatePolicy - policy_delete = openstackclient.identity.v3.policy:DeletePolicy - policy_list = openstackclient.identity.v3.policy:ListPolicy - policy_set = openstackclient.identity.v3.policy:SetPolicy - policy_show = openstackclient.identity.v3.policy:ShowPolicy - - project_create = openstackclient.identity.v3.project:CreateProject - project_delete = openstackclient.identity.v3.project:DeleteProject - project_list = openstackclient.identity.v3.project:ListProject - project_set = openstackclient.identity.v3.project:SetProject - project_show = openstackclient.identity.v3.project:ShowProject - - region_create = openstackclient.identity.v3.region:CreateRegion - region_delete = openstackclient.identity.v3.region:DeleteRegion - region_list = openstackclient.identity.v3.region:ListRegion - region_set = openstackclient.identity.v3.region:SetRegion - region_show = openstackclient.identity.v3.region:ShowRegion - - registered_limit_create = openstackclient.identity.v3.registered_limit:CreateRegisteredLimit - registered_limit_delete = openstackclient.identity.v3.registered_limit:DeleteRegisteredLimit - registered_limit_list = openstackclient.identity.v3.registered_limit:ListRegisteredLimit - registered_limit_set = openstackclient.identity.v3.registered_limit:SetRegisteredLimit - registered_limit_show = openstackclient.identity.v3.registered_limit:ShowRegisteredLimit - - request_token_authorize = openstackclient.identity.v3.token:AuthorizeRequestToken - request_token_create = openstackclient.identity.v3.token:CreateRequestToken - - role_add = openstackclient.identity.v3.role:AddRole - role_create = openstackclient.identity.v3.role:CreateRole - role_delete = openstackclient.identity.v3.role:DeleteRole - role_list = openstackclient.identity.v3.role:ListRole - role_remove = openstackclient.identity.v3.role:RemoveRole - role_show = openstackclient.identity.v3.role:ShowRole - role_set = openstackclient.identity.v3.role:SetRole - role_assignment_list = openstackclient.identity.v3.role_assignment:ListRoleAssignment - - service_create = openstackclient.identity.v3.service:CreateService - service_delete = openstackclient.identity.v3.service:DeleteService - service_list = openstackclient.identity.v3.service:ListService - service_show = openstackclient.identity.v3.service:ShowService - service_set = openstackclient.identity.v3.service:SetService - - service_provider_create = openstackclient.identity.v3.service_provider:CreateServiceProvider - service_provider_delete = openstackclient.identity.v3.service_provider:DeleteServiceProvider - service_provider_list = openstackclient.identity.v3.service_provider:ListServiceProvider - service_provider_set = openstackclient.identity.v3.service_provider:SetServiceProvider - service_provider_show = openstackclient.identity.v3.service_provider:ShowServiceProvider - - token_issue = openstackclient.identity.v3.token:IssueToken - token_revoke = openstackclient.identity.v3.token:RevokeToken - - trust_create = openstackclient.identity.v3.trust:CreateTrust - trust_delete = openstackclient.identity.v3.trust:DeleteTrust - trust_list = openstackclient.identity.v3.trust:ListTrust - trust_show = openstackclient.identity.v3.trust:ShowTrust - - user_create = openstackclient.identity.v3.user:CreateUser - user_delete = openstackclient.identity.v3.user:DeleteUser - user_list = openstackclient.identity.v3.user:ListUser - user_set = openstackclient.identity.v3.user:SetUser - user_password_set = openstackclient.identity.v3.user:SetPasswordUser - user_show = openstackclient.identity.v3.user:ShowUser - -openstack.image.v1 = - image_create = openstackclient.image.v1.image:CreateImage - image_delete = openstackclient.image.v1.image:DeleteImage - image_list = openstackclient.image.v1.image:ListImage - image_save = openstackclient.image.v1.image:SaveImage - image_set = openstackclient.image.v1.image:SetImage - image_show = openstackclient.image.v1.image:ShowImage - -openstack.image.v2 = - image_add_project = openstackclient.image.v2.image:AddProjectToImage - image_create = openstackclient.image.v2.image:CreateImage - image_delete = openstackclient.image.v2.image:DeleteImage - image_list = openstackclient.image.v2.image:ListImage - image_member_list = openstackclient.image.v2.image:ListImageProjects - image_remove_project = openstackclient.image.v2.image:RemoveProjectImage - image_member_get = openstackclient.image.v2.image:ShowProjectImage - image_save = openstackclient.image.v2.image:SaveImage - image_show = openstackclient.image.v2.image:ShowImage - image_set = openstackclient.image.v2.image:SetImage - image_unset = openstackclient.image.v2.image:UnsetImage - image_stage = openstackclient.image.v2.image:StageImage - image_task_show = openstackclient.image.v2.task:ShowTask - image_task_list = openstackclient.image.v2.task:ListTask - image_import_info = openstackclient.image.v2.info:ImportInfo - image_import = openstackclient.image.v2.image:ImportImage - image_stores_list = openstackclient.image.v2.image:StoresInfo - - image_metadef_namespace_create = openstackclient.image.v2.metadef_namespaces:CreateMetadefNamespace - image_metadef_namespace_delete = openstackclient.image.v2.metadef_namespaces:DeleteMetadefNamespace - image_metadef_namespace_list = openstackclient.image.v2.metadef_namespaces:ListMetadefNamespace - image_metadef_namespace_set = openstackclient.image.v2.metadef_namespaces:SetMetadefNamespace - image_metadef_namespace_show = openstackclient.image.v2.metadef_namespaces:ShowMetadefNamespace - - image_metadef_object_create = openstackclient.image.v2.metadef_objects:CreateMetadefObjects - image_metadef_object_show = openstackclient.image.v2.metadef_objects:ShowMetadefObjects - image_metadef_object_list = openstackclient.image.v2.metadef_objects:ListMetadefObjects - image_metadef_object_delete = openstackclient.image.v2.metadef_objects:DeleteMetadefObject - image_metadef_object_update = openstackclient.image.v2.metadef_objects:SetMetadefObject - image_metadef_object_property_show = openstackclient.image.v2.metadef_objects:ShowMetadefObjectProperty - - image_metadef_property_create = openstackclient.image.v2.metadef_properties:CreateMetadefProperty - image_metadef_property_delete = openstackclient.image.v2.metadef_properties:DeleteMetadefProperty - image_metadef_property_list = openstackclient.image.v2.metadef_properties:ListMetadefProperties - image_metadef_property_set = openstackclient.image.v2.metadef_properties:SetMetadefProperty - image_metadef_property_show = openstackclient.image.v2.metadef_properties:ShowMetadefProperty - - image_metadef_resource_type_list = openstackclient.image.v2.metadef_resource_types:ListMetadefResourceTypes - image_metadef_resource_type_association_create = openstackclient.image.v2.metadef_resource_type_association:CreateMetadefResourceTypeAssociation - image_metadef_resource_type_association_delete = openstackclient.image.v2.metadef_resource_type_association:DeleteMetadefResourceTypeAssociation - image_metadef_resource_type_association_list = openstackclient.image.v2.metadef_resource_type_association:ListMetadefResourceTypeAssociations - - cached_image_list = openstackclient.image.v2.cache:ListCachedImage - cached_image_queue = openstackclient.image.v2.cache:QueueCachedImage - cached_image_delete = openstackclient.image.v2.cache:DeleteCachedImage - cached_image_clear = openstackclient.image.v2.cache:ClearCachedImage - -openstack.network.v2 = - address_group_create = openstackclient.network.v2.address_group:CreateAddressGroup - address_group_delete = openstackclient.network.v2.address_group:DeleteAddressGroup - address_group_list = openstackclient.network.v2.address_group:ListAddressGroup - address_group_set = openstackclient.network.v2.address_group:SetAddressGroup - address_group_show = openstackclient.network.v2.address_group:ShowAddressGroup - address_group_unset = openstackclient.network.v2.address_group:UnsetAddressGroup - - address_scope_create = openstackclient.network.v2.address_scope:CreateAddressScope - address_scope_delete = openstackclient.network.v2.address_scope:DeleteAddressScope - address_scope_list = openstackclient.network.v2.address_scope:ListAddressScope - address_scope_set = openstackclient.network.v2.address_scope:SetAddressScope - address_scope_show = openstackclient.network.v2.address_scope:ShowAddressScope - - floating_ip_create = openstackclient.network.v2.floating_ip:CreateFloatingIP - floating_ip_delete = openstackclient.network.v2.floating_ip:DeleteFloatingIP - floating_ip_list = openstackclient.network.v2.floating_ip:ListFloatingIP - floating_ip_set = openstackclient.network.v2.floating_ip:SetFloatingIP - floating_ip_show = openstackclient.network.v2.floating_ip:ShowFloatingIP - floating_ip_unset = openstackclient.network.v2.floating_ip:UnsetFloatingIP - - floating_ip_pool_list = openstackclient.network.v2.floating_ip_pool:ListFloatingIPPool - - floating_ip_port_forwarding_create = openstackclient.network.v2.floating_ip_port_forwarding:CreateFloatingIPPortForwarding - floating_ip_port_forwarding_delete = openstackclient.network.v2.floating_ip_port_forwarding:DeleteFloatingIPPortForwarding - floating_ip_port_forwarding_list = openstackclient.network.v2.floating_ip_port_forwarding:ListFloatingIPPortForwarding - floating_ip_port_forwarding_set = openstackclient.network.v2.floating_ip_port_forwarding:SetFloatingIPPortForwarding - floating_ip_port_forwarding_show = openstackclient.network.v2.floating_ip_port_forwarding:ShowFloatingIPPortForwarding - - ip_availability_list = openstackclient.network.v2.ip_availability:ListIPAvailability - ip_availability_show = openstackclient.network.v2.ip_availability:ShowIPAvailability - - local_ip_create = openstackclient.network.v2.local_ip:CreateLocalIP - local_ip_delete = openstackclient.network.v2.local_ip:DeleteLocalIP - local_ip_list = openstackclient.network.v2.local_ip:ListLocalIP - local_ip_set = openstackclient.network.v2.local_ip:SetLocalIP - local_ip_show = openstackclient.network.v2.local_ip:ShowLocalIP - - local_ip_association_create = openstackclient.network.v2.local_ip_association:CreateLocalIPAssociation - local_ip_association_delete = openstackclient.network.v2.local_ip_association:DeleteLocalIPAssociation - local_ip_association_list = openstackclient.network.v2.local_ip_association:ListLocalIPAssociation - - network_agent_add_network = openstackclient.network.v2.network_agent:AddNetworkToAgent - network_agent_add_router = openstackclient.network.v2.network_agent:AddRouterToAgent - network_agent_delete = openstackclient.network.v2.network_agent:DeleteNetworkAgent - network_agent_list = openstackclient.network.v2.network_agent:ListNetworkAgent - network_agent_remove_network = openstackclient.network.v2.network_agent:RemoveNetworkFromAgent - network_agent_remove_router = openstackclient.network.v2.network_agent:RemoveRouterFromAgent - network_agent_set = openstackclient.network.v2.network_agent:SetNetworkAgent - network_agent_show = openstackclient.network.v2.network_agent:ShowNetworkAgent - - network_auto_allocated_topology_create = openstackclient.network.v2.network_auto_allocated_topology:CreateAutoAllocatedTopology - network_auto_allocated_topology_delete = openstackclient.network.v2.network_auto_allocated_topology:DeleteAutoAllocatedTopology - - network_flavor_add_profile = openstackclient.network.v2.network_flavor:AddNetworkFlavorToProfile - network_flavor_create = openstackclient.network.v2.network_flavor:CreateNetworkFlavor - network_flavor_delete = openstackclient.network.v2.network_flavor:DeleteNetworkFlavor - network_flavor_list = openstackclient.network.v2.network_flavor:ListNetworkFlavor - network_flavor_remove_profile = openstackclient.network.v2.network_flavor:RemoveNetworkFlavorFromProfile - network_flavor_set = openstackclient.network.v2.network_flavor:SetNetworkFlavor - network_flavor_show = openstackclient.network.v2.network_flavor:ShowNetworkFlavor - - network_flavor_profile_create = openstackclient.network.v2.network_flavor_profile:CreateNetworkFlavorProfile - network_flavor_profile_delete = openstackclient.network.v2.network_flavor_profile:DeleteNetworkFlavorProfile - network_flavor_profile_list = openstackclient.network.v2.network_flavor_profile:ListNetworkFlavorProfile - network_flavor_profile_set = openstackclient.network.v2.network_flavor_profile:SetNetworkFlavorProfile - network_flavor_profile_show = openstackclient.network.v2.network_flavor_profile:ShowNetworkFlavorProfile - - network_create = openstackclient.network.v2.network:CreateNetwork - network_delete = openstackclient.network.v2.network:DeleteNetwork - network_list = openstackclient.network.v2.network:ListNetwork - network_set = openstackclient.network.v2.network:SetNetwork - network_show = openstackclient.network.v2.network:ShowNetwork - network_unset = openstackclient.network.v2.network:UnsetNetwork - - network_l3_conntrack_helper_create = openstackclient.network.v2.l3_conntrack_helper:CreateConntrackHelper - network_l3_conntrack_helper_delete = openstackclient.network.v2.l3_conntrack_helper:DeleteConntrackHelper - network_l3_conntrack_helper_list = openstackclient.network.v2.l3_conntrack_helper:ListConntrackHelper - network_l3_conntrack_helper_set = openstackclient.network.v2.l3_conntrack_helper:SetConntrackHelper - network_l3_conntrack_helper_show = openstackclient.network.v2.l3_conntrack_helper:ShowConntrackHelper - - network_meter_create = openstackclient.network.v2.network_meter:CreateMeter - network_meter_delete = openstackclient.network.v2.network_meter:DeleteMeter - network_meter_list = openstackclient.network.v2.network_meter:ListMeter - network_meter_show = openstackclient.network.v2.network_meter:ShowMeter - - network_meter_rule_create = openstackclient.network.v2.network_meter_rule:CreateMeterRule - network_meter_rule_delete = openstackclient.network.v2.network_meter_rule:DeleteMeterRule - network_meter_rule_list = openstackclient.network.v2.network_meter_rule:ListMeterRule - network_meter_rule_show = openstackclient.network.v2.network_meter_rule:ShowMeterRule - - network_qos_policy_create = openstackclient.network.v2.network_qos_policy:CreateNetworkQosPolicy - network_qos_policy_delete = openstackclient.network.v2.network_qos_policy:DeleteNetworkQosPolicy - network_qos_policy_list = openstackclient.network.v2.network_qos_policy:ListNetworkQosPolicy - network_qos_policy_set = openstackclient.network.v2.network_qos_policy:SetNetworkQosPolicy - network_qos_policy_show = openstackclient.network.v2.network_qos_policy:ShowNetworkQosPolicy - - network_qos_rule_create = openstackclient.network.v2.network_qos_rule:CreateNetworkQosRule - network_qos_rule_delete = openstackclient.network.v2.network_qos_rule:DeleteNetworkQosRule - network_qos_rule_list = openstackclient.network.v2.network_qos_rule:ListNetworkQosRule - network_qos_rule_set = openstackclient.network.v2.network_qos_rule:SetNetworkQosRule - network_qos_rule_show = openstackclient.network.v2.network_qos_rule:ShowNetworkQosRule - - network_qos_rule_type_list = openstackclient.network.v2.network_qos_rule_type:ListNetworkQosRuleType - network_qos_rule_type_show = openstackclient.network.v2.network_qos_rule_type:ShowNetworkQosRuleType - - network_rbac_create = openstackclient.network.v2.network_rbac:CreateNetworkRBAC - network_rbac_delete = openstackclient.network.v2.network_rbac:DeleteNetworkRBAC - network_rbac_list = openstackclient.network.v2.network_rbac:ListNetworkRBAC - network_rbac_set = openstackclient.network.v2.network_rbac:SetNetworkRBAC - network_rbac_show = openstackclient.network.v2.network_rbac:ShowNetworkRBAC - - network_segment_create = openstackclient.network.v2.network_segment:CreateNetworkSegment - network_segment_delete = openstackclient.network.v2.network_segment:DeleteNetworkSegment - network_segment_list = openstackclient.network.v2.network_segment:ListNetworkSegment - network_segment_set = openstackclient.network.v2.network_segment:SetNetworkSegment - network_segment_show = openstackclient.network.v2.network_segment:ShowNetworkSegment - - network_segment_range_create = openstackclient.network.v2.network_segment_range:CreateNetworkSegmentRange - network_segment_range_delete = openstackclient.network.v2.network_segment_range:DeleteNetworkSegmentRange - network_segment_range_list = openstackclient.network.v2.network_segment_range:ListNetworkSegmentRange - network_segment_range_set = openstackclient.network.v2.network_segment_range:SetNetworkSegmentRange - network_segment_range_show = openstackclient.network.v2.network_segment_range:ShowNetworkSegmentRange - - network_service_provider_list = openstackclient.network.v2.network_service_provider:ListNetworkServiceProvider - - network_subport_list = openstackclient.network.v2.network_trunk:ListNetworkSubport - network_trunk_create = openstackclient.network.v2.network_trunk:CreateNetworkTrunk - network_trunk_delete = openstackclient.network.v2.network_trunk:DeleteNetworkTrunk - network_trunk_list = openstackclient.network.v2.network_trunk:ListNetworkTrunk - network_trunk_set = openstackclient.network.v2.network_trunk:SetNetworkTrunk - network_trunk_show = openstackclient.network.v2.network_trunk:ShowNetworkTrunk - network_trunk_unset = openstackclient.network.v2.network_trunk:UnsetNetworkTrunk - - port_create = openstackclient.network.v2.port:CreatePort - port_delete = openstackclient.network.v2.port:DeletePort - port_list = openstackclient.network.v2.port:ListPort - port_set = openstackclient.network.v2.port:SetPort - port_show = openstackclient.network.v2.port:ShowPort - port_unset = openstackclient.network.v2.port:UnsetPort - - router_add_gateway = openstackclient.network.v2.router:AddGatewayToRouter - router_add_port = openstackclient.network.v2.router:AddPortToRouter - router_add_route = openstackclient.network.v2.router:AddExtraRoutesToRouter - router_add_subnet = openstackclient.network.v2.router:AddSubnetToRouter - router_create = openstackclient.network.v2.router:CreateRouter - router_delete = openstackclient.network.v2.router:DeleteRouter - router_list = openstackclient.network.v2.router:ListRouter - router_remove_gateway = openstackclient.network.v2.router:RemoveGatewayFromRouter - router_remove_port = openstackclient.network.v2.router:RemovePortFromRouter - router_remove_route = openstackclient.network.v2.router:RemoveExtraRoutesFromRouter - router_remove_subnet = openstackclient.network.v2.router:RemoveSubnetFromRouter - router_set = openstackclient.network.v2.router:SetRouter - router_show = openstackclient.network.v2.router:ShowRouter - router_unset = openstackclient.network.v2.router:UnsetRouter - - router_ndp_proxy_create = openstackclient.network.v2.ndp_proxy:CreateNDPProxy - router_ndp_proxy_delete = openstackclient.network.v2.ndp_proxy:DeleteNDPProxy - router_ndp_proxy_list = openstackclient.network.v2.ndp_proxy:ListNDPProxy - router_ndp_proxy_set = openstackclient.network.v2.ndp_proxy:SetNDPProxy - router_ndp_proxy_show = openstackclient.network.v2.ndp_proxy:ShowNDPProxy - - security_group_create = openstackclient.network.v2.security_group:CreateSecurityGroup - security_group_delete = openstackclient.network.v2.security_group:DeleteSecurityGroup - security_group_list = openstackclient.network.v2.security_group:ListSecurityGroup - security_group_set = openstackclient.network.v2.security_group:SetSecurityGroup - security_group_show = openstackclient.network.v2.security_group:ShowSecurityGroup - security_group_unset = openstackclient.network.v2.security_group:UnsetSecurityGroup - - security_group_rule_create = openstackclient.network.v2.security_group_rule:CreateSecurityGroupRule - security_group_rule_delete = openstackclient.network.v2.security_group_rule:DeleteSecurityGroupRule - security_group_rule_list = openstackclient.network.v2.security_group_rule:ListSecurityGroupRule - security_group_rule_show = openstackclient.network.v2.security_group_rule:ShowSecurityGroupRule - - default_security_group_rule_create = openstackclient.network.v2.default_security_group_rule:CreateDefaultSecurityGroupRule - default_security_group_rule_delete = openstackclient.network.v2.default_security_group_rule:DeleteDefaultSecurityGroupRule - default_security_group_rule_list = openstackclient.network.v2.default_security_group_rule:ListDefaultSecurityGroupRule - default_security_group_rule_show = openstackclient.network.v2.default_security_group_rule:ShowDefaultSecurityGroupRule - - subnet_create = openstackclient.network.v2.subnet:CreateSubnet - subnet_delete = openstackclient.network.v2.subnet:DeleteSubnet - subnet_list = openstackclient.network.v2.subnet:ListSubnet - subnet_set = openstackclient.network.v2.subnet:SetSubnet - subnet_show = openstackclient.network.v2.subnet:ShowSubnet - subnet_unset = openstackclient.network.v2.subnet:UnsetSubnet - - subnet_pool_create = openstackclient.network.v2.subnet_pool:CreateSubnetPool - subnet_pool_delete = openstackclient.network.v2.subnet_pool:DeleteSubnetPool - subnet_pool_list = openstackclient.network.v2.subnet_pool:ListSubnetPool - subnet_pool_set = openstackclient.network.v2.subnet_pool:SetSubnetPool - subnet_pool_show = openstackclient.network.v2.subnet_pool:ShowSubnetPool - subnet_pool_unset = openstackclient.network.v2.subnet_pool:UnsetSubnetPool - -openstack.object_store.v1 = - object_store_account_set = openstackclient.object.v1.account:SetAccount - object_store_account_show = openstackclient.object.v1.account:ShowAccount - object_store_account_unset = openstackclient.object.v1.account:UnsetAccount - container_create = openstackclient.object.v1.container:CreateContainer - container_delete = openstackclient.object.v1.container:DeleteContainer - container_list = openstackclient.object.v1.container:ListContainer - container_save = openstackclient.object.v1.container:SaveContainer - container_set = openstackclient.object.v1.container:SetContainer - container_show = openstackclient.object.v1.container:ShowContainer - container_unset = openstackclient.object.v1.container:UnsetContainer - object_create = openstackclient.object.v1.object:CreateObject - object_delete = openstackclient.object.v1.object:DeleteObject - object_list = openstackclient.object.v1.object:ListObject - object_save = openstackclient.object.v1.object:SaveObject - object_set = openstackclient.object.v1.object:SetObject - object_show = openstackclient.object.v1.object:ShowObject - object_unset = openstackclient.object.v1.object:UnsetObject - -openstack.volume.v2 = - consistency_group_add_volume = openstackclient.volume.v2.consistency_group:AddVolumeToConsistencyGroup - consistency_group_create = openstackclient.volume.v2.consistency_group:CreateConsistencyGroup - consistency_group_delete = openstackclient.volume.v2.consistency_group:DeleteConsistencyGroup - consistency_group_list = openstackclient.volume.v2.consistency_group:ListConsistencyGroup - consistency_group_remove_volume = openstackclient.volume.v2.consistency_group:RemoveVolumeFromConsistencyGroup - consistency_group_set = openstackclient.volume.v2.consistency_group:SetConsistencyGroup - consistency_group_show = openstackclient.volume.v2.consistency_group:ShowConsistencyGroup - - consistency_group_snapshot_create = openstackclient.volume.v2.consistency_group_snapshot:CreateConsistencyGroupSnapshot - consistency_group_snapshot_delete = openstackclient.volume.v2.consistency_group_snapshot:DeleteConsistencyGroupSnapshot - consistency_group_snapshot_list = openstackclient.volume.v2.consistency_group_snapshot:ListConsistencyGroupSnapshot - consistency_group_snapshot_show = openstackclient.volume.v2.consistency_group_snapshot:ShowConsistencyGroupSnapshot - - 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 - - volume_backup_create = openstackclient.volume.v2.volume_backup:CreateVolumeBackup - volume_backup_delete = openstackclient.volume.v2.volume_backup:DeleteVolumeBackup - volume_backup_list = openstackclient.volume.v2.volume_backup:ListVolumeBackup - volume_backup_restore = openstackclient.volume.v2.volume_backup:RestoreVolumeBackup - volume_backup_set = openstackclient.volume.v2.volume_backup:SetVolumeBackup - volume_backup_show = openstackclient.volume.v2.volume_backup:ShowVolumeBackup - - volume_backup_record_export = openstackclient.volume.v2.backup_record:ExportBackupRecord - volume_backup_record_import = openstackclient.volume.v2.backup_record:ImportBackupRecord - - volume_backend_capability_show = openstackclient.volume.v2.volume_backend:ShowCapability - volume_backend_pool_list = openstackclient.volume.v2.volume_backend:ListPool - - volume_host_failover = openstackclient.volume.v2.volume_host:FailoverVolumeHost - volume_host_set = openstackclient.volume.v2.volume_host:SetVolumeHost - - volume_snapshot_create = openstackclient.volume.v2.volume_snapshot:CreateVolumeSnapshot - volume_snapshot_delete = openstackclient.volume.v2.volume_snapshot:DeleteVolumeSnapshot - volume_snapshot_list = openstackclient.volume.v2.volume_snapshot:ListVolumeSnapshot - volume_snapshot_set = openstackclient.volume.v2.volume_snapshot:SetVolumeSnapshot - volume_snapshot_show = openstackclient.volume.v2.volume_snapshot:ShowVolumeSnapshot - volume_snapshot_unset = openstackclient.volume.v2.volume_snapshot:UnsetVolumeSnapshot - - volume_type_create = openstackclient.volume.v2.volume_type:CreateVolumeType - volume_type_delete = openstackclient.volume.v2.volume_type:DeleteVolumeType - volume_type_list = openstackclient.volume.v2.volume_type:ListVolumeType - volume_type_set = openstackclient.volume.v2.volume_type:SetVolumeType - volume_type_show = openstackclient.volume.v2.volume_type:ShowVolumeType - volume_type_unset = openstackclient.volume.v2.volume_type:UnsetVolumeType - - volume_qos_associate = openstackclient.volume.v2.qos_specs:AssociateQos - volume_qos_create = openstackclient.volume.v2.qos_specs:CreateQos - volume_qos_delete = openstackclient.volume.v2.qos_specs:DeleteQos - volume_qos_disassociate = openstackclient.volume.v2.qos_specs:DisassociateQos - volume_qos_list = openstackclient.volume.v2.qos_specs:ListQos - volume_qos_set = openstackclient.volume.v2.qos_specs:SetQos - volume_qos_show = openstackclient.volume.v2.qos_specs:ShowQos - volume_qos_unset = openstackclient.volume.v2.qos_specs:UnsetQos - - volume_service_list = openstackclient.volume.v2.service:ListService - volume_service_set = openstackclient.volume.v2.service:SetService - - volume_transfer_request_accept = openstackclient.volume.v2.volume_transfer_request:AcceptTransferRequest - 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:ListTransferRequest - volume_transfer_request_show = openstackclient.volume.v2.volume_transfer_request:ShowTransferRequest - -openstack.volume.v3 = - consistency_group_add_volume = openstackclient.volume.v2.consistency_group:AddVolumeToConsistencyGroup - consistency_group_create = openstackclient.volume.v2.consistency_group:CreateConsistencyGroup - consistency_group_delete = openstackclient.volume.v2.consistency_group:DeleteConsistencyGroup - consistency_group_list = openstackclient.volume.v2.consistency_group:ListConsistencyGroup - consistency_group_remove_volume = openstackclient.volume.v2.consistency_group:RemoveVolumeFromConsistencyGroup - consistency_group_set = openstackclient.volume.v2.consistency_group:SetConsistencyGroup - consistency_group_show = openstackclient.volume.v2.consistency_group:ShowConsistencyGroup - - consistency_group_snapshot_create = openstackclient.volume.v2.consistency_group_snapshot:CreateConsistencyGroupSnapshot - consistency_group_snapshot_delete = openstackclient.volume.v2.consistency_group_snapshot:DeleteConsistencyGroupSnapshot - consistency_group_snapshot_list = openstackclient.volume.v2.consistency_group_snapshot:ListConsistencyGroupSnapshot - consistency_group_snapshot_show = openstackclient.volume.v2.consistency_group_snapshot:ShowConsistencyGroupSnapshot - - volume_create = openstackclient.volume.v3.volume:CreateVolume - volume_delete = openstackclient.volume.v3.volume:DeleteVolume - volume_list = openstackclient.volume.v3.volume:ListVolume - volume_migrate = openstackclient.volume.v3.volume:MigrateVolume - volume_set = openstackclient.volume.v3.volume:SetVolume - volume_show = openstackclient.volume.v3.volume:ShowVolume - volume_unset = openstackclient.volume.v3.volume:UnsetVolume - - volume_attachment_create = openstackclient.volume.v3.volume_attachment:CreateVolumeAttachment - volume_attachment_delete = openstackclient.volume.v3.volume_attachment:DeleteVolumeAttachment - volume_attachment_list = openstackclient.volume.v3.volume_attachment:ListVolumeAttachment - volume_attachment_complete = openstackclient.volume.v3.volume_attachment:CompleteVolumeAttachment - volume_attachment_set = openstackclient.volume.v3.volume_attachment:SetVolumeAttachment - volume_attachment_show = openstackclient.volume.v3.volume_attachment:ShowVolumeAttachment - - volume_backup_create = openstackclient.volume.v3.volume_backup:CreateVolumeBackup - volume_backup_delete = openstackclient.volume.v3.volume_backup:DeleteVolumeBackup - volume_backup_list = openstackclient.volume.v3.volume_backup:ListVolumeBackup - volume_backup_restore = openstackclient.volume.v3.volume_backup:RestoreVolumeBackup - volume_backup_set = openstackclient.volume.v3.volume_backup:SetVolumeBackup - volume_backup_unset = openstackclient.volume.v3.volume_backup:UnsetVolumeBackup - volume_backup_show = openstackclient.volume.v3.volume_backup:ShowVolumeBackup - - volume_backend_capability_show = openstackclient.volume.v2.volume_backend:ShowCapability - volume_backend_pool_list = openstackclient.volume.v2.volume_backend:ListPool - - volume_backup_record_export = openstackclient.volume.v2.backup_record:ExportBackupRecord - volume_backup_record_import = openstackclient.volume.v2.backup_record:ImportBackupRecord - - volume_group_create = openstackclient.volume.v3.volume_group:CreateVolumeGroup - volume_group_delete = openstackclient.volume.v3.volume_group:DeleteVolumeGroup - volume_group_list = openstackclient.volume.v3.volume_group:ListVolumeGroup - volume_group_failover = openstackclient.volume.v3.volume_group:FailoverVolumeGroup - volume_group_set = openstackclient.volume.v3.volume_group:SetVolumeGroup - volume_group_show = openstackclient.volume.v3.volume_group:ShowVolumeGroup - - volume_group_snapshot_create = openstackclient.volume.v3.volume_group_snapshot:CreateVolumeGroupSnapshot - volume_group_snapshot_delete = openstackclient.volume.v3.volume_group_snapshot:DeleteVolumeGroupSnapshot - volume_group_snapshot_list = openstackclient.volume.v3.volume_group_snapshot:ListVolumeGroupSnapshot - volume_group_snapshot_show = openstackclient.volume.v3.volume_group_snapshot:ShowVolumeGroupSnapshot - - volume_group_type_create = openstackclient.volume.v3.volume_group_type:CreateVolumeGroupType - volume_group_type_delete = openstackclient.volume.v3.volume_group_type:DeleteVolumeGroupType - volume_group_type_list = openstackclient.volume.v3.volume_group_type:ListVolumeGroupType - volume_group_type_set = openstackclient.volume.v3.volume_group_type:SetVolumeGroupType - volume_group_type_show = openstackclient.volume.v3.volume_group_type:ShowVolumeGroupType - - volume_host_set = openstackclient.volume.v2.volume_host:SetVolumeHost - - volume_message_delete = openstackclient.volume.v3.volume_message:DeleteMessage - volume_message_list = openstackclient.volume.v3.volume_message:ListMessages - volume_message_show = openstackclient.volume.v3.volume_message:ShowMessage - - block_storage_cluster_list = openstackclient.volume.v3.block_storage_cluster:ListBlockStorageCluster - block_storage_cluster_set = openstackclient.volume.v3.block_storage_cluster:SetBlockStorageCluster - block_storage_cluster_show = openstackclient.volume.v3.block_storage_cluster:ShowBlockStorageCluster - block_storage_resource_filter_list = openstackclient.volume.v3.block_storage_resource_filter:ListBlockStorageResourceFilter - block_storage_resource_filter_show = openstackclient.volume.v3.block_storage_resource_filter:ShowBlockStorageResourceFilter - - volume_snapshot_create = openstackclient.volume.v3.volume_snapshot:CreateVolumeSnapshot - volume_snapshot_delete = openstackclient.volume.v3.volume_snapshot:DeleteVolumeSnapshot - volume_snapshot_list = openstackclient.volume.v3.volume_snapshot:ListVolumeSnapshot - volume_snapshot_set = openstackclient.volume.v3.volume_snapshot:SetVolumeSnapshot - volume_snapshot_show = openstackclient.volume.v3.volume_snapshot:ShowVolumeSnapshot - volume_snapshot_unset = openstackclient.volume.v3.volume_snapshot:UnsetVolumeSnapshot - - volume_type_create = openstackclient.volume.v3.volume_type:CreateVolumeType - volume_type_delete = openstackclient.volume.v3.volume_type:DeleteVolumeType - volume_type_list = openstackclient.volume.v3.volume_type:ListVolumeType - volume_type_set = openstackclient.volume.v3.volume_type:SetVolumeType - volume_type_show = openstackclient.volume.v3.volume_type:ShowVolumeType - volume_type_unset = openstackclient.volume.v3.volume_type:UnsetVolumeType - - volume_qos_associate = openstackclient.volume.v2.qos_specs:AssociateQos - volume_qos_create = openstackclient.volume.v2.qos_specs:CreateQos - volume_qos_delete = openstackclient.volume.v2.qos_specs:DeleteQos - volume_qos_disassociate = openstackclient.volume.v2.qos_specs:DisassociateQos - volume_qos_list = openstackclient.volume.v2.qos_specs:ListQos - volume_qos_set = openstackclient.volume.v2.qos_specs:SetQos - volume_qos_show = openstackclient.volume.v2.qos_specs:ShowQos - volume_qos_unset = openstackclient.volume.v2.qos_specs:UnsetQos - - volume_service_list = openstackclient.volume.v3.service:ListService - volume_service_set = openstackclient.volume.v3.service:SetService - - volume_transfer_request_accept = openstackclient.volume.v3.volume_transfer_request:AcceptTransferRequest - volume_transfer_request_create = openstackclient.volume.v3.volume_transfer_request:CreateTransferRequest - volume_transfer_request_delete = openstackclient.volume.v3.volume_transfer_request:DeleteTransferRequest - volume_transfer_request_list = openstackclient.volume.v3.volume_transfer_request:ListTransferRequest - volume_transfer_request_show = openstackclient.volume.v3.volume_transfer_request:ShowTransferRequest - - volume_summary = openstackclient.volume.v3.volume:VolumeSummary - volume_revert = openstackclient.volume.v3.volume:VolumeRevertToSnapshot - block_storage_log_level_list = openstackclient.volume.v3.block_storage_log_level:BlockStorageLogLevelList - block_storage_log_level_set = openstackclient.volume.v3.block_storage_log_level:BlockStorageLogLevelSet - block_storage_cleanup = openstackclient.volume.v3.block_storage_cleanup:BlockStorageCleanup - block_storage_volume_manageable_list = openstackclient.volume.v3.block_storage_manage:BlockStorageManageVolumes - block_storage_snapshot_manageable_list = openstackclient.volume.v3.block_storage_manage:BlockStorageManageSnapshots From 4ea3deda61c8f0e7f14f0b1408a4929abeb30017 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 27 Jun 2025 09:18:19 +0100 Subject: [PATCH 174/245] Remove duplicate Python version declarations ruff can automatically detect this [1] now that it is defined in pyproject.toml. mypy defaults to the version of Python used to run mypy [2][3] so we need to keep its configuration around a while longer. [1] https://docs.astral.sh/ruff/settings/#target-version [2] https://mypy.readthedocs.io/en/stable/config_file.html#confval-python_version [3] https://github.com/python/mypy/issues/19349 Change-Id: If5b3e6ff2483d73d5ff54cc28c1558cb9852b464 Signed-off-by: Stephen Finucane --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 404bba04d..74f93ccb2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -742,7 +742,6 @@ ignore_errors = true [tool.ruff] line-length = 79 -target-version = "py310" [tool.ruff.format] quote-style = "preserve" From b6af7883b7f493f01142fe6774dab434454a080a Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Fri, 27 Jun 2025 23:46:22 +0900 Subject: [PATCH 175/245] Replace deprecated assertItemsEqual It has been provided by testtools to ease migration from python 2, but was deprecated in 2.7.2[1] and will be removed in 2.8.0[2]. [1] https://github.com/testing-cabal/testtools/commit/e0d56b7ce65ae5b3d [2] https://github.com/testing-cabal/testtools/commit/f01e86084e6a858d1 Change-Id: I8b68212a88553aff5c3b4182c246b3c0f7365cf6 --- openstackclient/tests/unit/network/v2/test_ndp_proxy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py index 13d3f3d99..f9d9a3b3c 100644 --- a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py +++ b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py @@ -311,7 +311,7 @@ def test_ndp_proxy_list_project(self): **{'project_id': project.id} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_ndp_proxy_list_project_domain(self): project = identity_fakes_v3.FakeProject.create_one_project() @@ -332,7 +332,7 @@ def test_ndp_proxy_list_project_domain(self): self.network_client.ndp_proxies.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) class TestSetNDPProxy(TestNDPProxy): From 572eeb6d38f92669580b34d0d0a2b217fc999179 Mon Sep 17 00:00:00 2001 From: ohjiwoo Date: Wed, 2 Jul 2025 10:15:29 +0900 Subject: [PATCH 176/245] Add metadata as a filter condition when listing volumes When listing volumes, the API provides metadata as a query parameter, but this feature is not implemented in the client code. So add parameter. story: 2011487 task: 52446 Change-Id: I4bf66d4e073c86296fa96ee29c2b33d771e18293 Signed-off-by: ohjiwoo --- openstackclient/tests/unit/volume/v3/test_volume.py | 11 +++++++++++ openstackclient/volume/v3/volume.py | 11 +++++++++++ ...-volume-list-property-option-62008dc24762663b.yaml | 3 +++ 3 files changed, 25 insertions(+) create mode 100644 releasenotes/notes/add-volume-list-property-option-62008dc24762663b.yaml diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index 359e3d06a..fe210280a 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -1239,6 +1239,7 @@ def test_volume_list_no_options(self): 'user_id': None, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1282,6 +1283,7 @@ def test_volume_list_project(self): 'user_id': None, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1328,6 +1330,7 @@ def test_volume_list_project_domain(self): 'user_id': None, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1371,6 +1374,7 @@ def test_volume_list_user(self): 'user_id': self.user.id, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1416,6 +1420,7 @@ def test_volume_list_user_domain(self): 'user_id': self.user.id, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1459,6 +1464,7 @@ def test_volume_list_name(self): 'user_id': None, 'name': self.mock_volume.name, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1502,6 +1508,7 @@ def test_volume_list_status(self): 'user_id': None, 'name': None, 'status': self.mock_volume.status, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1544,6 +1551,7 @@ def test_volume_list_all_projects(self): 'user_id': None, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1587,6 +1595,7 @@ def test_volume_list_long(self): 'user_id': None, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, @@ -1660,6 +1669,7 @@ def test_volume_list_with_marker_and_limit(self): 'user_id': None, 'name': None, 'all_tenants': False, + 'metadata': None, }, ) self.assertCountEqual(datalist, tuple(data)) @@ -1704,6 +1714,7 @@ def test_volume_list_backward_compatibility(self): 'user_id': None, 'name': None, 'status': None, + 'metadata': None, } self.volumes_mock.list.assert_called_once_with( search_opts=search_opts, diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 6852944ec..c59e413a2 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -574,6 +574,16 @@ def get_parser(self, prog_name): metavar='', help=_('Filter results by status'), ) + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + dest='properties', + help=_( + 'Filter by a property on the volume list ' + '(repeat option to filter by multiple properties) ' + ), + ) parser.add_argument( '--all-projects', action='store_true', @@ -642,6 +652,7 @@ def take_action(self, parsed_args): 'user_id': user_id, 'name': parsed_args.name, 'status': parsed_args.status, + 'metadata': parsed_args.properties, } data = volume_client.volumes.list( diff --git a/releasenotes/notes/add-volume-list-property-option-62008dc24762663b.yaml b/releasenotes/notes/add-volume-list-property-option-62008dc24762663b.yaml new file mode 100644 index 000000000..ab4b60025 --- /dev/null +++ b/releasenotes/notes/add-volume-list-property-option-62008dc24762663b.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add ``--property`` option to ``volume list`` command to filter volumes. From 1b2dfeacf49e14bb97449f3861a976351153057e Mon Sep 17 00:00:00 2001 From: elajkat Date: Wed, 21 May 2025 09:08:57 +0200 Subject: [PATCH 177/245] Add is_shared to security_groups Change-Id: I1ff4aa9c6579699e6b479fdb429668de894cd012 Related-Bug: #1999774 Signed-off-by: elajkat --- openstackclient/network/v2/security_group.py | 52 ++++++++++++++++--- .../tests/unit/network/v2/fakes.py | 1 + .../network/v2/test_security_group_network.py | 6 +++ requirements.txt | 2 +- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index ceca341f8..6fb98c6f6 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -90,10 +90,7 @@ def _get_columns(item): column_map = { 'security_group_rules': 'rules', } - # FIXME(lajoskatona): Stop hiding is_shared when - # https://review.opendev.org/c/openstack/openstacksdk/+/950305 - # is released and SDK version is bumped - hidden_columns = ['location', 'tenant_id', 'is_shared'] + hidden_columns = ['location', 'tenant_id'] return utils.get_osc_show_columns_for_sdk_resource( item, column_map, hidden_columns ) @@ -225,7 +222,14 @@ def take_action_compute(self, client, parsed_args): # the OSC minimum requirements include SDK 1.0. class ListSecurityGroup(common.NetworkAndComputeLister): _description = _("List security groups") - FIELDS_TO_RETRIEVE = ['id', 'name', 'description', 'project_id', 'tags'] + FIELDS_TO_RETRIEVE = [ + 'id', + 'name', + 'description', + 'project_id', + 'tags', + 'shared', + ] def update_parser_network(self, parser): if not self.is_docs_build: @@ -248,6 +252,23 @@ def update_parser_network(self, parser): identity_common.add_project_domain_option_to_parser( parser, enhance_help=self.enhance_help_neutron ) + + shared_group = parser.add_mutually_exclusive_group() + shared_group.add_argument( + '--share', + action='store_true', + dest='shared', + default=None, + help=_("List security groups shared between projects"), + ) + shared_group.add_argument( + '--no-share', + action='store_false', + dest='shared', + default=None, + help=_("List security groups not shared between projects"), + ) + _tag.add_tag_filtering_option_to_parser( parser, _('security group'), enhance_help=self.enhance_help_neutron ) @@ -275,13 +296,30 @@ def take_action_network(self, client, parsed_args): ).id filters['project_id'] = project_id + if parsed_args.shared is not None: + filters['shared'] = parsed_args.shared + _tag.get_tag_filtering_args(parsed_args, filters) data = client.security_groups( fields=self.FIELDS_TO_RETRIEVE, **filters ) - columns = ("id", "name", "description", "project_id", "tags") - column_headers = ("ID", "Name", "Description", "Project", "Tags") + columns = ( + "id", + "name", + "description", + "project_id", + "tags", + "is_shared", + ) + column_headers = ( + "ID", + "Name", + "Description", + "Project", + "Tags", + "Shared", + ) return ( column_headers, ( diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index d58f6e190..16bbfa5ea 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -1412,6 +1412,7 @@ def create_one_security_group(attrs=None): 'security_group_rules': [], 'tags': [], 'location': 'MUNCHMUNCHMUNCH', + 'is_shared': False, } # Overwrite default attributes. diff --git a/openstackclient/tests/unit/network/v2/test_security_group_network.py b/openstackclient/tests/unit/network/v2/test_security_group_network.py index 62b87642c..9bf1f672f 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py @@ -42,6 +42,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): 'created_at', 'description', 'id', + 'is_shared', 'name', 'project_id', 'revision_number', @@ -55,6 +56,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): _security_group.created_at, _security_group.description, _security_group.id, + _security_group.is_shared, _security_group.name, _security_group.project_id, _security_group.revision_number, @@ -274,6 +276,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork): 'Description', 'Project', 'Tags', + 'Shared', ) data = [] @@ -285,6 +288,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork): grp.description, grp.project_id, grp.tags, + grp.is_shared, ) ) @@ -524,6 +528,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork): 'created_at', 'description', 'id', + 'is_shared', 'name', 'project_id', 'revision_number', @@ -537,6 +542,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork): _security_group.created_at, _security_group.description, _security_group.id, + _security_group.is_shared, _security_group.name, _security_group.project_id, _security_group.revision_number, diff --git a/requirements.txt b/requirements.txt index 5e9b33d27..522502a37 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 cryptography>=2.7 # BSD/Apache-2.0 cliff>=3.5.0 # Apache-2.0 iso8601>=0.1.11 # MIT -openstacksdk>=4.5.0 # Apache-2.0 +openstacksdk>=4.6.0 # Apache-2.0 osc-lib>=2.3.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 python-keystoneclient>=3.22.0 # Apache-2.0 From 51ecb5f98486a38b535a3664f0957c30eec27abf Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Tue, 15 Jul 2025 13:20:44 -0500 Subject: [PATCH 178/245] volume: fix volume service set call The command line operation could never work due to the incorrect call of the openstacksdk API. This is updated to make it work and report errors back to the user. Closes-Bug: #2116969 Change-Id: I87cc410853c03b00dd1549d67cb1b9a8145bcfaa Signed-off-by: Doug Goldstein --- openstackclient/volume/v2/service.py | 2 +- openstackclient/volume/v3/service.py | 2 +- .../notes/volume-service-set-fix-345a8bc84267f743.yaml | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/volume-service-set-fix-345a8bc84267f743.yaml diff --git a/openstackclient/volume/v2/service.py b/openstackclient/volume/v2/service.py index 418ecec42..48cdba721 100644 --- a/openstackclient/volume/v2/service.py +++ b/openstackclient/volume/v2/service.py @@ -126,7 +126,7 @@ def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume service = volume_client.find_service( - host=parsed_args.host, service=parsed_args.service + parsed_args.service, ignore_missing=False, host=parsed_args.host ) if parsed_args.enable: diff --git a/openstackclient/volume/v3/service.py b/openstackclient/volume/v3/service.py index fb41b1f47..0f14e472c 100644 --- a/openstackclient/volume/v3/service.py +++ b/openstackclient/volume/v3/service.py @@ -133,7 +133,7 @@ def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume service = volume_client.find_service( - host=parsed_args.host, service=parsed_args.service + parsed_args.service, ignore_missing=False, host=parsed_args.host ) if parsed_args.enable: diff --git a/releasenotes/notes/volume-service-set-fix-345a8bc84267f743.yaml b/releasenotes/notes/volume-service-set-fix-345a8bc84267f743.yaml new file mode 100644 index 000000000..e21e12dbb --- /dev/null +++ b/releasenotes/notes/volume-service-set-fix-345a8bc84267f743.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + The 'volume service set' command could not work due to a bad API call. + [Bug `2116969 `_] From 504cbd24e28ba1c0af66a66f03fa085dc3ec0f69 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 19 May 2025 13:10:33 +0100 Subject: [PATCH 179/245] volume: Migrate 'volume create' to SDK We need a shim for consistency group support, which we may eventually port to SDK but have not yet. Otherwise, this is rather straightforward. Change-Id: Ic880b7a64cde2148c266d549c4768c669ba3f501 Signed-off-by: Stephen Finucane Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/943800 --- openstackclient/api/compute_v2.py | 4 +- openstackclient/api/volume_v2.py | 60 ++ openstackclient/api/volume_v3.py | 60 ++ .../tests/functional/volume/v3/test_volume.py | 2 +- .../tests/unit/api/test_volume_v2.py | 124 ++++ .../tests/unit/api/test_volume_v3.py | 124 ++++ .../tests/unit/volume/v2/test_volume.py | 477 ++++++------- .../tests/unit/volume/v3/test_volume.py | 649 ++++++++---------- openstackclient/volume/v2/volume.py | 93 ++- openstackclient/volume/v3/volume.py | 128 ++-- 10 files changed, 1032 insertions(+), 689 deletions(-) create mode 100644 openstackclient/api/volume_v2.py create mode 100644 openstackclient/api/volume_v3.py create mode 100644 openstackclient/tests/unit/api/test_volume_v2.py create mode 100644 openstackclient/tests/unit/api/test_volume_v3.py diff --git a/openstackclient/api/compute_v2.py b/openstackclient/api/compute_v2.py index 95521eae9..41e1b685b 100644 --- a/openstackclient/api/compute_v2.py +++ b/openstackclient/api/compute_v2.py @@ -64,7 +64,7 @@ def list_security_groups(compute_client, all_projects=None): def find_security_group(compute_client, name_or_id): - """Find the name for a given security group name or ID + """Find the security group for a given name or ID https://docs.openstack.org/api-ref/compute/#show-security-group-details @@ -240,7 +240,7 @@ def list_networks(compute_client): def find_network(compute_client, name_or_id): - """Find the ID for a given network name or ID + """Find the network for a given name or ID https://docs.openstack.org/api-ref/compute/#show-network-details diff --git a/openstackclient/api/volume_v2.py b/openstackclient/api/volume_v2.py new file mode 100644 index 000000000..9575379c6 --- /dev/null +++ b/openstackclient/api/volume_v2.py @@ -0,0 +1,60 @@ +# 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. + +"""Volume v2 API Library + +A collection of wrappers for deprecated Block Storage v2 APIs that are not +intentionally supported by SDK. +""" + +import http + +from openstack import exceptions as sdk_exceptions +from osc_lib import exceptions + + +# consistency groups + + +def find_consistency_group(compute_client, name_or_id): + """Find the consistency group for a given name or ID + + https://docs.openstack.org/api-ref/block-storage/v3/#show-a-consistency-group-s-details + + :param volume_client: A volume client + :param name_or_id: The name or ID of the consistency group to look up + :returns: A consistency group object + :raises exception.NotFound: If a matching consistency group could not be + found or more than one match was found + """ + response = compute_client.get(f'/consistencygroups/{name_or_id}') + if response.status_code != http.HTTPStatus.NOT_FOUND: + # there might be other, non-404 errors + sdk_exceptions.raise_from_response(response) + return response.json()['consistencygroup'] + + response = compute_client.get('/consistencygroups') + sdk_exceptions.raise_from_response(response) + found = None + consistency_groups = response.json()['consistencygroups'] + for consistency_group in consistency_groups: + if consistency_group['name'] == name_or_id: + if found: + raise exceptions.NotFound( + f'multiple matches found for {name_or_id}' + ) + found = consistency_group + + if not found: + raise exceptions.NotFound(f'{name_or_id} not found') + + return found diff --git a/openstackclient/api/volume_v3.py b/openstackclient/api/volume_v3.py new file mode 100644 index 000000000..1a3f25fa0 --- /dev/null +++ b/openstackclient/api/volume_v3.py @@ -0,0 +1,60 @@ +# 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. + +"""Volume v3 API Library + +A collection of wrappers for deprecated Block Storage v3 APIs that are not +intentionally supported by SDK. +""" + +import http + +from openstack import exceptions as sdk_exceptions +from osc_lib import exceptions + + +# consistency groups + + +def find_consistency_group(compute_client, name_or_id): + """Find the consistency group for a given name or ID + + https://docs.openstack.org/api-ref/block-storage/v3/#show-a-consistency-group-s-details + + :param volume_client: A volume client + :param name_or_id: The name or ID of the consistency group to look up + :returns: A consistency group object + :raises exception.NotFound: If a matching consistency group could not be + found or more than one match was found + """ + response = compute_client.get(f'/consistencygroups/{name_or_id}') + if response.status_code != http.HTTPStatus.NOT_FOUND: + # there might be other, non-404 errors + sdk_exceptions.raise_from_response(response) + return response.json()['consistencygroup'] + + response = compute_client.get('/consistencygroups') + sdk_exceptions.raise_from_response(response) + found = None + consistency_groups = response.json()['consistencygroups'] + for consistency_group in consistency_groups: + if consistency_group['name'] == name_or_id: + if found: + raise exceptions.NotFound( + f'multiple matches found for {name_or_id}' + ) + found = consistency_group + + if not found: + raise exceptions.NotFound(f'{name_or_id} not found') + + return found diff --git a/openstackclient/tests/functional/volume/v3/test_volume.py b/openstackclient/tests/functional/volume/v3/test_volume.py index 244fe91c1..d4cd0ac63 100644 --- a/openstackclient/tests/functional/volume/v3/test_volume.py +++ b/openstackclient/tests/functional/volume/v3/test_volume.py @@ -124,7 +124,7 @@ def test_volume_set_and_unset(self): cmd_output["properties"], ) self.assertEqual( - 'false', + False, cmd_output["bootable"], ) self.wait_for_status("volume", name, "available") diff --git a/openstackclient/tests/unit/api/test_volume_v2.py b/openstackclient/tests/unit/api/test_volume_v2.py new file mode 100644 index 000000000..046d1cb9b --- /dev/null +++ b/openstackclient/tests/unit/api/test_volume_v2.py @@ -0,0 +1,124 @@ +# 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. +# + +"""Volume v2 API Library Tests""" + +import http +from unittest import mock +import uuid + +from openstack.block_storage.v2 import _proxy +from osc_lib import exceptions as osc_lib_exceptions + +from openstackclient.api import volume_v2 as volume +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit import utils + + +class TestConsistencyGroup(utils.TestCase): + def setUp(self): + super().setUp() + + self.volume_sdk_client = mock.Mock(_proxy.Proxy) + + def test_find_consistency_group_by_id(self): + cg_id = uuid.uuid4().hex + cg_name = 'name-' + uuid.uuid4().hex + data = { + 'consistencygroup': { + 'id': cg_id, + 'name': cg_name, + 'status': 'available', + 'availability_zone': 'az1', + 'created_at': '2015-09-16T09:28:52.000000', + 'description': 'description-' + uuid.uuid4().hex, + 'volume_types': ['123456'], + } + } + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(data=data), + ] + + result = volume.find_consistency_group(self.volume_sdk_client, cg_id) + + self.volume_sdk_client.get.assert_has_calls( + [ + mock.call(f'/consistencygroups/{cg_id}'), + ] + ) + self.assertEqual(data['consistencygroup'], result) + + def test_find_consistency_group_by_name(self): + cg_id = uuid.uuid4().hex + cg_name = 'name-' + uuid.uuid4().hex + data = { + 'consistencygroups': [ + { + 'id': cg_id, + 'name': cg_name, + } + ], + } + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + + result = volume.find_consistency_group(self.volume_sdk_client, cg_name) + + self.volume_sdk_client.get.assert_has_calls( + [ + mock.call(f'/consistencygroups/{cg_name}'), + mock.call('/consistencygroups'), + ] + ) + self.assertEqual(data['consistencygroups'][0], result) + + def test_find_consistency_group_not_found(self): + data = {'consistencygroups': []} + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + self.assertRaises( + osc_lib_exceptions.NotFound, + volume.find_consistency_group, + self.volume_sdk_client, + 'invalid-cg', + ) + + def test_find_consistency_group_by_name_duplicate(self): + cg_name = 'name-' + uuid.uuid4().hex + data = { + 'consistencygroups': [ + { + 'id': uuid.uuid4().hex, + 'name': cg_name, + }, + { + 'id': uuid.uuid4().hex, + 'name': cg_name, + }, + ], + } + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + + self.assertRaises( + osc_lib_exceptions.NotFound, + volume.find_consistency_group, + self.volume_sdk_client, + cg_name, + ) diff --git a/openstackclient/tests/unit/api/test_volume_v3.py b/openstackclient/tests/unit/api/test_volume_v3.py new file mode 100644 index 000000000..d70f89933 --- /dev/null +++ b/openstackclient/tests/unit/api/test_volume_v3.py @@ -0,0 +1,124 @@ +# 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. +# + +"""Volume v3 API Library Tests""" + +import http +from unittest import mock +import uuid + +from openstack.block_storage.v3 import _proxy +from osc_lib import exceptions as osc_lib_exceptions + +from openstackclient.api import volume_v3 as volume +from openstackclient.tests.unit import fakes +from openstackclient.tests.unit import utils + + +class TestConsistencyGroup(utils.TestCase): + def setUp(self): + super().setUp() + + self.volume_sdk_client = mock.Mock(_proxy.Proxy) + + def test_find_consistency_group_by_id(self): + cg_id = uuid.uuid4().hex + cg_name = 'name-' + uuid.uuid4().hex + data = { + 'consistencygroup': { + 'id': cg_id, + 'name': cg_name, + 'status': 'available', + 'availability_zone': 'az1', + 'created_at': '2015-09-16T09:28:52.000000', + 'description': 'description-' + uuid.uuid4().hex, + 'volume_types': ['123456'], + } + } + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(data=data), + ] + + result = volume.find_consistency_group(self.volume_sdk_client, cg_id) + + self.volume_sdk_client.get.assert_has_calls( + [ + mock.call(f'/consistencygroups/{cg_id}'), + ] + ) + self.assertEqual(data['consistencygroup'], result) + + def test_find_consistency_group_by_name(self): + cg_id = uuid.uuid4().hex + cg_name = 'name-' + uuid.uuid4().hex + data = { + 'consistencygroups': [ + { + 'id': cg_id, + 'name': cg_name, + } + ], + } + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + + result = volume.find_consistency_group(self.volume_sdk_client, cg_name) + + self.volume_sdk_client.get.assert_has_calls( + [ + mock.call(f'/consistencygroups/{cg_name}'), + mock.call('/consistencygroups'), + ] + ) + self.assertEqual(data['consistencygroups'][0], result) + + def test_find_consistency_group_not_found(self): + data = {'consistencygroups': []} + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + self.assertRaises( + osc_lib_exceptions.NotFound, + volume.find_consistency_group, + self.volume_sdk_client, + 'invalid-cg', + ) + + def test_find_consistency_group_by_name_duplicate(self): + cg_name = 'name-' + uuid.uuid4().hex + data = { + 'consistencygroups': [ + { + 'id': uuid.uuid4().hex, + 'name': cg_name, + }, + { + 'id': uuid.uuid4().hex, + 'name': cg_name, + }, + ], + } + self.volume_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + + self.assertRaises( + osc_lib_exceptions.NotFound, + volume.find_consistency_group, + self.volume_sdk_client, + cg_name, + ) diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index ea43e78e2..e5b1ef133 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -13,6 +13,7 @@ from unittest import mock +from openstack.block_storage.v2 import snapshot as _snapshot from openstack.block_storage.v2 import volume as _volume from openstack import exceptions as sdk_exceptions from openstack.test import fakes as sdk_fakes @@ -20,6 +21,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.api import volume_v2 from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit.image.v2 import fakes as image_fakes from openstackclient.tests.unit import utils as test_utils @@ -50,129 +52,156 @@ def setUp(self): self.consistencygroups_mock.reset_mock() -class TestVolumeCreate(TestVolume): - project = identity_fakes.FakeProject.create_one_project() - user = identity_fakes.FakeUser.create_one_user() - +class TestVolumeCreate(volume_fakes.TestVolume): columns = ( 'attachments', 'availability_zone', 'bootable', + 'consistencygroup_id', + 'created_at', 'description', + 'encrypted', 'id', + 'multiattach', 'name', + 'os-vol-host-attr:host', + 'os-vol-mig-status-attr:migstat', + 'os-vol-mig-status-attr:name_id', + 'os-vol-tenant-attr:tenant_id', + 'os-volume-replication:driver_data', + 'os-volume-replication:extended_status', 'properties', + 'replication_status', 'size', 'snapshot_id', + 'source_volid', 'status', 'type', + 'updated_at', + 'user_id', + 'volume_image_metadata', ) def setUp(self): super().setUp() - self.new_volume = volume_fakes.create_one_volume() - self.volumes_mock.create.return_value = self.new_volume + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.create_volume.return_value = self.volume self.datalist = ( - self.new_volume.attachments, - self.new_volume.availability_zone, - self.new_volume.bootable, - self.new_volume.description, - self.new_volume.id, - self.new_volume.name, - format_columns.DictColumn(self.new_volume.metadata), - self.new_volume.size, - self.new_volume.snapshot_id, - self.new_volume.status, - self.new_volume.volume_type, + self.volume.attachments, + self.volume.availability_zone, + self.volume.is_bootable, + self.volume.consistency_group_id, + self.volume.created_at, + self.volume.description, + self.volume.is_encrypted, + self.volume.id, + self.volume.is_multiattach, + self.volume.name, + self.volume.host, + self.volume.migration_status, + self.volume.migration_id, + self.volume.project_id, + self.volume.replication_driver_data, + self.volume.extended_replication_status, + format_columns.DictColumn(self.volume.metadata), + self.volume.replication_status, + self.volume.size, + self.volume.snapshot_id, + self.volume.source_volume_id, + self.volume.status, + self.volume.volume_type, + self.volume.updated_at, + self.volume.user_id, + self.volume.volume_image_metadata, ) - # Get the command object to test self.cmd = volume.CreateVolume(self.app, None) def test_volume_create_min_options(self): arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), ] verifylist = [ - ('size', self.new_volume.size), + ('size', self.volume.size), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, name=None, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_options(self): - consistency_group = volume_fakes.create_one_consistency_group() - self.consistencygroups_mock.get.return_value = consistency_group + consistency_group_id = 'cg123' arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), '--description', - self.new_volume.description, + self.volume.description, '--type', - self.new_volume.volume_type, + self.volume.volume_type, '--availability-zone', - self.new_volume.availability_zone, + self.volume.availability_zone, '--consistency-group', - consistency_group.id, + consistency_group_id, '--hint', 'k=v', - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('size', self.new_volume.size), - ('description', self.new_volume.description), - ('type', self.new_volume.volume_type), - ('availability_zone', self.new_volume.availability_zone), - ('consistency_group', consistency_group.id), + ('size', self.volume.size), + ('description', self.volume.description), + ('type', self.volume.volume_type), + ('availability_zone', self.volume.availability_zone), + ('consistency_group', consistency_group_id), ('hint', {'k': 'v'}), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) + with mock.patch.object( + volume_v2, + 'find_consistency_group', + return_value={'id': consistency_group_id}, + ) as mock_find_cg: + columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, - description=self.new_volume.description, - volume_type=self.new_volume.volume_type, - availability_zone=self.new_volume.availability_zone, + name=self.volume.name, + description=self.volume.description, + volume_type=self.volume.volume_type, + availability_zone=self.volume.availability_zone, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=consistency_group.id, + image_id=None, + source_volume_id=None, + consistency_group_id=consistency_group_id, scheduler_hints={'k': 'v'}, ) + mock_find_cg.assert_called_once_with( + self.volume_sdk_client, consistency_group_id + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_properties(self): arglist = [ @@ -181,39 +210,36 @@ def test_volume_create_properties(self): '--property', 'Beta=b', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('properties', {'Alpha': 'a', 'Beta': 'b'}), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata={'Alpha': 'a', 'Beta': 'b'}, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) - def test_volume_create_image_id(self): + def test_volume_create_image(self): image = image_fakes.create_one_image() self.image_client.find_image.return_value = image @@ -221,152 +247,111 @@ def test_volume_create_image_id(self): '--image', image.id, '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('image', image.id), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=image.id, - source_volid=None, - consistencygroup_id=None, + image_id=image.id, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_image_name(self): - image = image_fakes.create_one_image() - self.image_client.find_image.return_value = image - - arglist = [ - '--image', - image.name, - '--size', - str(self.new_volume.size), - self.new_volume.name, - ] - verifylist = [ - ('image', image.name), - ('size', self.new_volume.size), - ('name', self.new_volume.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, - snapshot_id=None, - name=self.new_volume.name, - description=None, - volume_type=None, - availability_zone=None, - metadata=None, - imageRef=image.id, - source_volid=None, - consistencygroup_id=None, - scheduler_hints=None, + self.image_client.find_image.assert_called_once_with( + image.id, ignore_missing=False ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_with_snapshot(self): - snapshot = volume_fakes.create_one_snapshot() - self.new_volume.snapshot_id = snapshot.id + snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) + self.volume_sdk_client.find_snapshot.return_value = snapshot + arglist = [ '--snapshot', - self.new_volume.snapshot_id, - self.new_volume.name, + snapshot.id, + self.volume.name, ] verifylist = [ - ('snapshot', self.new_volume.snapshot_id), - ('name', self.new_volume.name), + ('snapshot', snapshot.id), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.snapshots_mock.get.return_value = snapshot - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_once_with( + self.volume_sdk_client.create_volume.assert_called_with( size=snapshot.size, snapshot_id=snapshot.id, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + snapshot.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_with_source_volume(self): - source_vol = "source_vol" + source_volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = source_volume + arglist = [ '--source', - self.new_volume.id, - source_vol, + source_volume.id, + self.volume.name, ] verifylist = [ - ('source', self.new_volume.id), - ('name', source_vol), + ('source', source_volume.id), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.volumes_mock.get.return_value = self.new_volume - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_once_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=source_volume.size, snapshot_id=None, - name=source_vol, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=self.new_volume.id, - consistencygroup_id=None, + image_id=None, + source_volume_id=source_volume.id, + consistency_group_id=None, scheduler_hints=None, ) + self.volume_sdk_client.find_volume.assert_called_once_with( + source_volume.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) @mock.patch.object(utils, 'wait_for_status', return_value=True) def test_volume_create_with_bootable_and_readonly(self, mock_wait): @@ -374,188 +359,187 @@ def test_volume_create_with_bootable_and_readonly(self, mock_wait): '--bootable', '--read-only', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', True), ('read_only', True), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True + self.volume_sdk_client.set_volume_bootable_status.assert_called_once_with( + self.volume, True ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True + self.volume_sdk_client.set_volume_readonly.assert_called_once_with( + self.volume, True ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + @mock.patch.object(utils, 'wait_for_status', return_value=True) def test_volume_create_with_nonbootable_and_readwrite(self, mock_wait): arglist = [ '--non-bootable', '--read-write', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', False), ('read_only', False), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, False + self.volume_sdk_client.set_volume_bootable_status.assert_called_once_with( + self.volume, False ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, False + self.volume_sdk_client.set_volume_readonly.assert_called_once_with( + self.volume, False ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + @mock.patch.object(volume.LOG, 'error') @mock.patch.object(utils, 'wait_for_status', return_value=True) def test_volume_create_with_bootable_and_readonly_fail( self, mock_wait, mock_error ): - self.volumes_mock.set_bootable.side_effect = exceptions.CommandError() - - self.volumes_mock.update_readonly_flag.side_effect = ( - exceptions.CommandError() + self.volume_sdk_client.set_volume_bootable_status.side_effect = ( + sdk_exceptions.NotFoundException('foo') + ) + self.volume_sdk_client.set_volume_readonly.side_effect = ( + sdk_exceptions.NotFoundException('foo') ) arglist = [ '--bootable', '--read-only', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', True), ('read_only', True), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) + self.volume_sdk_client.set_volume_bootable_status.assert_called_once_with( + self.volume, True + ) + self.volume_sdk_client.set_volume_readonly.assert_called_once_with( + self.volume, True + ) self.assertEqual(2, mock_error.call_count) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True - ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True - ) + self.assertEqual(self.datalist, data) @mock.patch.object(volume.LOG, 'error') @mock.patch.object(utils, 'wait_for_status', return_value=False) def test_volume_create_non_available_with_readonly( - self, - mock_wait, - mock_error, + self, mock_wait, mock_error ): arglist = [ '--non-bootable', '--read-only', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', False), ('read_only', True), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, ) self.assertEqual(2, mock_error.call_count) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_without_size(self): arglist = [ - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -572,15 +556,15 @@ def test_volume_create_with_multi_source(self): '--snapshot', 'source_snapshot', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('image', 'source_image'), ('source', 'source_volume'), ('snapshot', 'source_snapshot'), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] self.assertRaises( @@ -599,7 +583,7 @@ def test_volume_create_hints(self): """ arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), '--hint', 'k=v', '--hint', @@ -614,10 +598,10 @@ def test_volume_create_hints(self): 'local_to_instance=v6', '--hint', 'different_host=v7', - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('size', self.new_volume.size), + ('size', self.volume.size), ( 'hint', { @@ -627,26 +611,23 @@ def test_volume_create_hints(self): 'different_host': ['v5', 'v7'], }, ), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints={ 'k': 'v2', 'same_host': ['v3', 'v4'], @@ -656,7 +637,7 @@ def test_volume_create_hints(self): ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) class TestVolumeDelete(volume_fakes.TestVolume): diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index fe210280a..cdf22e293 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -14,6 +14,7 @@ import copy from unittest import mock +from openstack.block_storage.v3 import backup as _backup from openstack.block_storage.v3 import block_storage_summary as _summary from openstack.block_storage.v3 import snapshot as _snapshot from openstack.block_storage.v3 import volume as _volume @@ -23,6 +24,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.api import volume_v3 from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit.image.v2 import fakes as image_fakes from openstackclient.tests.unit import utils as test_utils @@ -30,55 +32,81 @@ from openstackclient.volume.v3 import volume -# TODO(stephenfin): Combine these two test classes -class TestVolumeCreateLegacy(volume_fakes.TestVolume): - project = identity_fakes.FakeProject.create_one_project() - user = identity_fakes.FakeUser.create_one_user() - +class TestVolumeCreate(volume_fakes.TestVolume): columns = ( 'attachments', 'availability_zone', 'bootable', + 'cluster_name', + 'consistencygroup_id', + 'consumes_quota', + 'created_at', 'description', + 'encrypted', + 'encryption_key_id', + 'group_id', 'id', + 'multiattach', 'name', + 'os-vol-host-attr:host', + 'os-vol-mig-status-attr:migstat', + 'os-vol-mig-status-attr:name_id', + 'os-vol-tenant-attr:tenant_id', 'properties', + 'provider_id', + 'replication_status', + 'service_uuid', + 'shared_targets', 'size', 'snapshot_id', + 'source_volid', 'status', 'type', + 'updated_at', + 'user_id', + 'volume_image_metadata', + 'volume_type_id', ) def setUp(self): super().setUp() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - - self.consistencygroups_mock = self.volume_client.consistencygroups - self.consistencygroups_mock.reset_mock() - - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - - self.backups_mock = self.volume_client.backups - self.backups_mock.reset_mock() - - self.new_volume = volume_fakes.create_one_volume() - self.volumes_mock.create.return_value = self.new_volume + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.create_volume.return_value = self.volume self.datalist = ( - self.new_volume.attachments, - self.new_volume.availability_zone, - self.new_volume.bootable, - self.new_volume.description, - self.new_volume.id, - self.new_volume.name, - format_columns.DictColumn(self.new_volume.metadata), - self.new_volume.size, - self.new_volume.snapshot_id, - self.new_volume.status, - self.new_volume.volume_type, + self.volume.attachments, + self.volume.availability_zone, + self.volume.is_bootable, + self.volume.cluster_name, + self.volume.consistency_group_id, + self.volume.consumes_quota, + self.volume.created_at, + self.volume.description, + self.volume.is_encrypted, + self.volume.encryption_key_id, + self.volume.group_id, + self.volume.id, + self.volume.is_multiattach, + self.volume.name, + self.volume.host, + self.volume.migration_status, + self.volume.migration_id, + self.volume.project_id, + format_columns.DictColumn(self.volume.metadata), + self.volume.provider_id, + self.volume.replication_status, + self.volume.service_uuid, + self.volume.shared_targets, + self.volume.size, + self.volume.snapshot_id, + self.volume.source_volume_id, + self.volume.status, + self.volume.volume_type, + self.volume.updated_at, + self.volume.user_id, + self.volume.volume_image_metadata, + self.volume.volume_type_id, ) # Get the command object to test @@ -87,87 +115,88 @@ def setUp(self): def test_volume_create_min_options(self): arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), ] verifylist = [ - ('size', self.new_volume.size), + ('size', self.volume.size), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, name=None, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_options(self): - consistency_group = volume_fakes.create_one_consistency_group() - self.consistencygroups_mock.get.return_value = consistency_group + consistency_group_id = 'cg123' arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), '--description', - self.new_volume.description, + self.volume.description, '--type', - self.new_volume.volume_type, + self.volume.volume_type, '--availability-zone', - self.new_volume.availability_zone, + self.volume.availability_zone, '--consistency-group', - consistency_group.id, + consistency_group_id, '--hint', 'k=v', - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('size', self.new_volume.size), - ('description', self.new_volume.description), - ('type', self.new_volume.volume_type), - ('availability_zone', self.new_volume.availability_zone), - ('consistency_group', consistency_group.id), + ('size', self.volume.size), + ('description', self.volume.description), + ('type', self.volume.volume_type), + ('availability_zone', self.volume.availability_zone), + ('consistency_group', consistency_group_id), ('hint', {'k': 'v'}), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) + with mock.patch.object( + volume_v3, + 'find_consistency_group', + return_value={'id': consistency_group_id}, + ) as mock_find_cg: + columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, - description=self.new_volume.description, - volume_type=self.new_volume.volume_type, - availability_zone=self.new_volume.availability_zone, + name=self.volume.name, + description=self.volume.description, + volume_type=self.volume.volume_type, + availability_zone=self.volume.availability_zone, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=consistency_group.id, + image_id=None, + source_volume_id=None, + consistency_group_id=consistency_group_id, scheduler_hints={'k': 'v'}, backup_id=None, ) + mock_find_cg.assert_called_once_with( + self.volume_sdk_client, consistency_group_id + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_properties(self): arglist = [ @@ -176,38 +205,35 @@ def test_volume_create_properties(self): '--property', 'Beta=b', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('properties', {'Alpha': 'a', 'Beta': 'b'}), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata={'Alpha': 'a', 'Beta': 'b'}, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_image_id(self): image = image_fakes.create_one_image() @@ -217,38 +243,35 @@ def test_volume_create_image_id(self): '--image', image.id, '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('image', image.id), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=image.id, - source_volid=None, - consistencygroup_id=None, + image_id=image.id, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_image_name(self): image = image_fakes.create_one_image() @@ -258,177 +281,173 @@ def test_volume_create_image_name(self): '--image', image.name, '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('image', image.name), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=image.id, - source_volid=None, - consistencygroup_id=None, + image_id=image.id, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_with_snapshot(self): - snapshot = volume_fakes.create_one_snapshot() - self.new_volume.snapshot_id = snapshot.id + snapshot = sdk_fakes.generate_fake_resource(_snapshot.Snapshot) + self.volume_sdk_client.find_snapshot.return_value = snapshot + arglist = [ '--snapshot', - self.new_volume.snapshot_id, - self.new_volume.name, + snapshot.id, + self.volume.name, ] verifylist = [ - ('snapshot', self.new_volume.snapshot_id), - ('name', self.new_volume.name), + ('snapshot', snapshot.id), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.snapshots_mock.get.return_value = snapshot - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_once_with( + self.volume_sdk_client.create_volume.assert_called_with( size=snapshot.size, snapshot_id=snapshot.id, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) + self.volume_sdk_client.find_snapshot.assert_called_once_with( + snapshot.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_with_backup(self): self.set_volume_api_version('3.47') - backup = volume_fakes.create_one_backup() - self.new_volume.backup_id = backup.id + backup = sdk_fakes.generate_fake_resource(_backup.Backup) + self.volume_sdk_client.find_backup.return_value = backup + arglist = [ '--backup', - self.new_volume.backup_id, - self.new_volume.name, + backup.id, + self.volume.name, ] verifylist = [ - ('backup', self.new_volume.backup_id), - ('name', self.new_volume.name), + ('backup', backup.id), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.backups_mock.get.return_value = backup - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_once_with( + self.volume_sdk_client.create_volume.assert_called_with( size=backup.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=backup.id, ) + self.volume_sdk_client.find_backup.assert_called_once_with( + backup.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_with_backup_pre_v347(self): - backup = volume_fakes.create_one_backup() - self.new_volume.backup_id = backup.id + backup = sdk_fakes.generate_fake_resource(_backup.Backup) + self.volume_sdk_client.find_backup.return_value = backup + arglist = [ '--backup', - self.new_volume.backup_id, - self.new_volume.name, + backup.id, + self.volume.name, ] verifylist = [ - ('backup', self.new_volume.backup_id), - ('name', self.new_volume.name), + ('backup', backup.id), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.backups_mock.get.return_value = backup - exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn("--os-volume-api-version 3.47 or greater", str(exc)) + self.volume_sdk_client.create_volume.assert_not_called() + def test_volume_create_with_source_volume(self): - source_vol = "source_vol" + source_volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = source_volume + arglist = [ '--source', - self.new_volume.id, - source_vol, + source_volume.id, + self.volume.name, ] verifylist = [ - ('source', self.new_volume.id), - ('name', source_vol), + ('source', source_volume.id), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.volumes_mock.get.return_value = self.new_volume - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_once_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=source_volume.size, snapshot_id=None, - name=source_vol, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=self.new_volume.id, - consistencygroup_id=None, + image_id=None, + source_volume_id=source_volume.id, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) + self.volume_sdk_client.find_volume.assert_called_once_with( + source_volume.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) @mock.patch.object(utils, 'wait_for_status', return_value=True) def test_volume_create_with_bootable_and_readonly(self, mock_wait): @@ -436,192 +455,191 @@ def test_volume_create_with_bootable_and_readonly(self, mock_wait): '--bootable', '--read-only', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', True), ('read_only', True), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True + self.volume_sdk_client.set_volume_bootable_status.assert_called_once_with( + self.volume, True ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True + self.volume_sdk_client.set_volume_readonly.assert_called_once_with( + self.volume, True ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + @mock.patch.object(utils, 'wait_for_status', return_value=True) def test_volume_create_with_nonbootable_and_readwrite(self, mock_wait): arglist = [ '--non-bootable', '--read-write', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', False), ('read_only', False), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, False + self.volume_sdk_client.set_volume_bootable_status.assert_called_once_with( + self.volume, False ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, False + self.volume_sdk_client.set_volume_readonly.assert_called_once_with( + self.volume, False ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + @mock.patch.object(volume.LOG, 'error') @mock.patch.object(utils, 'wait_for_status', return_value=True) def test_volume_create_with_bootable_and_readonly_fail( self, mock_wait, mock_error ): - self.volumes_mock.set_bootable.side_effect = exceptions.CommandError() - - self.volumes_mock.update_readonly_flag.side_effect = ( - exceptions.CommandError() + self.volume_sdk_client.set_volume_bootable_status.side_effect = ( + sdk_exceptions.NotFoundException('foo') + ) + self.volume_sdk_client.set_volume_readonly.side_effect = ( + sdk_exceptions.NotFoundException('foo') ) arglist = [ '--bootable', '--read-only', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', True), ('read_only', True), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) + self.volume_sdk_client.set_volume_bootable_status.assert_called_once_with( + self.volume, True + ) + self.volume_sdk_client.set_volume_readonly.assert_called_once_with( + self.volume, True + ) self.assertEqual(2, mock_error.call_count) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - self.volumes_mock.set_bootable.assert_called_with( - self.new_volume.id, True - ) - self.volumes_mock.update_readonly_flag.assert_called_with( - self.new_volume.id, True - ) + self.assertEqual(self.datalist, data) @mock.patch.object(volume.LOG, 'error') @mock.patch.object(utils, 'wait_for_status', return_value=False) def test_volume_create_non_available_with_readonly( - self, - mock_wait, - mock_error, + self, mock_wait, mock_error ): arglist = [ '--non-bootable', '--read-only', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('bootable', False), ('read_only', True), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints=None, backup_id=None, ) self.assertEqual(2, mock_error.call_count) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_without_size(self): arglist = [ - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -638,15 +656,15 @@ def test_volume_create_with_multi_source(self): '--snapshot', 'source_snapshot', '--size', - str(self.new_volume.size), - self.new_volume.name, + str(self.volume.size), + self.volume.name, ] verifylist = [ ('image', 'source_image'), ('source', 'source_volume'), ('snapshot', 'source_snapshot'), - ('size', self.new_volume.size), - ('name', self.new_volume.name), + ('size', self.volume.size), + ('name', self.volume.name), ] self.assertRaises( @@ -665,7 +683,7 @@ def test_volume_create_hints(self): """ arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), '--hint', 'k=v', '--hint', @@ -680,10 +698,10 @@ def test_volume_create_hints(self): 'local_to_instance=v6', '--hint', 'different_host=v7', - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('size', self.new_volume.size), + ('size', self.volume.size), ( 'hint', { @@ -693,26 +711,23 @@ def test_volume_create_hints(self): 'different_host': ['v5', 'v7'], }, ), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.create.assert_called_with( - size=self.new_volume.size, + self.volume_sdk_client.create_volume.assert_called_with( + size=self.volume.size, snapshot_id=None, - name=self.new_volume.name, + name=self.volume.name, description=None, volume_type=None, availability_zone=None, metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, + image_id=None, + source_volume_id=None, + consistency_group_id=None, scheduler_hints={ 'k': 'v2', 'same_host': ['v3', 'v4'], @@ -723,102 +738,22 @@ def test_volume_create_hints(self): ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - -class TestVolumeCreate(volume_fakes.TestVolume): - columns = ( - 'attachments', - 'availability_zone', - 'consistency_group_id', - 'created_at', - 'description', - 'extended_replication_status', - 'group_id', - 'host', - 'id', - 'image_id', - 'is_bootable', - 'is_encrypted', - 'is_multiattach', - 'location', - 'metadata', - 'migration_id', - 'migration_status', - 'name', - 'project_id', - 'provider_id', - 'replication_driver_data', - 'replication_status', - 'scheduler_hints', - 'size', - 'snapshot_id', - 'source_volume_id', - 'status', - 'updated_at', - 'user_id', - 'volume_image_metadata', - 'volume_type', - ) - - def setUp(self): - super().setUp() - - self.new_volume = sdk_fakes.generate_fake_resource( - _volume.Volume, **{'size': 1} - ) - - self.datalist = ( - self.new_volume.attachments, - self.new_volume.availability_zone, - self.new_volume.consistency_group_id, - self.new_volume.created_at, - self.new_volume.description, - self.new_volume.extended_replication_status, - self.new_volume.group_id, - self.new_volume.host, - self.new_volume.id, - self.new_volume.image_id, - self.new_volume.is_bootable, - self.new_volume.is_encrypted, - self.new_volume.is_multiattach, - self.new_volume.location, - self.new_volume.metadata, - self.new_volume.migration_id, - self.new_volume.migration_status, - self.new_volume.name, - self.new_volume.project_id, - self.new_volume.provider_id, - self.new_volume.replication_driver_data, - self.new_volume.replication_status, - self.new_volume.scheduler_hints, - self.new_volume.size, - self.new_volume.snapshot_id, - self.new_volume.source_volume_id, - self.new_volume.status, - self.new_volume.updated_at, - self.new_volume.user_id, - self.new_volume.volume_image_metadata, - self.new_volume.volume_type, - ) - - # Get the command object to test - self.cmd = volume.CreateVolume(self.app, None) + self.assertEqual(self.datalist, data) def test_volume_create_remote_source(self): - self.volume_sdk_client.manage_volume.return_value = self.new_volume + self.volume_sdk_client.manage_volume.return_value = self.volume arglist = [ '--remote-source', 'key=val', '--host', 'fake_host', - self.new_volume.name, + self.volume.name, ] verifylist = [ ('remote_source', {'key': 'val'}), ('host', 'fake_host'), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -837,7 +772,7 @@ def test_volume_create_remote_source(self): ) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) + self.assertEqual(self.datalist, data) def test_volume_create_remote_source_pre_v316(self): self.set_volume_api_version('3.15') @@ -846,12 +781,12 @@ def test_volume_create_remote_source_pre_v316(self): 'key=val', '--cluster', 'fake_cluster', - self.new_volume.name, + self.volume.name, ] verifylist = [ ('remote_source', {'key': 'val'}), ('cluster', 'fake_cluster'), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -871,13 +806,13 @@ def test_volume_create_remote_source_host_and_cluster(self): 'fake_host', '--cluster', 'fake_cluster', - self.new_volume.name, + self.volume.name, ] verifylist = [ ('remote_source', {'key': 'val'}), ('host', 'fake_host'), ('cluster', 'fake_cluster'), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -892,11 +827,11 @@ def test_volume_create_remote_source_no_host_or_cluster(self): arglist = [ '--remote-source', 'key=val', - self.new_volume.name, + self.volume.name, ] verifylist = [ ('remote_source', {'key': 'val'}), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -910,15 +845,15 @@ def test_volume_create_remote_source_no_host_or_cluster(self): def test_volume_create_remote_source_size(self): arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), '--remote-source', 'key=val', - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('size', self.new_volume.size), + ('size', self.volume.size), ('remote_source', {'key': 'val'}), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -934,15 +869,15 @@ def test_volume_create_remote_source_size(self): def test_volume_create_host_no_remote_source(self): arglist = [ '--size', - str(self.new_volume.size), + str(self.volume.size), '--host', 'fake_host', - self.new_volume.name, + self.volume.name, ] verifylist = [ - ('size', self.new_volume.size), + ('size', self.volume.size), ('host', 'fake_host'), - ('name', self.new_volume.name), + ('name', self.volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index ad1fbe1ac..0e351f741 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -18,8 +18,10 @@ import copy import functools import logging +import typing as ty from cliff import columns as cliff_columns +from openstack.block_storage.v2 import volume as _volume from openstack import exceptions as sdk_exceptions from osc_lib.cli import format_columns from osc_lib.cli import parseractions @@ -27,6 +29,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.api import volume_v2 from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -89,6 +92,47 @@ def human_readable(self): return msg +def _format_volume(volume: _volume.Volume) -> dict[str, ty.Any]: + # Some columns returned by openstacksdk should not be shown because they're + # either irrelevant or duplicates + ignored_columns = { + # computed columns + 'location', + # create-only columns + 'OS-SCH-HNT:scheduler_hints', + 'imageRef', + # unnecessary columns + 'links', + } + optional_columns = { + # only present if part of a consistency group + 'consistencygroup_id', + # only present if there are image properties associated + 'volume_image_metadata', + } + + info = volume.to_dict(original_names=True) + data = {} + for key, value in info.items(): + if key in ignored_columns: + continue + + if key in optional_columns: + if info[key] is None: + continue + + data[key] = value + + data.update( + { + 'properties': format_columns.DictColumn(data.pop('metadata')), + 'type': data.pop('volume_type'), + } + ) + + return data + + class CreateVolume(command.ShowOne): _description = _("Create new volume") @@ -226,22 +270,22 @@ def take_action(self, parsed_args): # volume from snapshot or source volume size = parsed_args.size - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume image_client = self.app.client_manager.image source_volume = None if parsed_args.source: - source_volume_obj = utils.find_resource( - volume_client.volumes, parsed_args.source + source_volume_obj = volume_client.find_volume( + parsed_args.source, ignore_missing=False ) source_volume = source_volume_obj.id size = max(size or 0, source_volume_obj.size) consistency_group = None if parsed_args.consistency_group: - consistency_group = utils.find_resource( - volume_client.consistencygroups, parsed_args.consistency_group - ).id + consistency_group = volume_v2.find_consistency_group( + volume_client, parsed_args.consistency_group + )['id'] image = None if parsed_args.image: @@ -251,8 +295,8 @@ def take_action(self, parsed_args): snapshot = None if parsed_args.snapshot: - snapshot_obj = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot + snapshot_obj = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False ) snapshot = snapshot_obj.id # Cinder requires a value for size when creating a volume @@ -263,7 +307,7 @@ def take_action(self, parsed_args): # snapshot size. size = max(size or 0, snapshot_obj.size) - volume = volume_client.volumes.create( + volume = volume_client.create_volume( size=size, snapshot_id=snapshot, name=parsed_args.name, @@ -271,23 +315,23 @@ def take_action(self, parsed_args): volume_type=parsed_args.type, availability_zone=parsed_args.availability_zone, metadata=parsed_args.properties, - imageRef=image, - source_volid=source_volume, - consistencygroup_id=consistency_group, + image_id=image, + source_volume_id=source_volume, + consistency_group_id=consistency_group, scheduler_hints=parsed_args.hint, ) if parsed_args.bootable is not None: try: if utils.wait_for_status( - volume_client.volumes.get, + volume_client.get_volume, volume.id, success_status=['available'], error_status=['error'], sleep_time=1, ): - volume_client.volumes.set_bootable( - volume.id, parsed_args.bootable + volume_client.set_volume_bootable_status( + volume, parsed_args.bootable ) else: msg = _( @@ -300,14 +344,14 @@ def take_action(self, parsed_args): if parsed_args.read_only is not None: try: if utils.wait_for_status( - volume_client.volumes.get, + volume_client.get_volume, volume.id, success_status=['available'], error_status=['error'], sleep_time=1, ): - volume_client.volumes.update_readonly_flag( - volume.id, parsed_args.read_only + volume_client.set_volume_readonly( + volume, parsed_args.read_only ) else: msg = _( @@ -321,17 +365,8 @@ def take_action(self, parsed_args): e, ) - # Remove key links from being displayed - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - } - ) - volume._info.pop("links", None) - return zip(*sorted(volume._info.items())) + data = _format_volume(volume) + return zip(*sorted(data.items())) class DeleteVolume(command.Command): diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index c59e413a2..ee2cb260f 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -18,8 +18,10 @@ import copy import functools import logging +import typing as ty from cliff import columns as cliff_columns +from openstack.block_storage.v3 import volume as _volume from openstack import exceptions as sdk_exceptions from openstack import utils as sdk_utils from osc_lib.cli import format_columns @@ -28,6 +30,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.api import volume_v3 from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -90,6 +93,52 @@ def human_readable(self): return msg +def _format_volume(volume: _volume.Volume) -> dict[str, ty.Any]: + # Some columns returned by openstacksdk should not be shown because they're + # either irrelevant or duplicates + ignored_columns = { + # computed columns + 'location', + # create-only columns + 'OS-SCH-HNT:scheduler_hints', + 'imageRef', + # removed columns + 'os-volume-replication:driver_data', + 'os-volume-replication:extended_status', + # unnecessary columns + 'links', + } + optional_columns = { + # only present if part of a consistency group + 'consistencygroup_id', + # only present if the volume is encrypted + 'encryption_key_id', + # only present if there are image properties associated + 'volume_image_metadata', + } + + info = volume.to_dict(original_names=True) + data = {} + for key, value in info.items(): + if key in ignored_columns: + continue + + if key in optional_columns: + if info[key] is None: + continue + + data[key] = value + + data.update( + { + 'properties': format_columns.DictColumn(data.pop('metadata')), + 'type': data.pop('volume_type'), + } + ) + + return data + + class CreateVolume(command.ShowOne): _description = _("Create new volume") @@ -272,8 +321,7 @@ def take_action(self, parsed_args): # volume from snapshot, backup or source volume size = parsed_args.size - volume_client_sdk = self.app.client_manager.sdk_connection.volume - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume image_client = self.app.client_manager.image if ( @@ -285,8 +333,8 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - if parsed_args.backup and not ( - volume_client.api_version.matches('3.47') + if parsed_args.backup and not sdk_utils.supports_microversion( + volume_client, '3.47' ): msg = _( "--os-volume-api-version 3.47 or greater is required " @@ -308,9 +356,7 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) if parsed_args.cluster: - if not sdk_utils.supports_microversion( - volume_client_sdk, '3.16' - ): + if not sdk_utils.supports_microversion(volume_client, '3.16'): msg = _( "--os-volume-api-version 3.16 or greater is required " "to support the cluster parameter." @@ -328,7 +374,7 @@ def take_action(self, parsed_args): "manage a volume." ) raise exceptions.CommandError(msg) - volume = volume_client_sdk.manage_volume( + volume = volume_client.manage_volume( host=parsed_args.host, cluster=parsed_args.cluster, ref=parsed_args.remote_source, @@ -339,35 +385,22 @@ def take_action(self, parsed_args): metadata=parsed_args.properties, bootable=parsed_args.bootable, ) - data = {} - for key, value in volume.to_dict().items(): - # FIXME(stephenfin): Stop ignoring these once we bump SDK - # https://review.opendev.org/c/openstack/openstacksdk/+/945836/ - if key in ( - 'cluster_name', - 'consumes_quota', - 'encryption_key_id', - 'service_uuid', - 'shared_targets', - 'volume_type_id', - ): - continue - data[key] = value + data = _format_volume(volume) return zip(*sorted(data.items())) source_volume = None if parsed_args.source: - source_volume_obj = utils.find_resource( - volume_client.volumes, parsed_args.source + source_volume_obj = volume_client.find_volume( + parsed_args.source, ignore_missing=False ) source_volume = source_volume_obj.id size = max(size or 0, source_volume_obj.size) consistency_group = None if parsed_args.consistency_group: - consistency_group = utils.find_resource( - volume_client.consistencygroups, parsed_args.consistency_group - ).id + consistency_group = volume_v3.find_consistency_group( + volume_client, parsed_args.consistency_group + )['id'] image = None if parsed_args.image: @@ -377,8 +410,8 @@ def take_action(self, parsed_args): snapshot = None if parsed_args.snapshot: - snapshot_obj = utils.find_resource( - volume_client.volume_snapshots, parsed_args.snapshot + snapshot_obj = volume_client.find_snapshot( + parsed_args.snapshot, ignore_missing=False ) snapshot = snapshot_obj.id # Cinder requires a value for size when creating a volume @@ -391,14 +424,14 @@ def take_action(self, parsed_args): backup = None if parsed_args.backup: - backup_obj = utils.find_resource( - volume_client.backups, parsed_args.backup + backup_obj = volume_client.find_backup( + parsed_args.backup, ignore_missing=False ) backup = backup_obj.id # As above size = max(size or 0, backup_obj.size) - volume = volume_client.volumes.create( + volume = volume_client.create_volume( size=size, snapshot_id=snapshot, name=parsed_args.name, @@ -406,9 +439,9 @@ def take_action(self, parsed_args): volume_type=parsed_args.type, availability_zone=parsed_args.availability_zone, metadata=parsed_args.properties, - imageRef=image, - source_volid=source_volume, - consistencygroup_id=consistency_group, + image_id=image, + source_volume_id=source_volume, + consistency_group_id=consistency_group, scheduler_hints=parsed_args.hint, backup_id=backup, ) @@ -416,14 +449,14 @@ def take_action(self, parsed_args): if parsed_args.bootable is not None: try: if utils.wait_for_status( - volume_client.volumes.get, + volume_client.get_volume, volume.id, success_status=['available'], error_status=['error'], sleep_time=1, ): - volume_client.volumes.set_bootable( - volume.id, parsed_args.bootable + volume_client.set_volume_bootable_status( + volume, parsed_args.bootable ) else: msg = _( @@ -436,14 +469,14 @@ def take_action(self, parsed_args): if parsed_args.read_only is not None: try: if utils.wait_for_status( - volume_client.volumes.get, + volume_client.get_volume, volume.id, success_status=['available'], error_status=['error'], sleep_time=1, ): - volume_client.volumes.update_readonly_flag( - volume.id, parsed_args.read_only + volume_client.set_volume_readonly( + volume, parsed_args.read_only ) else: msg = _( @@ -457,17 +490,8 @@ def take_action(self, parsed_args): e, ) - # Remove key links from being displayed - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - } - ) - volume._info.pop("links", None) - return zip(*sorted(volume._info.items())) + data = _format_volume(volume) + return zip(*sorted(data.items())) class DeleteVolume(command.Command): From 5e5f12ba4019cad347ac73f2c29a8faa3f2f6180 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Mar 2025 14:15:56 +0000 Subject: [PATCH 180/245] volume: Migrate 'volume migrate' to SDK Change-Id: I99af5fce0c2c184e300dfbf5624b91eeed38c94b Signed-off-by: Stephen Finucane --- .../tests/unit/volume/v2/test_volume.py | 54 ++++++++++++------- .../tests/unit/volume/v3/test_volume.py | 53 ++++++++++-------- openstackclient/volume/v2/volume.py | 14 ++--- openstackclient/volume/v3/volume.py | 15 +++--- 4 files changed, 83 insertions(+), 53 deletions(-) diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index e5b1ef133..5b81bdf7a 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -1293,69 +1293,80 @@ def test_volume_list_backward_compatibility(self): self.assertIn(self.mock_volume.name, each_volume) -class TestVolumeMigrate(TestVolume): - _volume = volume_fakes.create_one_volume() - +class TestVolumeMigrate(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volumes_mock.get.return_value = self._volume - self.volumes_mock.migrate_volume.return_value = None - # Get the command object to test + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume + self.volume_sdk_client.migrate_volume.return_value = None + self.cmd = volume.MigrateVolume(self.app, None) def test_volume_migrate(self): arglist = [ "--host", "host@backend-name#pool", - self._volume.id, + self.volume.id, ] verifylist = [ ("force_host_copy", False), ("lock_volume", False), ("host", "host@backend-name#pool"), - ("volume", self._volume.id), + ("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) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, ignore_missing=False + ) + self.volume_sdk_client.migrate_volume.assert_called_once_with( + self.volume.id, + host="host@backend-name#pool", + force_host_copy=False, + lock_volume=False, + ) + def test_volume_migrate_with_option(self): arglist = [ "--force-host-copy", "--lock-volume", "--host", "host@backend-name#pool", - self._volume.id, + self.volume.id, ] verifylist = [ ("force_host_copy", True), ("lock_volume", True), ("host", "host@backend-name#pool"), - ("volume", self._volume.id), + ("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) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, ignore_missing=False + ) + self.volume_sdk_client.migrate_volume.assert_called_once_with( + self.volume.id, + host="host@backend-name#pool", + force_host_copy=True, + lock_volume=True, + ) + def test_volume_migrate_without_host(self): arglist = [ - self._volume.id, + self.volume.id, ] verifylist = [ ("force_host_copy", False), ("lock_volume", False), - ("volume", self._volume.id), + ("volume", self.volume.id), ] self.assertRaises( @@ -1366,6 +1377,9 @@ def test_volume_migrate_without_host(self): verifylist, ) + self.volume_sdk_client.find_volume.assert_not_called() + self.volume_sdk_client.migrate_volume.assert_not_called() + class TestVolumeSet(TestVolume): volume_type = volume_fakes.create_one_volume_type() diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index cdf22e293..764958af6 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -1665,71 +1665,79 @@ def test_volume_list_backward_compatibility(self): class TestVolumeMigrate(volume_fakes.TestVolume): - _volume = volume_fakes.create_one_volume() - def setUp(self): super().setUp() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume + self.volume_sdk_client.migrate_volume.return_value = None - 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, + self.volume.id, ] verifylist = [ ("force_host_copy", False), ("lock_volume", False), ("host", "host@backend-name#pool"), - ("volume", self._volume.id), + ("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) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, ignore_missing=False + ) + self.volume_sdk_client.migrate_volume.assert_called_once_with( + self.volume.id, + host="host@backend-name#pool", + force_host_copy=False, + lock_volume=False, + ) + def test_volume_migrate_with_option(self): arglist = [ "--force-host-copy", "--lock-volume", "--host", "host@backend-name#pool", - self._volume.id, + self.volume.id, ] verifylist = [ ("force_host_copy", True), ("lock_volume", True), ("host", "host@backend-name#pool"), - ("volume", self._volume.id), + ("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) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, ignore_missing=False + ) + self.volume_sdk_client.migrate_volume.assert_called_once_with( + self.volume.id, + host="host@backend-name#pool", + force_host_copy=True, + lock_volume=True, + ) + def test_volume_migrate_without_host(self): arglist = [ - self._volume.id, + self.volume.id, ] verifylist = [ ("force_host_copy", False), ("lock_volume", False), - ("volume", self._volume.id), + ("volume", self.volume.id), ] self.assertRaises( @@ -1740,6 +1748,9 @@ def test_volume_migrate_without_host(self): verifylist, ) + self.volume_sdk_client.find_volume.assert_not_called() + self.volume_sdk_client.migrate_volume.assert_not_called() + class TestVolumeSet(volume_fakes.TestVolume): volume_type = volume_fakes.create_one_volume_type() diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 0e351f741..9881172d6 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -613,13 +613,15 @@ def get_parser(self, prog_name): 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_client = self.app.client_manager.sdk_connection.volume + volume = volume_client.find_volume( + parsed_args.volume, ignore_missing=False + ) + volume_client.migrate_volume( volume.id, - parsed_args.host, - parsed_args.force_host_copy, - parsed_args.lock_volume, + host=parsed_args.host, + force_host_copy=parsed_args.force_host_copy, + lock_volume=parsed_args.lock_volume, ) diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index ee2cb260f..2ecc11ba6 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -761,16 +761,19 @@ def get_parser(self, prog_name): "(possibly by another operation)" ), ) + # TODO(stephenfin): Add --cluster argument 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_client = self.app.client_manager.sdk_connection.volume + volume = volume_client.find_volume( + parsed_args.volume, ignore_missing=False + ) + volume_client.migrate_volume( volume.id, - parsed_args.host, - parsed_args.force_host_copy, - parsed_args.lock_volume, + host=parsed_args.host, + force_host_copy=parsed_args.force_host_copy, + lock_volume=parsed_args.lock_volume, ) From c7d465a221ced77427d33295b8dc79409b7c85a0 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Mar 2025 14:16:02 +0000 Subject: [PATCH 181/245] volume: Migrate 'volume show' to SDK Change-Id: Ibd9d7a62c2500a1f31aa2d3d13ac7e8bad4e6964 Signed-off-by: Stephen Finucane --- .../tests/functional/volume/v2/test_volume.py | 2 +- .../tests/functional/volume/v3/test_volume.py | 2 +- .../tests/unit/volume/v2/test_volume.py | 132 +++++++++++----- .../tests/unit/volume/v3/test_volume.py | 141 +++++++++++++----- openstackclient/volume/v2/volume.py | 21 +-- openstackclient/volume/v3/volume.py | 23 +-- 6 files changed, 212 insertions(+), 109 deletions(-) diff --git a/openstackclient/tests/functional/volume/v2/test_volume.py b/openstackclient/tests/functional/volume/v2/test_volume.py index 466785875..32b60dfaa 100644 --- a/openstackclient/tests/functional/volume/v2/test_volume.py +++ b/openstackclient/tests/functional/volume/v2/test_volume.py @@ -171,7 +171,7 @@ def test_volume_set_and_unset(self): cmd_output["volume_image_metadata"], ) self.assertEqual( - 'true', + True, cmd_output["bootable"], ) diff --git a/openstackclient/tests/functional/volume/v3/test_volume.py b/openstackclient/tests/functional/volume/v3/test_volume.py index d4cd0ac63..07a795916 100644 --- a/openstackclient/tests/functional/volume/v3/test_volume.py +++ b/openstackclient/tests/functional/volume/v3/test_volume.py @@ -172,7 +172,7 @@ def test_volume_set_and_unset(self): cmd_output["volume_image_metadata"], ) self.assertEqual( - 'true', + True, cmd_output["bootable"], ) diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index 5b81bdf7a..b68020fa9 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -12,6 +12,7 @@ # under the License. from unittest import mock +import uuid from openstack.block_storage.v2 import snapshot as _snapshot from openstack.block_storage.v2 import volume as _volume @@ -1622,42 +1623,83 @@ def test_volume_set_with_only_retype_policy(self, mock_warning): self.assertIsNone(result) -class TestVolumeShow(TestVolume): +class TestVolumeShow(volume_fakes.TestVolume): def setUp(self): super().setUp() - self._volume = volume_fakes.create_one_volume() - self.volumes_mock.get.return_value = self._volume - # Get the command object to test + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume + + self.columns = ( + 'attachments', + 'availability_zone', + 'bootable', + 'consistencygroup_id', + 'created_at', + 'description', + 'encrypted', + 'id', + 'multiattach', + 'name', + 'os-vol-host-attr:host', + 'os-vol-mig-status-attr:migstat', + 'os-vol-mig-status-attr:name_id', + 'os-vol-tenant-attr:tenant_id', + 'os-volume-replication:driver_data', + 'os-volume-replication:extended_status', + 'properties', + 'replication_status', + 'size', + 'snapshot_id', + 'source_volid', + 'status', + 'type', + 'updated_at', + 'user_id', + 'volume_image_metadata', + ) + self.data = ( + self.volume.attachments, + self.volume.availability_zone, + self.volume.is_bootable, + self.volume.consistency_group_id, + self.volume.created_at, + self.volume.description, + self.volume.is_encrypted, + self.volume.id, + self.volume.is_multiattach, + self.volume.name, + self.volume.host, + self.volume.migration_status, + self.volume.migration_id, + self.volume.project_id, + self.volume.replication_driver_data, + self.volume.extended_replication_status, + format_columns.DictColumn(self.volume.metadata), + self.volume.replication_status, + self.volume.size, + self.volume.snapshot_id, + self.volume.source_volume_id, + self.volume.status, + self.volume.volume_type, + self.volume.updated_at, + self.volume.user_id, + self.volume.volume_image_metadata, + ) + self.cmd = volume.ShowVolume(self.app, None) def test_volume_show(self): - arglist = [self._volume.id] - verifylist = [("volume", self._volume.id)] + arglist = [self.volume.id] + verifylist = [("volume", self.volume.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_with(self._volume.id) - self.assertEqual( - tuple(sorted(self._volume.keys())), - columns, - ) - self.assertTupleEqual( - ( - self._volume.attachments, - self._volume.availability_zone, - self._volume.bootable, - self._volume.description, - self._volume.id, - self._volume.name, - format_columns.DictColumn(self._volume.metadata), - self._volume.size, - self._volume.snapshot_id, - self._volume.status, - self._volume.volume_type, - ), - data, + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, ignore_missing=False ) @@ -1748,31 +1790,47 @@ def test_volume_unset_image_property_fail(self): ) -class TestColumns(TestVolume): +class TestColumns(volume_fakes.TestVolume): def test_attachments_column_without_server_cache(self): - _volume = volume_fakes.create_one_volume() - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] + vol = sdk_fakes.generate_fake_resource( + _volume.Volume, + attachments=[ + { + 'device': '/dev/' + uuid.uuid4().hex, + 'server_id': uuid.uuid4().hex, + }, + ], + ) + server_id = vol.attachments[0]['server_id'] + device = vol.attachments[0]['device'] - col = volume.AttachmentsColumn(_volume.attachments, {}) + col = volume.AttachmentsColumn(vol.attachments, {}) self.assertEqual( f'Attached to {server_id} on {device} ', col.human_readable(), ) - self.assertEqual(_volume.attachments, col.machine_readable()) + self.assertEqual(vol.attachments, col.machine_readable()) def test_attachments_column_with_server_cache(self): - _volume = volume_fakes.create_one_volume() + vol = sdk_fakes.generate_fake_resource( + _volume.Volume, + attachments=[ + { + 'device': '/dev/' + uuid.uuid4().hex, + 'server_id': uuid.uuid4().hex, + }, + ], + ) - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] + server_id = vol.attachments[0]['server_id'] + device = vol.attachments[0]['device'] fake_server = mock.Mock() fake_server.name = 'fake-server-name' server_cache = {server_id: fake_server} - col = volume.AttachmentsColumn(_volume.attachments, server_cache) + col = volume.AttachmentsColumn(vol.attachments, server_cache) self.assertEqual( 'Attached to {} on {} '.format('fake-server-name', device), col.human_readable(), ) - self.assertEqual(_volume.attachments, col.machine_readable()) + self.assertEqual(vol.attachments, col.machine_readable()) diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index 764958af6..5fb35a067 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -13,6 +13,7 @@ import copy from unittest import mock +import uuid from openstack.block_storage.v3 import backup as _backup from openstack.block_storage.v3 import block_storage_summary as _summary @@ -2004,41 +2005,91 @@ class TestVolumeShow(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_sdk_client.find_volume.return_value = self.volume + + self.columns = ( + 'attachments', + 'availability_zone', + 'bootable', + 'cluster_name', + 'consistencygroup_id', + 'consumes_quota', + 'created_at', + 'description', + 'encrypted', + 'encryption_key_id', + 'group_id', + 'id', + 'multiattach', + 'name', + 'os-vol-host-attr:host', + 'os-vol-mig-status-attr:migstat', + 'os-vol-mig-status-attr:name_id', + 'os-vol-tenant-attr:tenant_id', + 'properties', + 'provider_id', + 'replication_status', + 'service_uuid', + 'shared_targets', + 'size', + 'snapshot_id', + 'source_volid', + 'status', + 'type', + 'updated_at', + 'user_id', + 'volume_image_metadata', + 'volume_type_id', + ) + self.data = ( + self.volume.attachments, + self.volume.availability_zone, + self.volume.is_bootable, + self.volume.cluster_name, + self.volume.consistency_group_id, + self.volume.consumes_quota, + self.volume.created_at, + self.volume.description, + self.volume.is_encrypted, + self.volume.encryption_key_id, + self.volume.group_id, + self.volume.id, + self.volume.is_multiattach, + self.volume.name, + self.volume.host, + self.volume.migration_status, + self.volume.migration_id, + self.volume.project_id, + format_columns.DictColumn(self.volume.metadata), + self.volume.provider_id, + self.volume.replication_status, + self.volume.service_uuid, + self.volume.shared_targets, + self.volume.size, + self.volume.snapshot_id, + self.volume.source_volume_id, + self.volume.status, + self.volume.volume_type, + self.volume.updated_at, + self.volume.user_id, + self.volume.volume_image_metadata, + self.volume.volume_type_id, + ) - self._volume = volume_fakes.create_one_volume() - self.volumes_mock.get.return_value = self._volume - # Get the command object to test self.cmd = volume.ShowVolume(self.app, None) def test_volume_show(self): - arglist = [self._volume.id] - verifylist = [("volume", self._volume.id)] + arglist = [self.volume.id] + verifylist = [("volume", self.volume.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_with(self._volume.id) - self.assertEqual( - tuple(sorted(self._volume.keys())), - columns, - ) - self.assertTupleEqual( - ( - self._volume.attachments, - self._volume.availability_zone, - self._volume.bootable, - self._volume.description, - self._volume.id, - self._volume.name, - format_columns.DictColumn(self._volume.metadata), - self._volume.size, - self._volume.snapshot_id, - self._volume.status, - self._volume.volume_type, - ), - data, + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, ignore_missing=False ) @@ -2284,29 +2335,45 @@ def test_volume_revert_to_snapshot(self): class TestColumns(volume_fakes.TestVolume): def test_attachments_column_without_server_cache(self): - _volume = volume_fakes.create_one_volume() - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] + vol = sdk_fakes.generate_fake_resource( + _volume.Volume, + attachments=[ + { + 'device': '/dev/' + uuid.uuid4().hex, + 'server_id': uuid.uuid4().hex, + }, + ], + ) + server_id = vol.attachments[0]['server_id'] + device = vol.attachments[0]['device'] - col = volume.AttachmentsColumn(_volume.attachments, {}) + col = volume.AttachmentsColumn(vol.attachments, {}) self.assertEqual( f'Attached to {server_id} on {device} ', col.human_readable(), ) - self.assertEqual(_volume.attachments, col.machine_readable()) + self.assertEqual(vol.attachments, col.machine_readable()) def test_attachments_column_with_server_cache(self): - _volume = volume_fakes.create_one_volume() + vol = sdk_fakes.generate_fake_resource( + _volume.Volume, + attachments=[ + { + 'device': '/dev/' + uuid.uuid4().hex, + 'server_id': uuid.uuid4().hex, + }, + ], + ) - server_id = _volume.attachments[0]['server_id'] - device = _volume.attachments[0]['device'] + server_id = vol.attachments[0]['server_id'] + device = vol.attachments[0]['device'] fake_server = mock.Mock() fake_server.name = 'fake-server-name' server_cache = {server_id: fake_server} - col = volume.AttachmentsColumn(_volume.attachments, server_cache) + col = volume.AttachmentsColumn(vol.attachments, server_cache) self.assertEqual( 'Attached to {} on {} '.format('fake-server-name', device), col.human_readable(), ) - self.assertEqual(_volume.attachments, col.machine_readable()) + self.assertEqual(vol.attachments, col.machine_readable()) diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 9881172d6..fa76d776e 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -955,24 +955,13 @@ def get_parser(self, prog_name): 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) - - # Special mapping for columns to make the output easier to read: - # 'metadata' --> 'properties' - # 'volume_type' --> 'type' - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - }, + volume_client = self.app.client_manager.sdk_connection.volume + volume = volume_client.find_volume( + parsed_args.volume, ignore_missing=False ) - # Remove key links from being displayed - volume._info.pop("links", None) - return zip(*sorted(volume._info.items())) + data = _format_volume(volume) + return zip(*sorted(data.items())) class UnsetVolume(command.Command): diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 2ecc11ba6..54dd5c754 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -1116,24 +1116,13 @@ def get_parser(self, prog_name): 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 = self.app.client_manager.sdk_connection.volume + volume = volume_client.find_volume( + parsed_args.volume, ignore_missing=False + ) - # Special mapping for columns to make the output easier to read: - # 'metadata' --> 'properties' - # 'volume_type' --> 'type' - volume._info.update( - { - 'properties': format_columns.DictColumn( - volume._info.pop('metadata') - ), - 'type': volume._info.pop('volume_type'), - }, - ) - - # Remove key links from being displayed - volume._info.pop("links", None) - return zip(*sorted(volume._info.items())) + data = _format_volume(volume) + return zip(*sorted(data.items())) class UnsetVolume(command.Command): From 46b25c7c0babbf9bfe7b64a5de87994364b46675 Mon Sep 17 00:00:00 2001 From: Alexey Stupnikov Date: Thu, 24 Jul 2025 14:50:19 +0200 Subject: [PATCH 182/245] network: Add '--project' to SG rule list command Include '--project' and '--project-domain' filtering options to the 'security group rule list'. Closes-Bug: #1648317 Change-Id: I19e423906846073cfa1e45b4a295b3a8f5d11970 Signed-off-by: Alexey Stupnikov --- .../network/v2/security_group_rule.py | 17 +++++++ .../v2/test_security_group_rule_network.py | 49 +++++++++++++++++++ .../notes/bug-1648317-2d12dabc357c4d52.yaml | 10 ++++ 3 files changed, 76 insertions(+) create mode 100644 releasenotes/notes/bug-1648317-2d12dabc357c4d52.yaml diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index 4907fe66d..6cb0f7fa3 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -427,6 +427,14 @@ def update_parser_network(self, parser): _("**Deprecated** This argument is no longer needed") ), ) + parser.add_argument( + '--project', + metavar='', + help=self.enhance_help_neutron(_("Owner's project (name or ID)")), + ) + identity_common.add_project_domain_option_to_parser( + parser, enhance_help=self.enhance_help_neutron + ) return parser def update_parser_compute(self, parser): @@ -503,6 +511,15 @@ def take_action_network(self, client, parsed_args): query['direction'] = 'egress' if parsed_args.protocol is not None: query['protocol'] = parsed_args.protocol + if parsed_args.project is not None: + identity_client = self.app.client_manager.identity + project_id = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + query['tenant_id'] = project_id + query['project_id'] = project_id rules = [ self._format_network_security_group_rule(r) diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py index 34cf12e01..d5630c368 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py @@ -1251,6 +1251,55 @@ def test_list_with_wrong_egress(self): self.assertEqual(self.expected_columns_no_group, columns) self.assertEqual(self.expected_data_no_group, list(data)) + def test_list_with_project(self): + project = identity_fakes.FakeProject.create_one_project() + self._security_group_rule_tcp.port_range_min = 80 + self.projects_mock.get.return_value = project + + arglist = [ + '--project', + project.id, + ] + verifylist = [ + ('project', project.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'tenant_id': project.id, 'project_id': project.id} + + self.network_client.security_group_rules.assert_called_once_with( + **filters + ) + self.assertEqual(self.expected_columns_no_group, columns) + self.assertEqual(self.expected_data_no_group, list(data)) + + def test_list_with_project_domain(self): + project = identity_fakes.FakeProject.create_one_project() + self._security_group_rule_tcp.port_range_min = 80 + self.projects_mock.get.return_value = project + + arglist = [ + '--project', + project.id, + '--project-domain', + project.domain_id, + ] + verifylist = [ + ('project', project.id), + ('project_domain', project.domain_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'tenant_id': project.id, 'project_id': project.id} + + self.network_client.security_group_rules.assert_called_once_with( + **filters + ) + self.assertEqual(self.expected_columns_no_group, columns) + self.assertEqual(self.expected_data_no_group, list(data)) + class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): # The security group rule to be shown. diff --git a/releasenotes/notes/bug-1648317-2d12dabc357c4d52.yaml b/releasenotes/notes/bug-1648317-2d12dabc357c4d52.yaml new file mode 100644 index 000000000..ffd1ff90b --- /dev/null +++ b/releasenotes/notes/bug-1648317-2d12dabc357c4d52.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + Add ``--project`` and --project-domain`` options to the following network + commands: + + - ``openstack security group rule list`` + + [Bug `1648317 `_] + From e37148484c3db08200ed163d106a717b0effa1f0 Mon Sep 17 00:00:00 2001 From: Alexey Stupnikov Date: Fri, 25 Jul 2025 19:23:35 +0200 Subject: [PATCH 183/245] Remap custom named Image attributes when unsetting Some Image attributes defined in openstacksdk are named differently from actual properties managed by Glance. Because openstackclient checked property names to be unset against Image object properties, it was impossible to unset such properties. This patch introduces a IMAGE_ATTRIBUTES_CUSTOM_NAMES dictionary mapping real property names with custom attribute names. Closes-bug: #2115732 Change-Id: I7296fc293dff9208464c9a07f58ce3e9ffabd3e9 Signed-off-by: Alexey Stupnikov --- openstackclient/image/v2/image.py | 18 ++++++++++++++++++ .../tests/unit/image/v2/test_image.py | 10 +++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 6c7ca740e..d2a1a74ea 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -55,6 +55,19 @@ "iso", "ploop", ] +# A list of openstacksdk Image object attributes (values) that named +# differently from actual properties stored by Glance (keys). +IMAGE_ATTRIBUTES_CUSTOM_NAMES = { + 'os_hidden': 'is_hidden', + 'protected': 'is_protected', + 'os_hash_algo': 'hash_algo', + 'os_hash_value': 'hash_value', + 'img_config_drive': 'needs_config_drive', + 'os_secure_boot': 'needs_secure_boot', + 'hw_vif_multiqueue_enabled': 'is_hw_vif_multiqueue_enabled', + 'hw_boot_menu': 'is_hw_boot_menu_enabled', + 'auto_disk_config': 'has_auto_disk_config', +} MEMBER_STATUS_CHOICES = ["accepted", "pending", "rejected", "all"] LOG = logging.getLogger(__name__) @@ -1478,6 +1491,11 @@ def take_action(self, parsed_args): ) new_props.pop(k, None) kwargs['properties'] = new_props + elif ( + k in IMAGE_ATTRIBUTES_CUSTOM_NAMES + and IMAGE_ATTRIBUTES_CUSTOM_NAMES[k] in image + ): + delattr(image, IMAGE_ATTRIBUTES_CUSTOM_NAMES[k]) else: LOG.error( _( diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index 95d384c24..4f5db9f00 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -1779,6 +1779,7 @@ def setUp(self): attrs['hw_rng_model'] = 'virtio' attrs['prop'] = 'test' attrs['prop2'] = 'fake' + attrs['os_secure_boot'] = 'required' self.image = image_fakes.create_one_image(attrs) self.image_client.find_image.return_value = self.image @@ -1822,11 +1823,18 @@ def test_image_unset_property_option(self): 'hw_rng_model', '--property', 'prop', + '--property', + 'os_secure_boot', self.image.id, ] + # openstacksdk translates 'os_secure_boot' property to + # 'needs_secure_boot' Image attribute. This is true for + # all IMAGE_ATTRIBUTES_CUSTOM_NAMES keys + self.assertEqual(self.image.needs_secure_boot, 'required') + self.assertFalse(hasattr(self.image, 'os_secure_boot')) verifylist = [ - ('properties', ['hw_rng_model', 'prop']), + ('properties', ['hw_rng_model', 'prop', 'os_secure_boot']), ('image', self.image.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) From 9f55b253a3fb9303dfe6b136fb0d19cfd58d6cf0 Mon Sep 17 00:00:00 2001 From: Mridula Joshi Date: Thu, 21 Dec 2023 07:38:18 +0000 Subject: [PATCH 184/245] Adds CLI support for ``glance md-namespace-objects-delete`` This patch adds operation which delete all metadef object inside a namespace. This can be implemented by `image metadef object delete` Change-Id: Ib196e295aad1921d8bc0c451522e0ad530389134 Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/901671 Signed-off-by: Cyril Roelandt --- doc/source/cli/data/glance.csv | 2 +- openstackclient/image/v2/metadef_objects.py | 10 +++++++-- .../unit/image/v2/test_metadef_objects.py | 22 +++++++++++++++++++ ...espace-object-delete-b6b2de24fc66e602.yaml | 4 ++++ 4 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/add-image-metadef-namespace-object-delete-b6b2de24fc66e602.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index c248904fe..61cd6c1e5 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -26,7 +26,7 @@ md-namespace-create,image metadef namespace create,Create a new metadata definit md-namespace-delete,image metadef namespace delete,Delete specified metadata definitions namespace with its contents. md-namespace-import,,Import a metadata definitions namespace from file or standard input. md-namespace-list,image metadef namespace list,List metadata definitions namespaces. -md-namespace-objects-delete,,Delete all metadata definitions objects inside a specific namespace. +md-namespace-objects-delete,image metadef object delete,Delete all metadata definitions objects inside a specific namespace. md-namespace-properties-delete,,Delete all metadata definitions property inside a specific namespace. md-namespace-resource-type-list,image metadef resource type association list,List resource types associated to specific namespace. md-namespace-show,image metadef namespace show,Describe a specific metadata definitions namespace. diff --git a/openstackclient/image/v2/metadef_objects.py b/openstackclient/image/v2/metadef_objects.py index 04c0fa55f..09fc249cb 100644 --- a/openstackclient/image/v2/metadef_objects.py +++ b/openstackclient/image/v2/metadef_objects.py @@ -123,8 +123,11 @@ def get_parser(self, prog_name): parser.add_argument( "objects", metavar="", - nargs="+", - help=_("Metadef object(s) to delete (name)"), + nargs="*", + help=_( + "Metadef object(s) to delete (name) " + "(omit this argument to delete all objects in the namespace)" + ), ) return parser @@ -133,6 +136,9 @@ def take_action(self, parsed_args): namespace = parsed_args.namespace + if not parsed_args.objects: + return image_client.delete_all_metadef_objects(namespace) + result = 0 for obj in parsed_args.objects: try: diff --git a/openstackclient/tests/unit/image/v2/test_metadef_objects.py b/openstackclient/tests/unit/image/v2/test_metadef_objects.py index 4996060d9..6306e23eb 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_objects.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_objects.py @@ -113,6 +113,7 @@ def setUp(self): super().setUp() self.image_client.delete_metadef_object.return_value = None + self.image_client.delete_all_metadef_objects.return_value = None self.cmd = metadef_objects.DeleteMetadefObject(self.app, None) def test_object_delete(self): @@ -126,8 +127,29 @@ def test_object_delete(self): result = self.cmd.take_action(parsed_args) + self.image_client.delete_metadef_object.assert_called_once_with( + self.image_client.get_metadef_object(), + self._metadef_namespace.namespace, + ) + self.image_client.delete_all_metadef_objects.assert_not_called() self.assertIsNone(result) + def test_object_delete_all(self): + arglist = [ + self._metadef_namespace.namespace, + ] + + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.image_client.delete_all_metadef_objects.assert_called_with( + self._metadef_namespace.namespace, + ) + self.image_client.delete_metadef_object.assert_not_called() + class TestMetadefObjectList(fakes.TestImagev2): _metadef_namespace = fakes.create_one_metadef_namespace() diff --git a/releasenotes/notes/add-image-metadef-namespace-object-delete-b6b2de24fc66e602.yaml b/releasenotes/notes/add-image-metadef-namespace-object-delete-b6b2de24fc66e602.yaml new file mode 100644 index 000000000..7861a0c7a --- /dev/null +++ b/releasenotes/notes/add-image-metadef-namespace-object-delete-b6b2de24fc66e602.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Adds operation which deletes all metadef object inside a namespace. From e8a7db58589c446d2a9a1ed975115c531b6db055 Mon Sep 17 00:00:00 2001 From: dna Date: Wed, 21 May 2025 23:42:06 +0900 Subject: [PATCH 185/245] tests: Simplify mocking in server tests Replace assignments of `Mock` objects to methods that are already mocked in the class functions within test_server.py Change-Id: I446632301c1b9f94545a0b8314e54e761d5d296f Signed-off-by: dna Story: 2011459 Task: 52211 --- .../tests/unit/compute/v2/test_server.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 0898c67bf..f685d1615 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -408,7 +408,7 @@ def setUp(self): self.server = compute_fakes.create_one_server() self.compute_client.find_server.return_value = self.server - self.network_client.update_ip = mock.Mock(return_value=None) + self.network_client.update_ip.return_value = None # Get the command object to test self.cmd = server.AddFloatingIP(self.app, None) @@ -416,8 +416,8 @@ def setUp(self): def test_server_add_floating_ip(self): _port = network_fakes.create_one_port() _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - self.network_client.find_ip = mock.Mock(return_value=_floating_ip) - self.network_client.ports = mock.Mock(return_value=[_port]) + self.network_client.find_ip.return_value = _floating_ip + self.network_client.ports.return_value = [_port] arglist = [ self.server.id, _floating_ip['floating_ip_address'], @@ -448,8 +448,8 @@ def test_server_add_floating_ip(self): def test_server_add_floating_ip_no_ports(self): floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - self.network_client.find_ip = mock.Mock(return_value=floating_ip) - self.network_client.ports = mock.Mock(return_value=[]) + self.network_client.find_ip.return_value = floating_ip + self.network_client.ports.return_value = [] arglist = [ self.server.id, @@ -479,17 +479,17 @@ def test_server_add_floating_ip_no_ports(self): def test_server_add_floating_ip_no_external_gateway(self, success=False): _port = network_fakes.create_one_port() _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - self.network_client.find_ip = mock.Mock(return_value=_floating_ip) + self.network_client.find_ip.return_value = _floating_ip return_value = [_port] # In the success case, we'll have two ports, where the first port is # not attached to an external gateway but the second port is. if success: return_value.append(_port) - self.network_client.ports = mock.Mock(return_value=return_value) + self.network_client.ports.return_value = return_value side_effect = [sdk_exceptions.NotFoundException()] if success: side_effect.append(None) - self.network_client.update_ip = mock.Mock(side_effect=side_effect) + self.network_client.update_ip.side_effect = side_effect arglist = [ self.server.id, _floating_ip['floating_ip_address'], @@ -535,8 +535,8 @@ def test_server_add_floating_ip_one_external_gateway(self): def test_server_add_floating_ip_with_fixed_ip(self): _port = network_fakes.create_one_port() _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - self.network_client.find_ip = mock.Mock(return_value=_floating_ip) - self.network_client.ports = mock.Mock(return_value=[_port]) + self.network_client.find_ip.return_value = _floating_ip + self.network_client.ports.return_value = [_port] # The user has specified a fixed ip that matches one of the ports # already attached to the instance. arglist = [ @@ -575,8 +575,8 @@ def test_server_add_floating_ip_with_fixed_ip(self): def test_server_add_floating_ip_with_fixed_ip_no_port_found(self): _port = network_fakes.create_one_port() _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - self.network_client.find_ip = mock.Mock(return_value=_floating_ip) - self.network_client.ports = mock.Mock(return_value=[_port]) + self.network_client.find_ip.return_value = _floating_ip + self.network_client.ports.return_value = [_port] # The user has specified a fixed ip that does not match any of the # ports already attached to the instance. nonexistent_ip = '10.0.0.9' @@ -7351,14 +7351,14 @@ class TestServerRemoveFloatingIPNetwork(network_fakes.TestNetworkV2): def setUp(self): super().setUp() - self.network_client.update_ip = mock.Mock(return_value=None) + self.network_client.update_ip.return_value = None # Get the command object to test self.cmd = server.RemoveFloatingIP(self.app, None) def test_server_remove_floating_ip_default(self): _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - self.network_client.find_ip = mock.Mock(return_value=_floating_ip) + self.network_client.find_ip.return_value = _floating_ip arglist = [ 'fake_server', _floating_ip['ip'], From a99ae364fc094ccd6bbcdd2b7fc64f4395bef6ef Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 14 Aug 2025 12:01:03 +0100 Subject: [PATCH 186/245] tests: Avoid unnecessary mocks Change-Id: Ibb1bf5c29bf37d3f31889b091a055d0308e8cd85 Signed-off-by: Stephen Finucane --- .../tests/unit/compute/v2/test_server.py | 65 ++++++++----------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index f685d1615..86951360b 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -89,10 +89,6 @@ def setUp(self): # Get the command object to test self.cmd = server.AddFixedIP(self.app, None) - # Mock network methods - self.find_network = mock.Mock() - self.app.client_manager.network.find_network = self.find_network - def test_server_add_fixed_ip_pre_v249_with_tag(self): self.set_compute_api_version('2.48') @@ -614,9 +610,6 @@ def setUp(self): # Get the command object to test self.cmd = server.AddPort(self.app, None) - self.find_port = mock.Mock() - self.app.client_manager.network.find_port = self.find_port - def _test_server_add_port(self, port_id): servers = self.setup_sdk_servers_mock(count=1) port = 'fake-port' @@ -636,21 +629,23 @@ def _test_server_add_port(self, port_id): self.assertIsNone(result) def test_server_add_port(self): - self._test_server_add_port(self.find_port.return_value.id) - self.find_port.assert_called_once_with( + self._test_server_add_port( + self.network_client.find_port.return_value.id + ) + self.network_client.find_port.assert_called_once_with( 'fake-port', ignore_missing=False ) def test_server_add_port_no_neutron(self): self.app.client_manager.network_endpoint_enabled = False self._test_server_add_port('fake-port') - self.find_port.assert_not_called() + self.network_client.find_port.assert_not_called() def test_server_add_port_with_tag(self): self.set_compute_api_version('2.49') servers = self.setup_sdk_servers_mock(count=1) - self.find_port.return_value.id = 'fake-port' + self.network_client.find_port.return_value.id = 'fake-port' arglist = [ servers[0].id, 'fake-port', @@ -675,7 +670,7 @@ def test_server_add_port_with_tag_pre_v249(self): self.set_compute_api_version('2.48') servers = self.setup_sdk_servers_mock(count=1) - self.find_port.return_value.id = 'fake-port' + self.network_client.find_port.return_value.id = 'fake-port' arglist = [ servers[0].id, 'fake-port', @@ -1038,9 +1033,6 @@ def setUp(self): # Get the command object to test self.cmd = server.AddNetwork(self.app, None) - self.find_network = mock.Mock() - self.app.client_manager.network.find_network = self.find_network - def _test_server_add_network(self, net_id): servers = self.setup_sdk_servers_mock(count=1) network = 'fake-network' @@ -1060,21 +1052,23 @@ def _test_server_add_network(self, net_id): self.assertIsNone(result) def test_server_add_network(self): - self._test_server_add_network(self.find_network.return_value.id) - self.find_network.assert_called_once_with( + self._test_server_add_network( + self.network_client.find_network.return_value.id + ) + self.network_client.find_network.assert_called_once_with( 'fake-network', ignore_missing=False ) def test_server_add_network_no_neutron(self): self.app.client_manager.network_endpoint_enabled = False self._test_server_add_network('fake-network') - self.find_network.assert_not_called() + self.network_client.find_network.assert_not_called() def test_server_add_network_with_tag(self): self.set_compute_api_version('2.49') servers = self.setup_sdk_servers_mock(count=1) - self.find_network.return_value.id = 'fake-network' + self.network_client.find_network.return_value.id = 'fake-network' arglist = [ servers[0].id, @@ -1100,7 +1094,7 @@ def test_server_add_network_with_tag_pre_v249(self): self.set_compute_api_version('2.48') servers = self.setup_sdk_servers_mock(count=1) - self.find_network.return_value.id = 'fake-network' + self.network_client.find_network.return_value.id = 'fake-network' arglist = [ servers[0].id, @@ -7391,9 +7385,6 @@ def setUp(self): # Get the command object to test self.cmd = server.RemovePort(self.app, None) - self.find_port = mock.Mock() - self.app.client_manager.network.find_port = self.find_port - def _test_server_remove_port(self, port_id): servers = self.setup_sdk_servers_mock(count=1) port = 'fake-port' @@ -7416,15 +7407,17 @@ def _test_server_remove_port(self, port_id): self.assertIsNone(result) def test_server_remove_port(self): - self._test_server_remove_port(self.find_port.return_value.id) - self.find_port.assert_called_once_with( + self._test_server_remove_port( + self.network_client.find_port.return_value.id + ) + self.network_client.find_port.assert_called_once_with( 'fake-port', ignore_missing=False ) def test_server_remove_port_no_neutron(self): self.app.client_manager.network_endpoint_enabled = False self._test_server_remove_port('fake-port') - self.find_port.assert_not_called() + self.network_client.find_port.assert_not_called() class TestServerRemoveNetwork(TestServer): @@ -7434,16 +7427,12 @@ def setUp(self): # Get the command object to test self.cmd = server.RemoveNetwork(self.app, None) - # Set method to be tested. - self.fake_inf = mock.Mock() - - self.find_network = mock.Mock() - self.app.client_manager.network.find_network = self.find_network - self.compute_client.server_interfaces.return_value = [self.fake_inf] + self.fake_if = mock.Mock() + self.compute_client.server_interfaces.return_value = [self.fake_if] def _test_server_remove_network(self, network_id): - self.fake_inf.net_id = network_id - self.fake_inf.port_id = 'fake-port' + self.fake_if.net_id = network_id + self.fake_if.port_id = 'fake-port' servers = self.setup_sdk_servers_mock(count=1) network = 'fake-network' @@ -7468,15 +7457,17 @@ def _test_server_remove_network(self, network_id): self.assertIsNone(result) def test_server_remove_network(self): - self._test_server_remove_network(self.find_network.return_value.id) - self.find_network.assert_called_once_with( + self._test_server_remove_network( + self.network_client.find_network.return_value.id + ) + self.network_client.find_network.assert_called_once_with( 'fake-network', ignore_missing=False ) def test_server_remove_network_no_neutron(self): self.app.client_manager.network_endpoint_enabled = False self._test_server_remove_network('fake-network') - self.find_network.assert_not_called() + self.network_client.find_network.assert_not_called() class TestServerRemoveSecurityGroup(TestServer): From 81db99b32bd3532a764c2ece7187d0f5b04abf75 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 15 Aug 2025 11:47:09 +0100 Subject: [PATCH 187/245] doc: Indicate md-namespace-import as WONTFIX This is a meta command that can be easily achieved via some shell scripting. We don't need it in OSC. Change-Id: Ia3fc8d0458cb0c0dc4695347ef953028112a9c49 Signed-off-by: Stephen Finucane --- doc/source/cli/data/glance.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index 61cd6c1e5..b4758ffda 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -24,7 +24,7 @@ location-delete,,Remove locations (and related metadata) from an image. location-update,,Update metadata of an image's location. md-namespace-create,image metadef namespace create,Create a new metadata definitions namespace. md-namespace-delete,image metadef namespace delete,Delete specified metadata definitions namespace with its contents. -md-namespace-import,,Import a metadata definitions namespace from file or standard input. +md-namespace-import,WONTFIX,Import a metadata definitions namespace from file or standard input. md-namespace-list,image metadef namespace list,List metadata definitions namespaces. md-namespace-objects-delete,image metadef object delete,Delete all metadata definitions objects inside a specific namespace. md-namespace-properties-delete,,Delete all metadata definitions property inside a specific namespace. From 13fe80196827d38ad977458905a8741e1fc28848 Mon Sep 17 00:00:00 2001 From: Hang Yang Date: Fri, 23 Oct 2020 11:18:01 -0500 Subject: [PATCH 188/245] Support image save with chunk-size option Add '--chunk-size' option to 'image save' command to control the size of bytes to read at one time. Change-Id: I0a02323384433010b8c6804a4337040acb13da8f Signed-off-by: Hang Yang --- openstackclient/image/v1/image.py | 17 ++++++- openstackclient/image/v2/image.py | 17 ++++++- .../tests/unit/image/v1/test_image.py | 47 +++++++++++++++++++ .../tests/unit/image/v2/test_image.py | 24 +++++++++- ...k-size-to-image-save-37871f9e62693264.yaml | 5 ++ 5 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/add-chunk-size-to-image-save-37871f9e62693264.yaml diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 490b4122f..91b0db428 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -528,6 +528,16 @@ class SaveImage(command.Command): def get_parser(self, prog_name): parser = super().get_parser(prog_name) + parser.add_argument( + "--chunk-size", + type=int, + default=1024, + metavar="", + help=_( + "Size in bytes to read from the wire and buffer at one " + "time (default: 1024)" + ), + ) parser.add_argument( "--file", metavar="", @@ -550,7 +560,12 @@ def take_action(self, parsed_args): if output_file is None: output_file = getattr(sys.stdout, "buffer", sys.stdout) - image_client.download_image(image.id, stream=True, output=output_file) + image_client.download_image( + image.id, + stream=True, + output=output_file, + chunk_size=parsed_args.chunk_size, + ) class SetImage(command.Command): diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index d2a1a74ea..9cd24f2ed 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -1066,6 +1066,16 @@ class SaveImage(command.Command): def get_parser(self, prog_name): parser = super().get_parser(prog_name) + parser.add_argument( + "--chunk-size", + type=int, + default=1024, + metavar="", + help=_( + "Size in bytes to read from the wire and buffer at one " + "time (default: 1024)" + ), + ) parser.add_argument( "--file", metavar="", @@ -1090,7 +1100,12 @@ def take_action(self, parsed_args): if output_file is None: output_file = getattr(sys.stdout, "buffer", sys.stdout) - image_client.download_image(image.id, stream=True, output=output_file) + image_client.download_image( + image.id, + stream=True, + output=output_file, + chunk_size=parsed_args.chunk_size, + ) class SetImage(command.Command): diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py index 11d083020..2d870ff89 100644 --- a/openstackclient/tests/unit/image/v1/test_image.py +++ b/openstackclient/tests/unit/image/v1/test_image.py @@ -757,3 +757,50 @@ def test_image_show_human_readable(self): size_index = columns.index('size') self.assertEqual(data[size_index].human_readable(), '2K') + + +class TestImageSave(image_fakes.TestImagev1): + image = image_fakes.create_one_image({}) + + def setUp(self): + super().setUp() + + self.image_client.find_image.return_value = self.image + self.image_client.download_image.return_value = self.image + + # Get the command object to test + self.cmd = image.SaveImage(self.app, None) + + def test_save_data(self): + arglist = ['--file', '/path/to/file', self.image.id] + + verifylist = [('file', '/path/to/file'), ('image', self.image.id)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.image_client.download_image.assert_called_once_with( + self.image.id, output='/path/to/file', stream=True, chunk_size=1024 + ) + + def test_save_data_with_chunk_size(self): + arglist = [ + '--file', + '/path/to/file', + '--chunk-size', + '2048', + self.image.id, + ] + + verifylist = [ + ('file', '/path/to/file'), + ('chunk_size', 2048), + ('image', self.image.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.image_client.download_image.assert_called_once_with( + self.image.id, output='/path/to/file', stream=True, chunk_size=2048 + ) diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index 4f5db9f00..2389d685f 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -2228,7 +2228,29 @@ def test_save_data(self): self.cmd.take_action(parsed_args) self.image_client.download_image.assert_called_once_with( - self.image.id, stream=True, output='/path/to/file' + self.image.id, output='/path/to/file', stream=True, chunk_size=1024 + ) + + def test_save_data_with_chunk_size(self): + arglist = [ + '--file', + '/path/to/file', + '--chunk-size', + '2048', + self.image.id, + ] + + verifylist = [ + ('filename', '/path/to/file'), + ('chunk_size', 2048), + ('image', self.image.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.image_client.download_image.assert_called_once_with( + self.image.id, output='/path/to/file', stream=True, chunk_size=2048 ) diff --git a/releasenotes/notes/add-chunk-size-to-image-save-37871f9e62693264.yaml b/releasenotes/notes/add-chunk-size-to-image-save-37871f9e62693264.yaml new file mode 100644 index 000000000..433dc8773 --- /dev/null +++ b/releasenotes/notes/add-chunk-size-to-image-save-37871f9e62693264.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``--chunk-size`` option to ``image save`` command to control the size + of bytes to read at one time. From a73698490ac0eb8e286fd055dfe3a4f89d04772a Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Tue, 26 Feb 2019 13:18:27 +0100 Subject: [PATCH 189/245] image: Add hashing-related fields Add support for the 'os_hash_algo' and 'os_hash_value' image attributes added with Image API 2.7. Change-Id: Id8fe6f3fecf77f537587e9088b207ef2077a9def Signed-off-by: Artem Goncharov --- openstackclient/image/v2/image.py | 7 +++ .../tests/functional/image/v2/test_image.py | 50 +++++++++++++------ .../tests/unit/image/v2/test_image.py | 24 ++++++--- .../add-image-options-dcbc4ead7822c495.yaml | 4 ++ 4 files changed, 64 insertions(+), 21 deletions(-) create mode 100644 releasenotes/notes/add-image-options-dcbc4ead7822c495.yaml diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index d2a1a74ea..0e484afdc 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -98,6 +98,9 @@ def _format_image(image, human_readable=False): 'virtual_size', 'min_ram', 'schema', + 'is_hidden', + 'hash_algo', + 'hash_value', ] # TODO(gtema/anybody): actually it should be possible to drop this method, @@ -903,6 +906,8 @@ def take_action(self, parsed_args): 'visibility', 'is_protected', 'owner_id', + 'hash_algo', + 'hash_value', 'tags', ) column_headers: tuple[str, ...] = ( @@ -916,6 +921,8 @@ def take_action(self, parsed_args): 'Visibility', 'Protected', 'Project', + 'Hash Algorithm', + 'Hash Value', 'Tags', ) else: diff --git a/openstackclient/tests/functional/image/v2/test_image.py b/openstackclient/tests/functional/image/v2/test_image.py index 4d51737c3..828488c42 100644 --- a/openstackclient/tests/functional/image/v2/test_image.py +++ b/openstackclient/tests/functional/image/v2/test_image.py @@ -218,17 +218,39 @@ def test_image_members(self): 'image remove project ' + self.name + ' ' + my_project_id ) - # else: - # # Test not shared - # self.assertRaises( - # image_exceptions.HTTPForbidden, - # self.openstack, - # 'image add project ' + - # self.name + ' ' + - # my_project_id - # ) - # self.openstack( - # 'image set ' + - # '--share ' + - # self.name - # ) + def test_image_hidden(self): + # Test image is shown in list + output = self.openstack( + 'image list', + parse_output=True, + ) + self.assertIn( + self.name, + [img['Name'] for img in output], + ) + + # Hide the image and test image not show in the list + self.openstack('image set ' + '--hidden ' + self.name) + output = self.openstack( + 'image list', + parse_output=True, + ) + self.assertNotIn(self.name, [img['Name'] for img in output]) + + # Test image show in the list with flag + output = self.openstack( + 'image list', + parse_output=True, + ) + self.assertNotIn(self.name, [img['Name'] for img in output]) + + # Unhide the image and test image is again visible in regular list + self.openstack('image set ' + '--unhidden ' + self.name) + output = self.openstack( + 'image list', + parse_output=True, + ) + self.assertIn( + self.name, + [img['Name'] for img in output], + ) diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index 4f5db9f00..cc2b0ed83 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -822,6 +822,8 @@ def test_image_list_long_option(self): 'Visibility', 'Protected', 'Project', + 'Hash Algorithm', + 'Hash Value', 'Tags', ) @@ -830,14 +832,16 @@ def test_image_list_long_option(self): ( self._image.id, self._image.name, - None, - None, - None, - None, - None, + self._image.disk_format, + self._image.container_format, + self._image.size, + self._image.checksum, + self._image.status, self._image.visibility, self._image.is_protected, self._image.owner_id, + self._image.hash_algo, + self._image.hash_value, format_columns.ListColumn(self._image.tags), ), ) @@ -1356,15 +1360,17 @@ def test_image_set_with_unexist_project(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) - def test_image_set_bools1(self): + def test_image_set_bools_true(self): arglist = [ '--protected', '--private', + '--hidden', 'graven', ] verifylist = [ ('is_protected', True), ('visibility', 'private'), + ('is_hidden', True), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1374,6 +1380,7 @@ def test_image_set_bools1(self): kwargs = { 'is_protected': True, 'visibility': 'private', + 'is_hidden': True, } # ImageManager.update(image, **kwargs) self.image_client.update_image.assert_called_with( @@ -1381,15 +1388,17 @@ def test_image_set_bools1(self): ) self.assertIsNone(result) - def test_image_set_bools2(self): + def test_image_set_bools_false(self): arglist = [ '--unprotected', '--public', + '--unhidden', 'graven', ] verifylist = [ ('is_protected', False), ('visibility', 'public'), + ('is_hidden', False), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1399,6 +1408,7 @@ def test_image_set_bools2(self): kwargs = { 'is_protected': False, 'visibility': 'public', + 'is_hidden': False, } # ImageManager.update(image, **kwargs) self.image_client.update_image.assert_called_with( diff --git a/releasenotes/notes/add-image-options-dcbc4ead7822c495.yaml b/releasenotes/notes/add-image-options-dcbc4ead7822c495.yaml new file mode 100644 index 000000000..7a528d7c9 --- /dev/null +++ b/releasenotes/notes/add-image-options-dcbc4ead7822c495.yaml @@ -0,0 +1,4 @@ +--- +features: + - The ``os_hash_algo`` and ``os_hash_value image`` attributes are now shown + in the ``image list --long`` output. From 2177f07dfb658fb72b572e24f1797ef01598df4d Mon Sep 17 00:00:00 2001 From: Mridula Joshi Date: Tue, 16 Jan 2024 17:37:11 +0000 Subject: [PATCH 190/245] Adds CLI support for ``glance md-namespace-properties-delete`` This patch modifies the command to delete all metadef properties inside a namespace. This operation can be called by `image metadef property delete` Change-Id: Iff9bda0dddfa157be0438a66d1d05da7b0b437c3 Signed-off-by: Mridula Joshi --- doc/source/cli/data/glance.csv | 2 +- .../image/v2/metadef_properties.py | 11 ++++-- .../unit/image/v2/test_metadef_properties.py | 34 +++++++++++++------ ...adef-property-delete-1e1bb8410130d901.yaml | 5 +++ 4 files changed, 39 insertions(+), 13 deletions(-) create mode 100644 releasenotes/notes/add-image-metadef-property-delete-1e1bb8410130d901.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index 61cd6c1e5..277a14a13 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -27,7 +27,7 @@ md-namespace-delete,image metadef namespace delete,Delete specified metadata def md-namespace-import,,Import a metadata definitions namespace from file or standard input. md-namespace-list,image metadef namespace list,List metadata definitions namespaces. md-namespace-objects-delete,image metadef object delete,Delete all metadata definitions objects inside a specific namespace. -md-namespace-properties-delete,,Delete all metadata definitions property inside a specific namespace. +md-namespace-properties-delete,image metadef property delete,Delete all metadata definitions property inside a specific namespace. md-namespace-resource-type-list,image metadef resource type association list,List resource types associated to specific namespace. md-namespace-show,image metadef namespace show,Describe a specific metadata definitions namespace. md-namespace-tags-delete,,Delete all metadata definitions tags inside a specific namespace. diff --git a/openstackclient/image/v2/metadef_properties.py b/openstackclient/image/v2/metadef_properties.py index 40440c029..602777331 100644 --- a/openstackclient/image/v2/metadef_properties.py +++ b/openstackclient/image/v2/metadef_properties.py @@ -124,14 +124,21 @@ def get_parser(self, prog_name): parser.add_argument( "properties", metavar="", - nargs="+", - help=_("Metadef propert(ies) to delete (name)"), + nargs="*", + help=_( + "Metadef properties to delete (name) " + "(omit this argument to delete all properties in the namespace)" + ), ) return parser def take_action(self, parsed_args): image_client = self.app.client_manager.image + if not parsed_args.properties: + image_client.delete_all_metadef_properties(parsed_args.namespace) + return + result = 0 for prop in parsed_args.properties: try: diff --git a/openstackclient/tests/unit/image/v2/test_metadef_properties.py b/openstackclient/tests/unit/image/v2/test_metadef_properties.py index 274ed6635..e2f83d629 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_properties.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_properties.py @@ -91,6 +91,7 @@ def setUp(self): super().setUp() self.cmd = metadef_properties.DeleteMetadefProperty(self.app, None) + self.image_client.delete_all_metadef_properties.return_value = None def test_metadef_property_delete(self): arglist = ['namespace', 'property'] @@ -100,6 +101,10 @@ def test_metadef_property_delete(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) + self.image_client.delete_metadef_property.assert_called_with( + 'property', 'namespace', ignore_missing=False + ) + self.image_client.delete_all_metadef_properties.assert_not_called() def test_metadef_property_delete_missing_arguments(self): arglist = [] @@ -110,21 +115,13 @@ def test_metadef_property_delete_missing_arguments(self): arglist, [], ) - - arglist = ['namespace'] - self.assertRaises( - tests_utils.ParserException, - self.check_parser, - self.cmd, - arglist, - [], - ) + self.image_client.delete_all_metadef_properties.assert_not_called() + self.image_client.delete_metadef_property.assert_not_called() def test_metadef_property_delete_exception(self): arglist = ['namespace', 'property'] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.image_client.delete_metadef_property.side_effect = ( sdk_exceptions.ResourceNotFound ) @@ -132,6 +129,23 @@ def test_metadef_property_delete_exception(self): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.image_client.delete_metadef_property.assert_called_with( + 'property', 'namespace', ignore_missing=False + ) + self.image_client.delete_all_metadef_properties.assert_not_called() + + def test_metadef_property_delete_all(self): + arglist = ['namespace'] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.image_client.delete_all_metadef_properties.assert_called_with( + 'namespace' + ) + self.image_client.delete_metadef_property.assert_not_called() class TestMetadefPropertyList(image_fakes.TestImagev2): diff --git a/releasenotes/notes/add-image-metadef-property-delete-1e1bb8410130d901.yaml b/releasenotes/notes/add-image-metadef-property-delete-1e1bb8410130d901.yaml new file mode 100644 index 000000000..09aff5905 --- /dev/null +++ b/releasenotes/notes/add-image-metadef-property-delete-1e1bb8410130d901.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + The ``image property delete`` command will now delete all properties in + the provided namespace if no property is provided. From 5feaa952ad8db78f0b1651dbfc3575d53d50adff Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 18 Aug 2025 11:56:18 +0100 Subject: [PATCH 191/245] compute: Fix flavor create --id auto This was inadvertently broken during the switch from novaclient to SDK. Fix it for now but also deprecate it since it is an unnecessary alias. Change-Id: Iaf136d82e00defc86e57ae4ea7e848246f2ade2c Signed-off-by: Stephen Finucane Closes-bug: #2120833 --- openstackclient/compute/v2/flavor.py | 15 ++++++++++++++- .../tests/unit/compute/v2/test_flavor.py | 2 +- .../notes/flavor-id-auto-e21157f97dc1d7f2.yaml | 6 ++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/flavor-id-auto-e21157f97dc1d7f2.yaml diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index 76635e12b..c0e043e8f 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -156,12 +156,25 @@ def take_action(self, parsed_args): msg = _("--project is only allowed with --private") raise exceptions.CommandError(msg) + flavor_id = parsed_args.id + if parsed_args.id == 'auto': + # novaclient aliased 'auto' to mean "generate a UUID for me": we + # do the same to avoid breaking existing users + flavor_id = None + + msg = _( + "Passing '--id auto' is deprecated. Nova will automatically " + "assign a UUID-like ID if no ID is provided. Omit the '--id' " + "parameter instead." + ) + self.log.warning(msg) + args = { 'name': parsed_args.name, 'ram': parsed_args.ram, 'vcpus': parsed_args.vcpus, 'disk': parsed_args.disk, - 'id': parsed_args.id, + 'id': flavor_id, 'ephemeral': parsed_args.ephemeral, 'swap': parsed_args.swap, 'rxtx_factor': parsed_args.rxtx_factor, diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py index fa8d5ad56..f7599e0a4 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -245,7 +245,7 @@ def test_flavor_create_other_options(self): 'ram': self.flavor.ram, 'vcpus': self.flavor.vcpus, 'disk': self.flavor.disk, - 'id': 'auto', + 'id': None, 'ephemeral': self.flavor.ephemeral, 'swap': self.flavor.swap, 'rxtx_factor': self.flavor.rxtx_factor, diff --git a/releasenotes/notes/flavor-id-auto-e21157f97dc1d7f2.yaml b/releasenotes/notes/flavor-id-auto-e21157f97dc1d7f2.yaml new file mode 100644 index 000000000..96b56b8df --- /dev/null +++ b/releasenotes/notes/flavor-id-auto-e21157f97dc1d7f2.yaml @@ -0,0 +1,6 @@ +--- +deprecations: + - | + The ``--id auto`` alias for the ``flavor create`` command is deprecated + for removal. Omit the option entirely to ensure the server creates the + ID for you. From a312e9cdad626add8ea40e92844f867a9e586751 Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Wed, 7 Jun 2023 15:43:16 +0200 Subject: [PATCH 192/245] Adopt sdk_fakes for compute.test_flavor Use sdk_fakes inside test_flavor. The only left fake is for flavor_access, for which there is no resource in SDK. Change-Id: I8fcfb734eb45308b80aa1478c2935c9881fee928 Signed-off-by: Artem Goncharov --- .../tests/unit/compute/v2/test_flavor.py | 331 +++++++++--------- 1 file changed, 158 insertions(+), 173 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py index fa8d5ad56..e7e6bab70 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -16,12 +16,13 @@ from openstack.compute.v2 import flavor as _flavor from openstack import exceptions as sdk_exceptions +from openstack.identity.v3 import project as _project +from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions from openstackclient.compute.v2 import flavor from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit import utils as tests_utils @@ -34,59 +35,60 @@ def setUp(self): class TestFlavorCreate(TestFlavor): - flavor = compute_fakes.create_one_flavor(attrs={'links': 'flavor-links'}) - project = identity_fakes.FakeProject.create_one_project() - - columns = ( - 'OS-FLV-DISABLED:disabled', - 'OS-FLV-EXT-DATA:ephemeral', - 'description', - 'disk', - 'id', - 'name', - 'os-flavor-access:is_public', - 'properties', - 'ram', - 'rxtx_factor', - 'swap', - 'vcpus', - ) - - data = ( - flavor.is_disabled, - flavor.ephemeral, - flavor.description, - flavor.disk, - flavor.id, - flavor.name, - flavor.is_public, - format_columns.DictColumn(flavor.extra_specs), - flavor.ram, - flavor.rxtx_factor, - flavor.swap, - flavor.vcpus, - ) - data_private = ( - flavor.is_disabled, - flavor.ephemeral, - flavor.description, - flavor.disk, - flavor.id, - flavor.name, - False, - format_columns.DictColumn(flavor.extra_specs), - flavor.ram, - flavor.rxtx_factor, - flavor.swap, - flavor.vcpus, - ) - def setUp(self): super().setUp() - # Return a project + self.flavor = sdk_fakes.generate_fake_resource( + _flavor.Flavor, links='flavor-links' + ) + self.project = sdk_fakes.generate_fake_resource(_project.Project) + + self.columns = ( + 'OS-FLV-DISABLED:disabled', + 'OS-FLV-EXT-DATA:ephemeral', + 'description', + 'disk', + 'id', + 'name', + 'os-flavor-access:is_public', + 'properties', + 'ram', + 'rxtx_factor', + 'swap', + 'vcpus', + ) + self.data = ( + self.flavor.is_disabled, + self.flavor.ephemeral, + self.flavor.description, + self.flavor.disk, + self.flavor.id, + self.flavor.name, + self.flavor.is_public, + format_columns.DictColumn(self.flavor.extra_specs), + self.flavor.ram, + self.flavor.rxtx_factor, + self.flavor.swap, + self.flavor.vcpus, + ) + self.data_private = ( + self.flavor.is_disabled, + self.flavor.ephemeral, + self.flavor.description, + self.flavor.disk, + self.flavor.id, + self.flavor.name, + False, + format_columns.DictColumn(self.flavor.extra_specs), + self.flavor.ram, + self.flavor.rxtx_factor, + self.flavor.swap, + self.flavor.vcpus, + ) + self.projects_mock.get.return_value = self.project self.compute_client.create_flavor.return_value = self.flavor + self.cmd = flavor.CreateFlavor(self.app, None) def test_flavor_create_default_options(self): @@ -233,7 +235,7 @@ def test_flavor_create_other_options(self): ('vcpus', self.flavor.vcpus), ('rxtx_factor', self.flavor.rxtx_factor), ('public', False), - ('description', 'description'), + ('description', self.flavor.description), ('project', self.project.id), ('properties', {'key1': 'value1', 'key2': 'value2'}), ('name', self.flavor.name), @@ -328,7 +330,7 @@ def test_flavor_create_with_description(self): str(self.flavor.vcpus), '--rxtx-factor', str(self.flavor.rxtx_factor), - '--private', + '--public', '--description', 'fake description', self.flavor.name, @@ -341,7 +343,7 @@ def test_flavor_create_with_description(self): ('swap', self.flavor.swap), ('vcpus', self.flavor.vcpus), ('rxtx_factor', self.flavor.rxtx_factor), - ('public', False), + ('public', True), ('description', 'fake description'), ('name', self.flavor.name), ] @@ -358,14 +360,14 @@ def test_flavor_create_with_description(self): 'ephemeral': self.flavor.ephemeral, 'swap': self.flavor.swap, 'rxtx_factor': self.flavor.rxtx_factor, - 'is_public': self.flavor.is_public, + 'is_public': True, 'description': 'fake description', } self.compute_client.create_flavor.assert_called_once_with(**args) self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data_private, data) + self.assertCountEqual(self.data, data) def test_flavor_create_with_description_pre_v255(self): self.set_compute_api_version('2.54') @@ -395,11 +397,13 @@ def test_flavor_create_with_description_pre_v255(self): class TestFlavorDelete(TestFlavor): - flavors = compute_fakes.create_flavors(count=2) - def setUp(self): super().setUp() + self.flavors = list( + sdk_fakes.generate_fake_resources(_flavor.Flavor, 2) + ) + self.compute_client.delete_flavor.return_value = None self.cmd = flavor.DeleteFlavor(self.app, None) @@ -474,51 +478,50 @@ def test_multi_flavors_delete_with_exception(self): class TestFlavorList(TestFlavor): - _flavor = compute_fakes.create_one_flavor() - - columns = ( - 'ID', - 'Name', - 'RAM', - 'Disk', - 'Ephemeral', - 'VCPUs', - 'Is Public', - ) - columns_long = columns + ('Swap', 'RXTX Factor', 'Properties') - - data = ( - ( - _flavor.id, - _flavor.name, - _flavor.ram, - _flavor.disk, - _flavor.ephemeral, - _flavor.vcpus, - _flavor.is_public, - ), - ) - data_long = ( - data[0] - + ( - _flavor.swap, - _flavor.rxtx_factor, - format_columns.DictColumn(_flavor.extra_specs), - ), - ) - def setUp(self): super().setUp() - self.api_mock = mock.Mock() - self.api_mock.side_effect = [ - [self._flavor], - [], - ] + self._flavor = sdk_fakes.generate_fake_resource( + _flavor.Flavor, extra_specs={'property': 'value'} + ) - self.compute_client.flavors = self.api_mock + self.columns = ( + 'ID', + 'Name', + 'RAM', + 'Disk', + 'Ephemeral', + 'VCPUs', + 'Is Public', + ) + self.columns_long = self.columns + ( + 'Swap', + 'RXTX Factor', + 'Properties', + ) + + self.data = ( + ( + self._flavor.id, + self._flavor.name, + self._flavor.ram, + self._flavor.disk, + self._flavor.ephemeral, + self._flavor.vcpus, + self._flavor.is_public, + ), + ) + self.data_long = ( + self.data[0] + + ( + self._flavor.swap, + self._flavor.rxtx_factor, + format_columns.DictColumn(self._flavor.extra_specs), + ), + ) + + self.compute_client.flavors.side_effect = [[self._flavor], []] - # Get the command object to test self.cmd = flavor.ListFlavor(self.app, None) def test_flavor_list_no_options(self): @@ -653,7 +656,9 @@ def test_flavor_list_long(self): def test_flavor_list_long_no_extra_specs(self): # use flavor with no extra specs for this test - flavor = compute_fakes.create_one_flavor(attrs={"extra_specs": {}}) + flavor = sdk_fakes.generate_fake_resource( + _flavor.Flavor, extra_specs={} + ) self.data = ( ( flavor.id, @@ -673,12 +678,8 @@ def test_flavor_list_long_no_extra_specs(self): format_columns.DictColumn(flavor.extra_specs), ), ) - self.api_mock.side_effect = [ - [flavor], - [], - ] - self.compute_client.flavors = self.api_mock + self.compute_client.flavors.side_effect = [[flavor], []] self.compute_client.fetch_flavor_extra_specs = mock.Mock( return_value=None ) @@ -744,17 +745,15 @@ def test_flavor_list_min_disk_min_ram(self): class TestFlavorSet(TestFlavor): - # Return value of self.compute_client.find_flavor(). - flavor = compute_fakes.create_one_flavor( - attrs={'os-flavor-access:is_public': False} - ) - project = identity_fakes.FakeProject.create_one_project() - def setUp(self): super().setUp() + self.flavor = sdk_fakes.generate_fake_resource( + _flavor.Flavor, is_public=False, extra_specs={'property': 'value'} + ) + self.project = sdk_fakes.generate_fake_resource(_project.Project) + self.compute_client.find_flavor.return_value = self.flavor - # Return a project self.projects_mock.get.return_value = self.project self.cmd = flavor.SetFlavor(self.app, None) @@ -960,46 +959,42 @@ def test_flavor_set_description_using_name_pre_v255(self): class TestFlavorShow(TestFlavor): - # Return value of self.compute_client.find_flavor(). - flavor_access = compute_fakes.create_one_flavor_access() - flavor = compute_fakes.create_one_flavor() - - columns = ( - 'OS-FLV-DISABLED:disabled', - 'OS-FLV-EXT-DATA:ephemeral', - 'access_project_ids', - 'description', - 'disk', - 'id', - 'name', - 'os-flavor-access:is_public', - 'properties', - 'ram', - 'rxtx_factor', - 'swap', - 'vcpus', - ) - - data = ( - flavor.is_disabled, - flavor.ephemeral, - None, - flavor.description, - flavor.disk, - flavor.id, - flavor.name, - flavor.is_public, - format_columns.DictColumn(flavor.extra_specs), - flavor.ram, - flavor.rxtx_factor, - flavor.swap, - flavor.vcpus, - ) - def setUp(self): super().setUp() - # Return value of _find_resource() + self.flavor_access = compute_fakes.create_one_flavor_access() + self.flavor = sdk_fakes.generate_fake_resource(_flavor.Flavor) + + self.columns = ( + 'OS-FLV-DISABLED:disabled', + 'OS-FLV-EXT-DATA:ephemeral', + 'access_project_ids', + 'description', + 'disk', + 'id', + 'name', + 'os-flavor-access:is_public', + 'properties', + 'ram', + 'rxtx_factor', + 'swap', + 'vcpus', + ) + self.data = ( + self.flavor.is_disabled, + self.flavor.ephemeral, + None, + self.flavor.description, + self.flavor.disk, + self.flavor.id, + self.flavor.name, + self.flavor.is_public, + format_columns.DictColumn(self.flavor.extra_specs), + self.flavor.ram, + self.flavor.rxtx_factor, + self.flavor.swap, + self.flavor.vcpus, + ) self.compute_client.find_flavor.return_value = self.flavor self.compute_client.get_flavor_access.return_value = [ self.flavor_access @@ -1035,10 +1030,8 @@ def test_public_flavor_show(self): self.assertCountEqual(self.data, data) def test_private_flavor_show(self): - private_flavor = compute_fakes.create_one_flavor( - attrs={ - 'os-flavor-access:is_public': False, - } + private_flavor = sdk_fakes.generate_fake_resource( + _flavor.Flavor, is_public=False ) self.compute_client.find_flavor.return_value = private_flavor @@ -1077,23 +1070,18 @@ def test_private_flavor_show(self): class TestFlavorUnset(TestFlavor): - # Return value of self.compute_client.find_flavor(). - flavor = compute_fakes.create_one_flavor( - attrs={'os-flavor-access:is_public': False} - ) - project = identity_fakes.FakeProject.create_one_project() - def setUp(self): super().setUp() + self.flavor = sdk_fakes.generate_fake_resource( + _flavor.Flavor, is_public=False + ) + self.project = sdk_fakes.generate_fake_resource(_project.Project) + self.compute_client.find_flavor.return_value = self.flavor - # Return a project self.projects_mock.get.return_value = self.project - self.cmd = flavor.UnsetFlavor(self.app, None) - self.mock_shortcut = ( - self.compute_client.delete_flavor_extra_specs_property - ) + self.cmd = flavor.UnsetFlavor(self.app, None) def test_flavor_unset_property(self): arglist = ['--property', 'property', 'baremetal'] @@ -1107,7 +1095,9 @@ def test_flavor_unset_property(self): self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.mock_shortcut.assert_called_with(self.flavor.id, 'property') + self.compute_client.delete_flavor_extra_specs_property.assert_called_with( + self.flavor.id, 'property' + ) self.compute_client.flavor_remove_tenant_access.assert_not_called() self.assertIsNone(result) @@ -1126,21 +1116,16 @@ def test_flavor_unset_properties(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) + self.compute_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - calls = [ - mock.call(self.flavor.id, 'property1'), - mock.call(self.flavor.id, 'property2'), - ] - self.mock_shortcut.assert_has_calls(calls) - - # A bit tricky way to ensure we do not unset other properties - calls.append(mock.call(self.flavor.id, 'property')) - self.assertRaises( - AssertionError, self.mock_shortcut.assert_has_calls, calls + self.compute_client.delete_flavor_extra_specs_property.assert_has_calls( + [ + mock.call(self.flavor.id, 'property1'), + mock.call(self.flavor.id, 'property2'), + ] ) - self.compute_client.flavor_remove_tenant_access.assert_not_called() def test_flavor_unset_project(self): From dbddbf976008cd8aba31a787cc1f043952e2ef94 Mon Sep 17 00:00:00 2001 From: Rajesh Tailor Date: Mon, 23 Jun 2025 13:17:30 +0530 Subject: [PATCH 193/245] Fix microversion 2.96 This change fixes missing conditional logic for microversion 2.96 which adds `pinned_availability_zone` field to `openstack server list` output. Change-Id: I1e398bb3379fa6443b0a44db76baaf6241a945e7 Signed-off-by: Rajesh Tailor --- openstackclient/compute/v2/server.py | 23 ++- .../tests/unit/compute/v2/test_server.py | 165 ++++++++++++++++-- 2 files changed, 168 insertions(+), 20 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index e14392a53..28c28b194 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -183,9 +183,13 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): 'updated_at': 'updated', 'user_data': 'OS-EXT-SRV-ATTR:user_data', 'vm_state': 'OS-EXT-STS:vm_state', - 'pinned_availability_zone': 'pinned_availability_zone', 'scheduler_hints': 'scheduler_hints', } + # NOTE(ratailor): microversion 2.96 introduces + # pinned_availability_zone support + if sdk_utils.supports_microversion(compute_client, '2.96'): + column_map['pinned_availability_zone'] = 'pinned_availability_zone' + # Some columns returned by openstacksdk should not be shown because they're # either irrelevant or duplicates ignored_columns = { @@ -240,6 +244,11 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): if not sdk_utils.supports_microversion(compute_client, '2.100'): info.pop('scheduler_hints', None) + # NOTE(ratailor): microversion 2.96 introduces + # pinned_availability_zone support + if not sdk_utils.supports_microversion(compute_client, '2.96'): + info.pop('pinned_availability_zone', None) + # Convert the image blob to a name image_info = info.get('image', {}) if image_info and any(image_info.values()): @@ -2838,18 +2847,19 @@ def take_action(self, parsed_args): if parsed_args.long: columns += ( 'availability_zone', - 'pinned_availability_zone', 'hypervisor_hostname', 'metadata', 'scheduler_hints', ) column_headers += ( 'Availability Zone', - 'Pinned Availability Zone', 'Host', 'Properties', 'Scheduler Hints', ) + if sdk_utils.supports_microversion(compute_client, '2.96'): + columns += ('pinned_availability_zone',) + column_headers += ('Pinned Availability Zone',) if parsed_args.all_projects: columns += ('project_id',) @@ -2887,10 +2897,11 @@ def take_action(self, parsed_args): column_headers += ('Availability Zone',) if c in ( 'pinned_availability_zone', - "Pinned Availability Zone", + 'Pinned Availability Zone', ): - columns += ('Pinned Availability Zone',) - column_headers += ('Pinned Availability Zone',) + if sdk_utils.supports_microversion(compute_client, '2.96'): + columns += ('pinned_availability_zone',) + column_headers += ('Pinned Availability Zone',) if c in ('Host', "host"): columns += ('hypervisor_hostname',) column_headers += ('Host',) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 86951360b..82205bc18 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -1212,7 +1212,6 @@ class TestServerCreate(TestServer): 'locked', 'locked_reason', 'name', - 'pinned_availability_zone', 'progress', 'project_id', 'properties', @@ -1261,7 +1260,6 @@ def datalist(self): None, # locked None, # locked_reason self.server.name, - None, # pinned_availability_zone None, # progress None, # project_id format_columns.DictColumn({}), # properties @@ -4583,7 +4581,6 @@ class _TestServerList(TestServer): 'Flavor Name', 'Flavor ID', 'Availability Zone', - 'Pinned Availability Zone', 'Host', 'Properties', 'Scheduler Hints', @@ -4732,7 +4729,6 @@ def test_server_list_long_option(self): self.flavor.name, s.flavor['id'], getattr(s, 'availability_zone'), - getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), format_columns.DictListColumn(None), @@ -4809,8 +4805,6 @@ def test_server_list_column_option(self): '-c', 'Availability Zone', '-c', - 'Pinned Availability Zone', - '-c', 'Host', '-c', 'Properties', @@ -4835,7 +4829,6 @@ def test_server_list_column_option(self): self.assertIn('Image ID', columns) self.assertIn('Flavor ID', columns) self.assertIn('Availability Zone', columns) - self.assertIn('Pinned Availability Zone', columns) self.assertIn('Host', columns) self.assertIn('Properties', columns) self.assertIn('Scheduler Hints', columns) @@ -5249,7 +5242,6 @@ def test_server_list_long_with_host_status_v216(self): self.flavor.name, s.flavor['id'], getattr(s, 'availability_zone'), - getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), format_columns.DictListColumn(s.scheduler_hints), @@ -5305,7 +5297,6 @@ def test_server_list_long_with_host_status_v216(self): self.flavor.name, s.flavor['id'], getattr(s, 'availability_zone'), - getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), format_columns.DictListColumn(s.scheduler_hints), @@ -5343,7 +5334,6 @@ class TestServerListV273(_TestServerList): 'Image ID', 'Flavor', 'Availability Zone', - 'Pinned Availability Zone', 'Host', 'Properties', 'Scheduler Hints', @@ -5541,6 +5531,157 @@ def test_server_list_v269_with_partial_constructs(self): self.assertEqual(expected_row, partial_server) +class TestServerListV296(_TestServerList): + columns = ( + 'ID', + 'Name', + 'Status', + 'Networks', + 'Image', + 'Flavor', + ) + columns_long = ( + 'ID', + 'Name', + 'Status', + 'Task State', + 'Power State', + 'Networks', + 'Image Name', + 'Image ID', + 'Flavor', + 'Availability Zone', + 'Host', + 'Properties', + 'Scheduler Hints', + 'Pinned Availability Zone', + ) + + def setUp(self): + super().setUp() + self.set_compute_api_version('2.96') + + Image = collections.namedtuple('Image', 'id name') + self.image_client.images.return_value = [ + Image(id=s.image['id'], name=self.image.name) + # Image will be an empty string if boot-from-volume + for s in self.servers + if s.image + ] + + Flavor = collections.namedtuple('Flavor', 'id name') + self.compute_client.flavors.return_value = [ + Flavor(id=s.flavor['id'], name=self.flavor.name) + for s in self.servers + ] + + self.data = tuple( + ( + s.id, + s.name, + s.status, + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + ) + for s in self.servers + ) + + def test_server_list_long_option(self): + self.data = tuple( + ( + s.id, + s.name, + s.status, + getattr(s, 'task_state'), + server.PowerStateColumn(getattr(s, 'power_state')), + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + getattr(s, 'availability_zone'), + server.HostColumn(getattr(s, 'hypervisor_hostname')), + format_columns.DictColumn(s.metadata), + format_columns.DictListColumn(None), + getattr(s, 'pinned_availability_zone', ''), + ) + for s in self.servers + ) + arglist = [ + '--long', + ] + verifylist = [ + ('all_projects', False), + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.compute_client.servers.assert_called_with(**self.kwargs) + image_ids = {s.image['id'] for s in self.servers if s.image} + self.image_client.images.assert_called_once_with( + id=f'in:{",".join(image_ids)}', + ) + self.compute_client.flavors.assert_called_once_with(is_public=None) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data, tuple(data)) + + def test_server_list_column_option(self): + arglist = [ + '-c', + 'Project ID', + '-c', + 'User ID', + '-c', + 'Created At', + '-c', + 'Security Groups', + '-c', + 'Task State', + '-c', + 'Power State', + '-c', + 'Image ID', + '-c', + 'Flavor ID', + '-c', + 'Availability Zone', + '-c', + 'Host', + '-c', + 'Properties', + '-c', + 'Scheduler Hints', + '-c', + 'Pinned Availability Zone', + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute_client.servers.assert_called_with(**self.kwargs) + self.assertIn('Project ID', columns) + self.assertIn('User ID', columns) + self.assertIn('Created At', columns) + self.assertIn('Security Groups', columns) + self.assertIn('Task State', columns) + self.assertIn('Power State', columns) + self.assertIn('Image ID', columns) + self.assertIn('Flavor ID', columns) + self.assertIn('Availability Zone', columns) + self.assertIn('Pinned Availability Zone', columns) + self.assertIn('Host', columns) + self.assertIn('Properties', columns) + self.assertIn('Scheduler Hints', columns) + self.assertCountEqual(columns, set(columns)) + + class TestServerAction(compute_fakes.TestComputev2): def run_method_with_sdk_servers(self, method_name, server_count): servers = compute_fakes.create_servers(count=server_count) @@ -8533,7 +8674,6 @@ def setUp(self): 'locked', 'locked_reason', 'name', - 'pinned_availability_zone', 'progress', 'project_id', 'properties', @@ -8583,7 +8723,6 @@ def setUp(self): None, # locked None, # locked_reason self.server.name, - None, # pinned_availability_zone None, # progress 'tenant-id-xxx', # project_id format_columns.DictColumn({}), # properties @@ -9522,7 +9661,6 @@ def test_prep_server_detail(self): 'locked': None, 'locked_reason': None, 'name': _server.name, - 'pinned_availability_zone': None, 'progress': None, 'project_id': 'tenant-id-xxx', 'properties': format_columns.DictColumn({}), @@ -9609,7 +9747,6 @@ def test_prep_server_detail_v247(self): 'locked': None, 'locked_reason': None, 'name': _server.name, - 'pinned_availability_zone': None, 'progress': None, 'project_id': 'tenant-id-xxx', 'properties': format_columns.DictColumn({}), From 94e447af80ee3e5197e588946e13b2574c49e752 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 20 Aug 2025 17:41:21 +0100 Subject: [PATCH 194/245] tests: Remove use of namedtuple Change-Id: I19a272ffd260bab263dd63cb920802b792e192eb Signed-off-by: Stephen Finucane --- .../tests/unit/compute/v2/test_server.py | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 82205bc18..1d55d1efe 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -13,7 +13,6 @@ # under the License. import base64 -import collections import getpass import json import tempfile @@ -21,9 +20,11 @@ import uuid import iso8601 +from openstack.compute.v2 import flavor as _flavor from openstack.compute.v2 import server as _server from openstack.compute.v2 import server_group as _server_group from openstack import exceptions as sdk_exceptions +from openstack.image.v2 import image as _image from openstack.test import fakes as sdk_fakes from osc_lib.cli import format_columns from osc_lib import exceptions @@ -4650,17 +4651,19 @@ class TestServerList(_TestServerList): def setUp(self): super().setUp() - Image = collections.namedtuple('Image', 'id name') self.image_client.images.return_value = [ - Image(id=s.image['id'], name=self.image.name) + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) # Image will be an empty string if boot-from-volume for s in self.servers if s.image ] - Flavor = collections.namedtuple('Flavor', 'id name') self.compute_client.flavors.return_value = [ - Flavor(id=s.flavor['id'], name=self.flavor.name) + sdk_fakes.generate_fake_resource( + _flavor.Flavor, id=s.flavor['id'], name=self.flavor.name + ) for s in self.servers ] @@ -5273,9 +5276,10 @@ def test_server_list_long_with_host_status_v216(self): self.compute_client.servers.return_value = servers # Make sure the returned image and flavor IDs match the servers. - Image = collections.namedtuple('Image', 'id name') self.image_client.images.return_value = [ - Image(id=s.image['id'], name=self.image.name) + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) # Image will be an empty string if boot-from-volume for s in servers if s.image @@ -5358,9 +5362,10 @@ def setUp(self): self.servers = self.setup_sdk_servers_mock(3) self.compute_client.servers.return_value = self.servers - Image = collections.namedtuple('Image', 'id name') self.image_client.images.return_value = [ - Image(id=s.image['id'], name=self.image.name) + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) # Image will be an empty string if boot-from-volume for s in self.servers if s.image @@ -5561,17 +5566,19 @@ def setUp(self): super().setUp() self.set_compute_api_version('2.96') - Image = collections.namedtuple('Image', 'id name') self.image_client.images.return_value = [ - Image(id=s.image['id'], name=self.image.name) + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) # Image will be an empty string if boot-from-volume for s in self.servers if s.image ] - Flavor = collections.namedtuple('Flavor', 'id name') self.compute_client.flavors.return_value = [ - Flavor(id=s.flavor['id'], name=self.flavor.name) + sdk_fakes.generate_fake_resource( + _flavor.Flavor, id=s.flavor['id'], name=self.flavor.name + ) for s in self.servers ] From 68d1d01b2ae5b5726a947e03dbc0c1b019a8898b Mon Sep 17 00:00:00 2001 From: minkyukim Date: Sun, 17 Aug 2025 22:09:46 +0900 Subject: [PATCH 195/245] tests: Simplify catalog functional tests Combine multiple test cases into a single test case, in order to speed up execution. Change-Id: Idcfd0c8c5b7418046601d222248c0cd16886e079 Signed-off-by: minkyukim --- .../functional/identity/v3/test_catalog.py | 65 ++++++++++++------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/openstackclient/tests/functional/identity/v3/test_catalog.py b/openstackclient/tests/functional/identity/v3/test_catalog.py index f4089b7eb..429f94ac3 100644 --- a/openstackclient/tests/functional/identity/v3/test_catalog.py +++ b/openstackclient/tests/functional/identity/v3/test_catalog.py @@ -10,35 +10,54 @@ # License for the specific language governing permissions and limitations # under the License. + from openstackclient.tests.functional.identity.v3 import common class CatalogTests(common.IdentityTests): - def test_catalog_list(self): + """Functional tests for catalog commands""" + + def test_catalog(self): + """Test catalog list and show functionality""" + # Create a test service for isolated testing + _dummy_service_name = self._create_dummy_service(add_clean_up=True) + + # list catalogs raw_output = self.openstack('catalog list') items = self.parse_listing(raw_output) self.assert_table_structure(items, ['Name', 'Type', 'Endpoints']) - def test_catalog_show(self): - """test catalog show command - - The output example: - +-----------+----------------------------------------+ - | Field | Value | - +-----------+----------------------------------------+ - | endpoints | test1 | - | | public: http://localhost:5000/v2.0 | - | | test1 | - | | internal: http://localhost:5000/v2.0 | - | | test1 | - | | admin: http://localhost:35357/v2.0 | - | | | - | id | e1e68b5ba21a43a39ff1cf58e736c3aa | - | name | keystone | - | type | identity | - +-----------+----------------------------------------+ - """ - raw_output = self.openstack('catalog show {}'.format('identity')) + # Verify created service appears in catalog + service_names = [ + item.get('Name') for item in items if item.get('Name') + ] + self.assertIn( + _dummy_service_name, + service_names, + "Created dummy service should be present in catalog", + ) + + # show service (by name) + raw_output = self.openstack(f'catalog show {_dummy_service_name}') items = self.parse_show(raw_output) - # items may have multiple endpoint urls with empty key - self.assert_show_fields(items, ['endpoints', 'name', 'type', '', 'id']) + self.assert_show_fields(items, ['endpoints', 'name', 'type', 'id']) + + # Extract the type from the dummy service + _dummy_service_type = next( + (item['type'] for item in items if 'type' in item), None + ) + + # show service (by type) + raw_output = self.openstack(f'catalog show {_dummy_service_type}') + items = self.parse_show(raw_output) + self.assert_show_fields(items, ['endpoints', 'name', 'type', 'id']) + + # show service (non-existent) + result = self.openstack( + 'catalog show nonexistent-service-xyz', fail_ok=True + ) + self.assertEqual( + '', + result.strip(), + "Non-existent service should return empty result", + ) From 5f1ffe742cd8afb2c93927ff606d88a90fd6073e Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 29 Aug 2025 13:31:55 +0100 Subject: [PATCH 196/245] volume: Temporarily ignore Volume.backup_id column We really need a better way to do this. Change-Id: I631748e2dfe3c136156d7987eab952370a88d35b Signed-off-by: Stephen Finucane Related: https://review.opendev.org/c/openstack/openstacksdk/+/958801 --- openstackclient/volume/v3/volume.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 54dd5c754..d43a77d7b 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -107,6 +107,8 @@ def _format_volume(volume: _volume.Volume) -> dict[str, ty.Any]: 'os-volume-replication:extended_status', # unnecessary columns 'links', + # temporarily ignored columns + 'backup_id', } optional_columns = { # only present if part of a consistency group From e7554603acfb74a71386aa34e3c9bea3840a0861 Mon Sep 17 00:00:00 2001 From: Rajesh Tailor Date: Fri, 29 Aug 2025 12:15:39 +0530 Subject: [PATCH 197/245] Fix microversion 2.100 This change fixes missing conditional logic for microversion 2.100 which adds support for showing `scheduler_hints` field to `openstack server list --long` output. Change-Id: I2820e02a91deb73850f37dc737dbec79dea99e8d Signed-off-by: Rajesh Tailor --- openstackclient/compute/v2/server.py | 28 ++- .../tests/unit/compute/v2/test_server.py | 161 ++++++++++++++++-- 2 files changed, 170 insertions(+), 19 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 28c28b194..5ed122e9b 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -183,13 +183,17 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): 'updated_at': 'updated', 'user_data': 'OS-EXT-SRV-ATTR:user_data', 'vm_state': 'OS-EXT-STS:vm_state', - 'scheduler_hints': 'scheduler_hints', } # NOTE(ratailor): microversion 2.96 introduces # pinned_availability_zone support if sdk_utils.supports_microversion(compute_client, '2.96'): column_map['pinned_availability_zone'] = 'pinned_availability_zone' + # NOTE(ratailor): microversion 2.100 introduces + # scheduler_hints support + if sdk_utils.supports_microversion(compute_client, '2.100'): + column_map['scheduler_hints'] = 'scheduler_hints' + # Some columns returned by openstacksdk should not be shown because they're # either irrelevant or duplicates ignored_columns = { @@ -335,10 +339,11 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): info['OS-EXT-STS:power_state'] ) - if 'scheduler_hints' in info: - info['scheduler_hints'] = format_columns.DictListColumn( - info.pop('scheduler_hints', {}), - ) + if sdk_utils.supports_microversion(compute_client, '2.100'): + if 'scheduler_hints' in info: + info['scheduler_hints'] = format_columns.DictListColumn( + info.pop('scheduler_hints', {}), + ) return info @@ -2849,18 +2854,20 @@ def take_action(self, parsed_args): 'availability_zone', 'hypervisor_hostname', 'metadata', - 'scheduler_hints', ) column_headers += ( 'Availability Zone', 'Host', 'Properties', - 'Scheduler Hints', ) if sdk_utils.supports_microversion(compute_client, '2.96'): columns += ('pinned_availability_zone',) column_headers += ('Pinned Availability Zone',) + if sdk_utils.supports_microversion(compute_client, '2.100'): + columns += ('scheduler_hints',) + column_headers += ('Scheduler Hints',) + if parsed_args.all_projects: columns += ('project_id',) column_headers += ('Project ID',) @@ -2912,8 +2919,11 @@ def take_action(self, parsed_args): 'scheduler_hints', "Scheduler Hints", ): - columns += ('scheduler_hints',) - column_headers += ('Scheduler Hints',) + if sdk_utils.supports_microversion( + compute_client, '2.100' + ): + columns += ('scheduler_hints',) + column_headers += ('Scheduler Hints',) # remove duplicates column_headers = tuple(dict.fromkeys(column_headers)) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 1d55d1efe..1048394d3 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4584,7 +4584,6 @@ class _TestServerList(TestServer): 'Availability Zone', 'Host', 'Properties', - 'Scheduler Hints', ) columns_all_projects = ( 'ID', @@ -4734,7 +4733,6 @@ def test_server_list_long_option(self): getattr(s, 'availability_zone'), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), - format_columns.DictListColumn(None), ) for s in self.servers ) @@ -4811,8 +4809,6 @@ def test_server_list_column_option(self): 'Host', '-c', 'Properties', - '-c', - 'Scheduler Hints', '--long', ] verifylist = [ @@ -4834,7 +4830,6 @@ def test_server_list_column_option(self): self.assertIn('Availability Zone', columns) self.assertIn('Host', columns) self.assertIn('Properties', columns) - self.assertIn('Scheduler Hints', columns) self.assertCountEqual(columns, set(columns)) def test_server_list_no_name_lookup_option(self): @@ -5247,7 +5242,6 @@ def test_server_list_long_with_host_status_v216(self): getattr(s, 'availability_zone'), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), - format_columns.DictListColumn(s.scheduler_hints), ) for s in self.servers ) @@ -5303,7 +5297,6 @@ def test_server_list_long_with_host_status_v216(self): getattr(s, 'availability_zone'), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), - format_columns.DictListColumn(s.scheduler_hints), s.host_status, ) for s in servers @@ -5558,7 +5551,6 @@ class TestServerListV296(_TestServerList): 'Availability Zone', 'Host', 'Properties', - 'Scheduler Hints', 'Pinned Availability Zone', ) @@ -5611,7 +5603,6 @@ def test_server_list_long_option(self): getattr(s, 'availability_zone'), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), - format_columns.DictListColumn(None), getattr(s, 'pinned_availability_zone', ''), ) for s in self.servers @@ -5660,9 +5651,159 @@ def test_server_list_column_option(self): '-c', 'Properties', '-c', - 'Scheduler Hints', + 'Pinned Availability Zone', + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute_client.servers.assert_called_with(**self.kwargs) + self.assertIn('Project ID', columns) + self.assertIn('User ID', columns) + self.assertIn('Created At', columns) + self.assertIn('Security Groups', columns) + self.assertIn('Task State', columns) + self.assertIn('Power State', columns) + self.assertIn('Image ID', columns) + self.assertIn('Flavor ID', columns) + self.assertIn('Availability Zone', columns) + self.assertIn('Pinned Availability Zone', columns) + self.assertIn('Host', columns) + self.assertIn('Properties', columns) + self.assertCountEqual(columns, set(columns)) + + +class TestServerListV2100(_TestServerList): + columns = ( + 'ID', + 'Name', + 'Status', + 'Networks', + 'Image', + 'Flavor', + ) + columns_long = ( + 'ID', + 'Name', + 'Status', + 'Task State', + 'Power State', + 'Networks', + 'Image Name', + 'Image ID', + 'Flavor', + 'Availability Zone', + 'Host', + 'Properties', + 'Pinned Availability Zone', + 'Scheduler Hints', + ) + + def setUp(self): + super().setUp() + self.set_compute_api_version('2.100') + + self.image_client.images.return_value = [ + sdk_fakes.generate_fake_resource( + _image.Image, id=s.image['id'], name=self.image.name + ) + # Image will be an empty string if boot-from-volume + for s in self.servers + if s.image + ] + + self.compute_client.flavors.return_value = [ + sdk_fakes.generate_fake_resource( + _flavor.Flavor, id=s.flavor['id'], name=self.flavor.name + ) + for s in self.servers + ] + + self.data = tuple( + ( + s.id, + s.name, + s.status, + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + ) + for s in self.servers + ) + + def test_server_list_long_option(self): + self.data = tuple( + ( + s.id, + s.name, + s.status, + getattr(s, 'task_state'), + server.PowerStateColumn(getattr(s, 'power_state')), + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + getattr(s, 'availability_zone'), + server.HostColumn(getattr(s, 'hypervisor_hostname')), + format_columns.DictColumn(s.metadata), + getattr(s, 'pinned_availability_zone', ''), + format_columns.DictListColumn(None), + ) + for s in self.servers + ) + arglist = [ + '--long', + ] + verifylist = [ + ('all_projects', False), + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.compute_client.servers.assert_called_with(**self.kwargs) + image_ids = {s.image['id'] for s in self.servers if s.image} + self.image_client.images.assert_called_once_with( + id=f'in:{",".join(image_ids)}', + ) + self.compute_client.flavors.assert_called_once_with(is_public=None) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data, tuple(data)) + + def test_server_list_column_option(self): + arglist = [ + '-c', + 'Project ID', + '-c', + 'User ID', + '-c', + 'Created At', + '-c', + 'Security Groups', + '-c', + 'Task State', + '-c', + 'Power State', + '-c', + 'Image ID', + '-c', + 'Flavor ID', + '-c', + 'Availability Zone', + '-c', + 'Host', + '-c', + 'Properties', '-c', 'Pinned Availability Zone', + '-c', + 'Scheduler Hints', '--long', ] verifylist = [ From 3dfeb5ed08579a7a617f9c696ea3dea012f4a023 Mon Sep 17 00:00:00 2001 From: dlawton Date: Wed, 3 Sep 2025 16:43:04 +0100 Subject: [PATCH 198/245] Bug Fix: Skip invalid server ID during multi-server delete Change-Id: I8e5339f07b43dd0a9422eaf33346bbfdf2c9b328 Signed-off-by: Dan Lawton Closes-Bug: #2122056 --- openstackclient/compute/v2/server.py | 39 ++++++++++++--- .../tests/unit/compute/v2/test_server.py | 49 +++++++++++++++++++ 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 28c28b194..21a21974a 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2218,24 +2218,49 @@ def _show_progress(progress): self.app.stdout.flush() compute_client = self.app.client_manager.compute + + deleted_servers = [] for server in parsed_args.server: - server_obj = compute_client.find_server( - server, - ignore_missing=False, - all_projects=parsed_args.all_projects, - ) + try: + server_obj = compute_client.find_server( + server, + ignore_missing=False, + all_projects=parsed_args.all_projects, + ) - compute_client.delete_server(server_obj, force=parsed_args.force) + compute_client.delete_server( + server_obj, force=parsed_args.force + ) + deleted_servers.append(server_obj) + except Exception as e: + LOG.error( + _( + "Failed to delete server with " + "name or ID '%(server)s': %(e)s" + ), + {'server': server, 'e': e}, + ) - if parsed_args.wait: + if parsed_args.wait: + for server_obj in deleted_servers: try: compute_client.wait_for_delete( server_obj, callback=_show_progress ) except sdk_exceptions.ResourceTimeout: msg = _('Error deleting server: %s') % server_obj.id + deleted_servers.remove(server_obj) raise exceptions.CommandError(msg) + fails = len(parsed_args.server) - len(deleted_servers) + if fails > 0: + total = len(parsed_args.server) + msg = _("%(fails)s of %(total)s servers failed to delete.") % { + 'fails': fails, + 'total': total, + } + raise exceptions.CommandError(msg) + class PercentAction(argparse.Action): def __init__( diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 1d55d1efe..68e1d9ac2 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4449,6 +4449,55 @@ def test_server_delete_multi_servers(self): ) self.assertIsNone(result) + def test_server_delete_multi_servers_with_exceptions(self): + servers = compute_fakes.create_servers(count=2) + self.compute_client.find_server.side_effect = [ + servers[0], + sdk_exceptions.ResourceNotFound(), + servers[1], + ] + + arglist = [servers[0].id, 'unexist_server', servers[1].id] + + verifylist = [ + ('force', False), + ('all_projects', False), + ('wait', False), + ( + 'server', + [servers[0].id, 'unexist_server', servers[1].id], + ), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual('1 of 3 servers failed to delete.', str(exc)) + + self.compute_client.find_server.assert_has_calls( + [ + mock.call( + servers[0].id, ignore_missing=False, all_projects=False + ), + mock.call( + 'unexist_server', ignore_missing=False, all_projects=False + ), + mock.call( + servers[1].id, ignore_missing=False, all_projects=False + ), + ] + ) + + self.compute_client.delete_server.assert_has_calls( + [ + mock.call(servers[0], force=False), + mock.call(servers[1], force=False), + ] + ) + def test_server_delete_with_all_projects(self): arglist = [ self.server.id, From a5a6ec27e5c9e15c84109baa92cfedcdcb1f9503 Mon Sep 17 00:00:00 2001 From: doburn Date: Sat, 23 Aug 2025 21:25:55 +0900 Subject: [PATCH 199/245] Add functional tests for `role assignment list` Implements tests for `role assignment list` domain options. The options covered are: - `--user-domain` - `--group-domain` - `--project-domain` - `--role-domain` Change-Id: Ia42dcc337df0de7d5a93250696b807038a2d9d0e Signed-off-by: doburn --- .../identity/v3/test_role_assignment.py | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/openstackclient/tests/functional/identity/v3/test_role_assignment.py b/openstackclient/tests/functional/identity/v3/test_role_assignment.py index 76e33c286..1255841af 100644 --- a/openstackclient/tests/functional/identity/v3/test_role_assignment.py +++ b/openstackclient/tests/functional/identity/v3/test_role_assignment.py @@ -62,6 +62,47 @@ def test_role_assignment_list_group(self): items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + def test_role_assignment_list_group_domain(self): + domain_name_A = self._create_dummy_domain() + domain_name_B = self._create_dummy_domain() + role_name = self._create_dummy_role() + group_name = 'group_name' + self.openstack(f'group create --domain {domain_name_A} {group_name}') + self.addCleanup( + self.openstack, + f'group delete --domain {domain_name_A} {group_name}', + ) + self.openstack(f'group create --domain {domain_name_B} {group_name}') + self.addCleanup( + self.openstack, + f'group delete --domain {domain_name_B} {group_name}', + ) + raw_output = self.openstack( + 'role add ' + f'--project {self.project_name} ' + f'--group {group_name} --group-domain {domain_name_A} ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--project {self.project_name} ' + f'--group {group_name} --group-domain {domain_name_A} ' + f'{role_name}', + ) + self.assertEqual('', raw_output.strip()) + raw_output = self.openstack( + f'role assignment list ' + f'--group {group_name} --group-domain {domain_name_A} ' + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + raw_output = self.openstack( + f'role assignment list ' + f'--group {group_name} --group-domain {domain_name_B} ' + ) + self.assertEqual('', raw_output.strip()) + def test_role_assignment_list_domain(self): role_name = self._create_dummy_role() username = self._create_dummy_user() @@ -85,6 +126,89 @@ def test_role_assignment_list_domain(self): items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + def test_role_assignment_list_user_domain(self): + domain_name_A = self._create_dummy_domain() + domain_name_B = self._create_dummy_domain() + role_name = self._create_dummy_role() + username = 'username' + self.openstack(f'user create --domain {domain_name_A} {username}') + self.addCleanup( + self.openstack, f'user delete --domain {domain_name_A} {username}' + ) + self.openstack(f'user create --domain {domain_name_B} {username}') + self.addCleanup( + self.openstack, f'user delete --domain {domain_name_B} {username}' + ) + raw_output = self.openstack( + 'role add ' + f'--project {self.project_name} ' + f'--user {username} --user-domain {domain_name_A} ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--project {self.project_name} ' + f'--user {username} --user-domain {domain_name_A} ' + f'{role_name}', + ) + self.assertEqual('', raw_output.strip()) + raw_output = self.openstack( + f'role assignment list ' + f'--user {username} --user-domain {domain_name_A} ' + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + raw_output = self.openstack( + f'role assignment list ' + f'--user {username} --user-domain {domain_name_B} ' + ) + self.assertEqual('', raw_output.strip()) + + def test_role_assignment_list_role_domain(self): + domain_name_A = self._create_dummy_domain() + domain_name_B = self._create_dummy_domain() + role_name = 'role_name' + username = 'username' + self.openstack(f'role create --domain {domain_name_A} {role_name}') + self.addCleanup( + self.openstack, f'role delete --domain {domain_name_A} {role_name}' + ) + self.openstack(f'role create --domain {domain_name_B} {role_name}') + self.addCleanup( + self.openstack, f'role delete --domain {domain_name_B} {role_name}' + ) + self.openstack(f'user create --domain {domain_name_A} {username}') + self.addCleanup( + self.openstack, f'user delete --domain {domain_name_A} {username}' + ) + raw_output = self.openstack( + 'role add ' + f'--user {username} --domain {domain_name_A} ' + f'--role-domain {domain_name_A} ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--user {username} --domain {domain_name_A} ' + f'--role-domain {domain_name_A} ' + f'{role_name}', + ) + self.assertEqual('', raw_output.strip()) + raw_output = self.openstack( + f'role assignment list ' + f'--role {role_name} --role-domain {domain_name_A}' + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + raw_output = self.openstack( + f'role assignment list ' + f'--role {role_name} --role-domain {domain_name_B}' + ) + items = self.parse_listing(raw_output) + self.assertEqual('', raw_output.strip()) + def test_role_assignment_list_project(self): role_name = self._create_dummy_role() username = self._create_dummy_user() @@ -108,6 +232,56 @@ def test_role_assignment_list_project(self): items = self.parse_listing(raw_output) self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + def test_role_assignment_list_project_domain(self): + domain_name_A = self._create_dummy_domain() + domain_name_B = self._create_dummy_domain() + role_name = self._create_dummy_role() + project_name = 'project_name' + username = 'username' + self.openstack( + f'project create --domain {domain_name_A} {project_name}' + ) + self.addCleanup( + self.openstack, + f'project delete --domain {domain_name_A} {project_name}', + ) + self.openstack( + f'project create --domain {domain_name_B} {project_name}' + ) + self.addCleanup( + self.openstack, + f'project delete --domain {domain_name_B} {project_name}', + ) + self.openstack(f'user create --domain {domain_name_A} {username}') + self.addCleanup( + self.openstack, f'user delete --domain {domain_name_A} {username}' + ) + raw_output = self.openstack( + 'role add ' + f'--project {project_name} --project-domain {domain_name_A} ' + f'--user {username} --user-domain {domain_name_A} ' + f'{role_name}' + ) + self.addCleanup( + self.openstack, + 'role remove ' + f'--project {project_name} --project-domain {domain_name_A} ' + f'--user {username} --user-domain {domain_name_A} ' + f'{role_name}', + ) + self.assertEqual('', raw_output.strip()) + raw_output = self.openstack( + f'role assignment list ' + f'--project {project_name} --project-domain {domain_name_A} ' + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + raw_output = self.openstack( + f'role assignment list ' + f'--project {project_name} --project-domain {domain_name_B} ' + ) + self.assertEqual('', raw_output.strip()) + def test_role_assignment_list_effective(self): raw_output = self.openstack('role assignment list --effective') items = self.parse_listing(raw_output) From b808b82dfb7daf1343471b154f13f33ed89b9dfd Mon Sep 17 00:00:00 2001 From: dlawton Date: Thu, 11 Sep 2025 10:05:41 +0100 Subject: [PATCH 200/245] Validation: Cannot create network with segmentation id alone Change-Id: I7d98921fe6f2819a6427bc826d640a6685a00da7 Signed-off-by: Dan lawton Closes-bug: #1693106 --- openstackclient/network/v2/network.py | 10 ++++++++++ .../tests/unit/network/v2/test_network.py | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index e04efed61..33f7e04e4 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -401,6 +401,16 @@ def take_action_network(self, client, parsed_args): ) raise exceptions.CommandError(msg) + if ( + parsed_args.segmentation_id + and not parsed_args.provider_network_type + ): + msg = _( + "--provider-segment requires --provider-network-type " + "to be specified." + ) + raise exceptions.CommandError(msg) + attrs.update( self._parse_extra_properties(parsed_args.extra_properties) ) diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index f809cad0a..b2e65f213 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -330,6 +330,23 @@ def test_create_with_vlan_qinq_and_transparency_enabled(self): exceptions.CommandError, self.cmd.take_action, parsed_args ) + def test_create_with_provider_segment_without_provider_type(self): + arglist = [ + "--provider-segment", + "123", + self._network.name, + ] + verifylist = [ + ('provider_network_type', None), + ('segmentation_id', "123"), + ('name', self._network.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + class TestCreateNetworkIdentityV2( identity_fakes_v2.FakeClientMixin, From 3c3ea30bd3be7f310035ee94b96535273c2045b1 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Tue, 16 Sep 2025 08:11:27 +0000 Subject: [PATCH 201/245] Update master for stable/2025.2 Add file to the reno documentation build to show release notes for stable/2025.2. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/2025.2. Sem-Ver: feature Change-Id: I6aec2d1f91ed7fc2dba466574b4efb92b4bd7c88 Signed-off-by: OpenStack Release Bot Generated-By: openstack/project-config:roles/copy-release-tools-scripts/files/release-tools/add_release_note_page.sh --- releasenotes/source/2025.2.rst | 6 ++++++ releasenotes/source/index.rst | 1 + 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/2025.2.rst diff --git a/releasenotes/source/2025.2.rst b/releasenotes/source/2025.2.rst new file mode 100644 index 000000000..4dae18d86 --- /dev/null +++ b/releasenotes/source/2025.2.rst @@ -0,0 +1,6 @@ +=========================== +2025.2 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2025.2 diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index a9c057978..2b28cfb92 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ OpenStackClient Release Notes :maxdepth: 1 unreleased + 2025.2 2025.1 2024.2 2024.1 From c0ada2d6ab3cf9caa1cc879f05ac89d4948e741d Mon Sep 17 00:00:00 2001 From: Alexey Stupnikov Date: Fri, 19 Sep 2025 19:37:28 +0200 Subject: [PATCH 202/245] Extend project delete command description "openstack project delete" command doesn't try to figure out if other services are using specified project somehow before trying to delete it. This patch extends command description to ensure that this is clearly communicated to users. Related-bug: #2118900 Change-Id: I3ae0b2a8f04d4f791cab46ccd89f400549d24ecd Signed-off-by: Alexey Stupnikov --- openstackclient/identity/v2_0/project.py | 9 ++++++++- openstackclient/identity/v3/project.py | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py index a0c3c1e4d..8939aa12f 100644 --- a/openstackclient/identity/v2_0/project.py +++ b/openstackclient/identity/v2_0/project.py @@ -105,7 +105,14 @@ def take_action(self, parsed_args): class DeleteProject(command.Command): - _description = _("Delete project(s)") + _description = _( + "Delete project(s). This command will remove specified " + "existing project(s) if an active user is authorized to do " + "this. If there are resources managed by other services " + "(for example, Nova, Neutron, Cinder) associated with " + "specified project(s), delete operation will proceed " + "regardless." + ) def get_parser(self, prog_name): parser = super().get_parser(prog_name) diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 383215461..dd6eb75af 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -146,7 +146,14 @@ def take_action(self, parsed_args): class DeleteProject(command.Command): - _description = _("Delete project(s)") + _description = _( + "Delete project(s). This command will remove specified " + "existing project(s) if an active user is authorized to do " + "this. If there are resources managed by other services " + "(for example, Nova, Neutron, Cinder) associated with " + "specified project(s), delete operation will proceed " + "regardless." + ) def get_parser(self, prog_name): parser = super().get_parser(prog_name) From 6b6a9bafd80a90ee9d42d756489350ea7a77ff69 Mon Sep 17 00:00:00 2001 From: jiwonjang Date: Tue, 26 Aug 2025 00:35:08 +0900 Subject: [PATCH 203/245] Add functional tests for image metadef resource type list Implements functional tests for 'image metadef resource type list' command. Change-Id: If645a04d4b8800da44041769f08b1e81332af33c Signed-off-by: jiwonjang --- .../image/v2/test_metadef_resource_type.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 openstackclient/tests/functional/image/v2/test_metadef_resource_type.py diff --git a/openstackclient/tests/functional/image/v2/test_metadef_resource_type.py b/openstackclient/tests/functional/image/v2/test_metadef_resource_type.py new file mode 100644 index 000000000..ab8dc13ef --- /dev/null +++ b/openstackclient/tests/functional/image/v2/test_metadef_resource_type.py @@ -0,0 +1,55 @@ +# 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.image import base + + +class ImageMetadefResourceTypeTests(base.BaseImageTests): + """Functional tests for image metadef resource type commands.""" + + def setUp(self): + super().setUp() + + # Create unique namespace name using UUID + self.namespace_name = 'test-mdef-ns-' + uuid.uuid4().hex + self.resource_type_name = 'test-mdef-rt-' + uuid.uuid4().hex + + # Create namespace + self.openstack('image metadef namespace create ' + self.namespace_name) + self.addCleanup( + self.openstack, + 'image metadef namespace delete ' + self.namespace_name, + ) + + def test_metadef_resource_type(self): + """Test image metadef resource type commands""" + + self.openstack( + 'image metadef resource type association create ' + f'{self.namespace_name} {self.resource_type_name}', + ) + self.addCleanup( + self.openstack, + 'image metadef resource type association delete ' + f'{self.namespace_name} {self.resource_type_name}', + ) + + output = self.openstack( + 'image metadef resource type list', + parse_output=True, + ) + + self.assertIn( + self.resource_type_name, [item['Name'] for item in output] + ) From 4cf70113d2398e432bae4ba897de15d8f93615b2 Mon Sep 17 00:00:00 2001 From: wonjun0120 Date: Sat, 16 Aug 2025 22:40:01 +0900 Subject: [PATCH 204/245] Add functional test for cached image command Implements tests for cache clear operations including queue, cache, and combined clearing functionality. Change-Id: I71056bb5db6c3de4f9294ac1b661ab927f59c867 Signed-off-by: wonjun0120 --- .../tests/functional/image/v2/test_cache.py | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 openstackclient/tests/functional/image/v2/test_cache.py diff --git a/openstackclient/tests/functional/image/v2/test_cache.py b/openstackclient/tests/functional/image/v2/test_cache.py new file mode 100644 index 000000000..58245e5ca --- /dev/null +++ b/openstackclient/tests/functional/image/v2/test_cache.py @@ -0,0 +1,54 @@ +# 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.image import base + + +class CacheTests(base.BaseImageTests): + """Functional tests for Cache commands""" + + def test_cached_image(self): + """Test cached image operations including queue and clear""" + # Create test image + name = uuid.uuid4().hex + output = self.openstack( + f'image create {name}', + parse_output=True, + ) + image_id = output["id"] + self.assertOutput(name, output['name']) + + # Register cleanup for created image + self.addCleanup( + self.openstack, 'cached image delete ' + image_id, fail_ok=True + ) + self.addCleanup(self.openstack, 'image delete ' + image_id) + + # Queue image for caching + self.openstack('cached image queue ' + image_id) + + # Verify queuing worked + cache_output = self.openstack('cached image list', parse_output=True) + self.assertIsInstance(cache_output, list) + image_ids = [img['ID'] for img in cache_output] + self.assertIn(image_id, image_ids) + + # Clear cached images + self.openstack('cached image clear') + + # Verify clearing worked + output = self.openstack('cached image list', parse_output=True) + if output: + image_ids = [img['ID'] for img in output] + self.assertNotIn(image_id, image_ids) From de88853de29d30ef6d1cc1966c93befd3e100cf3 Mon Sep 17 00:00:00 2001 From: Thomas Goirand Date: Mon, 29 Sep 2025 14:59:03 +0200 Subject: [PATCH 205/245] Fix openstack quota show without cinder Per this Debian bug [1], 'openstack quota show --default' fails when cinder is NOT installed. This is also true of other services. [1] https://bugs.debian.org/1109288 Change-Id: I361da44b9f1d09ba3a454632d41e2110a3815395 Signed-off-by: Svein-Erik Skjelbred Signed-off-by: Thomas Goirand Signed-off-by: Stephen Finucane --- openstackclient/common/quota.py | 27 +++++++++++++++---- .../tests/unit/common/test_quota.py | 20 ++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index f706a5974..41c57e63d 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -746,21 +746,32 @@ def take_action(self, parsed_args): # values if the project or class does not exist. This is expected # behavior. However, we have already checked for the presence of the # project above so it shouldn't be an issue. - if parsed_args.service in {'all', 'compute'}: + if parsed_args.service == 'compute' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_compute_endpoint_enabled() + ): compute_quota_info = get_compute_quotas( self.app, project, detail=parsed_args.usage, default=parsed_args.default, ) - if parsed_args.service in {'all', 'volume'}: + + if parsed_args.service == 'volume' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_volume_endpoint_enabled() + ): volume_quota_info = get_volume_quotas( self.app, project, detail=parsed_args.usage, default=parsed_args.default, ) - if parsed_args.service in {'all', 'network'}: + + if parsed_args.service == 'network' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_network_endpoint_enabled() + ): network_quota_info = get_network_quotas( self.app, project, @@ -906,12 +917,18 @@ def take_action(self, parsed_args): ) # compute quotas - if parsed_args.service in {'all', 'compute'}: + if parsed_args.service == 'compute' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_compute_endpoint_enabled() + ): compute_client = self.app.client_manager.compute compute_client.revert_quota_set(project.id) # volume quotas - if parsed_args.service in {'all', 'volume'}: + if parsed_args.service == 'volume' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_volume_endpoint_enabled() + ): volume_client = self.app.client_manager.sdk_connection.volume volume_client.revert_quota_set(project.id) diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index 53df4da84..a2418d01b 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -1041,6 +1041,26 @@ def test_quota_show(self): ) self.assertNotCalled(self.network_client.get_quota_default) + def test_quota_show__missing_services(self): + self.app.client_manager.compute_endpoint_enabled = False + self.app.client_manager.volume_endpoint_enabled = False + self.app.client_manager.network_endpoint_enabled = False + + arglist = [ + self.projects[0].name, + ] + verifylist = [ + ('service', 'all'), + ('project', self.projects[0].name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.compute_client.get_quota_set.assert_not_called() + self.volume_sdk_client.get_quota_set.assert_not_called() + self.network_client.get_quota.assert_not_called() + def test_quota_show__with_compute(self): arglist = [ '--compute', From fb8cdd44414d0e16001d52d90d49d16dcc7a9509 Mon Sep 17 00:00:00 2001 From: Matt Anson Date: Wed, 1 Oct 2025 12:31:05 +0100 Subject: [PATCH 206/245] Ensure show on absent appcreds raises exception Currently, running ``application credential show`` on a non-existent appcred will exit normally and display a formatted application credential with no data, despite the Keystone API returning a 404. Ensure that querying a non-existent application credential raises an exception message and an exit-code 1 to the user. Closes-Bug: #2126565 Change-Id: I597d2d4064f1020c5ac40862ecc556f3c94b53eb Signed-off-by: Matt Anson --- openstackclient/identity/v3/application_credential.py | 2 +- .../tests/unit/identity/v3/test_application_credential.py | 2 +- releasenotes/notes/bug-2126565-a119ac242d9ac795.yaml | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/bug-2126565-a119ac242d9ac795.yaml diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py index 4c4cd3f5a..93367f8a3 100644 --- a/openstackclient/identity/v3/application_credential.py +++ b/openstackclient/identity/v3/application_credential.py @@ -352,7 +352,7 @@ def take_action(self, parsed_args): user_id = conn.config.get_auth().get_user_id(conn.identity) application_credential = identity_client.find_application_credential( - user_id, parsed_args.application_credential + user_id, parsed_args.application_credential, ignore_missing=False ) return _format_application_credential(application_credential) diff --git a/openstackclient/tests/unit/identity/v3/test_application_credential.py b/openstackclient/tests/unit/identity/v3/test_application_credential.py index a5fda7c4f..3bc2d0c29 100644 --- a/openstackclient/tests/unit/identity/v3/test_application_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py @@ -457,7 +457,7 @@ def test_application_credential_show(self): columns, data = self.cmd.take_action(parsed_args) self.identity_sdk_client.find_application_credential.assert_called_with( - user_id, self.application_credential.id + user_id, self.application_credential.id, ignore_missing=False ) self.assertEqual(self.columns, columns) diff --git a/releasenotes/notes/bug-2126565-a119ac242d9ac795.yaml b/releasenotes/notes/bug-2126565-a119ac242d9ac795.yaml new file mode 100644 index 000000000..87fe689b1 --- /dev/null +++ b/releasenotes/notes/bug-2126565-a119ac242d9ac795.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + Running ``openstack application credential show`` on + a non-existent application credential does not + raise an exception. + + [Bug `2126565 `_] From 0ed122094ae480a4b3a02948e374aefe0eb3390a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 1 Oct 2025 15:26:25 +0100 Subject: [PATCH 207/245] identity: Fix 'user list --project' option The 'role_assignments_filter' identity proxy method requires either a user or group, which defeats the entire purpose of the command when used with this option. Use 'role_assignments' instead. Change-Id: I8fb705c55fb4e81fa82d4a7dbe4c5bf7e1edd98a Signed-off-by: Stephen Finucane Closes-bug: #1616104 --- openstackclient/identity/v3/user.py | 8 +++----- openstackclient/tests/unit/identity/v3/test_user.py | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index fee7dbe33..34d85fba9 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -467,15 +467,13 @@ def take_action(self, parsed_args): ignore_missing=False, ).id - assignments = identity_client.role_assignments_filter( - project=project - ) - # NOTE(stevemar): If a user has more than one role on a project # then they will have two entries in the returned data. Since we # are looking for any role, let's just track unique user IDs. user_ids = set() - for assignment in assignments: + for assignment in identity_client.role_assignments( + scope_project_id=project + ): if assignment.user: user_ids.add(assignment.user['id']) diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index 1236b86e2..f0ed91405 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -891,7 +891,7 @@ def setUp(self): self.identity_sdk_client.find_domain.return_value = self.domain self.identity_sdk_client.find_group.return_value = self.group self.identity_sdk_client.find_project.return_value = self.project - self.identity_sdk_client.role_assignments_filter.return_value = [ + self.identity_sdk_client.role_assignments.return_value = [ self.role_assignment ] @@ -1029,12 +1029,10 @@ def test_user_list_project(self): columns, data = self.cmd.take_action(parsed_args) kwargs = { - 'project': self.project.id, + 'scope_project_id': self.project.id, } - self.identity_sdk_client.role_assignments_filter.assert_called_with( - **kwargs - ) + self.identity_sdk_client.role_assignments.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) From 20ad83bf84edf02496f4c41a732c8913b1525397 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 10 Oct 2025 10:39:41 +0100 Subject: [PATCH 208/245] pre-commit: Bump versions We need to rename two hooks. Change-Id: I15582a23da6ea6babf2b277ff443b7cdb764c9f9 Signed-off-by: Stephen Finucane --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 09a9172bd..47903a90d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,13 @@ --- repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: trailing-whitespace - id: mixed-line-ending args: ['--fix', 'lf'] exclude: '.*\.(svg)$' - - id: check-byte-order-marker + - id: fix-byte-order-marker - id: check-executables-have-shebangs - id: check-merge-conflict - id: debug-statements @@ -15,9 +15,9 @@ repos: files: .*\.(yaml|yml)$ args: ['--unsafe'] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.8 + rev: v0.14.0 hooks: - - id: ruff + - id: ruff-check args: ['--fix', '--unsafe-fixes'] - id: ruff-format - repo: https://opendev.org/openstack/hacking @@ -27,7 +27,7 @@ repos: additional_dependencies: [] exclude: '^(doc|releasenotes)/.*$' - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.15.0 + rev: v1.18.2 hooks: - id: mypy additional_dependencies: From 305e037df22c5ea3166df274cb9513b16680b711 Mon Sep 17 00:00:00 2001 From: Jan Ueberacker Date: Wed, 16 Jul 2025 16:01:09 +0200 Subject: [PATCH 209/245] Add option to filter for projects when listing volume backups Change-Id: Idb07c1be90a98b65b6c1b8f888d0ca5309f8cbc4 Signed-off-by: Jan Ueberacker --- .../tests/unit/volume/v3/test_volume_backup.py | 9 +++++++++ openstackclient/volume/v3/volume_backup.py | 17 ++++++++++++++++- ...-backup-project-filter-6c09b2c8aba83341.yaml | 5 +++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-volume-backup-project-filter-6c09b2c8aba83341.yaml diff --git a/openstackclient/tests/unit/volume/v3/test_volume_backup.py b/openstackclient/tests/unit/volume/v3/test_volume_backup.py index 5be0985a2..86bde785f 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_backup.py @@ -17,6 +17,7 @@ from openstack.block_storage.v3 import snapshot as _snapshot from openstack.block_storage.v3 import volume as _volume from openstack import exceptions as sdk_exceptions +from openstack.identity.v3 import project as _project from openstack.test import fakes as sdk_fakes from osc_lib import exceptions @@ -381,6 +382,7 @@ def test_backup_list_without_options(self): ("marker", None), ("limit", None), ('all_projects', False), + ("project", None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -395,11 +397,14 @@ def test_backup_list_without_options(self): all_tenants=False, marker=None, limit=None, + project_id=None, ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) def test_backup_list_with_options(self): + project = sdk_fakes.generate_fake_resource(_project.Project) + self.identity_sdk_client.find_project.return_value = project arglist = [ "--long", "--name", @@ -413,6 +418,8 @@ def test_backup_list_with_options(self): "--all-projects", "--limit", "3", + "--project", + project.id, ] verifylist = [ ("long", True), @@ -422,6 +429,7 @@ def test_backup_list_with_options(self): ("marker", self.backups[0].id), ('all_projects', True), ("limit", 3), + ("project", project.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -440,6 +448,7 @@ def test_backup_list_with_options(self): all_tenants=True, marker=self.backups[0].id, limit=3, + project_id=project.id, ) self.assertEqual(self.columns_long, columns) self.assertCountEqual(self.data_long, list(data)) diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py index 7df46f8e6..3875f802a 100644 --- a/openstackclient/volume/v3/volume_backup.py +++ b/openstackclient/volume/v3/volume_backup.py @@ -236,6 +236,11 @@ class ListVolumeBackup(command.Lister): def get_parser(self, prog_name): parser = super().get_parser(prog_name) + parser.add_argument( + '--project', + metavar='', + help=_('Filter results by project (name or ID) (admin only)'), + ) parser.add_argument( "--long", action="store_true", @@ -296,6 +301,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume + identity_client = self.app.client_manager.sdk_connection.identity columns: tuple[str, ...] = ( 'id', @@ -332,6 +338,14 @@ def take_action(self, parsed_args): VolumeIdColumn, volume_cache=volume_cache ) + all_tenants = parsed_args.all_projects + project_id = None + if parsed_args.project: + all_tenants = True + project_id = identity_client.find_project( + parsed_args.project, ignore_missing=False + ).id + filter_volume_id = None if parsed_args.volume: try: @@ -360,9 +374,10 @@ def take_action(self, parsed_args): name=parsed_args.name, status=parsed_args.status, volume_id=filter_volume_id, - all_tenants=parsed_args.all_projects, + all_tenants=all_tenants, marker=marker_backup_id, limit=parsed_args.limit, + project_id=project_id, ) return ( diff --git a/releasenotes/notes/add-volume-backup-project-filter-6c09b2c8aba83341.yaml b/releasenotes/notes/add-volume-backup-project-filter-6c09b2c8aba83341.yaml new file mode 100644 index 000000000..06bc7a4f0 --- /dev/null +++ b/releasenotes/notes/add-volume-backup-project-filter-6c09b2c8aba83341.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``--project`` option to ``volume backup list`` command, + to allow filtering for projects when listing volume backups. From 3cc6b24bb5a9334ae5117a508d653dc9a727689d Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Fri, 31 Oct 2025 12:05:25 +0000 Subject: [PATCH 210/245] reno: Update master for unmaintained/2024.1 Update the 2024.1 release notes configuration to build from unmaintained/2024.1. Change-Id: Ia4fff2a8e0f9bb083423c2e5c7339a46aaccd271 Signed-off-by: OpenStack Release Bot Generated-By: openstack/project-config:roles/copy-release-tools-scripts/files/release-tools/change_reno_branch_to_unmaintained.sh --- releasenotes/source/2024.1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/2024.1.rst b/releasenotes/source/2024.1.rst index 4977a4f1a..6896656be 100644 --- a/releasenotes/source/2024.1.rst +++ b/releasenotes/source/2024.1.rst @@ -3,4 +3,4 @@ =========================== .. release-notes:: - :branch: stable/2024.1 + :branch: unmaintained/2024.1 From 44dfa157e4172616fb4ff75e1b3aa843c105efae Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Nov 2025 12:39:35 +0000 Subject: [PATCH 211/245] tests: Remove duplicated fake network client This must have crept in some time after [1] merged. [1] Ic203964c7dede7dd80ae2d93b8fa1b7e6634a758 Change-Id: Ic0603db8b1a59b7704c51b0e0ffceb7db2e781d3 Signed-off-by: Stephen Finucane --- .../network/v2/default_security_group_rule.py | 8 +- .../v2/test_default_security_group_rule.py | 84 ++++++++----------- 2 files changed, 41 insertions(+), 51 deletions(-) diff --git a/openstackclient/network/v2/default_security_group_rule.py b/openstackclient/network/v2/default_security_group_rule.py index fddec6874..0a16a11a7 100644 --- a/openstackclient/network/v2/default_security_group_rule.py +++ b/openstackclient/network/v2/default_security_group_rule.py @@ -150,7 +150,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.sdk_connection.network + client = self.app.client_manager.network # Build the create attributes. attrs = {} attrs['protocol'] = network_utils.get_protocol(parsed_args) @@ -248,7 +248,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): result = 0 - client = self.app.client_manager.sdk_connection.network + client = self.app.client_manager.network for r in parsed_args.rule: try: obj = client.find_default_security_group_rule( @@ -334,7 +334,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.sdk_connection.network + client = self.app.client_manager.network column_headers = ( 'ID', 'IP Protocol', @@ -403,7 +403,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.sdk_connection.network + client = self.app.client_manager.network obj = client.find_default_security_group_rule( parsed_args.rule, ignore_missing=False ) diff --git a/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py b/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py index 9b461269c..7032047e3 100644 --- a/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py +++ b/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py @@ -15,7 +15,6 @@ from unittest.mock import call import uuid -from openstack.network.v2 import _proxy from openstack.network.v2 import ( default_security_group_rule as _default_security_group_rule, ) @@ -28,18 +27,7 @@ from openstackclient.tests.unit import utils as tests_utils -class TestDefaultSecurityGroupRule(network_fakes.TestNetworkV2): - def setUp(self): - super().setUp() - - self.app.client_manager.sdk_connection = mock.Mock() - self.app.client_manager.sdk_connection.network = mock.Mock( - spec=_proxy.Proxy, - ) - self.sdk_client = self.app.client_manager.sdk_connection.network - - -class TestCreateDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): +class TestCreateDefaultSecurityGroupRule(network_fakes.TestNetworkV2): expected_columns = ( 'description', 'direction', @@ -82,7 +70,7 @@ def _setup_default_security_group_rule(self, attrs=None): **default_security_group_rule_attrs, ) - self.sdk_client.create_default_security_group_rule.return_value = ( + self.network_client.create_default_security_group_rule.return_value = ( self._default_sg_rule ) self.expected_data = ( @@ -208,7 +196,7 @@ def test_create_default_rule(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -252,7 +240,7 @@ def _test_create_protocol_any_helper( columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -302,7 +290,7 @@ def test_create_remote_address_group(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -347,7 +335,7 @@ def test_create_remote_group(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -381,7 +369,7 @@ def test_create_source_group(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -415,7 +403,7 @@ def test_create_source_ip(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -449,7 +437,7 @@ def test_create_remote_ip(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -599,7 +587,7 @@ def test_create_icmp_type(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -637,7 +625,7 @@ def test_create_icmp_type_zero(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -675,7 +663,7 @@ def test_create_icmp_type_greater_than_zero(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -713,7 +701,7 @@ def test_create_icmp_type_negative_value(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -754,7 +742,7 @@ def test_create_ipv6_icmp_type_code(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -794,7 +782,7 @@ def test_create_icmpv6_type(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'direction': self._default_sg_rule.direction, 'ethertype': self._default_sg_rule.ether_type, @@ -825,7 +813,7 @@ def test_create_with_description(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_default_security_group_rule.assert_called_once_with( + self.network_client.create_default_security_group_rule.assert_called_once_with( **{ 'description': self._default_sg_rule.description, 'direction': self._default_sg_rule.direction, @@ -840,7 +828,7 @@ def test_create_with_description(self): self.assertEqual(self.expected_data, data) -class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): +class TestDeleteDefaultSecurityGroupRule(network_fakes.TestNetworkV2): # The default security group rules to be deleted. default_security_group_rule_attrs = { 'direction': 'ingress', @@ -866,7 +854,9 @@ class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): def setUp(self): super().setUp() - self.sdk_client.delete_default_security_group_rule.return_value = None + self.network_client.delete_default_security_group_rule.return_value = ( + None + ) # Get the command object to test self.cmd = default_security_group_rule.DeleteDefaultSecurityGroupRule( @@ -880,7 +870,7 @@ def test_default_security_group_rule_delete(self): verifylist = [ ('rule', [self._default_sg_rules[0].id]), ] - self.sdk_client.find_default_security_group_rule.return_value = ( + self.network_client.find_default_security_group_rule.return_value = ( self._default_sg_rules[0] ) @@ -888,7 +878,7 @@ def test_default_security_group_rule_delete(self): result = self.cmd.take_action(parsed_args) - self.sdk_client.delete_default_security_group_rule.assert_called_once_with( + self.network_client.delete_default_security_group_rule.assert_called_once_with( self._default_sg_rules[0] ) self.assertIsNone(result) @@ -902,7 +892,7 @@ def test_multi_default_security_group_rules_delete(self): verifylist = [ ('rule', arglist), ] - self.sdk_client.find_default_security_group_rule.side_effect = ( + self.network_client.find_default_security_group_rule.side_effect = ( self._default_sg_rules ) parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -912,7 +902,7 @@ def test_multi_default_security_group_rules_delete(self): calls = [] for s in self._default_sg_rules: calls.append(call(s)) - self.sdk_client.delete_default_security_group_rule.assert_has_calls( + self.network_client.delete_default_security_group_rule.assert_has_calls( calls ) self.assertIsNone(result) @@ -931,7 +921,7 @@ def test_multi_default_security_group_rules_delete_with_exception(self): self._default_sg_rules[0], exceptions.CommandError, ] - self.sdk_client.find_default_security_group_rule = mock.Mock( + self.network_client.find_default_security_group_rule = mock.Mock( side_effect=find_mock_result ) @@ -941,18 +931,18 @@ def test_multi_default_security_group_rules_delete_with_exception(self): except exceptions.CommandError as e: self.assertEqual('1 of 2 default rules failed to delete.', str(e)) - self.sdk_client.find_default_security_group_rule.assert_any_call( + self.network_client.find_default_security_group_rule.assert_any_call( self._default_sg_rules[0].id, ignore_missing=False ) - self.sdk_client.find_default_security_group_rule.assert_any_call( + self.network_client.find_default_security_group_rule.assert_any_call( 'unexist_rule', ignore_missing=False ) - self.sdk_client.delete_default_security_group_rule.assert_called_once_with( + self.network_client.delete_default_security_group_rule.assert_called_once_with( self._default_sg_rules[0] ) -class TestListDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): +class TestListDefaultSecurityGroupRule(network_fakes.TestNetworkV2): # The security group rule to be listed. _default_sg_rule_tcp = sdk_fakes.generate_fake_resource( _default_security_group_rule.DefaultSecurityGroupRule, @@ -1001,7 +991,7 @@ class TestListDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): def setUp(self): super().setUp() - self.sdk_client.default_security_group_rules.return_value = ( + self.network_client.default_security_group_rules.return_value = ( self._default_sg_rules ) @@ -1016,7 +1006,7 @@ def test_list_default(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.default_security_group_rules.assert_called_once_with( + self.network_client.default_security_group_rules.assert_called_once_with( **{} ) self.assertEqual(self.expected_columns, columns) @@ -1035,7 +1025,7 @@ def test_list_with_protocol(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.default_security_group_rules.assert_called_once_with( + self.network_client.default_security_group_rules.assert_called_once_with( **{ 'protocol': 'tcp', } @@ -1055,7 +1045,7 @@ def test_list_with_ingress(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.default_security_group_rules.assert_called_once_with( + self.network_client.default_security_group_rules.assert_called_once_with( **{ 'direction': 'ingress', } @@ -1075,7 +1065,7 @@ def test_list_with_wrong_egress(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.default_security_group_rules.assert_called_once_with( + self.network_client.default_security_group_rules.assert_called_once_with( **{ 'direction': 'egress', } @@ -1084,7 +1074,7 @@ def test_list_with_wrong_egress(self): self.assertEqual(self.expected_data, list(data)) -class TestShowDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): +class TestShowDefaultSecurityGroupRule(network_fakes.TestNetworkV2): # The default security group rule to be shown. _default_sg_rule = sdk_fakes.generate_fake_resource( _default_security_group_rule.DefaultSecurityGroupRule @@ -1123,7 +1113,7 @@ class TestShowDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): def setUp(self): super().setUp() - self.sdk_client.find_default_security_group_rule.return_value = ( + self.network_client.find_default_security_group_rule.return_value = ( self._default_sg_rule ) @@ -1148,7 +1138,7 @@ def test_show_all_options(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.find_default_security_group_rule.assert_called_once_with( + self.network_client.find_default_security_group_rule.assert_called_once_with( self._default_sg_rule.id, ignore_missing=False ) self.assertEqual(self.columns, columns) From 7116449190d9cbc144ede942163ed7678cde2edd Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Nov 2025 13:40:36 +0000 Subject: [PATCH 212/245] tests: Avoid more unnecessary mocks Change-Id: I04672d46595e93b19f873a54d5be9363d262370b Signed-off-by: Stephen Finucane --- .../tests/unit/common/test_extension.py | 5 +- .../tests/unit/compute/v2/test_aggregate.py | 8 +- .../tests/unit/compute/v2/test_console.py | 5 +- .../tests/unit/compute/v2/test_flavor.py | 4 +- .../unit/compute/v2/test_hypervisor_stats.py | 10 +- .../unit/compute/v2/test_server_backup.py | 4 +- .../tests/unit/compute/v2/test_service.py | 4 +- .../tests/unit/network/test_common.py | 8 +- .../unit/network/v2/test_address_group.py | 41 ++-- .../unit/network/v2/test_address_scope.py | 25 +- .../v2/test_default_security_group_rule.py | 5 +- .../network/v2/test_floating_ip_network.py | 48 ++-- .../v2/test_floating_ip_port_forwarding.py | 39 +-- .../unit/network/v2/test_ip_availability.py | 14 +- .../network/v2/test_l3_conntrack_helper.py | 21 +- .../tests/unit/network/v2/test_local_ip.py | 35 +-- .../network/v2/test_local_ip_association.py | 31 +-- .../tests/unit/network/v2/test_ndp_proxy.py | 32 +-- .../tests/unit/network/v2/test_network.py | 61 ++--- .../unit/network/v2/test_network_agent.py | 33 +-- .../test_network_auto_allocated_topology.py | 13 +- .../unit/network/v2/test_network_flavor.py | 40 ++- .../network/v2/test_network_flavor_profile.py | 31 ++- .../unit/network/v2/test_network_meter.py | 24 +- .../network/v2/test_network_meter_rule.py | 30 +-- .../network/v2/test_network_qos_policy.py | 20 +- .../unit/network/v2/test_network_qos_rule.py | 98 ++++---- .../network/v2/test_network_qos_rule_type.py | 9 +- .../unit/network/v2/test_network_rbac.py | 57 ++--- .../unit/network/v2/test_network_segment.py | 42 +--- .../network/v2/test_network_segment_range.py | 39 +-- .../v2/test_network_service_provider.py | 5 +- .../unit/network/v2/test_network_trunk.py | 97 ++++---- .../tests/unit/network/v2/test_port.py | 161 ++++++------ .../tests/unit/network/v2/test_router.py | 230 ++++++++---------- .../network/v2/test_security_group_network.py | 45 ++-- .../v2/test_security_group_rule_network.py | 35 ++- .../tests/unit/network/v2/test_subnet.py | 81 +++--- .../tests/unit/network/v2/test_subnet_pool.py | 54 ++-- 39 files changed, 651 insertions(+), 893 deletions(-) diff --git a/openstackclient/tests/unit/common/test_extension.py b/openstackclient/tests/unit/common/test_extension.py index 53d9d65d9..dd684312c 100644 --- a/openstackclient/tests/unit/common/test_extension.py +++ b/openstackclient/tests/unit/common/test_extension.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from openstackclient.common import extension from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes @@ -295,8 +294,8 @@ def setUp(self): self.cmd = extension.ShowExtension(self.app, None) - self.app.client_manager.network.find_extension = mock.Mock( - return_value=self.extension_details + self.app.client_manager.network.find_extension.return_value = ( + self.extension_details ) def test_show_no_options(self): diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index 813cbb95f..b68e76edc 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -165,9 +165,11 @@ def setUp(self): sdk_fakes.generate_fake_resources(_aggregate.Aggregate, 2) ) - self.compute_client.find_aggregate = mock.Mock( - side_effect=[self.fake_ags[0], self.fake_ags[1]] - ) + self.compute_client.find_aggregate.side_effect = [ + self.fake_ags[0], + self.fake_ags[1], + ] + self.cmd = aggregate.DeleteAggregate(self.app, None) def test_aggregate_delete(self): diff --git a/openstackclient/tests/unit/compute/v2/test_console.py b/openstackclient/tests/unit/compute/v2/test_console.py index 423290c11..8d9d36ccd 100644 --- a/openstackclient/tests/unit/compute/v2/test_console.py +++ b/openstackclient/tests/unit/compute/v2/test_console.py @@ -13,7 +13,6 @@ # under the License. # -from unittest import mock from openstack.compute.v2 import server as _server from openstack.test import fakes as sdk_fakes @@ -90,9 +89,7 @@ def setUp(self): 'protocol': 'fake_protocol', 'type': 'fake_type', } - self.compute_client.create_console = mock.Mock( - return_value=fake_console_data - ) + self.compute_client.create_console.return_value = fake_console_data self.columns = ( 'protocol', diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py index 0b3a4ae8d..25bc8eaa7 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -680,9 +680,7 @@ def test_flavor_list_long_no_extra_specs(self): ) self.compute_client.flavors.side_effect = [[flavor], []] - self.compute_client.fetch_flavor_extra_specs = mock.Mock( - return_value=None - ) + self.compute_client.fetch_flavor_extra_specs.return_value = None arglist = [ '--long', diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py index 045efda2d..89d4d459f 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py @@ -12,20 +12,12 @@ # License for the specific language governing permissions and limitations # under the License. # -from unittest import mock from openstackclient.compute.v2 import hypervisor_stats from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes from openstackclient.tests.unit import fakes -class TestHypervisorStats(compute_fakes.TestComputev2): - def setUp(self): - super().setUp() - - self.compute_client.get = mock.Mock() - - # Not in fakes.py because hypervisor stats has been deprecated @@ -61,7 +53,7 @@ def create_one_hypervisor_stats(attrs=None): return stats_info -class TestHypervisorStatsShow(TestHypervisorStats): +class TestHypervisorStatsShow(compute_fakes.TestComputev2): _stats = create_one_hypervisor_stats() def setUp(self): diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py index 0d039d210..cc8acfb34 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_backup.py +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -161,9 +161,7 @@ def test_server_backup_wait_fail(self, mock_wait_for_status): @mock.patch.object(common_utils, 'wait_for_status', return_value=True) def test_server_backup_wait_ok(self, mock_wait_for_status): - self.image_client.get_image = mock.Mock( - side_effect=self.image, - ) + self.image_client.get_image.side_effect = (self.image,) arglist = [ '--name', diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py index c64a2b9c8..a47ea7298 100644 --- a/openstackclient/tests/unit/compute/v2/test_service.py +++ b/openstackclient/tests/unit/compute/v2/test_service.py @@ -77,9 +77,7 @@ def test_multi_services_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) delete_mock_result = [None, exceptions.CommandError] - self.compute_client.delete_service = mock.Mock( - side_effect=delete_mock_result - ) + self.compute_client.delete_service.side_effect = delete_mock_result try: self.cmd.take_action(parsed_args) diff --git a/openstackclient/tests/unit/network/test_common.py b/openstackclient/tests/unit/network/test_common.py index c1ad9b28d..02cb021ef 100644 --- a/openstackclient/tests/unit/network/test_common.py +++ b/openstackclient/tests/unit/network/test_common.py @@ -128,15 +128,11 @@ def setUp(self): self.app.client_manager.network = mock.Mock() self.network_client = self.app.client_manager.network - self.network_client.network_action = mock.Mock( - return_value='take_action_network' - ) + self.network_client.network_action.return_value = 'take_action_network' self.app.client_manager.compute = mock.Mock() self.compute_client = self.app.client_manager.compute - self.compute_client.compute_action = mock.Mock( - return_value='take_action_compute' - ) + self.compute_client.compute_action.return_value = 'take_action_compute' self.cmd = FakeNetworkAndComputeCommand(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_address_group.py b/openstackclient/tests/unit/network/v2/test_address_group.py index 411d19923..48f706631 100644 --- a/openstackclient/tests/unit/network/v2/test_address_group.py +++ b/openstackclient/tests/unit/network/v2/test_address_group.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -58,8 +57,8 @@ class TestCreateAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.create_address_group = mock.Mock( - return_value=self.new_address_group + self.network_client.create_address_group.return_value = ( + self.new_address_group ) # Get the command object to test @@ -145,7 +144,7 @@ class TestDeleteAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.delete_address_group = mock.Mock(return_value=None) + self.network_client.delete_address_group.return_value = None self.network_client.find_address_group = ( network_fakes.get_address_groups( address_groups=self._address_groups @@ -206,9 +205,7 @@ def test_multi_address_groups_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._address_groups[0], exceptions.CommandError] - self.network_client.find_address_group = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_address_group.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -251,9 +248,7 @@ class TestListAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.address_groups = mock.Mock( - return_value=self.address_groups - ) + self.network_client.address_groups.return_value = self.address_groups # Get the command object to test self.cmd = address_group.ListAddressGroup(self.app, None) @@ -333,13 +328,15 @@ class TestSetAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.update_address_group = mock.Mock(return_value=None) - self.network_client.find_address_group = mock.Mock( - return_value=self._address_group + self.network_client.update_address_group.return_value = None + self.network_client.find_address_group.return_value = ( + self._address_group ) - self.network_client.add_addresses_to_address_group = mock.Mock( - return_value=self._address_group + + self.network_client.add_addresses_to_address_group.return_value = ( + self._address_group ) + # Get the command object to test self.cmd = address_group.SetAddressGroup(self.app, None) @@ -442,8 +439,8 @@ class TestShowAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.find_address_group = mock.Mock( - return_value=self._address_group + self.network_client.find_address_group.return_value = ( + self._address_group ) # Get the command object to test @@ -486,12 +483,12 @@ class TestUnsetAddressGroup(TestAddressGroup): def setUp(self): super().setUp() - self.network_client.find_address_group = mock.Mock( - return_value=self._address_group - ) - self.network_client.remove_addresses_from_address_group = mock.Mock( - return_value=self._address_group + self.network_client.find_address_group.return_value = ( + self._address_group ) + + self.network_client.remove_addresses_from_address_group.return_value = self._address_group + # Get the command object to test self.cmd = address_group.UnsetAddressGroup(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_address_scope.py b/openstackclient/tests/unit/network/v2/test_address_scope.py index c3eb83d06..6e2c05ed9 100644 --- a/openstackclient/tests/unit/network/v2/test_address_scope.py +++ b/openstackclient/tests/unit/network/v2/test_address_scope.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -52,8 +51,8 @@ class TestCreateAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.create_address_scope = mock.Mock( - return_value=self.new_address_scope + self.network_client.create_address_scope.return_value = ( + self.new_address_scope ) # Get the command object to test @@ -160,7 +159,7 @@ class TestDeleteAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.delete_address_scope = mock.Mock(return_value=None) + self.network_client.delete_address_scope.return_value = None self.network_client.find_address_scope = ( network_fakes.get_address_scopes( address_scopes=self._address_scopes @@ -222,9 +221,7 @@ def test_multi_address_scopes_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._address_scopes[0], exceptions.CommandError] - self.network_client.find_address_scope = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_address_scope.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -267,9 +264,7 @@ class TestListAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.address_scopes = mock.Mock( - return_value=self.address_scopes - ) + self.network_client.address_scopes.return_value = self.address_scopes # Get the command object to test self.cmd = address_scope.ListAddressScope(self.app, None) @@ -398,9 +393,9 @@ class TestSetAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.update_address_scope = mock.Mock(return_value=None) - self.network_client.find_address_scope = mock.Mock( - return_value=self._address_scope + self.network_client.update_address_scope.return_value = None + self.network_client.find_address_scope.return_value = ( + self._address_scope ) # Get the command object to test @@ -488,8 +483,8 @@ class TestShowAddressScope(TestAddressScope): def setUp(self): super().setUp() - self.network_client.find_address_scope = mock.Mock( - return_value=self._address_scope + self.network_client.find_address_scope.return_value = ( + self._address_scope ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py b/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py index 7032047e3..c44e553c7 100644 --- a/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py +++ b/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call import uuid @@ -921,8 +920,8 @@ def test_multi_default_security_group_rules_delete_with_exception(self): self._default_sg_rules[0], exceptions.CommandError, ] - self.network_client.find_default_security_group_rule = mock.Mock( - side_effect=find_mock_result + self.network_client.find_default_security_group_rule.side_effect = ( + find_mock_result ) try: diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py index 605f4d99a..ab0ec176a 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from openstack.network.v2 import floating_ip as _floating_ip @@ -86,16 +85,14 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.create_ip = mock.Mock( - return_value=self.floating_ip - ) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_ip.return_value = self.floating_ip - self.network_client.find_network = mock.Mock( - return_value=self.floating_network - ) - self.network_client.find_subnet = mock.Mock(return_value=self.subnet) - self.network_client.find_port = mock.Mock(return_value=self.port) + self.network_client.set_tags.return_value = None + + self.network_client.find_network.return_value = self.floating_network + + self.network_client.find_subnet.return_value = self.subnet + self.network_client.find_port.return_value = self.port # Get the command object to test self.cmd = fip.CreateFloatingIP(self.app, None) @@ -304,7 +301,7 @@ class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.delete_ip = mock.Mock(return_value=None) + self.network_client.delete_ip.return_value = None # Get the command object to test self.cmd = fip.DeleteFloatingIP(self.app, None) @@ -470,14 +467,11 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.ips = mock.Mock(return_value=self.floating_ips) - self.network_client.find_network = mock.Mock( - return_value=self.fake_network - ) - self.network_client.find_port = mock.Mock(return_value=self.fake_port) - self.network_client.find_router = mock.Mock( - return_value=self.fake_router - ) + self.network_client.ips.return_value = self.floating_ips + self.network_client.find_network.return_value = self.fake_network + + self.network_client.find_port.return_value = self.fake_port + self.network_client.find_router.return_value = self.fake_router # Get the command object to test self.cmd = fip.ListFloatingIP(self.app, None) @@ -713,7 +707,7 @@ def setUp(self): self.floating_ip = sdk_fakes.generate_fake_resource( _floating_ip.FloatingIP ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + self.network_client.find_ip.return_value = self.floating_ip self.columns = ( 'created_at', @@ -797,10 +791,10 @@ class TestSetFloatingIP(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) - self.network_client.find_port = mock.Mock(return_value=self.port) - self.network_client.update_ip = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_ip.return_value = self.floating_ip + self.network_client.find_port.return_value = self.port + self.network_client.update_ip.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = fip.SetFloatingIP(self.app, None) @@ -1044,9 +1038,9 @@ class TestUnsetFloatingIP(TestFloatingIPNetwork): def setUp(self): super().setUp() - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) - self.network_client.update_ip = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_ip.return_value = self.floating_ip + self.network_client.update_ip.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = fip.UnsetFloatingIP(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py index 2e4c93f8a..33b9011c6 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_port_forwarding.py @@ -33,7 +33,7 @@ def setUp(self): ) self.port = network_fakes.create_one_port() self.project = identity_fakes_v2.FakeProject.create_one_project() - self.network_client.find_port = mock.Mock(return_value=self.port) + self.network_client.find_port.return_value = self.port class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding): @@ -54,11 +54,11 @@ def setUp(self): }, ) - self.network_client.create_floating_ip_port_forwarding = mock.Mock( - return_value=self.new_port_forwarding + self.network_client.create_floating_ip_port_forwarding.return_value = ( + self.new_port_forwarding ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.CreateFloatingIPPortForwarding( @@ -351,11 +351,11 @@ def setUp(self): }, ) ) - self.network_client.delete_floating_ip_port_forwarding = mock.Mock( - return_value=None + self.network_client.delete_floating_ip_port_forwarding.return_value = ( + None ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.DeleteFloatingIPPortForwarding( self.app, None @@ -490,10 +490,11 @@ def setUp(self): port_forwarding.description, ) ) - self.network_client.floating_ip_port_forwardings = mock.Mock( - return_value=self.port_forwardings + self.network_client.floating_ip_port_forwardings.return_value = ( + self.port_forwardings ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.ListFloatingIPPortForwarding( self.app, None @@ -557,14 +558,15 @@ def setUp(self): 'floatingip_id': self.floating_ip.id, } ) - self.network_client.update_floating_ip_port_forwarding = mock.Mock( - return_value=None + self.network_client.update_floating_ip_port_forwarding.return_value = ( + None ) - self.network_client.find_floating_ip_port_forwarding = mock.Mock( - return_value=self._port_forwarding + self.network_client.find_floating_ip_port_forwarding.return_value = ( + self._port_forwarding ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.SetFloatingIPPortForwarding( self.app, None @@ -690,10 +692,11 @@ def setUp(self): self._port_forwarding.internal_port_range, self._port_forwarding.protocol, ) - self.network_client.find_floating_ip_port_forwarding = mock.Mock( - return_value=self._port_forwarding + self.network_client.find_floating_ip_port_forwarding.return_value = ( + self._port_forwarding ) - self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) + + self.network_client.find_ip.return_value = self.floating_ip # Get the command object to test self.cmd = floating_ip_port_forwarding.ShowFloatingIPPortForwarding( self.app, None diff --git a/openstackclient/tests/unit/network/v2/test_ip_availability.py b/openstackclient/tests/unit/network/v2/test_ip_availability.py index 4224d1266..def3e17da 100644 --- a/openstackclient/tests/unit/network/v2/test_ip_availability.py +++ b/openstackclient/tests/unit/network/v2/test_ip_availability.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from osc_lib.cli import format_columns @@ -55,8 +54,8 @@ def setUp(self): super().setUp() self.cmd = ip_availability.ListIPAvailability(self.app, None) - self.network_client.network_ip_availabilities = mock.Mock( - return_value=self._ip_availability + self.network_client.network_ip_availabilities.return_value = ( + self._ip_availability ) def test_list_no_options(self): @@ -134,13 +133,12 @@ class TestShowIPAvailability(TestIPAvailability): def setUp(self): super().setUp() - self.network_client.find_network_ip_availability = mock.Mock( - return_value=self._ip_availability - ) - self.network_client.find_network = mock.Mock( - return_value=self._network + self.network_client.find_network_ip_availability.return_value = ( + self._ip_availability ) + self.network_client.find_network.return_value = self._network + # Get the command object to test self.cmd = ip_availability.ShowIPAvailability(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py b/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py index b358024ff..0769e2e56 100644 --- a/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +++ b/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from osc_lib import exceptions @@ -46,8 +45,8 @@ def setUp(self): self.ct_helper.protocol, self.ct_helper.router_id, ) - self.network_client.create_conntrack_helper = mock.Mock( - return_value=self.ct_helper + self.network_client.create_conntrack_helper.return_value = ( + self.ct_helper ) # Get the command object to test @@ -119,9 +118,7 @@ def setUp(self): attrs ) ) - self.network_client.delete_conntrack_helper = mock.Mock( - return_value=None - ) + self.network_client.delete_conntrack_helper.return_value = None # Get the command object to test self.cmd = l3_conntrack_helper.DeleteConntrackHelper(self.app, None) @@ -181,9 +178,7 @@ def setUp(self): ct_helper.port, ) ) - self.network_client.conntrack_helpers = mock.Mock( - return_value=ct_helpers - ) + self.network_client.conntrack_helpers.return_value = ct_helpers # Get the command object to test self.cmd = l3_conntrack_helper.ListConntrackHelper(self.app, None) @@ -216,9 +211,7 @@ def setUp(self): attrs ) ) - self.network_client.update_conntrack_helper = mock.Mock( - return_value=None - ) + self.network_client.update_conntrack_helper.return_value = None # Get the command object to test self.cmd = l3_conntrack_helper.SetConntrackHelper(self.app, None) @@ -281,9 +274,7 @@ def setUp(self): self.ct_helper.protocol, self.ct_helper.router_id, ) - self.network_client.get_conntrack_helper = mock.Mock( - return_value=self.ct_helper - ) + self.network_client.get_conntrack_helper.return_value = self.ct_helper # Get the command object to test self.cmd = l3_conntrack_helper.ShowConntrackHelper(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_local_ip.py b/openstackclient/tests/unit/network/v2/test_local_ip.py index cf4105b02..585fec767 100644 --- a/openstackclient/tests/unit/network/v2/test_local_ip.py +++ b/openstackclient/tests/unit/network/v2/test_local_ip.py @@ -13,7 +13,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -77,13 +76,11 @@ class TestCreateLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.create_local_ip = mock.Mock( - return_value=self.new_local_ip - ) - self.network_client.find_network = mock.Mock( - return_value=self.local_ip_network - ) - self.network_client.find_port = mock.Mock(return_value=self.port) + self.network_client.create_local_ip.return_value = self.new_local_ip + + self.network_client.find_network.return_value = self.local_ip_network + + self.network_client.find_port.return_value = self.port # Get the command object to test self.cmd = local_ip.CreateLocalIP(self.app, None) @@ -149,7 +146,7 @@ class TestDeleteLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.delete_local_ip = mock.Mock(return_value=None) + self.network_client.delete_local_ip.return_value = None self.network_client.find_local_ip = network_fakes.get_local_ips( local_ips=self._local_ips ) @@ -205,9 +202,7 @@ def test_multi_local_ips_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._local_ips[0], exceptions.CommandError] - self.network_client.find_local_ip = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_local_ip.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -258,10 +253,8 @@ class TestListLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.local_ips = mock.Mock(return_value=self.local_ips) - self.network_client.find_network = mock.Mock( - return_value=self.fake_network - ) + self.network_client.local_ips.return_value = self.local_ips + self.network_client.find_network.return_value = self.fake_network # Get the command object to test self.cmd = local_ip.ListLocalIP(self.app, None) @@ -402,10 +395,8 @@ class TestSetLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.update_local_ip = mock.Mock(return_value=None) - self.network_client.find_local_ip = mock.Mock( - return_value=self._local_ip - ) + self.network_client.update_local_ip.return_value = None + self.network_client.find_local_ip.return_value = self._local_ip # Get the command object to test self.cmd = local_ip.SetLocalIP(self.app, None) @@ -482,9 +473,7 @@ class TestShowLocalIP(TestLocalIP): def setUp(self): super().setUp() - self.network_client.find_local_ip = mock.Mock( - return_value=self._local_ip - ) + self.network_client.find_local_ip.return_value = self._local_ip # Get the command object to test self.cmd = local_ip.ShowLocalIP(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_local_ip_association.py b/openstackclient/tests/unit/network/v2/test_local_ip_association.py index 2c1c0ff31..9efdc295f 100644 --- a/openstackclient/tests/unit/network/v2/test_local_ip_association.py +++ b/openstackclient/tests/unit/network/v2/test_local_ip_association.py @@ -29,7 +29,7 @@ def setUp(self): self.local_ip = network_fakes.create_one_local_ip() self.fixed_port = network_fakes.create_one_port() self.project = identity_fakes_v2.FakeProject.create_one_project() - self.network_client.find_port = mock.Mock(return_value=self.fixed_port) + self.network_client.find_port.return_value = self.fixed_port class TestCreateLocalIPAssociation(TestLocalIPAssociation): @@ -43,13 +43,11 @@ def setUp(self): } ) ) - self.network_client.create_local_ip_association = mock.Mock( - return_value=self.new_local_ip_association + self.network_client.create_local_ip_association.return_value = ( + self.new_local_ip_association ) - self.network_client.find_local_ip = mock.Mock( - return_value=self.local_ip - ) + self.network_client.find_local_ip.return_value = self.local_ip # Get the command object to test self.cmd = local_ip_association.CreateLocalIPAssociation( @@ -128,13 +126,10 @@ def setUp(self): }, ) ) - self.network_client.delete_local_ip_association = mock.Mock( - return_value=None - ) + self.network_client.delete_local_ip_association.return_value = None + + self.network_client.find_local_ip.return_value = self.local_ip - self.network_client.find_local_ip = mock.Mock( - return_value=self.local_ip - ) # Get the command object to test self.cmd = local_ip_association.DeleteLocalIPAssociation( self.app, None @@ -262,13 +257,13 @@ def setUp(self): lip_assoc.host, ) ) - self.network_client.local_ip_associations = mock.Mock( - return_value=self.local_ip_associations + self.network_client.local_ip_associations.return_value = ( + self.local_ip_associations ) - self.network_client.find_local_ip = mock.Mock( - return_value=self.local_ip - ) - self.network_client.find_port = mock.Mock(return_value=self.fixed_port) + + self.network_client.find_local_ip.return_value = self.local_ip + + self.network_client.find_port.return_value = self.fixed_port # Get the command object to test self.cmd = local_ip_association.ListLocalIPAssociation(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py index f9d9a3b3c..0fe8740da 100644 --- a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py +++ b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -31,9 +30,9 @@ def setUp(self): self.domains_mock = self.identity_client.domains self.router = network_fakes.create_one_router({'id': 'fake-router-id'}) - self.network_client.find_router = mock.Mock(return_value=self.router) + self.network_client.find_router.return_value = self.router self.port = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=self.port) + self.network_client.find_port.return_value = self.port class TestCreateNDPProxy(TestNDPProxy): @@ -66,9 +65,7 @@ def setUp(self): self.ndp_proxy.router_id, self.ndp_proxy.updated_at, ) - self.network_client.create_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) + self.network_client.create_ndp_proxy.return_value = self.ndp_proxy # Get the command object to test self.cmd = ndp_proxy.CreateNDPProxy(self.app, None) @@ -127,10 +124,8 @@ def setUp(self): attrs = {'router_id': self.router.id, 'port_id': self.port.id} self.ndp_proxies = network_fakes.create_ndp_proxies(attrs) self.ndp_proxy = self.ndp_proxies[0] - self.network_client.delete_ndp_proxy = mock.Mock(return_value=None) - self.network_client.find_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) + self.network_client.delete_ndp_proxy.return_value = None + self.network_client.find_ndp_proxy.return_value = self.ndp_proxy # Get the command object to test self.cmd = ndp_proxy.DeleteNDPProxy(self.app, None) @@ -203,7 +198,7 @@ def setUp(self): ) ) - self.network_client.ndp_proxies = mock.Mock(return_value=ndp_proxies) + self.network_client.ndp_proxies.return_value = ndp_proxies # Get the command object to test self.cmd = ndp_proxy.ListNDPProxy(self.app, None) @@ -340,10 +335,8 @@ def setUp(self): super().setUp() attrs = {'router_id': self.router.id, 'port_id': self.port.id} self.ndp_proxy = network_fakes.create_one_ndp_proxy(attrs) - self.network_client.update_ndp_proxy = mock.Mock(return_value=None) - self.network_client.find_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) + self.network_client.update_ndp_proxy.return_value = None + self.network_client.find_ndp_proxy.return_value = self.ndp_proxy # Get the command object to test self.cmd = ndp_proxy.SetNDPProxy(self.app, None) @@ -434,12 +427,9 @@ def setUp(self): self.ndp_proxy.router_id, self.ndp_proxy.updated_at, ) - self.network_client.get_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) - self.network_client.find_ndp_proxy = mock.Mock( - return_value=self.ndp_proxy - ) + self.network_client.get_ndp_proxy.return_value = self.ndp_proxy + + self.network_client.find_ndp_proxy.return_value = self.ndp_proxy # Get the command object to test self.cmd = ndp_proxy.ShowNDPProxy(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index b2e65f213..1e923d053 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -12,7 +12,6 @@ # import random -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -116,19 +115,16 @@ class TestCreateNetworkIdentityV3(TestNetwork): def setUp(self): super().setUp() - self.network_client.create_network = mock.Mock( - return_value=self._network - ) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_network.return_value = self._network + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = network.CreateNetwork(self.app, None) self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy - ) + self.network_client.find_qos_policy.return_value = self.qos_policy def test_create_no_options(self): arglist = [] @@ -423,10 +419,9 @@ class TestCreateNetworkIdentityV2( def setUp(self): super().setUp() - self.network_client.create_network = mock.Mock( - return_value=self._network - ) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_network.return_value = self._network + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = network.CreateNetwork(self.app, None) @@ -498,7 +493,7 @@ def setUp(self): # The networks to delete self._networks = network_fakes.create_networks(count=3) - self.network_client.delete_network = mock.Mock(return_value=None) + self.network_client.delete_network.return_value = None self.network_client.find_network = network_fakes.get_networks( networks=self._networks @@ -557,14 +552,14 @@ def test_delete_multiple_networks_exception(self): exceptions.NotFound('404'), self._networks[1], ] - self.network_client.find_network = mock.Mock(side_effect=ret_find) + self.network_client.find_network.side_effect = ret_find # Fake exception in delete_network() ret_delete = [ None, exceptions.NotFound('404'), ] - self.network_client.delete_network = mock.Mock(side_effect=ret_delete) + self.network_client.delete_network.side_effect = ret_delete self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args @@ -636,13 +631,13 @@ def setUp(self): # Get the command object to test self.cmd = network.ListNetwork(self.app, None) - self.network_client.networks = mock.Mock(return_value=self._network) + self.network_client.networks.return_value = self._network self._agent = network_fakes.create_one_network_agent() self.network_client.get_agent.return_value = self._agent - self.network_client.dhcp_agent_hosting_networks = mock.Mock( - return_value=self._network + self.network_client.dhcp_agent_hosting_networks.return_value = ( + self._network ) # TestListTagMixin @@ -991,15 +986,12 @@ class TestSetNetwork(TestNetwork): def setUp(self): super().setUp() - self.network_client.update_network = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.update_network.return_value = None + self.network_client.set_tags.return_value = None - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy - ) + self.network_client.find_network.return_value = self._network + + self.network_client.find_qos_policy.return_value = self.qos_policy # Get the command object to test self.cmd = network.SetNetwork(self.app, None) @@ -1242,9 +1234,7 @@ class TestShowNetwork(TestNetwork): def setUp(self): super().setUp() - self.network_client.find_network = mock.Mock( - return_value=self._network - ) + self.network_client.find_network.return_value = self._network # Get the command object to test self.cmd = network.ShowNetwork(self.app, None) @@ -1290,15 +1280,12 @@ class TestUnsetNetwork(TestNetwork): def setUp(self): super().setUp() - self.network_client.update_network = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.update_network.return_value = None + self.network_client.set_tags.return_value = None - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy - ) + self.network_client.find_network.return_value = self._network + + self.network_client.find_qos_policy.return_value = self.qos_policy # Get the command object to test self.cmd = network.UnsetNetwork(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_agent.py b/openstackclient/tests/unit/network/v2/test_network_agent.py index bd79950d5..48b394d7a 100644 --- a/openstackclient/tests/unit/network/v2/test_network_agent.py +++ b/openstackclient/tests/unit/network/v2/test_network_agent.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -34,8 +33,8 @@ class TestAddNetworkToAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock(return_value=self.agent) - self.network_client.find_network = mock.Mock(return_value=self.net) + self.network_client.get_agent.return_value = self.agent + self.network_client.find_network.return_value = self.net self.network_client.name = self.network_client.find_network.name self.cmd = network_agent.AddNetworkToAgent(self.app, None) @@ -76,8 +75,8 @@ class TestAddRouterAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock(return_value=self._agent) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.get_agent.return_value = self._agent + self.network_client.find_router.return_value = self._router self.cmd = network_agent.AddRouterToAgent(self.app, None) @@ -120,7 +119,7 @@ class TestDeleteNetworkAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.delete_agent = mock.Mock(return_value=None) + self.network_client.delete_agent.return_value = None # Get the command object to test self.cmd = network_agent.DeleteNetworkAgent(self.app, None) @@ -173,9 +172,7 @@ def test_multi_network_agents_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) delete_mock_result = [True, exceptions.CommandError] - self.network_client.delete_agent = mock.Mock( - side_effect=delete_mock_result - ) + self.network_client.delete_agent.side_effect = delete_mock_result try: self.cmd.take_action(parsed_args) @@ -357,8 +354,8 @@ class TestRemoveNetworkFromAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock(return_value=self.agent) - self.network_client.find_network = mock.Mock(return_value=self.net) + self.network_client.get_agent.return_value = self.agent + self.network_client.find_network.return_value = self.net self.network_client.name = self.network_client.find_network.name self.cmd = network_agent.RemoveNetworkFromAgent(self.app, None) @@ -414,8 +411,8 @@ class TestRemoveRouterAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock(return_value=self._agent) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.get_agent.return_value = self._agent + self.network_client.find_router.return_value = self._router self.cmd = network_agent.RemoveRouterFromAgent(self.app, None) @@ -458,10 +455,8 @@ class TestSetNetworkAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.update_agent = mock.Mock(return_value=None) - self.network_client.get_agent = mock.Mock( - return_value=self._network_agent - ) + self.network_client.update_agent.return_value = None + self.network_client.get_agent.return_value = self._network_agent # Get the command object to test self.cmd = network_agent.SetNetworkAgent(self.app, None) @@ -574,9 +569,7 @@ class TestShowNetworkAgent(TestNetworkAgent): def setUp(self): super().setUp() - self.network_client.get_agent = mock.Mock( - return_value=self._network_agent - ) + self.network_client.get_agent.return_value = self._network_agent # Get the command object to test self.cmd = network_agent.ShowNetworkAgent(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py b/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py index 20c645bd4..d13bd8cf9 100644 --- a/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py +++ b/openstackclient/tests/unit/network/v2/test_network_auto_allocated_topology.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from openstackclient.network.v2 import network_auto_allocated_topology from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes @@ -50,8 +49,8 @@ def setUp(self): self.cmd = network_auto_allocated_topology.CreateAutoAllocatedTopology( self.app, None ) - self.network_client.get_auto_allocated_topology = mock.Mock( - return_value=self.topology + self.network_client.get_auto_allocated_topology.return_value = ( + self.topology ) def test_create_no_options(self): @@ -155,8 +154,8 @@ def setUp(self): self.cmd = network_auto_allocated_topology.CreateAutoAllocatedTopology( self.app, None ) - self.network_client.validate_auto_allocated_topology = mock.Mock( - return_value=self.topology + self.network_client.validate_auto_allocated_topology.return_value = ( + self.topology ) def test_show_dry_run_no_project(self): @@ -228,9 +227,7 @@ def setUp(self): self.cmd = network_auto_allocated_topology.DeleteAutoAllocatedTopology( self.app, None ) - self.network_client.delete_auto_allocated_topology = mock.Mock( - return_value=None - ) + self.network_client.delete_auto_allocated_topology.return_value = None def test_delete_no_project(self): arglist = [] diff --git a/openstackclient/tests/unit/network/v2/test_network_flavor.py b/openstackclient/tests/unit/network/v2/test_network_flavor.py index cfbe1f7b0..10038e393 100644 --- a/openstackclient/tests/unit/network/v2/test_network_flavor.py +++ b/openstackclient/tests/unit/network/v2/test_network_flavor.py @@ -41,11 +41,9 @@ class TestAddNetworkFlavorToProfile(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.find_flavor = mock.Mock( - return_value=self.network_flavor - ) - self.network_client.find_service_profile = mock.Mock( - return_value=self.service_profile + self.network_client.find_flavor.return_value = self.network_flavor + self.network_client.find_service_profile.return_value = ( + self.service_profile ) self.cmd = network_flavor.AddNetworkFlavorToProfile(self.app, None) @@ -102,8 +100,8 @@ class TestCreateNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.create_flavor = mock.Mock( - return_value=self.new_network_flavor + self.network_client.create_flavor.return_value = ( + self.new_network_flavor ) # Get the command object to test @@ -218,7 +216,7 @@ class TestDeleteNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.delete_flavor = mock.Mock(return_value=None) + self.network_client.delete_flavor.return_value = None self.network_client.find_flavor = network_fakes.get_flavor( network_flavors=self._network_flavors ) @@ -278,9 +276,7 @@ def test_multi_network_flavors_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._network_flavors[0], exceptions.CommandError] - self.network_client.find_flavor = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_flavor.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -323,9 +319,7 @@ class TestListNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.flavors = mock.Mock( - return_value=self._network_flavors - ) + self.network_client.flavors.return_value = self._network_flavors # Get the command object to test self.cmd = network_flavor.ListNetworkFlavor(self.app, None) @@ -348,11 +342,9 @@ class TestRemoveNetworkFlavorFromProfile(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.find_flavor = mock.Mock( - return_value=self.network_flavor - ) - self.network_client.find_service_profile = mock.Mock( - return_value=self.service_profile + self.network_client.find_flavor.return_value = self.network_flavor + self.network_client.find_service_profile.return_value = ( + self.service_profile ) self.network_client.disassociate_flavor_from_service_profile = ( mock.Mock() @@ -412,9 +404,7 @@ class TestShowNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.find_flavor = mock.Mock( - return_value=self.new_network_flavor - ) + self.network_client.find_flavor.return_value = self.new_network_flavor # Get the command object to test self.cmd = network_flavor.ShowNetworkFlavor(self.app, None) @@ -456,10 +446,8 @@ class TestSetNetworkFlavor(TestNetworkFlavor): def setUp(self): super().setUp() - self.network_client.update_flavor = mock.Mock(return_value=None) - self.network_client.find_flavor = mock.Mock( - return_value=self.new_network_flavor - ) + self.network_client.update_flavor.return_value = None + self.network_client.find_flavor.return_value = self.new_network_flavor # Get the command object to test self.cmd = network_flavor.SetNetworkFlavor(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py index d94926c4c..c8235bfef 100644 --- a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py +++ b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py @@ -49,9 +49,10 @@ class TestCreateFlavorProfile(TestFlavorProfile): def setUp(self): super().setUp() - self.network_client.create_service_profile = mock.Mock( - return_value=self.new_flavor_profile + self.network_client.create_service_profile.return_value = ( + self.new_flavor_profile ) + # Get the command object to test self.cmd = network_flavor_profile.CreateNetworkFlavorProfile( self.app, None @@ -195,9 +196,8 @@ class TestDeleteFlavorProfile(TestFlavorProfile): def setUp(self): super().setUp() - self.network_client.delete_service_profile = mock.Mock( - return_value=None - ) + self.network_client.delete_service_profile.return_value = None + self.network_client.find_service_profile = ( network_fakes.get_service_profile( flavor_profile=self._network_flavor_profiles @@ -266,9 +266,7 @@ def test_multi_network_flavor_profiles_delete_with_exception(self): self._network_flavor_profiles[0], exceptions.CommandError, ] - self.network_client.find_service_profile = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_service_profile.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -314,8 +312,8 @@ class TestListFlavorProfile(TestFlavorProfile): def setUp(self): super().setUp() - self.network_client.service_profiles = mock.Mock( - return_value=self._network_flavor_profiles + self.network_client.service_profiles.return_value = ( + self._network_flavor_profiles ) # Get the command object to test @@ -355,8 +353,8 @@ class TestShowFlavorProfile(TestFlavorProfile): def setUp(self): super().setUp() - self.network_client.find_service_profile = mock.Mock( - return_value=self.network_flavor_profile + self.network_client.find_service_profile.return_value = ( + self.network_flavor_profile ) # Get the command object to test @@ -388,11 +386,10 @@ class TestSetFlavorProfile(TestFlavorProfile): def setUp(self): super().setUp() - self.network_client.update_service_profile = mock.Mock( - return_value=None - ) - self.network_client.find_service_profile = mock.Mock( - return_value=self.network_flavor_profile + self.network_client.update_service_profile.return_value = None + + self.network_client.find_service_profile.return_value = ( + self.network_flavor_profile ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_network_meter.py b/openstackclient/tests/unit/network/v2/test_network_meter.py index a92035d6d..f13839a62 100644 --- a/openstackclient/tests/unit/network/v2/test_network_meter.py +++ b/openstackclient/tests/unit/network/v2/test_network_meter.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -55,9 +54,8 @@ class TestCreateMeter(TestMeter): def setUp(self): super().setUp() - self.network_client.create_metering_label = mock.Mock( - return_value=self.new_meter - ) + self.network_client.create_metering_label.return_value = self.new_meter + self.projects_mock.get.return_value = self.project self.cmd = network_meter.CreateMeter(self.app, None) @@ -132,9 +130,7 @@ def setUp(self): self.meter_list = network_fakes.FakeNetworkMeter.create_meter(count=2) - self.network_client.delete_metering_label = mock.Mock( - return_value=None - ) + self.network_client.delete_metering_label.return_value = None self.network_client.find_metering_label = ( network_fakes.FakeNetworkMeter.get_meter(meter=self.meter_list) @@ -194,15 +190,13 @@ def test_delete_multiple_meter_exception(self): exceptions.NotFound('404'), self.meter_list[1], ] - self.network_client.find_meter = mock.Mock(side_effect=return_find) + self.network_client.find_metering_label.side_effect = return_find ret_delete = [ None, exceptions.NotFound('404'), ] - self.network_client.delete_metering_label = mock.Mock( - side_effect=ret_delete - ) + self.network_client.delete_metering_label.side_effect = ret_delete self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args @@ -240,9 +234,7 @@ class TestListMeter(TestMeter): def setUp(self): super().setUp() - self.network_client.metering_labels = mock.Mock( - return_value=self.meter_list - ) + self.network_client.metering_labels.return_value = self.meter_list self.cmd = network_meter.ListMeter(self.app, None) @@ -282,9 +274,7 @@ def setUp(self): self.cmd = network_meter.ShowMeter(self.app, None) - self.network_client.find_metering_label = mock.Mock( - return_value=self.new_meter - ) + self.network_client.find_metering_label.return_value = self.new_meter def test_show_no_options(self): arglist = [] diff --git a/openstackclient/tests/unit/network/v2/test_network_meter_rule.py b/openstackclient/tests/unit/network/v2/test_network_meter_rule.py index e90ed05f3..407fb04bb 100644 --- a/openstackclient/tests/unit/network/v2/test_network_meter_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_meter_rule.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -65,14 +64,13 @@ def setUp(self): {'id': self.new_rule.metering_label_id} ) - self.network_client.create_metering_label_rule = mock.Mock( - return_value=self.new_rule + self.network_client.create_metering_label_rule.return_value = ( + self.new_rule ) + self.projects_mock.get.return_value = self.project self.cmd = network_meter_rule.CreateMeterRule(self.app, None) - self.network_client.find_metering_label = mock.Mock( - return_value=fake_meter - ) + self.network_client.find_metering_label.return_value = fake_meter def test_create_no_options(self): arglist = [] @@ -146,9 +144,7 @@ def setUp(self): self.rule_list = network_fakes.FakeNetworkMeterRule.create_meter_rule( count=2 ) - self.network_client.delete_metering_label_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_metering_label_rule.return_value = None self.network_client.find_metering_label_rule = ( network_fakes.FakeNetworkMeterRule.get_meter_rule( @@ -210,17 +206,13 @@ def test_delete_multiple_rules_exception(self): exceptions.NotFound('404'), self.rule_list[1], ] - self.network_client.find_metering_label_rule = mock.Mock( - side_effect=return_find - ) + self.network_client.find_metering_label_rule.side_effect = return_find ret_delete = [ None, exceptions.NotFound('404'), ] - self.network_client.delete_metering_label_rule = mock.Mock( - side_effect=ret_delete - ) + self.network_client.delete_metering_label_rule.side_effect = ret_delete self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args @@ -262,9 +254,7 @@ class TestListMeterRule(TestMeterRule): def setUp(self): super().setUp() - self.network_client.metering_label_rules = mock.Mock( - return_value=self.rule_list - ) + self.network_client.metering_label_rules.return_value = self.rule_list self.cmd = network_meter_rule.ListMeterRule(self.app, None) @@ -311,8 +301,8 @@ def setUp(self): self.cmd = network_meter_rule.ShowMeterRule(self.app, None) - self.network_client.find_metering_label_rule = mock.Mock( - return_value=self.new_rule + self.network_client.find_metering_label_rule.return_value = ( + self.new_rule ) def test_show_no_options(self): diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_policy.py b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py index fae5b69d5..17f40ef6b 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_policy.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py @@ -63,8 +63,8 @@ class TestCreateNetworkQosPolicy(TestQosPolicy): def setUp(self): super().setUp() - self.network_client.create_qos_policy = mock.Mock( - return_value=self.new_qos_policy + self.network_client.create_qos_policy.return_value = ( + self.new_qos_policy ) # Get the command object to test @@ -163,7 +163,7 @@ class TestDeleteNetworkQosPolicy(TestQosPolicy): def setUp(self): super().setUp() - self.network_client.delete_qos_policy = mock.Mock(return_value=None) + self.network_client.delete_qos_policy.return_value = None self.network_client.find_qos_policy = network_fakes.get_qos_policies( qos_policies=self._qos_policies ) @@ -264,9 +264,7 @@ class TestListNetworkQosPolicy(TestQosPolicy): def setUp(self): super().setUp() - self.network_client.qos_policies = mock.Mock( - return_value=self.qos_policies - ) + self.network_client.qos_policies.return_value = self.qos_policies # Get the command object to test self.cmd = network_qos_policy.ListNetworkQosPolicy(self.app, None) @@ -344,10 +342,8 @@ class TestSetNetworkQosPolicy(TestQosPolicy): def setUp(self): super().setUp() - self.network_client.update_qos_policy = mock.Mock(return_value=None) - self.network_client.find_qos_policy = mock.Mock( - return_value=self._qos_policy - ) + self.network_client.update_qos_policy.return_value = None + self.network_client.find_qos_policy.return_value = self._qos_policy # Get the command object to test self.cmd = network_qos_policy.SetNetworkQosPolicy(self.app, None) @@ -447,9 +443,7 @@ class TestShowNetworkQosPolicy(TestQosPolicy): def setUp(self): super().setUp() - self.network_client.find_qos_policy = mock.Mock( - return_value=self._qos_policy - ) + self.network_client.find_qos_policy.return_value = self._qos_policy # Get the command object to test self.cmd = network_qos_policy.ShowNetworkQosPolicy(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py index e6586849e..430030402 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py @@ -56,9 +56,7 @@ class TestNetworkQosRule(network_fakes.TestNetworkV2): def setUp(self): super().setUp() self.qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy - ) + self.network_client.find_qos_policy.return_value = self.qos_policy class TestCreateNetworkQosRuleMinimumBandwidth(TestNetworkQosRule): @@ -87,8 +85,8 @@ def setUp(self): self.new_rule.project_id, self.new_rule.type, ) - self.network_client.create_qos_minimum_bandwidth_rule = mock.Mock( - return_value=self.new_rule + self.network_client.create_qos_minimum_bandwidth_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -190,9 +188,7 @@ def setUp(self): self.new_rule.project_id, self.new_rule.type, ) - self.network_client.create_qos_minimum_packet_rate_rule = mock.Mock( - return_value=self.new_rule - ) + self.network_client.create_qos_minimum_packet_rate_rule.return_value = self.new_rule # Get the command object to test self.cmd = network_qos_rule.CreateNetworkQosRule(self.app, None) @@ -291,8 +287,8 @@ def setUp(self): self.new_rule.project_id, self.new_rule.type, ) - self.network_client.create_qos_dscp_marking_rule = mock.Mock( - return_value=self.new_rule + self.network_client.create_qos_dscp_marking_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -389,8 +385,8 @@ def setUp(self): self.new_rule.project_id, self.new_rule.type, ) - self.network_client.create_qos_bandwidth_limit_rule = mock.Mock( - return_value=self.new_rule + self.network_client.create_qos_bandwidth_limit_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -529,9 +525,10 @@ def setUp(self): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.delete_qos_minimum_bandwidth_rule = mock.Mock( - return_value=None + self.network_client.delete_qos_minimum_bandwidth_rule.return_value = ( + None ) + self.network_client.find_qos_minimum_bandwidth_rule = ( network_fakes.get_qos_rules(qos_rules=self.new_rule) ) @@ -592,9 +589,8 @@ def setUp(self): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.delete_qos_minimum_packet_rate_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_qos_minimum_packet_rate_rule.return_value = None + self.network_client.find_qos_minimum_packet_rate_rule = ( network_fakes.get_qos_rules(qos_rules=self.new_rule) ) @@ -655,9 +651,8 @@ def setUp(self): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.delete_qos_dscp_marking_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_qos_dscp_marking_rule.return_value = None + self.network_client.find_qos_dscp_marking_rule = ( network_fakes.get_qos_rules(qos_rules=self.new_rule) ) @@ -718,9 +713,8 @@ def setUp(self): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.delete_qos_bandwidth_limit_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_qos_bandwidth_limit_rule.return_value = None + self.network_client.find_qos_bandwidth_limit_rule = ( network_fakes.get_qos_rules(qos_rules=self.new_rule) ) @@ -781,16 +775,16 @@ def setUp(self): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.update_qos_minimum_bandwidth_rule = mock.Mock( - return_value=None + self.network_client.update_qos_minimum_bandwidth_rule.return_value = ( + None ) - self.network_client.find_qos_minimum_bandwidth_rule = mock.Mock( - return_value=self.new_rule - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy + + self.network_client.find_qos_minimum_bandwidth_rule.return_value = ( + self.new_rule ) + self.network_client.find_qos_policy.return_value = self.qos_policy + # Get the command object to test self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) @@ -883,16 +877,14 @@ def setUp(self): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.update_qos_minimum_packet_rate_rule = mock.Mock( - return_value=None - ) - self.network_client.find_qos_minimum_packet_rate_rule = mock.Mock( - return_value=self.new_rule - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy + self.network_client.update_qos_minimum_packet_rate_rule.return_value = None + + self.network_client.find_qos_minimum_packet_rate_rule.return_value = ( + self.new_rule ) + self.network_client.find_qos_policy.return_value = self.qos_policy + # Get the command object to test self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) @@ -985,16 +977,14 @@ def setUp(self): } self.new_rule = network_fakes.create_one_qos_rule(attrs) self.qos_policy.rules = [self.new_rule] - self.network_client.update_qos_dscp_marking_rule = mock.Mock( - return_value=None - ) - self.network_client.find_qos_dscp_marking_rule = mock.Mock( - return_value=self.new_rule - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_policy + self.network_client.update_qos_dscp_marking_rule.return_value = None + + self.network_client.find_qos_dscp_marking_rule.return_value = ( + self.new_rule ) + self.network_client.find_qos_policy.return_value = self.qos_policy + # Get the command object to test self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) @@ -1351,8 +1341,8 @@ def setUp(self): self.new_rule.type, ) - self.network_client.get_qos_minimum_bandwidth_rule = mock.Mock( - return_value=self.new_rule + self.network_client.get_qos_minimum_bandwidth_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -1415,8 +1405,8 @@ def setUp(self): self.new_rule.type, ) - self.network_client.get_qos_minimum_packet_rate_rule = mock.Mock( - return_value=self.new_rule + self.network_client.get_qos_minimum_packet_rate_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -1477,8 +1467,8 @@ def setUp(self): self.new_rule.type, ) - self.network_client.get_qos_dscp_marking_rule = mock.Mock( - return_value=self.new_rule + self.network_client.get_qos_dscp_marking_rule.return_value = ( + self.new_rule ) # Get the command object to test @@ -1543,8 +1533,8 @@ def setUp(self): self.new_rule.type, ) - self.network_client.get_qos_bandwidth_limit_rule = mock.Mock( - return_value=self.new_rule + self.network_client.get_qos_bandwidth_limit_rule.return_value = ( + self.new_rule ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py b/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py index c0aafa0bc..1c50b9e93 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule_type.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from openstackclient.network.v2 import network_qos_rule_type as _qos_rule_type from openstackclient.tests.unit.network.v2 import fakes as network_fakes @@ -35,9 +34,7 @@ class TestShowNetworkQosRuleType(TestNetworkQosRuleType): def setUp(self): super().setUp() - self.network_client.get_qos_rule_type = mock.Mock( - return_value=self.qos_rule_type - ) + self.network_client.get_qos_rule_type.return_value = self.qos_rule_type # Get the command object to test self.cmd = _qos_rule_type.ShowNetworkQosRuleType(self.app, None) @@ -84,9 +81,7 @@ class TestListNetworkQosRuleType(TestNetworkQosRuleType): def setUp(self): super().setUp() - self.network_client.qos_rule_types = mock.Mock( - return_value=self.qos_rule_types - ) + self.network_client.qos_rule_types.return_value = self.qos_rule_types # Get the command object to test self.cmd = _qos_rule_type.ListNetworkQosRuleType(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py index 816e93237..d3a719245 100644 --- a/openstackclient/tests/unit/network/v2/test_network_rbac.py +++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call import ddt @@ -72,27 +71,20 @@ def setUp(self): # Get the command object to test self.cmd = network_rbac.CreateNetworkRBAC(self.app, None) - self.network_client.create_rbac_policy = mock.Mock( - return_value=self.rbac_policy - ) - self.network_client.find_network = mock.Mock( - return_value=self.network_object - ) - self.network_client.find_qos_policy = mock.Mock( - return_value=self.qos_object - ) - self.network_client.find_security_group = mock.Mock( - return_value=self.sg_object - ) - self.network_client.find_address_scope = mock.Mock( - return_value=self.as_object - ) - self.network_client.find_subnet_pool = mock.Mock( - return_value=self.snp_object - ) - self.network_client.find_address_group = mock.Mock( - return_value=self.ag_object - ) + self.network_client.create_rbac_policy.return_value = self.rbac_policy + + self.network_client.find_network.return_value = self.network_object + + self.network_client.find_qos_policy.return_value = self.qos_object + + self.network_client.find_security_group.return_value = self.sg_object + + self.network_client.find_address_scope.return_value = self.as_object + + self.network_client.find_subnet_pool.return_value = self.snp_object + + self.network_client.find_address_group.return_value = self.ag_object + self.projects_mock.get.return_value = self.project def test_network_rbac_create_no_type(self): @@ -343,7 +335,7 @@ class TestDeleteNetworkRBAC(TestNetworkRBAC): def setUp(self): super().setUp() - self.network_client.delete_rbac_policy = mock.Mock(return_value=None) + self.network_client.delete_rbac_policy.return_value = None self.network_client.find_rbac_policy = network_fakes.get_network_rbacs( rbac_policies=self.rbac_policies ) @@ -400,9 +392,7 @@ def test_multi_network_policies_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self.rbac_policies[0], exceptions.CommandError] - self.network_client.find_rbac_policy = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_rbac_policy.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -462,9 +452,7 @@ def setUp(self): # Get the command object to test self.cmd = network_rbac.ListNetworkRBAC(self.app, None) - self.network_client.rbac_policies = mock.Mock( - return_value=self.rbac_policies - ) + self.network_client.rbac_policies.return_value = self.rbac_policies self.project = identity_fakes_v3.FakeProject.create_one_project() self.projects_mock.get.return_value = self.project @@ -564,10 +552,9 @@ def setUp(self): # Get the command object to test self.cmd = network_rbac.SetNetworkRBAC(self.app, None) - self.network_client.find_rbac_policy = mock.Mock( - return_value=self.rbac_policy - ) - self.network_client.update_rbac_policy = mock.Mock(return_value=None) + self.network_client.find_rbac_policy.return_value = self.rbac_policy + + self.network_client.update_rbac_policy.return_value = None self.projects_mock.get.return_value = self.project def test_network_rbac_set_nothing(self): @@ -639,9 +626,7 @@ def setUp(self): # Get the command object to test self.cmd = network_rbac.ShowNetworkRBAC(self.app, None) - self.network_client.find_rbac_policy = mock.Mock( - return_value=self.rbac_policy - ) + self.network_client.find_rbac_policy.return_value = self.rbac_policy def test_show_no_options(self): arglist = [] diff --git a/openstackclient/tests/unit/network/v2/test_network_segment.py b/openstackclient/tests/unit/network/v2/test_network_segment.py index 936a20aef..ab71c3254 100644 --- a/openstackclient/tests/unit/network/v2/test_network_segment.py +++ b/openstackclient/tests/unit/network/v2/test_network_segment.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -58,12 +57,9 @@ class TestCreateNetworkSegment(TestNetworkSegment): def setUp(self): super().setUp() - self.network_client.create_segment = mock.Mock( - return_value=self._network_segment - ) - self.network_client.find_network = mock.Mock( - return_value=self._network - ) + self.network_client.create_segment.return_value = self._network_segment + + self.network_client.find_network.return_value = self._network # Get the command object to test self.cmd = network_segment.CreateNetworkSegment(self.app, None) @@ -172,10 +168,8 @@ class TestDeleteNetworkSegment(TestNetworkSegment): def setUp(self): super().setUp() - self.network_client.delete_segment = mock.Mock(return_value=None) - self.network_client.find_segment = mock.Mock( - side_effect=self._network_segments - ) + self.network_client.delete_segment.return_value = None + self.network_client.find_segment.side_effect = self._network_segments # Get the command object to test self.cmd = network_segment.DeleteNetworkSegment(self.app, None) @@ -224,9 +218,7 @@ def test_delete_multiple_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._network_segments[0], exceptions.CommandError] - self.network_client.find_segment = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_segment.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -291,12 +283,9 @@ def setUp(self): # Get the command object to test self.cmd = network_segment.ListNetworkSegment(self.app, None) - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.segments = mock.Mock( - return_value=self._network_segments - ) + self.network_client.find_network.return_value = self._network + + self.network_client.segments.return_value = self._network_segments def test_list_no_option(self): arglist = [] @@ -352,12 +341,9 @@ class TestSetNetworkSegment(TestNetworkSegment): def setUp(self): super().setUp() - self.network_client.find_segment = mock.Mock( - return_value=self._network_segment - ) - self.network_client.update_segment = mock.Mock( - return_value=self._network_segment - ) + self.network_client.find_segment.return_value = self._network_segment + + self.network_client.update_segment.return_value = self._network_segment # Get the command object to test self.cmd = network_segment.SetNetworkSegment(self.app, None) @@ -432,9 +418,7 @@ class TestShowNetworkSegment(TestNetworkSegment): def setUp(self): super().setUp() - self.network_client.find_segment = mock.Mock( - return_value=self._network_segment - ) + self.network_client.find_segment.return_value = self._network_segment # Get the command object to test self.cmd = network_segment.ShowNetworkSegment(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_segment_range.py b/openstackclient/tests/unit/network/v2/test_network_segment_range.py index d5e406f39..9c9c900e3 100644 --- a/openstackclient/tests/unit/network/v2/test_network_segment_range.py +++ b/openstackclient/tests/unit/network/v2/test_network_segment_range.py @@ -78,8 +78,8 @@ class TestCreateNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.create_network_segment_range = mock.Mock( - return_value=self._network_segment_range + self.network_client.create_network_segment_range.return_value = ( + self._network_segment_range ) # Get the command object to test @@ -351,11 +351,10 @@ class TestDeleteNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.delete_network_segment_range = mock.Mock( - return_value=None - ) - self.network_client.find_network_segment_range = mock.Mock( - side_effect=self._network_segment_ranges + self.network_client.delete_network_segment_range.return_value = None + + self.network_client.find_network_segment_range.side_effect = ( + self._network_segment_ranges ) # Get the command object to test @@ -412,8 +411,8 @@ def test_delete_multiple_with_exception(self): self._network_segment_ranges[0], exceptions.CommandError, ] - self.network_client.find_network_segment_range = mock.Mock( - side_effect=find_mock_result + self.network_client.find_network_segment_range.side_effect = ( + find_mock_result ) try: @@ -493,8 +492,8 @@ class TestListNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.network_segment_ranges = mock.Mock( - return_value=self._network_segment_ranges + self.network_client.network_segment_ranges.return_value = ( + self._network_segment_ranges ) # Get the command object to test @@ -567,8 +566,8 @@ class TestSetNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.find_network_segment_range = mock.Mock( - return_value=self._network_segment_range + self.network_client.find_network_segment_range.return_value = ( + self._network_segment_range ) # Get the command object to test @@ -583,9 +582,10 @@ def test_set_no_options(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.update_network_segment_range = mock.Mock( - return_value=self._network_segment_range + self.network_client.update_network_segment_range.return_value = ( + self._network_segment_range ) + result = self.cmd.take_action(parsed_args) self.network_client.update_network_segment_range.assert_called_once_with( @@ -611,9 +611,10 @@ def test_set_all_options(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.update_network_segment_range = mock.Mock( - return_value=self._network_segment_range_updated + self.network_client.update_network_segment_range.return_value = ( + self._network_segment_range_updated ) + result = self.cmd.take_action(parsed_args) attrs = { @@ -662,8 +663,8 @@ class TestShowNetworkSegmentRange(TestNetworkSegmentRange): def setUp(self): super().setUp() - self.network_client.find_network_segment_range = mock.Mock( - return_value=self._network_segment_range + self.network_client.find_network_segment_range.return_value = ( + self._network_segment_range ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_network_service_provider.py b/openstackclient/tests/unit/network/v2/test_network_service_provider.py index 9f61bce59..f84bcd38d 100644 --- a/openstackclient/tests/unit/network/v2/test_network_service_provider.py +++ b/openstackclient/tests/unit/network/v2/test_network_service_provider.py @@ -13,7 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from openstackclient.network.v2 import ( network_service_provider as service_provider, @@ -52,9 +51,7 @@ class TestListNetworkServiceProvider(TestNetworkServiceProvider): def setUp(self): super().setUp() - self.network_client.service_providers = mock.Mock( - return_value=self.provider_list - ) + self.network_client.service_providers.return_value = self.provider_list self.cmd = service_provider.ListNetworkServiceProvider(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_trunk.py b/openstackclient/tests/unit/network/v2/test_network_trunk.py index 180e80de5..3b6c364d3 100644 --- a/openstackclient/tests/unit/network/v2/test_network_trunk.py +++ b/openstackclient/tests/unit/network/v2/test_network_trunk.py @@ -11,7 +11,6 @@ # under the License. import copy -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -84,12 +83,12 @@ class TestCreateNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.create_trunk = mock.Mock( - return_value=self.new_trunk - ) - self.network_client.find_port = mock.Mock( - side_effect=[self.parent_port, self.sub_port] - ) + self.network_client.create_trunk.return_value = self.new_trunk + + self.network_client.find_port.side_effect = [ + self.parent_port, + self.sub_port, + ] # Get the command object to test self.cmd = network_trunk.CreateNetworkTrunk(self.app, None) @@ -313,13 +312,16 @@ class TestDeleteNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.find_trunk = mock.Mock( - side_effect=[self.new_trunks[0], self.new_trunks[1]] - ) - self.network_client.delete_trunk = mock.Mock(return_value=None) - self.network_client.find_port = mock.Mock( - side_effect=[self.parent_port, self.sub_port] - ) + self.network_client.find_trunk.side_effect = [ + self.new_trunks[0], + self.new_trunks[1], + ] + + self.network_client.delete_trunk.return_value = None + self.network_client.find_port.side_effect = [ + self.parent_port, + self.sub_port, + ] self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -371,9 +373,11 @@ def test_delete_trunk_multiple_with_exception(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.find_trunk = mock.Mock( - side_effect=[self.new_trunks[0], exceptions.CommandError] - ) + self.network_client.find_trunk.side_effect = [ + self.new_trunks[0], + exceptions.CommandError, + ] + with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) self.assertEqual('1 of 2 trunks failed to delete.', str(e)) @@ -412,8 +416,8 @@ class TestShowNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.find_trunk = mock.Mock(return_value=self.new_trunk) - self.network_client.get_trunk = mock.Mock(return_value=self.new_trunk) + self.network_client.find_trunk.return_value = self.new_trunk + self.network_client.get_trunk.return_value = self.new_trunk self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -485,7 +489,7 @@ class TestListNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.trunks = mock.Mock(return_value=self.new_trunks) + self.network_client.trunks.return_value = self.new_trunks self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -564,14 +568,14 @@ class TestSetNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.update_trunk = mock.Mock(return_value=self._trunk) - self.network_client.add_trunk_subports = mock.Mock( - return_value=self._trunk - ) - self.network_client.find_trunk = mock.Mock(return_value=self._trunk) - self.network_client.find_port = mock.Mock( - side_effect=[self.sub_port, self.sub_port] - ) + self.network_client.update_trunk.return_value = self._trunk + self.network_client.add_trunk_subports.return_value = self._trunk + + self.network_client.find_trunk.return_value = self._trunk + self.network_client.find_port.side_effect = [ + self.sub_port, + self.sub_port, + ] self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -763,9 +767,8 @@ def test_set_trunk_attrs_with_exception(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.update_trunk = mock.Mock( - side_effect=exceptions.CommandError - ) + self.network_client.update_trunk.side_effect = exceptions.CommandError + with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) self.assertEqual( @@ -790,12 +793,12 @@ def test_set_trunk_add_subport_with_exception(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.network_client.add_trunk_subports = mock.Mock( - side_effect=exceptions.CommandError - ) - self.network_client.find_port = mock.Mock( - return_value={'id': 'invalid_subport'} + self.network_client.add_trunk_subports.side_effect = ( + exceptions.CommandError ) + + self.network_client.find_port.side_effect = [{'id': 'invalid_subport'}] + with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) self.assertEqual( @@ -832,10 +835,10 @@ class TestListNetworkSubport(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.find_trunk = mock.Mock(return_value=self._trunk) - self.network_client.get_trunk_subports = mock.Mock( - return_value={network_trunk.SUB_PORTS: self._subports} - ) + self.network_client.find_trunk.return_value = self._trunk + self.network_client.get_trunk_subports.return_value = { + network_trunk.SUB_PORTS: self._subports + } # Get the command object to test self.cmd = network_trunk.ListNetworkSubport(self.app, None) @@ -902,13 +905,13 @@ class TestUnsetNetworkTrunk(TestNetworkTrunk): def setUp(self): super().setUp() - self.network_client.find_trunk = mock.Mock(return_value=self._trunk) - self.network_client.find_port = mock.Mock( - side_effect=[self.sub_port, self.sub_port] - ) - self.network_client.delete_trunk_subports = mock.Mock( - return_value=None - ) + self.network_client.find_trunk.return_value = self._trunk + self.network_client.find_port.side_effect = [ + self.sub_port, + self.sub_port, + ] + + self.network_client.delete_trunk_subports.return_value = None # Get the command object to test self.cmd = network_trunk.UnsetNetworkTrunk(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 0611be478..11a8711c7 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -136,19 +136,18 @@ class TestCreatePort(TestPort): def setUp(self): super().setUp() - self.network_client.create_port = mock.Mock(return_value=self._port) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_port.return_value = self._port + self.network_client.set_tags.return_value = None fake_net = network_fakes.create_one_network( { 'id': self._port.network_id, } ) - self.network_client.find_network = mock.Mock(return_value=fake_net) + self.network_client.find_network.return_value = fake_net self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) - self.network_client.find_extension = mock.Mock(return_value=[]) + self.network_client.find_subnet.return_value = self.fake_subnet + + self.network_client.find_extension.return_value = [] # Get the command object to test self.cmd = port.CreatePort(self.app, None) @@ -321,9 +320,8 @@ def test_create_json_binding_profile(self): def test_create_with_security_group(self): secgroup = network_fakes.create_one_security_group() - self.network_client.find_security_group = mock.Mock( - return_value=secgroup - ) + self.network_client.find_security_group.return_value = secgroup + arglist = [ '--network', self._port.network_id, @@ -393,9 +391,8 @@ def test_create_port_with_dns_name(self): def test_create_with_security_groups(self): sg_1 = network_fakes.create_one_security_group() sg_2 = network_fakes.create_one_security_group() - self.network_client.find_security_group = mock.Mock( - side_effect=[sg_1, sg_2] - ) + self.network_client.find_security_group.side_effect = [sg_1, sg_2] + arglist = [ '--network', self._port.network_id, @@ -583,9 +580,8 @@ def test_create_port_with_allowed_address_pair(self): def test_create_port_with_qos(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + arglist = [ '--network', self._port.network_id, @@ -701,9 +697,7 @@ def _test_create_with_tag(self, add_tags=True, add_tags_in_post=True): else: verifylist.append(('no_tag', True)) - self.network_client.find_extension = mock.Mock( - return_value=add_tags_in_post - ) + self.network_client.find_extension.return_value = add_tags_in_post parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -1170,7 +1164,7 @@ class TestDeletePort(TestPort): def setUp(self): super().setUp() - self.network_client.delete_port = mock.Mock(return_value=None) + self.network_client.delete_port.return_value = None self.network_client.find_port = network_fakes.get_ports( ports=self._ports ) @@ -1223,7 +1217,7 @@ def test_multi_ports_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._ports[0], exceptions.CommandError] - self.network_client.find_port = mock.Mock(side_effect=find_mock_result) + self.network_client.find_port.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -1324,7 +1318,7 @@ class TestListPort(compute_fakes.FakeClientMixin, TestPort): def setUp(self): super().setUp() - self.network_client.ports = mock.Mock(return_value=self._ports) + self.network_client.ports.return_value = self._ports fake_router = network_fakes.create_one_router( { 'id': 'fake-router-id', @@ -1335,8 +1329,8 @@ def setUp(self): 'id': 'fake-network-id', } ) - self.network_client.find_router = mock.Mock(return_value=fake_router) - self.network_client.find_network = mock.Mock(return_value=fake_network) + self.network_client.find_router.return_value = fake_router + self.network_client.find_network.return_value = fake_network # Get the command object to test self.cmd = port.ListPort(self.app, None) @@ -1550,9 +1544,8 @@ def test_port_list_fixed_ip_opt_subnet_id(self): self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet( {'id': subnet_id} ) - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) + self.network_client.find_subnet.return_value = self.fake_subnet + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -1579,9 +1572,8 @@ def test_port_list_fixed_ip_opts(self): self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet( {'id': subnet_id} ) - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) + self.network_client.find_subnet.return_value = self.fake_subnet + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -1616,9 +1608,8 @@ def test_port_list_fixed_ips(self): 'fields': LIST_FIELDS_TO_RETRIEVE, } ) - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) + self.network_client.find_subnet.return_value = self.fake_subnet + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -1825,12 +1816,11 @@ class TestSetPort(TestPort): def setUp(self): super().setUp() self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) - self.network_client.find_port = mock.Mock(return_value=self._port) - self.network_client.update_port = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_subnet.return_value = self.fake_subnet + + self.network_client.find_port.return_value = self._port + self.network_client.update_port.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = port.SetPort(self.app, None) @@ -1853,7 +1843,7 @@ def test_set_port_fixed_ip(self): _testport = network_fakes.create_one_port( {'fixed_ips': [{'ip_address': '0.0.0.1'}]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--fixed-ip', 'ip-address=10.0.0.12', @@ -1881,7 +1871,7 @@ def test_set_port_fixed_ip_clear(self): _testport = network_fakes.create_one_port( {'fixed_ips': [{'ip_address': '0.0.0.1'}]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--fixed-ip', 'ip-address=10.0.0.12', @@ -1931,7 +1921,7 @@ def test_set_port_overwrite_binding_profile(self): _testport = network_fakes.create_one_port( {'binding_profile': {'lok_i': 'visi_on'}} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--binding-profile', 'lok_i=than_os', @@ -1956,7 +1946,7 @@ def test_overwrite_mac_address(self): _testport = network_fakes.create_one_port( {'mac_address': '11:22:33:44:55:66'} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--mac-address', '66:55:44:33:22:11', @@ -2097,7 +2087,7 @@ def test_set_port_mixed_binding_profile(self): def test_set_port_security_group(self): sg = network_fakes.create_one_security_group() - self.network_client.find_security_group = mock.Mock(return_value=sg) + self.network_client.find_security_group.return_value = sg arglist = [ '--security-group', sg.id, @@ -2122,13 +2112,12 @@ def test_set_port_security_group_append(self): sg_1 = network_fakes.create_one_security_group() sg_2 = network_fakes.create_one_security_group() sg_3 = network_fakes.create_one_security_group() - self.network_client.find_security_group = mock.Mock( - side_effect=[sg_2, sg_3] - ) + self.network_client.find_security_group.side_effect = [sg_2, sg_3] + _testport = network_fakes.create_one_port( {'security_group_ids': [sg_1.id]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--security-group', sg_2.id, @@ -2177,8 +2166,8 @@ def test_set_port_security_group_replace(self): _testport = network_fakes.create_one_port( {'security_group_ids': [sg1.id]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) - self.network_client.find_security_group = mock.Mock(return_value=sg2) + self.network_client.find_port.return_value = _testport + self.network_client.find_security_group.return_value = sg2 arglist = [ '--security-group', sg2.id, @@ -2226,7 +2215,7 @@ def test_set_port_append_allowed_address_pair(self): _testport = network_fakes.create_one_port( {'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--allowed-address', 'ip-address=192.168.1.45', @@ -2255,7 +2244,7 @@ def test_set_port_overwrite_allowed_address_pair(self): _testport = network_fakes.create_one_port( {'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]} ) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--allowed-address', 'ip-address=192.168.1.45', @@ -2372,11 +2361,10 @@ def test_set_port_security_disabled(self): def test_set_port_with_qos(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + _testport = network_fakes.create_one_port({'qos_policy_id': None}) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--qos-policy', qos_policy.id, @@ -2400,7 +2388,7 @@ def test_set_port_with_qos(self): def test_set_port_data_plane_status(self): _testport = network_fakes.create_one_port({'data_plane_status': None}) - self.network_client.find_port = mock.Mock(return_value=_testport) + self.network_client.find_port.return_value = _testport arglist = [ '--data-plane-status', 'ACTIVE', @@ -2546,10 +2534,12 @@ def test_set_hints_invalid_value(self): def test_set_hints_valid_alias_value(self): testport = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=testport) - self.network_client.find_extension = mock.Mock( - return_value=['port-hints', 'port-hint-ovs-tx-steering'] - ) + self.network_client.find_port.return_value = testport + self.network_client.find_extension.return_value = [ + 'port-hints', + 'port-hint-ovs-tx-steering', + ] + arglist = [ '--hint', 'ovs-tx-steering=hash', @@ -2574,10 +2564,12 @@ def test_set_hints_valid_alias_value(self): def test_set_hints_valid_json(self): testport = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=testport) - self.network_client.find_extension = mock.Mock( - return_value=['port-hints', 'port-hint-ovs-tx-steering'] - ) + self.network_client.find_port.return_value = testport + self.network_client.find_extension.return_value = [ + 'port-hints', + 'port-hint-ovs-tx-steering', + ] + arglist = [ '--hint', '{"openvswitch": {"other_config": {"tx-steering": "hash"}}}', @@ -2670,7 +2662,7 @@ class TestShowPort(TestPort): def setUp(self): super().setUp() - self.network_client.find_port = mock.Mock(return_value=self._port) + self.network_client.find_port.return_value = self._port # Get the command object to test self.cmd = port.ShowPort(self.app, None) @@ -2731,12 +2723,11 @@ def setUp(self): self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet( {'id': '042eb10a-3a18-4658-ab-cf47c8d03152'} ) - self.network_client.find_subnet = mock.Mock( - return_value=self.fake_subnet - ) - self.network_client.find_port = mock.Mock(return_value=self._testport) - self.network_client.update_port = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_subnet.return_value = self.fake_subnet + + self.network_client.find_port.return_value = self._testport + self.network_client.update_port.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = port.UnsetPort(self.app, None) @@ -2826,10 +2817,9 @@ def test_unset_security_group(self): _fake_port = network_fakes.create_one_port( {'security_group_ids': [_fake_sg1.id, _fake_sg2.id]} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) - self.network_client.find_security_group = mock.Mock( - return_value=_fake_sg2 - ) + self.network_client.find_port.return_value = _fake_port + self.network_client.find_security_group.return_value = _fake_sg2 + arglist = [ '--security-group', _fake_sg2.id, @@ -2854,9 +2844,8 @@ def test_unset_port_security_group_not_existent(self): _fake_port = network_fakes.create_one_port( {'security_group_ids': [_fake_sg1.id]} ) - self.network_client.find_security_group = mock.Mock( - return_value=_fake_sg2 - ) + self.network_client.find_security_group.return_value = _fake_sg2 + arglist = [ '--security-group', _fake_sg2.id, @@ -2875,7 +2864,7 @@ def test_unset_port_allowed_address_pair(self): _fake_port = network_fakes.create_one_port( {'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) + self.network_client.find_port.return_value = _fake_port arglist = [ '--allowed-address', 'ip-address=192.168.1.123', @@ -2901,7 +2890,7 @@ def test_unset_port_allowed_address_pair_not_existent(self): _fake_port = network_fakes.create_one_port( {'allowed_address_pairs': [{'ip_address': '192.168.1.123'}]} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) + self.network_client.find_port.return_value = _fake_port arglist = [ '--allowed-address', 'ip-address=192.168.1.45', @@ -2920,7 +2909,7 @@ def test_unset_port_data_plane_status(self): _fake_port = network_fakes.create_one_port( {'data_plane_status': 'ACTIVE'} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) + self.network_client.find_port.return_value = _fake_port arglist = [ '--data-plane-status', _fake_port.name, @@ -2973,7 +2962,7 @@ def test_unset_numa_affinity_policy(self): _fake_port = network_fakes.create_one_port( {'numa_affinity_policy': 'required'} ) - self.network_client.find_port = mock.Mock(return_value=_fake_port) + self.network_client.find_port.return_value = _fake_port arglist = [ '--numa-policy', _fake_port.name, @@ -2997,7 +2986,7 @@ def test_unset_numa_affinity_policy(self): def test_unset_hints(self): testport = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=testport) + self.network_client.find_port.return_value = testport arglist = [ '--hints', testport.name, @@ -3017,7 +3006,7 @@ def test_unset_hints(self): def test_unset_device(self): testport = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=testport) + self.network_client.find_port.return_value = testport arglist = [ '--device', testport.name, @@ -3037,7 +3026,7 @@ def test_unset_device(self): def test_unset_device_owner(self): testport = network_fakes.create_one_port() - self.network_client.find_port = mock.Mock(return_value=testport) + self.network_client.find_port.return_value = testport arglist = [ '--device-owner', testport.name, diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 353658330..221fe67a9 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -39,8 +39,8 @@ class TestAddPortToRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_port = mock.Mock(return_value=self._port) + self.network_client.find_router.return_value = self._router + self.network_client.find_port.return_value = self._port self.cmd = router.AddPortToRouter(self.app, None) @@ -87,8 +87,8 @@ class TestAddSubnetToRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) + self.network_client.find_router.return_value = self._router + self.network_client.find_subnet.return_value = self._subnet self.cmd = router.AddSubnetToRouter(self.app, None) @@ -172,12 +172,11 @@ class TestCreateRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.create_router = mock.Mock( - return_value=self.new_router - ) - self.network_client.set_tags = mock.Mock(return_value=None) - self.network_client.find_extension = mock.Mock( - side_effect=lambda name: self._extensions.get(name) + self.network_client.create_router.return_value = self.new_router + + self.network_client.set_tags.return_value = None + self.network_client.find_extension.side_effect = ( + lambda name: self._extensions.get(name) ) # Get the command object to test self.cmd = router.CreateRouter(self.app, None) @@ -222,8 +221,8 @@ def test_create_default_options(self): def test_create_with_gateway(self): _network = network_fakes.create_one_network() _subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_network = mock.Mock(return_value=_network) - self.network_client.find_subnet = mock.Mock(return_value=_subnet) + self.network_client.find_network.return_value = _network + self.network_client.find_subnet.return_value = _subnet arglist = [ self.new_router.name, '--external-gateway', @@ -392,7 +391,7 @@ def test_create_with_no_tag(self): def test_create_with_flavor_id_id(self): _flavor = network_fakes.create_one_network_flavor() - self.network_client.find_flavor = mock.Mock(return_value=_flavor) + self.network_client.find_flavor.return_value = _flavor arglist = [ self.new_router.name, '--flavor-id', @@ -419,7 +418,7 @@ def test_create_with_flavor_id_id(self): def test_create_with_flavor_id_name(self): _flavor = network_fakes.create_one_network_flavor() - self.network_client.find_flavor = mock.Mock(return_value=_flavor) + self.network_client.find_flavor.return_value = _flavor arglist = [self.new_router.name, '--flavor-id', _flavor.name] verifylist = [ ('name', self.new_router.name), @@ -442,7 +441,7 @@ def test_create_with_flavor_id_name(self): def test_create_with_flavor_id(self): _flavor = network_fakes.create_one_network_flavor() - self.network_client.find_flavor = mock.Mock(return_value=_flavor) + self.network_client.find_flavor.return_value = _flavor arglist = [ self.new_router.name, '--flavor', @@ -469,7 +468,7 @@ def test_create_with_flavor_id(self): def test_create_with_flavor_name(self): _flavor = network_fakes.create_one_network_flavor() - self.network_client.find_flavor = mock.Mock(return_value=_flavor) + self.network_client.find_flavor.return_value = _flavor arglist = [self.new_router.name, '--flavor', _flavor.name] verifylist = [ ('name', self.new_router.name), @@ -491,11 +490,12 @@ def test_create_with_flavor_name(self): self.assertCountEqual(self.data, data) def test_create_with_enable_default_route_bfd(self): - self.network_client.find_extension = mock.Mock( - return_value=network_fakes.create_one_extension( + self._extensions = { + 'external-gateway-multihoming': network_fakes.create_one_extension( attrs={'name': 'external-gateway-multihoming'} - ) - ) + ), + } + arglist = [self.new_router.name, '--enable-default-route-bfd'] verifylist = [ ('name', self.new_router.name), @@ -525,11 +525,12 @@ def test_create_with_enable_default_route_bfd_no_extension(self): ) def test_create_with_enable_default_route_ecmp(self): - self.network_client.find_extension = mock.Mock( - return_value=network_fakes.create_one_extension( + self._extensions = { + 'external-gateway-multihoming': network_fakes.create_one_extension( attrs={'name': 'external-gateway-multihoming'} - ) - ) + ), + } + arglist = [self.new_router.name, '--enable-default-route-ecmp'] verifylist = [ ('name', self.new_router.name), @@ -560,11 +561,10 @@ def test_create_with_enable_default_route_ecmp_no_extension(self): def test_create_with_qos_policy(self): _network = network_fakes.create_one_network() - self.network_client.find_network = mock.Mock(return_value=_network) + self.network_client.find_network.return_value = _network _qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=_qos_policy - ) + self.network_client.find_qos_policy.return_value = _qos_policy + arglist = [ self.new_router.name, '--external-gateway', @@ -595,9 +595,8 @@ def test_create_with_qos_policy(self): def test_create_with_qos_policy_no_external_gateway(self): _qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=_qos_policy - ) + self.network_client.find_qos_policy.return_value = _qos_policy + arglist = [ self.new_router.name, '--qos-policy', @@ -625,7 +624,7 @@ class TestDeleteRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.delete_router = mock.Mock(return_value=None) + self.network_client.delete_router.return_value = None self.network_client.find_router = network_fakes.get_routers( self._routers @@ -679,9 +678,7 @@ def test_multi_routers_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._routers[0], exceptions.CommandError] - self.network_client.find_router = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_router.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -786,21 +783,16 @@ def setUp(self): # Get the command object to test self.cmd = router.ListRouter(self.app, None) - self.network_client.agent_hosted_routers = mock.Mock( - return_value=self.routers - ) - self.network_client.routers = mock.Mock(return_value=self.routers) - self.network_client.find_extension = mock.Mock( - return_value=self.extensions - ) - self.network_client.find_router = mock.Mock( - return_value=self.routers[0] - ) + self.network_client.agent_hosted_routers.return_value = self.routers + + self.network_client.routers.return_value = self.routers + self.network_client.find_extension.return_value = self.extensions + + self.network_client.find_router.return_value = self.routers[0] + self._testagent = network_fakes.create_one_network_agent() - self.network_client.get_agent = mock.Mock(return_value=self._testagent) - self.network_client.get_router = mock.Mock( - return_value=self.routers[0] - ) + self.network_client.get_agent.return_value = self._testagent + self.network_client.get_router.return_value = self.routers[0] def test_router_list_no_options(self): arglist = [] @@ -865,7 +857,7 @@ def test_router_list_long_no_az(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) # to mock, that no availability zone - self.network_client.find_extension = mock.Mock(return_value=None) + self.network_client.find_extension.return_value = None # In base command class Lister in cliff, abstract method take_action() # returns a tuple containing the column names and an iterable @@ -1044,8 +1036,8 @@ class TestRemovePortFromRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_port = mock.Mock(return_value=self._port) + self.network_client.find_router.return_value = self._router + self.network_client.find_port.return_value = self._port self.cmd = router.RemovePortFromRouter(self.app, None) @@ -1089,8 +1081,8 @@ class TestRemoveSubnetFromRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) + self.network_client.find_router.return_value = self._router + self.network_client.find_subnet.return_value = self._subnet self.cmd = router.RemoveSubnetFromRouter(self.app, None) @@ -1129,11 +1121,12 @@ class TestAddExtraRoutesToRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.add_extra_routes_to_router = mock.Mock( - return_value=self._router + self.network_client.add_extra_routes_to_router.return_value = ( + self._router ) + self.cmd = router.AddExtraRoutesToRouter(self.app, None) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.find_router.return_value = self._router def test_add_no_extra_route(self): arglist = [ @@ -1218,11 +1211,12 @@ class TestRemoveExtraRoutesFromRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.remove_extra_routes_from_router = mock.Mock( - return_value=self._router + self.network_client.remove_extra_routes_from_router.return_value = ( + self._router ) + self.cmd = router.RemoveExtraRoutesFromRouter(self.app, None) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.find_router.return_value = self._router def test_remove_no_extra_route(self): arglist = [ @@ -1316,15 +1310,14 @@ class TestSetRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.update_router = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) - self.network_client.find_extension = mock.Mock( - side_effect=lambda name: self._extensions.get(name) + self.network_client.update_router.return_value = None + self.network_client.set_tags.return_value = None + self.network_client.find_router.return_value = self._router + self.network_client.find_network.return_value = self._network + + self.network_client.find_subnet.return_value = self._subnet + self.network_client.find_extension.side_effect = ( + lambda name: self._extensions.get(name) ) # Get the command object to test self.cmd = router.SetRouter(self.app, None) @@ -1459,7 +1452,7 @@ def test_set_route_overwrite_route(self): _testrouter = network_fakes.create_one_router( {'routes': [{"destination": "10.0.0.2", "nexthop": "1.1.1.1"}]} ) - self.network_client.find_router = mock.Mock(return_value=_testrouter) + self.network_client.find_router.return_value = _testrouter arglist = [ _testrouter.name, '--route', @@ -1667,9 +1660,8 @@ def test_set_with_no_tag(self): def test_set_gateway_ip_qos(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + arglist = [ "--external-gateway", self._network.id, @@ -1724,9 +1716,8 @@ def test_unset_gateway_ip_qos(self): def test_set_unset_gateway_ip_qos(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + arglist = [ "--external-gateway", self._network.id, @@ -1752,11 +1743,10 @@ def test_set_unset_gateway_ip_qos(self): def test_set_gateway_ip_qos_no_gateway(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + router = network_fakes.create_one_router() - self.network_client.find_router = mock.Mock(return_value=router) + self.network_client.find_router.return_value = router arglist = [ "--qos-policy", qos_policy.id, @@ -1774,11 +1764,10 @@ def test_set_gateway_ip_qos_no_gateway(self): def test_unset_gateway_ip_qos_no_gateway(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + router = network_fakes.create_one_router() - self.network_client.find_router = mock.Mock(return_value=router) + self.network_client.find_router.return_value = router arglist = [ "--no-qos-policy", router.id, @@ -1857,8 +1846,8 @@ class TestShowRouter(TestRouter): def setUp(self): super().setUp() - self.network_client.find_router = mock.Mock(return_value=self._router) - self.network_client.ports = mock.Mock(return_value=[self._port]) + self.network_client.find_router.return_value = self._router + self.network_client.ports.return_value = [self._port] # Get the command object to test self.cmd = router.ShowRouter(self.app, None) @@ -1961,18 +1950,16 @@ def setUp(self): } ) self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_router = mock.Mock( - return_value=self._testrouter - ) - self.network_client.update_router = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_router.return_value = self._testrouter + + self.network_client.update_router.return_value = None + self.network_client.set_tags.return_value = None self._extensions = {'fake': network_fakes.create_one_extension()} - self.network_client.find_extension = mock.Mock( - side_effect=lambda name: self._extensions.get(name) - ) - self.network_client.remove_external_gateways = mock.Mock( - return_value=None + self.network_client.find_extension.side_effect = ( + lambda name: self._extensions.get(name) ) + self.network_client.remove_external_gateways.return_value = None + # Get the command object to test self.cmd = router.UnsetRouter(self.app, None) @@ -2109,11 +2096,10 @@ def test_unset_router_qos_policy(self): def test_unset_gateway_ip_qos_no_network(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + router = network_fakes.create_one_router() - self.network_client.find_router = mock.Mock(return_value=router) + self.network_client.find_router.return_value = router arglist = [ "--qos-policy", router.id, @@ -2129,13 +2115,12 @@ def test_unset_gateway_ip_qos_no_network(self): def test_unset_gateway_ip_qos_no_qos(self): qos_policy = network_fakes.create_one_qos_policy() - self.network_client.find_qos_policy = mock.Mock( - return_value=qos_policy - ) + self.network_client.find_qos_policy.return_value = qos_policy + router = network_fakes.create_one_router( {"external_gateway_info": {"network_id": "fake-id"}} ) - self.network_client.find_router = mock.Mock(return_value=router) + self.network_client.find_router.return_value = router arglist = [ "--qos-policy", router.id, @@ -2172,10 +2157,10 @@ def setUp(self): attrs={'name': 'external-gateway-multihoming'} ) } - self.network_client.find_extension = mock.Mock( - side_effect=lambda name: self._extensions.get(name) + self.network_client.find_extension.side_effect = ( + lambda name: self._extensions.get(name) ) - self.network_client.find_router = mock.Mock(return_value=self._router) + self.network_client.find_router.return_value = self._router def _find_network(name_or_id, ignore_missing): for network in self._networks: @@ -2185,15 +2170,12 @@ def _find_network(name_or_id, ignore_missing): return None raise Exception('Test resource not found') - self.network_client.find_network = mock.Mock(side_effect=_find_network) + self.network_client.find_network.side_effect = _find_network - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) - self.network_client.add_external_gateways = mock.Mock( - return_value=None - ) - self.network_client.remove_external_gateways = mock.Mock( - return_value=None - ) + self.network_client.find_subnet.return_value = self._subnet + self.network_client.add_external_gateways.return_value = None + + self.network_client.remove_external_gateways.return_value = None class TestCreateMultipleGateways(TestGatewayOps): @@ -2223,13 +2205,10 @@ def setUp(self): self._second_network = network_fakes.create_one_network() self._networks.append(self._second_network) - self.network_client.create_router = mock.Mock( - return_value=self._router - ) - self.network_client.update_router = mock.Mock(return_value=None) - self.network_client.update_external_gateways = mock.Mock( - return_value=None - ) + self.network_client.create_router.return_value = self._router + + self.network_client.update_router.return_value = None + self.network_client.update_external_gateways.return_value = None self._data = ( router.AdminStateColumn(self._router.is_admin_state_up), @@ -2353,10 +2332,9 @@ def setUp(self): self._second_network = network_fakes.create_one_network() self._networks.append(self._second_network) - self.network_client.update_router = mock.Mock(return_value=None) - self.network_client.update_external_gateways = mock.Mock( - return_value=None - ) + self.network_client.update_router.return_value = None + self.network_client.update_external_gateways.return_value = None + self.cmd = router.SetRouter(self.app, None) def test_update_one_gateway(self): diff --git a/openstackclient/tests/unit/network/v2/test_security_group_network.py b/openstackclient/tests/unit/network/v2/test_security_group_network.py index 9bf1f672f..c5ccf628e 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -69,13 +68,13 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): def setUp(self): super().setUp() - self.network_client.create_security_group = mock.Mock( - return_value=self._security_group + self.network_client.create_security_group.return_value = ( + self._security_group ) self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = security_group.CreateSecurityGroup(self.app, None) @@ -185,9 +184,7 @@ class TestDeleteSecurityGroupNetwork(TestSecurityGroupNetwork): def setUp(self): super().setUp() - self.network_client.delete_security_group = mock.Mock( - return_value=None - ) + self.network_client.delete_security_group.return_value = None self.network_client.find_security_group = ( network_fakes.get_security_groups(self._security_groups) @@ -245,9 +242,7 @@ def test_multi_security_groups_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._security_groups[0], exceptions.CommandError] - self.network_client.find_security_group = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_security_group.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -295,8 +290,8 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork): def setUp(self): super().setUp() - self.network_client.security_groups = mock.Mock( - return_value=self._security_groups + self.network_client.security_groups.return_value = ( + self._security_groups ) # Get the command object to test @@ -423,14 +418,13 @@ class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork): def setUp(self): super().setUp() - self.network_client.update_security_group = mock.Mock( - return_value=None - ) + self.network_client.update_security_group.return_value = None - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) - self.network_client.set_tags = mock.Mock(return_value=None) + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = security_group.SetSecurityGroup(self.app, None) @@ -557,8 +551,8 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork): def setUp(self): super().setUp() - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) # Get the command object to test @@ -596,14 +590,13 @@ class TestUnsetSecurityGroupNetwork(TestSecurityGroupNetwork): def setUp(self): super().setUp() - self.network_client.update_security_group = mock.Mock( - return_value=None - ) + self.network_client.update_security_group.return_value = None - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) - self.network_client.set_tags = mock.Mock(return_value=None) + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = security_group.UnsetSecurityGroup(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py index d5630c368..920e89141 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib import exceptions @@ -69,9 +68,10 @@ def _setup_security_group_rule(self, attrs=None): self._security_group_rule = ( network_fakes.create_one_security_group_rule(attrs) ) - self.network_client.create_security_group_rule = mock.Mock( - return_value=self._security_group_rule + self.network_client.create_security_group_rule.return_value = ( + self._security_group_rule ) + self.expected_data = ( self._security_group_rule.created_at, self._security_group_rule.description, @@ -93,12 +93,12 @@ def _setup_security_group_rule(self, attrs=None): def setUp(self): super().setUp() - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) - self.network_client.find_address_group = mock.Mock( - return_value=self._address_group + self.network_client.find_address_group.return_value = ( + self._address_group ) self.projects_mock.get.return_value = self.project @@ -970,9 +970,7 @@ class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): def setUp(self): super().setUp() - self.network_client.delete_security_group_rule = mock.Mock( - return_value=None - ) + self.network_client.delete_security_group_rule.return_value = None self.network_client.find_security_group_rule = ( network_fakes.get_security_group_rules(self._security_group_rules) @@ -1030,8 +1028,8 @@ def test_multi_security_group_rules_delete_with_exception(self): self._security_group_rules[0], exceptions.CommandError, ] - self.network_client.find_security_group_rule = mock.Mock( - side_effect=find_mock_result + self.network_client.find_security_group_rule.side_effect = ( + find_mock_result ) try: @@ -1134,11 +1132,12 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): def setUp(self): super().setUp() - self.network_client.find_security_group = mock.Mock( - return_value=self._security_group + self.network_client.find_security_group.return_value = ( + self._security_group ) - self.network_client.security_group_rules = mock.Mock( - return_value=self._security_group_rules + + self.network_client.security_group_rules.return_value = ( + self._security_group_rules ) # Get the command object to test @@ -1344,8 +1343,8 @@ class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): def setUp(self): super().setUp() - self.network_client.find_security_group_rule = mock.Mock( - return_value=self._security_group_rule + self.network_client.find_security_group_rule.return_value = ( + self._security_group_rule ) # Get the command object to test diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py index 8b8207411..e59168e51 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet.py +++ b/openstackclient/tests/unit/network/v2/test_subnet.py @@ -11,7 +11,6 @@ # under the License. # -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -266,19 +265,14 @@ def setUp(self): self.domains_mock.get.return_value = self.domain # Mock SDK calls for all tests. - self.network_client.create_subnet = mock.Mock( - return_value=self._subnet - ) - self.network_client.set_tags = mock.Mock(return_value=None) - self.network_client.find_network = mock.Mock( - return_value=self._network - ) - self.network_client.find_segment = mock.Mock( - return_value=self._network_segment - ) - self.network_client.find_subnet_pool = mock.Mock( - return_value=self._subnet_pool - ) + self.network_client.create_subnet.return_value = self._subnet + + self.network_client.set_tags.return_value = None + self.network_client.find_network.return_value = self._network + + self.network_client.find_segment.return_value = self._network_segment + + self.network_client.find_subnet_pool.return_value = self._subnet_pool def test_create_no_options(self): arglist = [] @@ -333,7 +327,7 @@ def test_create_default_options(self): def test_create_from_subnet_pool_options(self): # Mock SDK calls for this test. self.network_client.create_subnet.return_value = self._subnet_from_pool - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.set_tags.return_value = None self._network.id = self._subnet_from_pool.network_id arglist = [ @@ -721,7 +715,7 @@ class TestDeleteSubnet(TestSubnet): def setUp(self): super().setUp() - self.network_client.delete_subnet = mock.Mock(return_value=None) + self.network_client.delete_subnet.return_value = None self.network_client.find_subnet = network_fakes.FakeSubnet.get_subnets( self._subnets @@ -775,9 +769,7 @@ def test_multi_subnets_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._subnets[0], exceptions.CommandError] - self.network_client.find_subnet = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_subnet.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -855,7 +847,7 @@ def setUp(self): # Get the command object to test self.cmd = subnet_v2.ListSubnet(self.app, None) - self.network_client.subnets = mock.Mock(return_value=self._subnet) + self.network_client.subnets.return_value = self._subnet def test_subnet_list_no_options(self): arglist = [] @@ -1019,7 +1011,7 @@ def test_subnet_list_project_domain(self): def test_subnet_list_network(self): network = network_fakes.create_one_network() - self.network_client.find_network = mock.Mock(return_value=network) + self.network_client.find_network.return_value = network arglist = [ '--network', network.id, @@ -1038,7 +1030,7 @@ def test_subnet_list_network(self): def test_subnet_list_gateway(self): subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_network = mock.Mock(return_value=subnet) + self.network_client.find_network.return_value = subnet arglist = [ '--gateway', subnet.gateway_ip, @@ -1057,7 +1049,7 @@ def test_subnet_list_gateway(self): def test_subnet_list_name(self): subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_network = mock.Mock(return_value=subnet) + self.network_client.find_network.return_value = subnet arglist = [ '--name', subnet.name, @@ -1076,7 +1068,7 @@ def test_subnet_list_name(self): def test_subnet_list_subnet_range(self): subnet = network_fakes.FakeSubnet.create_one_subnet() - self.network_client.find_network = mock.Mock(return_value=subnet) + self.network_client.find_network.return_value = subnet arglist = [ '--subnet-range', subnet.cidr, @@ -1098,10 +1090,9 @@ def test_subnet_list_subnetpool_by_name(self): subnet = network_fakes.FakeSubnet.create_one_subnet( {'subnetpool_id': subnet_pool.id} ) - self.network_client.find_network = mock.Mock(return_value=subnet) - self.network_client.find_subnet_pool = mock.Mock( - return_value=subnet_pool - ) + self.network_client.find_network.return_value = subnet + self.network_client.find_subnet_pool.return_value = subnet_pool + arglist = [ '--subnet-pool', subnet_pool.name, @@ -1123,10 +1114,9 @@ def test_subnet_list_subnetpool_by_id(self): subnet = network_fakes.FakeSubnet.create_one_subnet( {'subnetpool_id': subnet_pool.id} ) - self.network_client.find_network = mock.Mock(return_value=subnet) - self.network_client.find_subnet_pool = mock.Mock( - return_value=subnet_pool - ) + self.network_client.find_network.return_value = subnet + self.network_client.find_subnet_pool.return_value = subnet_pool + arglist = [ '--subnet-pool', subnet_pool.id, @@ -1182,9 +1172,9 @@ class TestSetSubnet(TestSubnet): def setUp(self): super().setUp() - self.network_client.update_subnet = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) + self.network_client.update_subnet.return_value = None + self.network_client.set_tags.return_value = None + self.network_client.find_subnet.return_value = self._subnet self.cmd = subnet_v2.SetSubnet(self.app, None) def test_set_this(self): @@ -1263,7 +1253,7 @@ def test_append_options(self): 'service_types': ["network:router_gateway"], } ) - self.network_client.find_subnet = mock.Mock(return_value=_testsubnet) + self.network_client.find_subnet.return_value = _testsubnet arglist = [ '--dns-nameserver', '10.0.0.2', @@ -1329,7 +1319,7 @@ def test_overwrite_options(self): 'dns_nameservers': ["10.0.0.1"], } ) - self.network_client.find_subnet = mock.Mock(return_value=_testsubnet) + self.network_client.find_subnet.return_value = _testsubnet arglist = [ '--host-route', 'destination=10.30.30.30/24,gateway=10.30.30.1', @@ -1379,7 +1369,7 @@ def test_clear_options(self): 'dns_nameservers': ['10.0.0.1'], } ) - self.network_client.find_subnet = mock.Mock(return_value=_testsubnet) + self.network_client.find_subnet.return_value = _testsubnet arglist = [ '--no-host-route', '--no-allocation-pool', @@ -1448,8 +1438,8 @@ def test_set_segment(self): 'segment_id': None, } ) - self.network_client.find_subnet = mock.Mock(return_value=_subnet) - self.network_client.find_segment = mock.Mock(return_value=_segment) + self.network_client.find_subnet.return_value = _subnet + self.network_client.find_segment.return_value = _segment arglist = ['--network-segment', _segment.id, _subnet.name] verifylist = [('network_segment', _segment.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1514,7 +1504,7 @@ def setUp(self): # Get the command object to test self.cmd = subnet_v2.ShowSubnet(self.app, None) - self.network_client.find_subnet = mock.Mock(return_value=self._subnet) + self.network_client.find_subnet.return_value = self._subnet def test_show_no_options(self): arglist = [] @@ -1572,11 +1562,10 @@ def setUp(self): 'tags': ['green', 'red'], } ) - self.network_client.find_subnet = mock.Mock( - return_value=self._testsubnet - ) - self.network_client.update_subnet = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_subnet.return_value = self._testsubnet + + self.network_client.update_subnet.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = subnet_v2.UnsetSubnet(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py index 40aa1e990..013550ec1 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock from unittest.mock import call from osc_lib.cli import format_columns @@ -76,16 +75,15 @@ class TestCreateSubnetPool(TestSubnetPool): def setUp(self): super().setUp() - self.network_client.create_subnet_pool = mock.Mock( - return_value=self._subnet_pool - ) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.create_subnet_pool.return_value = self._subnet_pool + + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = subnet_pool.CreateSubnetPool(self.app, None) - self.network_client.find_address_scope = mock.Mock( - return_value=self._address_scope + self.network_client.find_address_scope.return_value = ( + self._address_scope ) self.projects_mock.get.return_value = self.project @@ -387,7 +385,7 @@ class TestDeleteSubnetPool(TestSubnetPool): def setUp(self): super().setUp() - self.network_client.delete_subnet_pool = mock.Mock(return_value=None) + self.network_client.delete_subnet_pool.return_value = None self.network_client.find_subnet_pool = ( network_fakes.FakeSubnetPool.get_subnet_pools(self._subnet_pools) @@ -445,9 +443,7 @@ def test_multi_subnet_pools_delete_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self._subnet_pools[0], exceptions.CommandError] - self.network_client.find_subnet_pool = mock.Mock( - side_effect=find_mock_result - ) + self.network_client.find_subnet_pool.side_effect = find_mock_result try: self.cmd.take_action(parsed_args) @@ -514,9 +510,7 @@ def setUp(self): # Get the command object to test self.cmd = subnet_pool.ListSubnetPool(self.app, None) - self.network_client.subnet_pools = mock.Mock( - return_value=self._subnet_pools - ) + self.network_client.subnet_pools.return_value = self._subnet_pools def test_subnet_pool_list_no_option(self): arglist = [] @@ -653,7 +647,7 @@ def test_subnet_pool_list_project_domain(self): def test_subnet_pool_list_name(self): subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool() - self.network_client.find_network = mock.Mock(return_value=subnet_pool) + self.network_client.find_network.return_value = subnet_pool arglist = [ '--name', subnet_pool.name, @@ -672,9 +666,8 @@ def test_subnet_pool_list_name(self): def test_subnet_pool_list_address_scope(self): addr_scope = network_fakes.create_one_address_scope() - self.network_client.find_address_scope = mock.Mock( - return_value=addr_scope - ) + self.network_client.find_address_scope.return_value = addr_scope + arglist = [ '--address-scope', addr_scope.id, @@ -734,15 +727,13 @@ class TestSetSubnetPool(TestSubnetPool): def setUp(self): super().setUp() - self.network_client.update_subnet_pool = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.update_subnet_pool.return_value = None + self.network_client.set_tags.return_value = None - self.network_client.find_subnet_pool = mock.Mock( - return_value=self._subnet_pool - ) + self.network_client.find_subnet_pool.return_value = self._subnet_pool - self.network_client.find_address_scope = mock.Mock( - return_value=self._address_scope + self.network_client.find_address_scope.return_value = ( + self._address_scope ) # Get the command object to test @@ -1079,9 +1070,7 @@ class TestShowSubnetPool(TestSubnetPool): def setUp(self): super().setUp() - self.network_client.find_subnet_pool = mock.Mock( - return_value=self._subnet_pool - ) + self.network_client.find_subnet_pool.return_value = self._subnet_pool # Get the command object to test self.cmd = subnet_pool.ShowSubnetPool(self.app, None) @@ -1122,11 +1111,10 @@ def setUp(self): self._subnetpool = network_fakes.FakeSubnetPool.create_one_subnet_pool( {'tags': ['green', 'red']} ) - self.network_client.find_subnet_pool = mock.Mock( - return_value=self._subnetpool - ) - self.network_client.update_subnet_pool = mock.Mock(return_value=None) - self.network_client.set_tags = mock.Mock(return_value=None) + self.network_client.find_subnet_pool.return_value = self._subnetpool + + self.network_client.update_subnet_pool.return_value = None + self.network_client.set_tags.return_value = None # Get the command object to test self.cmd = subnet_pool.UnsetSubnetPool(self.app, None) From 33d34bdfe8f31f422075e5ff5d8ae57de1469f23 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Nov 2025 14:14:05 +0000 Subject: [PATCH 213/245] Remove tests for other osc-lib These are already found in osc-lib itself. Change-Id: I51114a5a79d6cd6ea46f60284066132b2e54a1a5 Signed-off-by: Stephen Finucane --- .../tests/unit/common/test_logs.py | 221 ------------------ 1 file changed, 221 deletions(-) delete mode 100644 openstackclient/tests/unit/common/test_logs.py diff --git a/openstackclient/tests/unit/common/test_logs.py b/openstackclient/tests/unit/common/test_logs.py deleted file mode 100644 index 234ba427e..000000000 --- a/openstackclient/tests/unit/common/test_logs.py +++ /dev/null @@ -1,221 +0,0 @@ -# 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. -# - -# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release -# or Jun 2017. - -import logging -from unittest import mock - -from osc_lib import logs - -from openstackclient.tests.unit import utils - - -class TestContext(utils.TestCase): - def test_log_level_from_options(self): - opts = mock.Mock() - opts.verbose_level = 0 - self.assertEqual(logging.ERROR, logs.log_level_from_options(opts)) - opts.verbose_level = 1 - self.assertEqual(logging.WARNING, logs.log_level_from_options(opts)) - opts.verbose_level = 2 - self.assertEqual(logging.INFO, logs.log_level_from_options(opts)) - opts.verbose_level = 3 - self.assertEqual(logging.DEBUG, logs.log_level_from_options(opts)) - - def test_log_level_from_config(self): - cfg = {'verbose_level': 0} - self.assertEqual(logging.ERROR, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1} - self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 2} - self.assertEqual(logging.INFO, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 3} - self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'critical'} - self.assertEqual(logging.CRITICAL, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'error'} - self.assertEqual(logging.ERROR, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'warning'} - self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'info'} - self.assertEqual(logging.INFO, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'debug'} - self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'bogus'} - self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg)) - cfg = {'verbose_level': 1, 'log_level': 'info', 'debug': True} - self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg)) - - @mock.patch('warnings.simplefilter') - def test_set_warning_filter(self, simplefilter): - logs.set_warning_filter(logging.ERROR) - simplefilter.assert_called_with("ignore") - logs.set_warning_filter(logging.WARNING) - simplefilter.assert_called_with("ignore") - logs.set_warning_filter(logging.INFO) - simplefilter.assert_called_with("once") - - -class TestFileFormatter(utils.TestCase): - def test_nothing(self): - formatter = logs._FileFormatter() - self.assertEqual( - ( - '%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' - '%(name)s %(message)s' - ), - formatter.fmt, - ) - - def test_options(self): - class Opts: - cloud = 'cloudy' - os_project_name = 'projecty' - username = 'usernamey' - - options = Opts() - formatter = logs._FileFormatter(options=options) - self.assertEqual( - ( - '%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' - '%(name)s [cloudy usernamey projecty] %(message)s' - ), - formatter.fmt, - ) - - def test_config(self): - config = mock.Mock() - config.config = {'cloud': 'cloudy'} - config.auth = {'project_name': 'projecty', 'username': 'usernamey'} - formatter = logs._FileFormatter(config=config) - self.assertEqual( - ( - '%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' - '%(name)s [cloudy usernamey projecty] %(message)s' - ), - formatter.fmt, - ) - - -class TestLogConfigurator(utils.TestCase): - def setUp(self): - super().setUp() - self.options = mock.Mock() - self.options.verbose_level = 1 - self.options.log_file = None - self.options.debug = False - self.root_logger = mock.Mock() - self.root_logger.setLevel = mock.Mock() - self.root_logger.addHandler = mock.Mock() - self.requests_log = mock.Mock() - self.requests_log.setLevel = mock.Mock() - self.cliff_log = mock.Mock() - self.cliff_log.setLevel = mock.Mock() - self.stevedore_log = mock.Mock() - self.stevedore_log.setLevel = mock.Mock() - self.iso8601_log = mock.Mock() - self.iso8601_log.setLevel = mock.Mock() - self.loggers = [ - self.root_logger, - self.requests_log, - self.cliff_log, - self.stevedore_log, - self.iso8601_log, - ] - - @mock.patch('logging.StreamHandler') - @mock.patch('logging.getLogger') - @mock.patch('osc_lib.logs.set_warning_filter') - def test_init(self, warning_filter, getLogger, handle): - getLogger.side_effect = self.loggers - console_logger = mock.Mock() - console_logger.setFormatter = mock.Mock() - console_logger.setLevel = mock.Mock() - handle.return_value = console_logger - - configurator = logs.LogConfigurator(self.options) - - getLogger.assert_called_with('iso8601') # last call - warning_filter.assert_called_with(logging.WARNING) - self.root_logger.setLevel.assert_called_with(logging.DEBUG) - self.root_logger.addHandler.assert_called_with(console_logger) - self.requests_log.setLevel.assert_called_with(logging.ERROR) - self.cliff_log.setLevel.assert_called_with(logging.ERROR) - self.stevedore_log.setLevel.assert_called_with(logging.ERROR) - self.iso8601_log.setLevel.assert_called_with(logging.ERROR) - self.assertFalse(configurator.dump_trace) - - @mock.patch('logging.getLogger') - @mock.patch('osc_lib.logs.set_warning_filter') - def test_init_no_debug(self, warning_filter, getLogger): - getLogger.side_effect = self.loggers - self.options.debug = True - - configurator = logs.LogConfigurator(self.options) - - warning_filter.assert_called_with(logging.DEBUG) - self.requests_log.setLevel.assert_called_with(logging.DEBUG) - self.assertTrue(configurator.dump_trace) - - @mock.patch('logging.FileHandler') - @mock.patch('logging.getLogger') - @mock.patch('osc_lib.logs.set_warning_filter') - @mock.patch('osc_lib.logs._FileFormatter') - def test_init_log_file(self, formatter, warning_filter, getLogger, handle): - getLogger.side_effect = self.loggers - self.options.log_file = '/tmp/log_file' - file_logger = mock.Mock() - file_logger.setFormatter = mock.Mock() - file_logger.setLevel = mock.Mock() - handle.return_value = file_logger - mock_formatter = mock.Mock() - formatter.return_value = mock_formatter - - logs.LogConfigurator(self.options) - - handle.assert_called_with(filename=self.options.log_file) - self.root_logger.addHandler.assert_called_with(file_logger) - file_logger.setFormatter.assert_called_with(mock_formatter) - file_logger.setLevel.assert_called_with(logging.WARNING) - - @mock.patch('logging.FileHandler') - @mock.patch('logging.getLogger') - @mock.patch('osc_lib.logs.set_warning_filter') - @mock.patch('osc_lib.logs._FileFormatter') - def test_configure(self, formatter, warning_filter, getLogger, handle): - getLogger.side_effect = self.loggers - configurator = logs.LogConfigurator(self.options) - cloud_config = mock.Mock() - config_log = '/tmp/config_log' - cloud_config.config = { - 'log_file': config_log, - 'verbose_level': 1, - 'log_level': 'info', - } - file_logger = mock.Mock() - file_logger.setFormatter = mock.Mock() - file_logger.setLevel = mock.Mock() - handle.return_value = file_logger - mock_formatter = mock.Mock() - formatter.return_value = mock_formatter - - configurator.configure(cloud_config) - - warning_filter.assert_called_with(logging.INFO) - handle.assert_called_with(filename=config_log) - self.root_logger.addHandler.assert_called_with(file_logger) - file_logger.setFormatter.assert_called_with(mock_formatter) - file_logger.setLevel.assert_called_with(logging.INFO) - self.assertFalse(configurator.dump_trace) From eb7c4c61a9253b99e9e658d27b4f0170c183d6ce Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 7 Nov 2025 12:41:29 +0000 Subject: [PATCH 214/245] Add new hacking rules To catch some obvious issues. Change-Id: Ic0ddc95100811e7b313b519aad7d687a1415020b Signed-off-by: Stephen Finucane --- .pre-commit-config.yaml | 3 +- hacking/checks.py | 108 ++++++++++++++++++ .../tests/unit/network/test_common.py | 14 +-- pyproject.toml | 1 + tox.ini | 11 +- 5 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 hacking/checks.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 47903a90d..b277bb059 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,10 +32,11 @@ repos: - id: mypy additional_dependencies: - types-requests - # keep this in-sync with '[mypy] exclude' in 'setup.cfg' + # keep this in-sync with '[tool.mypy] exclude' in 'pyproject.toml' exclude: | (?x)( doc/.* | examples/.* + | hacking/.* | releasenotes/.* ) diff --git a/hacking/checks.py b/hacking/checks.py new file mode 100644 index 000000000..98b40370d --- /dev/null +++ b/hacking/checks.py @@ -0,0 +1,108 @@ +# 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 os +import re + +from hacking import core + +""" +Guidelines for writing new hacking checks + + - Use only for python-openstackclient specific tests. OpenStack general tests + should be submitted to the common 'hacking' module. + - Pick numbers in the range O4xx. Find the current test with the highest + allocated number and then pick the next value. +""" + + +@core.flake8ext +def assert_no_oslo(logical_line): + """Check for use of oslo libraries. + + O400 + """ + if re.match(r'(from|import) oslo_.*', logical_line): + yield (0, "0400: oslo libraries should not be used in SDK projects") + + +@core.flake8ext +def assert_no_duplicated_setup(logical_line, filename): + """Check for use of various unnecessary test duplications. + + O401 + """ + if os.path.join('openstackclient', 'tests', 'unit') not in filename: + return + + if re.match(r'self.app = .*\(self.app, self.namespace\)', logical_line): + yield ( + 0, + 'O401: It is not necessary to create dummy Namespace objects', + ) + + if os.path.basename(filename) != 'fakes.py': + if re.match( + r'self.[a-z_]+_client = self.app.client_manager.*', logical_line + ): + yield ( + 0, + "O401: Aliases for mocks of the service client are already " + "provided by the respective service's FakeClientMixin class", + ) + + if match := re.match( + r'self.app.client_manager.([a-z_]+) = mock.Mock', logical_line + ): + service = match.group(1) + if service == 'auth_ref': + return + yield ( + 0, + f"O401: client_manager.{service} mocks are already provided by " + f"the {service} service's FakeClientMixin class", + ) + + +@core.flake8ext +def assert_use_of_client_aliases(logical_line, filename): + """Ensure we use $service_client instead of $sdk_connection.service. + + O402 + """ + # we should expand the list of services as we drop legacy clients + if match := re.match( + r'self\.app\.client_manager\.sdk_connnection\.(compute|network|image)', + logical_line, + ): + service = match.group(1) + yield (0, f"0402: prefer {service}_client to sdk_connection.{service}") + + if match := re.match( + r'(self\.app\.client_manager\.(compute|network|image)+\.[a-z_]+) = mock.Mock', + logical_line, + ): + yield ( + 0, + f"O402: {match.group(1)} is already a mock: there's no need to " + f"assign a new mock.Mock instance.", + ) + + if match := re.match( + r'(self\.(compute|network|image)_client\.[a-z_]+) = mock.Mock', + logical_line, + ): + yield ( + 0, + f"O402: {match.group(1)} is already a mock: there's no need to " + f"assign a new mock.Mock instance.", + ) diff --git a/openstackclient/tests/unit/network/test_common.py b/openstackclient/tests/unit/network/test_common.py index 02cb021ef..cc84c8bdf 100644 --- a/openstackclient/tests/unit/network/test_common.py +++ b/openstackclient/tests/unit/network/test_common.py @@ -126,12 +126,12 @@ def setUp(self): # Create client mocks. Note that we intentionally do not use specced # mocks since we want to test fake methods. - self.app.client_manager.network = mock.Mock() - self.network_client = self.app.client_manager.network + self.app.client_manager.network = mock.Mock() # noqa: O401 + self.network_client = self.app.client_manager.network # noqa: O401 self.network_client.network_action.return_value = 'take_action_network' - self.app.client_manager.compute = mock.Mock() - self.compute_client = self.app.client_manager.compute + self.app.client_manager.compute = mock.Mock() # noqa: O401 + self.compute_client = self.app.client_manager.compute # noqa: O401 self.compute_client.compute_action.return_value = 'take_action_compute' self.cmd = FakeNetworkAndComputeCommand(self.app, None) @@ -201,9 +201,9 @@ def setUp(self): # Create client mocks. Note that we intentionally do not use specced # mocks since we want to test fake methods. - self.app.client_manager.network = mock.Mock() - self.network_client = self.app.client_manager.network - self.network_client.test_create_action = mock.Mock() + self.app.client_manager.network = mock.Mock() # noqa: O401 + self.network_client = self.app.client_manager.network # noqa: O401 + self.network_client.test_create_action = mock.Mock() # noqa: O402 # Subclasses can override the command object to test. self.cmd = FakeCreateNeutronCommandWithExtraArgs(self.app, None) diff --git a/pyproject.toml b/pyproject.toml index 74f93ccb2..2ebe0a52c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -732,6 +732,7 @@ exclude = ''' (?x)( doc | examples + | hacking | releasenotes ) ''' diff --git a/tox.ini b/tox.ini index 7b3d951d2..2ee2dc2ca 100644 --- a/tox.ini +++ b/tox.ini @@ -113,9 +113,16 @@ commands = [flake8] show-source = true exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,releasenotes -# We only enable the hacking (H) checks -select = H +# We only enable the hacking (H) and openstackclient (O) checks +select = H,O # H301 Black will put commas after imports that can't fit on one line ignore = H301 import-order-style = pep8 application_import_names = openstackclient + +[flake8:local-plugins] +extension = + O400 = checks:assert_no_oslo + O401 = checks:assert_no_duplicated_setup + O402 = checks:assert_use_of_client_aliases +paths = ./hacking From 188737c69c498a9b8736ff9c73183896ff5d2060 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Wed, 12 Nov 2025 01:41:37 +0900 Subject: [PATCH 215/245] ruff: Use more specific name to enable pyupgrade rule UP is the exact name of the rule, instead of U. Use the exact name to avoid potential problems caused by any UX rules which can be added in the future. Change-Id: I5fa59181fcd3e28bf3c87ce2a5e610561b2ee8a8 Signed-off-by: Takashi Kajinami --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 74f93ccb2..673da0dae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -748,7 +748,7 @@ quote-style = "preserve" docstring-code-format = true [tool.ruff.lint] -select = ["E4", "E7", "E9", "F", "S", "U"] +select = ["E4", "E7", "E9", "F", "S", "UP"] [tool.ruff.lint.per-file-ignores] "openstackclient/tests/*" = ["S"] From db2c1a5e2b1eec0afd7bbd87a5026b4df7ceb184 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 13 Nov 2025 11:57:55 +0000 Subject: [PATCH 216/245] trivial: Normalize some client usage Ahead of rework in this area. Change-Id: I1b1c2370967381903970870da8cbe0868b1e23e1 Signed-off-by: Stephen Finucane --- openstackclient/common/project_cleanup.py | 16 +++-- openstackclient/compute/v2/aggregate.py | 6 +- openstackclient/network/v2/network_trunk.py | 73 +++++++++++---------- 3 files changed, 50 insertions(+), 45 deletions(-) diff --git a/openstackclient/common/project_cleanup.py b/openstackclient/common/project_cleanup.py index 84a07353d..5b2d89c1a 100644 --- a/openstackclient/common/project_cleanup.py +++ b/openstackclient/common/project_cleanup.py @@ -90,17 +90,19 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - sdk = self.app.client_manager.sdk_connection + connection = self.app.client_manager.sdk_connection if parsed_args.auth_project: - project_connect = sdk + # is we've got a project already configured, use the connection + # as-is + pass elif parsed_args.project: - project = sdk.identity.find_project( + project = connection.identity.find_project( name_or_id=parsed_args.project, ignore_missing=False ) - project_connect = sdk.connect_as_project(project) + connection = connection.connect_as_project(project) - if project_connect: + if connection: status_queue: queue.Queue[ty.Any] = queue.Queue() parsed_args.max_width = int( os.environ.get('CLIFF_MAX_TERM_WIDTH', 0) @@ -120,7 +122,7 @@ def take_action(self, parsed_args): if parsed_args.updated_before: filters['updated_at'] = parsed_args.updated_before - project_connect.project_cleanup( + connection.project_cleanup( dry_run=True, status_queue=status_queue, filters=filters, @@ -150,7 +152,7 @@ def take_action(self, parsed_args): self.log.warning(_('Deleting resources')) - project_connect.project_cleanup( + connection.project_cleanup( dry_run=False, status_queue=status_queue, filters=filters, diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index e64960721..cc5817a0f 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -438,15 +438,15 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) + image_client = self.app.client_manager.sdk_connection.image + aggregate = compute_client.find_aggregate( parsed_args.aggregate, ignore_missing=False ) images = [] for img in parsed_args.image: - image = self.app.client_manager.sdk_connection.image.find_image( - img, ignore_missing=False - ) + image = image_client.find_image(img, ignore_missing=False) images.append(image.id) compute_client.aggregate_precache_images(aggregate.id, images) diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index f8f1c5bcb..61d378371 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -88,9 +88,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = _get_attrs_for_trunk(self.app.client_manager, parsed_args) - obj = client.create_trunk(**attrs) + network_client = self.app.client_manager.network + identity_client = self.app.client_manager.identity + attrs = _get_attrs_for_trunk( + network_client, identity_client, parsed_args + ) + obj = network_client.create_trunk(**attrs) display_columns, columns = _get_columns(obj) data = osc_utils.get_dict_properties( obj, columns, formatters=_formatters @@ -112,12 +115,12 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network + network_client = self.app.client_manager.network result = 0 for trunk in parsed_args.trunk: try: - trunk_id = client.find_trunk(trunk).id - client.delete_trunk(trunk_id) + trunk_id = network_client.find_trunk(trunk).id + network_client.delete_trunk(trunk_id) except Exception as e: result += 1 LOG.error( @@ -150,8 +153,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - data = client.trunks() + network_client = self.app.client_manager.network + data = network_client.trunks() headers: tuple[str, ...] = ('ID', 'Name', 'Parent Port', 'Description') columns: tuple[str, ...] = ('id', 'name', 'port_id', 'description') if parsed_args.long: @@ -215,11 +218,14 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - trunk_id = client.find_trunk(parsed_args.trunk) - attrs = _get_attrs_for_trunk(self.app.client_manager, parsed_args) + network_client = self.app.client_manager.network + identity_client = self.app.client_manager.identity + trunk_id = network_client.find_trunk(parsed_args.trunk) + attrs = _get_attrs_for_trunk( + network_client, identity_client, parsed_args + ) try: - client.update_trunk(trunk_id, **attrs) + network_client.update_trunk(trunk_id, **attrs) except Exception as e: msg = _("Failed to set trunk '%(t)s': %(e)s") % { 't': parsed_args.trunk, @@ -228,10 +234,10 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) if parsed_args.set_subports: subport_attrs = _get_attrs_for_subports( - self.app.client_manager, parsed_args + network_client, parsed_args ) try: - client.add_trunk_subports(trunk_id, subport_attrs) + network_client.add_trunk_subports(trunk_id, subport_attrs) except Exception as e: msg = _("Failed to add subports to trunk '%(t)s': %(e)s") % { 't': parsed_args.trunk, @@ -251,9 +257,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - trunk_id = client.find_trunk(parsed_args.trunk).id - obj = client.get_trunk(trunk_id) + network_client = self.app.client_manager.network + trunk_id = network_client.find_trunk(parsed_args.trunk).id + obj = network_client.get_trunk(trunk_id) display_columns, columns = _get_columns(obj) data = osc_utils.get_dict_properties( obj, columns, formatters=_formatters @@ -275,9 +281,9 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - trunk_id = client.find_trunk(parsed_args.trunk) - data = client.get_trunk_subports(trunk_id) + network_client = self.app.client_manager.network + trunk_id = network_client.find_trunk(parsed_args.trunk) + data = network_client.get_trunk_subports(trunk_id) headers: tuple[str, ...] = ( 'Port', 'Segmentation Type', @@ -324,10 +330,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - attrs = _get_attrs_for_subports(self.app.client_manager, parsed_args) - trunk_id = client.find_trunk(parsed_args.trunk) - client.delete_trunk_subports(trunk_id, attrs) + network_client = self.app.client_manager.network + attrs = _get_attrs_for_subports(network_client, parsed_args) + trunk_id = network_client.find_trunk(parsed_args.trunk) + network_client.delete_trunk_subports(trunk_id, attrs) _formatters = { @@ -343,7 +349,7 @@ def _get_columns(item): ) -def _get_attrs_for_trunk(client_manager, parsed_args): +def _get_attrs_for_trunk(network_client, identity_client, parsed_args): attrs: dict[str, ty.Any] = {} if parsed_args.name is not None: attrs['name'] = str(parsed_args.name) @@ -354,18 +360,15 @@ def _get_attrs_for_trunk(client_manager, parsed_args): if parsed_args.disable: attrs['admin_state_up'] = False if 'parent_port' in parsed_args and parsed_args.parent_port is not None: - port_id = client_manager.network.find_port(parsed_args.parent_port)[ - 'id' - ] + port_id = network_client.find_port(parsed_args.parent_port)['id'] attrs['port_id'] = port_id if 'add_subports' in parsed_args and parsed_args.add_subports is not None: attrs[SUB_PORTS] = _format_subports( - client_manager, parsed_args.add_subports + network_client, parsed_args.add_subports ) # "trunk set" command doesn't support setting project. if 'project' in parsed_args and parsed_args.project is not None: - identity_client = client_manager.identity project_id = identity_utils.find_project( identity_client, parsed_args.project, @@ -376,12 +379,12 @@ def _get_attrs_for_trunk(client_manager, parsed_args): return attrs -def _format_subports(client_manager, subports): +def _format_subports(network_client, subports): attrs = [] for subport in subports: subport_attrs = {} if subport.get('port'): - port_id = client_manager.network.find_port(subport['port'])['id'] + port_id = network_client.find_port(subport['port'])['id'] subport_attrs['port_id'] = port_id if subport.get('segmentation-id'): try: @@ -400,17 +403,17 @@ def _format_subports(client_manager, subports): return attrs -def _get_attrs_for_subports(client_manager, parsed_args): +def _get_attrs_for_subports(network_client, parsed_args): attrs = [] if 'set_subports' in parsed_args and parsed_args.set_subports is not None: - attrs = _format_subports(client_manager, parsed_args.set_subports) + attrs = _format_subports(network_client, parsed_args.set_subports) if ( 'unset_subports' in parsed_args and parsed_args.unset_subports is not None ): subports_list = [] for subport in parsed_args.unset_subports: - port_id = client_manager.network.find_port(subport)['id'] + port_id = network_client.find_port(subport)['id'] subports_list.append({'port_id': port_id}) attrs = subports_list return attrs From a5e4d5f0fa6cf19fce610508fa00128102613cfa Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 14 Nov 2025 11:47:15 +0000 Subject: [PATCH 217/245] identity: Fix filtering endpoints by project with domain We were incorrectly passing domain_id as a positional argument, causing it to get picked up as the ignore_missing argument instead. Correct this, fixing another bug where the look of projects or domains could be forbidden by policy, in the process. The latter is unlikely to happen, given endpoint lookup is typically an admin-only operation, but it's better to be safe. Change-Id: Idd3300040967d781b7743accd62298cb24c62872 Signed-off-by: Stephen Finucane --- openstackclient/identity/v3/endpoint.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index 1dfa88120..ee5900d71 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -231,11 +231,22 @@ def take_action(self, parsed_args): endpoint = None if parsed_args.endpoint: endpoint = identity_client.find_endpoint(parsed_args.endpoint) - project = None + + project_domain_id = None + if parsed_args.project_domain: + project_domain_id = common._find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.project_domain, + ) + + project_id = None if parsed_args.project: - project = identity_client.find_project( - parsed_args.project, - parsed_args.project_domain, + project_id = common._find_sdk_id( + identity_client.find_project, + name_or_id=common._get_token_resource( + identity_client, 'project', parsed_args.project + ), + domain_id=project_domain_id, ) if endpoint: @@ -273,9 +284,9 @@ def take_action(self, parsed_args): region = identity_client.get_region(parsed_args.region) kwargs['region_id'] = region.id - if project: + if project_id: data = list( - identity_client.project_endpoints(project=project.id) + identity_client.project_endpoints(project=project_id) ) else: data = list(identity_client.endpoints(**kwargs)) From 55fd501657417518626a68eb8c6ceaa6f5377652 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 14 Nov 2025 11:48:01 +0000 Subject: [PATCH 218/245] identity: Remove duplicated _find_sdk_id method We have a few instances of this. Settle on one. Change-Id: Id115fea1c59ad75ec8e00d665e587020f7177a55 Signed-off-by: Stephen Finucane --- openstackclient/identity/v3/role.py | 56 ++++------ .../identity/v3/role_assignment.py | 32 ++---- .../tests/unit/identity/test_common.py | 100 ++++++++++++++++++ .../tests/unit/identity/v3/test_role.py | 83 +-------------- 4 files changed, 130 insertions(+), 141 deletions(-) create mode 100644 openstackclient/tests/unit/identity/test_common.py diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index 3301c3f79..26af11a95 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -82,32 +82,12 @@ def _add_identity_and_resource_options_to_parser(parser): common.add_inherited_option_to_parser(parser) -def _find_sdk_id( - find_command, name_or_id, validate_actor_existence=True, **kwargs -): - try: - resource = find_command( - name_or_id=name_or_id, ignore_missing=False, **kwargs - ) - - # Mimic the behavior of - # openstackclient.identity.common._find_identity_resource() - # and ignore if we don't have permission to find a resource. - except sdk_exc.ForbiddenException: - return name_or_id - except sdk_exc.ResourceNotFound as exc: - if not validate_actor_existence: - return name_or_id - raise exceptions.CommandError from exc - return resource.id - - def _process_identity_and_resource_options( parsed_args, identity_client, validate_actor_existence=True ): def _find_user(): domain_id = ( - _find_sdk_id( + common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.user_domain, validate_actor_existence=validate_actor_existence, @@ -115,7 +95,7 @@ def _find_user(): if parsed_args.user_domain else None ) - return _find_sdk_id( + return common._find_sdk_id( identity_client.find_user, name_or_id=parsed_args.user, validate_actor_existence=validate_actor_existence, @@ -124,7 +104,7 @@ def _find_user(): def _find_group(): domain_id = ( - _find_sdk_id( + common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.group_domain, validate_actor_existence=validate_actor_existence, @@ -132,7 +112,7 @@ def _find_group(): if parsed_args.group_domain else None ) - return _find_sdk_id( + return common._find_sdk_id( identity_client.find_group, name_or_id=parsed_args.group, validate_actor_existence=validate_actor_existence, @@ -141,7 +121,7 @@ def _find_group(): def _find_project(): domain_id = ( - _find_sdk_id( + common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.project_domain, validate_actor_existence=validate_actor_existence, @@ -149,7 +129,7 @@ def _find_project(): if parsed_args.project_domain else None ) - return _find_sdk_id( + return common._find_sdk_id( identity_client.find_project, name_or_id=parsed_args.project, validate_actor_existence=validate_actor_existence, @@ -162,7 +142,7 @@ def _find_project(): kwargs['system'] = parsed_args.system elif parsed_args.user and parsed_args.domain: kwargs['user'] = _find_user() - kwargs['domain'] = _find_sdk_id( + kwargs['domain'] = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.domain, validate_actor_existence=validate_actor_existence, @@ -175,7 +155,7 @@ def _find_project(): kwargs['system'] = parsed_args.system elif parsed_args.group and parsed_args.domain: kwargs['group'] = _find_group() - kwargs['domain'] = _find_sdk_id( + kwargs['domain'] = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.domain, validate_actor_existence=validate_actor_existence, @@ -228,10 +208,10 @@ def take_action(self, parsed_args): domain_id = None if parsed_args.role_domain: - domain_id = _find_sdk_id( + domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.role_domain ) - role = _find_sdk_id( + role = common._find_sdk_id( identity_client.find_role, name_or_id=parsed_args.role, domain_id=domain_id, @@ -328,7 +308,7 @@ def take_action(self, parsed_args): create_kwargs = {} if parsed_args.domain: - create_kwargs['domain_id'] = _find_sdk_id( + create_kwargs['domain_id'] = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.domain ) @@ -381,13 +361,13 @@ def take_action(self, parsed_args): domain_id = None if parsed_args.domain: - domain_id = _find_sdk_id( + domain_id = common._find_sdk_id( identity_client.find_domain, parsed_args.domain ) errors = 0 for role in parsed_args.roles: try: - role_id = _find_sdk_id( + role_id = common._find_sdk_id( identity_client.find_role, name_or_id=role, domain_id=domain_id, @@ -482,11 +462,11 @@ def take_action(self, parsed_args): domain_id = None if parsed_args.role_domain: - domain_id = _find_sdk_id( + domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.role_domain, ) - role = _find_sdk_id( + role = common._find_sdk_id( identity_client.find_role, name_or_id=parsed_args.role, domain_id=domain_id, @@ -582,7 +562,7 @@ def take_action(self, parsed_args): domain_id = None if parsed_args.domain: - domain_id = _find_sdk_id( + domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.domain, ) @@ -591,7 +571,7 @@ def take_action(self, parsed_args): if parsed_args.immutable is not None: update_kwargs["options"] = {"immutable": parsed_args.immutable} - role = _find_sdk_id( + role = common._find_sdk_id( identity_client.find_role, name_or_id=parsed_args.role, domain_id=domain_id, @@ -623,7 +603,7 @@ def take_action(self, parsed_args): domain_id = None if parsed_args.domain: - domain_id = _find_sdk_id( + domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.domain, ) diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py index 12e1527b6..ed9e8c0f7 100644 --- a/openstackclient/identity/v3/role_assignment.py +++ b/openstackclient/identity/v3/role_assignment.py @@ -13,7 +13,6 @@ """Identity v3 Assignment action implementations""" -from openstack import exceptions as sdk_exceptions from osc_lib.command import command from openstackclient.i18n import _ @@ -51,15 +50,6 @@ def _get_ids(attr): ) -def _find_sdk_id(find_command, name_or_id, **kwargs): - try: - return find_command( - name_or_id=name_or_id, ignore_missing=False, **kwargs - ).id - except sdk_exceptions.ForbiddenException: - return name_or_id - - class ListRoleAssignment(command.Lister): _description = _("List role assignments") @@ -135,12 +125,12 @@ def take_action(self, parsed_args): role_id = None role_domain_id = None if parsed_args.role_domain: - role_domain_id = _find_sdk_id( + role_domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.role_domain, ) if parsed_args.role: - role_id = _find_sdk_id( + role_id = common._find_sdk_id( identity_client.find_role, name_or_id=parsed_args.role, domain_id=role_domain_id, @@ -148,21 +138,21 @@ def take_action(self, parsed_args): user_domain_id = None if parsed_args.user_domain: - user_domain_id = _find_sdk_id( + user_domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.user_domain, ) user_id = None if parsed_args.user: - user_id = _find_sdk_id( + user_id = common._find_sdk_id( identity_client.find_user, name_or_id=parsed_args.user, domain_id=user_domain_id, ) elif parsed_args.authuser: if auth_ref: - user_id = _find_sdk_id( + user_id = common._find_sdk_id( identity_client.find_user, name_or_id=auth_ref.user_id, ) @@ -173,21 +163,21 @@ def take_action(self, parsed_args): domain_id = None if parsed_args.domain: - domain_id = _find_sdk_id( + domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.domain, ) project_domain_id = None if parsed_args.project_domain: - project_domain_id = _find_sdk_id( + project_domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.project_domain, ) project_id = None if parsed_args.project: - project_id = _find_sdk_id( + project_id = common._find_sdk_id( identity_client.find_project, name_or_id=common._get_token_resource( identity_client, 'project', parsed_args.project @@ -196,21 +186,21 @@ def take_action(self, parsed_args): ) elif parsed_args.authproject: if auth_ref: - project_id = _find_sdk_id( + project_id = common._find_sdk_id( identity_client.find_project, name_or_id=auth_ref.project_id, ) group_domain_id = None if parsed_args.group_domain: - group_domain_id = _find_sdk_id( + group_domain_id = common._find_sdk_id( identity_client.find_domain, name_or_id=parsed_args.group_domain, ) group_id = None if parsed_args.group: - group_id = _find_sdk_id( + group_id = common._find_sdk_id( identity_client.find_group, name_or_id=parsed_args.group, domain_id=group_domain_id, diff --git a/openstackclient/tests/unit/identity/test_common.py b/openstackclient/tests/unit/identity/test_common.py new file mode 100644 index 000000000..ae85262dc --- /dev/null +++ b/openstackclient/tests/unit/identity/test_common.py @@ -0,0 +1,100 @@ +# 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. + +from unittest import mock + +from openstack import exceptions as sdk_exc +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes +from osc_lib import exceptions + +from openstackclient.identity import common +from openstackclient.tests.unit import utils as test_utils + + +class TestFindSDKId(test_utils.TestCase): + def setUp(self): + super().setUp() + self.user = sdk_fakes.generate_fake_resource(_user.User) + self.identity_sdk_client = mock.Mock() + self.identity_sdk_client.find_user = mock.Mock() + + def test_find_sdk_id_validate(self): + self.identity_sdk_client.find_user.side_effect = [self.user] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=True, + ) + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_no_validate(self): + self.identity_sdk_client.find_user.side_effect = [self.user] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=False, + ) + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_not_found_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] + + self.assertRaises( + exceptions.CommandError, + common._find_sdk_id, + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=True, + ) + + def test_find_sdk_id_not_found_no_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ResourceNotFound, + ] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=False, + ) + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_forbidden_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ForbiddenException, + ] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=True, + ) + + self.assertEqual(self.user.id, result) + + def test_find_sdk_id_forbidden_no_validate(self): + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ForbiddenException, + ] + + result = common._find_sdk_id( + self.identity_sdk_client.find_user, + name_or_id=self.user.id, + validate_actor_existence=False, + ) + + self.assertEqual(self.user.id, result) diff --git a/openstackclient/tests/unit/identity/v3/test_role.py b/openstackclient/tests/unit/identity/v3/test_role.py index 3ed1d447a..90b2d7121 100644 --- a/openstackclient/tests/unit/identity/v3/test_role.py +++ b/openstackclient/tests/unit/identity/v3/test_role.py @@ -15,8 +15,6 @@ from unittest import mock -from osc_lib import exceptions - from openstack import exceptions as sdk_exc from openstack.identity.v3 import domain as _domain from openstack.identity.v3 import group as _group @@ -25,10 +23,10 @@ from openstack.identity.v3 import system as _system from openstack.identity.v3 import user as _user from openstack.test import fakes as sdk_fakes +from osc_lib import exceptions from openstackclient.identity.v3 import role from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -from openstackclient.tests.unit import utils as test_utils class TestRoleInherited(identity_fakes.TestIdentityv3): @@ -36,85 +34,6 @@ def _is_inheritance_testcase(self): return True -class TestFindSDKId(test_utils.TestCase): - def setUp(self): - super().setUp() - self.user = sdk_fakes.generate_fake_resource(_user.User) - self.identity_sdk_client = mock.Mock() - self.identity_sdk_client.find_user = mock.Mock() - - def test_find_sdk_id_validate(self): - self.identity_sdk_client.find_user.side_effect = [self.user] - - result = role._find_sdk_id( - self.identity_sdk_client.find_user, - name_or_id=self.user.id, - validate_actor_existence=True, - ) - self.assertEqual(self.user.id, result) - - def test_find_sdk_id_no_validate(self): - self.identity_sdk_client.find_user.side_effect = [self.user] - - result = role._find_sdk_id( - self.identity_sdk_client.find_user, - name_or_id=self.user.id, - validate_actor_existence=False, - ) - self.assertEqual(self.user.id, result) - - def test_find_sdk_id_not_found_validate(self): - self.identity_sdk_client.find_user.side_effect = [ - sdk_exc.ResourceNotFound, - ] - - self.assertRaises( - exceptions.CommandError, - role._find_sdk_id, - self.identity_sdk_client.find_user, - name_or_id=self.user.id, - validate_actor_existence=True, - ) - - def test_find_sdk_id_not_found_no_validate(self): - self.identity_sdk_client.find_user.side_effect = [ - sdk_exc.ResourceNotFound, - ] - - result = role._find_sdk_id( - self.identity_sdk_client.find_user, - name_or_id=self.user.id, - validate_actor_existence=False, - ) - self.assertEqual(self.user.id, result) - - def test_find_sdk_id_forbidden_validate(self): - self.identity_sdk_client.find_user.side_effect = [ - sdk_exc.ForbiddenException, - ] - - result = role._find_sdk_id( - self.identity_sdk_client.find_user, - name_or_id=self.user.id, - validate_actor_existence=True, - ) - - self.assertEqual(self.user.id, result) - - def test_find_sdk_id_forbidden_no_validate(self): - self.identity_sdk_client.find_user.side_effect = [ - sdk_exc.ForbiddenException, - ] - - result = role._find_sdk_id( - self.identity_sdk_client.find_user, - name_or_id=self.user.id, - validate_actor_existence=False, - ) - - self.assertEqual(self.user.id, result) - - class TestRoleAdd(identity_fakes.TestIdentityv3): def _is_inheritance_testcase(self): return False From 73021165ff486bed3a747051ce3db8e4270d6547 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 13 Nov 2025 12:08:27 +0000 Subject: [PATCH 219/245] trivial: Add missing ignore_missing arguments This prevents a class of bugs. Change-Id: I96e1cd8ed4a682ef5c95f67f3d1246f7026eada9 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 4 +- .../identity/v3/application_credential.py | 2 +- openstackclient/identity/v3/domain.py | 12 +++-- openstackclient/identity/v3/endpoint.py | 20 ++++++--- openstackclient/identity/v3/group.py | 9 +++- openstackclient/identity/v3/role.py | 1 + openstackclient/identity/v3/trust.py | 4 +- openstackclient/identity/v3/user.py | 1 + openstackclient/network/v2/network_trunk.py | 44 ++++++++++++++----- openstackclient/network/v2/port.py | 28 ++++++------ openstackclient/network/v2/router.py | 11 ++++- .../v3/test_application_credential.py | 2 +- .../tests/unit/identity/v3/test_domain.py | 2 +- .../tests/unit/identity/v3/test_endpoint.py | 2 +- .../tests/unit/identity/v3/test_group.py | 6 ++- .../unit/network/v2/test_network_trunk.py | 4 +- .../tests/unit/network/v2/test_router.py | 8 ++-- .../unit/volume/v2/test_volume_backup.py | 4 +- openstackclient/volume/v2/volume_backup.py | 4 +- 19 files changed, 116 insertions(+), 52 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 33a0bb226..500395114 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2180,7 +2180,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): compute_client = self.app.client_manager.compute for name_or_id in parsed_args.server: - server = compute_client.find_server(name_or_id) + server = compute_client.find_server( + name_or_id, ignore_missing=False + ) server.trigger_crash_dump(compute_client) diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py index 93367f8a3..c6bad267d 100644 --- a/openstackclient/identity/v3/application_credential.py +++ b/openstackclient/identity/v3/application_credential.py @@ -278,7 +278,7 @@ def take_action(self, parsed_args): for ac in parsed_args.application_credential: try: app_cred = identity_client.find_application_credential( - user_id, ac + user_id, ac, ignore_missing=False ) identity_client.delete_application_credential( user_id, app_cred.id diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index 536243a70..06c7191cb 100644 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -107,7 +107,9 @@ def take_action(self, parsed_args): ) except sdk_exceptions.ConflictException: if parsed_args.or_show: - domain = identity_client.find_domain(parsed_args.name) + domain = identity_client.find_domain( + parsed_args.name, ignore_missing=False + ) LOG.info(_('Returning existing domain %s'), domain.name) else: raise @@ -238,7 +240,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity - domain = identity_client.find_domain(parsed_args.domain) + domain = identity_client.find_domain( + parsed_args.domain, ignore_missing=False + ) kwargs = {} if parsed_args.name: kwargs['name'] = parsed_args.name @@ -266,6 +270,8 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity - domain = identity_client.find_domain(parsed_args.domain) + domain = identity_client.find_domain( + parsed_args.domain, ignore_missing=False + ) return _format_domain(domain) diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index ee5900d71..bc690939d 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -169,7 +169,9 @@ def take_action(self, parsed_args): result = 0 for i in parsed_args.endpoint: try: - endpoint_id = identity_client.find_endpoint(i).id + endpoint_id = identity_client.find_endpoint( + i, ignore_missing=False + ).id identity_client.delete_endpoint(endpoint_id) except Exception as e: result += 1 @@ -230,7 +232,9 @@ def take_action(self, parsed_args): endpoint = None if parsed_args.endpoint: - endpoint = identity_client.find_endpoint(parsed_args.endpoint) + endpoint = identity_client.find_endpoint( + parsed_args.endpoint, ignore_missing=False + ) project_domain_id = None if parsed_args.project_domain: @@ -292,7 +296,9 @@ def take_action(self, parsed_args): data = list(identity_client.endpoints(**kwargs)) for ep in data: - service = identity_client.find_service(ep.service_id) + service = identity_client.find_service( + ep.service_id, ignore_missing=False + ) ep.service_name = getattr(service, 'name', '') ep.service_type = service.type @@ -393,7 +399,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity - endpoint = identity_client.find_endpoint(parsed_args.endpoint) + endpoint = identity_client.find_endpoint( + parsed_args.endpoint, ignore_missing=False + ) kwargs = {} @@ -440,7 +448,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity - endpoint = identity_client.find_endpoint(parsed_args.endpoint) + endpoint = identity_client.find_endpoint( + parsed_args.endpoint, ignore_missing=False + ) service = common.find_service_sdk(identity_client, endpoint.service_id) diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index 92980acc6..2e3d9f9b0 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -211,10 +211,15 @@ def take_action(self, parsed_args): if parsed_args.or_show: if parsed_args.domain: group = identity_client.find_group( - parsed_args.name, domain_id=parsed_args.domain + parsed_args.name, + domain_id=parsed_args.domain, + ignore_missing=False, ) else: - group = identity_client.find_group(parsed_args.name) + group = identity_client.find_group( + parsed_args.name, + ignore_missing=False, + ) LOG.info(_('Returning existing group %s'), group.name) else: raise diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index 26af11a95..b4e7bd175 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -410,6 +410,7 @@ def take_action(self, parsed_args): if parsed_args.domain: domain = identity_client.find_domain( name_or_id=parsed_args.domain, + ignore_missing=False, ) data = identity_client.roles(domain_id=domain.id) return ( diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py index df8e2f4ea..255f7877d 100644 --- a/openstackclient/identity/v3/trust.py +++ b/openstackclient/identity/v3/trust.py @@ -179,7 +179,9 @@ def take_action(self, parsed_args): roles = [] for role in parsed_args.roles: try: - role_id = identity_client.find_role(role).id + role_id = identity_client.find_role( + role, ignore_missing=False + ).id except sdk_exceptions.ForbiddenException: role_id = role roles.append({"id": role_id}) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 34d85fba9..6b3b7217e 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -441,6 +441,7 @@ def take_action(self, parsed_args): if parsed_args.domain: domain = identity_client.find_domain( name_or_id=parsed_args.domain, + ignore_missing=False, ).id group = None diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index 61d378371..53681b774 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -119,7 +119,10 @@ def take_action(self, parsed_args): result = 0 for trunk in parsed_args.trunk: try: - trunk_id = network_client.find_trunk(trunk).id + trunk_id = network_client.find_trunk( + trunk, + ignore_missing=False, + ).id network_client.delete_trunk(trunk_id) except Exception as e: result += 1 @@ -220,7 +223,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): network_client = self.app.client_manager.network identity_client = self.app.client_manager.identity - trunk_id = network_client.find_trunk(parsed_args.trunk) + trunk_id = network_client.find_trunk( + parsed_args.trunk, + ignore_missing=False, + ) attrs = _get_attrs_for_trunk( network_client, identity_client, parsed_args ) @@ -258,7 +264,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): network_client = self.app.client_manager.network - trunk_id = network_client.find_trunk(parsed_args.trunk).id + trunk_id = network_client.find_trunk( + parsed_args.trunk, + ignore_missing=False, + ).id obj = network_client.get_trunk(trunk_id) display_columns, columns = _get_columns(obj) data = osc_utils.get_dict_properties( @@ -282,7 +291,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): network_client = self.app.client_manager.network - trunk_id = network_client.find_trunk(parsed_args.trunk) + trunk_id = network_client.find_trunk( + parsed_args.trunk, + ignore_missing=False, + ) data = network_client.get_trunk_subports(trunk_id) headers: tuple[str, ...] = ( 'Port', @@ -332,7 +344,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): network_client = self.app.client_manager.network attrs = _get_attrs_for_subports(network_client, parsed_args) - trunk_id = network_client.find_trunk(parsed_args.trunk) + trunk_id = network_client.find_trunk( + parsed_args.trunk, + ignore_missing=False, + ) network_client.delete_trunk_subports(trunk_id, attrs) @@ -360,7 +375,10 @@ def _get_attrs_for_trunk(network_client, identity_client, parsed_args): if parsed_args.disable: attrs['admin_state_up'] = False if 'parent_port' in parsed_args and parsed_args.parent_port is not None: - port_id = network_client.find_port(parsed_args.parent_port)['id'] + port_id = network_client.find_port( + parsed_args.parent_port, + ignore_missing=False, + ).id attrs['port_id'] = port_id if 'add_subports' in parsed_args and parsed_args.add_subports is not None: attrs[SUB_PORTS] = _format_subports( @@ -384,7 +402,10 @@ def _format_subports(network_client, subports): for subport in subports: subport_attrs = {} if subport.get('port'): - port_id = network_client.find_port(subport['port'])['id'] + port_id = network_client.find_port( + subport['port'], + ignore_missing=False, + ).id subport_attrs['port_id'] = port_id if subport.get('segmentation-id'): try: @@ -413,11 +434,10 @@ def _get_attrs_for_subports(network_client, parsed_args): ): subports_list = [] for subport in parsed_args.unset_subports: - port_id = network_client.find_port(subport)['id'] + port_id = network_client.find_port( + subport, + ignore_missing=False, + )['id'] subports_list.append({'port_id': port_id}) attrs = subports_list return attrs - - -def _get_id(client, id_or_name, resource): - return client.find_resource(resource, str(id_or_name))['id'] diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 5cfc41db0..ff0e42a87 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -636,11 +636,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - client = self.app.client_manager.network - _network = client.find_network( + network_client = self.app.client_manager.network + network = network_client.find_network( parsed_args.network, ignore_missing=False ) - parsed_args.network = _network.id + parsed_args.network = network.id _prepare_fixed_ips(self.app.client_manager, parsed_args) attrs = _get_attrs(self.app.client_manager, parsed_args) @@ -654,7 +654,7 @@ def take_action(self, parsed_args): if parsed_args.security_groups is not None: attrs['security_group_ids'] = [ - client.find_security_group(sg, ignore_missing=False).id + network_client.find_security_group(sg, ignore_missing=False).id for sg in parsed_args.security_groups ] @@ -667,7 +667,7 @@ def take_action(self, parsed_args): attrs["extra_dhcp_opts"] = _convert_extra_dhcp_options(parsed_args) if parsed_args.qos_policy: - attrs['qos_policy_id'] = client.find_qos_policy( + attrs['qos_policy_id'] = network_client.find_qos_policy( parsed_args.qos_policy, ignore_missing=False ).id @@ -675,7 +675,9 @@ def take_action(self, parsed_args): _validate_port_hints(parsed_args.hint) expanded_hints = _expand_port_hint_aliases(parsed_args.hint) try: - client.find_extension('port-hints', ignore_missing=False) + network_client.find_extension( + 'port-hints', ignore_missing=False + ) except Exception as e: msg = _('Not supported by Network API: %(e)s') % {'e': e} raise exceptions.CommandError(msg) @@ -686,7 +688,7 @@ def take_action(self, parsed_args): in expanded_hints['openvswitch']['other_config'] ): try: - client.find_extension( + network_client.find_extension( 'port-hint-ovs-tx-steering', ignore_missing=False ) except Exception as e: @@ -695,7 +697,9 @@ def take_action(self, parsed_args): attrs['hints'] = expanded_hints set_tags_in_post = bool( - client.find_extension('tag-ports-during-bulk-creation') + network_client.find_extension( + 'tag-ports-during-bulk-creation', ignore_missing=True + ) ) if set_tags_in_post: if parsed_args.no_tag: @@ -707,14 +711,12 @@ def take_action(self, parsed_args): self._parse_extra_properties(parsed_args.extra_properties) ) - with common.check_missing_extension_if_error( - self.app.client_manager.network, attrs - ): - obj = client.create_port(**attrs) + with common.check_missing_extension_if_error(network_client, attrs): + obj = network_client.create_port(**attrs) if not set_tags_in_post: # tags cannot be set when created, so tags need to be set later. - _tag.update_tags_for_set(client, obj, parsed_args) + _tag.update_tags_for_set(network_client, obj, parsed_args) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters=_formatters) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 67e3de06f..29dfcce89 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -89,7 +89,12 @@ def _get_columns(item): def is_multiple_gateways_supported(n_client): - return n_client.find_extension("external-gateway-multihoming") is not None + return ( + n_client.find_extension( + "external-gateway-multihoming", ignore_missing=True + ) + is not None + ) def _passed_multiple_gateways(extension_supported, external_gateways): @@ -236,7 +241,9 @@ def _get_attrs(client_manager, parsed_args): # "router set" command doesn't support setting flavor_id. if 'flavor_id' in parsed_args and parsed_args.flavor_id is not None: - flavor = n_client.find_flavor(parsed_args.flavor_id) + flavor = n_client.find_flavor( + parsed_args.flavor_id, ignore_missing=False + ) attrs['flavor_id'] = flavor.id elif 'flavor' in parsed_args and parsed_args.flavor is not None: flavor = n_client.find_flavor(parsed_args.flavor, ignore_missing=False) diff --git a/openstackclient/tests/unit/identity/v3/test_application_credential.py b/openstackclient/tests/unit/identity/v3/test_application_credential.py index 3bc2d0c29..a7307d6ee 100644 --- a/openstackclient/tests/unit/identity/v3/test_application_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py @@ -302,7 +302,7 @@ def test_delete_multi_app_creds_with_exception(self): calls = [] for a in arglist: - calls.append(call(user_id, a)) + calls.append(call(user_id, a, ignore_missing=False)) self.identity_sdk_client.find_application_credential.assert_has_calls( calls diff --git a/openstackclient/tests/unit/identity/v3/test_domain.py b/openstackclient/tests/unit/identity/v3/test_domain.py index abe8076af..cc0593d1f 100644 --- a/openstackclient/tests/unit/identity/v3/test_domain.py +++ b/openstackclient/tests/unit/identity/v3/test_domain.py @@ -520,7 +520,7 @@ def test_domain_show(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) self.identity_sdk_client.find_domain.assert_called_with( - self.domain.id, + self.domain.id, ignore_missing=False ) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/unit/identity/v3/test_endpoint.py b/openstackclient/tests/unit/identity/v3/test_endpoint.py index 84e10e55a..ba69317f5 100644 --- a/openstackclient/tests/unit/identity/v3/test_endpoint.py +++ b/openstackclient/tests/unit/identity/v3/test_endpoint.py @@ -678,7 +678,7 @@ def test_endpoint_show(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) self.identity_sdk_client.find_endpoint.assert_called_with( - self.endpoint.id, + self.endpoint.id, ignore_missing=False ) collist = ( diff --git a/openstackclient/tests/unit/identity/v3/test_group.py b/openstackclient/tests/unit/identity/v3/test_group.py index d212e4565..598402e07 100644 --- a/openstackclient/tests/unit/identity/v3/test_group.py +++ b/openstackclient/tests/unit/identity/v3/test_group.py @@ -253,7 +253,7 @@ def test_group_create_or_show(self): columns, data = self.cmd.take_action(parsed_args) self.identity_sdk_client.find_group.assert_called_once_with( - self.group.name + self.group.name, ignore_missing=False ) self.assertEqual(self.columns, columns) datalist = ( @@ -286,7 +286,9 @@ def test_group_create_or_show_with_domain(self): columns, data = self.cmd.take_action(parsed_args) self.identity_sdk_client.find_group.assert_called_once_with( - self.group_with_options.name, domain_id=self.domain.id + self.group_with_options.name, + domain_id=self.domain.id, + ignore_missing=False, ) self.assertEqual(self.columns, columns) datalist = ( diff --git a/openstackclient/tests/unit/network/v2/test_network_trunk.py b/openstackclient/tests/unit/network/v2/test_network_trunk.py index 3b6c364d3..1056c21c3 100644 --- a/openstackclient/tests/unit/network/v2/test_network_trunk.py +++ b/openstackclient/tests/unit/network/v2/test_network_trunk.py @@ -797,7 +797,9 @@ def test_set_trunk_add_subport_with_exception(self): exceptions.CommandError ) - self.network_client.find_port.side_effect = [{'id': 'invalid_subport'}] + self.network_client.find_port.side_effect = [ + network_fakes.create_one_port({'id': 'invalid_subport'}) + ] with testtools.ExpectedException(exceptions.CommandError) as e: self.cmd.take_action(parsed_args) diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 221fe67a9..6ebb7809e 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -176,7 +176,7 @@ def setUp(self): self.network_client.set_tags.return_value = None self.network_client.find_extension.side_effect = ( - lambda name: self._extensions.get(name) + lambda name, ignore_missing=True: self._extensions.get(name) ) # Get the command object to test self.cmd = router.CreateRouter(self.app, None) @@ -1317,7 +1317,7 @@ def setUp(self): self.network_client.find_subnet.return_value = self._subnet self.network_client.find_extension.side_effect = ( - lambda name: self._extensions.get(name) + lambda name, ignore_missing=True: self._extensions.get(name) ) # Get the command object to test self.cmd = router.SetRouter(self.app, None) @@ -1956,7 +1956,7 @@ def setUp(self): self.network_client.set_tags.return_value = None self._extensions = {'fake': network_fakes.create_one_extension()} self.network_client.find_extension.side_effect = ( - lambda name: self._extensions.get(name) + lambda name, ignore_missing=True: self._extensions.get(name) ) self.network_client.remove_external_gateways.return_value = None @@ -2158,7 +2158,7 @@ def setUp(self): ) } self.network_client.find_extension.side_effect = ( - lambda name: self._extensions.get(name) + lambda name, ignore_missing=True: self._extensions.get(name) ) self.network_client.find_router.return_value = self._router diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index 7d4fba92d..e7bbb6999 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -565,7 +565,9 @@ def test_backup_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_sdk_client.find_backup.assert_called_with(self.backup.id) + self.volume_sdk_client.find_backup.assert_called_with( + self.backup.id, ignore_missing=False + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index 55cde32f1..30e67e296 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -453,7 +453,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume - backup = volume_client.find_backup(parsed_args.backup) + backup = volume_client.find_backup( + parsed_args.backup, ignore_missing=False + ) columns: tuple[str, ...] = ( "availability_zone", "container", From db6c34c2c78ad4c5939afce8e1fa954de9dcbf5e Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 13 Nov 2025 11:58:51 +0000 Subject: [PATCH 220/245] hacking: Check for missing ignore_missing calls This comes up in reviews frequently. Let's automate it. Change-Id: Ia7ebd7cf29fe4550b22921e898bebaaa5f7bb4f6 Signed-off-by: Stephen Finucane --- hacking/checks.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++- tox.ini | 1 + 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/hacking/checks.py b/hacking/checks.py index 98b40370d..facb0cc6a 100644 --- a/hacking/checks.py +++ b/hacking/checks.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +import ast import os import re @@ -74,7 +75,7 @@ def assert_no_duplicated_setup(logical_line, filename): @core.flake8ext -def assert_use_of_client_aliases(logical_line, filename): +def assert_use_of_client_aliases(logical_line): """Ensure we use $service_client instead of $sdk_connection.service. O402 @@ -106,3 +107,73 @@ def assert_use_of_client_aliases(logical_line, filename): f"O402: {match.group(1)} is already a mock: there's no need to " f"assign a new mock.Mock instance.", ) + + +class SDKProxyFindChecker(ast.NodeVisitor): + """NodeVisitor to find ``*_client.find_*`` statements.""" + + def __init__(self): + self.error = False + + def visit_Call(self, node): + # No need to keep visiting the AST if we already found something. + if self.error: + return + + self.generic_visit(node) + + if not ( + isinstance(node.func, ast.Attribute) + and node.func.attr.startswith('find_') # and + # isinstance(node.func.value, ast.Attribute) and + # node.func.value.attr.endswith('_client') + ): + # print(f'skipping: got {node.func}') + return + + if not ( + ( + # handle calls like 'identity_client.find_project' + isinstance(node.func.value, ast.Name) + and node.func.value.id.endswith('client') + ) + or ( + # handle calls like 'self.app.client_manager.image.find_image' + isinstance(node.func.value, ast.Attribute) + and node.func.value.attr + in ('identity', 'network', 'image', 'compute') + ) + ): + return + + if not any(kw.arg == 'ignore_missing' for kw in node.keywords): + self.error = True + + +@core.flake8ext +def assert_find_ignore_missing_kwargs(logical_line, filename): + """Ensure ignore_missing is always used for ``find_*`` SDK proxy calls. + + Okay: self.compute_client.find_server(foo, ignore_missing=True) + Okay: self.image_client.find_server(foo, ignore_missing=False) + Okay: self.volume_client.volumes.find(name='foo') + O403: self.network_client.find_network(parsed_args.network) + O403: self.compute_client.find_flavor(flavor_id, get_extra_specs=True) + """ + if 'tests' in filename: + return + + checker = SDKProxyFindChecker() + try: + parsed_logical_line = ast.parse(logical_line) + except SyntaxError: + # let flake8 catch this itself + # https://github.com/PyCQA/flake8/issues/1948 + return + checker.visit(parsed_logical_line) + if checker.error: + yield ( + 0, + 'O403: Calls to find_* proxy methods must explicitly set ' + 'ignore_missing', + ) diff --git a/tox.ini b/tox.ini index 2ee2dc2ca..4392a36a8 100644 --- a/tox.ini +++ b/tox.ini @@ -125,4 +125,5 @@ extension = O400 = checks:assert_no_oslo O401 = checks:assert_no_duplicated_setup O402 = checks:assert_use_of_client_aliases + O403 = checks:assert_find_ignore_missing_kwargs paths = ./hacking From aa87db6a932461581c4b55b21f6718c9df45809f Mon Sep 17 00:00:00 2001 From: seheonnn Date: Tue, 16 Sep 2025 11:10:07 +0900 Subject: [PATCH 221/245] tests: Update functional test for image metadef object - Add functional tests for 'openstack image metadef object' - Drop '-f json' where parse_output=True - Address review comments Change-Id: Ibbb8bcdebc7e751f2bd220240eb47b4780e331f6 Signed-off-by: seheonnn --- .../image/v2/test_metadef_objects.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 openstackclient/tests/functional/image/v2/test_metadef_objects.py diff --git a/openstackclient/tests/functional/image/v2/test_metadef_objects.py b/openstackclient/tests/functional/image/v2/test_metadef_objects.py new file mode 100644 index 000000000..5216c933f --- /dev/null +++ b/openstackclient/tests/functional/image/v2/test_metadef_objects.py @@ -0,0 +1,69 @@ +# 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. + +from openstackclient.tests.functional import base + + +class MetadefObjectTests(base.TestCase): + def setUp(self): + super().setUp() + self.obj_name = self.getUniqueString('metadef-obj') + self.ns_name = self.getUniqueString('metadef-ns') + self.openstack(f"image metadef namespace create {self.ns_name}") + self.addCleanup( + lambda: self.openstack( + f"image metadef namespace delete {self.ns_name}" + ) + ) + + def test_metadef_objects(self): + # CREATE + created = self.openstack( + ( + "image metadef object create " + f"--namespace {self.ns_name} " + f"{self.obj_name}" + ), + parse_output=True, + ) + self.addCleanup( + lambda: self.openstack( + f"image metadef object delete {self.ns_name} {self.obj_name}" + ) + ) + self.assertEqual(self.obj_name, created["name"]) + self.assertEqual(self.ns_name, created["namespace_name"]) + + # UPDATE + new_name = f"{self.obj_name}-updated" + self.openstack( + "image metadef object update " + f"{self.ns_name} {self.obj_name} " + f"--name {new_name}" + ) + self.obj_name = new_name + + # READ (get) + shown = self.openstack( + f"image metadef object show {self.ns_name} {self.obj_name}", + parse_output=True, + ) + self.assertEqual(self.obj_name, shown["name"]) + self.assertEqual(self.ns_name, shown["namespace_name"]) + + # READ (list) + rows = self.openstack( + f"image metadef object list {self.ns_name}", + parse_output=True, + ) + names = {row["name"] for row in rows} + self.assertIn(self.obj_name, names) From c17c5f0df61ad36876c63192c888f27141905af0 Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Tue, 18 Nov 2025 19:36:28 -0500 Subject: [PATCH 222/245] Try to make help text of network code consistent Just change all text to be as consistent as possible. TrivialFix Change-Id: I959cda9b0688f0fcec0f55ce4c8cadf209d3537f Signed-off-by: Brian Haley --- openstackclient/network/v2/address_group.py | 5 +-- openstackclient/network/v2/address_scope.py | 12 ++++--- .../network/v2/default_security_group_rule.py | 7 ++-- openstackclient/network/v2/floating_ip.py | 25 ++++++++----- .../network/v2/floating_ip_port_forwarding.py | 13 ++++--- openstackclient/network/v2/ip_availability.py | 9 +++-- .../network/v2/l3_conntrack_helper.py | 13 +++++-- openstackclient/network/v2/local_ip.py | 18 ++++++---- .../network/v2/local_ip_association.py | 9 +++-- openstackclient/network/v2/ndp_proxy.py | 17 ++++++--- openstackclient/network/v2/network.py | 35 +++++++++++-------- openstackclient/network/v2/network_agent.py | 4 +-- .../network/v2/network_qos_policy.py | 7 ++-- openstackclient/network/v2/network_rbac.py | 11 +++--- openstackclient/network/v2/network_segment.py | 2 +- .../network/v2/network_segment_range.py | 15 +++++--- openstackclient/network/v2/network_trunk.py | 2 +- openstackclient/network/v2/port.py | 10 +++--- openstackclient/network/v2/router.py | 8 +++-- openstackclient/network/v2/security_group.py | 9 +++-- .../network/v2/security_group_rule.py | 16 ++++++--- openstackclient/network/v2/subnet.py | 27 +++++++------- openstackclient/network/v2/subnet_pool.py | 18 +++++----- 23 files changed, 182 insertions(+), 110 deletions(-) diff --git a/openstackclient/network/v2/address_group.py b/openstackclient/network/v2/address_group.py index d47c34cdd..53a91561a 100644 --- a/openstackclient/network/v2/address_group.py +++ b/openstackclient/network/v2/address_group.py @@ -151,13 +151,14 @@ def get_parser(self, prog_name): parser.add_argument( '--name', metavar='', - help=_("List only address groups of given name in output"), + help=_("List only address groups with the specified name"), ) parser.add_argument( '--project', metavar="", help=_( - "List address groups according to their project (name or ID)" + "List only address groups with the specified project " + "(name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py index a7f396b73..f61dcff55 100644 --- a/openstackclient/network/v2/address_scope.py +++ b/openstackclient/network/v2/address_scope.py @@ -160,7 +160,7 @@ def get_parser(self, prog_name): parser.add_argument( '--name', metavar='', - help=_("List only address scopes of given name in output"), + help=_("List only address scopes with the specified name"), ) parser.add_argument( '--ip-version', @@ -169,14 +169,16 @@ def get_parser(self, prog_name): metavar='', dest='ip_version', help=_( - "List address scopes of given IP version networks (4 or 6)" + "List only address scopes with the specified IP version " + "networks (4 or 6)" ), ) parser.add_argument( '--project', metavar="", help=_( - "List address scopes according to their project (name or ID)" + "List only address scopes with the specified project " + "(name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -185,12 +187,12 @@ def get_parser(self, prog_name): shared_group.add_argument( '--share', action='store_true', - help=_("List address scopes shared between projects"), + help=_("List only address scopes shared between projects"), ) shared_group.add_argument( '--no-share', action='store_true', - help=_("List address scopes not shared between projects"), + help=_("List only address scopes not shared between projects"), ) return parser diff --git a/openstackclient/network/v2/default_security_group_rule.py b/openstackclient/network/v2/default_security_group_rule.py index 0a16a11a7..0f9441488 100644 --- a/openstackclient/network/v2/default_security_group_rule.py +++ b/openstackclient/network/v2/default_security_group_rule.py @@ -300,7 +300,8 @@ def get_parser(self, prog_name): metavar='', type=network_utils.convert_to_lowercase, help=_( - "List rules by the IP protocol (ah, dhcp, egp, esp, gre, " + "List only default rules with the specified IP protocol " + "(ah, dhcp, egp, esp, gre, " "icmp, igmp, ipv6-encap, ipv6-frag, ipv6-icmp, " "ipv6-nonxt, ipv6-opts, ipv6-route, ospf, pgm, rsvp, " "sctp, tcp, udp, udplite, vrrp and integer " @@ -319,7 +320,7 @@ def get_parser(self, prog_name): '--ingress', action='store_true', help=_( - "List default rules which will be applied to incoming " + "List only default rules which will be applied to incoming " "network traffic" ), ) @@ -327,7 +328,7 @@ def get_parser(self, prog_name): '--egress', action='store_true', help=_( - "List default rules which will be applied to outgoing " + "List only default rules which will be applied to outgoing " "network traffic" ), ) diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index 0b2f7fa98..76b91a0ec 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -247,7 +247,7 @@ def update_parser_network(self, parser): action='append', help=self.enhance_help_neutron( _( - "List floating IP(s) according to given network " + "List only floating IP(s) with the specified network " "(name or ID) " "(repeat option to fiter on multiple networks)" ) @@ -260,7 +260,8 @@ def update_parser_network(self, parser): action='append', help=self.enhance_help_neutron( _( - "List floating IP(s) according to given port (name or ID) " + "List only floating IP(s) with the specified port " + "(name or ID) " "(repeat option to fiter on multiple ports)" ) ), @@ -269,14 +270,20 @@ def update_parser_network(self, parser): '--fixed-ip-address', metavar='', help=self.enhance_help_neutron( - _("List floating IP(s) according to given fixed IP address") + _( + "List only floating IP(s) with the specified fixed IP " + "address" + ) ), ) parser.add_argument( '--floating-ip-address', metavar='', help=self.enhance_help_neutron( - _("List floating IP(s) according to given floating IP address") + _( + "List only floating IP(s) with the specified floating IP " + "address" + ) ), ) parser.add_argument( @@ -285,8 +292,8 @@ def update_parser_network(self, parser): choices=['ACTIVE', 'DOWN'], help=self.enhance_help_neutron( _( - "List floating IP(s) according to given status ('ACTIVE', " - "'DOWN')" + "List only floating IP(s) with the specified status " + "('ACTIVE', 'DOWN')" ) ), ) @@ -295,8 +302,8 @@ def update_parser_network(self, parser): metavar='', help=self.enhance_help_neutron( _( - "List floating IP(s) according to given project " - "(name or ID) " + "List only floating IP(s) with the specified project " + "(name or ID)" ) ), ) @@ -308,7 +315,7 @@ def update_parser_network(self, parser): action='append', help=self.enhance_help_neutron( _( - "List floating IP(s) according to given router " + "List only floating IP(s) with the specified router " "(name or ID) " "(repeat option to fiter on multiple routers)" ) diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py index 3810deddf..99546a1d5 100644 --- a/openstackclient/network/v2/floating_ip_port_forwarding.py +++ b/openstackclient/network/v2/floating_ip_port_forwarding.py @@ -265,8 +265,8 @@ def get_parser(self, prog_name): '--port', metavar='', help=_( - "Filter the list result by the ID or name of " - "the internal network port" + "List only floating IP port forwardings with the " + "specified internal network port (name or ID)" ), ) parser.add_argument( @@ -274,14 +274,17 @@ def get_parser(self, prog_name): metavar='', dest='external_protocol_port', help=_( - "Filter the list result by the " - "protocol port number of the floating IP" + "List only floating IP port forwardings with the " + "specified external protocol port number" ), ) parser.add_argument( '--protocol', metavar='', - help=_("Filter the list result by the port protocol"), + help=_( + "List only floating IP port forwardings with the " + "specified protocol number" + ), ) return parser diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py index 51ea01199..e36b89771 100644 --- a/openstackclient/network/v2/ip_availability.py +++ b/openstackclient/network/v2/ip_availability.py @@ -47,14 +47,17 @@ def get_parser(self, prog_name): metavar='', dest='ip_version', help=_( - "List IP availability of given IP version " - "networks (default is 4)" + "List only IP availability with the specified IP version " + "networks (4 or 6, default is 4)" ), ) parser.add_argument( '--project', metavar='', - help=_("List IP availability of given project (name or ID)"), + help=_( + "List only IP availability with the specified project " + "(name or ID)" + ), ) identity_common.add_project_domain_option_to_parser(parser) return parser diff --git a/openstackclient/network/v2/l3_conntrack_helper.py b/openstackclient/network/v2/l3_conntrack_helper.py index 011db39e0..982021ec6 100644 --- a/openstackclient/network/v2/l3_conntrack_helper.py +++ b/openstackclient/network/v2/l3_conntrack_helper.py @@ -150,19 +150,26 @@ def get_parser(self, prog_name): parser.add_argument( '--helper', metavar='', - help=_('The netfilter conntrack helper module'), + help=_( + 'List only helpers using the specified netfilter conntrack ' + 'helper module' + ), ) parser.add_argument( '--protocol', metavar='', help=_( - 'The network protocol for the netfilter conntrack target rule' + 'List only helpers with the specified network protocol for ' + 'the netfilter conntrack target rule' ), ) parser.add_argument( '--port', metavar='', - help=_('The network port for the netfilter conntrack target rule'), + help=_( + 'List only helpers with the specified network port for ' + 'the netfilter conntrack target rule (name or ID)' + ), ) return parser diff --git a/openstackclient/network/v2/local_ip.py b/openstackclient/network/v2/local_ip.py index 7fd58818c..a2937937e 100644 --- a/openstackclient/network/v2/local_ip.py +++ b/openstackclient/network/v2/local_ip.py @@ -198,32 +198,38 @@ def get_parser(self, prog_name): parser.add_argument( '--name', metavar='', - help=_("List only Local IPs of given name in output"), + help=_("List only local IP(s) with the specified name"), ) parser.add_argument( '--project', metavar="", - help=_("List Local IPs according to their project (name or ID)"), + help=_( + "List only local IP(s) with the specified project (name or ID)" + ), ) parser.add_argument( '--network', metavar='', - help=_("List Local IP(s) according to given network (name or ID)"), + help=_( + "List only local IP(s) with the specified network (name or ID)" + ), ) parser.add_argument( '--local-port', metavar='', - help=_("List Local IP(s) according to given port (name or ID)"), + help=_( + "List only local IP(s) with the specified port (name or ID)" + ), ) parser.add_argument( '--local-ip-address', metavar='', - help=_("List Local IP(s) according to given Local IP Address"), + help=_("List only local IP(s) with the specified IP address"), ) parser.add_argument( '--ip-mode', metavar='', - help=_("List Local IP(s) according to given IP mode"), + help=_("List only local IP(s) with the specified IP mode"), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/local_ip_association.py b/openstackclient/network/v2/local_ip_association.py index c6b59184b..814bbdb0b 100644 --- a/openstackclient/network/v2/local_ip_association.py +++ b/openstackclient/network/v2/local_ip_association.py @@ -150,18 +150,21 @@ def get_parser(self, prog_name): '--fixed-port', metavar='', help=_( - "Filter the list result by the ID or name of the fixed port" + "List only local IP assocations with the specified fixed IP " + "port (name or ID)" ), ) parser.add_argument( '--fixed-ip', metavar='', - help=_("Filter the list result by fixed ip"), + help=_( + "List only local IP associations with the specified fixed IP" + ), ) parser.add_argument( '--host', metavar='', - help=_("Filter the list result by given host"), + help=_("List only local IP associations with the specified host"), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/ndp_proxy.py b/openstackclient/network/v2/ndp_proxy.py index fe97cd078..95e39ce15 100644 --- a/openstackclient/network/v2/ndp_proxy.py +++ b/openstackclient/network/v2/ndp_proxy.py @@ -143,30 +143,37 @@ def get_parser(self, prog_name): '--router', metavar='', help=_( - "List only NDP proxies belonging to this router (name or ID)" + "List only NDP proxies associated with the specifed router " + "(name or ID)" ), ) parser.add_argument( '--port', metavar='', help=_( - "List only NDP proxies associated to this port (name or ID)" + "List only NDP proxies associated with the specified port " + "(name or ID)" ), ) parser.add_argument( '--ip-address', metavar='', - help=_("List only NDP proxies associated to this IPv6 address"), + help=_( + "List only NDP proxies associated with the specified " + "IPv6 address" + ), ) parser.add_argument( '--project', metavar='', - help=_("List only NDP proxies of given project (name or ID)"), + help=_( + "List only NDP proxies with the specified project (name or ID)" + ), ) parser.add_argument( '--name', metavar='', - help=_("List only NDP proxies of given name"), + help=_("List only NDP proxies with the specified name"), ) identity_common.add_project_domain_option_to_parser(parser) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 33f7e04e4..7aefe50c3 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -469,12 +469,12 @@ def update_parser_network(self, parser): router_ext_group.add_argument( '--external', action='store_true', - help=self.enhance_help_neutron(_("List external networks")), + help=self.enhance_help_neutron(_("List only external networks")), ) router_ext_group.add_argument( '--internal', action='store_true', - help=self.enhance_help_neutron(_("List internal networks")), + help=self.enhance_help_neutron(_("List only internal networks")), ) parser.add_argument( '--long', @@ -487,24 +487,26 @@ def update_parser_network(self, parser): '--name', metavar='', help=self.enhance_help_neutron( - _("List networks according to their name") + _("List only networks with the specified name") ), ) admin_state_group = parser.add_mutually_exclusive_group() admin_state_group.add_argument( '--enable', action='store_true', - help=self.enhance_help_neutron(_("List enabled networks")), + help=self.enhance_help_neutron(_("List only enabled networks")), ) admin_state_group.add_argument( '--disable', action='store_true', - help=self.enhance_help_neutron(_("List disabled networks")), + help=self.enhance_help_neutron(_("List only disabled networks")), ) parser.add_argument( '--project', metavar='', - help=_("List networks according to their project (name or ID)"), + help=_( + "List only networks with the specified project (name or ID)" + ), ) identity_common.add_project_domain_option_to_parser( parser, enhance_help=self.enhance_help_neutron @@ -514,14 +516,14 @@ def update_parser_network(self, parser): '--share', action='store_true', help=self.enhance_help_neutron( - _("List networks shared between projects") + _("List only networks shared between projects") ), ) shared_group.add_argument( '--no-share', action='store_true', help=self.enhance_help_neutron( - _("List networks not shared between projects") + _("List only networks not shared between projects") ), ) parser.add_argument( @@ -530,7 +532,7 @@ def update_parser_network(self, parser): choices=['ACTIVE', 'BUILD', 'DOWN', 'ERROR'], help=self.enhance_help_neutron( _( - "List networks according to their status " + "List only networks with the specified status " "('ACTIVE', 'BUILD', 'DOWN', 'ERROR')" ) ), @@ -541,7 +543,8 @@ def update_parser_network(self, parser): choices=['flat', 'geneve', 'gre', 'local', 'vlan', 'vxlan'], help=self.enhance_help_neutron( _( - "List networks according to their physical mechanisms. " + "List only networks with the specified physical " + "mechanisms. " "The supported options are: flat, geneve, gre, local, " "vlan and vxlan." ) @@ -552,7 +555,10 @@ def update_parser_network(self, parser): metavar='', dest='physical_network', help=self.enhance_help_neutron( - _("List networks according to name of the physical network") + _( + "List only networks with the specified physical network " + "name" + ) ), ) parser.add_argument( @@ -561,8 +567,9 @@ def update_parser_network(self, parser): dest='segmentation_id', help=self.enhance_help_neutron( _( - "List networks according to VLAN ID for VLAN networks or " - "Tunnel ID for GENEVE/GRE/VXLAN networks" + "List only networks with the specified provider segment " + "ID (VLAN ID for VLAN networks or " + "Tunnel ID for GENEVE/GRE/VXLAN networks)" ) ), ) @@ -571,7 +578,7 @@ def update_parser_network(self, parser): metavar='', dest='agent_id', help=self.enhance_help_neutron( - _('List networks hosted by agent (ID only)') + _('List only networks hosted the specified agent (ID only)') ), ) _tag.add_tag_filtering_option_to_parser( diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py index 35b0fab66..cc4de2b42 100644 --- a/openstackclient/network/v2/network_agent.py +++ b/openstackclient/network/v2/network_agent.py @@ -205,12 +205,12 @@ def get_parser(self, prog_name): agent_type_group.add_argument( '--network', metavar='', - help=_('List agents hosting a network (name or ID)'), + help=_('List agents hosting the specified network (name or ID)'), ) agent_type_group.add_argument( '--router', metavar='', - help=_('List agents hosting this router (name or ID)'), + help=_('List agents hosting the specified router (name or ID)'), ) parser.add_argument( '--long', diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py index 1a96f939e..abdd044af 100644 --- a/openstackclient/network/v2/network_qos_policy.py +++ b/openstackclient/network/v2/network_qos_policy.py @@ -190,7 +190,8 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - "List QoS policies according to their project (name or ID)" + "List only QoS policies with the specified project " + "(name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -198,12 +199,12 @@ def get_parser(self, prog_name): shared_group.add_argument( '--share', action='store_true', - help=_("List QoS policies shared between projects"), + help=_("List only QoS policies shared between projects"), ) shared_group.add_argument( '--no-share', action='store_true', - help=_("List QoS policies not shared between projects"), + help=_("List only QoS policies not shared between projects"), ) return parser diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py index 0df74375d..d266f352d 100644 --- a/openstackclient/network/v2/network_rbac.py +++ b/openstackclient/network/v2/network_rbac.py @@ -234,8 +234,8 @@ def get_parser(self, prog_name): 'network', ], help=_( - 'List network RBAC policies according to ' - 'given object type ("address_group", "address_scope", ' + 'List only network RBAC policies with the specified ' + 'object type ("address_group", "address_scope", ' '"security_group", "subnetpool", "qos_policy" or ' '"network")' ), @@ -245,14 +245,17 @@ def get_parser(self, prog_name): metavar='', choices=['access_as_external', 'access_as_shared'], help=_( - 'List network RBAC policies according to given ' + 'List only network RBAC policies with the specified ' 'action ("access_as_external" or "access_as_shared")' ), ) parser.add_argument( '--target-project', metavar='', - help=_('List network RBAC policies for a specific target project'), + help=_( + 'List only network RBAC policies with the specified ' + 'target project (name or ID)' + ), ) parser.add_argument( '--long', diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py index 5e40108ca..76a967ecc 100644 --- a/openstackclient/network/v2/network_segment.py +++ b/openstackclient/network/v2/network_segment.py @@ -160,7 +160,7 @@ def get_parser(self, prog_name): '--network', metavar='', help=_( - 'List network segments that belong to this ' + 'List only network segments associated with the specified ' 'network (name or ID)' ), ) diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py index e2716f13f..c9b9ccc23 100644 --- a/openstackclient/network/v2/network_segment_range.py +++ b/openstackclient/network/v2/network_segment_range.py @@ -315,25 +315,32 @@ def get_parser(self, prog_name): used_group.add_argument( '--used', action='store_true', - help=_('List network segment ranges that have segments in use'), + help=_( + 'List only network segment ranges that have segments in use' + ), ) used_group.add_argument( '--unused', action='store_true', help=_( - 'List network segment ranges that have segments not in use' + 'List only network segment ranges that have segments ' + 'not in use' ), ) available_group = parser.add_mutually_exclusive_group() available_group.add_argument( '--available', action='store_true', - help=_('List network segment ranges that have available segments'), + help=_( + 'List only network segment ranges that have available segments' + ), ) available_group.add_argument( '--unavailable', action='store_true', - help=_('List network segment ranges without available segments'), + help=_( + 'List only network segment ranges without available segments' + ), ) return parser diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index 53681b774..cd0d47ffd 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -285,7 +285,7 @@ def get_parser(self, prog_name): '--trunk', required=True, metavar="", - help=_("List subports belonging to this trunk (name or ID)"), + help=_("List only subports belonging to this trunk (name or ID)"), ) return parser diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index ff0e42a87..c28131440 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -810,7 +810,7 @@ def get_parser(self, prog_name): parser.add_argument( '--mac-address', metavar='', - help=_("List only ports with this MAC address"), + help=_("List only ports with the specified MAC address"), ) parser.add_argument( '--long', @@ -821,12 +821,12 @@ def get_parser(self, prog_name): parser.add_argument( '--project', metavar='', - help=_("List ports according to their project (name or ID)"), + help=_("List only ports with the specified project (name or ID)"), ) parser.add_argument( '--name', metavar='', - help=_("List ports according to their name"), + help=_("List only ports with the specified name"), ) parser.add_argument( '--security-group', @@ -844,7 +844,7 @@ def get_parser(self, prog_name): metavar='', choices=('ACTIVE', 'BUILD', 'DOWN', 'ERROR'), help=_( - "List ports according to their status " + "List only ports with the specified status " "('ACTIVE', 'BUILD', 'DOWN', 'ERROR')" ), ) @@ -861,7 +861,7 @@ def get_parser(self, prog_name): "Desired IP and/or subnet for filtering ports " "(name or ID): subnet=,ip-address=," "ip-substring= " - "(repeat option to set multiple fixed IP addresses)" + "(repeat option to filter multiple fixed IP addresses)" ), ) _tag.add_tag_filtering_option_to_parser(parser, _('ports')) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 29dfcce89..1ea85331c 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -726,13 +726,17 @@ def get_parser(self, prog_name): parser.add_argument( '--project', metavar='', - help=_("List routers according to their project (name or ID)"), + help=_( + "List only routers with the specified project (name or ID)" + ), ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--agent', metavar='', - help=_("List routers hosted by an agent (ID only)"), + help=_( + "List only routers hosted by the specified agent (ID only)" + ), ) _tag.add_tag_filtering_option_to_parser(parser, _('routers')) diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index 6fb98c6f6..c4b56a216 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -246,7 +246,10 @@ def update_parser_network(self, parser): '--project', metavar='', help=self.enhance_help_neutron( - _("List security groups according to the project (name or ID)") + _( + "List only security groups with the specified project " + "(name or ID)" + ) ), ) identity_common.add_project_domain_option_to_parser( @@ -259,14 +262,14 @@ def update_parser_network(self, parser): action='store_true', dest='shared', default=None, - help=_("List security groups shared between projects"), + help=_("List only security groups shared between projects"), ) shared_group.add_argument( '--no-share', action='store_false', dest='shared', default=None, - help=_("List security groups not shared between projects"), + help=_("List only security groups not shared between projects"), ) _tag.add_tag_filtering_option_to_parser( diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index 6cb0f7fa3..f6baac3cb 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -387,7 +387,8 @@ def update_parser_network(self, parser): type=network_utils.convert_to_lowercase, help=self.enhance_help_neutron( _( - "List rules by the IP protocol (ah, dhcp, egp, esp, gre, " + "List only rules with the specified IP protocol " + "(ah, dhcp, egp, esp, gre, " "icmp, igmp, ipv6-encap, ipv6-frag, ipv6-icmp, " "ipv6-nonxt, ipv6-opts, ipv6-route, ospf, pgm, rsvp, " "sctp, tcp, udp, udplite, vrrp and integer " @@ -401,7 +402,10 @@ def update_parser_network(self, parser): metavar='', type=network_utils.convert_to_lowercase, help=self.enhance_help_neutron( - _("List rules by the Ethertype (IPv4 or IPv6)") + _( + "List only rules with the specified Ethertype " + "(IPv4 or IPv6)" + ) ), ) direction_group = parser.add_mutually_exclusive_group() @@ -409,14 +413,14 @@ def update_parser_network(self, parser): '--ingress', action='store_true', help=self.enhance_help_neutron( - _("List rules applied to incoming network traffic") + _("List only rules applied to incoming network traffic") ), ) direction_group.add_argument( '--egress', action='store_true', help=self.enhance_help_neutron( - _("List rules applied to outgoing network traffic") + _("List only rules applied to outgoing network traffic") ), ) parser.add_argument( @@ -430,7 +434,9 @@ def update_parser_network(self, parser): parser.add_argument( '--project', metavar='', - help=self.enhance_help_neutron(_("Owner's project (name or ID)")), + help=self.enhance_help_neutron( + _("List only rules with the specified project (name or ID)") + ), ) identity_common.add_project_domain_option_to_parser( parser, enhance_help=self.enhance_help_neutron diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 30196bf86..7dc1df087 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -493,7 +493,7 @@ def get_parser(self, prog_name): metavar='', dest='ip_version', help=_( - "List only subnets of given IP version in output. " + "List only subnets with the specified IP version. " "Allowed values for IP version are 4 and 6." ), ) @@ -501,12 +501,12 @@ def get_parser(self, prog_name): dhcp_enable_group.add_argument( '--dhcp', action='store_true', - help=_("List subnets which have DHCP enabled"), + help=_("List only subnets which have DHCP enabled"), ) dhcp_enable_group.add_argument( '--no-dhcp', action='store_true', - help=_("List subnets which have DHCP disabled"), + help=_("List only subnets which have DHCP disabled"), ) parser.add_argument( '--service-type', @@ -514,7 +514,7 @@ def get_parser(self, prog_name): action='append', dest='service_types', help=_( - "List only subnets of a given service type in output, " + "List only subnets with the specified service type, " "for example, network:floatingip_agent_gateway. " "Must be a valid device owner value for a network port " "(repeat option to list multiple service types)." @@ -524,8 +524,7 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - "List only subnets which belong to a given project " - "in output (name or ID)" + "List only subnets with the specified project (name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) @@ -533,26 +532,26 @@ def get_parser(self, prog_name): '--network', metavar='', help=_( - "List only subnets which belong to a given network " - "in output (name or ID)" + "List only subnets which belong to the specified network " + "(name or ID)" ), ) parser.add_argument( '--gateway', metavar='', - help=_("List only subnets of given gateway IP in output"), + help=_("List only subnets with the specified gateway IP"), ) parser.add_argument( '--name', metavar='', - help=_("List only subnets of given name in output"), + help=_("List only subnets with the specified name"), ) parser.add_argument( '--subnet-range', metavar='', help=_( - "List only subnets of given subnet range " - "(in CIDR notation) in output. " + "List only subnets with the specified subnet range " + "(in CIDR notation). " "For example, --subnet-range 10.10.0.0/16" ), ) @@ -560,8 +559,8 @@ def get_parser(self, prog_name): '--subnet-pool', metavar='', help=_( - "List only subnets which belong to a given subnet pool " - "in output (name or ID)" + "List only subnets which belong to the specified subnet pool " + "(name or ID)" ), ) _tag.add_tag_filtering_option_to_parser(parser, _('subnets')) diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index 2917c4026..2f900a61d 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -278,26 +278,27 @@ def get_parser(self, prog_name): shared_group.add_argument( '--share', action='store_true', - help=_("List subnet pools shared between projects"), + help=_("List only subnet pools shared between projects"), ) shared_group.add_argument( '--no-share', action='store_true', - help=_("List subnet pools not shared between projects"), + help=_("List only subnet pools not shared between projects"), ) default_group = parser.add_mutually_exclusive_group() default_group.add_argument( '--default', action='store_true', help=_( - "List subnet pools used as the default external subnet pool" + "List only subnet pools used as the default external " + "subnet pool" ), ) default_group.add_argument( '--no-default', action='store_true', help=_( - "List subnet pools not used as the default external " + "List only subnet pools not used as the default external " "subnet pool" ), ) @@ -305,21 +306,22 @@ def get_parser(self, prog_name): '--project', metavar='', help=_( - "List subnet pools according to their project (name or ID)" + "List only subnet pools with the specified project " + "(name or ID)" ), ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--name', metavar='', - help=_("List only subnet pools of given name in output"), + help=_("List only subnet pools with the specified name"), ) parser.add_argument( '--address-scope', metavar='', help=_( - "List only subnet pools of given address scope " - "in output (name or ID)" + "List only subnet pools with the specified address scope " + "(name or ID)" ), ) _tag.add_tag_filtering_option_to_parser(parser, _('subnet pools')) From 04118056085042ca3359d7cf0600d43c4cacd87f Mon Sep 17 00:00:00 2001 From: 0weng Date: Tue, 18 Nov 2025 09:36:38 -0800 Subject: [PATCH 223/245] Change metavar name for `registered limit delete` Change registered limit argument to plural and remove `id` suffix for `registered limit delete` command. Also, note that service can be specified by name or ID in help description. Change-Id: I16950a5ac1a197761592304dcb71dcb09d608d78 Signed-off-by: 0weng --- .../identity/v3/registered_limit.py | 25 +++++++++++-------- .../unit/identity/v3/test_registered_limit.py | 4 +-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/openstackclient/identity/v3/registered_limit.py b/openstackclient/identity/v3/registered_limit.py index 34ce2101f..41b8cdac0 100644 --- a/openstackclient/identity/v3/registered_limit.py +++ b/openstackclient/identity/v3/registered_limit.py @@ -44,7 +44,10 @@ def get_parser(self, prog_name): '--service', metavar='', required=True, - help=_('Service responsible for the resource to limit (required)'), + help=_( + 'Service responsible for the resource to limit (required) ' + '(name or ID)' + ), ) parser.add_argument( '--default-limit', @@ -106,10 +109,10 @@ class DeleteRegisteredLimit(command.Command): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( - 'registered_limit_id', - metavar='', + 'registered_limits', + metavar='', nargs="+", - help=_('Registered limit to delete (ID)'), + help=_('Registered limit(s) to delete (ID)'), ) return parser @@ -117,7 +120,7 @@ def take_action(self, parsed_args): identity_client = self.app.client_manager.identity errors = 0 - for registered_limit_id in parsed_args.registered_limit_id: + for registered_limit_id in parsed_args.registered_limits: try: identity_client.registered_limits.delete(registered_limit_id) except Exception as e: @@ -134,7 +137,7 @@ def take_action(self, parsed_args): ) if errors > 0: - total = len(parsed_args.registered_limit_id) + total = len(parsed_args.registered_limits) msg = _( "%(errors)s of %(total)s registered limits failed to delete." ) % {'errors': errors, 'total': total} @@ -149,7 +152,9 @@ def get_parser(self, prog_name): parser.add_argument( '--service', metavar='', - help=_('Service responsible for the resource to limit'), + help=_( + 'Service responsible for the resource to limit (name or ID)' + ), ) parser.add_argument( '--resource-name', @@ -228,9 +233,9 @@ def get_parser(self, prog_name): '--service', metavar='', help=_( - 'Service to be updated responsible for the resource to ' - 'limit. Either --service, --resource-name or --region must ' - 'be different than existing value otherwise it will be ' + 'Service to be updated responsible for the resource to limit ' + '(name or ID). Either --service, --resource-name or --region ' + 'must be different than existing value otherwise it will be ' 'duplicate entry' ), ) diff --git a/openstackclient/tests/unit/identity/v3/test_registered_limit.py b/openstackclient/tests/unit/identity/v3/test_registered_limit.py index 792096cde..a120714ec 100644 --- a/openstackclient/tests/unit/identity/v3/test_registered_limit.py +++ b/openstackclient/tests/unit/identity/v3/test_registered_limit.py @@ -168,7 +168,7 @@ def test_registered_limit_delete(self): arglist = [identity_fakes.registered_limit_id] verifylist = [ - ('registered_limit_id', [identity_fakes.registered_limit_id]) + ('registered_limits', [identity_fakes.registered_limit_id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -184,7 +184,7 @@ def test_registered_limit_delete_with_exception(self): self.registered_limit_mock.delete.side_effect = return_value arglist = ['fake-registered-limit-id'] - verifylist = [('registered_limit_id', ['fake-registered-limit-id'])] + verifylist = [('registered_limits', ['fake-registered-limit-id'])] parsed_args = self.check_parser(self.cmd, arglist, verifylist) try: From 6366763ce4b73ad5b13e3cca18319ae1d8dfb330 Mon Sep 17 00:00:00 2001 From: asdasd7183 Date: Wed, 20 Aug 2025 21:26:38 +0900 Subject: [PATCH 224/245] Add functional test for volume snapshot This patch adds functional test support unset and show commands from volume snapshot and refactors existing test methods by combining them into a single method. Change-Id: I567bdfad6ce8ee6098d6e4c270bc200ff53ae4f7 Signed-off-by: asdasd7183 --- .../volume/v3/test_volume_snapshot.py | 178 +++++------------- 1 file changed, 46 insertions(+), 132 deletions(-) diff --git a/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py b/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py index 2c9b72ba1..b84bb0368 100644 --- a/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py +++ b/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py @@ -23,7 +23,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests): @classmethod def setUpClass(cls): super().setUpClass() - # create a volume for all tests to create snapshot + # create a test volume used by all snapshot tests cmd_output = cls.openstack( 'volume create ' + '--size 1 ' + cls.VOLLY, parse_output=True, @@ -40,147 +40,57 @@ def tearDownClass(cls): finally: super().tearDownClass() - def test_volume_snapshot_delete(self): - """Test create, delete multiple""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.assertEqual( - name1, - cmd_output["name"], - ) + def test_volume_snapshot(self): + # create volume snapshot + name = uuid.uuid4().hex - name2 = uuid.uuid4().hex cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, + 'volume snapshot create ' + + '--volume ' + + self.VOLLY + + ' --description aaaa ' + + '--property Alpha=a ' + + name, parse_output=True, ) - self.assertEqual( - name2, - cmd_output["name"], - ) - - self.wait_for_status('volume snapshot', name1, 'available') - self.wait_for_status('volume snapshot', name2, 'available') + snap_id = cmd_output['id'] - del_output = self.openstack( - 'volume snapshot delete ' + name1 + ' ' + name2 + self.addCleanup(self.wait_for_delete, 'volume snapshot', snap_id) + # delete volume snapshot + self.addCleanup( + self.openstack, + 'volume snapshot delete ' + snap_id, ) - self.assertOutput('', del_output) - self.wait_for_delete('volume snapshot', name1) - self.wait_for_delete('volume snapshot', name2) + self.wait_for_status('volume snapshot', snap_id, 'available') - def test_volume_snapshot_list(self): - """Test create, list filter""" - name1 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, + # show volume snapshot + snapshot_info = self.openstack( + 'volume snapshot show ' + name, parse_output=True, ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', name1) - self.addCleanup(self.openstack, 'volume snapshot delete ' + name1) - self.assertEqual( - name1, - cmd_output["name"], - ) - self.assertEqual( - self.VOLUME_ID, - cmd_output["volume_id"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status('volume snapshot', name1, 'available') - name2 = uuid.uuid4().hex - cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', name2) - self.addCleanup(self.openstack, 'volume snapshot delete ' + name2) - self.assertEqual( - name2, - cmd_output["name"], - ) - self.assertEqual( - self.VOLUME_ID, - cmd_output["volume_id"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.wait_for_status('volume snapshot', name2, 'available') - raw_output = self.openstack( - 'volume snapshot set ' + '--state error ' + name2 - ) - self.assertOutput('', raw_output) + self.assertEqual(name, snapshot_info['name']) + self.assertEqual('aaaa', snapshot_info["description"]) + self.assertEqual({'Alpha': 'a'}, snapshot_info["properties"]) - # Test list --long, --status + # list volume snapshot --name cmd_output = self.openstack( - 'volume snapshot list ' + '--long ' + '--status error', + 'volume snapshot list --name ' + name, parse_output=True, ) - names = [x["Name"] for x in cmd_output] - self.assertNotIn(name1, names) - self.assertIn(name2, names) + names = [x['Name'] for x in cmd_output] + self.assertIn(name, names) - # Test list --volume + # list volume snapshot --volume cmd_output = self.openstack( 'volume snapshot list ' + '--volume ' + self.VOLLY, parse_output=True, ) names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertIn(name2, names) + self.assertIn(name, names) - # Test list --name - cmd_output = self.openstack( - 'volume snapshot list ' + '--name ' + name1, - parse_output=True, - ) - names = [x["Name"] for x in cmd_output] - self.assertIn(name1, names) - self.assertNotIn(name2, names) - - def test_volume_snapshot_set(self): - """Test create, set, unset, show, delete volume snapshot""" - name = uuid.uuid4().hex + # set volume snapshot new_name = name + "_" - cmd_output = self.openstack( - 'volume snapshot create ' - + '--volume ' - + self.VOLLY - + ' --description aaaa ' - + '--property Alpha=a ' - + name, - parse_output=True, - ) - self.addCleanup(self.wait_for_delete, 'volume snapshot', new_name) - self.addCleanup(self.openstack, 'volume snapshot delete ' + new_name) - self.assertEqual( - name, - cmd_output["name"], - ) - self.assertEqual( - 1, - cmd_output["size"], - ) - self.assertEqual( - 'aaaa', - cmd_output["description"], - ) - self.assertEqual( - {'Alpha': 'a'}, - cmd_output["properties"], - ) - self.wait_for_status('volume snapshot', name, 'available') - - # Test volume snapshot set raw_output = self.openstack( 'volume snapshot set ' + '--name ' @@ -188,11 +98,10 @@ def test_volume_snapshot_set(self): + ' --description bbbb ' + '--property Alpha=c ' + '--property Beta=b ' - + name, + + snap_id, ) self.assertOutput('', raw_output) - # Show snapshot set result cmd_output = self.openstack( 'volume snapshot show ' + new_name, parse_output=True, @@ -201,10 +110,6 @@ def test_volume_snapshot_set(self): new_name, cmd_output["name"], ) - self.assertEqual( - 1, - cmd_output["size"], - ) self.assertEqual( 'bbbb', cmd_output["description"], @@ -214,7 +119,7 @@ def test_volume_snapshot_set(self): cmd_output["properties"], ) - # Test volume snapshot unset + # unset volume snapshot raw_output = self.openstack( 'volume snapshot unset ' + '--property Alpha ' + new_name, ) @@ -229,16 +134,25 @@ def test_volume_snapshot_set(self): cmd_output["properties"], ) - # Test volume snapshot set --no-property + # set volume snapshot --no-property, --state error raw_output = self.openstack( - 'volume snapshot set ' + '--no-property ' + new_name, + 'volume snapshot set ' + + '--no-property ' + + '--state error ' + + new_name, ) self.assertOutput('', raw_output) + cmd_output = self.openstack( 'volume snapshot show ' + new_name, parse_output=True, ) - self.assertNotIn( - {'Beta': 'b'}, - cmd_output["properties"], + self.assertEqual({}, cmd_output["properties"]) + + # list volume snapshot --long --status + cmd_output = self.openstack( + 'volume snapshot list ' + '--long ' + '--status error', + parse_output=True, ) + names = [x["Name"] for x in cmd_output] + self.assertIn(new_name, names) From 4132ca1818e894c9e5ca9482b4a82b8baaed4377 Mon Sep 17 00:00:00 2001 From: Luan Utimura Date: Thu, 28 Aug 2025 16:11:05 -0300 Subject: [PATCH 225/245] volume: Add missing backup_id field in tests This change also reverts commit: * 5f1ffe742cd8afb2c93927ff606d88a90fd6073e Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/958801 Change-Id: Icac78179bc324e6fbe762f8095f2cba490ef6aea Signed-off-by: Luan Utimura --- openstackclient/tests/unit/volume/v3/test_volume.py | 4 ++++ openstackclient/volume/v3/volume.py | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index 5fb35a067..33dcfe5a4 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -37,6 +37,7 @@ class TestVolumeCreate(volume_fakes.TestVolume): columns = ( 'attachments', 'availability_zone', + 'backup_id', 'bootable', 'cluster_name', 'consistencygroup_id', @@ -78,6 +79,7 @@ def setUp(self): self.datalist = ( self.volume.attachments, self.volume.availability_zone, + self.volume.backup_id, self.volume.is_bootable, self.volume.cluster_name, self.volume.consistency_group_id, @@ -2011,6 +2013,7 @@ def setUp(self): self.columns = ( 'attachments', 'availability_zone', + 'backup_id', 'bootable', 'cluster_name', 'consistencygroup_id', @@ -2045,6 +2048,7 @@ def setUp(self): self.data = ( self.volume.attachments, self.volume.availability_zone, + self.volume.backup_id, self.volume.is_bootable, self.volume.cluster_name, self.volume.consistency_group_id, diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index d43a77d7b..54dd5c754 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -107,8 +107,6 @@ def _format_volume(volume: _volume.Volume) -> dict[str, ty.Any]: 'os-volume-replication:extended_status', # unnecessary columns 'links', - # temporarily ignored columns - 'backup_id', } optional_columns = { # only present if part of a consistency group From fb6dad48db5a802be8d36bba6d7d34618bdaa12a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 4 Nov 2025 17:33:26 +0000 Subject: [PATCH 226/245] Remove duplicate test utilities We cannot remove them fully, but we can remove a lot of them. Further cleanup is needed here to remove the references but that will be done once a version of osc_lib with fixes is included. Change-Id: Ifd200bd3d3e5c02c239a8ad0e6cee6d823e26544 Signed-off-by: Stephen Finucane --- .../tests/unit/common/test_module.py | 21 ++- openstackclient/tests/unit/fakes.py | 169 ++++-------------- .../tests/unit/identity/v3/fakes.py | 12 +- 3 files changed, 59 insertions(+), 143 deletions(-) diff --git a/openstackclient/tests/unit/common/test_module.py b/openstackclient/tests/unit/common/test_module.py index 476538a19..839620368 100644 --- a/openstackclient/tests/unit/common/test_module.py +++ b/openstackclient/tests/unit/common/test_module.py @@ -11,7 +11,6 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# """Test module module""" @@ -19,10 +18,18 @@ from unittest import mock from openstackclient.common import module as osc_module -from openstackclient.tests.unit import fakes from openstackclient.tests.unit import utils +class FakeModule: + def __init__(self, name, version): + self.name = name + self.__version__ = version + # Workaround for openstacksdk case + self.version = mock.Mock() + self.version.__version__ = version + + # NOTE(dtroyer): module_1 must match the version list filter (not --all) # currently == '*client*' module_name_1 = 'fakeclient' @@ -45,11 +52,11 @@ MODULES = { 'sys': sys, - module_name_1: fakes.FakeModule(module_name_1, module_version_1), - module_name_2: fakes.FakeModule(module_name_2, module_version_2), - module_name_3: fakes.FakeModule(module_name_3, module_version_3), - module_name_4: fakes.FakeModule(module_name_4, module_version_4), - module_name_5: fakes.FakeModule(module_name_5, module_version_5), + module_name_1: FakeModule(module_name_1, module_version_1), + module_name_2: FakeModule(module_name_2, module_version_2), + module_name_3: FakeModule(module_name_3, module_version_3), + module_name_4: FakeModule(module_name_4, module_version_4), + module_name_5: FakeModule(module_name_5, module_version_5), } diff --git a/openstackclient/tests/unit/fakes.py b/openstackclient/tests/unit/fakes.py index 0d378ff8d..be9d7f218 100644 --- a/openstackclient/tests/unit/fakes.py +++ b/openstackclient/tests/unit/fakes.py @@ -12,13 +12,43 @@ # License for the specific language governing permissions and limitations # under the License. +# TODO(stephenfin): Remove the contents of this module in favour of the osc_lib +# version once our min version is bumped to 4.3.0 + import json -import sys from unittest import mock from keystoneauth1 import fixture +from osc_lib.tests.fakes import ( + FakeApp, + FakeClientManager as BaseFakeClientManager, + FakeLog, + FakeOptions, + FakeResource as BaseFakeResource, + FakeStdout, +) import requests +__all__ = [ + 'AUTH_TOKEN', + 'AUTH_URL', + 'INTERFACE', + 'PASSWORD', + 'PROJECT_NAME', + 'REGION_NAME', + 'TEST_RESPONSE_DICT', + 'TEST_RESPONSE_DICT_V3', + 'TEST_VERSIONS', + 'USERNAME', + 'VERSION', + 'FakeApp', + 'FakeClientManager', + 'FakeLog', + 'FakeOptions', + 'FakeResource', + 'FakeResponse', + 'FakeStdout', +] AUTH_TOKEN = "foobar" AUTH_URL = "http://0.0.0.0" @@ -47,79 +77,15 @@ TEST_VERSIONS = fixture.DiscoveryList(href=AUTH_URL) -class FakeStdout: - def __init__(self): - self.content = [] - - def write(self, text): - self.content.append(text) - - def make_string(self): - result = '' - for line in self.content: - result = result + line - return result - - -class FakeLog: - def __init__(self): - self.messages = {} - - def debug(self, msg): - self.messages['debug'] = msg - - def info(self, msg): - self.messages['info'] = msg - - def warning(self, msg): - self.messages['warning'] = msg - - def error(self, msg): - self.messages['error'] = msg - - def critical(self, msg): - self.messages['critical'] = msg - - -class FakeApp: - def __init__(self, _stdout, _log): - self.stdout = _stdout - self.client_manager = None - self.api_version = {} - self.stdin = sys.stdin - self.stdout = _stdout or sys.stdout - self.stderr = sys.stderr - self.log = _log - - -class FakeOptions: - def __init__(self, **kwargs): - self.os_beta_command = False - - -class FakeClient: - def __init__(self, **kwargs): - self.endpoint = kwargs['endpoint'] - self.token = kwargs['token'] - - -class FakeClientManager: +class FakeClientManager(BaseFakeClientManager): _api_version = { 'image': '2', } def __init__(self): - self.compute = None - self.identity = None - self.image = None - self.object_store = None - self.volume = None - self.network = None - self.sdk_connection = mock.Mock() + super().__init__() - self.session = None - self.auth_ref = None - self.auth_plugin_name = None + self.sdk_connection = mock.Mock() self.network_endpoint_enabled = True self.compute_endpoint_enabled = True @@ -158,64 +124,7 @@ def is_volume_endpoint_enabled(self, client=None): return self.volume_endpoint_enabled -class FakeModule: - def __init__(self, name, version): - self.name = name - self.__version__ = version - # Workaround for openstacksdk case - self.version = mock.Mock() - self.version.__version__ = version - - -class FakeResource: - def __init__(self, manager=None, info=None, loaded=False, methods=None): - """Set attributes and methods for a resource. - - :param manager: - The resource manager - :param Dictionary info: - A dictionary with all attributes - :param bool loaded: - True if the resource is loaded in memory - :param Dictionary methods: - A dictionary with all methods - """ - info = info or {} - methods = methods or {} - - self.__name__ = type(self).__name__ - self.manager = manager - self._info = info - self._add_details(info) - self._add_methods(methods) - self._loaded = loaded - - def _add_details(self, info): - for k, v in info.items(): - setattr(self, k, v) - - def _add_methods(self, methods): - """Fake methods with MagicMock objects. - - For each <@key, @value> pairs in methods, add an callable MagicMock - object named @key as an attribute, and set the mock's return_value to - @value. When users access the attribute with (), @value will be - returned, which looks like a function call. - """ - for name, ret in methods.items(): - method = mock.Mock(return_value=ret) - setattr(self, name, method) - - def __repr__(self): - reprkeys = sorted( - k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager' - ) - info = ", ".join(f"{k}={getattr(self, k)}" for k in reprkeys) - return f"<{self.__class__.__name__} {info}>" - - def keys(self): - return self._info.keys() - +class FakeResource(BaseFakeResource): def to_dict(self): return self._info @@ -247,11 +156,3 @@ def __init__( self._content = json.dumps(data) if not isinstance(self._content, bytes): self._content = self._content.encode() - - -class FakeModel(dict): - def __getattr__(self, key): - try: - return self[key] - except KeyError: - raise AttributeError(key) diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py index ad5ceb284..c42ed4731 100644 --- a/openstackclient/tests/unit/identity/v3/fakes.py +++ b/openstackclient/tests/unit/identity/v3/fakes.py @@ -693,6 +693,14 @@ class TestIdentityv3( ): ... +class FakeModel(dict): + def __getattr__(self, key): + try: + return self[key] + except KeyError: + raise AttributeError(key) + + # We don't use FakeClientMixin since we want a different fake legacy client class TestFederatedIdentity(utils.TestCommand): def setUp(self): @@ -1075,7 +1083,7 @@ def create_one_endpoint_filter(attrs=None): # Overwrite default attributes if there are some attributes set endpoint_filter_info.update(attrs) - endpoint_filter = fakes.FakeModel(copy.deepcopy(endpoint_filter_info)) + endpoint_filter = FakeModel(copy.deepcopy(endpoint_filter_info)) return endpoint_filter @@ -1133,7 +1141,7 @@ def create_one_endpointgroup_filter(attrs=None): # Overwrite default attributes if there are some attributes set endpointgroup_filter_info.update(attrs) - endpointgroup_filter = fakes.FakeModel( + endpointgroup_filter = FakeModel( copy.deepcopy(endpointgroup_filter_info) ) From 97c2238df17e7e586f00a5fb06da4ec008ad834d Mon Sep 17 00:00:00 2001 From: Miro Tomaska Date: Wed, 8 Oct 2025 15:27:38 -0400 Subject: [PATCH 227/245] Moving tapas osc client code from neutronclient Proposal to move all stadium projects from neutronclient to openstackclient repo. Tap-as-a-service is the first example. The tapas osc client code was recently moved to neutronclient see https://review.opendev.org/c/openstack/tap-as-a-service/+/960849 but proposal is to make openstackclient its final destination. This change also includes automatic lint fixes required in this repo. Change-Id: Ied47f40c6947600d40bf675ec06f0bf88fd15f1f Signed-off-by: Miro Tomaska --- openstackclient/network/v2/taas/__init__.py | 0 openstackclient/network/v2/taas/tap_flow.py | 245 +++++++++++++++ openstackclient/network/v2/taas/tap_mirror.py | 238 +++++++++++++++ .../network/v2/taas/tap_service.py | 212 +++++++++++++ .../tests/unit/network/v2/taas/__init__.py | 0 .../tests/unit/network/v2/taas/fakes.py | 118 ++++++++ .../unit/network/v2/taas/test_osc_tap_flow.py | 283 ++++++++++++++++++ .../network/v2/taas/test_osc_tap_mirror.py | 283 ++++++++++++++++++ .../network/v2/taas/test_osc_tap_service.py | 266 ++++++++++++++++ pyproject.toml | 17 ++ 10 files changed, 1662 insertions(+) create mode 100644 openstackclient/network/v2/taas/__init__.py create mode 100644 openstackclient/network/v2/taas/tap_flow.py create mode 100644 openstackclient/network/v2/taas/tap_mirror.py create mode 100644 openstackclient/network/v2/taas/tap_service.py create mode 100644 openstackclient/tests/unit/network/v2/taas/__init__.py create mode 100644 openstackclient/tests/unit/network/v2/taas/fakes.py create mode 100644 openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py create mode 100644 openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py create mode 100644 openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py diff --git a/openstackclient/network/v2/taas/__init__.py b/openstackclient/network/v2/taas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstackclient/network/v2/taas/tap_flow.py b/openstackclient/network/v2/taas/tap_flow.py new file mode 100644 index 000000000..441f559bf --- /dev/null +++ b/openstackclient/network/v2/taas/tap_flow.py @@ -0,0 +1,245 @@ +# All Rights Reserved 2020 +# +# 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 logging + +from osc_lib.cli import format_columns +from osc_lib.cli import identity as identity_utils +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient.i18n import _ +from openstackclient.identity import common +from openstackclient.network.v2.taas import tap_service + + +LOG = logging.getLogger(__name__) + +TAP_FLOW = 'tap_flow' +TAP_FLOWS = f'{TAP_FLOW}s' + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('status', 'Status', column_util.LIST_BOTH), + ('source_port', 'source_port', column_util.LIST_BOTH), + ('tap_service_id', 'tap_service_id', column_util.LIST_BOTH), + ('direction', 'Direction', column_util.LIST_BOTH), +) + +_formatters = { + 'vlan_filter': format_columns.ListColumn, +} + + +def _add_updatable_args(parser): + parser.add_argument('--name', help=_('Name of this Tap service.')) + parser.add_argument( + '--description', help=_('Description for this Tap service.') + ) + + +class CreateTapFlow(command.ShowOne): + _description = _("Create a tap flow") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + _add_updatable_args(parser) + parser.add_argument( + '--port', + required=True, + metavar="SOURCE_PORT", + help=_('Source port to which the Tap Flow is connected.'), + ) + parser.add_argument( + '--tap-service', + required=True, + metavar="TAP_SERVICE", + help=_('Tap Service to which the Tap Flow belongs.'), + ) + parser.add_argument( + '--direction', + required=True, + metavar="DIRECTION", + choices=['IN', 'OUT', 'BOTH'], + type=lambda s: s.upper(), + help=_( + 'Direction of the Tap flow. Possible options are: ' + 'IN, OUT, BOTH' + ), + ) + parser.add_argument( + '--vlan-filter', + required=False, + metavar="VLAN_FILTER", + help=_('VLAN Ids to be mirrored in the form of range string.'), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if parsed_args.port is not None: + source_port = client.find_port( + parsed_args.port, ignore_missing=False + )['id'] + attrs['source_port'] = source_port + if parsed_args.tap_service is not None: + tap_service_id = client.find_tap_service( + parsed_args.tap_service, ignore_missing=False + )['id'] + attrs['tap_service_id'] = tap_service_id + if parsed_args.direction is not None: + attrs['direction'] = parsed_args.direction + if parsed_args.vlan_filter is not None: + attrs['vlan_filter'] = parsed_args.vlan_filter + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + obj = client.create_tap_flow(**attrs) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class ListTapFlow(command.Lister): + _description = _("List tap flows that belong to a given tenant") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + params = {} + if parsed_args.project is not None: + params['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + objs = client.tap_flows(retrieve_all=True, params=params) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=True + ) + return ( + headers, + ( + osc_utils.get_dict_properties( + s, columns, formatters=_formatters + ) + for s in objs + ), + ) + + +class ShowTapFlow(command.ShowOne): + _description = _("Show information of a given tap flow") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_FLOW, + metavar=f"<{TAP_FLOW}>", + help=_("ID or name of tap flow to look up."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_tap_flow( + parsed_args.tap_flow, ignore_missing=False + ).id + obj = client.get_tap_flow(id) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteTapFlow(command.Command): + _description = _("Delete a tap flow") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_FLOW, + metavar=f"<{TAP_FLOW}>", + nargs="+", + help=_("ID(s) or name(s) of tap flow to delete."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fails = 0 + for id_or_name in parsed_args.tap_flow: + try: + id = client.find_tap_flow(id_or_name, ignore_missing=False).id + client.delete_tap_flow(id) + LOG.warning("Tap flow %(id)s deleted", {'id': id}) + except Exception as e: + fails += 1 + LOG.error( + "Failed to delete tap flow with name or ID " + "'%(id_or_name)s': %(e)s", + {'id_or_name': id_or_name, 'e': e}, + ) + if fails > 0: + msg = _("Failed to delete %(fails)s of %(total)s tap flow.") % { + 'fails': fails, + 'total': len(parsed_args.tap_flow), + } + raise exceptions.CommandError(msg) + + +class UpdateTapFlow(command.ShowOne): + _description = _("Update a tap flow.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_FLOW, + metavar=f"<{TAP_FLOW}>", + help=_("ID or name of tap flow to update."), + ) + _add_updatable_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + original_t_f = client.find_tap_flow( + parsed_args.tap_flow, ignore_missing=False + ).id + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + obj = client.update_tap_flow(original_t_f, **attrs) + columns, display_columns = column_util.get_columns(obj, _attr_map) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/openstackclient/network/v2/taas/tap_mirror.py b/openstackclient/network/v2/taas/tap_mirror.py new file mode 100644 index 000000000..a11ea91d9 --- /dev/null +++ b/openstackclient/network/v2/taas/tap_mirror.py @@ -0,0 +1,238 @@ +# 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 logging + +from osc_lib.cli import identity as identity_utils +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient.i18n import _ +from openstackclient.identity import common +from openstackclient.network.v2 import port as osc_port +from openstackclient.network.v2.taas import tap_service + + +LOG = logging.getLogger(__name__) + +TAP_MIRROR = 'tap_mirror' +TAP_MIRRORS = f'{TAP_MIRROR}s' + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('port_id', 'Port', column_util.LIST_BOTH), + ('directions', 'Directions', column_util.LIST_LONG_ONLY), + ('remote_ip', 'Remote IP', column_util.LIST_BOTH), + ('mirror_type', 'Mirror Type', column_util.LIST_LONG_ONLY), +) + + +def _get_columns(item): + column_map: dict[str, str] = {} + hidden_columns = ['location', 'tenant_id'] + return osc_utils.get_osc_show_columns_for_sdk_resource( + item, column_map, hidden_columns + ) + + +class CreateTapMirror(command.ShowOne): + _description = _("Create a Tap Mirror") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + tap_service._add_updatable_args(parser) + parser.add_argument( + '--port', + dest='port_id', + required=True, + metavar="PORT", + help=_('Port to which the Tap Mirror is connected.'), + ) + parser.add_argument( + '--directions', + dest='directions', + action=osc_port.JSONKeyValueAction, + required=True, + help=_( + 'A dictionary of direction and tunnel_id. Direction can ' + 'be IN and OUT.' + ), + ) + parser.add_argument( + '--remote-ip', + dest='remote_ip', + required=True, + help=_( + 'The remote IP of the Tap Mirror, this will be the ' + 'remote end of the GRE or ERSPAN v1 tunnel' + ), + ) + parser.add_argument( + '--mirror-type', + dest='mirror_type', + required=True, + help=_('The type of the mirroring, it can be gre or erspanv1'), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if parsed_args.port_id is not None: + port_id = client.find_port( + parsed_args.port_id, ignore_missing=False + )['id'] + attrs['port_id'] = port_id + if parsed_args.directions is not None: + attrs['directions'] = parsed_args.directions + if parsed_args.remote_ip is not None: + attrs['remote_ip'] = parsed_args.remote_ip + if parsed_args.mirror_type is not None: + attrs['mirror_type'] = parsed_args.mirror_type + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + obj = client.create_tap_mirror(**attrs) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class ListTapMirror(command.Lister): + _description = _("List Tap Mirrors that belong to a given tenant") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + params = {} + if parsed_args.project is not None: + params['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + objs = client.tap_mirrors(retrieve_all=True, params=params) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=True + ) + return ( + headers, + (osc_utils.get_dict_properties(s, columns) for s in objs), + ) + + +class ShowTapMirror(command.ShowOne): + _description = _("Show information of a given Tap Mirror") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_MIRROR, + metavar=f"<{TAP_MIRROR}>", + help=_("ID or name of Tap Mirror to look up."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_tap_mirror( + parsed_args.tap_mirror, ignore_missing=False + ).id + obj = client.get_tap_mirror(id) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteTapMirror(command.Command): + _description = _("Delete a Tap Mirror") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_MIRROR, + metavar=f"<{TAP_MIRROR}>", + nargs="+", + help=_("ID(s) or name(s) of the Tap Mirror to delete."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fails = 0 + for id_or_name in parsed_args.tap_mirror: + try: + id = client.find_tap_mirror( + id_or_name, ignore_missing=False + ).id + + client.delete_tap_mirror(id) + LOG.warning("Tap Mirror %(id)s deleted", {'id': id}) + except Exception as e: + fails += 1 + LOG.error( + "Failed to delete Tap Mirror with name or ID " + "'%(id_or_name)s': %(e)s", + {'id_or_name': id_or_name, 'e': e}, + ) + if fails > 0: + msg = _("Failed to delete %(fails)s of %(total)s Tap Mirror.") % { + 'fails': fails, + 'total': len(parsed_args.tap_mirror), + } + raise exceptions.CommandError(msg) + + +class UpdateTapMirror(command.ShowOne): + _description = _("Update a Tap Mirror.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_MIRROR, + metavar=f"<{TAP_MIRROR}>", + help=_("ID or name of the Tap Mirror to update."), + ) + tap_service._add_updatable_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + original_t_s = client.find_tap_mirror( + parsed_args.tap_mirror, ignore_missing=False + ).id + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + obj = client.update_tap_mirror(original_t_s, **attrs) + display_columns, columns = tap_service._get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/openstackclient/network/v2/taas/tap_service.py b/openstackclient/network/v2/taas/tap_service.py new file mode 100644 index 000000000..392d76c51 --- /dev/null +++ b/openstackclient/network/v2/taas/tap_service.py @@ -0,0 +1,212 @@ +# All Rights Reserved 2020 +# +# 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 logging + +from osc_lib.cli import identity as identity_utils +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient.i18n import _ +from openstackclient.identity import common + + +LOG = logging.getLogger(__name__) + +TAP_SERVICE = 'tap_service' +TAP_SERVICES = f'{TAP_SERVICE}s' + +_attr_map = ( + ('id', 'ID', column_util.LIST_BOTH), + ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), + ('name', 'Name', column_util.LIST_BOTH), + ('port_id', 'Port', column_util.LIST_BOTH), + ('status', 'Status', column_util.LIST_BOTH), +) + + +def _add_updatable_args(parser): + parser.add_argument('--name', help=_('Name of this Tap service.')) + parser.add_argument( + '--description', help=_('Description for this Tap service.') + ) + + +def _get_columns(item): + column_map: dict[str, str] = {} + hidden_columns = ['location', 'tenant_id'] + return osc_utils.get_osc_show_columns_for_sdk_resource( + item, column_map, hidden_columns + ) + + +class CreateTapService(command.ShowOne): + _description = _("Create a tap service") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + _add_updatable_args(parser) + parser.add_argument( + '--port', + dest='port_id', + required=True, + metavar="PORT", + help=_('Port to which the Tap service is connected.'), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + if parsed_args.port_id is not None: + port_id = client.find_port( + parsed_args.port_id, ignore_missing=False + )['id'] + attrs['port_id'] = port_id + if 'project' in parsed_args and parsed_args.project is not None: + attrs['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + obj = client.create_tap_service(**attrs) + display_columns, columns = _get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class ListTapService(command.Lister): + _description = _("List tap services that belong to a given project") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + identity_utils.add_project_owner_option_to_parser(parser) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + params = {} + if parsed_args.project is not None: + params['project_id'] = common.find_project( + self.app.client_manager.identity, + parsed_args.project, + parsed_args.project_domain, + ).id + objs = client.tap_services(retrieve_all=True, params=params) + headers, columns = column_util.get_column_definitions( + _attr_map, long_listing=True + ) + return ( + headers, + (osc_utils.get_dict_properties(s, columns) for s in objs), + ) + + +class ShowTapService(command.ShowOne): + _description = _("Show information of a given tap service") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_SERVICE, + metavar=f"<{TAP_SERVICE}>", + help=_("ID or name of tap service to look up."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + id = client.find_tap_service( + parsed_args.tap_service, ignore_missing=False + ).id + obj = client.get_tap_service(id) + display_columns, columns = _get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data + + +class DeleteTapService(command.Command): + _description = _("Delete a tap service") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_SERVICE, + metavar=f"<{TAP_SERVICE}>", + nargs="+", + help=_("ID(s) or name(s) of tap service to delete."), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + fails = 0 + for id_or_name in parsed_args.tap_service: + try: + id = client.find_tap_service( + id_or_name, ignore_missing=False + ).id + + client.delete_tap_service(id) + LOG.warning("Tap service %(id)s deleted", {'id': id}) + except Exception as e: + fails += 1 + LOG.error( + "Failed to delete tap service with name or ID " + "'%(id_or_name)s': %(e)s", + {'id_or_name': id_or_name, 'e': e}, + ) + if fails > 0: + msg = _("Failed to delete %(fails)s of %(total)s tap service.") % { + 'fails': fails, + 'total': len(parsed_args.tap_service), + } + raise exceptions.CommandError(msg) + + +class UpdateTapService(command.ShowOne): + _description = _("Update a tap service.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + TAP_SERVICE, + metavar=f"<{TAP_SERVICE}>", + help=_("ID or name of tap service to update."), + ) + _add_updatable_args(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + original_t_s = client.find_tap_service( + parsed_args.tap_service, ignore_missing=False + ).id + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + obj = client.update_tap_service(original_t_s, **attrs) + display_columns, columns = _get_columns(obj) + data = osc_utils.get_dict_properties(obj, columns) + return display_columns, data diff --git a/openstackclient/tests/unit/network/v2/taas/__init__.py b/openstackclient/tests/unit/network/v2/taas/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openstackclient/tests/unit/network/v2/taas/fakes.py b/openstackclient/tests/unit/network/v2/taas/fakes.py new file mode 100644 index 000000000..0480a0d90 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/taas/fakes.py @@ -0,0 +1,118 @@ +# All Rights Reserved 2020 +# +# 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 copy + +from oslo_utils import uuidutils + + +class FakeTapService: + @staticmethod + def create_tap_service(attrs=None): + """Create a fake tap service.""" + attrs = attrs or {} + tap_service_attrs = { + 'id': uuidutils.generate_uuid(), + 'tenant_id': uuidutils.generate_uuid(), + 'name': 'test_tap_service' + uuidutils.generate_uuid(), + 'status': 'ACTIVE', + } + tap_service_attrs.update(attrs) + return copy.deepcopy(tap_service_attrs) + + @staticmethod + def create_tap_services(attrs=None, count=1): + """Create multiple fake tap services.""" + + tap_services = [] + for i in range(0, count): + if attrs is None: + attrs = {'id': f'fake_id{i}'} + elif getattr(attrs, 'id', None) is None: + attrs['id'] = f'fake_id{i}' + tap_services.append(FakeTapService.create_tap_service(attrs=attrs)) + + return tap_services + + +class FakeTapFlow: + @staticmethod + def create_tap_flow(attrs=None): + """Create a fake tap service.""" + attrs = attrs or {} + tap_flow_attrs = { + 'id': uuidutils.generate_uuid(), + 'tenant_id': uuidutils.generate_uuid(), + 'name': 'test_tap_flow' + uuidutils.generate_uuid(), + 'status': 'ACTIVE', + 'direction': 'BOTH', + } + tap_flow_attrs.update(attrs) + return copy.deepcopy(tap_flow_attrs) + + @staticmethod + def create_tap_flows(attrs=None, count=1): + """Create multiple fake tap flows.""" + + tap_flows = [] + for i in range(0, count): + if attrs is None: + attrs = { + 'id': f'fake_id{i}', + 'source_port': uuidutils.generate_uuid(), + 'tap_service_id': uuidutils.generate_uuid(), + } + elif getattr(attrs, 'id', None) is None: + attrs['id'] = f'fake_id{i}' + tap_flows.append(FakeTapFlow.create_tap_flow(attrs=attrs)) + + return tap_flows + + +class FakeTapMirror: + @staticmethod + def create_tap_mirror(attrs=None): + """Create a fake tap mirror.""" + attrs = attrs or {} + tap_mirror_attrs = { + 'id': uuidutils.generate_uuid(), + 'tenant_id': uuidutils.generate_uuid(), + 'name': 'test_tap_mirror' + uuidutils.generate_uuid(), + 'port_id': uuidutils.generate_uuid(), + 'directions': 'IN=99', + 'remote_ip': '192.10.10.2', + 'mirror_type': 'gre', + } + tap_mirror_attrs.update(attrs) + return copy.deepcopy(tap_mirror_attrs) + + @staticmethod + def create_tap_mirrors(attrs=None, count=1): + """Create multiple fake tap mirrors.""" + + tap_mirrors = [] + for i in range(0, count): + if attrs is None: + attrs = { + 'id': f'fake_id{i}', + 'port_id': uuidutils.generate_uuid(), + 'name': f'test_tap_mirror_{i}', + 'directions': f'IN={99 + i}', + 'remote_ip': f'192.10.10.{i + 3}', + } + elif getattr(attrs, 'id', None) is None: + attrs['id'] = f'fake_id{i}' + tap_mirrors.append(FakeTapMirror.create_tap_mirror(attrs=attrs)) + + return tap_mirrors diff --git a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py new file mode 100644 index 000000000..e401a0f97 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py @@ -0,0 +1,283 @@ +# All Rights Reserved 2020 +# +# 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 copy +import operator +import uuid + +from openstack.network.v2 import tap_flow as _tap_flow +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient.network.v2.taas import tap_flow as osc_tap_flow +from openstackclient.network.v2.taas import tap_service as osc_tap_service +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit.network.v2.taas import fakes + + +columns_long = tuple( + col + for col, _, listing_mode in osc_tap_flow._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +headers_long = tuple( + head + for _, head, listing_mode in osc_tap_flow._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +sorted_attr_map = sorted(osc_tap_flow._attr_map, key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties(attrs, columns) + + +class TestCreateTapFlow(network_fakes.TestNetworkV2): + columns = ( + 'direction', + 'id', + 'name', + 'source_port', + 'status', + 'tap_service_id', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_flow.CreateTapFlow(self.app, None) + + def test_create_tap_flow(self): + """Test Create Tap Flow.""" + fake_tap_service = fakes.FakeTapService.create_tap_service( + attrs={'port_id': str(uuid.uuid4())} + ) + port_id = str(uuid.uuid4()) + fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( + attrs={ + 'source_port': port_id, + 'tap_service_id': fake_tap_service['id'], + } + ) + self.app.client_manager.network.create_tap_flow.return_value = ( + fake_tap_flow + ) + self.app.client_manager.network.find_port.return_value = { + 'id': port_id + } + self.app.client_manager.network.find_tap_service.return_value = ( + fake_tap_service + ) + arg_list = [ + '--name', + fake_tap_flow['name'], + '--port', + fake_tap_flow['source_port'], + '--tap-service', + fake_tap_flow['tap_service_id'], + '--direction', + fake_tap_flow['direction'], + ] + + verify_list = [ + ('name', fake_tap_flow['name']), + ('port', fake_tap_flow['source_port']), + ('tap_service', fake_tap_flow['tap_service_id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + mock_create_t_f = self.app.client_manager.network.create_tap_flow + mock_create_t_f.assert_called_once_with( + **{ + 'name': fake_tap_flow['name'], + 'source_port': fake_tap_flow['source_port'], + 'tap_service_id': fake_tap_flow['tap_service_id'], + 'direction': fake_tap_flow['direction'], + } + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + fake_tap_flow, osc_tap_service._get_columns(fake_tap_flow)[1] + ) + self.assertEqual(fake_data, data) + + +class TestListTapFlow(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.cmd = osc_tap_flow.ListTapFlow(self.app, None) + + def test_list_tap_flows(self): + """Test List Tap Flow.""" + fake_tap_flows = fakes.FakeTapFlow.create_tap_flows( + attrs={ + 'source_port': str(uuid.uuid4()), + 'tap_service_id': str(uuid.uuid4()), + }, + count=2, + ) + self.app.client_manager.network.tap_flows.return_value = fake_tap_flows + arg_list = [] + verify_list = [] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.tap_flows.assert_called_once() + self.assertEqual(headers, list(headers_long)) + self.assertCountEqual( + list(data), + [ + _get_data(fake_tap_flow, columns_long) + for fake_tap_flow in fake_tap_flows + ], + ) + + +class TestDeleteTapFlow(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_flow.side_effect = ( + lambda name_or_id, ignore_missing: _tap_flow.TapFlow(id=name_or_id) + ) + self.cmd = osc_tap_flow.DeleteTapFlow(self.app, None) + + def test_delete_tap_flow(self): + """Test Delete tap flow.""" + + fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( + attrs={ + 'source_port': str(uuid.uuid4()), + 'tap_service_id': str(uuid.uuid4()), + } + ) + + arg_list = [ + fake_tap_flow['id'], + ] + verify_list = [ + (osc_tap_flow.TAP_FLOW, [fake_tap_flow['id']]), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + result = self.cmd.take_action(parsed_args) + + mock_delete_tap_flow = self.app.client_manager.network.delete_tap_flow + mock_delete_tap_flow.assert_called_once_with(fake_tap_flow['id']) + self.assertIsNone(result) + + +class TestShowTapFlow(network_fakes.TestNetworkV2): + columns = ( + 'direction', + 'id', + 'name', + 'source_port', + 'status', + 'tap_service_id', + ) + + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_flow.side_effect = ( + lambda name_or_id, ignore_missing: _tap_flow.TapFlow(id=name_or_id) + ) + self.cmd = osc_tap_flow.ShowTapFlow(self.app, None) + + def test_show_tap_flow(self): + """Test Show tap flow.""" + fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( + attrs={ + 'source_port': str(uuid.uuid4()), + 'tap_service_id': str(uuid.uuid4()), + } + ) + self.app.client_manager.network.get_tap_flow.return_value = ( + fake_tap_flow + ) + arg_list = [ + fake_tap_flow['id'], + ] + verify_list = [ + (osc_tap_flow.TAP_FLOW, fake_tap_flow['id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.get_tap_flow.assert_called_once_with( + fake_tap_flow['id'] + ) + self.assertEqual(self.columns, headers) + fake_data = _get_data( + fake_tap_flow, osc_tap_service._get_columns(fake_tap_flow)[1] + ) + self.assertEqual(fake_data, data) + + +class TestUpdateTapFlow(network_fakes.TestNetworkV2): + _new_name = 'new_name' + + columns = ( + 'Direction', + 'ID', + 'Name', + 'Status', + 'Tenant', + 'source_port', + 'tap_service_id', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_flow.UpdateTapFlow(self.app, None) + self.app.client_manager.network.find_tap_flow.side_effect = ( + lambda name_or_id, ignore_missing: _tap_flow.TapFlow(id=name_or_id) + ) + + def test_update_tap_flow(self): + """Test update tap service""" + fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( + attrs={ + 'source_port': str(uuid.uuid4()), + 'tap_service_id': str(uuid.uuid4()), + } + ) + new_tap_flow = copy.deepcopy(fake_tap_flow) + new_tap_flow['name'] = self._new_name + + self.app.client_manager.network.update_tap_flow.return_value = ( + new_tap_flow + ) + + arg_list = [ + fake_tap_flow['id'], + '--name', + self._new_name, + ] + verify_list = [('name', self._new_name)] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + attrs = {'name': self._new_name} + + mock_update_t_f = self.app.client_manager.network.update_tap_flow + mock_update_t_f.assert_called_once_with(new_tap_flow['id'], **attrs) + self.assertEqual(self.columns, columns) + self.assertEqual(_get_data(new_tap_flow), data) diff --git a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py new file mode 100644 index 000000000..451d6a25b --- /dev/null +++ b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py @@ -0,0 +1,283 @@ +# 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 copy +import operator +import uuid + +from openstack.network.v2 import tap_mirror +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient.network.v2.taas import tap_mirror as osc_tap_mirror +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit.network.v2.taas import fakes + + +columns_long = tuple( + col + for col, _, listing_mode in osc_tap_mirror._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +headers_long = tuple( + head + for _, head, listing_mode in osc_tap_mirror._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +sorted_attr_map = sorted(osc_tap_mirror._attr_map, key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties(attrs, columns) + + +class TestCreateTapMirror(network_fakes.TestNetworkV2): + columns = ( + 'directions', + 'id', + 'mirror_type', + 'name', + 'port_id', + 'remote_ip', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_mirror.CreateTapMirror(self.app, None) + + def test_create_tap_mirror(self): + port_id = str(uuid.uuid4()) + fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( + attrs={'port_id': port_id} + ) + self.app.client_manager.network.create_tap_mirror.return_value = ( + fake_tap_mirror + ) + self.app.client_manager.network.find_port.return_value = { + 'id': port_id + } + self.app.client_manager.network.find_tap_mirror.side_effect = ( + lambda _, name_or_id: {'id': name_or_id} + ) + arg_list = [ + '--name', + fake_tap_mirror['name'], + '--port', + fake_tap_mirror['port_id'], + '--directions', + fake_tap_mirror['directions'], + '--remote-ip', + fake_tap_mirror['remote_ip'], + '--mirror-type', + fake_tap_mirror['mirror_type'], + ] + + verify_directions = fake_tap_mirror['directions'].split('=') + verify_directions_dict = {verify_directions[0]: verify_directions[1]} + + verify_list = [ + ('name', fake_tap_mirror['name']), + ('port_id', fake_tap_mirror['port_id']), + ('directions', verify_directions_dict), + ('remote_ip', fake_tap_mirror['remote_ip']), + ('mirror_type', fake_tap_mirror['mirror_type']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + self.app.client_manager.network.find_tap_mirror.return_value = ( + fake_tap_mirror + ) + + columns, data = self.cmd.take_action(parsed_args) + create_tap_m_mock = self.app.client_manager.network.create_tap_mirror + create_tap_m_mock.assert_called_once_with( + **{ + 'name': fake_tap_mirror['name'], + 'port_id': fake_tap_mirror['port_id'], + 'directions': verify_directions_dict, + 'remote_ip': fake_tap_mirror['remote_ip'], + 'mirror_type': fake_tap_mirror['mirror_type'], + } + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + fake_tap_mirror, osc_tap_mirror._get_columns(fake_tap_mirror)[1] + ) + self.assertEqual(fake_data, data) + + +class TestListTapMirror(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.cmd = osc_tap_mirror.ListTapMirror(self.app, None) + + def test_list_tap_mirror(self): + """Test List Tap Mirror.""" + fake_tap_mirrors = fakes.FakeTapMirror.create_tap_mirrors( + attrs={'port_id': str(uuid.uuid4())}, count=4 + ) + self.app.client_manager.network.tap_mirrors.return_value = ( + fake_tap_mirrors + ) + + arg_list = [] + verify_list = [] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.tap_mirrors.assert_called_once() + self.assertEqual(headers, list(headers_long)) + self.assertCountEqual( + list(data), + [ + _get_data(fake_tap_mirror, columns_long) + for fake_tap_mirror in fake_tap_mirrors + ], + ) + + +class TestDeleteTapMirror(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_mirror.side_effect = ( + lambda name_or_id, ignore_missing: tap_mirror.TapMirror( + id=name_or_id + ) + ) + self.cmd = osc_tap_mirror.DeleteTapMirror(self.app, None) + + def test_delete_tap_mirror(self): + """Test Delete Tap Mirror.""" + + fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( + attrs={'port_id': str(uuid.uuid4())} + ) + + arg_list = [ + fake_tap_mirror['id'], + ] + verify_list = [ + (osc_tap_mirror.TAP_MIRROR, [fake_tap_mirror['id']]), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + result = self.cmd.take_action(parsed_args) + + mock_delete_tap_m = self.app.client_manager.network.delete_tap_mirror + mock_delete_tap_m.assert_called_once_with(fake_tap_mirror['id']) + self.assertIsNone(result) + + +class TestShowTapMirror(network_fakes.TestNetworkV2): + columns = ( + 'directions', + 'id', + 'mirror_type', + 'name', + 'port_id', + 'remote_ip', + ) + + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_mirror.side_effect = ( + lambda name_or_id, ignore_missing: tap_mirror.TapMirror( + id=name_or_id + ) + ) + self.cmd = osc_tap_mirror.ShowTapMirror(self.app, None) + + def test_show_tap_mirror(self): + """Test Show Tap Mirror.""" + + fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( + attrs={'port_id': str(uuid.uuid4())} + ) + self.app.client_manager.network.get_tap_mirror.return_value = ( + fake_tap_mirror + ) + arg_list = [ + fake_tap_mirror['id'], + ] + verify_list = [ + (osc_tap_mirror.TAP_MIRROR, fake_tap_mirror['id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + mock_get_tap_m = self.app.client_manager.network.get_tap_mirror + mock_get_tap_m.assert_called_once_with(fake_tap_mirror['id']) + self.assertEqual(self.columns, headers) + fake_data = _get_data( + fake_tap_mirror, osc_tap_mirror._get_columns(fake_tap_mirror)[1] + ) + self.assertEqual(fake_data, data) + + +class TestUpdateTapMirror(network_fakes.TestNetworkV2): + _new_name = 'new_name' + columns = ( + 'directions', + 'id', + 'mirror_type', + 'name', + 'port_id', + 'remote_ip', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_mirror.UpdateTapMirror(self.app, None) + self.app.client_manager.network.find_tap_mirror.side_effect = ( + lambda name_or_id, ignore_missing: tap_mirror.TapMirror( + id=name_or_id + ) + ) + + def test_update_tap_mirror(self): + """Test update Tap Mirror""" + fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( + attrs={'port_id': str(uuid.uuid4())} + ) + new_tap_mirror = copy.deepcopy(fake_tap_mirror) + new_tap_mirror['name'] = self._new_name + + self.app.client_manager.network.update_tap_mirror.return_value = ( + new_tap_mirror + ) + + arg_list = [ + fake_tap_mirror['id'], + '--name', + self._new_name, + ] + verify_list = [('name', self._new_name)] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + attrs = {'name': self._new_name} + + mock_update_tap_m = self.app.client_manager.network.update_tap_mirror + mock_update_tap_m.assert_called_once_with( + fake_tap_mirror['id'], **attrs + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + new_tap_mirror, osc_tap_mirror._get_columns(new_tap_mirror)[1] + ) + self.assertEqual(fake_data, data) diff --git a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py new file mode 100644 index 000000000..0b8180151 --- /dev/null +++ b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py @@ -0,0 +1,266 @@ +# All Rights Reserved 2020 +# +# 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 copy +import operator +import uuid + +from openstack.network.v2 import tap_service +from osc_lib import utils as osc_utils +from osc_lib.utils import columns as column_util + +from openstackclient.network.v2.taas import tap_service as osc_tap_service +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit.network.v2.taas import fakes + + +columns_long = tuple( + col + for col, _, listing_mode in osc_tap_service._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +headers_long = tuple( + head + for _, head, listing_mode in osc_tap_service._attr_map + if listing_mode in (column_util.LIST_BOTH, column_util.LIST_LONG_ONLY) +) +sorted_attr_map = sorted(osc_tap_service._attr_map, key=operator.itemgetter(1)) +sorted_columns = tuple(col for col, _, _ in sorted_attr_map) +sorted_headers = tuple(head for _, head, _ in sorted_attr_map) + + +def _get_data(attrs, columns=sorted_columns): + return osc_utils.get_dict_properties(attrs, columns) + + +class TestCreateTapService(network_fakes.TestNetworkV2): + columns = ( + 'id', + 'name', + 'port_id', + 'status', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_service.CreateTapService(self.app, None) + + def test_create_tap_service(self): + """Test Create Tap Service.""" + port_id = str(uuid.uuid4()) + fake_tap_service = fakes.FakeTapService.create_tap_service( + attrs={'port_id': port_id} + ) + self.app.client_manager.network.create_tap_service.return_value = ( + fake_tap_service + ) + self.app.client_manager.network.find_port.return_value = { + 'id': port_id + } + self.app.client_manager.network.find_tap_service.side_effect = ( + lambda _, name_or_id: {'id': name_or_id} + ) + arg_list = [ + '--name', + fake_tap_service['name'], + '--port', + fake_tap_service['port_id'], + ] + + verify_list = [ + ('name', fake_tap_service['name']), + ('port_id', fake_tap_service['port_id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + self.app.client_manager.network.find_tap_service.return_value = ( + fake_tap_service + ) + + columns, data = self.cmd.take_action(parsed_args) + create_tap_s_mock = self.app.client_manager.network.create_tap_service + create_tap_s_mock.assert_called_once_with( + **{ + 'name': fake_tap_service['name'], + 'port_id': fake_tap_service['port_id'], + } + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + fake_tap_service, osc_tap_service._get_columns(fake_tap_service)[1] + ) + self.assertEqual(fake_data, data) + + +class TestListTapService(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.cmd = osc_tap_service.ListTapService(self.app, None) + + def test_list_tap_service(self): + """Test List Tap Service.""" + fake_tap_services = fakes.FakeTapService.create_tap_services( + attrs={'port_id': str(uuid.uuid4())}, count=4 + ) + self.app.client_manager.network.tap_services.return_value = ( + fake_tap_services + ) + + arg_list = [] + verify_list = [] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + self.app.client_manager.network.tap_services.assert_called_once() + self.assertEqual(headers, list(headers_long)) + self.assertCountEqual( + list(data), + [ + _get_data(fake_tap_service, columns_long) + for fake_tap_service in fake_tap_services + ], + ) + + +class TestDeleteTapService(network_fakes.TestNetworkV2): + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_service.side_effect = ( + lambda name_or_id, ignore_missing: tap_service.TapService( + id=name_or_id + ) + ) + self.cmd = osc_tap_service.DeleteTapService(self.app, None) + + def test_delete_tap_service(self): + """Test Delete tap service.""" + + fake_tap_service = fakes.FakeTapService.create_tap_service( + attrs={'port_id': str(uuid.uuid4())} + ) + + arg_list = [ + fake_tap_service['id'], + ] + verify_list = [ + (osc_tap_service.TAP_SERVICE, [fake_tap_service['id']]), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + result = self.cmd.take_action(parsed_args) + + mock_delete_tap_s = self.app.client_manager.network.delete_tap_service + mock_delete_tap_s.assert_called_once_with(fake_tap_service['id']) + self.assertIsNone(result) + + +class TestShowTapService(network_fakes.TestNetworkV2): + columns = ( + 'id', + 'name', + 'port_id', + 'status', + ) + + def setUp(self): + super().setUp() + self.app.client_manager.network.find_tap_service.side_effect = ( + lambda name_or_id, ignore_missing: tap_service.TapService( + id=name_or_id + ) + ) + self.cmd = osc_tap_service.ShowTapService(self.app, None) + + def test_show_tap_service(self): + """Test Show tap service.""" + + fake_tap_service = fakes.FakeTapService.create_tap_service( + attrs={'port_id': str(uuid.uuid4())} + ) + self.app.client_manager.network.get_tap_service.return_value = ( + fake_tap_service + ) + arg_list = [ + fake_tap_service['id'], + ] + verify_list = [ + (osc_tap_service.TAP_SERVICE, fake_tap_service['id']), + ] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + + headers, data = self.cmd.take_action(parsed_args) + + mock_get_tap_s = self.app.client_manager.network.get_tap_service + mock_get_tap_s.assert_called_once_with(fake_tap_service['id']) + self.assertEqual(self.columns, headers) + fake_data = _get_data( + fake_tap_service, osc_tap_service._get_columns(fake_tap_service)[1] + ) + self.assertEqual(fake_data, data) + + +class TestUpdateTapService(network_fakes.TestNetworkV2): + _new_name = 'new_name' + + columns = ( + 'id', + 'name', + 'port_id', + 'status', + ) + + def setUp(self): + super().setUp() + self.cmd = osc_tap_service.UpdateTapService(self.app, None) + self.app.client_manager.network.find_tap_service.side_effect = ( + lambda name_or_id, ignore_missing: tap_service.TapService( + id=name_or_id + ) + ) + + def test_update_tap_service(self): + """Test update tap service""" + fake_tap_service = fakes.FakeTapService.create_tap_service( + attrs={'port_id': str(uuid.uuid4())} + ) + new_tap_service = copy.deepcopy(fake_tap_service) + new_tap_service['name'] = self._new_name + + self.app.client_manager.network.update_tap_service.return_value = ( + new_tap_service + ) + + arg_list = [ + fake_tap_service['id'], + '--name', + self._new_name, + ] + verify_list = [('name', self._new_name)] + + parsed_args = self.check_parser(self.cmd, arg_list, verify_list) + columns, data = self.cmd.take_action(parsed_args) + attrs = {'name': self._new_name} + + mock_update_tap_s = self.app.client_manager.network.update_tap_service + mock_update_tap_s.assert_called_once_with( + fake_tap_service['id'], **attrs + ) + self.assertEqual(self.columns, columns) + fake_data = _get_data( + new_tap_service, osc_tap_service._get_columns(new_tap_service)[1] + ) + self.assertEqual(fake_data, data) diff --git a/pyproject.toml b/pyproject.toml index aeb5ab495..dc2cbf386 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -540,6 +540,23 @@ subnet_pool_set = "openstackclient.network.v2.subnet_pool:SetSubnetPool" subnet_pool_show = "openstackclient.network.v2.subnet_pool:ShowSubnetPool" subnet_pool_unset = "openstackclient.network.v2.subnet_pool:UnsetSubnetPool" +# Tap-as-a-Service +tap_flow_create = "openstackclient.network.v2.taas.tap_flow:CreateTapFlow" +tap_flow_delete = "openstackclient.network.v2.taas.tap_flow:DeleteTapFlow" +tap_flow_list = "openstackclient.network.v2.taas.tap_flow:ListTapFlow" +tap_flow_show = "openstackclient.network.v2.taas.tap_flow:ShowTapFlow" +tap_flow_update = "openstackclient.network.v2.taas.tap_flow:UpdateTapFlow" +tap_mirror_create = "openstackclient.network.v2.taas.tap_mirror:CreateTapMirror" +tap_mirror_delete = "openstackclient.network.v2.taas.tap_mirror:DeleteTapMirror" +tap_mirror_list = "openstackclient.network.v2.taas.tap_mirror:ListTapMirror" +tap_mirror_show = "openstackclient.network.v2.taas.tap_mirror:ShowTapMirror" +tap_mirror_update = "openstackclient.network.v2.taas.tap_mirror:UpdateTapMirror" +tap_service_create = "openstackclient.network.v2.taas.tap_service:CreateTapService" +tap_service_delete = "openstackclient.network.v2.taas.tap_service:DeleteTapService" +tap_service_list = "openstackclient.network.v2.taas.tap_service:ListTapService" +tap_service_show = "openstackclient.network.v2.taas.tap_service:ShowTapService" +tap_service_update = "openstackclient.network.v2.taas.tap_service:UpdateTapService" + [project.entry-points."openstack.object_store.v1"] object_store_account_set = "openstackclient.object.v1.account:SetAccount" object_store_account_show = "openstackclient.object.v1.account:ShowAccount" From 9e49047ed11a790a025d94c0d3f61cd5a684fb07 Mon Sep 17 00:00:00 2001 From: Miro Tomaska Date: Wed, 26 Nov 2025 13:56:05 -0500 Subject: [PATCH 228/245] Improve help strings for tap services This is a follow up patch to feedback from[1] [1] https://review.opendev.org/c/openstack/python-openstackclient/+/963445/comment/8f9576d4_938391ea/ Change-Id: I1c1ee68b37ef4c87c13d18e773c19b4ca5814ead Signed-off-by: Miro Tomaska --- openstackclient/network/v2/taas/tap_flow.py | 31 ++++++++++--------- openstackclient/network/v2/taas/tap_mirror.py | 28 ++++++++--------- .../network/v2/taas/tap_service.py | 20 ++++++------ 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/openstackclient/network/v2/taas/tap_flow.py b/openstackclient/network/v2/taas/tap_flow.py index 441f559bf..92eb36d3e 100644 --- a/openstackclient/network/v2/taas/tap_flow.py +++ b/openstackclient/network/v2/taas/tap_flow.py @@ -47,14 +47,14 @@ def _add_updatable_args(parser): - parser.add_argument('--name', help=_('Name of this Tap service.')) + parser.add_argument('--name', help=_('Name of the tap flow.')) parser.add_argument( - '--description', help=_('Description for this Tap service.') + '--description', help=_('Description of the tap flow.') ) class CreateTapFlow(command.ShowOne): - _description = _("Create a tap flow") + _description = _("Create a new tap flow.") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -64,13 +64,15 @@ def get_parser(self, prog_name): '--port', required=True, metavar="SOURCE_PORT", - help=_('Source port to which the Tap Flow is connected.'), + help=_('Source port (name or ID) to monitor.'), ) parser.add_argument( '--tap-service', required=True, metavar="TAP_SERVICE", - help=_('Tap Service to which the Tap Flow belongs.'), + help=_( + 'Tap service (name or ID) to associate with this tap flow.' + ), ) parser.add_argument( '--direction', @@ -79,15 +81,15 @@ def get_parser(self, prog_name): choices=['IN', 'OUT', 'BOTH'], type=lambda s: s.upper(), help=_( - 'Direction of the Tap flow. Possible options are: ' - 'IN, OUT, BOTH' + 'Direction of the Tap flow. Valid options are: ' + 'IN, OUT and BOTH' ), ) parser.add_argument( '--vlan-filter', required=False, metavar="VLAN_FILTER", - help=_('VLAN Ids to be mirrored in the form of range string.'), + help=_('VLAN IDs to mirror in the form of range string.'), ) return parser @@ -125,7 +127,7 @@ def take_action(self, parsed_args): class ListTapFlow(command.Lister): - _description = _("List tap flows that belong to a given tenant") + _description = _("List tap flows.") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -158,14 +160,14 @@ def take_action(self, parsed_args): class ShowTapFlow(command.ShowOne): - _description = _("Show information of a given tap flow") + _description = _("Show tap flow details.") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( TAP_FLOW, metavar=f"<{TAP_FLOW}>", - help=_("ID or name of tap flow to look up."), + help=_("Tap flow to display (name or ID)."), ) return parser @@ -181,7 +183,7 @@ def take_action(self, parsed_args): class DeleteTapFlow(command.Command): - _description = _("Delete a tap flow") + _description = _("Delete a tap flow.") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -189,7 +191,7 @@ def get_parser(self, prog_name): TAP_FLOW, metavar=f"<{TAP_FLOW}>", nargs="+", - help=_("ID(s) or name(s) of tap flow to delete."), + help=_("Tap flow to delete (name or ID)."), ) return parser @@ -200,7 +202,6 @@ def take_action(self, parsed_args): try: id = client.find_tap_flow(id_or_name, ignore_missing=False).id client.delete_tap_flow(id) - LOG.warning("Tap flow %(id)s deleted", {'id': id}) except Exception as e: fails += 1 LOG.error( @@ -224,7 +225,7 @@ def get_parser(self, prog_name): parser.add_argument( TAP_FLOW, metavar=f"<{TAP_FLOW}>", - help=_("ID or name of tap flow to update."), + help=_("Tap flow to modify (name or ID)."), ) _add_updatable_args(parser) return parser diff --git a/openstackclient/network/v2/taas/tap_mirror.py b/openstackclient/network/v2/taas/tap_mirror.py index a11ea91d9..e48a3ae2b 100644 --- a/openstackclient/network/v2/taas/tap_mirror.py +++ b/openstackclient/network/v2/taas/tap_mirror.py @@ -49,7 +49,7 @@ def _get_columns(item): class CreateTapMirror(command.ShowOne): - _description = _("Create a Tap Mirror") + _description = _("Create a new tap mirror.") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -60,7 +60,7 @@ def get_parser(self, prog_name): dest='port_id', required=True, metavar="PORT", - help=_('Port to which the Tap Mirror is connected.'), + help=_('Port (name or ID) to which the Tap Mirror is connected.'), ) parser.add_argument( '--directions', @@ -68,8 +68,8 @@ def get_parser(self, prog_name): action=osc_port.JSONKeyValueAction, required=True, help=_( - 'A dictionary of direction and tunnel_id. Direction can ' - 'be IN and OUT.' + 'Dictionary of direction and tunnel_id. Valid directions are: ' + 'IN and OUT.' ), ) parser.add_argument( @@ -77,15 +77,15 @@ def get_parser(self, prog_name): dest='remote_ip', required=True, help=_( - 'The remote IP of the Tap Mirror, this will be the ' - 'remote end of the GRE or ERSPAN v1 tunnel' + 'Remote IP address for the tap mirror (remote end of the ' + 'GRE or ERSPAN v1 tunnel).' ), ) parser.add_argument( '--mirror-type', dest='mirror_type', required=True, - help=_('The type of the mirroring, it can be gre or erspanv1'), + help=_('Mirror type. Valid values are: gre and erspanv1.'), ) return parser @@ -120,7 +120,7 @@ def take_action(self, parsed_args): class ListTapMirror(command.Lister): - _description = _("List Tap Mirrors that belong to a given tenant") + _description = _("List tap mirrors.") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -148,14 +148,14 @@ def take_action(self, parsed_args): class ShowTapMirror(command.ShowOne): - _description = _("Show information of a given Tap Mirror") + _description = _("Show tap mirror details.") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( TAP_MIRROR, metavar=f"<{TAP_MIRROR}>", - help=_("ID or name of Tap Mirror to look up."), + help=_("Tap mirror to display (name or ID)."), ) return parser @@ -171,7 +171,7 @@ def take_action(self, parsed_args): class DeleteTapMirror(command.Command): - _description = _("Delete a Tap Mirror") + _description = _("Delete a tap mirror.") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -179,7 +179,7 @@ def get_parser(self, prog_name): TAP_MIRROR, metavar=f"<{TAP_MIRROR}>", nargs="+", - help=_("ID(s) or name(s) of the Tap Mirror to delete."), + help=_("Tap mirror to delete (name or ID)."), ) return parser @@ -210,14 +210,14 @@ def take_action(self, parsed_args): class UpdateTapMirror(command.ShowOne): - _description = _("Update a Tap Mirror.") + _description = _("Update a tap mirror.") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( TAP_MIRROR, metavar=f"<{TAP_MIRROR}>", - help=_("ID or name of the Tap Mirror to update."), + help=_("Tap mirror to modify (name or ID)."), ) tap_service._add_updatable_args(parser) return parser diff --git a/openstackclient/network/v2/taas/tap_service.py b/openstackclient/network/v2/taas/tap_service.py index 392d76c51..486388ec1 100644 --- a/openstackclient/network/v2/taas/tap_service.py +++ b/openstackclient/network/v2/taas/tap_service.py @@ -39,9 +39,9 @@ def _add_updatable_args(parser): - parser.add_argument('--name', help=_('Name of this Tap service.')) + parser.add_argument('--name', help=_('Name of the tap service.')) parser.add_argument( - '--description', help=_('Description for this Tap service.') + '--description', help=_('Description of the tap service.') ) @@ -54,7 +54,7 @@ def _get_columns(item): class CreateTapService(command.ShowOne): - _description = _("Create a tap service") + _description = _("Create a new tap service.") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -65,7 +65,7 @@ def get_parser(self, prog_name): dest='port_id', required=True, metavar="PORT", - help=_('Port to which the Tap service is connected.'), + help=_('Port (name or ID) to connect to the tap service.'), ) return parser @@ -94,7 +94,7 @@ def take_action(self, parsed_args): class ListTapService(command.Lister): - _description = _("List tap services that belong to a given project") + _description = _("List tap services.") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -122,14 +122,14 @@ def take_action(self, parsed_args): class ShowTapService(command.ShowOne): - _description = _("Show information of a given tap service") + _description = _("Show tap service details.") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( TAP_SERVICE, metavar=f"<{TAP_SERVICE}>", - help=_("ID or name of tap service to look up."), + help=_("Tap service to display (name or ID)."), ) return parser @@ -145,7 +145,7 @@ def take_action(self, parsed_args): class DeleteTapService(command.Command): - _description = _("Delete a tap service") + _description = _("Delete a tap service.") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -153,7 +153,7 @@ def get_parser(self, prog_name): TAP_SERVICE, metavar=f"<{TAP_SERVICE}>", nargs="+", - help=_("ID(s) or name(s) of tap service to delete."), + help=_("Tap service to delete (name or ID)."), ) return parser @@ -191,7 +191,7 @@ def get_parser(self, prog_name): parser.add_argument( TAP_SERVICE, metavar=f"<{TAP_SERVICE}>", - help=_("ID or name of tap service to update."), + help=_("Tap service to modify (name or ID)."), ) _add_updatable_args(parser) return parser From 92a277ff4cb5cdf6caed2d16777e6eb0e7474511 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Thu, 11 Dec 2025 02:42:46 +0900 Subject: [PATCH 229/245] ruff: Enable E5 check ... to enforce maximum line length, to keep consistent code format. Note that E501 check is disabled in test code now, until we decide how to update ~50 lines violating the limit due to too long names. Change-Id: I122c8b9035d6381dafb34438517c26b01e5201f5 Signed-off-by: Takashi Kajinami --- hacking/checks.py | 6 +++--- openstackclient/api/object_store_v1.py | 5 ++++- openstackclient/identity/common.py | 3 ++- openstackclient/identity/v3/group.py | 5 +++-- openstackclient/identity/v3/user.py | 6 ++++-- openstackclient/image/v2/metadef_properties.py | 3 ++- openstackclient/volume/v2/volume_type.py | 12 ++++++++---- openstackclient/volume/v3/volume_attachment.py | 2 +- openstackclient/volume/v3/volume_type.py | 18 ++++++++++++------ pyproject.toml | 4 ++-- 10 files changed, 41 insertions(+), 23 deletions(-) diff --git a/hacking/checks.py b/hacking/checks.py index facb0cc6a..0eb485e7e 100644 --- a/hacking/checks.py +++ b/hacking/checks.py @@ -69,8 +69,8 @@ def assert_no_duplicated_setup(logical_line, filename): return yield ( 0, - f"O401: client_manager.{service} mocks are already provided by " - f"the {service} service's FakeClientMixin class", + f"O401: client_manager.{service} mocks are already provided " + f"by the {service} service's FakeClientMixin class", ) @@ -89,7 +89,7 @@ def assert_use_of_client_aliases(logical_line): yield (0, f"0402: prefer {service}_client to sdk_connection.{service}") if match := re.match( - r'(self\.app\.client_manager\.(compute|network|image)+\.[a-z_]+) = mock.Mock', + r'(self\.app\.client_manager\.(compute|network|image)+\.[a-z_]+) = mock.Mock', # noqa: E501 logical_line, ): yield ( diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py index fd941e483..933b01b83 100644 --- a/openstackclient/api/object_store_v1.py +++ b/openstackclient/api/object_store_v1.py @@ -256,7 +256,10 @@ def object_create( # object's name in the container. object_name_str = name if name else object - full_url = f"{urllib.parse.quote(container)}/{urllib.parse.quote(object_name_str)}" + full_url = ( + f"{urllib.parse.quote(container)}/" + f"{urllib.parse.quote(object_name_str)}" + ) with open(object, 'rb') as f: response = self.create( full_url, diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index c6e6cfca2..068476470 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -99,7 +99,8 @@ def find_service_sdk(identity_client, name_type_or_id): if next(services, None): msg = _( - "Multiple service matches found for '%(query)s', use an ID to be more specific." + "Multiple service matches found for '%(query)s', " + "use an ID to be more specific." ) % {"query": name_type_or_id} raise exceptions.CommandError(msg) diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index 2e3d9f9b0..a1dff6ee4 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -315,8 +315,9 @@ def take_action(self, parsed_args): parsed_args.user_domain, ) if domain: - # NOTE(0weng): The API doesn't actually support filtering additionally by domain_id, - # so this doesn't really do anything. + # NOTE(0weng): The API doesn't actually support filtering + # additionally by domain_id, so this doesn't really do + # anything. data = identity_client.user_groups(user, domain_id=domain) else: data = identity_client.user_groups(user) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 6b3b7217e..c6c759096 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -420,7 +420,8 @@ def get_parser(self, prog_name): dest='is_enabled', default=None, help=_( - 'List only enabled users, does nothing with --project and --group' + 'List only enabled users, does nothing with ' + '--project and --group' ), ) parser.add_argument( @@ -429,7 +430,8 @@ def get_parser(self, prog_name): dest='is_enabled', default=None, help=_( - 'List only disabled users, does nothing with --project and --group' + 'List only disabled users, does nothing with ' + '--project and --group' ), ) return parser diff --git a/openstackclient/image/v2/metadef_properties.py b/openstackclient/image/v2/metadef_properties.py index 602777331..440d4010a 100644 --- a/openstackclient/image/v2/metadef_properties.py +++ b/openstackclient/image/v2/metadef_properties.py @@ -127,7 +127,8 @@ def get_parser(self, prog_name): nargs="*", help=_( "Metadef properties to delete (name) " - "(omit this argument to delete all properties in the namespace)" + "(omit this argument to delete all properties " + "in the namespace)" ), ) return parser diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index 9e2ac20cb..8df39d654 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -171,7 +171,8 @@ def get_parser(self, prog_name): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled= True') " + "(this is an alias for " + "'--property replication_enabled= True') " "(requires driver support)" ), ) @@ -181,7 +182,8 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:') " + "(this is an alias for " + "'--property RESKEY:availability_zones:') " "(repeat option to set multiple availability zones)" ), ) @@ -534,7 +536,8 @@ def get_parser(self, prog_name): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled= True') " + "(this is an alias for " + "'--property replication_enabled= True') " "(requires driver support)" ), ) @@ -544,7 +547,8 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:') " + "(this is an alias for " + "'--property RESKEY:availability_zones:') " "(repeat option to set multiple availability zones)" ), ) diff --git a/openstackclient/volume/v3/volume_attachment.py b/openstackclient/volume/v3/volume_attachment.py index 5773a121c..6d36e63fb 100644 --- a/openstackclient/volume/v3/volume_attachment.py +++ b/openstackclient/volume/v3/volume_attachment.py @@ -458,7 +458,7 @@ def take_action(self, parsed_args): } # Update search option with `filters` # if AppendFilters.filters: - # search_opts.update(shell_utils.extract_filters(AppendFilters.filters)) + # search_opts.update(shell_utils.extract_filters(AppendFilters.filters)) # noqa: E501 # TODO(stephenfin): Implement sorting attachments = volume_client.attachments( diff --git a/openstackclient/volume/v3/volume_type.py b/openstackclient/volume/v3/volume_type.py index b5b328cb8..bf193ec60 100644 --- a/openstackclient/volume/v3/volume_type.py +++ b/openstackclient/volume/v3/volume_type.py @@ -172,7 +172,8 @@ def get_parser(self, prog_name): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled= True') " + "(this is an alias for " + "'--property replication_enabled= True') " "(requires driver support)" ), ) @@ -182,7 +183,8 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:') " + "(this is an alias for " + "'--property RESKEY:availability_zones:') " "(repeat option to set multiple availability zones)" ), ) @@ -447,7 +449,8 @@ def get_parser(self, prog_name): default=False, help=_( "List only volume types with replication enabled " - "(this is an alias for '--property replication_enabled= True') " + "(this is an alias for " + "'--property replication_enabled= True') " "(supported by --os-volume-api-version 3.52 or above)" ), ) @@ -457,7 +460,8 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "List only volume types with this availability configured " - "(this is an alias for '--property RESKEY:availability_zones:') " + "(this is an alias for " + "'--property RESKEY:availability_zones:') " "(repeat option to filter on multiple availability zones)" ), ) @@ -616,7 +620,8 @@ def get_parser(self, prog_name): default=False, help=_( "Enabled replication for this volume type " - "(this is an alias for '--property replication_enabled= True') " + "(this is an alias for " + "'--property replication_enabled= True') " "(requires driver support)" ), ) @@ -626,7 +631,8 @@ def get_parser(self, prog_name): dest='availability_zones', help=_( "Set an availability zone for this volume type " - "(this is an alias for '--property RESKEY:availability_zones:') " + "(this is an alias for " + "'--property RESKEY:availability_zones:') " "(repeat option to set multiple availability zones)" ), ) diff --git a/pyproject.toml b/pyproject.toml index aeb5ab495..566289a7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -749,7 +749,7 @@ quote-style = "preserve" docstring-code-format = true [tool.ruff.lint] -select = ["E4", "E7", "E9", "F", "S", "UP"] +select = ["E4", "E5", "E7", "E9", "F", "S", "UP"] [tool.ruff.lint.per-file-ignores] -"openstackclient/tests/*" = ["S"] +"openstackclient/tests/*" = ["E501", "S"] From 060299c749ce455937111e83494f6f20fa559f64 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 24 Nov 2025 12:28:26 +0000 Subject: [PATCH 230/245] Implement conflict resolution Take advantage of functionality recently introduced in cliff to allow us to prefer commands that are in-tree over those provided via plugin packages. This will allow us to move the extensions themselves in-tree. Change-Id: I5dd9bc9743bea779ea1b4a71264c9a77c80033b3 Signed-off-by: Stephen Finucane --- openstackclient/shell.py | 13 +++++++++++-- requirements.txt | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/openstackclient/shell.py b/openstackclient/shell.py index 6bbbb5f7b..dfc559a04 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -26,16 +26,25 @@ import openstackclient from openstackclient.common import clientmanager - DEFAULT_DOMAIN = 'default' +# list of modules that were originally out-of-tree and are now in +# core OSC +IGNORED_MODULES = ( + 'neutron_taas.taas_client.osc', + 'neutronclient.osc.v2.taas', +) class OpenStackShell(shell.OpenStackShell): def __init__(self): + command_manager = commandmanager.CommandManager( + 'openstack.cli', ignored_modules=IGNORED_MODULES + ) + super().__init__( description=__doc__.strip(), version=openstackclient.__version__, - command_manager=commandmanager.CommandManager('openstack.cli'), + command_manager=command_manager, deferred_help=True, ) diff --git a/requirements.txt b/requirements.txt index 9a9c79a98..fc31d7820 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 cryptography>=2.7 # BSD/Apache-2.0 -cliff>=4.8.0 # Apache-2.0 +cliff>=4.13.0 # Apache-2.0 iso8601>=0.1.11 # MIT openstacksdk>=4.6.0 # Apache-2.0 osc-lib>=2.3.0 # Apache-2.0 From dedc1a342c861a6061e51e135a53c8ecb8a5db45 Mon Sep 17 00:00:00 2001 From: Miro Tomaska Date: Thu, 6 Nov 2025 16:11:59 -0500 Subject: [PATCH 231/245] Use openstacksdk test generate_fake_resources factory Instead of building fake test objects in the local fakes.py file, use existing generate_fake_resource(s) factory methods to automatically populate class attributes. Doing this ensures that fake objects are always build with actual attributes of the class. Change-Id: If424b87c79e7dab102cbd8a7938df85411c9465d Signed-off-by: Miro Tomaska --- openstackclient/network/v2/taas/tap_flow.py | 4 +- openstackclient/network/v2/taas/tap_mirror.py | 2 +- .../network/v2/taas/tap_service.py | 2 +- .../tests/unit/network/v2/taas/fakes.py | 118 ------------------ .../unit/network/v2/taas/test_osc_tap_flow.py | 63 +++++----- .../network/v2/taas/test_osc_tap_mirror.py | 33 ++--- .../network/v2/taas/test_osc_tap_service.py | 33 ++--- 7 files changed, 70 insertions(+), 185 deletions(-) delete mode 100644 openstackclient/tests/unit/network/v2/taas/fakes.py diff --git a/openstackclient/network/v2/taas/tap_flow.py b/openstackclient/network/v2/taas/tap_flow.py index 441f559bf..92037f037 100644 --- a/openstackclient/network/v2/taas/tap_flow.py +++ b/openstackclient/network/v2/taas/tap_flow.py @@ -101,12 +101,12 @@ def take_action(self, parsed_args): if parsed_args.port is not None: source_port = client.find_port( parsed_args.port, ignore_missing=False - )['id'] + ).id attrs['source_port'] = source_port if parsed_args.tap_service is not None: tap_service_id = client.find_tap_service( parsed_args.tap_service, ignore_missing=False - )['id'] + ).id attrs['tap_service_id'] = tap_service_id if parsed_args.direction is not None: attrs['direction'] = parsed_args.direction diff --git a/openstackclient/network/v2/taas/tap_mirror.py b/openstackclient/network/v2/taas/tap_mirror.py index a11ea91d9..4d0937787 100644 --- a/openstackclient/network/v2/taas/tap_mirror.py +++ b/openstackclient/network/v2/taas/tap_mirror.py @@ -99,7 +99,7 @@ def take_action(self, parsed_args): if parsed_args.port_id is not None: port_id = client.find_port( parsed_args.port_id, ignore_missing=False - )['id'] + ).id attrs['port_id'] = port_id if parsed_args.directions is not None: attrs['directions'] = parsed_args.directions diff --git a/openstackclient/network/v2/taas/tap_service.py b/openstackclient/network/v2/taas/tap_service.py index 392d76c51..8651a9d73 100644 --- a/openstackclient/network/v2/taas/tap_service.py +++ b/openstackclient/network/v2/taas/tap_service.py @@ -79,7 +79,7 @@ def take_action(self, parsed_args): if parsed_args.port_id is not None: port_id = client.find_port( parsed_args.port_id, ignore_missing=False - )['id'] + ).id attrs['port_id'] = port_id if 'project' in parsed_args and parsed_args.project is not None: attrs['project_id'] = common.find_project( diff --git a/openstackclient/tests/unit/network/v2/taas/fakes.py b/openstackclient/tests/unit/network/v2/taas/fakes.py deleted file mode 100644 index 0480a0d90..000000000 --- a/openstackclient/tests/unit/network/v2/taas/fakes.py +++ /dev/null @@ -1,118 +0,0 @@ -# All Rights Reserved 2020 -# -# 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 copy - -from oslo_utils import uuidutils - - -class FakeTapService: - @staticmethod - def create_tap_service(attrs=None): - """Create a fake tap service.""" - attrs = attrs or {} - tap_service_attrs = { - 'id': uuidutils.generate_uuid(), - 'tenant_id': uuidutils.generate_uuid(), - 'name': 'test_tap_service' + uuidutils.generate_uuid(), - 'status': 'ACTIVE', - } - tap_service_attrs.update(attrs) - return copy.deepcopy(tap_service_attrs) - - @staticmethod - def create_tap_services(attrs=None, count=1): - """Create multiple fake tap services.""" - - tap_services = [] - for i in range(0, count): - if attrs is None: - attrs = {'id': f'fake_id{i}'} - elif getattr(attrs, 'id', None) is None: - attrs['id'] = f'fake_id{i}' - tap_services.append(FakeTapService.create_tap_service(attrs=attrs)) - - return tap_services - - -class FakeTapFlow: - @staticmethod - def create_tap_flow(attrs=None): - """Create a fake tap service.""" - attrs = attrs or {} - tap_flow_attrs = { - 'id': uuidutils.generate_uuid(), - 'tenant_id': uuidutils.generate_uuid(), - 'name': 'test_tap_flow' + uuidutils.generate_uuid(), - 'status': 'ACTIVE', - 'direction': 'BOTH', - } - tap_flow_attrs.update(attrs) - return copy.deepcopy(tap_flow_attrs) - - @staticmethod - def create_tap_flows(attrs=None, count=1): - """Create multiple fake tap flows.""" - - tap_flows = [] - for i in range(0, count): - if attrs is None: - attrs = { - 'id': f'fake_id{i}', - 'source_port': uuidutils.generate_uuid(), - 'tap_service_id': uuidutils.generate_uuid(), - } - elif getattr(attrs, 'id', None) is None: - attrs['id'] = f'fake_id{i}' - tap_flows.append(FakeTapFlow.create_tap_flow(attrs=attrs)) - - return tap_flows - - -class FakeTapMirror: - @staticmethod - def create_tap_mirror(attrs=None): - """Create a fake tap mirror.""" - attrs = attrs or {} - tap_mirror_attrs = { - 'id': uuidutils.generate_uuid(), - 'tenant_id': uuidutils.generate_uuid(), - 'name': 'test_tap_mirror' + uuidutils.generate_uuid(), - 'port_id': uuidutils.generate_uuid(), - 'directions': 'IN=99', - 'remote_ip': '192.10.10.2', - 'mirror_type': 'gre', - } - tap_mirror_attrs.update(attrs) - return copy.deepcopy(tap_mirror_attrs) - - @staticmethod - def create_tap_mirrors(attrs=None, count=1): - """Create multiple fake tap mirrors.""" - - tap_mirrors = [] - for i in range(0, count): - if attrs is None: - attrs = { - 'id': f'fake_id{i}', - 'port_id': uuidutils.generate_uuid(), - 'name': f'test_tap_mirror_{i}', - 'directions': f'IN={99 + i}', - 'remote_ip': f'192.10.10.{i + 3}', - } - elif getattr(attrs, 'id', None) is None: - attrs['id'] = f'fake_id{i}' - tap_mirrors.append(FakeTapMirror.create_tap_mirror(attrs=attrs)) - - return tap_mirrors diff --git a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py index e401a0f97..8e4f185c8 100644 --- a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py +++ b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_flow.py @@ -17,13 +17,14 @@ import uuid from openstack.network.v2 import tap_flow as _tap_flow +from openstack.network.v2 import tap_service as _tap_service +from openstack.test import fakes as sdk_fakes from osc_lib import utils as osc_utils from osc_lib.utils import columns as column_util from openstackclient.network.v2.taas import tap_flow as osc_tap_flow from openstackclient.network.v2.taas import tap_service as osc_tap_service from openstackclient.tests.unit.network.v2 import fakes as network_fakes -from openstackclient.tests.unit.network.v2.taas import fakes columns_long = tuple( @@ -47,9 +48,11 @@ def _get_data(attrs, columns=sorted_columns): class TestCreateTapFlow(network_fakes.TestNetworkV2): columns = ( + 'description', 'direction', 'id', 'name', + 'project_id', 'source_port', 'status', 'tap_service_id', @@ -61,22 +64,23 @@ def setUp(self): def test_create_tap_flow(self): """Test Create Tap Flow.""" - fake_tap_service = fakes.FakeTapService.create_tap_service( - attrs={'port_id': str(uuid.uuid4())} + fake_tap_service = sdk_fakes.generate_fake_resource( + _tap_service.TapService ) port_id = str(uuid.uuid4()) - fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( - attrs={ + fake_port = network_fakes.create_one_port(attrs={'id': port_id}) + fake_tap_flow = sdk_fakes.generate_fake_resource( + _tap_flow.TapFlow, + **{ 'source_port': port_id, 'tap_service_id': fake_tap_service['id'], - } + 'direction': 'BOTH', + }, ) self.app.client_manager.network.create_tap_flow.return_value = ( fake_tap_flow ) - self.app.client_manager.network.find_port.return_value = { - 'id': port_id - } + self.app.client_manager.network.find_port.return_value = fake_port self.app.client_manager.network.find_tap_service.return_value = ( fake_tap_service ) @@ -122,12 +126,8 @@ def setUp(self): def test_list_tap_flows(self): """Test List Tap Flow.""" - fake_tap_flows = fakes.FakeTapFlow.create_tap_flows( - attrs={ - 'source_port': str(uuid.uuid4()), - 'tap_service_id': str(uuid.uuid4()), - }, - count=2, + fake_tap_flows = list( + sdk_fakes.generate_fake_resources(_tap_flow.TapFlow, count=2) ) self.app.client_manager.network.tap_flows.return_value = fake_tap_flows arg_list = [] @@ -159,13 +159,7 @@ def setUp(self): def test_delete_tap_flow(self): """Test Delete tap flow.""" - fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( - attrs={ - 'source_port': str(uuid.uuid4()), - 'tap_service_id': str(uuid.uuid4()), - } - ) - + fake_tap_flow = sdk_fakes.generate_fake_resource(_tap_flow.TapFlow) arg_list = [ fake_tap_flow['id'], ] @@ -184,9 +178,11 @@ def test_delete_tap_flow(self): class TestShowTapFlow(network_fakes.TestNetworkV2): columns = ( + 'description', 'direction', 'id', 'name', + 'project_id', 'source_port', 'status', 'tap_service_id', @@ -201,12 +197,7 @@ def setUp(self): def test_show_tap_flow(self): """Test Show tap flow.""" - fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( - attrs={ - 'source_port': str(uuid.uuid4()), - 'tap_service_id': str(uuid.uuid4()), - } - ) + fake_tap_flow = sdk_fakes.generate_fake_resource(_tap_flow.TapFlow) self.app.client_manager.network.get_tap_flow.return_value = ( fake_tap_flow ) @@ -234,12 +225,19 @@ def test_show_tap_flow(self): class TestUpdateTapFlow(network_fakes.TestNetworkV2): _new_name = 'new_name' + # NOTE(mtomaska): The Resource class from which TapFlow inherits from + # returns duplicate `ID and `Name` keys. columns = ( 'Direction', 'ID', + 'ID', + 'Name', 'Name', 'Status', 'Tenant', + 'description', + 'location', + 'project_id', 'source_port', 'tap_service_id', ) @@ -253,12 +251,7 @@ def setUp(self): def test_update_tap_flow(self): """Test update tap service""" - fake_tap_flow = fakes.FakeTapFlow.create_tap_flow( - attrs={ - 'source_port': str(uuid.uuid4()), - 'tap_service_id': str(uuid.uuid4()), - } - ) + fake_tap_flow = sdk_fakes.generate_fake_resource(_tap_flow.TapFlow) new_tap_flow = copy.deepcopy(fake_tap_flow) new_tap_flow['name'] = self._new_name @@ -280,4 +273,4 @@ def test_update_tap_flow(self): mock_update_t_f = self.app.client_manager.network.update_tap_flow mock_update_t_f.assert_called_once_with(new_tap_flow['id'], **attrs) self.assertEqual(self.columns, columns) - self.assertEqual(_get_data(new_tap_flow), data) + self.assertEqual(_get_data(new_tap_flow, self.columns), data) diff --git a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py index 451d6a25b..10f3251c3 100644 --- a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py +++ b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_mirror.py @@ -15,12 +15,12 @@ import uuid from openstack.network.v2 import tap_mirror +from openstack.test import fakes as sdk_fakes from osc_lib import utils as osc_utils from osc_lib.utils import columns as column_util from openstackclient.network.v2.taas import tap_mirror as osc_tap_mirror from openstackclient.tests.unit.network.v2 import fakes as network_fakes -from openstackclient.tests.unit.network.v2.taas import fakes columns_long = tuple( @@ -44,11 +44,13 @@ def _get_data(attrs, columns=sorted_columns): class TestCreateTapMirror(network_fakes.TestNetworkV2): columns = ( + 'description', 'directions', 'id', 'mirror_type', 'name', 'port_id', + 'project_id', 'remote_ip', ) @@ -58,15 +60,14 @@ def setUp(self): def test_create_tap_mirror(self): port_id = str(uuid.uuid4()) - fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( - attrs={'port_id': port_id} + fake_port = network_fakes.create_one_port(attrs={'id': port_id}) + fake_tap_mirror = sdk_fakes.generate_fake_resource( + tap_mirror.TapMirror, **{'port_id': port_id, 'directions': 'IN=99'} ) self.app.client_manager.network.create_tap_mirror.return_value = ( fake_tap_mirror ) - self.app.client_manager.network.find_port.return_value = { - 'id': port_id - } + self.app.client_manager.network.find_port.return_value = fake_port self.app.client_manager.network.find_tap_mirror.side_effect = ( lambda _, name_or_id: {'id': name_or_id} ) @@ -124,8 +125,8 @@ def setUp(self): def test_list_tap_mirror(self): """Test List Tap Mirror.""" - fake_tap_mirrors = fakes.FakeTapMirror.create_tap_mirrors( - attrs={'port_id': str(uuid.uuid4())}, count=4 + fake_tap_mirrors = list( + sdk_fakes.generate_fake_resources(tap_mirror.TapMirror, count=4) ) self.app.client_manager.network.tap_mirrors.return_value = ( fake_tap_mirrors @@ -162,8 +163,8 @@ def setUp(self): def test_delete_tap_mirror(self): """Test Delete Tap Mirror.""" - fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( - attrs={'port_id': str(uuid.uuid4())} + fake_tap_mirror = sdk_fakes.generate_fake_resource( + tap_mirror.TapMirror ) arg_list = [ @@ -183,11 +184,13 @@ def test_delete_tap_mirror(self): class TestShowTapMirror(network_fakes.TestNetworkV2): columns = ( + 'description', 'directions', 'id', 'mirror_type', 'name', 'port_id', + 'project_id', 'remote_ip', ) @@ -203,8 +206,8 @@ def setUp(self): def test_show_tap_mirror(self): """Test Show Tap Mirror.""" - fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( - attrs={'port_id': str(uuid.uuid4())} + fake_tap_mirror = sdk_fakes.generate_fake_resource( + tap_mirror.TapMirror ) self.app.client_manager.network.get_tap_mirror.return_value = ( fake_tap_mirror @@ -232,11 +235,13 @@ def test_show_tap_mirror(self): class TestUpdateTapMirror(network_fakes.TestNetworkV2): _new_name = 'new_name' columns = ( + 'description', 'directions', 'id', 'mirror_type', 'name', 'port_id', + 'project_id', 'remote_ip', ) @@ -251,8 +256,8 @@ def setUp(self): def test_update_tap_mirror(self): """Test update Tap Mirror""" - fake_tap_mirror = fakes.FakeTapMirror.create_tap_mirror( - attrs={'port_id': str(uuid.uuid4())} + fake_tap_mirror = sdk_fakes.generate_fake_resource( + tap_mirror.TapMirror ) new_tap_mirror = copy.deepcopy(fake_tap_mirror) new_tap_mirror['name'] = self._new_name diff --git a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py index 0b8180151..fa766891e 100644 --- a/openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py +++ b/openstackclient/tests/unit/network/v2/taas/test_osc_tap_service.py @@ -17,12 +17,12 @@ import uuid from openstack.network.v2 import tap_service +from openstack.test import fakes as sdk_fakes from osc_lib import utils as osc_utils from osc_lib.utils import columns as column_util from openstackclient.network.v2.taas import tap_service as osc_tap_service from openstackclient.tests.unit.network.v2 import fakes as network_fakes -from openstackclient.tests.unit.network.v2.taas import fakes columns_long = tuple( @@ -46,9 +46,11 @@ def _get_data(attrs, columns=sorted_columns): class TestCreateTapService(network_fakes.TestNetworkV2): columns = ( + 'description', 'id', 'name', 'port_id', + 'project_id', 'status', ) @@ -59,15 +61,14 @@ def setUp(self): def test_create_tap_service(self): """Test Create Tap Service.""" port_id = str(uuid.uuid4()) - fake_tap_service = fakes.FakeTapService.create_tap_service( - attrs={'port_id': port_id} + fake_port = network_fakes.create_one_port(attrs={'id': port_id}) + fake_tap_service = sdk_fakes.generate_fake_resource( + tap_service.TapService, **{'port_id': port_id} ) self.app.client_manager.network.create_tap_service.return_value = ( fake_tap_service ) - self.app.client_manager.network.find_port.return_value = { - 'id': port_id - } + self.app.client_manager.network.find_port.return_value = fake_port self.app.client_manager.network.find_tap_service.side_effect = ( lambda _, name_or_id: {'id': name_or_id} ) @@ -110,8 +111,8 @@ def setUp(self): def test_list_tap_service(self): """Test List Tap Service.""" - fake_tap_services = fakes.FakeTapService.create_tap_services( - attrs={'port_id': str(uuid.uuid4())}, count=4 + fake_tap_services = list( + sdk_fakes.generate_fake_resources(tap_service.TapService, count=4) ) self.app.client_manager.network.tap_services.return_value = ( fake_tap_services @@ -148,8 +149,8 @@ def setUp(self): def test_delete_tap_service(self): """Test Delete tap service.""" - fake_tap_service = fakes.FakeTapService.create_tap_service( - attrs={'port_id': str(uuid.uuid4())} + fake_tap_service = sdk_fakes.generate_fake_resource( + tap_service.TapService ) arg_list = [ @@ -169,9 +170,11 @@ def test_delete_tap_service(self): class TestShowTapService(network_fakes.TestNetworkV2): columns = ( + 'description', 'id', 'name', 'port_id', + 'project_id', 'status', ) @@ -187,8 +190,8 @@ def setUp(self): def test_show_tap_service(self): """Test Show tap service.""" - fake_tap_service = fakes.FakeTapService.create_tap_service( - attrs={'port_id': str(uuid.uuid4())} + fake_tap_service = sdk_fakes.generate_fake_resource( + tap_service.TapService ) self.app.client_manager.network.get_tap_service.return_value = ( fake_tap_service @@ -217,9 +220,11 @@ class TestUpdateTapService(network_fakes.TestNetworkV2): _new_name = 'new_name' columns = ( + 'description', 'id', 'name', 'port_id', + 'project_id', 'status', ) @@ -234,8 +239,8 @@ def setUp(self): def test_update_tap_service(self): """Test update tap service""" - fake_tap_service = fakes.FakeTapService.create_tap_service( - attrs={'port_id': str(uuid.uuid4())} + fake_tap_service = sdk_fakes.generate_fake_resource( + tap_service.TapService ) new_tap_service = copy.deepcopy(fake_tap_service) new_tap_service['name'] = self._new_name From 3fbe41cd52cd140c4a6382dea155ff35bf86d40d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 21 Nov 2025 16:23:37 +0000 Subject: [PATCH 232/245] clientmanager: Remove legacy cruft No has used Initialize functions in years, while the _auth_required attribute has long since been handled by the base class in osc-lib. Change-Id: I3af9a6d8c339b2170a13346b009392d51e044443 Signed-off-by: Stephen Finucane --- openstackclient/common/clientmanager.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index a0455bf1a..94c25e3f9 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -40,12 +40,6 @@ class ClientManager(clientmanager.ClientManager): in osc-lib so we need to maintain a transition period. """ - # A simple incrementing version for the plugin to know what is available - PLUGIN_INTERFACE_VERSION = "2" - - # Let the commands set this - _auth_required = False - def __init__( self, cli_options=None, @@ -185,9 +179,6 @@ def get_plugin_modules(group): continue mod_list.append(module) - init_func = getattr(module, 'Initialize', None) - if init_func: - init_func('x') # Add the plugin to the ClientManager setattr( From 3cd544df53e7c282092512dc7380840ac254a5c5 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 11 Dec 2025 13:42:39 +0000 Subject: [PATCH 233/245] Add custom command classes These are effectively identical to the osc-lib variants except they include the attributes that the OSC shell implementation will set on this during shell init. This helps from a typing perspective. Change-Id: I53d9058273748ecd4d4eecec5f7291d5f38ce5ab Signed-off-by: Stephen Finucane --- openstackclient/command.py | 27 +++++++++++++++++++ openstackclient/common/availability_zone.py | 2 +- openstackclient/common/configuration.py | 2 +- openstackclient/common/extension.py | 2 +- openstackclient/common/limits.py | 2 +- openstackclient/common/module.py | 2 +- openstackclient/common/project_cleanup.py | 2 +- openstackclient/common/quota.py | 2 +- openstackclient/common/versions.py | 3 +-- openstackclient/compute/v2/agent.py | 2 +- openstackclient/compute/v2/aggregate.py | 2 +- openstackclient/compute/v2/console.py | 2 +- .../compute/v2/console_connection.py | 2 +- openstackclient/compute/v2/flavor.py | 2 +- openstackclient/compute/v2/host.py | 2 +- openstackclient/compute/v2/hypervisor.py | 2 +- .../compute/v2/hypervisor_stats.py | 2 +- openstackclient/compute/v2/keypair.py | 2 +- openstackclient/compute/v2/server.py | 2 +- openstackclient/compute/v2/server_backup.py | 2 +- openstackclient/compute/v2/server_event.py | 2 +- openstackclient/compute/v2/server_group.py | 2 +- openstackclient/compute/v2/server_image.py | 2 +- .../compute/v2/server_migration.py | 2 +- openstackclient/compute/v2/server_volume.py | 2 +- openstackclient/compute/v2/service.py | 2 +- openstackclient/compute/v2/usage.py | 2 +- openstackclient/identity/v2_0/catalog.py | 2 +- openstackclient/identity/v2_0/ec2creds.py | 2 +- openstackclient/identity/v2_0/endpoint.py | 2 +- openstackclient/identity/v2_0/project.py | 2 +- openstackclient/identity/v2_0/role.py | 2 +- .../identity/v2_0/role_assignment.py | 2 +- openstackclient/identity/v2_0/service.py | 2 +- openstackclient/identity/v2_0/token.py | 2 +- openstackclient/identity/v2_0/user.py | 2 +- openstackclient/identity/v3/access_rule.py | 2 +- .../identity/v3/application_credential.py | 2 +- openstackclient/identity/v3/catalog.py | 2 +- openstackclient/identity/v3/consumer.py | 2 +- openstackclient/identity/v3/credential.py | 2 +- openstackclient/identity/v3/domain.py | 2 +- openstackclient/identity/v3/ec2creds.py | 2 +- openstackclient/identity/v3/endpoint.py | 2 +- openstackclient/identity/v3/endpoint_group.py | 2 +- .../identity/v3/federation_protocol.py | 2 +- openstackclient/identity/v3/group.py | 2 +- .../identity/v3/identity_provider.py | 2 +- openstackclient/identity/v3/implied_role.py | 2 +- openstackclient/identity/v3/limit.py | 2 +- openstackclient/identity/v3/mapping.py | 2 +- openstackclient/identity/v3/policy.py | 2 +- openstackclient/identity/v3/project.py | 2 +- openstackclient/identity/v3/region.py | 2 +- .../identity/v3/registered_limit.py | 2 +- openstackclient/identity/v3/role.py | 2 +- .../identity/v3/role_assignment.py | 3 +-- openstackclient/identity/v3/service.py | 2 +- .../identity/v3/service_provider.py | 2 +- openstackclient/identity/v3/token.py | 2 +- openstackclient/identity/v3/trust.py | 2 +- openstackclient/identity/v3/unscoped_saml.py | 2 +- openstackclient/identity/v3/user.py | 2 +- openstackclient/image/v1/image.py | 2 +- openstackclient/image/v2/cache.py | 2 +- openstackclient/image/v2/image.py | 2 +- openstackclient/image/v2/info.py | 2 +- .../image/v2/metadef_namespaces.py | 2 +- openstackclient/image/v2/metadef_objects.py | 2 +- .../image/v2/metadef_properties.py | 2 +- .../v2/metadef_resource_type_association.py | 2 +- .../image/v2/metadef_resource_types.py | 2 +- openstackclient/image/v2/task.py | 2 +- openstackclient/network/common.py | 12 +++++---- openstackclient/network/v2/address_group.py | 2 +- openstackclient/network/v2/address_scope.py | 2 +- .../network/v2/default_security_group_rule.py | 2 +- .../network/v2/floating_ip_port_forwarding.py | 2 +- openstackclient/network/v2/ip_availability.py | 2 +- .../network/v2/l3_conntrack_helper.py | 2 +- openstackclient/network/v2/local_ip.py | 2 +- .../network/v2/local_ip_association.py | 2 +- openstackclient/network/v2/ndp_proxy.py | 2 +- openstackclient/network/v2/network_agent.py | 2 +- .../v2/network_auto_allocated_topology.py | 2 +- openstackclient/network/v2/network_flavor.py | 2 +- .../network/v2/network_flavor_profile.py | 2 +- openstackclient/network/v2/network_meter.py | 2 +- .../network/v2/network_meter_rule.py | 2 +- .../network/v2/network_qos_policy.py | 2 +- .../network/v2/network_qos_rule.py | 2 +- .../network/v2/network_qos_rule_type.py | 2 +- openstackclient/network/v2/network_rbac.py | 2 +- openstackclient/network/v2/network_segment.py | 2 +- .../network/v2/network_segment_range.py | 4 +-- .../network/v2/network_service_provider.py | 2 +- openstackclient/network/v2/network_trunk.py | 2 +- openstackclient/network/v2/port.py | 2 +- openstackclient/network/v2/router.py | 2 +- openstackclient/network/v2/security_group.py | 2 +- openstackclient/network/v2/subnet.py | 2 +- openstackclient/network/v2/subnet_pool.py | 2 +- openstackclient/object/v1/account.py | 2 +- openstackclient/object/v1/container.py | 2 +- openstackclient/object/v1/object.py | 2 +- .../tests/unit/common/test_command.py | 2 +- openstackclient/volume/v2/backup_record.py | 2 +- .../volume/v2/consistency_group.py | 2 +- .../volume/v2/consistency_group_snapshot.py | 2 +- openstackclient/volume/v2/qos_specs.py | 2 +- openstackclient/volume/v2/service.py | 2 +- openstackclient/volume/v2/volume.py | 2 +- openstackclient/volume/v2/volume_backend.py | 2 +- openstackclient/volume/v2/volume_backup.py | 2 +- openstackclient/volume/v2/volume_host.py | 3 +-- openstackclient/volume/v2/volume_snapshot.py | 2 +- .../volume/v2/volume_transfer_request.py | 2 +- openstackclient/volume/v2/volume_type.py | 2 +- .../volume/v3/block_storage_cleanup.py | 2 +- .../volume/v3/block_storage_cluster.py | 2 +- .../volume/v3/block_storage_log_level.py | 2 +- .../volume/v3/block_storage_manage.py | 2 +- .../v3/block_storage_resource_filter.py | 2 +- openstackclient/volume/v3/service.py | 2 +- openstackclient/volume/v3/volume.py | 2 +- .../volume/v3/volume_attachment.py | 2 +- openstackclient/volume/v3/volume_backup.py | 2 +- openstackclient/volume/v3/volume_group.py | 2 +- .../volume/v3/volume_group_snapshot.py | 2 +- .../volume/v3/volume_group_type.py | 2 +- openstackclient/volume/v3/volume_message.py | 2 +- openstackclient/volume/v3/volume_snapshot.py | 2 +- .../volume/v3/volume_transfer_request.py | 2 +- openstackclient/volume/v3/volume_type.py | 2 +- 134 files changed, 167 insertions(+), 141 deletions(-) create mode 100644 openstackclient/command.py diff --git a/openstackclient/command.py b/openstackclient/command.py new file mode 100644 index 000000000..124d755aa --- /dev/null +++ b/openstackclient/command.py @@ -0,0 +1,27 @@ +# 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. + +from cliff import lister +from cliff import show +from osc_lib.command import command + +from openstackclient import shell + + +class Command(command.Command): + app: shell.OpenStackShell + + +class Lister(Command, lister.Lister): ... + + +class ShowOne(Command, show.ShowOne): ... diff --git a/openstackclient/common/availability_zone.py b/openstackclient/common/availability_zone.py index 61f77cc06..6f5e4fd45 100644 --- a/openstackclient/common/availability_zone.py +++ b/openstackclient/common/availability_zone.py @@ -17,9 +17,9 @@ import logging from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/common/configuration.py b/openstackclient/common/configuration.py index 418f7fdd0..4637ad22b 100644 --- a/openstackclient/common/configuration.py +++ b/openstackclient/common/configuration.py @@ -14,8 +14,8 @@ """Configuration action implementations""" from keystoneauth1.loading import base -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ REDACTED = "" diff --git a/openstackclient/common/extension.py b/openstackclient/common/extension.py index 42c2e9b9f..3f9b257bf 100644 --- a/openstackclient/common/extension.py +++ b/openstackclient/common/extension.py @@ -17,9 +17,9 @@ import logging -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py index 0c9280593..6512e0fcd 100644 --- a/openstackclient/common/limits.py +++ b/openstackclient/common/limits.py @@ -17,9 +17,9 @@ import itertools -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/common/module.py b/openstackclient/common/module.py index d4a5b955a..997b5bc9a 100644 --- a/openstackclient/common/module.py +++ b/openstackclient/common/module.py @@ -17,9 +17,9 @@ import sys -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/common/project_cleanup.py b/openstackclient/common/project_cleanup.py index 5b2d89c1a..444f23ec2 100644 --- a/openstackclient/common/project_cleanup.py +++ b/openstackclient/common/project_cleanup.py @@ -20,8 +20,8 @@ import typing as ty from cliff.formatters import table -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 41c57e63d..efe1ce203 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -21,10 +21,10 @@ import typing as ty from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common diff --git a/openstackclient/common/versions.py b/openstackclient/common/versions.py index 662233875..dfd84e059 100644 --- a/openstackclient/common/versions.py +++ b/openstackclient/common/versions.py @@ -14,8 +14,7 @@ """Versions Action Implementation""" -from osc_lib.command import command - +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py index 5bc6603f4..71b68d4c6 100644 --- a/openstackclient/compute/v2/agent.py +++ b/openstackclient/compute/v2/agent.py @@ -18,10 +18,10 @@ import logging from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index cc5817a0f..48f585b5f 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -22,10 +22,10 @@ from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py index bcabcd2d8..ac8e10d99 100644 --- a/openstackclient/compute/v2/console.py +++ b/openstackclient/compute/v2/console.py @@ -16,9 +16,9 @@ """Compute v2 Console action implementations""" from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/console_connection.py b/openstackclient/compute/v2/console_connection.py index d90b2ceb9..97eb1a80e 100644 --- a/openstackclient/compute/v2/console_connection.py +++ b/openstackclient/compute/v2/console_connection.py @@ -13,9 +13,9 @@ """Compute v2 Console auth token implementations.""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index c0e043e8f..de3a71029 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -21,10 +21,10 @@ from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/compute/v2/host.py b/openstackclient/compute/v2/host.py index aa1d1a5eb..58023676d 100644 --- a/openstackclient/compute/v2/host.py +++ b/openstackclient/compute/v2/host.py @@ -16,9 +16,9 @@ """Host action implementations""" from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py index 0a6cc5326..9e1b265b1 100644 --- a/openstackclient/compute/v2/hypervisor.py +++ b/openstackclient/compute/v2/hypervisor.py @@ -21,10 +21,10 @@ from openstack import exceptions as sdk_exceptions from openstack import utils as sdk_utils from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/hypervisor_stats.py b/openstackclient/compute/v2/hypervisor_stats.py index 3b9a68614..d151b4161 100644 --- a/openstackclient/compute/v2/hypervisor_stats.py +++ b/openstackclient/compute/v2/hypervisor_stats.py @@ -13,9 +13,9 @@ """Hypervisor Stats action implementations""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index 97dd04f4e..b7744698b 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -22,10 +22,10 @@ from cryptography.hazmat.primitives.asymmetric import ed25519 from cryptography.hazmat.primitives import serialization from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 500395114..250eacc77 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -28,11 +28,11 @@ from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from openstackclient.api import compute_v2 +from openstackclient import command from openstackclient.common import envvars from openstackclient.common import pagination from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py index b0395db26..bb06f761c 100644 --- a/openstackclient/compute/v2/server_backup.py +++ b/openstackclient/compute/v2/server_backup.py @@ -17,10 +17,10 @@ import importlib -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/server_event.py b/openstackclient/compute/v2/server_event.py index 3275df993..0e1e2b463 100644 --- a/openstackclient/compute/v2/server_event.py +++ b/openstackclient/compute/v2/server_event.py @@ -22,10 +22,10 @@ import iso8601 from openstack import exceptions as sdk_exceptions from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py index 73567488c..f64460111 100644 --- a/openstackclient/compute/v2/server_group.py +++ b/openstackclient/compute/v2/server_group.py @@ -20,10 +20,10 @@ from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py index cb19173bc..26dbb4ccc 100644 --- a/openstackclient/compute/v2/server_image.py +++ b/openstackclient/compute/v2/server_image.py @@ -19,10 +19,10 @@ import logging from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/server_migration.py b/openstackclient/compute/v2/server_migration.py index c356bd81f..f2ea68343 100644 --- a/openstackclient/compute/v2/server_migration.py +++ b/openstackclient/compute/v2/server_migration.py @@ -15,10 +15,10 @@ import uuid from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/compute/v2/server_volume.py b/openstackclient/compute/v2/server_volume.py index a27c63f53..d92d137b7 100644 --- a/openstackclient/compute/v2/server_volume.py +++ b/openstackclient/compute/v2/server_volume.py @@ -15,10 +15,10 @@ """Compute v2 Server action implementations""" from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index 3660ddd49..b911835f8 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -18,10 +18,10 @@ import logging from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index d045c1efe..3fe7f419a 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -19,9 +19,9 @@ import functools from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v2_0/catalog.py b/openstackclient/identity/v2_0/catalog.py index ccac5d04b..a184f97df 100644 --- a/openstackclient/identity/v2_0/catalog.py +++ b/openstackclient/identity/v2_0/catalog.py @@ -16,10 +16,10 @@ import logging from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v2_0/ec2creds.py b/openstackclient/identity/v2_0/ec2creds.py index 25ee8a75d..360a09024 100644 --- a/openstackclient/identity/v2_0/ec2creds.py +++ b/openstackclient/identity/v2_0/ec2creds.py @@ -18,10 +18,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v2_0/endpoint.py b/openstackclient/identity/v2_0/endpoint.py index db8efede1..38f7f4e56 100644 --- a/openstackclient/identity/v2_0/endpoint.py +++ b/openstackclient/identity/v2_0/endpoint.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py index 8939aa12f..a72c40b07 100644 --- a/openstackclient/identity/v2_0/project.py +++ b/openstackclient/identity/v2_0/project.py @@ -20,10 +20,10 @@ from keystoneauth1 import exceptions as ks_exc from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py index 1faab2e5a..e54c07af0 100644 --- a/openstackclient/identity/v2_0/role.py +++ b/openstackclient/identity/v2_0/role.py @@ -18,10 +18,10 @@ import logging from keystoneauth1 import exceptions as ks_exc -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v2_0/role_assignment.py b/openstackclient/identity/v2_0/role_assignment.py index 54d01b4e5..093df6a85 100644 --- a/openstackclient/identity/v2_0/role_assignment.py +++ b/openstackclient/identity/v2_0/role_assignment.py @@ -13,10 +13,10 @@ """Identity v2 Assignment action implementations""" -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ # noqa diff --git a/openstackclient/identity/v2_0/service.py b/openstackclient/identity/v2_0/service.py index 978e926f3..5e8dca735 100644 --- a/openstackclient/identity/v2_0/service.py +++ b/openstackclient/identity/v2_0/service.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v2_0/token.py b/openstackclient/identity/v2_0/token.py index fe9436d8b..ebb2269a9 100644 --- a/openstackclient/identity/v2_0/token.py +++ b/openstackclient/identity/v2_0/token.py @@ -15,9 +15,9 @@ """Identity v2 Token action implementations""" -from osc_lib.command import command from osc_lib import exceptions +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py index c4cd52b8d..d80d0e8d0 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -20,10 +20,10 @@ from cliff import columns as cliff_columns from keystoneauth1 import exceptions as ks_exc -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/access_rule.py b/openstackclient/identity/v3/access_rule.py index 367c64901..94e1b0ae8 100644 --- a/openstackclient/identity/v3/access_rule.py +++ b/openstackclient/identity/v3/access_rule.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py index c6bad267d..bc3e9ad04 100644 --- a/openstackclient/identity/v3/application_credential.py +++ b/openstackclient/identity/v3/application_credential.py @@ -21,10 +21,10 @@ import uuid from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/catalog.py b/openstackclient/identity/v3/catalog.py index a161461ce..e9f03a31f 100644 --- a/openstackclient/identity/v3/catalog.py +++ b/openstackclient/identity/v3/catalog.py @@ -16,10 +16,10 @@ import logging from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/consumer.py b/openstackclient/identity/v3/consumer.py index 933f48aa7..c58441ca8 100644 --- a/openstackclient/identity/v3/consumer.py +++ b/openstackclient/identity/v3/consumer.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py index 7d9c7c46c..02eef649c 100644 --- a/openstackclient/identity/v3/credential.py +++ b/openstackclient/identity/v3/credential.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index 06c7191cb..28481c00d 100644 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -18,10 +18,10 @@ import logging from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/ec2creds.py b/openstackclient/identity/v3/ec2creds.py index 84700f496..dbbf7a247 100644 --- a/openstackclient/identity/v3/ec2creds.py +++ b/openstackclient/identity/v3/ec2creds.py @@ -14,10 +14,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index bc690939d..9083fdc70 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/endpoint_group.py b/openstackclient/identity/v3/endpoint_group.py index e4611a167..3965f3197 100644 --- a/openstackclient/identity/v3/endpoint_group.py +++ b/openstackclient/identity/v3/endpoint_group.py @@ -16,10 +16,10 @@ import json import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/federation_protocol.py b/openstackclient/identity/v3/federation_protocol.py index 4e980860d..850ec0ac7 100644 --- a/openstackclient/identity/v3/federation_protocol.py +++ b/openstackclient/identity/v3/federation_protocol.py @@ -16,10 +16,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index a1dff6ee4..a2c2fd336 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -18,10 +18,10 @@ import logging from openstack import exceptions as sdk_exc -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py index d0c324362..77d2bf339 100644 --- a/openstackclient/identity/v3/identity_provider.py +++ b/openstackclient/identity/v3/identity_provider.py @@ -16,10 +16,10 @@ import logging from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/implied_role.py b/openstackclient/identity/v3/implied_role.py index 3958896b0..c1236ad01 100644 --- a/openstackclient/identity/v3/implied_role.py +++ b/openstackclient/identity/v3/implied_role.py @@ -17,8 +17,8 @@ import logging -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/limit.py b/openstackclient/identity/v3/limit.py index 7f2fe885d..15da04369 100644 --- a/openstackclient/identity/v3/limit.py +++ b/openstackclient/identity/v3/limit.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as common_utils diff --git a/openstackclient/identity/v3/mapping.py b/openstackclient/identity/v3/mapping.py index 8c2d0bf8d..a041f19e5 100644 --- a/openstackclient/identity/v3/mapping.py +++ b/openstackclient/identity/v3/mapping.py @@ -18,10 +18,10 @@ import json import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py index d5b8fec6b..355490395 100644 --- a/openstackclient/identity/v3/policy.py +++ b/openstackclient/identity/v3/policy.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index dd6eb75af..cb91609fa 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -19,10 +19,10 @@ from keystoneauth1 import exceptions as ks_exc from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common from openstackclient.identity.v3 import tag diff --git a/openstackclient/identity/v3/region.py b/openstackclient/identity/v3/region.py index 40d381981..4882c9e9c 100644 --- a/openstackclient/identity/v3/region.py +++ b/openstackclient/identity/v3/region.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/registered_limit.py b/openstackclient/identity/v3/registered_limit.py index 41b8cdac0..e0afb4133 100644 --- a/openstackclient/identity/v3/registered_limit.py +++ b/openstackclient/identity/v3/registered_limit.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as common_utils diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index b4e7bd175..3c580d6f8 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -18,10 +18,10 @@ import logging from openstack import exceptions as sdk_exc -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py index ed9e8c0f7..78c010f0e 100644 --- a/openstackclient/identity/v3/role_assignment.py +++ b/openstackclient/identity/v3/role_assignment.py @@ -13,8 +13,7 @@ """Identity v3 Assignment action implementations""" -from osc_lib.command import command - +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index e8f483266..53a706299 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/service_provider.py b/openstackclient/identity/v3/service_provider.py index 5b49cffaf..02aae66bf 100644 --- a/openstackclient/identity/v3/service_provider.py +++ b/openstackclient/identity/v3/service_provider.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py index 5500ce1d8..cc6d31e77 100644 --- a/openstackclient/identity/v3/token.py +++ b/openstackclient/identity/v3/token.py @@ -15,10 +15,10 @@ """Identity v3 Token action implementations""" -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py index 255f7877d..80808aa12 100644 --- a/openstackclient/identity/v3/trust.py +++ b/openstackclient/identity/v3/trust.py @@ -18,10 +18,10 @@ import logging from openstack import exceptions as sdk_exceptions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/identity/v3/unscoped_saml.py b/openstackclient/identity/v3/unscoped_saml.py index d26035d35..e1efc15cf 100644 --- a/openstackclient/identity/v3/unscoped_saml.py +++ b/openstackclient/identity/v3/unscoped_saml.py @@ -17,9 +17,9 @@ the user can list domains and projects they are allowed to access, and request a scoped token.""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index c6c759096..7a2e8d6ca 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -20,10 +20,10 @@ import typing as ty from openstack import exceptions as sdk_exc -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 91b0db428..3f3d3a1a2 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -24,10 +24,10 @@ from osc_lib.api import utils as api_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ CONTAINER_CHOICES = ["ami", "ari", "aki", "bare", "docker", "ova", "ovf"] diff --git a/openstackclient/image/v2/cache.py b/openstackclient/image/v2/cache.py index 9fbce84e4..952d9ed01 100644 --- a/openstackclient/image/v2/cache.py +++ b/openstackclient/image/v2/cache.py @@ -17,10 +17,10 @@ import datetime import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index f35c57231..fa66c7736 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -30,10 +30,10 @@ from osc_lib.api import utils as api_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.common import progressbar from openstackclient.i18n import _ diff --git a/openstackclient/image/v2/info.py b/openstackclient/image/v2/info.py index 469b15ac2..68848136e 100644 --- a/openstackclient/image/v2/info.py +++ b/openstackclient/image/v2/info.py @@ -12,8 +12,8 @@ from osc_lib.cli import format_columns -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/image/v2/metadef_namespaces.py b/openstackclient/image/v2/metadef_namespaces.py index f753286c4..af30f718f 100644 --- a/openstackclient/image/v2/metadef_namespaces.py +++ b/openstackclient/image/v2/metadef_namespaces.py @@ -18,10 +18,10 @@ import logging from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ _formatters = { diff --git a/openstackclient/image/v2/metadef_objects.py b/openstackclient/image/v2/metadef_objects.py index 09fc249cb..d5cbec1cd 100644 --- a/openstackclient/image/v2/metadef_objects.py +++ b/openstackclient/image/v2/metadef_objects.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/image/v2/metadef_properties.py b/openstackclient/image/v2/metadef_properties.py index 440d4010a..3a923c522 100644 --- a/openstackclient/image/v2/metadef_properties.py +++ b/openstackclient/image/v2/metadef_properties.py @@ -15,10 +15,10 @@ import json import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/image/v2/metadef_resource_type_association.py b/openstackclient/image/v2/metadef_resource_type_association.py index 306783cdd..4d3ee4668 100644 --- a/openstackclient/image/v2/metadef_resource_type_association.py +++ b/openstackclient/image/v2/metadef_resource_type_association.py @@ -12,10 +12,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) diff --git a/openstackclient/image/v2/metadef_resource_types.py b/openstackclient/image/v2/metadef_resource_types.py index 3477df35e..88001b7e1 100644 --- a/openstackclient/image/v2/metadef_resource_types.py +++ b/openstackclient/image/v2/metadef_resource_types.py @@ -12,9 +12,9 @@ """Image V2 Action Implementations""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/image/v2/task.py b/openstackclient/image/v2/task.py index 1b7170f63..a0f1d35de 100644 --- a/openstackclient/image/v2/task.py +++ b/openstackclient/image/v2/task.py @@ -11,9 +11,9 @@ # under the License. from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ _formatters = { diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index 9bcd16583..373fd72af 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -12,19 +12,19 @@ # import abc -import argparse import contextlib import logging import typing as ty -import cliff.app +from cliff import _argparse import openstack.exceptions from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import utils +from openstackclient import shell LOG = logging.getLogger(__name__) @@ -68,7 +68,7 @@ class NetDetectionMixin(metaclass=abc.ABCMeta): present the options for both network types, often qualified accordingly. """ - app: cliff.app.App + app: shell.OpenStackShell @property def _network_type(self): @@ -136,7 +136,7 @@ def split_help(network_help, compute_help): ) ) - def get_parser(self, prog_name: str) -> argparse.ArgumentParser: + def get_parser(self, prog_name: str) -> _argparse.ArgumentParser: LOG.debug('get_parser(%s)', prog_name) parser = super().get_parser(prog_name) # type: ignore parser = self.update_parser_common(parser) @@ -203,6 +203,8 @@ class NetworkAndComputeDelete(NetworkAndComputeCommand, metaclass=abc.ABCMeta): following the rules in doc/source/command-errors.rst. """ + resource: str + def take_action(self, parsed_args): ret = 0 resources = getattr(parsed_args, self.resource, []) diff --git a/openstackclient/network/v2/address_group.py b/openstackclient/network/v2/address_group.py index 53a91561a..178c3afbf 100644 --- a/openstackclient/network/v2/address_group.py +++ b/openstackclient/network/v2/address_group.py @@ -16,10 +16,10 @@ import logging import netaddr -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py index f61dcff55..8a38dab4d 100644 --- a/openstackclient/network/v2/address_scope.py +++ b/openstackclient/network/v2/address_scope.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/default_security_group_rule.py b/openstackclient/network/v2/default_security_group_rule.py index 0f9441488..24475852f 100644 --- a/openstackclient/network/v2/default_security_group_rule.py +++ b/openstackclient/network/v2/default_security_group_rule.py @@ -16,10 +16,10 @@ import logging from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common from openstackclient.network import utils as network_utils diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py index 99546a1d5..cf770c2cd 100644 --- a/openstackclient/network/v2/floating_ip_port_forwarding.py +++ b/openstackclient/network/v2/floating_ip_port_forwarding.py @@ -16,10 +16,10 @@ import logging import typing as ty -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py index e36b89771..f78c7ec86 100644 --- a/openstackclient/network/v2/ip_availability.py +++ b/openstackclient/network/v2/ip_availability.py @@ -14,9 +14,9 @@ """IP Availability Info implementations""" from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/network/v2/l3_conntrack_helper.py b/openstackclient/network/v2/l3_conntrack_helper.py index 982021ec6..742c26396 100644 --- a/openstackclient/network/v2/l3_conntrack_helper.py +++ b/openstackclient/network/v2/l3_conntrack_helper.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) diff --git a/openstackclient/network/v2/local_ip.py b/openstackclient/network/v2/local_ip.py index a2937937e..dde6ccd0c 100644 --- a/openstackclient/network/v2/local_ip.py +++ b/openstackclient/network/v2/local_ip.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/network/v2/local_ip_association.py b/openstackclient/network/v2/local_ip_association.py index 814bbdb0b..123faa67c 100644 --- a/openstackclient/network/v2/local_ip_association.py +++ b/openstackclient/network/v2/local_ip_association.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/network/v2/ndp_proxy.py b/openstackclient/network/v2/ndp_proxy.py index 95e39ce15..78a7ae911 100644 --- a/openstackclient/network/v2/ndp_proxy.py +++ b/openstackclient/network/v2/ndp_proxy.py @@ -17,10 +17,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py index cc4de2b42..b2b4eaa73 100644 --- a/openstackclient/network/v2/network_agent.py +++ b/openstackclient/network/v2/network_agent.py @@ -17,10 +17,10 @@ from cliff import columns as cliff_columns from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) diff --git a/openstackclient/network/v2/network_auto_allocated_topology.py b/openstackclient/network/v2/network_auto_allocated_topology.py index 3f5795e6a..1107cb709 100644 --- a/openstackclient/network/v2/network_auto_allocated_topology.py +++ b/openstackclient/network/v2/network_auto_allocated_topology.py @@ -15,9 +15,9 @@ import logging -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py index c4d262dea..99993f64e 100644 --- a/openstackclient/network/v2/network_flavor.py +++ b/openstackclient/network/v2/network_flavor.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py index eeb4ce899..9792b010a 100644 --- a/openstackclient/network/v2/network_flavor_profile.py +++ b/openstackclient/network/v2/network_flavor_profile.py @@ -13,10 +13,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py index 625b7422b..bb3bf94a1 100644 --- a/openstackclient/network/v2/network_meter.py +++ b/openstackclient/network/v2/network_meter.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py index c7551543e..7a7c231dd 100644 --- a/openstackclient/network/v2/network_meter_rule.py +++ b/openstackclient/network/v2/network_meter_rule.py @@ -16,10 +16,10 @@ import logging import typing as ty -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py index abdd044af..05f6d11be 100644 --- a/openstackclient/network/v2/network_qos_policy.py +++ b/openstackclient/network/v2/network_qos_policy.py @@ -16,10 +16,10 @@ import logging from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py index 86f342f6e..b2ddd823a 100644 --- a/openstackclient/network/v2/network_qos_rule.py +++ b/openstackclient/network/v2/network_qos_rule.py @@ -15,10 +15,10 @@ import itertools -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common diff --git a/openstackclient/network/v2/network_qos_rule_type.py b/openstackclient/network/v2/network_qos_rule_type.py index fd4da84af..c79c5fe01 100644 --- a/openstackclient/network/v2/network_qos_rule_type.py +++ b/openstackclient/network/v2/network_qos_rule_type.py @@ -13,9 +13,9 @@ # License for the specific language governing permissions and limitations # under the License. -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py index d266f352d..200175ac9 100644 --- a/openstackclient/network/v2/network_rbac.py +++ b/openstackclient/network/v2/network_rbac.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py index 76a967ecc..c2e7becfa 100644 --- a/openstackclient/network/v2/network_segment.py +++ b/openstackclient/network/v2/network_segment.py @@ -15,10 +15,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import common diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py index c9b9ccc23..c8b9a0e4c 100644 --- a/openstackclient/network/v2/network_segment_range.py +++ b/openstackclient/network/v2/network_segment_range.py @@ -20,10 +20,10 @@ import logging import typing as ty -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -402,7 +402,7 @@ def take_action(self, parsed_args): 'available', ) - display_props: tuple[str, ...] = tuple() + display_props: tuple[ty.Any, ...] = tuple() for s in data: props = utils.get_item_properties(s, columns) if ( diff --git a/openstackclient/network/v2/network_service_provider.py b/openstackclient/network/v2/network_service_provider.py index f743bfa6e..1433c097a 100644 --- a/openstackclient/network/v2/network_service_provider.py +++ b/openstackclient/network/v2/network_service_provider.py @@ -13,9 +13,9 @@ """Network Service Providers Implementation""" -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index cd0d47ffd..5d1389b9b 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -23,10 +23,10 @@ from osc_lib.cli import format_columns from osc_lib.cli import identity as identity_utils from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index c28131440..080499d4b 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -22,11 +22,11 @@ from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 1ea85331c..1d21a8023 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -23,11 +23,11 @@ from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index c4b56a216..ee56f7236 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -16,11 +16,11 @@ import argparse from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import utils from osc_lib.utils import tags as _tag from openstackclient.api import compute_v2 +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 7dc1df087..4ff7ea8cb 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -20,11 +20,11 @@ from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index 2f900a61d..399ce483f 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -17,11 +17,11 @@ from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common diff --git a/openstackclient/object/v1/account.py b/openstackclient/object/v1/account.py index 686fdcc62..199e5222f 100644 --- a/openstackclient/object/v1/account.py +++ b/openstackclient/object/v1/account.py @@ -15,8 +15,8 @@ from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py index f4250dbba..b0f92c761 100644 --- a/openstackclient/object/v1/container.py +++ b/openstackclient/object/v1/container.py @@ -19,9 +19,9 @@ from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ diff --git a/openstackclient/object/v1/object.py b/openstackclient/object/v1/object.py index cb8e4ffeb..e8ee0fc69 100644 --- a/openstackclient/object/v1/object.py +++ b/openstackclient/object/v1/object.py @@ -19,10 +19,10 @@ from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ diff --git a/openstackclient/tests/unit/common/test_command.py b/openstackclient/tests/unit/common/test_command.py index 8233f8994..1f1efcead 100644 --- a/openstackclient/tests/unit/common/test_command.py +++ b/openstackclient/tests/unit/common/test_command.py @@ -14,9 +14,9 @@ from unittest import mock -from osc_lib.command import command from osc_lib import exceptions +from openstackclient import command from openstackclient.tests.unit import fakes as test_fakes from openstackclient.tests.unit import utils as test_utils diff --git a/openstackclient/volume/v2/backup_record.py b/openstackclient/volume/v2/backup_record.py index 98f7c7171..93492f87f 100644 --- a/openstackclient/volume/v2/backup_record.py +++ b/openstackclient/volume/v2/backup_record.py @@ -16,9 +16,9 @@ import logging -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/consistency_group.py b/openstackclient/volume/v2/consistency_group.py index 347724f92..4910bb129 100644 --- a/openstackclient/volume/v2/consistency_group.py +++ b/openstackclient/volume/v2/consistency_group.py @@ -18,10 +18,10 @@ import logging from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/consistency_group_snapshot.py b/openstackclient/volume/v2/consistency_group_snapshot.py index 3c582d6b1..23c3f1034 100644 --- a/openstackclient/volume/v2/consistency_group_snapshot.py +++ b/openstackclient/volume/v2/consistency_group_snapshot.py @@ -16,10 +16,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/qos_specs.py b/openstackclient/volume/v2/qos_specs.py index 72706a740..39aa99eb4 100644 --- a/openstackclient/volume/v2/qos_specs.py +++ b/openstackclient/volume/v2/qos_specs.py @@ -19,10 +19,10 @@ from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/service.py b/openstackclient/volume/v2/service.py index 48cdba721..7777e7e63 100644 --- a/openstackclient/volume/v2/service.py +++ b/openstackclient/volume/v2/service.py @@ -14,10 +14,10 @@ """Service action implementations""" -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index fa76d776e..6b97d9b7e 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -25,11 +25,11 @@ from openstack import exceptions as sdk_exceptions from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from openstackclient.api import volume_v2 +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/volume/v2/volume_backend.py b/openstackclient/volume/v2/volume_backend.py index 2fbbed64a..e51e37bb9 100644 --- a/openstackclient/volume/v2/volume_backend.py +++ b/openstackclient/volume/v2/volume_backend.py @@ -15,9 +15,9 @@ """Storage backend action implementations""" from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index 30e67e296..e698af676 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -18,10 +18,10 @@ import logging from cliff import columns as cliff_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/volume_host.py b/openstackclient/volume/v2/volume_host.py index 2b0df0aa2..44fd58a5c 100644 --- a/openstackclient/volume/v2/volume_host.py +++ b/openstackclient/volume/v2/volume_host.py @@ -14,8 +14,7 @@ """Volume v2 host action implementations""" -from osc_lib.command import command - +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index fb774611f..113b8badd 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -22,10 +22,10 @@ from openstack.block_storage.v2 import snapshot as _snapshot from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/volume/v2/volume_transfer_request.py b/openstackclient/volume/v2/volume_transfer_request.py index 3bd91d052..dcdc52762 100644 --- a/openstackclient/volume/v2/volume_transfer_request.py +++ b/openstackclient/volume/v2/volume_transfer_request.py @@ -16,10 +16,10 @@ import logging -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index 8df39d654..7b6dc0392 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -20,10 +20,10 @@ from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/volume/v3/block_storage_cleanup.py b/openstackclient/volume/v3/block_storage_cleanup.py index 361440c99..5208504a3 100644 --- a/openstackclient/volume/v3/block_storage_cleanup.py +++ b/openstackclient/volume/v3/block_storage_cleanup.py @@ -11,9 +11,9 @@ # under the License. from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/block_storage_cluster.py b/openstackclient/volume/v3/block_storage_cluster.py index 8cf01b247..d99ec52b0 100644 --- a/openstackclient/volume/v3/block_storage_cluster.py +++ b/openstackclient/volume/v3/block_storage_cluster.py @@ -11,10 +11,10 @@ # under the License. from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/block_storage_log_level.py b/openstackclient/volume/v3/block_storage_log_level.py index 1f8d5b330..2e2fdc513 100644 --- a/openstackclient/volume/v3/block_storage_log_level.py +++ b/openstackclient/volume/v3/block_storage_log_level.py @@ -15,9 +15,9 @@ """Block Storage Service action implementations""" from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/block_storage_manage.py b/openstackclient/volume/v3/block_storage_manage.py index 17c699b99..78756385c 100644 --- a/openstackclient/volume/v3/block_storage_manage.py +++ b/openstackclient/volume/v3/block_storage_manage.py @@ -16,10 +16,10 @@ import argparse from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/block_storage_resource_filter.py b/openstackclient/volume/v3/block_storage_resource_filter.py index 494d9cbe1..fc564386e 100644 --- a/openstackclient/volume/v3/block_storage_resource_filter.py +++ b/openstackclient/volume/v3/block_storage_resource_filter.py @@ -14,10 +14,10 @@ from openstack import utils as sdk_utils from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/service.py b/openstackclient/volume/v3/service.py index 0f14e472c..eecd8e0d0 100644 --- a/openstackclient/volume/v3/service.py +++ b/openstackclient/volume/v3/service.py @@ -15,10 +15,10 @@ """Service action implementations""" from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 54dd5c754..4e689a37c 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -26,11 +26,11 @@ from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils from openstackclient.api import volume_v3 +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/volume/v3/volume_attachment.py b/openstackclient/volume/v3/volume_attachment.py index 6d36e63fb..7e32b0c2a 100644 --- a/openstackclient/volume/v3/volume_attachment.py +++ b/openstackclient/volume/v3/volume_attachment.py @@ -14,10 +14,10 @@ from openstack import utils as sdk_utils from osc_lib.cli import format_columns -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import envvars from openstackclient.common import pagination from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py index 3875f802a..f836eb39b 100644 --- a/openstackclient/volume/v3/volume_backup.py +++ b/openstackclient/volume/v3/volume_backup.py @@ -21,10 +21,10 @@ from cliff import columns as cliff_columns from openstack import utils as sdk_utils from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/volume_group.py b/openstackclient/volume/v3/volume_group.py index 8433e5736..1810feef5 100644 --- a/openstackclient/volume/v3/volume_group.py +++ b/openstackclient/volume/v3/volume_group.py @@ -13,10 +13,10 @@ import argparse from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import envvars from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/volume_group_snapshot.py b/openstackclient/volume/v3/volume_group_snapshot.py index 5a760ac9b..530b7d5d1 100644 --- a/openstackclient/volume/v3/volume_group_snapshot.py +++ b/openstackclient/volume/v3/volume_group_snapshot.py @@ -13,10 +13,10 @@ import logging from openstack import utils as sdk_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import envvars from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/volume_group_type.py b/openstackclient/volume/v3/volume_group_type.py index f1ef61740..bdedd25a1 100644 --- a/openstackclient/volume/v3/volume_group_type.py +++ b/openstackclient/volume/v3/volume_group_type.py @@ -15,10 +15,10 @@ from cinderclient import api_versions from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ LOG = logging.getLogger(__name__) diff --git a/openstackclient/volume/v3/volume_message.py b/openstackclient/volume/v3/volume_message.py index 0fc724a58..b39c57946 100644 --- a/openstackclient/volume/v3/volume_message.py +++ b/openstackclient/volume/v3/volume_message.py @@ -17,10 +17,10 @@ import logging as LOG from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/volume/v3/volume_snapshot.py b/openstackclient/volume/v3/volume_snapshot.py index 9cbc198b7..78e0f116a 100644 --- a/openstackclient/volume/v3/volume_snapshot.py +++ b/openstackclient/volume/v3/volume_snapshot.py @@ -22,10 +22,10 @@ from openstack.block_storage.v3 import snapshot as _snapshot from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common diff --git a/openstackclient/volume/v3/volume_transfer_request.py b/openstackclient/volume/v3/volume_transfer_request.py index b7add1228..afd462603 100644 --- a/openstackclient/volume/v3/volume_transfer_request.py +++ b/openstackclient/volume/v3/volume_transfer_request.py @@ -17,10 +17,10 @@ import logging from cinderclient import api_versions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ diff --git a/openstackclient/volume/v3/volume_type.py b/openstackclient/volume/v3/volume_type.py index bf193ec60..562283ecd 100644 --- a/openstackclient/volume/v3/volume_type.py +++ b/openstackclient/volume/v3/volume_type.py @@ -21,10 +21,10 @@ from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common as identity_common From e799a4a676a81f29a8a77ffb13378700cb338d33 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 11 Dec 2025 15:20:21 +0000 Subject: [PATCH 234/245] typing: Add types to custom formatters We make a lot of use of typing.Any just to get this over the line. We can come back to this later. Change-Id: I03c18b0b44f210b2ad3e4012344d521fb85cae97 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 7 ++++--- openstackclient/compute/v2/usage.py | 8 +++++--- openstackclient/identity/v2_0/catalog.py | 3 ++- openstackclient/identity/v2_0/role_assignment.py | 2 +- openstackclient/identity/v2_0/user.py | 2 +- openstackclient/identity/v3/application_credential.py | 5 +++-- openstackclient/identity/v3/catalog.py | 4 ++-- openstackclient/image/v1/image.py | 4 ++-- openstackclient/network/v2/network.py | 4 ++-- openstackclient/network/v2/network_agent.py | 4 ++-- openstackclient/network/v2/network_qos_policy.py | 3 ++- openstackclient/network/v2/network_trunk.py | 2 +- openstackclient/network/v2/port.py | 2 +- openstackclient/network/v2/router.py | 6 +++--- openstackclient/network/v2/security_group.py | 5 +++-- openstackclient/network/v2/subnet.py | 6 +++--- openstackclient/volume/v2/volume.py | 2 +- openstackclient/volume/v2/volume_backup.py | 2 +- openstackclient/volume/v2/volume_snapshot.py | 2 +- openstackclient/volume/v2/volume_type.py | 3 ++- openstackclient/volume/v3/volume.py | 2 +- openstackclient/volume/v3/volume_backup.py | 2 +- openstackclient/volume/v3/volume_snapshot.py | 2 +- openstackclient/volume/v3/volume_type.py | 4 ++-- 24 files changed, 47 insertions(+), 39 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 250eacc77..1eee828d9 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -21,6 +21,7 @@ import json import logging import os +import typing as ty from cliff import columns as cliff_columns import iso8601 @@ -44,7 +45,7 @@ IMAGE_STRING_FOR_BFV = 'N/A (booted from volume)' -class PowerStateColumn(cliff_columns.FormattableColumn): +class PowerStateColumn(cliff_columns.FormattableColumn[int]): """Generate a formatted string of a server's power state.""" power_states = [ @@ -65,7 +66,7 @@ def human_readable(self): return 'N/A' -class AddressesColumn(cliff_columns.FormattableColumn): +class AddressesColumn(cliff_columns.FormattableColumn[ty.Any]): """Generate a formatted string of a server's addresses.""" def human_readable(self): @@ -86,7 +87,7 @@ def machine_readable(self): } -class HostColumn(cliff_columns.FormattableColumn): +class HostColumn(cliff_columns.FormattableColumn[str | None]): """Generate a formatted string of a hostname.""" def human_readable(self): diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index 3fe7f419a..3015a9e70 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -15,8 +15,10 @@ """Usage action implementations""" +from collections.abc import Collection import datetime import functools +import typing as ty from cliff import columns as cliff_columns from osc_lib import utils @@ -27,7 +29,7 @@ # TODO(stephenfin): This exists in a couple of places and should be moved to a # common module -class ProjectColumn(cliff_columns.FormattableColumn): +class ProjectColumn(cliff_columns.FormattableColumn[str]): """Formattable column for project column. Unlike the parent FormattableColumn class, the initializer of the class @@ -53,12 +55,12 @@ def human_readable(self): return project -class CountColumn(cliff_columns.FormattableColumn): +class CountColumn(cliff_columns.FormattableColumn[Collection[ty.Any]]): def human_readable(self): return len(self._value) if self._value is not None else None -class FloatColumn(cliff_columns.FormattableColumn): +class FloatColumn(cliff_columns.FormattableColumn[float]): def human_readable(self): return float(f"{self._value:.2f}") diff --git a/openstackclient/identity/v2_0/catalog.py b/openstackclient/identity/v2_0/catalog.py index a184f97df..437cad2fc 100644 --- a/openstackclient/identity/v2_0/catalog.py +++ b/openstackclient/identity/v2_0/catalog.py @@ -14,6 +14,7 @@ """Identity v2 Service Catalog action implementations""" import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib import exceptions @@ -26,7 +27,7 @@ LOG = logging.getLogger(__name__) -class EndpointsColumn(cliff_columns.FormattableColumn): +class EndpointsColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): if not self._value: return "" diff --git a/openstackclient/identity/v2_0/role_assignment.py b/openstackclient/identity/v2_0/role_assignment.py index 093df6a85..d616725df 100644 --- a/openstackclient/identity/v2_0/role_assignment.py +++ b/openstackclient/identity/v2_0/role_assignment.py @@ -80,7 +80,7 @@ def take_action(self, parsed_args): parsed_args.project, ) elif parsed_args.authproject: - if auth_ref: + if auth_ref and auth_ref.project_id: project = utils.find_resource( identity_client.projects, auth_ref.project_id ) diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py index d80d0e8d0..244b9e9da 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) -class ProjectColumn(cliff_columns.FormattableColumn): +class ProjectColumn(cliff_columns.FormattableColumn[str]): """Formattable column for project column. Unlike the parent FormattableColumn class, the initializer of the diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py index bc3e9ad04..2894d6ee9 100644 --- a/openstackclient/identity/v3/application_credential.py +++ b/openstackclient/identity/v3/application_credential.py @@ -18,6 +18,7 @@ import datetime import json import logging +import typing as ty import uuid from cliff import columns as cliff_columns @@ -31,11 +32,11 @@ LOG = logging.getLogger(__name__) -class RolesColumn(cliff_columns.FormattableColumn): +class RolesColumn(cliff_columns.FormattableColumn[ty.Any]): """Generate a formatted string of role names.""" def human_readable(self): - return utils.format_list(r['name'] for r in self._value) + return utils.format_list(list(r['name'] for r in self._value)) def _format_application_credential( diff --git a/openstackclient/identity/v3/catalog.py b/openstackclient/identity/v3/catalog.py index e9f03a31f..7d37e6cbd 100644 --- a/openstackclient/identity/v3/catalog.py +++ b/openstackclient/identity/v3/catalog.py @@ -9,11 +9,11 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# """Identity v3 Service Catalog action implementations""" import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib import exceptions @@ -26,7 +26,7 @@ LOG = logging.getLogger(__name__) -class EndpointsColumn(cliff_columns.FormattableColumn): +class EndpointsColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): if not self._value: return "" diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 3f3d3a1a2..58fc3f4dd 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -70,7 +70,7 @@ def _get_columns(item): _formatters = {} -class HumanReadableSizeColumn(cliff_columns.FormattableColumn): +class HumanReadableSizeColumn(cliff_columns.FormattableColumn[int]): def human_readable(self): """Return a formatted visibility string @@ -84,7 +84,7 @@ def human_readable(self): return '' -class VisibilityColumn(cliff_columns.FormattableColumn): +class VisibilityColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): """Return a formatted visibility string diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 7aefe50c3..c8e1f360a 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -24,12 +24,12 @@ from openstackclient.network import common -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' -class RouterExternalColumn(cliff_columns.FormattableColumn): +class RouterExternalColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'External' if self._value else 'Internal' diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py index b2b4eaa73..806422b10 100644 --- a/openstackclient/network/v2/network_agent.py +++ b/openstackclient/network/v2/network_agent.py @@ -26,12 +26,12 @@ LOG = logging.getLogger(__name__) -class AliveColumn(cliff_columns.FormattableColumn): +class AliveColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return ":-)" if self._value else "XXX" -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py index 05f6d11be..ab620b849 100644 --- a/openstackclient/network/v2/network_qos_policy.py +++ b/openstackclient/network/v2/network_qos_policy.py @@ -14,6 +14,7 @@ # under the License. import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib import exceptions @@ -27,7 +28,7 @@ LOG = logging.getLogger(__name__) -class RulesColumn(cliff_columns.FormattableColumn): +class RulesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): return '\n'.join(str(v) for v in self._value) diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index 5d1389b9b..974a99763 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -36,7 +36,7 @@ SUB_PORTS = 'sub_ports' -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 080499d4b..e1205153e 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -34,7 +34,7 @@ LOG = logging.getLogger(__name__) -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 1d21a8023..939167d85 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -35,12 +35,12 @@ LOG = logging.getLogger(__name__) -class AdminStateColumn(cliff_columns.FormattableColumn): +class AdminStateColumn(cliff_columns.FormattableColumn[bool]): def human_readable(self): return 'UP' if self._value else 'DOWN' -class RouterInfoColumn(cliff_columns.FormattableColumn): +class RouterInfoColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): try: return json.dumps(self._value) @@ -48,7 +48,7 @@ def human_readable(self): return '' -class RoutesColumn(cliff_columns.FormattableColumn): +class RoutesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): # Map the route keys to match --route option. for route in self._value or []: diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index ee56f7236..c6930de78 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -14,6 +14,7 @@ """Security Group action implementations""" import argparse +import typing as ty from cliff import columns as cliff_columns from osc_lib import utils @@ -65,12 +66,12 @@ def _format_compute_security_group_rules(sg_rules): return utils.format_list(rules, separator='\n') -class NetworkSecurityGroupRulesColumn(cliff_columns.FormattableColumn): +class NetworkSecurityGroupRulesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): return _format_network_security_group_rules(self._value) -class ComputeSecurityGroupRulesColumn(cliff_columns.FormattableColumn): +class ComputeSecurityGroupRulesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): return _format_compute_security_group_rules(self._value) diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 4ff7ea8cb..3357f8930 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -44,7 +44,7 @@ def _update_arguments(obj_list, parsed_args_list, option): raise exceptions.CommandError(msg) -class AllocationPoolsColumn(cliff_columns.FormattableColumn): +class AllocationPoolsColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): pool_formatted = [ '{}-{}'.format(pool.get('start', ''), pool.get('end', '')) @@ -53,7 +53,7 @@ def human_readable(self): return ','.join(pool_formatted) -class HostRoutesColumn(cliff_columns.FormattableColumn): +class HostRoutesColumn(cliff_columns.FormattableColumn[ty.Any]): def human_readable(self): # Map the host route keys to match --host-route option. return utils.format_list_of_dicts( @@ -61,7 +61,7 @@ def human_readable(self): ) -class UnsortedListColumn(cliff_columns.FormattableColumn): +class UnsortedListColumn(cliff_columns.FormattableColumn[list[ty.Any]]): # format_columns.ListColumn sorts the output, but for things like # DNS server addresses the order matters def human_readable(self): diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 6b97d9b7e..b4761ffab 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -61,7 +61,7 @@ def __call__(self, parser, namespace, values, option_string=None): ) -class AttachmentsColumn(cliff_columns.FormattableColumn): +class AttachmentsColumn(cliff_columns.FormattableColumn[list[str]]): """Formattable column for attachments column. Unlike the parent FormattableColumn class, the initializer of the diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index e698af676..7dbe92c96 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -28,7 +28,7 @@ LOG = logging.getLogger(__name__) -class VolumeIdColumn(cliff_columns.FormattableColumn): +class VolumeIdColumn(cliff_columns.FormattableColumn[str]): """Formattable column for volume ID column. Unlike the parent FormattableColumn class, the initializer of the diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index 113b8badd..3b1dbbabf 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -34,7 +34,7 @@ LOG = logging.getLogger(__name__) -class VolumeIdColumn(cliff_columns.FormattableColumn): +class VolumeIdColumn(cliff_columns.FormattableColumn[str]): """Formattable column for volume ID column. Unlike the parent FormattableColumn class, the initializer of the diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index 7b6dc0392..e7b90af95 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -16,6 +16,7 @@ import functools import logging +import typing as ty from cliff import columns as cliff_columns from osc_lib.cli import format_columns @@ -31,7 +32,7 @@ LOG = logging.getLogger(__name__) -class EncryptionInfoColumn(cliff_columns.FormattableColumn): +class EncryptionInfoColumn(cliff_columns.FormattableColumn[ty.Any]): """Formattable column for encryption info column. Unlike the parent FormattableColumn class, the initializer of the diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 4e689a37c..50ea77fb5 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -62,7 +62,7 @@ def __call__(self, parser, namespace, values, option_string=None): ) -class AttachmentsColumn(cliff_columns.FormattableColumn): +class AttachmentsColumn(cliff_columns.FormattableColumn[list[ty.Any]]): """Formattable column for attachments column. Unlike the parent FormattableColumn class, the initializer of the diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py index f836eb39b..df9a17eb0 100644 --- a/openstackclient/volume/v3/volume_backup.py +++ b/openstackclient/volume/v3/volume_backup.py @@ -31,7 +31,7 @@ LOG = logging.getLogger(__name__) -class VolumeIdColumn(cliff_columns.FormattableColumn): +class VolumeIdColumn(cliff_columns.FormattableColumn[str]): """Formattable column for volume ID column. Unlike the parent FormattableColumn class, the initializer of the diff --git a/openstackclient/volume/v3/volume_snapshot.py b/openstackclient/volume/v3/volume_snapshot.py index 78e0f116a..f89174c3d 100644 --- a/openstackclient/volume/v3/volume_snapshot.py +++ b/openstackclient/volume/v3/volume_snapshot.py @@ -33,7 +33,7 @@ LOG = logging.getLogger(__name__) -class VolumeIdColumn(cliff_columns.FormattableColumn): +class VolumeIdColumn(cliff_columns.FormattableColumn[str]): """Formattable column for volume ID column. Unlike the parent FormattableColumn class, the initializer of the diff --git a/openstackclient/volume/v3/volume_type.py b/openstackclient/volume/v3/volume_type.py index 562283ecd..fbce2f2c9 100644 --- a/openstackclient/volume/v3/volume_type.py +++ b/openstackclient/volume/v3/volume_type.py @@ -10,12 +10,12 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -# """Volume v3 Type action implementations""" import functools import logging +import typing as ty from cinderclient import api_versions from cliff import columns as cliff_columns @@ -32,7 +32,7 @@ LOG = logging.getLogger(__name__) -class EncryptionInfoColumn(cliff_columns.FormattableColumn): +class EncryptionInfoColumn(cliff_columns.FormattableColumn[ty.Any]): """Formattable column for encryption info column. Unlike the parent FormattableColumn class, the initializer of the From a7e2f31ecc32ff939799c09d41234c2a991fc87b Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 28 Nov 2025 16:47:52 +0000 Subject: [PATCH 235/245] volume: Remove negotiation for v1 API Change Ibe1cd6461d2cb78826467078aa17272f171746aa removed support for the v1 volume API. We should have removed this check at the same time. We also remove some god-awful monkey patching that references v1 cinderclient but in practice modified all clients. Change-Id: I3727fd9238df966b3bc59812c5efcf3398da5c72 Signed-off-by: Stephen Finucane --- .../tests/unit/volume/test_find_resource.py | 17 ++++--------- openstackclient/volume/client.py | 24 ++++++------------- 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/openstackclient/tests/unit/volume/test_find_resource.py b/openstackclient/tests/unit/volume/test_find_resource.py index df087dd5d..614fa9a51 100644 --- a/openstackclient/tests/unit/volume/test_find_resource.py +++ b/openstackclient/tests/unit/volume/test_find_resource.py @@ -21,15 +21,6 @@ from osc_lib import utils from openstackclient.tests.unit import utils as test_utils -from openstackclient.volume import client # noqa - - -# Monkey patch for v1 cinderclient -# NOTE(dtroyer): Do here because openstackclient.volume.client -# doesn't do it until the client object is created now. -volumes.Volume.NAME_ATTR = 'display_name' -volume_snapshots.Snapshot.NAME_ATTR = 'display_name' - ID = '1after909' NAME = 'PhilSpector' @@ -42,14 +33,14 @@ def setUp(self): api.client = mock.Mock() api.client.get = mock.Mock() resp = mock.Mock() - body = {"volumes": [{"id": ID, 'display_name': NAME}]} + body = {"volumes": [{"id": ID, 'name': NAME}]} api.client.get.side_effect = [Exception("Not found"), (resp, body)] self.manager = volumes.VolumeManager(api) def test_find(self): result = utils.find_resource(self.manager, NAME) self.assertEqual(ID, result.id) - self.assertEqual(NAME, result.display_name) + self.assertEqual(NAME, result.name) def test_not_find(self): self.assertRaises( @@ -67,14 +58,14 @@ def setUp(self): api.client = mock.Mock() api.client.get = mock.Mock() resp = mock.Mock() - body = {"snapshots": [{"id": ID, 'display_name': NAME}]} + body = {"snapshots": [{"id": ID, 'name': NAME}]} api.client.get.side_effect = [Exception("Not found"), (resp, body)] self.manager = volume_snapshots.SnapshotManager(api) def test_find(self): result = utils.find_resource(self.manager, NAME) self.assertEqual(ID, result.id) - self.assertEqual(NAME, result.display_name) + self.assertEqual(NAME, result.name) def test_not_find(self): self.assertRaises( diff --git a/openstackclient/volume/client.py b/openstackclient/volume/client.py index d3a3406a3..dbef055fa 100644 --- a/openstackclient/volume/client.py +++ b/openstackclient/volume/client.py @@ -20,16 +20,14 @@ from openstackclient.i18n import _ - LOG = logging.getLogger(__name__) DEFAULT_API_VERSION = '3' API_VERSION_OPTION = 'os_volume_api_version' -API_NAME = "volume" +API_NAME = 'volume' API_VERSIONS = { - "1": "cinderclient.v1.client.Client", - "2": "cinderclient.v2.client.Client", - "3": "cinderclient.v3.client.Client", + '2': 'cinderclient.v2.client.Client', + '3': 'cinderclient.v3.client.Client', } # Save the microversion if in use @@ -45,11 +43,6 @@ def make_client(instance): from cinderclient.v3 import volume_snapshots from cinderclient.v3 import volumes - # Check whether the available cinderclient supports v1 or v2 - try: - from cinderclient.v1 import services # noqa - except Exception: - del API_VERSIONS['1'] try: from cinderclient.v2 import services # noqa except Exception: @@ -127,21 +120,18 @@ def check_api_version(check_version): global _volume_api_version - # Copy some logic from novaclient 3.3.0 for basic version detection - # NOTE(dtroyer): This is only enough to resume operations using API - # version 3.0 or any valid version supplied by the user. _volume_api_version = api_versions.get_api_version(check_version) # Bypass X.latest format microversion if not _volume_api_version.is_latest(): - if _volume_api_version > api_versions.APIVersion("3.0"): + if _volume_api_version > api_versions.APIVersion('3.0'): if not _volume_api_version.matches( api_versions.MIN_VERSION, api_versions.MAX_VERSION, ): - msg = _("versions supported by client: %(min)s - %(max)s") % { - "min": api_versions.MIN_VERSION, - "max": api_versions.MAX_VERSION, + msg = _('versions supported by client: %(min)s - %(max)s') % { + 'min': api_versions.MIN_VERSION, + 'max': api_versions.MAX_VERSION, } raise exceptions.CommandError(msg) From 841d95b095105a934a4860a1a6e4b96d5b0555da Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 28 Nov 2025 17:40:20 +0000 Subject: [PATCH 236/245] common: Remove references to pkg_resources Even though the comment here attributed this to stevedore, it was in fact the use of pkg_resources that changed things. Change-Id: I35377dd7d773024aa6423b72b1412e11b1b6f2e4 Signed-off-by: Stephen Finucane --- openstackclient/common/clientmanager.py | 11 +---------- openstackclient/shell.py | 5 +---- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 94c25e3f9..2213f1e46 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -158,16 +158,7 @@ def get_plugin_modules(group): for ep in mgr: LOG.debug('Found plugin %s', ep.name) - # Different versions of stevedore use different - # implementations of EntryPoint from other libraries, which - # are not API-compatible. - try: - module_name = ep.entry_point.module_name - except AttributeError: - try: - module_name = ep.entry_point.module - except AttributeError: - module_name = ep.entry_point.value + module_name = ep.entry_point.module try: module = importlib.import_module(module_name) diff --git a/openstackclient/shell.py b/openstackclient/shell.py index dfc559a04..a494d7438 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -74,10 +74,7 @@ def _final_defaults(self): self._auth_type = 'password' def _load_plugins(self): - """Load plugins via stevedore - - osc-lib has no opinion on what plugins should be loaded - """ + """Load plugins via stevedore.""" # Loop through extensions to get API versions for mod in clientmanager.PLUGIN_MODULES: default_version = getattr(mod, 'DEFAULT_API_VERSION', None) From 748cff59142e716ef81f349de5f7a3f29d8e9e27 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 11 Dec 2025 13:16:30 +0000 Subject: [PATCH 237/245] zuul: Make openstackclient-check-plugins voting This will ensure we do not forget to ignore a module when migrating plugins in-tree. Change-Id: Id4dd657746f7c5f8ebf5ef55964593123303b996 Signed-off-by: Stephen Finucane Depends-on: https://review.opendev.org/c/openstack/openstackclient/+/970618 --- .zuul.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.zuul.yaml b/.zuul.yaml index 74ca67d57..d63ee7c5f 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -173,6 +173,8 @@ - release-notes-jobs-python3 check: jobs: + - openstackclient-check-plugins: + voting: true - osc-build-image: voting: false - osc-functional-devstack From f2f0f92d419d04422c241e0c1d77a48fa2e64013 Mon Sep 17 00:00:00 2001 From: Koya Watanabe Date: Sat, 22 Nov 2025 18:02:27 +0900 Subject: [PATCH 238/245] Remove functional testenv for py38/py39 Python 3.8 and 3.9 are no longer supported. Refer to pyproject.toml for the current supported versions. Change-Id: Ie7f917c26299509050294037cc27e1fd9c20e78b Signed-off-by: Koya Watanabe --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 4392a36a8..1988ec823 100644 --- a/tox.ini +++ b/tox.ini @@ -44,7 +44,7 @@ commands = python -m pip freeze stestr run {posargs} -[testenv:functional{,-tips,-py38,-py39,-py310,-py311,-py312}] +[testenv:functional{,-tips,-py310,-py311,-py312,-py313,-py314}] description = Run functional tests. setenv = From 8dbb7126c68f0885c5dcab12613444885bbbf6e2 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 15 Dec 2025 10:45:17 +0000 Subject: [PATCH 239/245] identity: Use plural dest for append opts Change-Id: I73a263a309e022b7606ced43a814a1d1914bc751 Signed-off-by: Stephen Finucane --- openstackclient/identity/v2_0/project.py | 13 +++++++----- .../identity/v3/application_credential.py | 3 ++- .../identity/v3/identity_provider.py | 15 +++++++------- openstackclient/identity/v3/project.py | 2 +- openstackclient/identity/v3/tag.py | 5 +++-- openstackclient/identity/v3/token.py | 3 ++- openstackclient/identity/v3/user.py | 7 ++++--- .../tests/unit/identity/v2_0/test_project.py | 8 ++++---- .../v3/test_application_credential.py | 2 +- .../identity/v3/test_identity_provider.py | 20 +++++++++---------- .../tests/unit/identity/v3/test_oauth.py | 2 +- .../tests/unit/identity/v3/test_project.py | 2 +- .../tests/unit/identity/v3/test_user.py | 8 ++++---- 13 files changed, 49 insertions(+), 41 deletions(-) diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py index a72c40b07..bf19d7d09 100644 --- a/openstackclient/identity/v2_0/project.py +++ b/openstackclient/identity/v2_0/project.py @@ -59,6 +59,7 @@ def get_parser(self, prog_name): parser.add_argument( '--property', metavar='', + dest='properties', action=parseractions.KeyValueAction, help=_( 'Add a property to ' @@ -79,8 +80,8 @@ def take_action(self, parsed_args): if parsed_args.disable: enabled = False kwargs = {} - if parsed_args.property: - kwargs = parsed_args.property.copy() + if parsed_args.properties: + kwargs.update(parsed_args.properties) try: project = identity_client.tenants.create( @@ -230,6 +231,7 @@ def get_parser(self, prog_name): parser.add_argument( '--property', metavar='', + dest='properties', action=parseractions.KeyValueAction, help=_( 'Set a project property ' @@ -255,8 +257,8 @@ def take_action(self, parsed_args): kwargs['enabled'] = True if parsed_args.disable: kwargs['enabled'] = False - if parsed_args.property: - kwargs.update(parsed_args.property) + if parsed_args.properties: + kwargs.update(parsed_args.properties) if 'id' in kwargs: del kwargs['id'] if 'name' in kwargs: @@ -338,6 +340,7 @@ def get_parser(self, prog_name): parser.add_argument( '--property', metavar='', + dest='properties', action='append', default=[], help=_( @@ -354,7 +357,7 @@ def take_action(self, parsed_args): parsed_args.project, ) kwargs = project._info - for key in parsed_args.property: + for key in parsed_args.properties: if key in kwargs: kwargs[key] = None identity_client.tenants.update(project.id, **kwargs) diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py index 2894d6ee9..86e2ea4e8 100644 --- a/openstackclient/identity/v3/application_credential.py +++ b/openstackclient/identity/v3/application_credential.py @@ -149,6 +149,7 @@ def get_parser(self, prog_name): parser.add_argument( '--role', metavar='', + dest='roles', action='append', default=[], help=_( @@ -208,7 +209,7 @@ def take_action(self, parsed_args): user_id = conn.config.get_auth().get_user_id(conn.identity) role_ids = [] - for role in parsed_args.role: + for role in parsed_args.roles: if is_uuid_like(role): role_ids.append({'id': role}) else: diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py index 77d2bf339..7d90636e3 100644 --- a/openstackclient/identity/v3/identity_provider.py +++ b/openstackclient/identity/v3/identity_provider.py @@ -41,6 +41,7 @@ def get_parser(self, prog_name): identity_remote_id_provider.add_argument( '--remote-id', metavar='', + dest='remote_ids', action='append', help=_( 'Remote IDs to associate with the Identity Provider ' @@ -99,16 +100,15 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity + remote_ids: list[str] | None = None if parsed_args.remote_id_file: file_content = utils.read_blob_file_contents( parsed_args.remote_id_file ) remote_ids = file_content.splitlines() remote_ids = list(map(str.strip, remote_ids)) - else: - remote_ids = ( - parsed_args.remote_id if parsed_args.remote_id else None - ) + elif parsed_args.remote_ids: + remote_ids = parsed_args.remote_ids domain_id = None if parsed_args.domain: @@ -240,6 +240,7 @@ def get_parser(self, prog_name): identity_remote_id_provider.add_argument( '--remote-id', metavar='', + dest='remote_ids', action='append', help=_( 'Remote IDs to associate with the Identity Provider ' @@ -287,8 +288,8 @@ def take_action(self, parsed_args): ) remote_ids = file_content.splitlines() remote_ids = list(map(str.strip, remote_ids)) - elif parsed_args.remote_id: - remote_ids = parsed_args.remote_id + elif parsed_args.remote_ids: + remote_ids = parsed_args.remote_ids # Setup keyword args for the client kwargs = {} @@ -298,7 +299,7 @@ def take_action(self, parsed_args): kwargs['enabled'] = True if parsed_args.disable: kwargs['enabled'] = False - if parsed_args.remote_id_file or parsed_args.remote_id: + if parsed_args.remote_id_file or parsed_args.remote_ids: kwargs['remote_ids'] = remote_ids # TODO(pas-ha) actually check for 3.14 microversion diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index cb91609fa..e70a8a501 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -73,8 +73,8 @@ def get_parser(self, prog_name): parser.add_argument( '--property', metavar='', - action=parseractions.KeyValueAction, dest='properties', + action=parseractions.KeyValueAction, help=_( 'Add a property to ' '(repeat option to set multiple properties)' diff --git a/openstackclient/identity/v3/tag.py b/openstackclient/identity/v3/tag.py index 0909fd122..41493c993 100644 --- a/openstackclient/identity/v3/tag.py +++ b/openstackclient/identity/v3/tag.py @@ -114,6 +114,7 @@ def add_tag_option_to_parser_for_set(parser, resource_name): parser.add_argument( '--remove-tag', action='append', + dest='remove_tags', metavar='', default=[], help=_( @@ -128,8 +129,8 @@ def update_tags_in_args(parsed_args, obj, args): if parsed_args.clear_tags: args['tags'] = [] obj.tags = [] - if parsed_args.remove_tag: - args['tags'] = sorted(set(obj.tags) - set(parsed_args.remove_tag)) + if parsed_args.remove_tags: + args['tags'] = sorted(set(obj.tags) - set(parsed_args.remove_tags)) return if parsed_args.tags: args['tags'] = sorted(set(obj.tags).union(set(parsed_args.tags))) diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py index cc6d31e77..05e374caf 100644 --- a/openstackclient/identity/v3/token.py +++ b/openstackclient/identity/v3/token.py @@ -37,6 +37,7 @@ def get_parser(self, prog_name): parser.add_argument( '--role', metavar='', + dest='roles', action='append', default=[], required=True, @@ -52,7 +53,7 @@ def take_action(self, parsed_args): # NOTE(stevemar): We want a list of role ids roles = [] - for role in parsed_args.role: + for role in parsed_args.roles: role_id = utils.find_resource( identity_client.roles, role, diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 7a2e8d6ca..196a7e062 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -82,9 +82,9 @@ def _get_options_for_user(identity_client, parsed_args): options['multi_factor_auth_enabled'] = True if parsed_args.disable_multi_factor_auth: options['multi_factor_auth_enabled'] = False - if parsed_args.multi_factor_auth_rule: + if parsed_args.multi_factor_auth_rules: auth_rules = [ - rule.split(",") for rule in parsed_args.multi_factor_auth_rule + rule.split(",") for rule in parsed_args.multi_factor_auth_rules ] if auth_rules: options['multi_factor_auth_rules'] = auth_rules @@ -175,7 +175,8 @@ def _add_user_options(parser): parser.add_argument( '--multi-factor-auth-rule', metavar='', - action="append", + dest='multi_factor_auth_rules', + action='append', default=[], help=_( 'Set multi-factor auth rules. For example, to set a rule ' diff --git a/openstackclient/tests/unit/identity/v2_0/test_project.py b/openstackclient/tests/unit/identity/v2_0/test_project.py index 9b203b22b..bb6a64374 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_project.py +++ b/openstackclient/tests/unit/identity/v2_0/test_project.py @@ -195,7 +195,7 @@ def test_project_create_property(self): self.fake_project.name, ] verifylist = [ - ('property', {'fee': 'fi', 'fo': 'fum'}), + ('properties', {'fee': 'fi', 'fo': 'fum'}), ('name', self.fake_project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -464,7 +464,7 @@ def test_project_set_unexist_project(self): ('description', None), ('enable', False), ('disable', False), - ('property', None), + ('properties', None), ] self.projects_mock.get.side_effect = exceptions.NotFound(None) self.projects_mock.find.side_effect = exceptions.NotFound(None) @@ -588,7 +588,7 @@ def test_project_set_property(self): self.fake_project.name, ] verifylist = [ - ('property', {'fee': 'fi', 'fo': 'fum'}), + ('properties', {'fee': 'fi', 'fo': 'fum'}), ('project', self.fake_project.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -683,7 +683,7 @@ def test_project_unset_key(self): self.fake_proj.name, ] verifylist = [ - ('property', ['fee', 'fo']), + ('properties', ['fee', 'fo']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/unit/identity/v3/test_application_credential.py b/openstackclient/tests/unit/identity/v3/test_application_credential.py index a7307d6ee..3a3a80e4a 100644 --- a/openstackclient/tests/unit/identity/v3/test_application_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py @@ -120,7 +120,7 @@ def test_application_credential_create_with_options(self): verifylist = [ ('name', self.application_credential.name), ('secret', 'moresecuresecret'), - ('role', [self.roles.id]), + ('roles', [self.roles.id]), ('expiration', '2024-01-01T00:00:00'), ('description', 'credential for testing'), ] diff --git a/openstackclient/tests/unit/identity/v3/test_identity_provider.py b/openstackclient/tests/unit/identity/v3/test_identity_provider.py index 20e7f497a..c65e947ef 100644 --- a/openstackclient/tests/unit/identity/v3/test_identity_provider.py +++ b/openstackclient/tests/unit/identity/v3/test_identity_provider.py @@ -127,7 +127,7 @@ def test_create_identity_provider_remote_id(self): ] verifylist = [ ('identity_provider_id', identity_fakes.idp_id), - ('remote_id', identity_fakes.idp_remote_ids[:1]), + ('remote_ids', identity_fakes.idp_remote_ids[:1]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -157,7 +157,7 @@ def test_create_identity_provider_remote_ids_multiple(self): ] verifylist = [ ('identity_provider_id', identity_fakes.idp_id), - ('remote_id', identity_fakes.idp_remote_ids), + ('remote_ids', identity_fakes.idp_remote_ids), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) @@ -561,7 +561,7 @@ def prepare(self): ('description', new_description), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) @@ -597,7 +597,7 @@ def prepare(self): ('description', None), ('enable', False), ('disable', True), - ('remote_id', identity_fakes.idp_remote_ids), + ('remote_ids', identity_fakes.idp_remote_ids), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -637,7 +637,7 @@ def prepare(self): ('description', None), ('enable', True), ('disable', False), - ('remote_id', identity_fakes.idp_remote_ids), + ('remote_ids', identity_fakes.idp_remote_ids), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -675,7 +675,7 @@ def prepare(self): ('description', None), ('enable', True), ('disable', False), - ('remote_id', [self.new_remote_id]), + ('remote_ids', [self.new_remote_id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -756,7 +756,7 @@ def prepare(self): ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -776,7 +776,7 @@ def prepare(self): ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ('authorization_ttl', 60), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -800,7 +800,7 @@ def prepare(self): ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ('authorization_ttl', 0), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -816,7 +816,7 @@ def test_identity_provider_set_authttl_negative(self): ('identity_provider', identity_fakes.idp_id), ('enable', False), ('disable', False), - ('remote_id', None), + ('remote_ids', None), ('authorization_ttl', -1), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/unit/identity/v3/test_oauth.py b/openstackclient/tests/unit/identity/v3/test_oauth.py index 576f8b20e..9dcf0be89 100644 --- a/openstackclient/tests/unit/identity/v3/test_oauth.py +++ b/openstackclient/tests/unit/identity/v3/test_oauth.py @@ -109,7 +109,7 @@ def test_authorize_request_tokens(self): ] verifylist = [ ('request_key', identity_fakes.request_token_id), - ('role', [identity_fakes.role_name]), + ('roles', [identity_fakes.role_name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py index 024b74f65..065a65cb1 100644 --- a/openstackclient/tests/unit/identity/v3/test_project.py +++ b/openstackclient/tests/unit/identity/v3/test_project.py @@ -1142,7 +1142,7 @@ def test_project_remove_tags(self): verifylist = [ ('enabled', None), ('project', self.project.name), - ('remove_tag', ['tag1', 'tag2']), + ('remove_tags', ['tag1', 'tag2']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index f0ed91405..2dacc72a8 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -730,7 +730,7 @@ def test_user_create_option_with_multi_factor_auth_rule(self): ] verifylist = [ ( - 'multi_factor_auth_rule', + 'multi_factor_auth_rules', [identity_fakes.mfa_opt1, identity_fakes.mfa_opt2], ), ('enable', False), @@ -769,7 +769,7 @@ def test_user_create_with_multiple_options(self): verifylist = [ ('ignore_password_expiry', True), ('disable_multi_factor_auth', True), - ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]), + ('multi_factor_auth_rules', [identity_fakes.mfa_opt1]), ('enable', False), ('disable', False), ('name', self.user.name), @@ -1667,7 +1667,7 @@ def test_user_set_option_multi_factor_auth_rule(self): ('name', None), ('password', None), ('email', None), - ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]), + ('multi_factor_auth_rules', [identity_fakes.mfa_opt1]), ('project', None), ('enable', False), ('disable', False), @@ -1701,7 +1701,7 @@ def test_user_set_with_multiple_options(self): ('email', None), ('ignore_password_expiry', True), ('enable_multi_factor_auth', True), - ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]), + ('multi_factor_auth_rules', [identity_fakes.mfa_opt1]), ('project', None), ('enable', False), ('disable', False), From 7246a07834563c042a439a8207d79027631c98a3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 15 Dec 2025 11:27:53 +0000 Subject: [PATCH 240/245] taas: Use custom command classes In change I53d9058273748ecd4d4eecec5f7291d5f38ce5ab we added custom Command classes for typing purposes. However, the Tap-as-a-Service code merged around the same time and was missed. Correct this. Change-Id: I3a9fe20b5b8eb54708644527538f27396f29b476 Signed-off-by: Stephen Finucane --- openstackclient/network/v2/taas/tap_flow.py | 2 +- openstackclient/network/v2/taas/tap_mirror.py | 2 +- openstackclient/network/v2/taas/tap_service.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openstackclient/network/v2/taas/tap_flow.py b/openstackclient/network/v2/taas/tap_flow.py index 309c4caa0..ef95edb24 100644 --- a/openstackclient/network/v2/taas/tap_flow.py +++ b/openstackclient/network/v2/taas/tap_flow.py @@ -16,11 +16,11 @@ from osc_lib.cli import format_columns from osc_lib.cli import identity as identity_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils from osc_lib.utils import columns as column_util +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common from openstackclient.network.v2.taas import tap_service diff --git a/openstackclient/network/v2/taas/tap_mirror.py b/openstackclient/network/v2/taas/tap_mirror.py index 52e45e4a9..40e74da1b 100644 --- a/openstackclient/network/v2/taas/tap_mirror.py +++ b/openstackclient/network/v2/taas/tap_mirror.py @@ -13,11 +13,11 @@ import logging from osc_lib.cli import identity as identity_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils from osc_lib.utils import columns as column_util +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common from openstackclient.network.v2 import port as osc_port diff --git a/openstackclient/network/v2/taas/tap_service.py b/openstackclient/network/v2/taas/tap_service.py index e0022b99d..41dda41e3 100644 --- a/openstackclient/network/v2/taas/tap_service.py +++ b/openstackclient/network/v2/taas/tap_service.py @@ -15,11 +15,11 @@ import logging from osc_lib.cli import identity as identity_utils -from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as osc_utils from osc_lib.utils import columns as column_util +from openstackclient import command from openstackclient.i18n import _ from openstackclient.identity import common From e8ae075c386511c0b7176aecb961115def2ca0b8 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 21 Nov 2025 15:42:40 +0000 Subject: [PATCH 241/245] typing: Fixups for typed osc-lib Change-Id: I436983a13e8812d704af2f1eb3f600277ef8a531 Signed-off-by: Stephen Finucane --- openstackclient/common/clientmanager.py | 62 ++++++++++++++++++- openstackclient/common/module.py | 4 +- openstackclient/compute/v2/aggregate.py | 3 +- openstackclient/compute/v2/console.py | 6 +- openstackclient/compute/v2/server_group.py | 4 +- .../identity/v2_0/role_assignment.py | 2 +- openstackclient/identity/v3/access_rule.py | 18 +++++- .../identity/v3/application_credential.py | 26 ++++++-- .../identity/v3/identity_provider.py | 5 +- openstackclient/identity/v3/user.py | 7 ++- openstackclient/image/v1/image.py | 29 +++++---- openstackclient/image/v2/image.py | 18 +++--- openstackclient/network/common.py | 11 ++-- openstackclient/network/v2/taas/tap_flow.py | 5 +- openstackclient/network/v2/taas/tap_mirror.py | 5 +- .../network/v2/taas/tap_service.py | 5 +- openstackclient/shell.py | 8 ++- openstackclient/volume/v2/volume.py | 2 +- .../volume/v3/volume_attachment.py | 7 ++- 19 files changed, 165 insertions(+), 62 deletions(-) diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 2213f1e46..51911aaf9 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -15,15 +15,25 @@ """Manage access to the clients, including authenticating when needed.""" +import argparse +from collections.abc import Callable import importlib import logging import sys import typing as ty +from osc_lib.cli import client_config from osc_lib import clientmanager from osc_lib import shell import stevedore +if ty.TYPE_CHECKING: + from keystoneauth1 import access as ksa_access + from openstack.compute.v2 import _proxy as compute_proxy + from openstack.image.v2 import _proxy as image_proxy + from openstack.network.v2 import _proxy as network_proxy + + from openstackclient.api import object_store_v1 LOG = logging.getLogger(__name__) @@ -40,6 +50,24 @@ class ClientManager(clientmanager.ClientManager): in osc-lib so we need to maintain a transition period. """ + if ty.TYPE_CHECKING: + # we know this will be set by us and will not be nullable + auth_ref: ksa_access.AccessInfo + + # this is a hack to keep mypy happy: the actual attributes are set in + # get_plugin_modules below + # TODO(stephenfin): Change the types of identity and volume once we've + # migrated everything to SDK. Hopefully by then we'll have figured out + # how to statically distinguish between the v2 and v3 versions of both + # services... + # TODO(stephenfin): We also need to migrate object storage... + compute: compute_proxy.Proxy + identity: ty.Any + image: image_proxy.Proxy + network: network_proxy.Proxy + object_store: object_store_v1.APIv1 + volume: ty.Any + def __init__( self, cli_options=None, @@ -75,6 +103,12 @@ def setup_auth(self): self._auth_required and self._cli_options._openstack_config is not None ): + if not isinstance( + self._cli_options._openstack_config, client_config.OSC_Config + ): + # programmer error + raise TypeError('unexpected type for _openstack_config') + self._cli_options._openstack_config._pw_callback = ( shell.prompt_for_password ) @@ -101,6 +135,13 @@ def _fallback_load_auth_plugin(self, e): self._cli_options.config['auth_type'] = self._original_auth_type del self._cli_options.config['auth']['token'] del self._cli_options.config['auth']['endpoint'] + + if not isinstance( + self._cli_options._openstack_config, client_config.OSC_Config + ): + # programmer error + raise TypeError('unexpected type for _openstack_config') + self._cli_options._auth = ( self._cli_options._openstack_config.load_auth_plugin( self._cli_options.config, @@ -138,11 +179,25 @@ def is_volume_endpoint_enabled(self, volume_client=None): # Plugin Support +ArgumentParserT = ty.TypeVar('ArgumentParserT', bound=argparse.ArgumentParser) + + +@ty.runtime_checkable # Optional: allows usage with isinstance() +class PluginModule(ty.Protocol): + DEFAULT_API_VERSION: str + API_VERSION_OPTION: str + API_NAME: str + API_VERSIONS: tuple[str] + + make_client: Callable[..., ty.Any] + build_option_parser: Callable[[ArgumentParserT], ArgumentParserT] + check_api_version: Callable[[str], bool] + def _on_load_failure_callback( manager: stevedore.ExtensionManager, ep: importlib.metadata.EntryPoint, - err: Exception, + err: BaseException, ) -> None: sys.stderr.write( f"WARNING: Failed to import plugin {ep.group}:{ep.name}: {err}.\n" @@ -152,6 +207,7 @@ def _on_load_failure_callback( def get_plugin_modules(group): """Find plugin entry points""" mod_list = [] + mgr: stevedore.ExtensionManager[PluginModule] mgr = stevedore.ExtensionManager( group, on_load_failure_callback=_on_load_failure_callback ) @@ -164,8 +220,8 @@ def get_plugin_modules(group): module = importlib.import_module(module_name) except Exception as err: sys.stderr.write( - f"WARNING: Failed to import plugin {ep.group}:{ep.name}: " - f"{err}.\n" + f"WARNING: Failed to import plugin " + f"{ep.module_name}:{ep.name}: {err}.\n" ) continue diff --git a/openstackclient/common/module.py b/openstackclient/common/module.py index 997b5bc9a..6ca5dc231 100644 --- a/openstackclient/common/module.py +++ b/openstackclient/common/module.py @@ -48,7 +48,9 @@ def take_action(self, parsed_args): columns = ('Command Group', 'Commands') if parsed_args.group: - groups = (group for group in groups if parsed_args.group in group) + groups = sorted( + group for group in groups if parsed_args.group in group + ) commands = [] for group in groups: diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index 48f585b5f..f8c3d9677 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -19,6 +19,7 @@ import logging import typing as ty +from cliff import columns from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions @@ -32,7 +33,7 @@ LOG = logging.getLogger(__name__) -_aggregate_formatters = { +_aggregate_formatters: dict[str, type[columns.FormattableColumn[ty.Any]]] = { 'Hosts': format_columns.ListColumn, 'Metadata': format_columns.DictColumn, 'hosts': format_columns.ListColumn, diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py index ac8e10d99..cbcce4b09 100644 --- a/openstackclient/compute/v2/console.py +++ b/openstackclient/compute/v2/console.py @@ -64,13 +64,15 @@ def take_action(self, parsed_args): output = compute_client.get_server_console_output( server.id, length=parsed_args.lines ) - data = None + data: str | None = None if output: data = output.get('output', None) if data and data[-1] != '\n': data += '\n' - self.app.stdout.write(data) + + if data: + self.app.stdout.write(data) class ShowConsoleURL(command.ShowOne): diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py index f64460111..b74c25626 100644 --- a/openstackclient/compute/v2/server_group.py +++ b/openstackclient/compute/v2/server_group.py @@ -16,7 +16,9 @@ """Compute v2 Server Group action implementations""" import logging +import typing as ty +from cliff import columns from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions @@ -30,7 +32,7 @@ LOG = logging.getLogger(__name__) -_formatters = { +_formatters: dict[str, type[columns.FormattableColumn[ty.Any]]] = { 'member_ids': format_columns.ListColumn, 'policies': format_columns.ListColumn, 'rules': format_columns.DictColumn, diff --git a/openstackclient/identity/v2_0/role_assignment.py b/openstackclient/identity/v2_0/role_assignment.py index d616725df..0aa800ef8 100644 --- a/openstackclient/identity/v2_0/role_assignment.py +++ b/openstackclient/identity/v2_0/role_assignment.py @@ -68,7 +68,7 @@ def take_action(self, parsed_args): parsed_args.user, ) elif parsed_args.authuser: - if auth_ref: + if auth_ref and auth_ref.user_id: user = utils.find_resource( identity_client.users, auth_ref.user_id ) diff --git a/openstackclient/identity/v3/access_rule.py b/openstackclient/identity/v3/access_rule.py index 94e1b0ae8..1859ef6fa 100644 --- a/openstackclient/identity/v3/access_rule.py +++ b/openstackclient/identity/v3/access_rule.py @@ -44,7 +44,11 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) errors = 0 for ac in parsed_args.access_rule: @@ -87,7 +91,11 @@ def take_action(self, parsed_args): ).id else: conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) columns = ('ID', 'Service', 'Method', 'Path') data = identity_client.access_rules(user=user_id) @@ -119,7 +127,11 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) access_rule = identity_client.get_access_rule( user_id, parsed_args.access_rule diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py index 86e2ea4e8..3b38f17b5 100644 --- a/openstackclient/identity/v3/application_credential.py +++ b/openstackclient/identity/v3/application_credential.py @@ -206,7 +206,12 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + + user_id = auth.get_user_id(conn.identity) role_ids = [] for role in parsed_args.roles: @@ -274,7 +279,12 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + + user_id = auth.get_user_id(conn.identity) errors = 0 for ac in parsed_args.application_credential: @@ -327,7 +337,11 @@ def take_action(self, parsed_args): ) else: conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) application_credentials = identity_client.application_credentials( user=user_id @@ -351,7 +365,11 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + user_id = auth.get_user_id(conn.identity) application_credential = identity_client.find_application_credential( user_id, parsed_args.application_credential, ignore_missing=False diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py index 7d90636e3..f1af03f05 100644 --- a/openstackclient/identity/v3/identity_provider.py +++ b/openstackclient/identity/v3/identity_provider.py @@ -137,8 +137,9 @@ def take_action(self, parsed_args): ) idp._info.pop('links', None) - remote_ids = format_columns.ListColumn(idp._info.pop('remote_ids', [])) - idp._info['remote_ids'] = remote_ids + idp._info['remote_ids'] = format_columns.ListColumn( + idp._info.pop('remote_ids', []) + ) return zip(*sorted(idp._info.items())) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 196a7e062..b0ed1d205 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -691,7 +691,12 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.sdk_connection.identity conn = self.app.client_manager.sdk_connection - user_id = conn.config.get_auth().get_user_id(conn.identity) + auth = conn.config.get_auth() + if auth is None: + # this will never happen + raise exceptions.CommandError('invalid authentication info') + + user_id = auth.get_user_id(conn.identity) # FIXME(gyee): there are two scenarios: # diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 58fc3f4dd..0ea7eca71 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -19,6 +19,7 @@ import logging import os import sys +import typing as ty from cliff import columns as cliff_columns from osc_lib.api import utils as api_utils @@ -67,9 +68,6 @@ def _get_columns(item): ) -_formatters = {} - - class HumanReadableSizeColumn(cliff_columns.FormattableColumn[int]): def human_readable(self): """Return a formatted visibility string @@ -340,9 +338,12 @@ def take_action(self, parsed_args): if image: display_columns, columns = _get_columns(image) - _formatters['properties'] = format_columns.DictColumn data = utils.get_item_properties( - image, columns, formatters=_formatters + image, + columns, + formatters={ + 'properties': format_columns.DictColumn, + }, ) return (display_columns, data) elif info: @@ -493,19 +494,19 @@ def take_action(self, parsed_args): column_headers = columns # List of image data received - data = list(image_client.images(**kwargs)) + images = list(image_client.images(**kwargs)) if parsed_args.property: # NOTE(dtroyer): coerce to a list to subscript it in py3 attr, value = list(parsed_args.property.items())[0] api_utils.simple_filter( - data, + images, attr=attr, value=value, property_field='properties', ) - data = utils.sort_items(data, parsed_args.sort) + data = utils.sort_items(images, parsed_args.sort) return ( column_headers, @@ -839,11 +840,13 @@ def take_action(self, parsed_args): parsed_args.image, ignore_missing=False ) + formatters: dict[ + str, type[cliff_columns.FormattableColumn[ty.Any]] + ] = { + 'properties': format_columns.DictColumn, + } if parsed_args.human_readable: - _formatters['size'] = HumanReadableSizeColumn + formatters['size'] = HumanReadableSizeColumn display_columns, columns = _get_columns(image) - _formatters['properties'] = format_columns.DictColumn - data = utils.get_item_properties( - image, columns, formatters=_formatters - ) + data = utils.get_item_properties(image, columns, formatters=formatters) return (display_columns, data) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index fa66c7736..c5dd8aeb7 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -551,7 +551,7 @@ def _take_action_image(self, parsed_args): sign_cert_id = parsed_args.sign_cert_id signer = image_signer.ImageSigner() try: - pw = utils.get_password( + pw: str | None = utils.get_password( self.app.stdin, prompt=( "Please enter private key password, leave " @@ -562,12 +562,11 @@ def _take_action_image(self, parsed_args): if not pw or len(pw) < 1: pw = None - else: - # load_private_key() requires the password to be - # passed as bytes - pw = pw.encode() - signer.load_private_key(sign_key_path, password=pw) + signer.load_private_key( + sign_key_path, + password=pw.encode() if pw else None, + ) except Exception: msg = _( "Error during sign operation: private key " @@ -933,18 +932,19 @@ def take_action(self, parsed_args): if 'limit' in kwargs: # Disable automatic pagination in SDK kwargs['paginated'] = False - data = list(image_client.images(**kwargs)) + + images = list(image_client.images(**kwargs)) if parsed_args.property: for attr, value in parsed_args.property.items(): api_utils.simple_filter( - data, + images, attr=attr, value=value, property_field='properties', ) - data = utils.sort_items(data, parsed_args.sort, str) + data = utils.sort_items(images, parsed_args.sort, str) return ( column_headers, diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index 373fd72af..bc110e0b1 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -24,7 +24,6 @@ from openstackclient import command from openstackclient.i18n import _ from openstackclient.network import utils -from openstackclient import shell LOG = logging.getLogger(__name__) @@ -68,8 +67,6 @@ class NetDetectionMixin(metaclass=abc.ABCMeta): present the options for both network types, often qualified accordingly. """ - app: shell.OpenStackShell - @property def _network_type(self): """Discover whether the running cloud is using neutron or nova-network. @@ -84,7 +81,7 @@ def _network_type(self): # Have we set it up yet for this command? if not hasattr(self, '_net_type'): try: - if self.app.client_manager.is_network_endpoint_enabled(): + if self.app.client_manager.is_network_endpoint_enabled(): # type: ignore net_type = _NET_TYPE_NEUTRON else: net_type = _NET_TYPE_COMPUTE @@ -163,11 +160,13 @@ def update_parser_compute(self, parser): def take_action(self, parsed_args): if self.is_neutron: return self.take_action_network( - self.app.client_manager.network, parsed_args + self.app.client_manager.network, # type: ignore + parsed_args, ) elif self.is_nova_network: return self.take_action_compute( - self.app.client_manager.compute, parsed_args + self.app.client_manager.compute, # type: ignore + parsed_args, ) def take_action_network(self, client, parsed_args): diff --git a/openstackclient/network/v2/taas/tap_flow.py b/openstackclient/network/v2/taas/tap_flow.py index ef95edb24..206fbde7f 100644 --- a/openstackclient/network/v2/taas/tap_flow.py +++ b/openstackclient/network/v2/taas/tap_flow.py @@ -25,13 +25,12 @@ from openstackclient.identity import common from openstackclient.network.v2.taas import tap_service - LOG = logging.getLogger(__name__) TAP_FLOW = 'tap_flow' TAP_FLOWS = f'{TAP_FLOW}s' -_attr_map = ( +_attr_map = [ ('id', 'ID', column_util.LIST_BOTH), ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), ('name', 'Name', column_util.LIST_BOTH), @@ -39,7 +38,7 @@ ('source_port', 'source_port', column_util.LIST_BOTH), ('tap_service_id', 'tap_service_id', column_util.LIST_BOTH), ('direction', 'Direction', column_util.LIST_BOTH), -) +] _formatters = { 'vlan_filter': format_columns.ListColumn, diff --git a/openstackclient/network/v2/taas/tap_mirror.py b/openstackclient/network/v2/taas/tap_mirror.py index 40e74da1b..876109dc4 100644 --- a/openstackclient/network/v2/taas/tap_mirror.py +++ b/openstackclient/network/v2/taas/tap_mirror.py @@ -23,13 +23,12 @@ from openstackclient.network.v2 import port as osc_port from openstackclient.network.v2.taas import tap_service - LOG = logging.getLogger(__name__) TAP_MIRROR = 'tap_mirror' TAP_MIRRORS = f'{TAP_MIRROR}s' -_attr_map = ( +_attr_map = [ ('id', 'ID', column_util.LIST_BOTH), ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), ('name', 'Name', column_util.LIST_BOTH), @@ -37,7 +36,7 @@ ('directions', 'Directions', column_util.LIST_LONG_ONLY), ('remote_ip', 'Remote IP', column_util.LIST_BOTH), ('mirror_type', 'Mirror Type', column_util.LIST_LONG_ONLY), -) +] def _get_columns(item): diff --git a/openstackclient/network/v2/taas/tap_service.py b/openstackclient/network/v2/taas/tap_service.py index 41dda41e3..df27658f5 100644 --- a/openstackclient/network/v2/taas/tap_service.py +++ b/openstackclient/network/v2/taas/tap_service.py @@ -23,19 +23,18 @@ from openstackclient.i18n import _ from openstackclient.identity import common - LOG = logging.getLogger(__name__) TAP_SERVICE = 'tap_service' TAP_SERVICES = f'{TAP_SERVICE}s' -_attr_map = ( +_attr_map = [ ('id', 'ID', column_util.LIST_BOTH), ('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY), ('name', 'Name', column_util.LIST_BOTH), ('port_id', 'Port', column_util.LIST_BOTH), ('status', 'Status', column_util.LIST_BOTH), -) +] def _add_updatable_args(parser): diff --git a/openstackclient/shell.py b/openstackclient/shell.py index a494d7438..743ed2bc8 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -36,6 +36,8 @@ class OpenStackShell(shell.OpenStackShell): + client_manager: clientmanager.ClientManager + def __init__(self): command_manager = commandmanager.CommandManager( 'openstack.cli', ignored_modules=IGNORED_MODULES @@ -57,8 +59,10 @@ def __init__(self): # about them warnings.filterwarnings('ignore', module='openstack') - def build_option_parser(self, description, version): - parser = super().build_option_parser(description, version) + def build_option_parser(self, description, version, argparse_kwargs=None): + parser = super().build_option_parser( + description, version, argparse_kwargs + ) parser = clientmanager.build_plugin_option_parser(parser) parser = auth.build_auth_plugins_option_parser(parser) return parser diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index b4761ffab..61cce04f7 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -61,7 +61,7 @@ def __call__(self, parser, namespace, values, option_string=None): ) -class AttachmentsColumn(cliff_columns.FormattableColumn[list[str]]): +class AttachmentsColumn(cliff_columns.FormattableColumn[list[ty.Any]]): """Formattable column for attachments column. Unlike the parent FormattableColumn class, the initializer of the diff --git a/openstackclient/volume/v3/volume_attachment.py b/openstackclient/volume/v3/volume_attachment.py index 7e32b0c2a..3201da34b 100644 --- a/openstackclient/volume/v3/volume_attachment.py +++ b/openstackclient/volume/v3/volume_attachment.py @@ -11,6 +11,7 @@ # under the License. import logging +import typing as ty from openstack import utils as sdk_utils from osc_lib.cli import format_columns @@ -56,12 +57,12 @@ def _format_attachment(attachment): # VolumeAttachmentManager.create returns a dict while everything else # returns a VolumeAttachment object if isinstance(attachment, dict): - data = [] + data: tuple[ty.Any, ...] = () for column in columns: if column == 'connection_info': - data.append(format_columns.DictColumn(attachment[column])) + data += (format_columns.DictColumn(attachment[column]),) continue - data.append(attachment[column]) + data += (attachment[column],) else: data = utils.get_item_properties( attachment, From 0b05fd89687b9e0da3d7bd04c30bddff195d00b0 Mon Sep 17 00:00:00 2001 From: Doug Goldstein Date: Mon, 15 Dec 2025 10:14:41 -0600 Subject: [PATCH 242/245] fix(keystone): correct the args submitted on user creation When a user is created without a password then no parameter called 'password' should be submitted to the keystone API. This removes the incorrect 'password' with null being supplied. Closes-Bug: 2136148 Change-Id: If1c2eb5db360764a5f7660ce4e5353da85b6d3da Signed-off-by: Doug Goldstein --- openstackclient/identity/v3/user.py | 4 +- .../tests/unit/identity/v3/test_user.py | 42 ------------------- ...ate-user-no-password-619bcddcd046dda8.yaml | 6 +++ 3 files changed, 9 insertions(+), 43 deletions(-) create mode 100644 releasenotes/notes/keystone-create-user-no-password-619bcddcd046dda8.yaml diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 7a2e8d6ca..919939605 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -298,6 +298,9 @@ def take_action(self, parsed_args): "when a user does not have a password." ) ) + else: + kwargs['password'] = password + options = _get_options_for_user(identity_client, parsed_args) if options: kwargs['options'] = options @@ -306,7 +309,6 @@ def take_action(self, parsed_args): user = identity_client.create_user( is_enabled=is_enabled, name=parsed_args.name, - password=password, **kwargs, ) except sdk_exc.ConflictException: diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index f0ed91405..ab904e545 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -94,7 +94,6 @@ def test_user_create_no_options(self): kwargs = { 'name': self.user.name, 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -138,7 +137,6 @@ def test_user_create_password_prompt(self): self.user.name, ] verifylist = [ - ('password', None), ('password_prompt', True), ('enable', False), ('disable', False), @@ -171,7 +169,6 @@ def test_user_create_password_prompt_no_warning(self): self.user.name, ] verifylist = [ - ('password', None), ('password_prompt', True), ('enable', False), ('disable', False), @@ -236,7 +233,6 @@ def test_user_create_email(self): 'name': self.user.name, 'email': 'barney@example.com', 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -267,7 +263,6 @@ def test_user_create_project(self): 'name': self.user.name, 'default_project_id': self.project.id, 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -312,7 +307,6 @@ def test_user_create_project_domain(self): 'name': self.user.name, 'default_project_id': self.project.id, 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_once_with(**kwargs) self.identity_sdk_client.find_domain.assert_called_once_with( @@ -357,7 +351,6 @@ def test_user_create_domain(self): 'name': self.user.name, 'domain_id': self.domain.id, 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -385,7 +378,6 @@ def test_user_create_enable(self): kwargs = { 'name': self.user.name, 'is_enabled': True, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -413,7 +405,6 @@ def test_user_create_disable(self): kwargs = { 'name': self.user.name, 'is_enabled': False, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -443,7 +434,6 @@ def test_user_create_ignore_lockout_failure_attempts(self): 'name': self.user.name, 'is_enabled': True, 'options': {'ignore_lockout_failure_attempts': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -473,7 +463,6 @@ def test_user_create_no_ignore_lockout_failure_attempts(self): 'name': self.user.name, 'is_enabled': True, 'options': {'ignore_lockout_failure_attempts': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -503,7 +492,6 @@ def test_user_create_ignore_password_expiry(self): 'name': self.user.name, 'is_enabled': True, 'options': {'ignore_password_expiry': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -533,7 +521,6 @@ def test_user_create_no_ignore_password_expiry(self): 'name': self.user.name, 'is_enabled': True, 'options': {'ignore_password_expiry': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -563,7 +550,6 @@ def test_user_create_ignore_change_password_upon_first_use(self): 'name': self.user.name, 'is_enabled': True, 'options': {'ignore_change_password_upon_first_use': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -593,7 +579,6 @@ def test_user_create_no_ignore_change_password_upon_first_use(self): 'name': self.user.name, 'is_enabled': True, 'options': {'ignore_change_password_upon_first_use': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -623,7 +608,6 @@ def test_user_create_enables_lock_password(self): 'name': self.user.name, 'is_enabled': True, 'options': {'lock_password': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -653,7 +637,6 @@ def test_user_create_disables_lock_password(self): 'name': self.user.name, 'is_enabled': True, 'options': {'lock_password': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -683,7 +666,6 @@ def test_user_create_enable_multi_factor_auth(self): 'name': self.user.name, 'is_enabled': True, 'options': {'multi_factor_auth_enabled': True}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -713,7 +695,6 @@ def test_user_create_disable_multi_factor_auth(self): 'name': self.user.name, 'is_enabled': True, 'options': {'multi_factor_auth_enabled': False}, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -751,7 +732,6 @@ def test_user_create_option_with_multi_factor_auth_rule(self): 'options': { 'multi_factor_auth_rules': [["password", "totp"], ["password"]] }, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -790,7 +770,6 @@ def test_user_create_with_multiple_options(self): 'multi_factor_auth_enabled': False, 'multi_factor_auth_rules': [["password", "totp"]], }, - 'password': None, } self.identity_sdk_client.create_user.assert_called_with(**kwargs) @@ -1084,7 +1063,6 @@ def test_user_set_no_options(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', None), ('enable', False), @@ -1105,7 +1083,6 @@ def test_user_set_name(self): ] verifylist = [ ('name', 'qwerty'), - ('password', None), ('email', None), ('project', None), ('enable', False), @@ -1136,7 +1113,6 @@ def test_user_set_specify_domain(self): ] verifylist = [ ('name', 'qwerty'), - ('password', None), ('domain', self.domain.id), ('email', None), ('project', None), @@ -1192,7 +1168,6 @@ def test_user_set_password_prompt(self): ] verifylist = [ ('name', None), - ('password', None), ('password_prompt', True), ('email', None), ('project', None), @@ -1225,7 +1200,6 @@ def test_user_set_email(self): ] verifylist = [ ('name', None), - ('password', None), ('email', 'barney@example.com'), ('project', None), ('enable', False), @@ -1254,7 +1228,6 @@ def test_user_set_project(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', self.project.id), ('enable', False), @@ -1296,7 +1269,6 @@ def test_user_set_project_domain(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', self.project.id), ('project_domain', self.project.domain_id), @@ -1330,7 +1302,6 @@ def test_user_set_enable(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', None), ('enable', True), @@ -1357,7 +1328,6 @@ def test_user_set_disable(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('project', None), ('enable', False), @@ -1384,7 +1354,6 @@ def test_user_set_ignore_lockout_failure_attempts(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('ignore_lockout_failure_attempts', True), ('project', None), @@ -1412,7 +1381,6 @@ def test_user_set_no_ignore_lockout_failure_attempts(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('no_ignore_lockout_failure_attempts', True), ('project', None), @@ -1440,7 +1408,6 @@ def test_user_set_ignore_password_expiry(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('ignore_password_expiry', True), ('project', None), @@ -1468,7 +1435,6 @@ def test_user_set_no_ignore_password_expiry(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('no_ignore_password_expiry', True), ('project', None), @@ -1496,7 +1462,6 @@ def test_user_set_ignore_change_password_upon_first_use(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('ignore_change_password_upon_first_use', True), ('project', None), @@ -1524,7 +1489,6 @@ def test_user_set_no_ignore_change_password_upon_first_use(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('no_ignore_change_password_upon_first_use', True), ('project', None), @@ -1552,7 +1516,6 @@ def test_user_set_enable_lock_password(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('enable_lock_password', True), ('project', None), @@ -1580,7 +1543,6 @@ def test_user_set_disable_lock_password(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('disable_lock_password', True), ('project', None), @@ -1608,7 +1570,6 @@ def test_user_set_enable_multi_factor_auth(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('enable_multi_factor_auth', True), ('project', None), @@ -1636,7 +1597,6 @@ def test_user_set_disable_multi_factor_auth(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('disable_multi_factor_auth', True), ('project', None), @@ -1665,7 +1625,6 @@ def test_user_set_option_multi_factor_auth_rule(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('multi_factor_auth_rule', [identity_fakes.mfa_opt1]), ('project', None), @@ -1697,7 +1656,6 @@ def test_user_set_with_multiple_options(self): ] verifylist = [ ('name', None), - ('password', None), ('email', None), ('ignore_password_expiry', True), ('enable_multi_factor_auth', True), diff --git a/releasenotes/notes/keystone-create-user-no-password-619bcddcd046dda8.yaml b/releasenotes/notes/keystone-create-user-no-password-619bcddcd046dda8.yaml new file mode 100644 index 000000000..7cd6acba0 --- /dev/null +++ b/releasenotes/notes/keystone-create-user-no-password-619bcddcd046dda8.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + [Bug `2136148 `_] Keystone allows + users to be created with no password but no value should be submitted for + the password instead of a ``null`` value. From ed2dc692ddaf0b5b7fd62d2c7e0eb03c2e4b1287 Mon Sep 17 00:00:00 2001 From: Abhishek Kekane Date: Thu, 18 Dec 2025 06:43:10 +0000 Subject: [PATCH 243/245] Fix image owner change when accepting membership with --project When using 'openstack image set --project --accept ', the command incorrectly changed the image owner. The --project parameter when used with membership flags should only identify which member's status to update, not change ownership. Closes-Bug: #2136795 Change-Id: I1044b51f38000fb5339740bc40c7f8645c794402 Signed-off-by: Abhishek Kekane --- openstackclient/image/v2/image.py | 5 +- .../tests/unit/image/v2/test_image.py | 111 ++++++++++++++++++ ...-set-project-accept-owner-bug-2136795.yaml | 10 ++ 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/fix-image-set-project-accept-owner-bug-2136795.yaml diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index c5dd8aeb7..cbb6d874d 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -1393,7 +1393,10 @@ def take_action(self, parsed_args): if parsed_args.visibility is not None: kwargs['visibility'] = parsed_args.visibility - if parsed_args.project: + # Only set owner_id if --project is used WITHOUT membership flags + # When --project is used with --accept/--reject/--pending, it should + # only identify which member's status to update, not change ownership + if parsed_args.project and not parsed_args.membership: # We already did the project lookup above kwargs['owner_id'] = project_id diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index 7eb7331f3..e6de9f2eb 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -1295,6 +1295,117 @@ def test_image_set_membership_option_pending(self): # the 'update membership' route. self.image_client.update_image.assert_called_with(self._image.id) + def test_image_set_membership_accept_with_project_no_owner_change(self): + """Test that --project with --accept doesn't change image owner.""" + membership = image_fakes.create_one_image_member( + attrs={ + 'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', + 'member_id': self.project.id, + } + ) + self.image_client.update_member.return_value = membership + + arglist = [ + '--project', + self.project.name, + '--accept', + self._image.id, + ] + verifylist = [ + ('project', self.project.name), + ('membership', 'accepted'), + ('image', self._image.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.image_client.update_member.assert_called_once_with( + image=self._image.id, + member=self.project.id, + status='accepted', + ) + + self.image_client.update_image.assert_called() + call_args = self.image_client.update_image.call_args + if call_args: + args, kwargs = call_args + self.assertNotIn('owner_id', kwargs) + + def test_image_set_membership_reject_with_project_no_owner_change(self): + """Test that --project with --reject doesn't change image owner.""" + membership = image_fakes.create_one_image_member( + attrs={ + 'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', + 'member_id': self.project.id, + } + ) + self.image_client.update_member.return_value = membership + + arglist = [ + '--project', + self.project.name, + '--reject', + self._image.id, + ] + verifylist = [ + ('project', self.project.name), + ('membership', 'rejected'), + ('image', self._image.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.image_client.update_member.assert_called_once_with( + image=self._image.id, + member=self.project.id, + status='rejected', + ) + + self.image_client.update_image.assert_called() + call_args = self.image_client.update_image.call_args + if call_args: + args, kwargs = call_args + self.assertNotIn('owner_id', kwargs) + + def test_image_set_membership_pending_with_project_no_owner_change(self): + """Test that --project with --pending doesn't change image owner.""" + membership = image_fakes.create_one_image_member( + attrs={ + 'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', + 'member_id': self.project.id, + } + ) + self.image_client.update_member.return_value = membership + + arglist = [ + '--project', + self.project.name, + '--pending', + self._image.id, + ] + verifylist = [ + ('project', self.project.name), + ('membership', 'pending'), + ('image', self._image.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.image_client.update_member.assert_called_once_with( + image=self._image.id, + member=self.project.id, + status='pending', + ) + + self.image_client.update_image.assert_called() + call_args = self.image_client.update_image.call_args + if call_args: + args, kwargs = call_args + self.assertNotIn('owner_id', kwargs) + def test_image_set_options(self): arglist = [ '--name', diff --git a/releasenotes/notes/fix-image-set-project-accept-owner-bug-2136795.yaml b/releasenotes/notes/fix-image-set-project-accept-owner-bug-2136795.yaml new file mode 100644 index 000000000..4cd755b3f --- /dev/null +++ b/releasenotes/notes/fix-image-set-project-accept-owner-bug-2136795.yaml @@ -0,0 +1,10 @@ +--- +fixes: + - | + Fix a bug where using ``openstack image set --project + --accept `` incorrectly changed the image owner to the specified + project instead of only updating the member status. The ``--project`` + parameter when used with ``--accept``, ``--reject``, or ``--pending`` + should only identify which member's status to update, not change the + image ownership. + [Bug `2136795 `_] From 0a937332932b2a0263b12d307026fee43d125418 Mon Sep 17 00:00:00 2001 From: Andriy Kurilin Date: Wed, 7 Jan 2026 13:26:47 +0100 Subject: [PATCH 244/245] Fix quota usage and reservation display Fix `openstack quota show --usage` to correctly display resource usage and reservations by applying proper name normalization for corresponding sections of data. Previously, name normalization was applied only for "limits" which is the root section, leaving 'usage' and 'reservation' sections untouched. Change-Id: Id14fe894b30a74b9b8d78b00c3d4ff151f8b4210 Closes-bug: #2137636 Signed-off-by: Andriy Kurilin --- openstackclient/common/quota.py | 33 ++++++----- .../tests/unit/common/test_quota.py | 59 +++++++++++++++++++ ...-quota-usage-display-2d8f07dccc21f79c.yaml | 5 ++ 3 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 releasenotes/notes/bug-2137636-fix-quota-usage-display-2d8f07dccc21f79c.yaml diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index efe1ce203..6d0025a75 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -797,20 +797,25 @@ def take_action(self, parsed_args): info.update(volume_quota_info) info.update(network_quota_info) - # Map the internal quota names to the external ones - # COMPUTE_QUOTAS and NETWORK_QUOTAS share floating-ips, - # secgroup-rules and secgroups as dict value, so when - # neutron is enabled, quotas of these three resources - # in nova will be replaced by neutron's. - for k, v in itertools.chain( - COMPUTE_QUOTAS.items(), - NOVA_NETWORK_QUOTAS.items(), - VOLUME_QUOTAS.items(), - NETWORK_QUOTAS.items(), - ): - if not k == v and info.get(k) is not None: - info[v] = info[k] - info.pop(k) + def _normalize_names(section: dict) -> None: + # Map the internal quota names to the external ones + # COMPUTE_QUOTAS and NETWORK_QUOTAS share floating-ips, + # secgroup-rules and secgroups as dict value, so when + # neutron is enabled, quotas of these three resources + # in nova will be replaced by neutron's. + for k, v in itertools.chain( + COMPUTE_QUOTAS.items(), + NOVA_NETWORK_QUOTAS.items(), + VOLUME_QUOTAS.items(), + NETWORK_QUOTAS.items(), + ): + if not k == v and section.get(k) is not None: + section[v] = section.pop(k) + + _normalize_names(info) + if parsed_args.usage: + _normalize_names(info["reservation"]) + _normalize_names(info["usage"]) # Remove the 'id' field since it's not very useful if 'id' in info: diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index a2418d01b..7bfb2e7f2 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +import copy from unittest import mock from openstack.block_storage.v3 import quota_set as _volume_quota_set @@ -1122,6 +1123,64 @@ def test_quota_show__with_network(self): ) self.assertNotCalled(self.network_client.get_quota_default) + def test_quota_show__with_network_and_usage(self): + # ensure we do not interfere with other tests + self._network_quota_details = copy.deepcopy( + self._network_quota_details + ) + # set a couple of resources + self._network_quota_details["floating_ips"].update( + limit=30, reserved=20, used=7 + ) + self._network_quota_details["security_group_rules"].update( + limit=9, reserved=7, used=5 + ) + + arglist = [ + '--network', + '--usage', + self.projects[0].name, + ] + verifylist = [ + ('service', 'network'), + ('project', self.projects[0].name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + headers, result_gen = self.cmd.take_action(parsed_args) + + self.assertEqual(('Resource', 'Limit', 'In Use', 'Reserved'), headers) + + result = sorted(result_gen) + + self.assertEqual( + [ + ('floating-ips', 30, 7, 20), + ('health_monitors', 0, 0, 0), + ('l7_policies', 0, 0, 0), + ('listeners', 0, 0, 0), + ('load_balancers', 0, 0, 0), + ('networks', 0, 0, 0), + ('pools', 0, 0, 0), + ('ports', 0, 0, 0), + ('rbac_policies', 0, 0, 0), + ('routers', 0, 0, 0), + ('secgroup-rules', 9, 5, 7), + ('secgroups', 0, 0, 0), + ('subnet_pools', 0, 0, 0), + ('subnets', 0, 0, 0), + ], + result, + ) + + self.compute_client.get_quota_set.assert_not_called() + self.volume_sdk_client.get_quota_set.assert_not_called() + self.network_client.get_quota.assert_called_once_with( + self.projects[0].id, + details=True, + ) + self.assertNotCalled(self.network_client.get_quota_default) + def test_quota_show__with_default(self): arglist = [ '--default', diff --git a/releasenotes/notes/bug-2137636-fix-quota-usage-display-2d8f07dccc21f79c.yaml b/releasenotes/notes/bug-2137636-fix-quota-usage-display-2d8f07dccc21f79c.yaml new file mode 100644 index 000000000..818e29ae8 --- /dev/null +++ b/releasenotes/notes/bug-2137636-fix-quota-usage-display-2d8f07dccc21f79c.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fix ``openstack quota show --usage`` to correctly display resource usage + and reservation. From 68f22c209d05baa0985dcd7a8c7dfecd01839897 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Wed, 21 Jan 2026 08:48:06 +0900 Subject: [PATCH 245/245] Replace obsolete PCRE packages pcre3 was removed from recent debian-based releases (eg. Trixie[1]), while RHEL10/CentOS Stream 10 no longer ships pcre in favor of pcre2. Use the latest whereto library release (0.5.0) which uses pcre2 instead. [1] https://lists.debian.org/debian-devel/2021/11/msg00176.html Depends-on: https://review.opendev.org/c/openstack/requirements/+/97142 Change-Id: Ide59346a03f4aea2d6ec4b410e102faeddf2bac4 Signed-off-by: Takashi Kajinami --- bindep.txt | 2 +- doc/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindep.txt b/bindep.txt index 83ebf2d43..8402431ae 100644 --- a/bindep.txt +++ b/bindep.txt @@ -8,4 +8,4 @@ libffi-dev [compile test platform:dpkg] libssl-dev [compile test platform:dpkg] python3-dev [compile test platform:dpkg] python3-devel [compile test platform:rpm] -libpcre3-dev [test platform:dpkg] +libpcre2-dev [test platform:dpkg] diff --git a/doc/requirements.txt b/doc/requirements.txt index 79e3ded4f..05a9bfa87 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -4,7 +4,7 @@ sphinx>=2.0.0,!=2.1.0 # BSD sphinxcontrib-apidoc>=0.2.0 # BSD # redirect tests in docs -whereto>=0.4.0 # Apache-2.0 +whereto>=0.5.0 # Apache-2.0 # Install these to generate sphinx autodocs aodhclient>=0.9.0 # Apache-2.0