From aa227f8d16f22159548bc992f28c1963709e8bce Mon Sep 17 00:00:00 2001 From: Johannes Kulik Date: Thu, 16 Mar 2023 11:46:00 +0100 Subject: [PATCH 001/403] Fix --security-group for port list "openstack port list --security-group " was using a filter that didn't exist in `openstacksdk`: "security_groups". Up until version 1.0.1, `openstacksdk` did not support filtering `Port` objects by security groups. Later versions will support the filtering (via [1]), but the filter is called the same way the attribute on the `Port` object is called: `security_group_ids`. We're not bumping the `openstacksdk` version here, because the feature we're using [1] is merged too recently and a bump of min requirements thus unlikely. `openstackclient` continue to work with older versions of `openstacksdk` - the "--security-group" filter will just do nothing like before. [1] https://review.opendev.org/c/openstack/openstacksdk/+/866008 Change-Id: I07088484592e99ce0a12b67d68a3e47ae7c7af81 --- openstackclient/network/v2/port.py | 2 +- openstackclient/tests/unit/network/v2/test_port.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 8bf14d6a7..46b70bb80 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -694,7 +694,7 @@ def take_action(self, parsed_args): filters['fixed_ips'] = _prepare_filter_fixed_ips( self.app.client_manager, parsed_args) if parsed_args.security_groups: - filters['security_groups'] = parsed_args.security_groups + filters['security_group_ids'] = parsed_args.security_groups _tag.get_tag_filtering_args(parsed_args, filters) diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 04412c5a8..f7be71750 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -1318,7 +1318,7 @@ def test_port_list_security_group(self): columns, data = self.cmd.take_action(parsed_args) filters = { - 'security_groups': ['sg-id1', 'sg-id2'], + 'security_group_ids': ['sg-id1', 'sg-id2'], 'fields': LIST_FIELDS_TO_RETRIEVE, } From f43e2ed20d790b6cc84bf0e2b0ada37b22a4e6dc Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 20 Mar 2023 12:16:17 +0000 Subject: [PATCH 002/403] compute: Fix formatting of 'server show' In change Ic253184ee5f911ec2052419d328260dc4664b273, we switched to using the SDK for the 'server show' command. There were a couple of issues with this change, which we address here: - openstacksdk uses different names for fields than the nova API. We opted to output both the original names and the openstacksdk aliases in the output. With testing, however, it's become obvious that the resulting output is very long and rather unfriendly from a UX perspective. We opt to only show fields with their original names. - A number of fields included in the output are only valid in requests and will never be present in responses. These are removed. - A number of fields are not present in later API microversions or are only present under certain conditions. These are removed from output when not included in responses. - The image and flavor fields both had errant logic that resulted in unnecessary or incorrect information being show. This logic is corrected. With these changes, the output now resembles the output seen before the migration to openstacksdk. In the future we may wish to build on this further and switch from a blacklist model (removing the fields we do not wish to show from output) to a whitelist model (specifically stating which fields to show) but that's a change for another day. Change-Id: I7e3eaa0149bff202c8fd4538356cbc75b4f7e708 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 103 ++++++++++++------ .../tests/unit/compute/v2/test_server.py | 16 +-- 2 files changed, 76 insertions(+), 43 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index cde4ab058..23c2f3630 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -150,16 +150,13 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): # Some commands using this routine were originally implemented with the # nova python wrappers, and were later migrated to use the SDK. Map the # SDK's property names to the original property names to maintain backward - # compatibility for existing users. Data is duplicated under both the old - # and new name so users can consume the data by either name. + # compatibility for existing users. column_map = { 'access_ipv4': 'accessIPv4', 'access_ipv6': 'accessIPv6', 'admin_password': 'adminPass', - 'admin_password': 'adminPass', - 'volumes': 'os-extended-volumes:volumes_attached', + 'attached_volumes': 'volumes_attached', 'availability_zone': 'OS-EXT-AZ:availability_zone', - 'block_device_mapping': 'block_device_mapping_v2', 'compute_host': 'OS-EXT-SRV-ATTR:host', 'created_at': 'created', 'disk_config': 'OS-DCF:diskConfig', @@ -169,7 +166,6 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): 'fault': 'fault', 'hostname': 'OS-EXT-SRV-ATTR:hostname', 'hypervisor_hostname': 'OS-EXT-SRV-ATTR:hypervisor_hostname', - 'image_id': 'imageRef', 'instance_name': 'OS-EXT-SRV-ATTR:instance_name', 'is_locked': 'locked', 'kernel_id': 'OS-EXT-SRV-ATTR:kernel_id', @@ -180,21 +176,56 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): 'ramdisk_id': 'OS-EXT-SRV-ATTR:ramdisk_id', 'reservation_id': 'OS-EXT-SRV-ATTR:reservation_id', 'root_device_name': 'OS-EXT-SRV-ATTR:root_device_name', - 'scheduler_hints': 'OS-SCH-HNT:scheduler_hints', 'task_state': 'OS-EXT-STS:task_state', 'terminated_at': 'OS-SRV-USG:terminated_at', 'updated_at': 'updated', 'user_data': 'OS-EXT-SRV-ATTR:user_data', 'vm_state': 'OS-EXT-STS:vm_state', } + # Some columns returned by openstacksdk should not be shown because they're + # either irrelevant or duplicates + ignored_columns = { + # computed columns + 'interface_ip', + 'location', + 'private_v4', + 'private_v6', + 'public_v4', + 'public_v6', + # create-only columns + 'block_device_mapping', + 'image_id', + 'max_count', + 'min_count', + 'scheduler_hints', + # aliases + 'volumes', + # unnecessary + 'links', + } + # Some columns are only present in certain responses and should not be + # shown otherwise. + optional_columns = { + 'admin_password', # removed in 2.14 + 'fault', # only present in errored servers + 'flavor_id', # removed in 2.47 + 'networks', # only present in create responses + 'security_groups', # only present in create, detail responses + } - info.update( - { - column_map[column]: data - for column, data in info.items() - if column in column_map - } - ) + data = {} + for key, value in info.items(): + if key in ignored_columns: + continue + + if key in optional_columns: + if info[key] is None: + continue + + alias = column_map.get(key) + data[alias or key] = value + + info = data # Convert the image blob to a name image_info = info.get('image', {}) @@ -215,46 +246,57 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): # Convert the flavor blob to a name flavor_info = info.get('flavor', {}) # Microversion 2.47 puts the embedded flavor into the server response - # body but omits the id, so if not present we just expose the flavor - # dict in the server output. - if 'id' in flavor_info: + # body. The presence of the 'original_name' attribute indicates this. + if flavor_info.get('original_name') is None: # microversion < 2.47 flavor_id = flavor_info.get('id', '') try: flavor = utils.find_resource(compute_client.flavors, flavor_id) info['flavor'] = "%s (%s)" % (flavor.name, flavor_id) except Exception: info['flavor'] = flavor_id - else: + else: # microversion >= 2.47 info['flavor'] = format_columns.DictColumn(flavor_info) - if 'os-extended-volumes:volumes_attached' in info: + # there's a lot of redundant information in BDMs - strip it + if 'volumes_attached' in info: info.update( { 'volumes_attached': format_columns.ListDictColumn( - info.pop('os-extended-volumes:volumes_attached') + [ + { + k: v + for k, v in volume.items() + if v is not None and k != 'location' + } + for volume in info.pop('volumes_attached') or [] + ] ) } ) + if 'security_groups' in info: info.update( { 'security_groups': format_columns.ListDictColumn( - info.pop('security_groups') + info.pop('security_groups'), ) } ) + if 'tags' in info: info.update({'tags': format_columns.ListColumn(info.pop('tags'))}) - # NOTE(dtroyer): novaclient splits these into separate entries... - # Format addresses in a useful way - info['addresses'] = ( - AddressesColumn(info['addresses']) - if 'addresses' in info - else format_columns.DictListColumn(info.get('networks')) - ) + # Map 'networks' to 'addresses', if present. Note that the 'networks' key + # is used for create responses, otherwise it's 'addresses'. We know it'll + # be set because this is one of our optional columns. + if 'networks' in info: + info['addresses'] = format_columns.DictListColumn( + info.pop('networks', {}), + ) + else: + info['addresses'] = AddressesColumn(info.get('addresses', {})) - # Map 'metadata' field to 'properties' + # Map 'metadata' field to 'properties' and format info['properties'] = format_columns.DictColumn(info.pop('metadata')) # Migrate tenant_id to project_id naming @@ -267,9 +309,6 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): info['OS-EXT-STS:power_state'] ) - # Remove values that are long and not too useful - info.pop('links', None) - return info diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index a4414af2c..4a0db7195 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -1380,7 +1380,6 @@ class TestServerCreate(TestServer): 'id', 'image', 'name', - 'networks', 'properties', ) @@ -1394,7 +1393,6 @@ def datalist(self): self.new_server.id, self.image.name + ' (' + self.new_server.image.get('id') + ')', self.new_server.name, - self.new_server.networks, format_columns.DictColumn(self.new_server.metadata), ) return datalist @@ -2399,7 +2397,7 @@ def test_server_create_with_wait_ok(self, mock_wait_for_status): self.new_server.name, self.image, self.flavor, **kwargs ) self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist(), data) + self.assertTupleEqual(self.datalist(), data) @mock.patch.object(common_utils, 'wait_for_status', return_value=False) def test_server_create_with_wait_fails(self, mock_wait_for_status): @@ -8245,7 +8243,7 @@ def setUp(self): 'image': {'id': self.image.id}, 'flavor': {'id': self.flavor.id}, 'tenant_id': 'tenant-id-xxx', - 'networks': {'public': ['10.20.30.40', '2001:db8::f']}, + 'addresses': {'public': ['10.20.30.40', '2001:db8::f']}, } self.sdk_client.get_server_diagnostics.return_value = {'test': 'test'} server_method = { @@ -8270,7 +8268,6 @@ def setUp(self): 'id', 'image', 'name', - 'networks', 'project_id', 'properties', ) @@ -8279,12 +8276,11 @@ def setUp(self): server.PowerStateColumn( getattr(self.server, 'OS-EXT-STS:power_state') ), - format_columns.DictListColumn(self.server.networks), self.flavor.name + " (" + self.flavor.id + ")", self.server.id, self.image.name + " (" + self.image.id + ")", self.server.name, - {'public': ['10.20.30.40', '2001:db8::f']}, + server.AddressesColumn({'public': ['10.20.30.40', '2001:db8::f']}), 'tenant-id-xxx', format_columns.DictColumn({}), ) @@ -9095,7 +9091,7 @@ def test_prep_server_detail(self, find_resource): 'image': {u'id': _image.id}, 'flavor': {u'id': _flavor.id}, 'tenant_id': u'tenant-id-xxx', - 'networks': {u'public': [u'10.20.30.40', u'2001:db8::f']}, + 'addresses': {u'public': [u'10.20.30.40', u'2001:db8::f']}, 'links': u'http://xxx.yyy.com', 'properties': '', 'volumes_attached': [{"id": "6344fe9d-ef20-45b2-91a6"}], @@ -9115,7 +9111,7 @@ def test_prep_server_detail(self, find_resource): ), 'properties': '', 'volumes_attached': [{"id": "6344fe9d-ef20-45b2-91a6"}], - 'addresses': format_columns.DictListColumn(_server.networks), + 'addresses': format_columns.DictListColumn(_server.addresses), 'project_id': 'tenant-id-xxx', } @@ -9125,8 +9121,6 @@ def test_prep_server_detail(self, find_resource): self.app.client_manager.image, _server, ) - # 'networks' is used to create _server. Remove it. - server_detail.pop('networks') # Check the results. self.assertCountEqual(info, server_detail) From 564e4f76fb896053c229406e710565cc52476916 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 25 Aug 2023 15:09:00 +0100 Subject: [PATCH 003/403] volume: Migrate 'volume group snapshot' commands to SDK Change-Id: Ie49e1f4b63de8b3bc699f7a9ef6eaa72801b9d05 Signed-off-by: Stephen Finucane Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/892974 --- openstackclient/tests/unit/volume/v3/fakes.py | 6 +- .../volume/v3/test_volume_group_snapshot.py | 149 +++++++++++------- .../volume/v3/volume_group_snapshot.py | 54 +++---- 3 files changed, 120 insertions(+), 89 deletions(-) diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index cd914e7d9..b1366a613 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -21,7 +21,6 @@ from openstack.block_storage.v3 import resource_filter as _filters from openstack.block_storage.v3 import volume as _volume -from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit import utils @@ -91,6 +90,11 @@ def setUp(self): endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN ) + # avoid circular imports + from openstackclient.tests.unit.compute.v2 import ( + fakes as compute_fakes, + ) + self.app.client_manager.compute = compute_fakes.FakeComputev2Client( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN, diff --git a/openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py b/openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py index 1c680f45e..baa50980b 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py @@ -10,28 +10,32 @@ # License for the specific language governing permissions and limitations # under the License. -from cinderclient import api_versions +from unittest import mock + +from keystoneauth1 import discover +from openstack.block_storage.v3 import group as _group +from openstack.block_storage.v3 import group_snapshot as _group_snapshot +from openstack.test import fakes as sdk_fakes +from openstack import utils as sdk_utils from osc_lib import exceptions from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes from openstackclient.volume.v3 import volume_group_snapshot -class TestVolumeGroupSnapshot(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - self.volume_groups_mock = self.volume_client.groups - self.volume_groups_mock.reset_mock() +def fake_supports_microversion(mocked_version): + def supports_microversion(adapter, microversion, raise_exception=False): + required = discover.normalize_version_number(microversion) + candidate = discover.normalize_version_number(mocked_version) + return discover.version_match(required, candidate) - self.volume_group_snapshots_mock = self.volume_client.group_snapshots - self.volume_group_snapshots_mock.reset_mock() + return supports_microversion -class TestVolumeGroupSnapshotCreate(TestVolumeGroupSnapshot): - fake_volume_group = volume_fakes.create_one_volume_group() - fake_volume_group_snapshot = ( - volume_fakes.create_one_volume_group_snapshot() +class TestVolumeGroupSnapshotCreate(volume_fakes.TestVolume): + fake_volume_group = sdk_fakes.generate_fake_resource(_group.Group) + fake_volume_group_snapshot = sdk_fakes.generate_fake_resource( + _group_snapshot.GroupSnapshot, ) columns = ( @@ -54,11 +58,11 @@ class TestVolumeGroupSnapshotCreate(TestVolumeGroupSnapshot): def setUp(self): super().setUp() - self.volume_groups_mock.get.return_value = self.fake_volume_group - self.volume_group_snapshots_mock.create.return_value = ( + self.volume_sdk_client.find_group.return_value = self.fake_volume_group + self.volume_sdk_client.create_group_snapshot.return_value = ( self.fake_volume_group_snapshot ) - self.volume_group_snapshots_mock.get.return_value = ( + self.volume_sdk_client.find_group_snapshot.return_value = ( self.fake_volume_group_snapshot ) @@ -66,8 +70,9 @@ def setUp(self): self.app, None ) - def test_volume_group_snapshot_create(self): - self.volume_client.api_version = api_versions.APIVersion('3.14') + @mock.patch.object(sdk_utils, 'supports_microversion') + def test_volume_group_snapshot_create(self, mock_mv): + mock_mv.side_effect = fake_supports_microversion('3.14') arglist = [ self.fake_volume_group.id, @@ -81,19 +86,22 @@ def test_volume_group_snapshot_create(self): columns, data = self.cmd.take_action(parsed_args) - self.volume_groups_mock.get.assert_called_once_with( - self.fake_volume_group.id - ) - self.volume_group_snapshots_mock.create.assert_called_once_with( + self.volume_sdk_client.find_group.assert_called_once_with( self.fake_volume_group.id, - None, - None, + ignore_missing=False, + details=False, + ) + self.volume_sdk_client.create_group_snapshot.assert_called_once_with( + group_id=self.fake_volume_group.id, + name=None, + description=None, ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) - def test_volume_group_snapshot_create_with_options(self): - self.volume_client.api_version = api_versions.APIVersion('3.14') + @mock.patch.object(sdk_utils, 'supports_microversion') + def test_volume_group_snapshot_create_with_options(self, mock_mv): + mock_mv.side_effect = fake_supports_microversion('3.14') arglist = [ self.fake_volume_group.id, @@ -111,19 +119,22 @@ def test_volume_group_snapshot_create_with_options(self): columns, data = self.cmd.take_action(parsed_args) - self.volume_groups_mock.get.assert_called_once_with( - self.fake_volume_group.id - ) - self.volume_group_snapshots_mock.create.assert_called_once_with( + self.volume_sdk_client.find_group.assert_called_once_with( self.fake_volume_group.id, - 'foo', - 'hello, world', + ignore_missing=False, + details=False, + ) + self.volume_sdk_client.create_group_snapshot.assert_called_once_with( + group_id=self.fake_volume_group.id, + name='foo', + description='hello, world', ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) - def test_volume_group_snapshot_create_pre_v314(self): - self.volume_client.api_version = api_versions.APIVersion('3.13') + @mock.patch.object(sdk_utils, 'supports_microversion') + def test_volume_group_snapshot_create_pre_v314(self, mock_mv): + mock_mv.side_effect = fake_supports_microversion('3.13') arglist = [ self.fake_volume_group.id, @@ -136,32 +147,36 @@ def test_volume_group_snapshot_create_pre_v314(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args + exceptions.CommandError, + self.cmd.take_action, + parsed_args, ) self.assertIn( - '--os-volume-api-version 3.14 or greater is required', str(exc) + '--os-volume-api-version 3.14 or greater is required', + str(exc), ) -class TestVolumeGroupSnapshotDelete(TestVolumeGroupSnapshot): - fake_volume_group_snapshot = ( - volume_fakes.create_one_volume_group_snapshot() +class TestVolumeGroupSnapshotDelete(volume_fakes.TestVolume): + fake_volume_group_snapshot = sdk_fakes.generate_fake_resource( + _group_snapshot.GroupSnapshot, ) def setUp(self): super().setUp() - self.volume_group_snapshots_mock.get.return_value = ( + self.volume_sdk_client.find_group_snapshot.return_value = ( self.fake_volume_group_snapshot ) - self.volume_group_snapshots_mock.delete.return_value = None + self.volume_sdk_client.delete_group_snapshot.return_value = None self.cmd = volume_group_snapshot.DeleteVolumeGroupSnapshot( self.app, None ) - def test_volume_group_snapshot_delete(self): - self.volume_client.api_version = api_versions.APIVersion('3.14') + @mock.patch.object(sdk_utils, 'supports_microversion') + def test_volume_group_snapshot_delete(self, mock_mv): + mock_mv.side_effect = fake_supports_microversion('3.14') arglist = [ self.fake_volume_group_snapshot.id, @@ -173,13 +188,14 @@ def test_volume_group_snapshot_delete(self): result = self.cmd.take_action(parsed_args) - self.volume_group_snapshots_mock.delete.assert_called_once_with( + self.volume_sdk_client.delete_group_snapshot.assert_called_once_with( self.fake_volume_group_snapshot.id, ) self.assertIsNone(result) - def test_volume_group_snapshot_delete_pre_v314(self): - self.volume_client.api_version = api_versions.APIVersion('3.13') + @mock.patch.object(sdk_utils, 'supports_microversion') + def test_volume_group_snapshot_delete_pre_v314(self, mock_mv): + mock_mv.side_effect = fake_supports_microversion('3.13') arglist = [ self.fake_volume_group_snapshot.id, @@ -190,15 +206,23 @@ def test_volume_group_snapshot_delete_pre_v314(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args + exceptions.CommandError, + self.cmd.take_action, + parsed_args, ) self.assertIn( - '--os-volume-api-version 3.14 or greater is required', str(exc) + '--os-volume-api-version 3.14 or greater is required', + str(exc), ) -class TestVolumeGroupSnapshotList(TestVolumeGroupSnapshot): - fake_volume_group_snapshots = volume_fakes.create_volume_group_snapshots() +class TestVolumeGroupSnapshotList(volume_fakes.TestVolume): + fake_volume_group_snapshots = list( + sdk_fakes.generate_fake_resources( + _group_snapshot.GroupSnapshot, + count=3, + ) + ) columns = ( 'ID', @@ -217,7 +241,7 @@ class TestVolumeGroupSnapshotList(TestVolumeGroupSnapshot): def setUp(self): super().setUp() - self.volume_group_snapshots_mock.list.return_value = ( + self.volume_sdk_client.group_snapshots.return_value = ( self.fake_volume_group_snapshots ) @@ -225,8 +249,9 @@ def setUp(self): self.app, None ) - def test_volume_group_snapshot_list(self): - self.volume_client.api_version = api_versions.APIVersion('3.14') + @mock.patch.object(sdk_utils, 'supports_microversion') + def test_volume_group_snapshot_list(self, mock_mv): + mock_mv.side_effect = fake_supports_microversion('3.14') arglist = [ '--all-projects', @@ -238,16 +263,15 @@ def test_volume_group_snapshot_list(self): columns, data = self.cmd.take_action(parsed_args) - self.volume_group_snapshots_mock.list.assert_called_once_with( - search_opts={ - 'all_tenants': True, - }, + self.volume_sdk_client.group_snapshots.assert_called_once_with( + all_projects=True, ) self.assertEqual(self.columns, columns) self.assertCountEqual(tuple(self.data), data) - def test_volume_group_snapshot_list_pre_v314(self): - self.volume_client.api_version = api_versions.APIVersion('3.13') + @mock.patch.object(sdk_utils, 'supports_microversion') + def test_volume_group_snapshot_list_pre_v314(self, mock_mv): + mock_mv.side_effect = fake_supports_microversion('3.13') arglist = [] verifylist = [ @@ -256,8 +280,11 @@ def test_volume_group_snapshot_list_pre_v314(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args + exceptions.CommandError, + self.cmd.take_action, + parsed_args, ) self.assertIn( - '--os-volume-api-version 3.14 or greater is required', str(exc) + '--os-volume-api-version 3.14 or greater is required', + str(exc), ) diff --git a/openstackclient/volume/v3/volume_group_snapshot.py b/openstackclient/volume/v3/volume_group_snapshot.py index bb7d46f58..eabf70028 100644 --- a/openstackclient/volume/v3/volume_group_snapshot.py +++ b/openstackclient/volume/v3/volume_group_snapshot.py @@ -12,7 +12,7 @@ import logging -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 @@ -75,22 +75,25 @@ 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.14'): + if not sdk_utils.supports_microversion(volume_client, '3.14'): msg = _( "--os-volume-api-version 3.14 or greater is required to " "support the 'volume group snapshot create' command" ) raise exceptions.CommandError(msg) - volume_group = utils.find_resource( - volume_client.groups, + group = volume_client.find_group( parsed_args.volume_group, + ignore_missing=False, + details=False, ) - snapshot = volume_client.group_snapshots.create( - volume_group.id, parsed_args.name, parsed_args.description + snapshot = volume_client.create_group_snapshot( + group_id=group.id, + name=parsed_args.name, + description=parsed_args.description, ) return _format_group_snapshot(snapshot) @@ -112,21 +115,22 @@ 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.14'): + if not sdk_utils.supports_microversion(volume_client, '3.14'): msg = _( "--os-volume-api-version 3.14 or greater is required to " "support the 'volume group snapshot delete' command" ) raise exceptions.CommandError(msg) - snapshot = utils.find_resource( - volume_client.group_snapshots, + group_snapshot = volume_client.find_group_snapshot( parsed_args.snapshot, + ignore_missing=False, + details=False, ) - volume_client.group_snapshots.delete(snapshot.id) + volume_client.delete_group_snapshot(group_snapshot.id) class ListVolumeGroupSnapshot(command.Lister): @@ -161,20 +165,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.14'): + if not sdk_utils.supports_microversion(volume_client, '3.14'): msg = _( "--os-volume-api-version 3.14 or greater is required to " "support the 'volume group snapshot list' command" ) raise exceptions.CommandError(msg) - search_opts = { - 'all_tenants': parsed_args.all_projects, - } - - groups = volume_client.group_snapshots.list(search_opts=search_opts) + groups = volume_client.group_snapshots( + all_projects=parsed_args.all_projects, + ) column_headers = ( 'ID', @@ -209,21 +211,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 - if volume_client.api_version < api_versions.APIVersion('3.14'): + if not sdk_utils.supports_microversion(volume_client, '3.14'): msg = _( "--os-volume-api-version 3.14 or greater is required to " "support the 'volume group snapshot show' command" ) raise exceptions.CommandError(msg) - snapshot = utils.find_resource( - volume_client.group_snapshots, + group_snapshot = volume_client.find_group_snapshot( parsed_args.snapshot, + ignore_missing=False, + details=True, ) - # TODO(stephenfin): Do we need this? - snapshot = volume_client.groups.show(snapshot.id) - - return _format_group_snapshot(snapshot) + return _format_group_snapshot(group_snapshot) From 89e5d67a16bd86af7bbe8480252686e2eef55095 Mon Sep 17 00:00:00 2001 From: rladntjr4 Date: Fri, 25 Aug 2023 02:30:32 +0900 Subject: [PATCH 004/403] Fix "server create"command with --boot-from-volume This patch modifes the "server create --boot-from-volume" command without image option print "'NoneType' object has no attribute 'id'" story: 2010892 task: 48669 Change-Id: I566f81c285d4ebc1e23ea0762d67492fb6b3bcbe --- 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 61fffe2d3..3f31bc26e 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1580,6 +1580,12 @@ def _match_image(image_api, wanted_properties): ] elif parsed_args.boot_from_volume: # Tell nova to create a root volume from the image provided. + if not image: + msg = _( + "An image (--image or --image-property) is required " + "to support --boot-from-volume option" + ) + raise exceptions.CommandError(msg) block_device_mapping_v2 = [ { 'uuid': image.id, diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 5ed5eee92..42fea03de 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -3436,6 +3436,34 @@ def test_server_create_volume_boot_from_volume_conflict(self): '--volume is not allowed with --boot-from-volume', str(ex) ) + def test_server_create_boot_from_volume_no_image(self): + # Test --boot-from-volume option without --image or + # --image-property. + arglist = [ + '--flavor', + self.flavor.id, + '--boot-from-volume', + '1', + self.new_server.name, + ] + verifylist = [ + ('flavor', self.flavor.id), + ('boot_from_volume', 1), + ('config_drive', False), + ('server_name', self.new_server.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + ex = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + # Assert it is the error we expect. + self.assertIn( + 'An image (--image or --image-property) is required ' + 'to support --boot-from-volume option', + str(ex), + ) + def test_server_create_image_property(self): arglist = [ '--image-property', From 02cc06425898b16a985626752fe74a26e68da531 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 6 Sep 2023 11:09:36 +0100 Subject: [PATCH 005/403] tests: Use central SDK client fake Avoid double mocking. Change-Id: Ic8fadd41f3687eabd3a149681effae6883edb12e Signed-off-by: Stephen Finucane --- .../tests/unit/common/test_availability_zone.py | 2 -- openstackclient/tests/unit/common/test_extension.py | 5 +---- .../tests/unit/common/test_project_cleanup.py | 13 +++---------- .../tests/unit/compute/v2/test_aggregate.py | 1 - .../tests/unit/compute/v2/test_console.py | 1 - .../tests/unit/compute/v2/test_flavor.py | 1 - openstackclient/tests/unit/compute/v2/test_host.py | 1 - .../tests/unit/compute/v2/test_hypervisor.py | 1 - .../tests/unit/compute/v2/test_hypervisor_stats.py | 1 - .../tests/unit/compute/v2/test_keypair.py | 1 - .../tests/unit/compute/v2/test_server.py | 1 - .../tests/unit/compute/v2/test_server_backup.py | 1 - .../tests/unit/compute/v2/test_server_event.py | 1 - .../tests/unit/compute/v2/test_server_group.py | 1 - .../tests/unit/compute/v2/test_server_image.py | 1 - .../tests/unit/compute/v2/test_server_migration.py | 1 - .../tests/unit/compute/v2/test_server_volume.py | 1 - .../tests/unit/compute/v2/test_service.py | 1 - openstackclient/tests/unit/compute/v2/test_usage.py | 1 - openstackclient/tests/unit/fakes.py | 4 ++++ .../tests/unit/volume/v2/test_volume_backup.py | 1 - 21 files changed, 8 insertions(+), 33 deletions(-) diff --git a/openstackclient/tests/unit/common/test_availability_zone.py b/openstackclient/tests/unit/common/test_availability_zone.py index ede3a81c9..a281e2ecc 100644 --- a/openstackclient/tests/unit/common/test_availability_zone.py +++ b/openstackclient/tests/unit/common/test_availability_zone.py @@ -82,8 +82,6 @@ class TestAvailabilityZone(network_fakes.FakeClientMixin, utils.TestCommand): def setUp(self): super().setUp() - self.app.client_manager.sdk_connection = mock.Mock() - self.app.client_manager.sdk_connection.compute = mock.Mock() self.compute_client = self.app.client_manager.sdk_connection.compute self.compute_client.availability_zones = mock.Mock() diff --git a/openstackclient/tests/unit/common/test_extension.py b/openstackclient/tests/unit/common/test_extension.py index 8eced0c3c..765903b3b 100644 --- a/openstackclient/tests/unit/common/test_extension.py +++ b/openstackclient/tests/unit/common/test_extension.py @@ -34,12 +34,9 @@ def setUp(self): self.identity_extensions_mock = identity_client.extensions self.identity_extensions_mock.reset_mock() - sdk_connection = mock.Mock() - self.app.client_manager.sdk_connection = sdk_connection - + 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() diff --git a/openstackclient/tests/unit/common/test_project_cleanup.py b/openstackclient/tests/unit/common/test_project_cleanup.py index fc09bbfdd..df6ac1732 100644 --- a/openstackclient/tests/unit/common/test_project_cleanup.py +++ b/openstackclient/tests/unit/common/test_project_cleanup.py @@ -15,17 +15,10 @@ from openstackclient.common import project_cleanup from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -from openstackclient.tests.unit import utils as tests_utils +from openstackclient.tests.unit import utils as test_utils -class TestProjectCleanupBase(tests_utils.TestCommand): - def setUp(self): - super(TestProjectCleanupBase, self).setUp() - - self.app.client_manager.sdk_connection = mock.Mock() - - -class TestProjectCleanup(TestProjectCleanupBase): +class TestProjectCleanup(test_utils.TestCommand): project = identity_fakes.FakeProject.create_one_project() def setUp(self): @@ -51,7 +44,7 @@ def test_project_no_options(self): verifylist = [] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index 4e7456a01..dbed0058e 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -59,7 +59,6 @@ def setUp(self): super(TestAggregate, self).setUp() # Get a shortcut to the AggregateManager Mock - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute self.sdk_client.aggregates = mock.Mock() diff --git a/openstackclient/tests/unit/compute/v2/test_console.py b/openstackclient/tests/unit/compute/v2/test_console.py index 18643dd8a..efc4f4d4e 100644 --- a/openstackclient/tests/unit/compute/v2/test_console.py +++ b/openstackclient/tests/unit/compute/v2/test_console.py @@ -25,7 +25,6 @@ def setUp(self): super(TestConsole, self).setUp() # SDK mock - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute self.sdk_client.find_server = mock.Mock() diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py index 1a12b5cd5..f7701e492 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -31,7 +31,6 @@ def setUp(self): super(TestFlavor, self).setUp() # SDK mock - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute self.sdk_client.flavors = mock.Mock() diff --git a/openstackclient/tests/unit/compute/v2/test_host.py b/openstackclient/tests/unit/compute/v2/test_host.py index 99bf91275..64103940c 100644 --- a/openstackclient/tests/unit/compute/v2/test_host.py +++ b/openstackclient/tests/unit/compute/v2/test_host.py @@ -26,7 +26,6 @@ def setUp(self): super(TestHost, self).setUp() # Get a shortcut to the compute client - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute self.sdk_client.get = mock.Mock() diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor.py b/openstackclient/tests/unit/compute/v2/test_hypervisor.py index 49c86a4b0..5148b0d1c 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor.py @@ -30,7 +30,6 @@ def setUp(self): super().setUp() # Create and get a shortcut to the compute client mock - self.app.client_manager.sdk_connection = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute self.sdk_client.reset_mock() diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py index 146a9bd96..58d101c17 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py @@ -24,7 +24,6 @@ def setUp(self): super(TestHypervisorStats, self).setUp() # Get a shortcut to the compute client hypervisors mock - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute self.sdk_client.get = mock.Mock() diff --git a/openstackclient/tests/unit/compute/v2/test_keypair.py b/openstackclient/tests/unit/compute/v2/test_keypair.py index fb085c358..8ac055790 100644 --- a/openstackclient/tests/unit/compute/v2/test_keypair.py +++ b/openstackclient/tests/unit/compute/v2/test_keypair.py @@ -42,7 +42,6 @@ def setUp(self): loaded=True, ) - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute self.sdk_client.keypairs = mock.Mock() diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 515f1ba95..51d718583 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -70,7 +70,6 @@ def setUp(self): self.servers_mock = self.app.client_manager.compute.servers self.servers_mock.reset_mock() - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py index 4d141ff5a..e5ad8c5b8 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_backup.py +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -27,7 +27,6 @@ def setUp(self): super(TestServerBackup, self).setUp() # Get a shortcut to the compute client ServerManager Mock - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute diff --git a/openstackclient/tests/unit/compute/v2/test_server_event.py b/openstackclient/tests/unit/compute/v2/test_server_event.py index 018a60611..9d64762d2 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_event.py +++ b/openstackclient/tests/unit/compute/v2/test_server_event.py @@ -29,7 +29,6 @@ class TestServerEvent(compute_fakes.TestComputev2): def setUp(self): super(TestServerEvent, self).setUp() - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute self.sdk_client.find_server = mock.Mock() diff --git a/openstackclient/tests/unit/compute/v2/test_server_group.py b/openstackclient/tests/unit/compute/v2/test_server_group.py index 9a0860673..ef6f13e0c 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_group.py +++ b/openstackclient/tests/unit/compute/v2/test_server_group.py @@ -51,7 +51,6 @@ def setUp(self): super().setUp() # Create and get a shortcut to the compute client mock - self.app.client_manager.sdk_connection = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute self.sdk_client.reset_mock() diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py index 17fc92fd2..83afe3519 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_image.py +++ b/openstackclient/tests/unit/compute/v2/test_server_image.py @@ -26,7 +26,6 @@ def setUp(self): super().setUp() # Get a shortcut to the compute client ServerManager Mock - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute diff --git a/openstackclient/tests/unit/compute/v2/test_server_migration.py b/openstackclient/tests/unit/compute/v2/test_server_migration.py index 47174713c..4031c6ae5 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_migration.py +++ b/openstackclient/tests/unit/compute/v2/test_server_migration.py @@ -36,7 +36,6 @@ def setUp(self): ) self.server_migrations_mock.reset_mock() - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute diff --git a/openstackclient/tests/unit/compute/v2/test_server_volume.py b/openstackclient/tests/unit/compute/v2/test_server_volume.py index 132834551..4e10c3559 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_volume.py +++ b/openstackclient/tests/unit/compute/v2/test_server_volume.py @@ -26,7 +26,6 @@ class TestServerVolume(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.app.client_manager.sdk_connection.volume = mock.Mock() self.compute_client = self.app.client_manager.sdk_connection.compute diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py index eb4f29d8b..97ac27523 100644 --- a/openstackclient/tests/unit/compute/v2/test_service.py +++ b/openstackclient/tests/unit/compute/v2/test_service.py @@ -28,7 +28,6 @@ class TestService(compute_fakes.TestComputev2): def setUp(self): super(TestService, self).setUp() - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute diff --git a/openstackclient/tests/unit/compute/v2/test_usage.py b/openstackclient/tests/unit/compute/v2/test_usage.py index 113be0cd8..76b70117a 100644 --- a/openstackclient/tests/unit/compute/v2/test_usage.py +++ b/openstackclient/tests/unit/compute/v2/test_usage.py @@ -22,7 +22,6 @@ class TestUsage(compute_fakes.TestComputev2): def setUp(self): super(TestUsage, self).setUp() - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.compute = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.compute diff --git a/openstackclient/tests/unit/fakes.py b/openstackclient/tests/unit/fakes.py index dd7428852..e450e6296 100644 --- a/openstackclient/tests/unit/fakes.py +++ b/openstackclient/tests/unit/fakes.py @@ -115,12 +115,16 @@ def __init__(self): self.object_store = None self.volume = None self.network = None + self.sdk_connection = mock.Mock() + self.session = None self.auth_ref = None self.auth_plugin_name = None + self.network_endpoint_enabled = True self.compute_endpoint_enabled = True self.volume_endpoint_enabled = True + # The source of configuration. This is either 'cloud_config' (a # clouds.yaml file) or 'global_env' ('OS_'-prefixed envvars) self.configuration_type = 'cloud_config' diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index e802a7540..b6429d0c3 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -41,7 +41,6 @@ class TestBackup(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.volume = mock.Mock() self.sdk_client = self.app.client_manager.sdk_connection.volume patcher = mock.patch.object( From 4cabf6f7a2fe11e57b939efc4a8011b00418735c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 6 Sep 2023 11:02:11 +0100 Subject: [PATCH 006/403] tests: Use consistent shortcut to fake volume client This removes the need for a number of base test case subclasses. We use 'volume_client' rather than 'client' to avoid conflicts with clients for other services. Change-Id: I28d78f32142bb3a3fde0ba1780c504adc31cc77c Signed-off-by: Stephen Finucane --- .../unit/common/test_availability_zone.py | 18 +-- .../tests/unit/common/test_limits.py | 3 +- .../tests/unit/common/test_quota.py | 19 ++- .../tests/unit/compute/v2/fakes.py | 1 + .../tests/unit/compute/v2/test_server.py | 8 +- .../unit/compute/v2/test_server_volume.py | 4 +- openstackclient/tests/unit/image/v1/fakes.py | 1 + .../tests/unit/image/v1/test_image.py | 2 +- .../tests/unit/image/v2/test_image.py | 6 +- openstackclient/tests/unit/volume/v1/fakes.py | 1 + .../tests/unit/volume/v1/test_qos_specs.py | 4 +- .../tests/unit/volume/v1/test_service.py | 2 +- .../unit/volume/v1/test_transfer_request.py | 4 +- .../tests/unit/volume/v1/test_type.py | 6 +- .../tests/unit/volume/v1/test_volume.py | 2 +- .../unit/volume/v1/test_volume_backup.py | 8 +- openstackclient/tests/unit/volume/v2/fakes.py | 14 +- .../unit/volume/v2/test_backup_record.py | 2 +- .../unit/volume/v2/test_consistency_group.py | 10 +- .../v2/test_consistency_group_snapshot.py | 6 +- .../tests/unit/volume/v2/test_qos_specs.py | 4 +- .../tests/unit/volume/v2/test_service.py | 2 +- .../tests/unit/volume/v2/test_volume.py | 16 +-- .../unit/volume/v2/test_volume_backend.py | 4 +- .../unit/volume/v2/test_volume_backup.py | 126 ++++++++---------- .../tests/unit/volume/v2/test_volume_host.py | 2 +- .../unit/volume/v2/test_volume_snapshot.py | 4 +- .../volume/v2/test_volume_transfer_request.py | 12 +- .../tests/unit/volume/v2/test_volume_type.py | 8 +- openstackclient/tests/unit/volume/v3/fakes.py | 22 ++- .../volume/v3/test_block_storage_cleanup.py | 10 +- .../volume/v3/test_block_storage_cluster.py | 42 ++---- .../volume/v3/test_block_storage_log_level.py | 22 +-- .../volume/v3/test_block_storage_manage.py | 37 ++--- .../v3/test_block_storage_resource_filter.py | 20 +-- .../tests/unit/volume/v3/test_volume.py | 98 +++++++------- .../unit/volume/v3/test_volume_attachment.py | 62 +++------ .../tests/unit/volume/v3/test_volume_group.py | 88 ++++-------- .../volume/v3/test_volume_group_snapshot.py | 34 ++--- .../unit/volume/v3/test_volume_group_type.py | 56 ++------ .../unit/volume/v3/test_volume_message.py | 38 ++---- 41 files changed, 305 insertions(+), 523 deletions(-) diff --git a/openstackclient/tests/unit/common/test_availability_zone.py b/openstackclient/tests/unit/common/test_availability_zone.py index a281e2ecc..fbab128dc 100644 --- a/openstackclient/tests/unit/common/test_availability_zone.py +++ b/openstackclient/tests/unit/common/test_availability_zone.py @@ -87,8 +87,8 @@ def setUp(self): self.compute_client.availability_zones = mock.Mock() self.app.client_manager.sdk_connection.volume = mock.Mock() - self.volume_client = self.app.client_manager.sdk_connection.volume - self.volume_client.availability_zones = mock.Mock() + self.volume_sdk_client = self.app.client_manager.sdk_connection.volume + self.volume_sdk_client.availability_zones = mock.Mock() class TestAvailabilityZoneList(TestAvailabilityZone): @@ -110,7 +110,9 @@ def setUp(self): super().setUp() self.compute_client.availability_zones.return_value = self.compute_azs - self.volume_client.availability_zones.return_value = self.volume_azs + self.volume_sdk_client.availability_zones.return_value = ( + self.volume_azs + ) self.network_client.availability_zones.return_value = self.network_azs # Get the command object to test @@ -127,7 +129,7 @@ def test_availability_zone_list_no_options(self): columns, data = self.cmd.take_action(parsed_args) self.compute_client.availability_zones.assert_called_with(details=True) - self.volume_client.availability_zones.assert_called_with() + self.volume_sdk_client.availability_zones.assert_called_with() self.network_client.availability_zones.assert_called_with() self.assertEqual(self.short_columnslist, columns) @@ -155,7 +157,7 @@ def test_availability_zone_list_long(self): columns, data = self.cmd.take_action(parsed_args) self.compute_client.availability_zones.assert_called_with(details=True) - self.volume_client.availability_zones.assert_called_with() + self.volume_sdk_client.availability_zones.assert_called_with() self.network_client.availability_zones.assert_called_with() self.assertEqual(self.long_columnslist, columns) @@ -189,7 +191,7 @@ def test_availability_zone_list_compute(self): columns, data = self.cmd.take_action(parsed_args) self.compute_client.availability_zones.assert_called_with(details=True) - self.volume_client.availability_zones.assert_not_called() + self.volume_sdk_client.availability_zones.assert_not_called() self.network_client.availability_zones.assert_not_called() self.assertEqual(self.short_columnslist, columns) @@ -213,7 +215,7 @@ def test_availability_zone_list_volume(self): columns, data = self.cmd.take_action(parsed_args) self.compute_client.availability_zones.assert_not_called() - self.volume_client.availability_zones.assert_called_with() + self.volume_sdk_client.availability_zones.assert_called_with() self.network_client.availability_zones.assert_not_called() self.assertEqual(self.short_columnslist, columns) @@ -237,7 +239,7 @@ def test_availability_zone_list_network(self): columns, data = self.cmd.take_action(parsed_args) self.compute_client.availability_zones.assert_not_called() - self.volume_client.availability_zones.assert_not_called() + self.volume_sdk_client.availability_zones.assert_not_called() self.network_client.availability_zones.assert_called_with() self.assertEqual(self.short_columnslist, columns) diff --git a/openstackclient/tests/unit/common/test_limits.py b/openstackclient/tests/unit/common/test_limits.py index a29fa4152..87f6c1921 100644 --- a/openstackclient/tests/unit/common/test_limits.py +++ b/openstackclient/tests/unit/common/test_limits.py @@ -74,10 +74,9 @@ class TestVolumeLimits(volume_fakes.TestVolume): def setUp(self): super().setUp() self.app.client_manager.compute_endpoint_enabled = False - self.volume = self.app.client_manager.volume self.fake_limits = volume_fakes.FakeLimits() - self.volume.limits.get.return_value = self.fake_limits + self.volume_client.limits.get.return_value = self.fake_limits def test_volume_show_absolute(self): arglist = ['--absolute'] diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index de29df1c7..647f0b0db 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -55,11 +55,9 @@ def setUp(self): ) self.compute_quotas_class_mock.reset_mock() - self.volume_quotas_mock = self.app.client_manager.volume.quotas + self.volume_quotas_mock = self.volume_client.quotas self.volume_quotas_mock.reset_mock() - self.volume_quotas_class_mock = ( - self.app.client_manager.volume.quota_classes - ) + self.volume_quotas_class_mock = self.volume_client.quota_classes self.volume_quotas_class_mock.reset_mock() self.app.client_manager.auth_ref = mock.Mock() @@ -180,8 +178,7 @@ def setUp(self): volume_fakes.create_one_default_vol_quota(), volume_fakes.create_one_default_vol_quota(), ] - self.volume = self.app.client_manager.volume - self.volume.quotas.defaults = mock.Mock( + self.volume_client.quotas.defaults = mock.Mock( side_effect=self.volume_default_quotas, ) @@ -288,7 +285,7 @@ def test_quota_list_details_volume(self): detailed_quota ) - self.volume.quotas.get = mock.Mock(return_value=detailed_quota) + self.volume_client.quotas.get = mock.Mock(return_value=detailed_quota) arglist = [ '--detail', @@ -541,7 +538,7 @@ def test_quota_list_network_by_project(self): def test_quota_list_volume(self): # Two projects with non-default quotas - self.volume.quotas.get = mock.Mock( + self.volume_client.quotas.get = mock.Mock( side_effect=self.volume_quotas, ) @@ -562,7 +559,7 @@ def test_quota_list_volume(self): def test_quota_list_volume_default(self): # Two projects with non-default quotas - self.volume.quotas.get = mock.Mock( + self.volume_client.quotas.get = mock.Mock( side_effect=[ self.volume_quotas[0], volume_fakes.create_one_default_vol_quota(), @@ -586,7 +583,7 @@ def test_quota_list_volume_default(self): def test_quota_list_volume_no_project(self): # Two projects with non-default quotas - self.volume.quotas.get = mock.Mock( + self.volume_client.quotas.get = mock.Mock( side_effect=[ self.volume_quotas[0], volume_fakes.create_one_default_vol_quota(), @@ -610,7 +607,7 @@ def test_quota_list_volume_no_project(self): def test_quota_list_volume_by_project(self): # Two projects with non-default quotas - self.volume.quotas.get = mock.Mock( + self.volume_client.quotas.get = mock.Mock( side_effect=self.volume_quotas, ) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index aeca448c2..1d6584979 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -175,6 +175,7 @@ def setUp(self): endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN, ) + self.volume_client = self.app.client_manager.volume def create_one_aggregate(attrs=None): diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 51d718583..31b864f48 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -92,14 +92,14 @@ def setUp(self): self.flavors_mock.reset_mock() # Get a shortcut to the volume client VolumeManager Mock - self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() self.app.client_manager.sdk_connection.volume = mock.Mock() - self.sdk_volume_client = self.app.client_manager.sdk_connection.volume + self.volume_sdk_client = self.app.client_manager.sdk_connection.volume # Get a shortcut to the volume client VolumeManager Mock - self.snapshots_mock = self.app.client_manager.volume.volume_snapshots + self.snapshots_mock = self.volume_client.volume_snapshots self.snapshots_mock.reset_mock() # Set object attributes to be tested. Could be overwritten in subclass. @@ -162,7 +162,7 @@ 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.sdk_volume_client.find_volume.side_effect = volumes + self.volume_sdk_client.find_volume.side_effect = volumes return volumes diff --git a/openstackclient/tests/unit/compute/v2/test_server_volume.py b/openstackclient/tests/unit/compute/v2/test_server_volume.py index 4e10c3559..33dd3a071 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_volume.py +++ b/openstackclient/tests/unit/compute/v2/test_server_volume.py @@ -29,7 +29,7 @@ def setUp(self): self.app.client_manager.sdk_connection.compute = mock.Mock() self.app.client_manager.sdk_connection.volume = mock.Mock() self.compute_client = self.app.client_manager.sdk_connection.compute - self.volume_client = self.app.client_manager.sdk_connection.volume + self.volume_sdk_client = self.app.client_manager.sdk_connection.volume class TestServerVolumeList(TestServerVolume): @@ -243,7 +243,7 @@ def setUp(self): self.compute_client.find_server.return_value = self.server self.volume = volume_fakes.create_one_sdk_volume() - self.volume_client.find_volume.return_value = self.volume + self.volume_sdk_client.find_volume.return_value = self.volume # Get the command object to test self.cmd = server_volume.UpdateServerVolume(self.app, None) diff --git a/openstackclient/tests/unit/image/v1/fakes.py b/openstackclient/tests/unit/image/v1/fakes.py index 293b41a16..67ebc000f 100644 --- a/openstackclient/tests/unit/image/v1/fakes.py +++ b/openstackclient/tests/unit/image/v1/fakes.py @@ -39,6 +39,7 @@ def setUp(self): endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN, ) + self.volume_client = self.app.client_manager.volume def create_one_image(attrs=None): diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py index ed1e202fd..2edbe1e2a 100644 --- a/openstackclient/tests/unit/image/v1/test_image.py +++ b/openstackclient/tests/unit/image/v1/test_image.py @@ -593,7 +593,7 @@ def test_image_set_properties(self): def test_image_update_volume(self): # Set up VolumeManager Mock - volumes_mock = self.app.client_manager.volume.volumes + volumes_mock = self.volume_client.volumes volumes_mock.reset_mock() volumes_mock.get.return_value = fakes.FakeResource( None, diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index e9ecd4ec3..451bb89c0 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -37,7 +37,7 @@ def setUp(self): self.project_mock.reset_mock() self.domain_mock = self.app.client_manager.identity.domains self.domain_mock.reset_mock() - self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock = self.volume_client.volumes fake_body = { 'os-volume_upload_image': {'volume_type': {'name': 'fake_type'}} } @@ -369,9 +369,7 @@ class FakeVolume: @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.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.1' - ) + self.volume_client.api_version = api_versions.APIVersion('3.1') fake_vol_id = 'fake-volume-id' mock_get_data_f.return_value = None diff --git a/openstackclient/tests/unit/volume/v1/fakes.py b/openstackclient/tests/unit/volume/v1/fakes.py index 2564cd3f0..b4da68c33 100644 --- a/openstackclient/tests/unit/volume/v1/fakes.py +++ b/openstackclient/tests/unit/volume/v1/fakes.py @@ -61,6 +61,7 @@ def setUp(self): endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN, ) + self.volume_client = self.app.client_manager.volume self.app.client_manager.identity = identity_fakes.FakeIdentityv2Client( endpoint=fakes.AUTH_URL, diff --git a/openstackclient/tests/unit/volume/v1/test_qos_specs.py b/openstackclient/tests/unit/volume/v1/test_qos_specs.py index cbfd1fa4c..004749fa2 100644 --- a/openstackclient/tests/unit/volume/v1/test_qos_specs.py +++ b/openstackclient/tests/unit/volume/v1/test_qos_specs.py @@ -29,10 +29,10 @@ class TestQos(volume_fakes.TestVolumev1): def setUp(self): super().setUp() - self.qos_mock = self.app.client_manager.volume.qos_specs + self.qos_mock = self.volume_client.qos_specs self.qos_mock.reset_mock() - self.types_mock = self.app.client_manager.volume.volume_types + self.types_mock = self.volume_client.volume_types self.types_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v1/test_service.py b/openstackclient/tests/unit/volume/v1/test_service.py index 4d32b63eb..95a21994f 100644 --- a/openstackclient/tests/unit/volume/v1/test_service.py +++ b/openstackclient/tests/unit/volume/v1/test_service.py @@ -23,7 +23,7 @@ def setUp(self): super().setUp() # Get a shortcut to the ServiceManager Mock - self.service_mock = self.app.client_manager.volume.services + self.service_mock = self.volume_client.services self.service_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v1/test_transfer_request.py b/openstackclient/tests/unit/volume/v1/test_transfer_request.py index e342d38cf..e73a4c3ba 100644 --- a/openstackclient/tests/unit/volume/v1/test_transfer_request.py +++ b/openstackclient/tests/unit/volume/v1/test_transfer_request.py @@ -27,11 +27,11 @@ def setUp(self): super().setUp() # Get a shortcut to the TransferManager Mock - self.transfer_mock = self.app.client_manager.volume.transfers + self.transfer_mock = self.volume_client.transfers self.transfer_mock.reset_mock() # Get a shortcut to the VolumeManager Mock - self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v1/test_type.py b/openstackclient/tests/unit/volume/v1/test_type.py index 425c164f5..daa495995 100644 --- a/openstackclient/tests/unit/volume/v1/test_type.py +++ b/openstackclient/tests/unit/volume/v1/test_type.py @@ -28,12 +28,10 @@ class TestType(volume_fakes.TestVolumev1): def setUp(self): super().setUp() - self.types_mock = self.app.client_manager.volume.volume_types + self.types_mock = self.volume_client.volume_types self.types_mock.reset_mock() - self.encryption_types_mock = ( - self.app.client_manager.volume.volume_encryption_types - ) + self.encryption_types_mock = self.volume_client.volume_encryption_types self.encryption_types_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py index 8badd2a6f..85610787d 100644 --- a/openstackclient/tests/unit/volume/v1/test_volume.py +++ b/openstackclient/tests/unit/volume/v1/test_volume.py @@ -33,7 +33,7 @@ def setUp(self): super().setUp() # Get a shortcut to the VolumeManager Mock - self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() # Get a shortcut to the TenantManager Mock diff --git a/openstackclient/tests/unit/volume/v1/test_volume_backup.py b/openstackclient/tests/unit/volume/v1/test_volume_backup.py index 971aebd8d..c551d159e 100644 --- a/openstackclient/tests/unit/volume/v1/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v1/test_volume_backup.py @@ -26,13 +26,13 @@ class TestBackup(volume_fakes.TestVolumev1): def setUp(self): super().setUp() - self.backups_mock = self.app.client_manager.volume.backups + self.backups_mock = self.volume_client.backups self.backups_mock.reset_mock() - self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() - self.snapshots_mock = self.app.client_manager.volume.volume_snapshots + self.snapshots_mock = self.volume_client.volume_snapshots self.snapshots_mock.reset_mock() - self.restores_mock = self.app.client_manager.volume.restores + self.restores_mock = self.volume_client.restores self.restores_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index a74ecda22..5145796a4 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -17,9 +17,11 @@ from unittest import mock import uuid +# FIXME(stephenfin): We are using v3 resource versions despite being v2 fakes from cinderclient import api_versions +from openstack.block_storage.v2 import _proxy as block_storage_v2_proxy from openstack.block_storage.v3 import backup as _backup -from openstack.block_storage.v3 import volume +from openstack.block_storage.v3 import volume as _volume from openstack.image.v2 import _proxy as image_v2_proxy from osc_lib.cli import format_columns @@ -94,6 +96,14 @@ def setUp(self): self.app.client_manager.volume = FakeVolumeClient( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN ) + self.volume_client = self.app.client_manager.volume + + # TODO(stephenfin): Rename to 'volume_client' once all commands are + # migrated to SDK + self.app.client_manager.sdk_connection.volume = mock.Mock( + spec=block_storage_v2_proxy.Proxy, + ) + self.volume_sdk_client = self.app.client_manager.sdk_connection.volume self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN @@ -413,7 +423,7 @@ def create_one_sdk_volume(attrs=None): # Overwrite default attributes if there are some attributes set volume_info.update(attrs) - return volume.Volume(**volume_info) + return _volume.Volume(**volume_info) def create_sdk_volumes(attrs=None, count=2): diff --git a/openstackclient/tests/unit/volume/v2/test_backup_record.py b/openstackclient/tests/unit/volume/v2/test_backup_record.py index 4933af16a..d677b9a28 100644 --- a/openstackclient/tests/unit/volume/v2/test_backup_record.py +++ b/openstackclient/tests/unit/volume/v2/test_backup_record.py @@ -20,7 +20,7 @@ class TestBackupRecord(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.backups_mock = self.app.client_manager.volume.backups + self.backups_mock = self.volume_client.backups self.backups_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v2/test_consistency_group.py b/openstackclient/tests/unit/volume/v2/test_consistency_group.py index 523762044..dc62e5e42 100644 --- a/openstackclient/tests/unit/volume/v2/test_consistency_group.py +++ b/openstackclient/tests/unit/volume/v2/test_consistency_group.py @@ -28,18 +28,16 @@ def setUp(self): super().setUp() # Get a shortcut to the TransferManager Mock - self.consistencygroups_mock = ( - self.app.client_manager.volume.consistencygroups - ) + self.consistencygroups_mock = self.volume_client.consistencygroups self.consistencygroups_mock.reset_mock() - self.cgsnapshots_mock = self.app.client_manager.volume.cgsnapshots + self.cgsnapshots_mock = self.volume_client.cgsnapshots self.cgsnapshots_mock.reset_mock() - self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() - self.types_mock = self.app.client_manager.volume.volume_types + self.types_mock = self.volume_client.volume_types self.types_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py b/openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py index 873f9d0b3..6580ac1f7 100644 --- a/openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py +++ b/openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py @@ -23,11 +23,9 @@ def setUp(self): super(TestConsistencyGroupSnapshot, self).setUp() # Get a shortcut to the TransferManager Mock - self.cgsnapshots_mock = self.app.client_manager.volume.cgsnapshots + self.cgsnapshots_mock = self.volume_client.cgsnapshots self.cgsnapshots_mock.reset_mock() - self.consistencygroups_mock = ( - self.app.client_manager.volume.consistencygroups - ) + self.consistencygroups_mock = self.volume_client.consistencygroups self.consistencygroups_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v2/test_qos_specs.py b/openstackclient/tests/unit/volume/v2/test_qos_specs.py index 54a13efcd..950012416 100644 --- a/openstackclient/tests/unit/volume/v2/test_qos_specs.py +++ b/openstackclient/tests/unit/volume/v2/test_qos_specs.py @@ -29,10 +29,10 @@ class TestQos(volume_fakes.TestVolume): def setUp(self): super(TestQos, self).setUp() - self.qos_mock = self.app.client_manager.volume.qos_specs + self.qos_mock = self.volume_client.qos_specs self.qos_mock.reset_mock() - self.types_mock = self.app.client_manager.volume.volume_types + self.types_mock = self.volume_client.volume_types self.types_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v2/test_service.py b/openstackclient/tests/unit/volume/v2/test_service.py index 90fd08baa..a55398564 100644 --- a/openstackclient/tests/unit/volume/v2/test_service.py +++ b/openstackclient/tests/unit/volume/v2/test_service.py @@ -23,7 +23,7 @@ def setUp(self): super().setUp() # Get a shortcut to the ServiceManager Mock - self.service_mock = self.app.client_manager.volume.services + self.service_mock = self.volume_client.services self.service_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index 992b99742..bfc5cc836 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -32,7 +32,7 @@ class TestVolume(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() self.projects_mock = self.app.client_manager.identity.projects @@ -41,18 +41,16 @@ def setUp(self): self.users_mock = self.app.client_manager.identity.users self.users_mock.reset_mock() - self.snapshots_mock = self.app.client_manager.volume.volume_snapshots + self.snapshots_mock = self.volume_client.volume_snapshots self.snapshots_mock.reset_mock() - self.backups_mock = self.app.client_manager.volume.backups + self.backups_mock = self.volume_client.backups self.backups_mock.reset_mock() - self.types_mock = self.app.client_manager.volume.volume_types + self.types_mock = self.volume_client.volume_types self.types_mock.reset_mock() - self.consistencygroups_mock = ( - self.app.client_manager.volume.consistencygroups - ) + self.consistencygroups_mock = self.volume_client.consistencygroups self.consistencygroups_mock.reset_mock() def setup_volumes_mock(self, count): @@ -365,9 +363,7 @@ def test_volume_create_with_backup(self): self.backups_mock.get.return_value = backup - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.47' - ) + self.volume_client.api_version = api_versions.APIVersion('3.47') # 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 diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backend.py b/openstackclient/tests/unit/volume/v2/test_volume_backend.py index 5c80d9e6c..1652581b7 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backend.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backend.py @@ -26,7 +26,7 @@ def setUp(self): super().setUp() # Get a shortcut to the capability Mock - self.capability_mock = self.app.client_manager.volume.capabilities + self.capability_mock = self.volume_client.capabilities self.capability_mock.get.return_value = self.capability # Get the command object to test @@ -82,7 +82,7 @@ class TestListVolumePool(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.pool_mock = self.app.client_manager.volume.pools + self.pool_mock = self.volume_client.pools self.pool_mock.list.return_value = [self.pools] # Get the command object to test diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index b6429d0c3..3cacb0aa1 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -27,13 +27,13 @@ class TestBackupLegacy(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.backups_mock = self.app.client_manager.volume.backups + self.backups_mock = self.volume_client.backups self.backups_mock.reset_mock() - self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() - self.snapshots_mock = self.app.client_manager.volume.volume_snapshots + self.snapshots_mock = self.volume_client.volume_snapshots self.snapshots_mock.reset_mock() - self.restores_mock = self.app.client_manager.volume.restores + self.restores_mock = self.volume_client.restores self.restores_mock.reset_mock() @@ -42,7 +42,7 @@ def setUp(self): super().setUp() self.app.client_manager.sdk_connection.volume = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.volume + self.volume_sdk_client = self.app.client_manager.sdk_connection.volume patcher = mock.patch.object( sdk_utils, 'supports_microversion', return_value=True ) @@ -82,9 +82,9 @@ class TestBackupCreate(TestBackup): def setUp(self): super().setUp() - self.sdk_client.find_volume.return_value = self.volume - self.sdk_client.find_snapshot.return_value = self.snapshot - self.sdk_client.create_backup.return_value = self.new_backup + self.volume_sdk_client.find_volume.return_value = self.volume + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.volume_sdk_client.create_backup.return_value = self.new_backup # Get the command object to test self.cmd = volume_backup.CreateVolumeBackup(self.app, None) @@ -116,7 +116,7 @@ def test_backup_create(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_backup.assert_called_with( + 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, @@ -146,7 +146,7 @@ def test_backup_create_with_properties(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_backup.assert_called_with( + self.volume_sdk_client.create_backup.assert_called_with( volume_id=self.new_backup.volume_id, container=None, name=None, @@ -195,7 +195,7 @@ def test_backup_create_with_availability_zone(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_backup.assert_called_with( + self.volume_sdk_client.create_backup.assert_called_with( volume_id=self.new_backup.volume_id, container=None, name=None, @@ -243,7 +243,7 @@ def test_backup_create_without_name(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_backup.assert_called_with( + self.volume_sdk_client.create_backup.assert_called_with( volume_id=self.new_backup.volume_id, container=self.new_backup.container, name=None, @@ -261,8 +261,10 @@ class TestBackupDelete(TestBackup): def setUp(self): super().setUp() - self.sdk_client.find_backup = volume_fakes.get_backups(self.backups) - self.sdk_client.delete_backup.return_value = None + self.volume_sdk_client.find_backup = volume_fakes.get_backups( + 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) @@ -274,7 +276,7 @@ def test_backup_delete(self): result = self.cmd.take_action(parsed_args) - self.sdk_client.delete_backup.assert_called_with( + self.volume_sdk_client.delete_backup.assert_called_with( self.backups[0].id, ignore_missing=False, force=False ) self.assertIsNone(result) @@ -289,7 +291,7 @@ def test_backup_delete_with_force(self): result = self.cmd.take_action(parsed_args) - self.sdk_client.delete_backup.assert_called_with( + self.volume_sdk_client.delete_backup.assert_called_with( self.backups[0].id, ignore_missing=False, force=True ) self.assertIsNone(result) @@ -308,7 +310,7 @@ def test_delete_multiple_backups(self): calls = [] for b in self.backups: calls.append(call(b.id, ignore_missing=False, force=False)) - self.sdk_client.delete_backup.assert_has_calls(calls) + self.volume_sdk_client.delete_backup.assert_has_calls(calls) self.assertIsNone(result) def test_delete_multiple_backups_with_exception(self): @@ -324,7 +326,7 @@ def test_delete_multiple_backups_with_exception(self): find_mock_result = [self.backups[0], exceptions.CommandError] with mock.patch.object( - self.sdk_client, 'find_backup', side_effect=find_mock_result + self.volume_sdk_client, 'find_backup', side_effect=find_mock_result ) as find_mock: try: self.cmd.take_action(parsed_args) @@ -336,7 +338,7 @@ def test_delete_multiple_backups_with_exception(self): find_mock.assert_any_call('unexist_backup', ignore_missing=False) self.assertEqual(2, find_mock.call_count) - self.sdk_client.delete_backup.assert_called_once_with( + self.volume_sdk_client.delete_backup.assert_called_once_with( self.backups[0].id, ignore_missing=False, force=False, @@ -394,10 +396,10 @@ class TestBackupList(TestBackup): def setUp(self): super().setUp() - self.sdk_client.volumes.return_value = [self.volume] - self.sdk_client.backups.return_value = self.backups - self.sdk_client.find_volume.return_value = self.volume - self.sdk_client.find_backup.return_value = self.backups[0] + self.volume_sdk_client.volumes.return_value = [self.volume] + 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.cmd = volume_backup.ListVolumeBackup(self.app, None) @@ -417,9 +419,9 @@ def test_backup_list_without_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.find_volume.assert_not_called() - self.sdk_client.find_backup.assert_not_called() - self.sdk_client.backups.assert_called_with( + self.volume_sdk_client.find_volume.assert_not_called() + self.volume_sdk_client.find_backup.assert_not_called() + self.volume_sdk_client.backups.assert_called_with( name=None, status=None, volume_id=None, @@ -458,13 +460,13 @@ def test_backup_list_with_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.find_volume.assert_called_once_with( + self.volume_sdk_client.find_volume.assert_called_once_with( self.volume.id, ignore_missing=False ) - self.sdk_client.find_backup.assert_called_once_with( + self.volume_sdk_client.find_backup.assert_called_once_with( self.backups[0].id, ignore_missing=False ) - self.sdk_client.backups.assert_called_with( + self.volume_sdk_client.backups.assert_called_with( name=self.backups[0].name, status="error", volume_id=self.volume.id, @@ -485,9 +487,9 @@ class TestBackupRestore(TestBackup): def setUp(self): super().setUp() - self.sdk_client.find_backup.return_value = self.backup - self.sdk_client.find_volume.return_value = self.volume - self.sdk_client.restore_backup.return_value = ( + 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']}, ) @@ -497,8 +499,9 @@ def setUp(self): self.cmd = volume_backup.RestoreVolumeBackup(self.app, None) def test_backup_restore(self): - self.sdk_client.find_volume.side_effect = exceptions.CommandError() - # self.sdk_client.side_effect = exceptions.CommandError() + self.volume_sdk_client.find_volume.side_effect = ( + exceptions.CommandError() + ) arglist = [self.backup.id] verifylist = [ ("backup", self.backup.id), @@ -507,7 +510,7 @@ def test_backup_restore(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.sdk_client.restore_backup.assert_called_with( + self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=None, name=None, @@ -515,8 +518,9 @@ def test_backup_restore(self): self.assertIsNotNone(result) def test_backup_restore_with_volume(self): - self.sdk_client.find_volume.side_effect = exceptions.CommandError() - # self.volumes_mock.find.side_effect = exceptions.CommandError() + self.volume_sdk_client.find_volume.side_effect = ( + exceptions.CommandError() + ) arglist = [ self.backup.id, self.backup.volume_id, @@ -528,7 +532,7 @@ def test_backup_restore_with_volume(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.sdk_client.restore_backup.assert_called_with( + self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=None, name=self.backup.volume_id, @@ -549,7 +553,7 @@ def test_backup_restore_with_volume_force(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.sdk_client.restore_backup.assert_called_with( + self.volume_sdk_client.restore_backup.assert_called_with( self.backup.id, volume_id=self.volume.id, name=None, @@ -588,9 +592,7 @@ def setUp(self): self.cmd = volume_backup.SetVolumeBackup(self.app, None) def test_backup_set_name(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.9' - ) + self.volume_client.api_version = api_versions.APIVersion('3.9') arglist = [ '--name', @@ -612,9 +614,7 @@ def test_backup_set_name(self): self.assertIsNone(result) def test_backup_set_name_pre_v39(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.8' - ) + self.volume_client.api_version = api_versions.APIVersion('3.8') arglist = [ '--name', @@ -633,9 +633,7 @@ def test_backup_set_name_pre_v39(self): self.assertIn("--os-volume-api-version 3.9 or greater", str(exc)) def test_backup_set_description(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.9' - ) + self.volume_client.api_version = api_versions.APIVersion('3.9') arglist = [ '--description', @@ -659,9 +657,7 @@ def test_backup_set_description(self): self.assertIsNone(result) def test_backup_set_description_pre_v39(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.8' - ) + self.volume_client.api_version = api_versions.APIVersion('3.8') arglist = [ '--description', @@ -710,9 +706,7 @@ def test_backup_set_state_failed(self): ) def test_backup_set_no_property(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.43' - ) + self.volume_client.api_version = api_versions.APIVersion('3.43') arglist = [ '--no-property', @@ -736,9 +730,7 @@ def test_backup_set_no_property(self): self.assertIsNone(result) def test_backup_set_no_property_pre_v343(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.42' - ) + self.volume_client.api_version = api_versions.APIVersion('3.42') arglist = [ '--no-property', @@ -756,9 +748,7 @@ def test_backup_set_no_property_pre_v343(self): self.assertIn("--os-volume-api-version 3.43 or greater", str(exc)) def test_backup_set_property(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.43' - ) + self.volume_client.api_version = api_versions.APIVersion('3.43') arglist = [ '--property', @@ -783,9 +773,7 @@ def test_backup_set_property(self): self.assertIsNone(result) def test_backup_set_property_pre_v343(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.42' - ) + self.volume_client.api_version = api_versions.APIVersion('3.42') arglist = [ '--property', @@ -818,9 +806,7 @@ def setUp(self): self.cmd = volume_backup.UnsetVolumeBackup(self.app, None) def test_backup_unset_property(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.43' - ) + self.volume_client.api_version = api_versions.APIVersion('3.43') arglist = [ '--property', @@ -845,9 +831,7 @@ def test_backup_unset_property(self): self.assertIsNone(result) def test_backup_unset_property_pre_v343(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.42' - ) + self.volume_client.api_version = api_versions.APIVersion('3.42') arglist = [ '--property', @@ -917,7 +901,7 @@ class TestBackupShow(TestBackup): def setUp(self): super().setUp() - self.sdk_client.get_backup.return_value = self.backup + self.volume_sdk_client.get_backup.return_value = self.backup # Get the command object to test self.cmd = volume_backup.ShowVolumeBackup(self.app, None) @@ -927,7 +911,7 @@ def test_backup_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.get_backup.assert_called_with(self.backup.id) + self.volume_sdk_client.get_backup.assert_called_with(self.backup.id) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_host.py b/openstackclient/tests/unit/volume/v2/test_volume_host.py index 7d976c91c..498ab0d38 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_host.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_host.py @@ -20,7 +20,7 @@ class TestVolumeHost(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.host_mock = self.app.client_manager.volume.services + self.host_mock = self.volume_client.services self.host_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py index f8120a150..c4762595b 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py @@ -29,9 +29,9 @@ class TestVolumeSnapshot(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.snapshots_mock = self.app.client_manager.volume.volume_snapshots + self.snapshots_mock = self.volume_client.volume_snapshots self.snapshots_mock.reset_mock() - self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() self.project_mock = self.app.client_manager.identity.projects self.project_mock.reset_mock() 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 706783bd7..bedcadd66 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py @@ -29,11 +29,11 @@ def setUp(self): super().setUp() # Get a shortcut to the TransferManager Mock - self.transfer_mock = self.app.client_manager.volume.transfers + self.transfer_mock = self.volume_client.transfers self.transfer_mock.reset_mock() # Get a shortcut to the VolumeManager Mock - self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() @@ -177,9 +177,7 @@ def test_transfer_create_with_name(self): self.assertEqual(self.data, data) def test_transfer_create_with_no_snapshots(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.55' - ) + self.volume_client.api_version = api_versions.APIVersion('3.55') arglist = [ '--no-snapshots', @@ -201,9 +199,7 @@ def test_transfer_create_with_no_snapshots(self): self.assertEqual(self.data, data) def test_transfer_create_pre_v355(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.54' - ) + self.volume_client.api_version = api_versions.APIVersion('3.54') arglist = [ '--no-snapshots', diff --git a/openstackclient/tests/unit/volume/v2/test_volume_type.py b/openstackclient/tests/unit/volume/v2/test_volume_type.py index ebcff45fa..084e23745 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_type.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_type.py @@ -29,16 +29,14 @@ class TestType(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volume_types_mock = self.app.client_manager.volume.volume_types + self.volume_types_mock = self.volume_client.volume_types self.volume_types_mock.reset_mock() - self.volume_type_access_mock = ( - self.app.client_manager.volume.volume_type_access - ) + self.volume_type_access_mock = self.volume_client.volume_type_access self.volume_type_access_mock.reset_mock() self.volume_encryption_types_mock = ( - self.app.client_manager.volume.volume_encryption_types + self.volume_client.volume_encryption_types ) self.volume_encryption_types_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index 454c90fbd..4801d07e1 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -15,8 +15,8 @@ import uuid from cinderclient import api_versions +from openstack.block_storage.v3 import _proxy from openstack.block_storage.v3 import availability_zone as _availability_zone -from openstack.block_storage.v3 import block_storage_summary as _summary from openstack.block_storage.v3 import extension as _extension from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes @@ -63,6 +63,15 @@ def setUp(self): self.app.client_manager.volume = FakeVolumeClient( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN ) + self.volume_client = self.app.client_manager.volume + + # TODO(stephenfin): Rename to 'volume_client' once all commands are + # migrated to SDK + self.app.client_manager.sdk_connection.volume = mock.Mock( + spec=_proxy.Proxy, + ) + self.volume_sdk_client = self.app.client_manager.sdk_connection.volume + self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN ) @@ -605,14 +614,3 @@ def create_snapshot_manage_list_records(count=2): ) return snapshot_manage_list - - -def get_one_block_storage_summary(total_size, metadata=None): - summary_dict = { - 'total_count': 2, - 'total_size': total_size, - } - if metadata: - summary_dict['metadata'] = metadata - block_storage_summary = _summary.BlockStorageSummary(**summary_dict) - return block_storage_summary diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_cleanup.py b/openstackclient/tests/unit/volume/v3/test_block_storage_cleanup.py index 7739dbc25..756ea3257 100644 --- a/openstackclient/tests/unit/volume/v3/test_block_storage_cleanup.py +++ b/openstackclient/tests/unit/volume/v3/test_block_storage_cleanup.py @@ -24,7 +24,7 @@ def setUp(self): super().setUp() # Get a shortcut to the BlockStorageWorkerManager Mock - self.worker_mock = self.app.client_manager.volume.workers + self.worker_mock = self.volume_client.workers self.worker_mock.reset_mock() @@ -40,9 +40,7 @@ def setUp(self): self.cmd = block_storage_cleanup.BlockStorageCleanup(self.app, None) def test_cleanup(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.24' - ) + self.volume_client.api_version = api_versions.APIVersion('3.24') arglist = [] verifylist = [ @@ -98,9 +96,7 @@ def test_block_storage_cleanup_pre_324(self): ) def test_cleanup_with_args(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.24' - ) + self.volume_client.api_version = api_versions.APIVersion('3.24') fake_cluster = 'fake-cluster' fake_host = 'fake-host' diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py b/openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py index c6ba6340e..f572b060d 100644 --- a/openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py +++ b/openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py @@ -22,7 +22,7 @@ def setUp(self): super().setUp() # Get a shortcut to the BlockStorageClusterManager Mock - self.cluster_mock = self.app.client_manager.volume.clusters + self.cluster_mock = self.volume_client.clusters self.cluster_mock.reset_mock() @@ -41,9 +41,7 @@ def setUp(self): ) def test_cluster_list(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.7' - ) + self.volume_client.api_version = api_versions.APIVersion('3.7') arglist = [] verifylist = [ @@ -84,9 +82,7 @@ def test_cluster_list(self): ) def test_cluster_list_with_full_options(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.7' - ) + self.volume_client.api_version = api_versions.APIVersion('3.7') arglist = [ '--cluster', @@ -156,9 +152,7 @@ def test_cluster_list_with_full_options(self): ) def test_cluster_list_pre_v37(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.6' - ) + self.volume_client.api_version = api_versions.APIVersion('3.6') arglist = [] verifylist = [ @@ -221,9 +215,7 @@ def setUp(self): self.cmd = block_storage_cluster.SetBlockStorageCluster(self.app, None) def test_cluster_set(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.7' - ) + self.volume_client.api_version = api_versions.APIVersion('3.7') arglist = [ '--enable', @@ -250,9 +242,7 @@ def test_cluster_set(self): ) def test_cluster_set_disable_with_reason(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.7' - ) + self.volume_client.api_version = api_versions.APIVersion('3.7') arglist = [ '--binary', @@ -282,9 +272,7 @@ def test_cluster_set_disable_with_reason(self): ) def test_cluster_set_only_with_disable_reason(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.7' - ) + self.volume_client.api_version = api_versions.APIVersion('3.7') arglist = [ '--disable-reason', @@ -307,9 +295,7 @@ def test_cluster_set_only_with_disable_reason(self): ) def test_cluster_set_enable_with_disable_reason(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.7' - ) + self.volume_client.api_version = api_versions.APIVersion('3.7') arglist = [ '--enable', @@ -333,9 +319,7 @@ def test_cluster_set_enable_with_disable_reason(self): ) def test_cluster_set_pre_v37(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.6' - ) + self.volume_client.api_version = api_versions.APIVersion('3.6') arglist = [ '--enable', @@ -401,9 +385,7 @@ def setUp(self): ) def test_cluster_show(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.7' - ) + self.volume_client.api_version = api_versions.APIVersion('3.7') arglist = [ '--binary', @@ -427,9 +409,7 @@ def test_cluster_show(self): ) def test_cluster_show_pre_v37(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.6' - ) + self.volume_client.api_version = api_versions.APIVersion('3.6') arglist = [ '--binary', 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 769954a29..028be2af7 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 @@ -26,7 +26,7 @@ def setUp(self): super().setUp() # Get a shortcut to the ServiceManager Mock - self.service_mock = self.app.client_manager.volume.services + self.service_mock = self.volume_client.services self.service_mock.reset_mock() @@ -42,9 +42,7 @@ def setUp(self): self.cmd = service.BlockStorageLogLevelList(self.app, None) def test_block_storage_log_level_list(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.32' - ) + self.volume_client.api_version = api_versions.APIVersion('3.32') arglist = [ '--host', self.service_log.host, @@ -115,9 +113,7 @@ def test_block_storage_log_level_list_pre_332(self): ) def test_block_storage_log_level_list_invalid_service_name(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.32' - ) + self.volume_client.api_version = api_versions.APIVersion('3.32') arglist = [ '--host', self.service_log.host, @@ -152,9 +148,7 @@ def setUp(self): self.cmd = service.BlockStorageLogLevelSet(self.app, None) def test_block_storage_log_level_set(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.32' - ) + self.volume_client.api_version = api_versions.APIVersion('3.32') arglist = [ 'ERROR', '--host', @@ -208,9 +202,7 @@ def test_block_storage_log_level_set_pre_332(self): ) def test_block_storage_log_level_set_invalid_service_name(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.32' - ) + self.volume_client.api_version = api_versions.APIVersion('3.32') arglist = [ 'ERROR', '--host', @@ -237,9 +229,7 @@ def test_block_storage_log_level_set_invalid_service_name(self): @ddt.data('WARNING', 'info', 'Error', 'debuG', 'fake-log-level') def test_block_storage_log_level_set_log_level(self, log_level): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.32' - ) + self.volume_client.api_version = api_versions.APIVersion('3.32') arglist = [ log_level, '--host', diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py b/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py index fc3a0029f..54f484815 100644 --- a/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py +++ b/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py @@ -25,9 +25,9 @@ class TestBlockStorageManage(v2_volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() - self.snapshots_mock = self.app.client_manager.volume.volume_snapshots + self.snapshots_mock = self.volume_client.volume_snapshots self.snapshots_mock.reset_mock() @@ -47,9 +47,7 @@ def setUp(self): ) def test_block_storage_volume_manage_list(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.8' - ) + self.volume_client.api_version = api_versions.APIVersion('3.8') arglist = [ 'fake_host', ] @@ -106,9 +104,7 @@ def test_block_storage_volume_manage_list__pre_v38(self): ) def test_block_storage_volume_manage_list__pre_v317(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.16' - ) + self.volume_client.api_version = api_versions.APIVersion('3.16') arglist = [ '--cluster', 'fake_cluster', @@ -127,9 +123,7 @@ def test_block_storage_volume_manage_list__pre_v317(self): self.assertIn('--cluster', str(exc)) def test_block_storage_volume_manage_list__host_and_cluster(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.17' - ) + self.volume_client.api_version = api_versions.APIVersion('3.17') arglist = [ 'fake_host', '--cluster', @@ -152,9 +146,7 @@ def test_block_storage_volume_manage_list__host_and_cluster(self): def test_block_storage_volume_manage_list__detailed(self): """This option is deprecated.""" - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.8' - ) + self.volume_client.api_version = api_versions.APIVersion('3.8') arglist = [ '--detailed', 'True', @@ -294,9 +286,7 @@ def setUp(self): ) def test_block_storage_snapshot_manage_list(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.8' - ) + self.volume_client.api_version = api_versions.APIVersion('3.8') arglist = [ 'fake_host', ] @@ -355,9 +345,7 @@ def test_block_storage_snapshot_manage_list__pre_v38(self): ) def test_block_storage_snapshot_manage_list__pre_v317(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.16' - ) + self.volume_client.api_version = api_versions.APIVersion('3.16') arglist = [ '--cluster', 'fake_cluster', @@ -376,9 +364,7 @@ def test_block_storage_snapshot_manage_list__pre_v317(self): self.assertIn('--cluster', str(exc)) def test_block_storage_snapshot_manage_list__host_and_cluster(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.17' - ) + self.volume_client.api_version = api_versions.APIVersion('3.17') arglist = [ 'fake_host', '--cluster', @@ -400,10 +386,7 @@ def test_block_storage_snapshot_manage_list__host_and_cluster(self): ) def test_block_storage_snapshot_manage_list__detailed(self): - """This option is deprecated.""" - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.8' - ) + self.volume_client.api_version = api_versions.APIVersion('3.8') arglist = [ '--detailed', 'True', diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py b/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py index 782da15db..a5c0da3d3 100644 --- a/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py +++ b/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py @@ -22,9 +22,7 @@ def setUp(self): super().setUp() # Get a shortcut to the ResourceFilterManager Mock - self.resource_filter_mock = ( - self.app.client_manager.volume.resource_filters - ) + self.resource_filter_mock = self.volume_client.resource_filters self.resource_filter_mock.reset_mock() @@ -47,9 +45,7 @@ def setUp(self): ) def test_resource_filter_list(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.33' - ) + self.volume_client.api_version = api_versions.APIVersion('3.33') arglist = [] verifylist = [] @@ -72,9 +68,7 @@ def test_resource_filter_list(self): self.resource_filter_mock.list.assert_called_with() def test_resource_filter_list_pre_v333(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.32' - ) + self.volume_client.api_version = api_versions.APIVersion('3.32') arglist = [] verifylist = [] @@ -107,9 +101,7 @@ def setUp(self): ) def test_resource_filter_show(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.33' - ) + self.volume_client.api_version = api_versions.APIVersion('3.33') arglist = [ self.fake_resource_filter.resource, @@ -133,9 +125,7 @@ def test_resource_filter_show(self): self.resource_filter_mock.list.assert_called_with(resource='volume') def test_resource_filter_show_pre_v333(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.32' - ) + self.volume_client.api_version = api_versions.APIVersion('3.32') arglist = [ self.fake_resource_filter.resource, diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index 93b7a018e..a9db383ad 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -16,30 +16,29 @@ from unittest import mock from cinderclient import api_versions +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.test import fakes as sdk_fakes from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib import exceptions -from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes -from openstackclient.tests.unit.volume.v3 import fakes as v3_fakes +from openstackclient.tests.unit.volume.v3 import fakes from openstackclient.volume.v3 import volume -class BaseVolumeTest(volume_fakes.TestVolume): +class BaseVolumeTest(fakes.TestVolume): def setUp(self): super().setUp() - self.app.client_manager.sdk_connection = mock.Mock() - self.app.client_manager.sdk_connection.volume = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.volume - patcher = mock.patch.object( sdk_utils, 'supports_microversion', return_value=True ) self.addCleanup(patcher.stop) self.supports_microversion_mock = patcher.start() self._set_mock_microversion( - self.app.client_manager.volume.api_version.get_string() + self.volume_client.api_version.get_string() ) def _set_mock_microversion(self, mock_v): @@ -60,12 +59,14 @@ class TestVolumeSummary(BaseVolumeTest): def setUp(self): super().setUp() - self.mock_vol_1 = volume_fakes.create_one_volume() - self.mock_vol_2 = volume_fakes.create_one_volume() - block_storage_summary = v3_fakes.get_one_block_storage_summary( - self.mock_vol_1.size + self.mock_vol_2.size + self.volume_a = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_b = sdk_fakes.generate_fake_resource(_volume.Volume) + self.summary = sdk_fakes.generate_fake_resource( + _summary.BlockStorageSummary, + total_count=2, + total_size=self.volume_a.size + self.volume_b.size, ) - self.sdk_client.summary.return_value = block_storage_summary + self.volume_sdk_client.summary.return_value = self.summary # Get the command object to test self.cmd = volume.VolumeSummary(self.app, None) @@ -82,11 +83,11 @@ def test_volume_summary(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.summary.assert_called_once_with(True) + self.volume_sdk_client.summary.assert_called_once_with(True) self.assertEqual(self.columns, columns) - datalist = (2, self.mock_vol_1.size + self.mock_vol_2.size) + datalist = (2, self.volume_a.size + self.volume_b.size) self.assertCountEqual(datalist, tuple(data)) def test_volume_summary_pre_312(self): @@ -108,11 +109,14 @@ def test_volume_summary_pre_312(self): def test_volume_summary_with_metadata(self): self._set_mock_microversion('3.36') - combine_meta = {**self.mock_vol_1.metadata, **self.mock_vol_2.metadata} - block_storage_summary = v3_fakes.get_one_block_storage_summary( - self.mock_vol_1.size + self.mock_vol_2.size, metadata=combine_meta + metadata = {**self.volume_a.metadata, **self.volume_b.metadata} + self.summary = sdk_fakes.generate_fake_resource( + _summary.BlockStorageSummary, + total_count=2, + total_size=self.volume_a.size + self.volume_b.size, + metadata=metadata, ) - self.sdk_client.summary.return_value = block_storage_summary + self.volume_sdk_client.summary.return_value = self.summary new_cols = copy.deepcopy(self.columns) new_cols.extend(['Metadata']) @@ -127,14 +131,14 @@ def test_volume_summary_with_metadata(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.summary.assert_called_once_with(True) + self.volume_sdk_client.summary.assert_called_once_with(True) self.assertEqual(new_cols, columns) datalist = ( 2, - self.mock_vol_1.size + self.mock_vol_2.size, - format_columns.DictColumn(combine_meta), + self.volume_a.size + self.volume_b.size, + format_columns.DictColumn(metadata), ) self.assertCountEqual(datalist, tuple(data)) @@ -143,20 +147,23 @@ class TestVolumeRevertToSnapshot(BaseVolumeTest): def setUp(self): super().setUp() - self.mock_volume = volume_fakes.create_one_volume() - self.mock_snapshot = volume_fakes.create_one_snapshot( - attrs={'volume_id': self.mock_volume.id} + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.snapshot = sdk_fakes.generate_fake_resource( + _snapshot.Snapshot, + volume_id=self.volume.id, ) + self.volume_sdk_client.find_volume.return_value = self.volume + self.volume_sdk_client.find_snapshot.return_value = self.snapshot # Get the command object to test self.cmd = volume.VolumeRevertToSnapshot(self.app, None) def test_volume_revert_to_snapshot_pre_340(self): arglist = [ - self.mock_snapshot.id, + self.snapshot.id, ] verifylist = [ - ('snapshot', self.mock_snapshot.id), + ('snapshot', self.snapshot.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -170,29 +177,24 @@ def test_volume_revert_to_snapshot_pre_340(self): def test_volume_revert_to_snapshot(self): self._set_mock_microversion('3.40') arglist = [ - self.mock_snapshot.id, + self.snapshot.id, ] verifylist = [ - ('snapshot', self.mock_snapshot.id), + ('snapshot', self.snapshot.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object( - self.sdk_client, 'find_volume', return_value=self.mock_volume - ), mock.patch.object( - self.sdk_client, 'find_snapshot', return_value=self.mock_snapshot - ): - self.cmd.take_action(parsed_args) - - self.sdk_client.revert_volume_to_snapshot.assert_called_once_with( - self.mock_volume, - self.mock_snapshot, - ) - self.sdk_client.find_volume.assert_called_with( - self.mock_volume.id, - ignore_missing=False, - ) - self.sdk_client.find_snapshot.assert_called_with( - self.mock_snapshot.id, - ignore_missing=False, - ) + self.cmd.take_action(parsed_args) + + self.volume_sdk_client.revert_volume_to_snapshot.assert_called_once_with( + self.volume, + self.snapshot, + ) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, + ignore_missing=False, + ) + self.volume_sdk_client.find_snapshot.assert_called_with( + self.snapshot.id, + ignore_missing=False, + ) diff --git a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py index 73f0e1a8b..3655c8bfb 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py @@ -24,12 +24,10 @@ class TestVolumeAttachment(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volumes_mock = self.app.client_manager.volume.volumes + self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() - self.volume_attachments_mock = ( - self.app.client_manager.volume.attachments - ) + self.volume_attachments_mock = self.volume_client.attachments self.volume_attachments_mock.reset_mock() self.projects_mock = self.app.client_manager.identity.projects @@ -80,9 +78,7 @@ def setUp(self): self.cmd = volume_attachment.CreateVolumeAttachment(self.app, None) def test_volume_attachment_create(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.27' - ) + self.volume_client.api_version = api_versions.APIVersion('3.27') arglist = [ self.volume.id, @@ -117,9 +113,7 @@ def test_volume_attachment_create(self): self.assertCountEqual(self.data, data) def test_volume_attachment_create_with_connect(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.54' - ) + self.volume_client.api_version = api_versions.APIVersion('3.54') arglist = [ self.volume.id, @@ -182,9 +176,7 @@ def test_volume_attachment_create_with_connect(self): self.assertCountEqual(self.data, data) def test_volume_attachment_create_pre_v327(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.26' - ) + self.volume_client.api_version = api_versions.APIVersion('3.26') arglist = [ self.volume.id, @@ -204,9 +196,7 @@ def test_volume_attachment_create_pre_v327(self): ) def test_volume_attachment_create_with_mode_pre_v354(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.53' - ) + self.volume_client.api_version = api_versions.APIVersion('3.53') arglist = [ self.volume.id, @@ -229,9 +219,7 @@ def test_volume_attachment_create_with_mode_pre_v354(self): ) def test_volume_attachment_create_with_connect_missing_arg(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.54' - ) + self.volume_client.api_version = api_versions.APIVersion('3.54') arglist = [ self.volume.id, @@ -266,9 +254,7 @@ def setUp(self): self.cmd = volume_attachment.DeleteVolumeAttachment(self.app, None) def test_volume_attachment_delete(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.27' - ) + self.volume_client.api_version = api_versions.APIVersion('3.27') arglist = [ self.volume_attachment.id, @@ -286,9 +272,7 @@ def test_volume_attachment_delete(self): self.assertIsNone(result) def test_volume_attachment_delete_pre_v327(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.26' - ) + self.volume_client.api_version = api_versions.APIVersion('3.26') arglist = [ self.volume_attachment.id, @@ -340,9 +324,7 @@ def setUp(self): self.cmd = volume_attachment.SetVolumeAttachment(self.app, None) def test_volume_attachment_set(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.27' - ) + self.volume_client.api_version = api_versions.APIVersion('3.27') arglist = [ self.volume_attachment.id, @@ -394,9 +376,7 @@ def test_volume_attachment_set(self): self.assertCountEqual(self.data, data) def test_volume_attachment_set_pre_v327(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.26' - ) + self.volume_client.api_version = api_versions.APIVersion('3.26') arglist = [ self.volume_attachment.id, @@ -428,9 +408,7 @@ def setUp(self): self.cmd = volume_attachment.CompleteVolumeAttachment(self.app, None) def test_volume_attachment_complete(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.44' - ) + self.volume_client.api_version = api_versions.APIVersion('3.44') arglist = [ self.volume_attachment.id, @@ -448,9 +426,7 @@ def test_volume_attachment_complete(self): self.assertIsNone(result) def test_volume_attachment_complete_pre_v344(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.43' - ) + self.volume_client.api_version = api_versions.APIVersion('3.43') arglist = [ self.volume_attachment.id, @@ -499,9 +475,7 @@ def setUp(self): self.cmd = volume_attachment.ListVolumeAttachment(self.app, None) def test_volume_attachment_list(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.27' - ) + self.volume_client.api_version = api_versions.APIVersion('3.27') arglist = [] verifylist = [ @@ -530,9 +504,7 @@ def test_volume_attachment_list(self): self.assertCountEqual(tuple(self.data), data) def test_volume_attachment_list_with_options(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.27' - ) + self.volume_client.api_version = api_versions.APIVersion('3.27') arglist = [ '--project', @@ -572,9 +544,7 @@ def test_volume_attachment_list_with_options(self): self.assertCountEqual(tuple(self.data), data) def test_volume_attachment_list_pre_v327(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.26' - ) + self.volume_client.api_version = api_versions.APIVersion('3.26') arglist = [] verifylist = [ diff --git a/openstackclient/tests/unit/volume/v3/test_volume_group.py b/openstackclient/tests/unit/volume/v3/test_volume_group.py index e79eefb58..444ea707f 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_group.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_group.py @@ -24,20 +24,16 @@ class TestVolumeGroup(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volume_groups_mock = self.app.client_manager.volume.groups + self.volume_groups_mock = self.volume_client.groups self.volume_groups_mock.reset_mock() - self.volume_group_types_mock = ( - self.app.client_manager.volume.group_types - ) + self.volume_group_types_mock = self.volume_client.group_types self.volume_group_types_mock.reset_mock() - self.volume_types_mock = self.app.client_manager.volume.volume_types + self.volume_types_mock = self.volume_client.volume_types self.volume_types_mock.reset_mock() - self.volume_group_snapshots_mock = ( - self.app.client_manager.volume.group_snapshots - ) + self.volume_group_snapshots_mock = self.volume_client.group_snapshots self.volume_group_snapshots_mock.reset_mock() @@ -100,9 +96,7 @@ def setUp(self): self.cmd = volume_group.CreateVolumeGroup(self.app, None) def test_volume_group_create(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.13' - ) + self.volume_client.api_version = api_versions.APIVersion('3.13') arglist = [ '--volume-group-type', @@ -138,9 +132,7 @@ def test_volume_group_create(self): self.assertCountEqual(self.data, data) def test_volume_group_create__legacy(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.13' - ) + self.volume_client.api_version = api_versions.APIVersion('3.13') arglist = [ self.fake_volume_group_type.id, @@ -180,9 +172,7 @@ def test_volume_group_create__legacy(self): ) def test_volume_group_create_no_volume_type(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.13' - ) + self.volume_client.api_version = api_versions.APIVersion('3.13') arglist = [ '--volume-group-type', @@ -204,9 +194,7 @@ def test_volume_group_create_no_volume_type(self): ) def test_volume_group_create_with_options(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.13' - ) + self.volume_client.api_version = api_versions.APIVersion('3.13') arglist = [ '--volume-group-type', @@ -248,9 +236,7 @@ def test_volume_group_create_with_options(self): self.assertCountEqual(self.data, data) def test_volume_group_create_pre_v313(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.12' - ) + self.volume_client.api_version = api_versions.APIVersion('3.12') arglist = [ '--volume-group-type', @@ -275,9 +261,7 @@ def test_volume_group_create_pre_v313(self): ) def test_volume_group_create_from_source_group(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.14' - ) + self.volume_client.api_version = api_versions.APIVersion('3.14') arglist = [ '--source-group', @@ -306,9 +290,7 @@ def test_volume_group_create_from_source_group(self): self.assertCountEqual(self.data, data) def test_volume_group_create_from_group_snapshot(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.14' - ) + self.volume_client.api_version = api_versions.APIVersion('3.14') arglist = [ '--group-snapshot', @@ -337,9 +319,7 @@ def test_volume_group_create_from_group_snapshot(self): self.assertCountEqual(self.data, data) def test_volume_group_create_from_src_pre_v314(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.13' - ) + self.volume_client.api_version = api_versions.APIVersion('3.13') arglist = [ '--source-group', @@ -358,9 +338,7 @@ def test_volume_group_create_from_src_pre_v314(self): ) def test_volume_group_create_from_src_source_group_group_snapshot(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.14' - ) + self.volume_client.api_version = api_versions.APIVersion('3.14') arglist = [ '--source-group', @@ -398,9 +376,7 @@ def setUp(self): self.cmd = volume_group.DeleteVolumeGroup(self.app, None) def test_volume_group_delete(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.13' - ) + self.volume_client.api_version = api_versions.APIVersion('3.13') arglist = [ self.fake_volume_group.id, @@ -421,9 +397,7 @@ def test_volume_group_delete(self): self.assertIsNone(result) def test_volume_group_delete_pre_v313(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.12' - ) + self.volume_client.api_version = api_versions.APIVersion('3.12') arglist = [ self.fake_volume_group.id, @@ -481,9 +455,7 @@ def setUp(self): self.cmd = volume_group.SetVolumeGroup(self.app, None) def test_volume_group_set(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.13' - ) + self.volume_client.api_version = api_versions.APIVersion('3.13') arglist = [ self.fake_volume_group.id, @@ -510,9 +482,7 @@ def test_volume_group_set(self): self.assertCountEqual(self.data, data) def test_volume_group_with_enable_replication_option(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.38' - ) + self.volume_client.api_version = api_versions.APIVersion('3.38') arglist = [ self.fake_volume_group.id, @@ -533,9 +503,7 @@ def test_volume_group_with_enable_replication_option(self): self.assertCountEqual(self.data, data) def test_volume_group_set_pre_v313(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.12' - ) + self.volume_client.api_version = api_versions.APIVersion('3.12') arglist = [ self.fake_volume_group.id, @@ -559,9 +527,7 @@ def test_volume_group_set_pre_v313(self): ) def test_volume_group_with_enable_replication_option_pre_v338(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.37' - ) + self.volume_client.api_version = api_versions.APIVersion('3.37') arglist = [ self.fake_volume_group.id, @@ -606,9 +572,7 @@ def setUp(self): self.cmd = volume_group.ListVolumeGroup(self.app, None) def test_volume_group_list(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.13' - ) + self.volume_client.api_version = api_versions.APIVersion('3.13') arglist = [ '--all-projects', @@ -629,9 +593,7 @@ def test_volume_group_list(self): self.assertCountEqual(tuple(self.data), data) def test_volume_group_list_pre_v313(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.12' - ) + self.volume_client.api_version = api_versions.APIVersion('3.12') arglist = [ '--all-projects', @@ -661,9 +623,7 @@ def setUp(self): self.cmd = volume_group.FailoverVolumeGroup(self.app, None) def test_volume_group_failover(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.38' - ) + self.volume_client.api_version = api_versions.APIVersion('3.38') arglist = [ self.fake_volume_group.id, @@ -688,9 +648,7 @@ def test_volume_group_failover(self): self.assertIsNone(result) def test_volume_group_failover_pre_v338(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.37' - ) + self.volume_client.api_version = api_versions.APIVersion('3.37') arglist = [ self.fake_volume_group.id, diff --git a/openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py b/openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py index 664e56e6c..1c680f45e 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py @@ -21,12 +21,10 @@ class TestVolumeGroupSnapshot(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volume_groups_mock = self.app.client_manager.volume.groups + self.volume_groups_mock = self.volume_client.groups self.volume_groups_mock.reset_mock() - self.volume_group_snapshots_mock = ( - self.app.client_manager.volume.group_snapshots - ) + self.volume_group_snapshots_mock = self.volume_client.group_snapshots self.volume_group_snapshots_mock.reset_mock() @@ -69,9 +67,7 @@ def setUp(self): ) def test_volume_group_snapshot_create(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.14' - ) + self.volume_client.api_version = api_versions.APIVersion('3.14') arglist = [ self.fake_volume_group.id, @@ -97,9 +93,7 @@ def test_volume_group_snapshot_create(self): self.assertCountEqual(self.data, data) def test_volume_group_snapshot_create_with_options(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.14' - ) + self.volume_client.api_version = api_versions.APIVersion('3.14') arglist = [ self.fake_volume_group.id, @@ -129,9 +123,7 @@ def test_volume_group_snapshot_create_with_options(self): self.assertCountEqual(self.data, data) def test_volume_group_snapshot_create_pre_v314(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.13' - ) + self.volume_client.api_version = api_versions.APIVersion('3.13') arglist = [ self.fake_volume_group.id, @@ -169,9 +161,7 @@ def setUp(self): ) def test_volume_group_snapshot_delete(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.14' - ) + self.volume_client.api_version = api_versions.APIVersion('3.14') arglist = [ self.fake_volume_group_snapshot.id, @@ -189,9 +179,7 @@ def test_volume_group_snapshot_delete(self): self.assertIsNone(result) def test_volume_group_snapshot_delete_pre_v314(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.13' - ) + self.volume_client.api_version = api_versions.APIVersion('3.13') arglist = [ self.fake_volume_group_snapshot.id, @@ -238,9 +226,7 @@ def setUp(self): ) def test_volume_group_snapshot_list(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.14' - ) + self.volume_client.api_version = api_versions.APIVersion('3.14') arglist = [ '--all-projects', @@ -261,9 +247,7 @@ def test_volume_group_snapshot_list(self): self.assertCountEqual(tuple(self.data), data) def test_volume_group_snapshot_list_pre_v314(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.13' - ) + self.volume_client.api_version = api_versions.APIVersion('3.13') arglist = [] verifylist = [ diff --git a/openstackclient/tests/unit/volume/v3/test_volume_group_type.py b/openstackclient/tests/unit/volume/v3/test_volume_group_type.py index 7ea2a2da8..e44b057d1 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_group_type.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_group_type.py @@ -24,9 +24,7 @@ class TestVolumeGroupType(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volume_group_types_mock = ( - self.app.client_manager.volume.group_types - ) + self.volume_group_types_mock = self.volume_client.group_types self.volume_group_types_mock.reset_mock() @@ -60,9 +58,7 @@ def setUp(self): self.cmd = volume_group_type.CreateVolumeGroupType(self.app, None) def test_volume_group_type_create(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.11' - ) + self.volume_client.api_version = api_versions.APIVersion('3.11') arglist = [ self.fake_volume_group_type.name, @@ -83,9 +79,7 @@ def test_volume_group_type_create(self): self.assertCountEqual(self.data, data) def test_volume_group_type_create_with_options(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.11' - ) + self.volume_client.api_version = api_versions.APIVersion('3.11') arglist = [ self.fake_volume_group_type.name, @@ -109,9 +103,7 @@ def test_volume_group_type_create_with_options(self): self.assertCountEqual(self.data, data) def test_volume_group_type_create_pre_v311(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.10' - ) + self.volume_client.api_version = api_versions.APIVersion('3.10') arglist = [ self.fake_volume_group_type.name, @@ -145,9 +137,7 @@ def setUp(self): self.cmd = volume_group_type.DeleteVolumeGroupType(self.app, None) def test_volume_group_type_delete(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.11' - ) + self.volume_client.api_version = api_versions.APIVersion('3.11') arglist = [ self.fake_volume_group_type.id, @@ -165,9 +155,7 @@ def test_volume_group_type_delete(self): self.assertIsNone(result) def test_volume_group_type_delete_pre_v311(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.10' - ) + self.volume_client.api_version = api_versions.APIVersion('3.10') arglist = [ self.fake_volume_group_type.id, @@ -222,9 +210,7 @@ def setUp(self): self.cmd = volume_group_type.SetVolumeGroupType(self.app, None) def test_volume_group_type_set(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.11' - ) + self.volume_client.api_version = api_versions.APIVersion('3.11') self.fake_volume_group_type.set_keys.return_value = None @@ -263,9 +249,7 @@ def test_volume_group_type_set(self): self.assertCountEqual(self.data, data) def test_volume_group_type_with_no_property_option(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.11' - ) + self.volume_client.api_version = api_versions.APIVersion('3.11') arglist = [ self.fake_volume_group_type.id, @@ -296,9 +280,7 @@ def test_volume_group_type_with_no_property_option(self): self.assertCountEqual(self.data, data) def test_volume_group_type_set_pre_v311(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.10' - ) + self.volume_client.api_version = api_versions.APIVersion('3.10') arglist = [ self.fake_volume_group_type.id, @@ -355,9 +337,7 @@ def setUp(self): self.cmd = volume_group_type.UnsetVolumeGroupType(self.app, None) def test_volume_group_type_unset(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.11' - ) + self.volume_client.api_version = api_versions.APIVersion('3.11') arglist = [ self.fake_volume_group_type.id, @@ -385,9 +365,7 @@ def test_volume_group_type_unset(self): self.assertCountEqual(self.data, data) def test_volume_group_type_unset_pre_v311(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.10' - ) + self.volume_client.api_version = api_versions.APIVersion('3.10') arglist = [ self.fake_volume_group_type.id, @@ -440,9 +418,7 @@ def setUp(self): self.cmd = volume_group_type.ListVolumeGroupType(self.app, None) def test_volume_group_type_list(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.11' - ) + self.volume_client.api_version = api_versions.APIVersion('3.11') arglist = [] verifylist = [ @@ -457,9 +433,7 @@ def test_volume_group_type_list(self): self.assertCountEqual(tuple(self.data), data) def test_volume_group_type_list_with_default_option(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.11' - ) + self.volume_client.api_version = api_versions.APIVersion('3.11') arglist = [ '--default', @@ -476,9 +450,7 @@ def test_volume_group_type_list_with_default_option(self): self.assertCountEqual(tuple([self.data[0]]), data) def test_volume_group_type_list_pre_v311(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.10' - ) + self.volume_client.api_version = api_versions.APIVersion('3.10') arglist = [] verifylist = [] diff --git a/openstackclient/tests/unit/volume/v3/test_volume_message.py b/openstackclient/tests/unit/volume/v3/test_volume_message.py index 652deb64d..aa3fc8058 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_message.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_message.py @@ -27,7 +27,7 @@ def setUp(self): self.projects_mock = self.app.client_manager.identity.projects self.projects_mock.reset_mock() - self.volume_messages_mock = self.app.client_manager.volume.messages + self.volume_messages_mock = self.volume_client.messages self.volume_messages_mock.reset_mock() @@ -46,9 +46,7 @@ def setUp(self): self.cmd = volume_message.DeleteMessage(self.app, None) def test_message_delete(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.3' - ) + self.volume_client.api_version = api_versions.APIVersion('3.3') arglist = [ self.fake_messages[0].id, @@ -66,9 +64,7 @@ def test_message_delete(self): self.assertIsNone(result) def test_message_delete_multiple_messages(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.3' - ) + self.volume_client.api_version = api_versions.APIVersion('3.3') arglist = [ self.fake_messages[0].id, @@ -88,9 +84,7 @@ def test_message_delete_multiple_messages(self): self.assertIsNone(result) def test_message_delete_multiple_messages_with_exception(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.3' - ) + self.volume_client.api_version = api_versions.APIVersion('3.3') arglist = [ self.fake_messages[0].id, @@ -120,9 +114,7 @@ def test_message_delete_multiple_messages_with_exception(self): self.assertEqual(2, self.volume_messages_mock.delete.call_count) def test_message_delete_pre_v33(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.2' - ) + self.volume_client.api_version = api_versions.APIVersion('3.2') arglist = [ self.fake_messages[0].id, @@ -180,9 +172,7 @@ def setUp(self): self.cmd = volume_message.ListMessages(self.app, None) def test_message_list(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.3' - ) + self.volume_client.api_version = api_versions.APIVersion('3.3') arglist = [] verifylist = [ @@ -206,9 +196,7 @@ def test_message_list(self): self.assertCountEqual(self.data, list(data)) def test_message_list_with_options(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.3' - ) + self.volume_client.api_version = api_versions.APIVersion('3.3') arglist = [ '--project', @@ -239,9 +227,7 @@ def test_message_list_with_options(self): self.assertCountEqual(self.data, list(data)) def test_message_list_pre_v33(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.2' - ) + self.volume_client.api_version = api_versions.APIVersion('3.2') arglist = [] verifylist = [ @@ -294,9 +280,7 @@ def setUp(self): self.cmd = volume_message.ShowMessage(self.app, None) def test_message_show(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.3' - ) + self.volume_client.api_version = api_versions.APIVersion('3.3') arglist = [self.fake_message.id] verifylist = [('message_id', self.fake_message.id)] @@ -309,9 +293,7 @@ def test_message_show(self): self.assertEqual(self.data, data) def test_message_show_pre_v33(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.2' - ) + self.volume_client.api_version = api_versions.APIVersion('3.2') arglist = [self.fake_message.id] verifylist = [('message_id', self.fake_message.id)] From 98fb1678bc0bdb8de8c9260aaba752fbadf81f40 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 6 Sep 2023 11:22:49 +0100 Subject: [PATCH 007/403] tests: Add volume v1, v2, v3 FakeClientMixin This ensures we are speccing the image proxy API, as we did previously for the network tests in Ic203964c7dede7dd80ae2d93b8fa1b7e6634a758. Change-Id: I132ccd1170cc903f6edc505926b071170aeaa08c Signed-off-by: Stephen Finucane --- .../unit/common/test_availability_zone.py | 10 +-- .../tests/unit/compute/v2/fakes.py | 9 +-- .../tests/unit/compute/v2/test_server.py | 5 +- .../unit/compute/v2/test_server_volume.py | 2 - openstackclient/tests/unit/utils.py | 5 +- openstackclient/tests/unit/volume/v1/fakes.py | 7 +- openstackclient/tests/unit/volume/v2/fakes.py | 7 +- .../unit/volume/v2/test_volume_backup.py | 41 ++++++----- openstackclient/tests/unit/volume/v3/fakes.py | 73 ++++++++++++++++++- 9 files changed, 115 insertions(+), 44 deletions(-) diff --git a/openstackclient/tests/unit/common/test_availability_zone.py b/openstackclient/tests/unit/common/test_availability_zone.py index fbab128dc..1071f23d2 100644 --- a/openstackclient/tests/unit/common/test_availability_zone.py +++ b/openstackclient/tests/unit/common/test_availability_zone.py @@ -78,7 +78,11 @@ def _build_network_az_datalist(network_az, long_datalist=False): return (datalist,) -class TestAvailabilityZone(network_fakes.FakeClientMixin, utils.TestCommand): +class TestAvailabilityZone( + network_fakes.FakeClientMixin, + volume_fakes.FakeClientMixin, + utils.TestCommand, +): def setUp(self): super().setUp() @@ -86,10 +90,6 @@ def setUp(self): self.compute_client = self.app.client_manager.sdk_connection.compute self.compute_client.availability_zones = mock.Mock() - self.app.client_manager.sdk_connection.volume = mock.Mock() - self.volume_sdk_client = self.app.client_manager.sdk_connection.volume - self.volume_sdk_client.availability_zones = mock.Mock() - class TestAvailabilityZoneList(TestAvailabilityZone): compute_azs = compute_fakes.create_availability_zones() diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 1d6584979..b27471d98 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -41,7 +41,7 @@ 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 -from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes +from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes floating_ip_num = 100 fix_ip_num = 100 @@ -151,6 +151,7 @@ def __init__(self, **kwargs): class TestComputev2( network_fakes.FakeClientMixin, image_fakes.FakeClientMixin, + volume_fakes.FakeClientMixin, utils.TestCommand, ): def setUp(self): @@ -171,12 +172,6 @@ def setUp(self): token=fakes.AUTH_TOKEN, ) - self.app.client_manager.volume = volume_fakes.FakeVolumeClient( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, - ) - self.volume_client = self.app.client_manager.volume - def create_one_aggregate(attrs=None): """Create a fake aggregate. diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 31b864f48..e0e3fd06e 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -34,7 +34,7 @@ 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 -from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes +from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes class TestPowerStateColumn(utils.TestCase): @@ -95,9 +95,6 @@ def setUp(self): self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() - self.app.client_manager.sdk_connection.volume = mock.Mock() - self.volume_sdk_client = self.app.client_manager.sdk_connection.volume - # Get a shortcut to the volume client VolumeManager Mock self.snapshots_mock = self.volume_client.volume_snapshots self.snapshots_mock.reset_mock() diff --git a/openstackclient/tests/unit/compute/v2/test_server_volume.py b/openstackclient/tests/unit/compute/v2/test_server_volume.py index 33dd3a071..1ef726016 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_volume.py +++ b/openstackclient/tests/unit/compute/v2/test_server_volume.py @@ -27,9 +27,7 @@ def setUp(self): super().setUp() self.app.client_manager.sdk_connection.compute = mock.Mock() - self.app.client_manager.sdk_connection.volume = mock.Mock() self.compute_client = self.app.client_manager.sdk_connection.compute - self.volume_sdk_client = self.app.client_manager.sdk_connection.volume class TestServerVolumeList(TestServerVolume): diff --git a/openstackclient/tests/unit/utils.py b/openstackclient/tests/unit/utils.py index fa16389a5..1691424c6 100644 --- a/openstackclient/tests/unit/utils.py +++ b/openstackclient/tests/unit/utils.py @@ -12,9 +12,8 @@ # 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 io import StringIO +import io import os import fixtures @@ -75,7 +74,7 @@ def setUp(self): def check_parser(self, cmd, args, verify_args): cmd_parser = cmd.get_parser('check_parser') - stderr = StringIO() + stderr = io.StringIO() with fixtures.MonkeyPatch('sys.stderr', stderr): try: parsed_args = cmd_parser.parse_args(args) diff --git a/openstackclient/tests/unit/volume/v1/fakes.py b/openstackclient/tests/unit/volume/v1/fakes.py index b4da68c33..b9d9bf264 100644 --- a/openstackclient/tests/unit/volume/v1/fakes.py +++ b/openstackclient/tests/unit/volume/v1/fakes.py @@ -53,7 +53,7 @@ def __init__(self, **kwargs): self.management_url = kwargs['endpoint'] -class TestVolumev1(utils.TestCommand): +class FakeClientMixin: def setUp(self): super().setUp() @@ -63,6 +63,11 @@ def setUp(self): ) self.volume_client = self.app.client_manager.volume + +class TestVolumev1(FakeClientMixin, utils.TestCommand): + def setUp(self): + super().setUp() + self.app.client_manager.identity = identity_fakes.FakeIdentityv2Client( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN, diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index 5145796a4..2af5cf0de 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -89,7 +89,7 @@ def __init__(self, **kwargs): self.volumes.resource_class = fakes.FakeResource(None, {}) -class TestVolume(utils.TestCommand): +class FakeClientMixin: def setUp(self): super().setUp() @@ -105,6 +105,11 @@ def setUp(self): ) self.volume_sdk_client = self.app.client_manager.sdk_connection.volume + +class TestVolume(FakeClientMixin, utils.TestCommand): + def setUp(self): + super().setUp() + self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN ) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index 3cacb0aa1..aa0c050cd 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -41,8 +41,6 @@ class TestBackup(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.app.client_manager.sdk_connection.volume = mock.Mock() - self.volume_sdk_client = self.app.client_manager.sdk_connection.volume patcher = mock.patch.object( sdk_utils, 'supports_microversion', return_value=True ) @@ -325,24 +323,27 @@ def test_delete_multiple_backups_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) find_mock_result = [self.backups[0], exceptions.CommandError] - with mock.patch.object( - self.volume_sdk_client, 'find_backup', 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[0].id, ignore_missing=False) - find_mock.assert_any_call('unexist_backup', ignore_missing=False) - - self.assertEqual(2, find_mock.call_count) - self.volume_sdk_client.delete_backup.assert_called_once_with( - self.backups[0].id, - ignore_missing=False, - force=False, - ) + self.volume_sdk_client.find_backup.side_effect = find_mock_result + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 backups failed to delete.', str(e)) + + self.volume_sdk_client.find_backup.assert_any_call( + self.backups[0].id, ignore_missing=False + ) + self.volume_sdk_client.find_backup.assert_any_call( + 'unexist_backup', ignore_missing=False + ) + + self.assertEqual(2, self.volume_sdk_client.find_backup.call_count) + self.volume_sdk_client.delete_backup.assert_called_once_with( + self.backups[0].id, + ignore_missing=False, + force=False, + ) class TestBackupList(TestBackup): diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index 4801d07e1..ea8f54566 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -18,6 +18,7 @@ from openstack.block_storage.v3 import _proxy from openstack.block_storage.v3 import availability_zone as _availability_zone from openstack.block_storage.v3 import extension as _extension +from openstack.block_storage.v3 import volume as _volume from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes from openstackclient.tests.unit import fakes @@ -32,6 +33,8 @@ def __init__(self, **kwargs): self.management_url = kwargs['endpoint'] self.api_version = api_versions.APIVersion('3.0') + self.availability_zones = mock.Mock() + self.availability_zones.resource_class = fakes.FakeResource(None, {}) self.attachments = mock.Mock() self.attachments.resource_class = fakes.FakeResource(None, {}) self.clusters = mock.Mock() @@ -44,10 +47,16 @@ def __init__(self, **kwargs): self.group_types.resource_class = fakes.FakeResource(None, {}) self.messages = mock.Mock() self.messages.resource_class = fakes.FakeResource(None, {}) + self.quota_classes = mock.Mock() + self.quota_classes.resource_class = fakes.FakeResource(None, {}) + self.quotas = mock.Mock() + self.quotas.resource_class = fakes.FakeResource(None, {}) self.resource_filters = mock.Mock() self.resource_filters.resource_class = fakes.FakeResource(None, {}) self.volumes = mock.Mock() self.volumes.resource_class = fakes.FakeResource(None, {}) + self.volume_snapshots = mock.Mock() + self.volume_snapshots.resource_class = fakes.FakeResource(None, {}) self.volume_types = mock.Mock() self.volume_types.resource_class = fakes.FakeResource(None, {}) self.services = mock.Mock() @@ -56,7 +65,7 @@ def __init__(self, **kwargs): self.workers.resource_class = fakes.FakeResource(None, {}) -class TestVolume(utils.TestCommand): +class FakeClientMixin: def setUp(self): super().setUp() @@ -72,6 +81,11 @@ def setUp(self): ) self.volume_sdk_client = self.app.client_manager.sdk_connection.volume + +class TestVolume(FakeClientMixin, utils.TestCommand): + def setUp(self): + super().setUp() + self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN ) @@ -82,6 +96,7 @@ def setUp(self): # TODO(stephenfin): Check if the responses are actually the same +create_one_snapshot = volume_v2_fakes.create_one_snapshot create_one_volume = volume_v2_fakes.create_one_volume create_one_volume_type = volume_v2_fakes.create_one_volume_type @@ -243,6 +258,62 @@ def create_resource_filters(attrs=None, count=2): return resource_filters +def create_one_sdk_volume(attrs=None): + """Create a fake volume. + + :param dict 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, + 'name': 'volume-name' + uuid.uuid4().hex, + 'description': 'description' + uuid.uuid4().hex, + 'status': random.choice(['available', 'in_use']), + 'size': random.randint(1, 20), + 'volume_type': random.choice(['fake_lvmdriver-1', 'fake_lvmdriver-2']), + 'bootable': random.choice(['true', 'false']), + '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': random.randint(1, 5), + 'availability_zone': 'zone' + uuid.uuid4().hex, + 'attachments': [ + { + 'device': '/dev/' + uuid.uuid4().hex, + 'server_id': uuid.uuid4().hex, + }, + ], + } + + # Overwrite default attributes if there are some attributes set + volume_info.update(attrs) + return _volume.Volume(**volume_info) + + +def create_sdk_volumes(attrs=None, count=2): + """Create multiple fake volumes. + + :param dict 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_sdk_volume(attrs)) + + return volumes + + def create_one_volume_group(attrs=None): """Create a fake group. From 187a454ec028d6e39b604565c5c4d462182d0756 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 6 Sep 2023 11:34:06 +0100 Subject: [PATCH 008/403] tests: Use consistent shortcut to fake compute client This removes the need for a number of base test case subclasses. We use 'compute_client' rather than 'client' to avoid conflicts with clients for other services. Change-Id: I430214cd79eca481bd8d8c53bf97eaede6766eb4 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 8 +- .../unit/common/test_availability_zone.py | 26 +- .../tests/unit/common/test_limits.py | 4 +- .../tests/unit/common/test_quota.py | 24 +- .../tests/unit/compute/v2/fakes.py | 3 +- .../tests/unit/compute/v2/test_agent.py | 2 +- .../tests/unit/compute/v2/test_aggregate.py | 138 ++--- .../tests/unit/compute/v2/test_console.py | 40 +- .../tests/unit/compute/v2/test_flavor.py | 186 ++++--- .../tests/unit/compute/v2/test_host.py | 20 +- .../tests/unit/compute/v2/test_hypervisor.py | 40 +- .../unit/compute/v2/test_hypervisor_stats.py | 8 +- .../tests/unit/compute/v2/test_keypair.py | 68 ++- .../tests/unit/compute/v2/test_server.py | 518 ++++++++---------- .../unit/compute/v2/test_server_backup.py | 14 +- .../unit/compute/v2/test_server_event.py | 52 +- .../unit/compute/v2/test_server_group.py | 72 ++- .../unit/compute/v2/test_server_image.py | 16 +- .../unit/compute/v2/test_server_migration.py | 90 +-- .../unit/compute/v2/test_server_volume.py | 33 +- .../tests/unit/compute/v2/test_service.py | 96 ++-- .../tests/unit/compute/v2/test_usage.py | 14 +- .../tests/unit/network/test_common.py | 6 +- .../network/v2/test_floating_ip_compute.py | 4 +- .../v2/test_floating_ip_pool_compute.py | 2 +- .../unit/network/v2/test_network_compute.py | 4 +- .../tests/unit/network/v2/test_port.py | 7 +- .../network/v2/test_security_group_compute.py | 6 +- .../v2/test_security_group_rule_compute.py | 28 +- openstackclient/tests/unit/volume/v3/fakes.py | 2 + .../unit/volume/v3/test_volume_attachment.py | 3 +- 31 files changed, 772 insertions(+), 762 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 4ead22112..91c5b476a 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1575,8 +1575,8 @@ def _match_image(image_api, wanted_properties): if parsed_args.description: if compute_client.api_version < api_versions.APIVersion("2.19"): msg = _( - "Description is not supported for " - "--os-compute-api-version less than 2.19" + '--os-compute-api-version 2.19 or greater is ' + 'required to support the --description option' ) raise exceptions.CommandError(msg) @@ -4958,8 +4958,8 @@ def take_action(self, parsed_args): if parsed_args.description: if compute_client.api_version < api_versions.APIVersion("2.19"): msg = _( - "Description is not supported for " - "--os-compute-api-version less than 2.19" + '--os-compute-api-version 2.19 or greater is ' + 'required to support the --description option' ) raise exceptions.CommandError(msg) compute_client.servers.update( diff --git a/openstackclient/tests/unit/common/test_availability_zone.py b/openstackclient/tests/unit/common/test_availability_zone.py index 1071f23d2..1e5cbf9d3 100644 --- a/openstackclient/tests/unit/common/test_availability_zone.py +++ b/openstackclient/tests/unit/common/test_availability_zone.py @@ -87,8 +87,10 @@ def setUp(self): super().setUp() self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_client = self.app.client_manager.sdk_connection.compute - self.compute_client.availability_zones = mock.Mock() + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) + self.compute_sdk_client.availability_zones = mock.Mock() class TestAvailabilityZoneList(TestAvailabilityZone): @@ -109,7 +111,9 @@ class TestAvailabilityZoneList(TestAvailabilityZone): def setUp(self): super().setUp() - self.compute_client.availability_zones.return_value = self.compute_azs + self.compute_sdk_client.availability_zones.return_value = ( + self.compute_azs + ) self.volume_sdk_client.availability_zones.return_value = ( self.volume_azs ) @@ -128,7 +132,9 @@ def test_availability_zone_list_no_options(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_client.availability_zones.assert_called_with(details=True) + self.compute_sdk_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() @@ -156,7 +162,9 @@ def test_availability_zone_list_long(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_client.availability_zones.assert_called_with(details=True) + self.compute_sdk_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() @@ -190,7 +198,9 @@ def test_availability_zone_list_compute(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_client.availability_zones.assert_called_with(details=True) + self.compute_sdk_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() @@ -214,7 +224,7 @@ def test_availability_zone_list_volume(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_client.availability_zones.assert_not_called() + self.compute_sdk_client.availability_zones.assert_not_called() self.volume_sdk_client.availability_zones.assert_called_with() self.network_client.availability_zones.assert_not_called() @@ -238,7 +248,7 @@ def test_availability_zone_list_network(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.compute_client.availability_zones.assert_not_called() + self.compute_sdk_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_limits.py b/openstackclient/tests/unit/common/test_limits.py index 87f6c1921..ecbbe534a 100644 --- a/openstackclient/tests/unit/common/test_limits.py +++ b/openstackclient/tests/unit/common/test_limits.py @@ -27,10 +27,10 @@ class TestComputeLimits(compute_fakes.TestComputev2): def setUp(self): super().setUp() self.app.client_manager.volume_endpoint_enabled = False - self.compute = self.app.client_manager.compute + self.compute_client = self.app.client_manager.compute self.fake_limits = compute_fakes.FakeLimits() - self.compute.limits.get.return_value = self.fake_limits + self.compute_client.limits.get.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 647f0b0db..8a15fb4c4 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -48,11 +48,10 @@ def setUp(self): self.projects_mock.reset_mock() self.projects_mock.get.return_value = self.projects[0] - self.compute_quotas_mock = self.app.client_manager.compute.quotas + self.compute_client = self.app.client_manager.compute + self.compute_quotas_mock = self.compute_client.quotas self.compute_quotas_mock.reset_mock() - self.compute_quotas_class_mock = ( - self.app.client_manager.compute.quota_classes - ) + self.compute_quotas_class_mock = self.compute_client.quota_classes self.compute_quotas_class_mock.reset_mock() self.volume_quotas_mock = self.volume_client.quotas @@ -125,8 +124,7 @@ def setUp(self): compute_fakes.create_one_default_comp_quota(), compute_fakes.create_one_default_comp_quota(), ] - self.compute = self.app.client_manager.compute - self.compute.quotas.defaults = mock.Mock( + self.compute_client.quotas.defaults = mock.Mock( side_effect=self.compute_default_quotas, ) @@ -221,7 +219,7 @@ def test_quota_list_details_compute(self): detailed_quota ) - self.compute.quotas.get = mock.Mock(return_value=detailed_quota) + self.compute_client.quotas.get = mock.Mock(return_value=detailed_quota) arglist = [ '--detail', @@ -305,7 +303,7 @@ def test_quota_list_details_volume(self): def test_quota_list_compute(self): # Two projects with non-default quotas - self.compute.quotas.get = mock.Mock( + self.compute_client.quotas.get = mock.Mock( side_effect=self.compute_quotas, ) @@ -326,7 +324,7 @@ def test_quota_list_compute(self): def test_quota_list_compute_default(self): # One of the projects is at defaults - self.compute.quotas.get = mock.Mock( + self.compute_client.quotas.get = mock.Mock( side_effect=[ self.compute_quotas[0], compute_fakes.create_one_default_comp_quota(), @@ -350,7 +348,7 @@ def test_quota_list_compute_default(self): def test_quota_list_compute_no_project_not_found(self): # Make one of the projects disappear - self.compute.quotas.get = mock.Mock( + self.compute_client.quotas.get = mock.Mock( side_effect=[ self.compute_quotas[0], exceptions.NotFound("NotFound"), @@ -374,7 +372,7 @@ def test_quota_list_compute_no_project_not_found(self): def test_quota_list_compute_no_project_4xx(self): # Make one of the projects disappear - self.compute.quotas.get = mock.Mock( + self.compute_client.quotas.get = mock.Mock( side_effect=[ self.compute_quotas[0], exceptions.BadRequest("Bad request"), @@ -398,7 +396,7 @@ def test_quota_list_compute_no_project_4xx(self): def test_quota_list_compute_no_project_5xx(self): # Make one of the projects disappear - self.compute.quotas.get = mock.Mock( + self.compute_client.quotas.get = mock.Mock( side_effect=[ self.compute_quotas[0], exceptions.HTTPNotImplemented("Not implemented??"), @@ -421,7 +419,7 @@ def test_quota_list_compute_no_project_5xx(self): def test_quota_list_compute_by_project(self): # Two projects with non-default quotas - self.compute.quotas.get = mock.Mock( + self.compute_client.quotas.get = mock.Mock( side_effect=self.compute_quotas, ) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index b27471d98..563245863 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -161,8 +161,9 @@ def setUp(self): endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN, ) + self.compute_client = self.app.client_manager.compute - self.app.client_manager.compute.api = compute_v2.APIv2( + self.compute_client.api = compute_v2.APIv2( session=self.app.client_manager.session, endpoint=fakes.AUTH_URL, ) diff --git a/openstackclient/tests/unit/compute/v2/test_agent.py b/openstackclient/tests/unit/compute/v2/test_agent.py index 76172ae0e..dc3ba6730 100644 --- a/openstackclient/tests/unit/compute/v2/test_agent.py +++ b/openstackclient/tests/unit/compute/v2/test_agent.py @@ -51,7 +51,7 @@ class TestAgent(compute_fakes.TestComputev2): def setUp(self): super(TestAgent, self).setUp() - self.agents_mock = self.app.client_manager.compute.agents + self.agents_mock = self.compute_client.agents self.agents_mock.reset_mock() diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index dbed0058e..52f6759a8 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -60,23 +60,27 @@ def setUp(self): # Get a shortcut to the AggregateManager Mock self.app.client_manager.sdk_connection.compute = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.compute - self.sdk_client.aggregates = mock.Mock() - self.sdk_client.find_aggregate = mock.Mock() - self.sdk_client.create_aggregate = mock.Mock() - self.sdk_client.update_aggregate = mock.Mock() - self.sdk_client.update_aggregate = mock.Mock() - self.sdk_client.set_aggregate_metadata = mock.Mock() - self.sdk_client.add_host_to_aggregate = mock.Mock() - self.sdk_client.remove_host_from_aggregate = mock.Mock() + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) + self.compute_sdk_client.aggregates = mock.Mock() + self.compute_sdk_client.find_aggregate = mock.Mock() + self.compute_sdk_client.create_aggregate = mock.Mock() + self.compute_sdk_client.update_aggregate = mock.Mock() + self.compute_sdk_client.update_aggregate = mock.Mock() + self.compute_sdk_client.set_aggregate_metadata = mock.Mock() + self.compute_sdk_client.add_host_to_aggregate = mock.Mock() + self.compute_sdk_client.remove_host_from_aggregate = mock.Mock() class TestAggregateAddHost(TestAggregate): def setUp(self): super(TestAggregateAddHost, self).setUp() - self.sdk_client.find_aggregate.return_value = self.fake_ag - self.sdk_client.add_host_to_aggregate.return_value = self.fake_ag + 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.cmd = aggregate.AddAggregateHost(self.app, None) def test_aggregate_add_host(self): @@ -90,10 +94,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.sdk_client.find_aggregate.assert_called_once_with( + self.compute_sdk_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.sdk_client.add_host_to_aggregate.assert_called_once_with( + self.compute_sdk_client.add_host_to_aggregate.assert_called_once_with( self.fake_ag.id, parsed_args.host ) self.assertEqual(self.columns, columns) @@ -104,8 +108,10 @@ class TestAggregateCreate(TestAggregate): def setUp(self): super(TestAggregateCreate, self).setUp() - self.sdk_client.create_aggregate.return_value = self.fake_ag - self.sdk_client.set_aggregate_metadata.return_value = self.fake_ag + self.compute_sdk_client.create_aggregate.return_value = self.fake_ag + self.compute_sdk_client.set_aggregate_metadata.return_value = ( + self.fake_ag + ) self.cmd = aggregate.CreateAggregate(self.app, None) def test_aggregate_create(self): @@ -117,7 +123,7 @@ def test_aggregate_create(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_aggregate.assert_called_once_with( + self.compute_sdk_client.create_aggregate.assert_called_once_with( name=parsed_args.name ) self.assertEqual(self.columns, columns) @@ -136,7 +142,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.sdk_client.create_aggregate.assert_called_once_with( + self.compute_sdk_client.create_aggregate.assert_called_once_with( name=parsed_args.name, availability_zone=parsed_args.zone ) self.assertEqual(self.columns, columns) @@ -156,10 +162,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.sdk_client.create_aggregate.assert_called_once_with( + self.compute_sdk_client.create_aggregate.assert_called_once_with( name=parsed_args.name ) - self.sdk_client.set_aggregate_metadata.assert_called_once_with( + self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, parsed_args.properties ) self.assertEqual(self.columns, columns) @@ -172,7 +178,7 @@ class TestAggregateDelete(TestAggregate): def setUp(self): super(TestAggregateDelete, self).setUp() - self.sdk_client.find_aggregate = compute_fakes.get_aggregates( + self.compute_sdk_client.find_aggregate = compute_fakes.get_aggregates( self.fake_ags ) self.cmd = aggregate.DeleteAggregate(self.app, None) @@ -184,10 +190,10 @@ def test_aggregate_delete(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.sdk_client.find_aggregate.assert_called_once_with( + self.compute_sdk_client.find_aggregate.assert_called_once_with( self.fake_ags[0].id, ignore_missing=False ) - self.sdk_client.delete_aggregate.assert_called_once_with( + self.compute_sdk_client.delete_aggregate.assert_called_once_with( self.fake_ags[0].id, ignore_missing=False ) @@ -205,8 +211,8 @@ def test_delete_multiple_aggregates(self): calls = [] for a in self.fake_ags: calls.append(call(a.id, ignore_missing=False)) - self.sdk_client.find_aggregate.assert_has_calls(calls) - self.sdk_client.delete_aggregate.assert_has_calls(calls) + self.compute_sdk_client.find_aggregate.assert_has_calls(calls) + self.compute_sdk_client.delete_aggregate.assert_has_calls(calls) def test_delete_multiple_agggregates_with_exception(self): arglist = [ @@ -219,7 +225,7 @@ def test_delete_multiple_agggregates_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.sdk_client.find_aggregate.side_effect = [ + self.compute_sdk_client.find_aggregate.side_effect = [ self.fake_ags[0], sdk_exceptions.NotFoundException, ] @@ -232,8 +238,8 @@ def test_delete_multiple_agggregates_with_exception(self): calls = [] for a in arglist: calls.append(call(a, ignore_missing=False)) - self.sdk_client.find_aggregate.assert_has_calls(calls) - self.sdk_client.delete_aggregate.assert_called_with( + self.compute_sdk_client.find_aggregate.assert_has_calls(calls) + self.compute_sdk_client.delete_aggregate.assert_called_with( self.fake_ags[0].id, ignore_missing=False ) @@ -280,7 +286,7 @@ class TestAggregateList(TestAggregate): def setUp(self): super(TestAggregateList, self).setUp() - self.sdk_client.aggregates.return_value = [self.fake_ag] + self.compute_sdk_client.aggregates.return_value = [self.fake_ag] self.cmd = aggregate.ListAggregate(self.app, None) def test_aggregate_list(self): @@ -308,8 +314,10 @@ class TestAggregateRemoveHost(TestAggregate): def setUp(self): super(TestAggregateRemoveHost, self).setUp() - self.sdk_client.find_aggregate.return_value = self.fake_ag - self.sdk_client.remove_host_from_aggregate.return_value = self.fake_ag + self.compute_sdk_client.find_aggregate.return_value = self.fake_ag + self.compute_sdk_client.remove_host_from_aggregate.return_value = ( + self.fake_ag + ) self.cmd = aggregate.RemoveAggregateHost(self.app, None) def test_aggregate_remove_host(self): @@ -323,10 +331,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.sdk_client.find_aggregate.assert_called_once_with( + self.compute_sdk_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.sdk_client.remove_host_from_aggregate.assert_called_once_with( + self.compute_sdk_client.remove_host_from_aggregate.assert_called_once_with( self.fake_ag.id, parsed_args.host ) self.assertEqual(self.columns, columns) @@ -337,7 +345,7 @@ class TestAggregateSet(TestAggregate): def setUp(self): super(TestAggregateSet, self).setUp() - self.sdk_client.find_aggregate.return_value = self.fake_ag + self.compute_sdk_client.find_aggregate.return_value = self.fake_ag self.cmd = aggregate.SetAggregate(self.app, None) def test_aggregate_set_no_option(self): @@ -350,11 +358,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.sdk_client.find_aggregate.assert_called_once_with( + self.compute_sdk_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.assertNotCalled(self.sdk_client.update_aggregate) - self.assertNotCalled(self.sdk_client.set_aggregate_metadata) + self.assertNotCalled(self.compute_sdk_client.update_aggregate) + self.assertNotCalled(self.compute_sdk_client.set_aggregate_metadata) self.assertIsNone(result) def test_aggregate_set_with_name(self): @@ -370,13 +378,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.sdk_client.find_aggregate.assert_called_once_with( + self.compute_sdk_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.sdk_client.update_aggregate.assert_called_once_with( + self.compute_sdk_client.update_aggregate.assert_called_once_with( self.fake_ag.id, name=parsed_args.name ) - self.assertNotCalled(self.sdk_client.set_aggregate_metadata) + self.assertNotCalled(self.compute_sdk_client.set_aggregate_metadata) self.assertIsNone(result) def test_aggregate_set_with_zone(self): @@ -392,13 +400,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.sdk_client.find_aggregate.assert_called_once_with( + self.compute_sdk_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.sdk_client.update_aggregate.assert_called_once_with( + self.compute_sdk_client.update_aggregate.assert_called_once_with( self.fake_ag.id, availability_zone=parsed_args.zone ) - self.assertNotCalled(self.sdk_client.set_aggregate_metadata) + self.assertNotCalled(self.compute_sdk_client.set_aggregate_metadata) self.assertIsNone(result) def test_aggregate_set_with_property(self): @@ -416,11 +424,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.sdk_client.find_aggregate.assert_called_once_with( + self.compute_sdk_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.assertNotCalled(self.sdk_client.update_aggregate) - self.sdk_client.set_aggregate_metadata.assert_called_once_with( + self.assertNotCalled(self.compute_sdk_client.update_aggregate) + self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, parsed_args.properties ) self.assertIsNone(result) @@ -439,11 +447,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.sdk_client.find_aggregate.assert_called_once_with( + self.compute_sdk_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.assertNotCalled(self.sdk_client.update_aggregate) - self.sdk_client.set_aggregate_metadata.assert_called_once_with( + self.assertNotCalled(self.compute_sdk_client.update_aggregate) + self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'key1': None, 'key2': 'value2'} ) self.assertIsNone(result) @@ -459,11 +467,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.sdk_client.find_aggregate.assert_called_once_with( + self.compute_sdk_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.assertNotCalled(self.sdk_client.update_aggregate) - self.sdk_client.set_aggregate_metadata.assert_called_once_with( + self.assertNotCalled(self.compute_sdk_client.update_aggregate) + self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'key1': None} ) self.assertIsNone(result) @@ -482,13 +490,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.sdk_client.find_aggregate.assert_called_once_with( + self.compute_sdk_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.sdk_client.update_aggregate.assert_called_once_with( + self.compute_sdk_client.update_aggregate.assert_called_once_with( self.fake_ag.id, availability_zone=parsed_args.zone ) - self.sdk_client.set_aggregate_metadata.assert_called_once_with( + self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'key1': None} ) self.assertIsNone(result) @@ -524,7 +532,7 @@ class TestAggregateShow(TestAggregate): def setUp(self): super(TestAggregateShow, self).setUp() - self.sdk_client.find_aggregate.return_value = self.fake_ag + self.compute_sdk_client.find_aggregate.return_value = self.fake_ag self.cmd = aggregate.ShowAggregate(self.app, None) def test_aggregate_show(self): @@ -536,7 +544,7 @@ def test_aggregate_show(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.find_aggregate.assert_called_once_with( + self.compute_sdk_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) @@ -548,7 +556,7 @@ class TestAggregateUnset(TestAggregate): def setUp(self): super(TestAggregateUnset, self).setUp() - self.sdk_client.find_aggregate.return_value = self.fake_ag + self.compute_sdk_client.find_aggregate.return_value = self.fake_ag self.cmd = aggregate.UnsetAggregate(self.app, None) def test_aggregate_unset(self): @@ -564,7 +572,7 @@ def test_aggregate_unset(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.sdk_client.set_aggregate_metadata.assert_called_once_with( + self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'unset_key': None} ) self.assertIsNone(result) @@ -584,7 +592,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.sdk_client.set_aggregate_metadata.assert_called_once_with( + self.compute_sdk_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, {'unset_key1': None, 'unset_key2': None} ) self.assertIsNone(result) @@ -599,7 +607,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.sdk_client.set_aggregate_metadata) + self.assertNotCalled(self.compute_sdk_client.set_aggregate_metadata) self.assertIsNone(result) @@ -609,7 +617,7 @@ class TestAggregateCacheImage(TestAggregate): def setUp(self): super(TestAggregateCacheImage, self).setUp() - self.sdk_client.find_aggregate.return_value = self.fake_ag + self.compute_sdk_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 @@ -638,10 +646,10 @@ def test_aggregate_add_single_image(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.sdk_client.find_aggregate.assert_called_once_with( + self.compute_sdk_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.sdk_client.aggregate_precache_images.assert_called_once_with( + self.compute_sdk_client.aggregate_precache_images.assert_called_once_with( self.fake_ag.id, [self.images[0].id] ) @@ -658,9 +666,9 @@ def test_aggregate_add_multiple_images(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.sdk_client.find_aggregate.assert_called_once_with( + self.compute_sdk_client.find_aggregate.assert_called_once_with( parsed_args.aggregate, ignore_missing=False ) - self.sdk_client.aggregate_precache_images.assert_called_once_with( + self.compute_sdk_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 efc4f4d4e..07772fa44 100644 --- a/openstackclient/tests/unit/compute/v2/test_console.py +++ b/openstackclient/tests/unit/compute/v2/test_console.py @@ -26,9 +26,11 @@ def setUp(self): # SDK mock self.app.client_manager.sdk_connection.compute = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.compute - self.sdk_client.find_server = mock.Mock() - self.sdk_client.get_server_console_output = mock.Mock() + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) + self.compute_sdk_client.find_server = mock.Mock() + self.compute_sdk_client.get_server_console_output = mock.Mock() class TestConsoleLog(TestConsole): @@ -37,7 +39,7 @@ class TestConsoleLog(TestConsole): def setUp(self): super(TestConsoleLog, self).setUp() - self.sdk_client.find_server.return_value = self._server + self.compute_sdk_client.find_server.return_value = self._server self.cmd = console.ShowConsoleLog(self.app, None) @@ -58,13 +60,13 @@ def test_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) output = {'output': '1st line\n2nd line\n'} - self.sdk_client.get_server_console_output.return_value = output + self.compute_sdk_client.get_server_console_output.return_value = output self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( name_or_id='fake_server', ignore_missing=False ) - self.sdk_client.get_server_console_output.assert_called_with( + self.compute_sdk_client.get_server_console_output.assert_called_with( self._server.id, length=None ) stdout = self.app.stdout.content @@ -76,13 +78,13 @@ def test_show_lines(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) output = {'output': '1st line\n2nd line'} - self.sdk_client.get_server_console_output.return_value = output + self.compute_sdk_client.get_server_console_output.return_value = output self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( name_or_id='fake_server', ignore_missing=False ) - self.sdk_client.get_server_console_output.assert_called_with( + self.compute_sdk_client.get_server_console_output.assert_called_with( self._server.id, length=15 ) @@ -92,13 +94,13 @@ class TestConsoleUrlShow(TestConsole): def setUp(self): super(TestConsoleUrlShow, self).setUp() - self.sdk_client.find_server.return_value = self._server + self.compute_sdk_client.find_server.return_value = self._server fake_console_data = { 'url': 'http://localhost', 'protocol': 'fake_protocol', 'type': 'fake_type', } - self.sdk_client.create_console = mock.Mock( + self.compute_sdk_client.create_console = mock.Mock( return_value=fake_console_data ) @@ -125,7 +127,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.sdk_client.create_console.assert_called_once_with( + self.compute_sdk_client.create_console.assert_called_once_with( self._server.id, console_type='novnc' ) self.assertEqual(self.columns, columns) @@ -142,7 +144,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.sdk_client.create_console.assert_called_once_with( + self.compute_sdk_client.create_console.assert_called_once_with( self._server.id, console_type='novnc' ) self.assertEqual(self.columns, columns) @@ -159,7 +161,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.sdk_client.create_console.assert_called_once_with( + self.compute_sdk_client.create_console.assert_called_once_with( self._server.id, console_type='xvpvnc' ) self.assertEqual(self.columns, columns) @@ -176,7 +178,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.sdk_client.create_console.assert_called_once_with( + self.compute_sdk_client.create_console.assert_called_once_with( self._server.id, console_type='spice-html5' ) self.assertEqual(self.columns, columns) @@ -193,7 +195,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.sdk_client.create_console.assert_called_once_with( + self.compute_sdk_client.create_console.assert_called_once_with( self._server.id, console_type='rdp-html5' ) self.assertEqual(self.columns, columns) @@ -210,7 +212,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.sdk_client.create_console.assert_called_once_with( + self.compute_sdk_client.create_console.assert_called_once_with( self._server.id, console_type='serial' ) self.assertEqual(self.columns, columns) @@ -227,7 +229,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.sdk_client.create_console.assert_called_once_with( + self.compute_sdk_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 f7701e492..1f2854563 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -32,16 +32,22 @@ def setUp(self): # SDK mock self.app.client_manager.sdk_connection.compute = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.compute - self.sdk_client.flavors = mock.Mock() - self.sdk_client.find_flavor = mock.Mock() - self.sdk_client.delete_flavor = mock.Mock() - self.sdk_client.update_flavor = mock.Mock() - self.sdk_client.flavor_add_tenant_access = mock.Mock() - self.sdk_client.flavor_remove_tenant_access = mock.Mock() - self.sdk_client.create_flavor_extra_specs = mock.Mock() - self.sdk_client.update_flavor_extra_specs_property = mock.Mock() - self.sdk_client.delete_flavor_extra_specs_property = mock.Mock() + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) + self.compute_sdk_client.flavors = mock.Mock() + self.compute_sdk_client.find_flavor = mock.Mock() + self.compute_sdk_client.delete_flavor = mock.Mock() + self.compute_sdk_client.update_flavor = mock.Mock() + self.compute_sdk_client.flavor_add_tenant_access = mock.Mock() + self.compute_sdk_client.flavor_remove_tenant_access = mock.Mock() + self.compute_sdk_client.create_flavor_extra_specs = mock.Mock() + self.compute_sdk_client.update_flavor_extra_specs_property = ( + mock.Mock() + ) + self.compute_sdk_client.delete_flavor_extra_specs_property = ( + mock.Mock() + ) self.projects_mock = self.app.client_manager.identity.projects self.projects_mock.reset_mock() @@ -100,7 +106,7 @@ def setUp(self): # Return a project self.projects_mock.get.return_value = self.project - self.sdk_client.create_flavor.return_value = self.flavor + self.compute_sdk_client.create_flavor.return_value = self.flavor self.cmd = flavor.CreateFlavor(self.app, None) def test_flavor_create_default_options(self): @@ -123,7 +129,9 @@ def test_flavor_create_default_options(self): } columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_flavor.assert_called_once_with(**default_args) + self.compute_sdk_client.create_flavor.assert_called_once_with( + **default_args + ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) @@ -190,8 +198,8 @@ 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.sdk_client.create_flavor.return_value = create_flavor - self.sdk_client.create_flavor_extra_specs.return_value = ( + self.compute_sdk_client.create_flavor.return_value = create_flavor + self.compute_sdk_client.create_flavor_extra_specs.return_value = ( expected_flavor ) @@ -199,11 +207,13 @@ def test_flavor_create_all_options(self): sdk_utils, 'supports_microversion', return_value=True ): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_flavor.assert_called_once_with(**args) - self.sdk_client.create_flavor_extra_specs.assert_called_once_with( + self.compute_sdk_client.create_flavor.assert_called_once_with( + **args + ) + self.compute_sdk_client.create_flavor_extra_specs.assert_called_once_with( create_flavor, props ) - self.sdk_client.get_flavor_access.assert_not_called() + self.compute_sdk_client.get_flavor_access.assert_not_called() self.assertEqual(self.columns, columns) self.assertCountEqual(tuple(cmp_data), data) @@ -276,8 +286,8 @@ 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.sdk_client.create_flavor.return_value = create_flavor - self.sdk_client.create_flavor_extra_specs.return_value = ( + self.compute_sdk_client.create_flavor.return_value = create_flavor + self.compute_sdk_client.create_flavor_extra_specs.return_value = ( expected_flavor ) @@ -285,12 +295,12 @@ def test_flavor_create_other_options(self): sdk_utils, 'supports_microversion', return_value=True ): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_flavor.assert_called_once_with(**args) - self.sdk_client.flavor_add_tenant_access.assert_called_with( + self.compute_sdk_client.create_flavor.assert_called_once_with(**args) + self.compute_sdk_client.flavor_add_tenant_access.assert_called_with( self.flavor.id, self.project.id, ) - self.sdk_client.create_flavor_extra_specs.assert_called_with( + self.compute_sdk_client.create_flavor_extra_specs.assert_called_with( create_flavor, props ) self.assertEqual(self.columns, columns) @@ -375,7 +385,7 @@ def test_flavor_create_with_description_api_newer(self): 'description': 'fake description', } - self.sdk_client.create_flavor.assert_called_once_with(**args) + self.compute_sdk_client.create_flavor.assert_called_once_with(**args) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data_private, data) @@ -414,7 +424,7 @@ class TestFlavorDelete(TestFlavor): def setUp(self): super(TestFlavorDelete, self).setUp() - self.sdk_client.delete_flavor.return_value = None + self.compute_sdk_client.delete_flavor.return_value = None self.cmd = flavor.DeleteFlavor(self.app, None) @@ -425,14 +435,16 @@ def test_flavor_delete(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.sdk_client.find_flavor.return_value = self.flavors[0] + self.compute_sdk_client.find_flavor.return_value = self.flavors[0] result = self.cmd.take_action(parsed_args) - self.sdk_client.find_flavor.assert_called_with( + self.compute_sdk_client.find_flavor.assert_called_with( self.flavors[0].id, ignore_missing=False ) - self.sdk_client.delete_flavor.assert_called_with(self.flavors[0].id) + self.compute_sdk_client.delete_flavor.assert_called_with( + self.flavors[0].id + ) self.assertIsNone(result) def test_delete_multiple_flavors(self): @@ -445,7 +457,7 @@ def test_delete_multiple_flavors(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.sdk_client.find_flavor.side_effect = self.flavors + self.compute_sdk_client.find_flavor.side_effect = self.flavors result = self.cmd.take_action(parsed_args) @@ -453,8 +465,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.sdk_client.find_flavor.assert_has_calls(find_calls) - self.sdk_client.delete_flavor.assert_has_calls(delete_calls) + self.compute_sdk_client.find_flavor.assert_has_calls(find_calls) + self.compute_sdk_client.delete_flavor.assert_has_calls(delete_calls) self.assertIsNone(result) def test_multi_flavors_delete_with_exception(self): @@ -465,7 +477,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.sdk_client.find_flavor.side_effect = [ + self.compute_sdk_client.find_flavor.side_effect = [ self.flavors[0], sdk_exceptions.ResourceNotFound, ] @@ -481,8 +493,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.sdk_client.find_flavor.assert_has_calls(find_calls) - self.sdk_client.delete_flavor.assert_has_calls(delete_calls) + self.compute_sdk_client.find_flavor.assert_has_calls(find_calls) + self.compute_sdk_client.delete_flavor.assert_has_calls(delete_calls) class TestFlavorList(TestFlavor): @@ -528,7 +540,7 @@ def setUp(self): [], ] - self.sdk_client.flavors = self.api_mock + self.compute_sdk_client.flavors = self.api_mock # Get the command object to test self.cmd = flavor.ListFlavor(self.app, None) @@ -553,8 +565,8 @@ def test_flavor_list_no_options(self): 'is_public': True, } - self.sdk_client.flavors.assert_called_with(**kwargs) - self.sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_sdk_client.flavors.assert_called_with(**kwargs) + self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -579,8 +591,8 @@ def test_flavor_list_all_flavors(self): 'is_public': None, } - self.sdk_client.flavors.assert_called_with(**kwargs) - self.sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_sdk_client.flavors.assert_called_with(**kwargs) + self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -605,8 +617,8 @@ def test_flavor_list_private_flavors(self): 'is_public': False, } - self.sdk_client.flavors.assert_called_with(**kwargs) - self.sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_sdk_client.flavors.assert_called_with(**kwargs) + self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -631,8 +643,8 @@ def test_flavor_list_public_flavors(self): 'is_public': True, } - self.sdk_client.flavors.assert_called_with(**kwargs) - self.sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_sdk_client.flavors.assert_called_with(**kwargs) + self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -657,8 +669,8 @@ def test_flavor_list_long(self): 'is_public': True, } - self.sdk_client.flavors.assert_called_with(**kwargs) - self.sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_sdk_client.flavors.assert_called_with(**kwargs) + self.compute_sdk_client.fetch_flavor_extra_specs.assert_not_called() self.assertEqual(self.columns_long, columns) self.assertCountEqual(self.data_long, tuple(data)) @@ -690,8 +702,10 @@ def test_flavor_list_long_no_extra_specs(self): [], ] - self.sdk_client.flavors = self.api_mock - self.sdk_client.fetch_flavor_extra_specs = mock.Mock(return_value=None) + self.compute_sdk_client.flavors = self.api_mock + self.compute_sdk_client.fetch_flavor_extra_specs = mock.Mock( + return_value=None + ) arglist = [ '--long', @@ -712,8 +726,8 @@ def test_flavor_list_long_no_extra_specs(self): 'is_public': True, } - self.sdk_client.flavors.assert_called_with(**kwargs) - self.sdk_client.fetch_flavor_extra_specs.assert_called_once_with( + self.compute_sdk_client.flavors.assert_called_with(**kwargs) + self.compute_sdk_client.fetch_flavor_extra_specs.assert_called_once_with( flavor ) @@ -746,15 +760,15 @@ def test_flavor_list_min_disk_min_ram(self): 'min_ram': 2048, } - self.sdk_client.flavors.assert_called_with(**kwargs) - self.sdk_client.fetch_flavor_extra_specs.assert_not_called() + self.compute_sdk_client.flavors.assert_called_with(**kwargs) + self.compute_sdk_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.sdk_client.find_flavor(). + # Return value of self.compute_sdk_client.find_flavor(). flavor = compute_fakes.create_one_flavor( attrs={'os-flavor-access:is_public': False} ) @@ -763,7 +777,7 @@ class TestFlavorSet(TestFlavor): def setUp(self): super(TestFlavorSet, self).setUp() - self.sdk_client.find_flavor.return_value = self.flavor + self.compute_sdk_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) @@ -777,10 +791,10 @@ def test_flavor_set_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.sdk_client.find_flavor.assert_called_with( + self.compute_sdk_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.sdk_client.create_flavor_extra_specs.assert_called_with( + self.compute_sdk_client.create_flavor_extra_specs.assert_called_with( self.flavor.id, {'FOO': '"B A R"'} ) self.assertIsNone(result) @@ -791,10 +805,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.sdk_client.find_flavor.assert_called_with( + self.compute_sdk_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.sdk_client.delete_flavor_extra_specs_property.assert_called_with( + self.compute_sdk_client.delete_flavor_extra_specs_property.assert_called_with( self.flavor.id, 'property' ) self.assertIsNone(result) @@ -813,14 +827,14 @@ def test_flavor_set_project(self): result = self.cmd.take_action(parsed_args) - self.sdk_client.find_flavor.assert_called_with( + self.compute_sdk_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.sdk_client.flavor_add_tenant_access.assert_called_with( + self.compute_sdk_client.flavor_add_tenant_access.assert_called_with( self.flavor.id, self.project.id, ) - self.sdk_client.create_flavor_extra_specs.assert_not_called() + self.compute_sdk_client.create_flavor_extra_specs.assert_not_called() self.assertIsNone(result) def test_flavor_set_no_project(self): @@ -857,7 +871,7 @@ def test_flavor_set_no_flavor(self): ) def test_flavor_set_with_unexist_flavor(self): - self.sdk_client.find_flavor.side_effect = [ + self.compute_sdk_client.find_flavor.side_effect = [ sdk_exceptions.ResourceNotFound() ] @@ -886,10 +900,10 @@ def test_flavor_set_nothing(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.sdk_client.find_flavor.assert_called_with( + self.compute_sdk_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.sdk_client.flavor_add_tenant_access.assert_not_called() + self.compute_sdk_client.flavor_add_tenant_access.assert_not_called() self.assertIsNone(result) def test_flavor_set_description_api_newer(self): @@ -903,12 +917,12 @@ def test_flavor_set_description_api_newer(self): ('flavor', self.flavor.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.compute.api_version = 2.55 + with mock.patch.object( sdk_utils, 'supports_microversion', return_value=True ): result = self.cmd.take_action(parsed_args) - self.sdk_client.update_flavor.assert_called_with( + self.compute_sdk_client.update_flavor.assert_called_with( flavor=self.flavor.id, description='description' ) self.assertIsNone(result) @@ -924,7 +938,7 @@ def test_flavor_set_description_api_older(self): ('flavor', self.flavor.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.compute.api_version = 2.54 + with mock.patch.object( sdk_utils, 'supports_microversion', return_value=False ): @@ -943,13 +957,12 @@ def test_flavor_set_description_using_name_api_newer(self): ('flavor', self.flavor.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.compute.api_version = 2.55 with mock.patch.object( sdk_utils, 'supports_microversion', return_value=True ): result = self.cmd.take_action(parsed_args) - self.sdk_client.update_flavor.assert_called_with( + self.compute_sdk_client.update_flavor.assert_called_with( flavor=self.flavor.id, description='description' ) self.assertIsNone(result) @@ -965,7 +978,6 @@ def test_flavor_set_description_using_name_api_older(self): ('flavor', self.flavor.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.compute.api_version = 2.54 with mock.patch.object( sdk_utils, 'supports_microversion', return_value=False @@ -976,7 +988,7 @@ def test_flavor_set_description_using_name_api_older(self): class TestFlavorShow(TestFlavor): - # Return value of self.sdk_client.find_flavor(). + # Return value of self.compute_sdk_client.find_flavor(). flavor_access = compute_fakes.create_one_flavor_access() flavor = compute_fakes.create_one_flavor() @@ -1016,8 +1028,10 @@ def setUp(self): super(TestFlavorShow, self).setUp() # Return value of _find_resource() - self.sdk_client.find_flavor.return_value = self.flavor - self.sdk_client.get_flavor_access.return_value = [self.flavor_access] + self.compute_sdk_client.find_flavor.return_value = self.flavor + self.compute_sdk_client.get_flavor_access.return_value = [ + self.flavor_access + ] self.cmd = flavor.ShowFlavor(self.app, None) def test_show_no_options(self): @@ -1054,7 +1068,7 @@ def test_private_flavor_show(self): 'os-flavor-access:is_public': False, } ) - self.sdk_client.find_flavor.return_value = private_flavor + self.compute_sdk_client.find_flavor.return_value = private_flavor arglist = [ private_flavor.name, @@ -1083,7 +1097,7 @@ def test_private_flavor_show(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.get_flavor_access.assert_called_with( + self.compute_sdk_client.get_flavor_access.assert_called_with( flavor=private_flavor.id ) self.assertEqual(self.columns, columns) @@ -1091,7 +1105,7 @@ def test_private_flavor_show(self): class TestFlavorUnset(TestFlavor): - # Return value of self.sdk_client.find_flavor(). + # Return value of self.compute_sdk_client.find_flavor(). flavor = compute_fakes.create_one_flavor( attrs={'os-flavor-access:is_public': False} ) @@ -1100,12 +1114,14 @@ class TestFlavorUnset(TestFlavor): def setUp(self): super(TestFlavorUnset, self).setUp() - self.sdk_client.find_flavor.return_value = self.flavor + self.compute_sdk_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.sdk_client.delete_flavor_extra_specs_property + self.mock_shortcut = ( + self.compute_sdk_client.delete_flavor_extra_specs_property + ) def test_flavor_unset_property(self): arglist = ['--property', 'property', 'baremetal'] @@ -1116,11 +1132,11 @@ def test_flavor_unset_property(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.sdk_client.find_flavor.assert_called_with( + self.compute_sdk_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.sdk_client.flavor_remove_tenant_access.assert_not_called() + self.compute_sdk_client.flavor_remove_tenant_access.assert_not_called() self.assertIsNone(result) def test_flavor_unset_properties(self): @@ -1138,7 +1154,7 @@ def test_flavor_unset_properties(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.sdk_client.find_flavor.assert_called_with( + self.compute_sdk_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) calls = [ @@ -1153,7 +1169,7 @@ def test_flavor_unset_properties(self): AssertionError, self.mock_shortcut.assert_has_calls, calls ) - self.sdk_client.flavor_remove_tenant_access.assert_not_called() + self.compute_sdk_client.flavor_remove_tenant_access.assert_not_called() def test_flavor_unset_project(self): arglist = [ @@ -1170,14 +1186,14 @@ def test_flavor_unset_project(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.sdk_client.find_flavor.assert_called_with( + self.compute_sdk_client.find_flavor.assert_called_with( parsed_args.flavor, get_extra_specs=True, ignore_missing=False ) - self.sdk_client.flavor_remove_tenant_access.assert_called_with( + self.compute_sdk_client.flavor_remove_tenant_access.assert_called_with( self.flavor.id, self.project.id, ) - self.sdk_client.delete_flavor_extra_specs_proerty.assert_not_called() + self.compute_sdk_client.delete_flavor_extra_specs_proerty.assert_not_called() self.assertIsNone(result) def test_flavor_unset_no_project(self): @@ -1214,7 +1230,7 @@ def test_flavor_unset_no_flavor(self): ) def test_flavor_unset_with_unexist_flavor(self): - self.sdk_client.find_flavor.side_effect = [ + self.compute_sdk_client.find_flavor.side_effect = [ sdk_exceptions.ResourceNotFound ] @@ -1244,4 +1260,4 @@ def test_flavor_unset_nothing(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.sdk_client.flavor_remove_tenant_access.assert_not_called() + self.compute_sdk_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 64103940c..6fbce9afa 100644 --- a/openstackclient/tests/unit/compute/v2/test_host.py +++ b/openstackclient/tests/unit/compute/v2/test_host.py @@ -27,8 +27,10 @@ def setUp(self): # Get a shortcut to the compute client self.app.client_manager.sdk_connection.compute = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.compute - self.sdk_client.get = mock.Mock() + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) + self.compute_sdk_client.get = mock.Mock() @mock.patch('openstackclient.api.compute_v2.APIv2.host_list') @@ -38,7 +40,7 @@ class TestHostList(TestHost): def setUp(self): super(TestHostList, self).setUp() - self.sdk_client.get.return_value = fakes.FakeResponse( + self.compute_sdk_client.get.return_value = fakes.FakeResponse( data={'hosts': [self._host]} ) @@ -63,7 +65,9 @@ def test_host_list_no_option(self, h_mock): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.get.assert_called_with('/os-hosts', microversion='2.1') + self.compute_sdk_client.get.assert_called_with( + '/os-hosts', microversion='2.1' + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -81,7 +85,9 @@ def test_host_list_with_option(self, h_mock): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.get.assert_called_with('/os-hosts', microversion='2.1') + self.compute_sdk_client.get.assert_called_with( + '/os-hosts', microversion='2.1' + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -153,7 +159,7 @@ def setUp(self): } } - self.sdk_client.get.return_value = fakes.FakeResponse( + self.compute_sdk_client.get.return_value = fakes.FakeResponse( data={'host': [output_data]} ) @@ -204,7 +210,7 @@ def test_host_show_with_option(self, h_mock): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.get.assert_called_with( + self.compute_sdk_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 5148b0d1c..1651b04a1 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor.py @@ -30,8 +30,10 @@ def setUp(self): super().setUp() # Create and get a shortcut to the compute client mock - self.sdk_client = self.app.client_manager.sdk_connection.compute - self.sdk_client.reset_mock() + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) + self.compute_sdk_client.reset_mock() class TestHypervisorList(TestHypervisor): @@ -40,7 +42,7 @@ def setUp(self): # Fake hypervisors to be listed up self.hypervisors = compute_fakes.create_hypervisors() - self.sdk_client.hypervisors.return_value = self.hypervisors + self.compute_sdk_client.hypervisors.return_value = self.hypervisors self.columns = ( "ID", @@ -114,7 +116,7 @@ def test_hypervisor_list_no_option(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.hypervisors.assert_called_with(details=True) + self.compute_sdk_client.hypervisors.assert_called_with(details=True) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -129,7 +131,9 @@ def test_hypervisor_list_matching_option_found(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) # Fake the return value of search() - self.sdk_client.hypervisors.return_value = [self.hypervisors[0]] + self.compute_sdk_client.hypervisors.return_value = [ + self.hypervisors[0] + ] self.data = ( ( @@ -146,7 +150,7 @@ def test_hypervisor_list_matching_option_found(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.hypervisors.assert_called_with( + self.compute_sdk_client.hypervisors.assert_called_with( hypervisor_hostname_pattern=self.hypervisors[0].name, details=True ) self.assertEqual(self.columns, columns) @@ -163,7 +167,9 @@ def test_hypervisor_list_matching_option_not_found(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) # Fake exception raised from search() - self.sdk_client.hypervisors.side_effect = exceptions.NotFound(None) + self.compute_sdk_client.hypervisors.side_effect = exceptions.NotFound( + None + ) self.assertRaises( exceptions.NotFound, self.cmd.take_action, parsed_args @@ -211,7 +217,7 @@ def test_hypervisor_list_long_option(self, sm_mock): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.hypervisors.assert_called_with(details=True) + self.compute_sdk_client.hypervisors.assert_called_with(details=True) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, tuple(data)) @@ -228,7 +234,9 @@ def test_hypervisor_list_with_limit(self, sm_mock): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.sdk_client.hypervisors.assert_called_with(limit=1, details=True) + self.compute_sdk_client.hypervisors.assert_called_with( + limit=1, details=True + ) @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) def test_hypervisor_list_with_limit_pre_v233(self, sm_mock): @@ -262,7 +270,7 @@ def test_hypervisor_list_with_marker(self, sm_mock): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.sdk_client.hypervisors.assert_called_with( + self.compute_sdk_client.hypervisors.assert_called_with( marker='test_hyp', details=True ) @@ -303,10 +311,10 @@ def setUp(self): ) # Return value of compute_client.find_hypervisor - self.sdk_client.find_hypervisor.return_value = self.hypervisor + self.compute_sdk_client.find_hypervisor.return_value = self.hypervisor # Return value of compute_client.aggregates() - self.sdk_client.aggregates.return_value = [] + self.compute_sdk_client.aggregates.return_value = [] # Return value of compute_client.get_hypervisor_uptime() uptime_info = { @@ -316,7 +324,9 @@ def setUp(self): 'hypervisor_hostname': self.hypervisor.name, 'uptime': uptime_string, } - self.sdk_client.get_hypervisor_uptime.return_value = uptime_info + self.compute_sdk_client.get_hypervisor_uptime.return_value = ( + uptime_info + ) self.columns_v288 = ( 'aggregates', @@ -457,7 +467,7 @@ def test_hypervisor_show_pre_v228(self, sm_mock): # before microversion 2.28, nova returned a stringified version of this # field self.hypervisor.cpu_info = json.dumps(self.hypervisor.cpu_info) - self.sdk_client.find_hypervisor.return_value = self.hypervisor + self.compute_sdk_client.find_hypervisor.return_value = self.hypervisor arglist = [ self.hypervisor.name, @@ -487,7 +497,7 @@ def test_hypervisor_show_uptime_not_implemented(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.sdk_client.get_hypervisor_uptime.side_effect = ( + self.compute_sdk_client.get_hypervisor_uptime.side_effect = ( nova_exceptions.HTTPNotImplemented(501) ) diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py index 58d101c17..b35475cfc 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py @@ -25,8 +25,10 @@ def setUp(self): # Get a shortcut to the compute client hypervisors mock self.app.client_manager.sdk_connection.compute = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.compute - self.sdk_client.get = mock.Mock() + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) + self.compute_sdk_client.get = mock.Mock() # Not in fakes.py because hypervisor stats has been deprecated @@ -70,7 +72,7 @@ class TestHypervisorStatsShow(TestHypervisorStats): def setUp(self): super(TestHypervisorStatsShow, self).setUp() - self.sdk_client.get.return_value = fakes.FakeResponse( + self.compute_sdk_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 8ac055790..7fd56d4e8 100644 --- a/openstackclient/tests/unit/compute/v2/test_keypair.py +++ b/openstackclient/tests/unit/compute/v2/test_keypair.py @@ -11,14 +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. -# import copy from unittest import mock from unittest.mock import call import uuid -from novaclient import api_versions from openstack import utils as sdk_utils from osc_lib import exceptions @@ -43,11 +41,13 @@ def setUp(self): ) self.app.client_manager.sdk_connection.compute = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.compute - self.sdk_client.keypairs = mock.Mock() - self.sdk_client.create_keypair = mock.Mock() - self.sdk_client.delete_keypair = mock.Mock() - self.sdk_client.find_keypair = mock.Mock() + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) + self.compute_sdk_client.keypairs = mock.Mock() + self.compute_sdk_client.create_keypair = mock.Mock() + self.compute_sdk_client.delete_keypair = mock.Mock() + self.compute_sdk_client.find_keypair = mock.Mock() class TestKeypairCreate(TestKeypair): @@ -78,7 +78,7 @@ def setUp(self): # Get the command object to test self.cmd = keypair.CreateKeypair(self.app, None) - self.sdk_client.create_keypair.return_value = self.keypair + self.compute_sdk_client.create_keypair.return_value = self.keypair @mock.patch.object( keypair, @@ -96,7 +96,7 @@ def test_keypair_create_no_options(self, mock_generate): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_keypair.assert_called_with( + self.compute_sdk_client.create_keypair.assert_called_with( name=self.keypair.name, public_key=mock_generate.return_value.public_key, ) @@ -134,7 +134,7 @@ def test_keypair_create_public_key(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_keypair.assert_called_with( + self.compute_sdk_client.create_keypair.assert_called_with( name=self.keypair.name, public_key=self.keypair.public_key, ) @@ -167,7 +167,7 @@ def test_keypair_create_private_key(self, mock_generate): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_keypair.assert_called_with( + self.compute_sdk_client.create_keypair.assert_called_with( name=self.keypair.name, public_key=mock_generate.return_value.public_key, ) @@ -183,7 +183,7 @@ def test_keypair_create_private_key(self, mock_generate): @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) def test_keypair_create_with_key_type(self, sm_mock): for key_type in ['x509', 'ssh']: - self.sdk_client.create_keypair.return_value = self.keypair + self.compute_sdk_client.create_keypair.return_value = self.keypair self.data = ( self.keypair.created_at, @@ -214,7 +214,7 @@ def test_keypair_create_with_key_type(self, sm_mock): m_file.read.return_value = 'dummy' columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_keypair.assert_called_with( + self.compute_sdk_client.create_keypair.assert_called_with( name=self.keypair.name, public_key=self.keypair.public_key, key_type=key_type, @@ -273,7 +273,7 @@ def test_key_pair_create_with_user(self, sm_mock, mock_generate): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_keypair.assert_called_with( + self.compute_sdk_client.create_keypair.assert_called_with( name=self.keypair.name, user_id=identity_fakes.user_id, public_key=mock_generate.return_value.public_key, @@ -322,7 +322,7 @@ def test_keypair_delete(self): ret = self.cmd.take_action(parsed_args) self.assertIsNone(ret) - self.sdk_client.delete_keypair.assert_called_with( + self.compute_sdk_client.delete_keypair.assert_called_with( self.keypairs[0].name, ignore_missing=False ) @@ -340,7 +340,7 @@ def test_delete_multiple_keypairs(self): calls = [] for k in self.keypairs: calls.append(call(k.name, ignore_missing=False)) - self.sdk_client.delete_keypair.assert_has_calls(calls) + self.compute_sdk_client.delete_keypair.assert_has_calls(calls) self.assertIsNone(result) def test_delete_multiple_keypairs_with_exception(self): @@ -354,7 +354,7 @@ def test_delete_multiple_keypairs_with_exception(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.sdk_client.delete_keypair.side_effect = [ + self.compute_sdk_client.delete_keypair.side_effect = [ None, exceptions.CommandError, ] @@ -367,7 +367,7 @@ def test_delete_multiple_keypairs_with_exception(self): calls = [] for k in arglist: calls.append(call(k, ignore_missing=False)) - self.sdk_client.delete_keypair.assert_has_calls(calls) + self.compute_sdk_client.delete_keypair.assert_has_calls(calls) @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) def test_keypair_delete_with_user(self, sm_mock): @@ -381,7 +381,7 @@ def test_keypair_delete_with_user(self, sm_mock): ret = self.cmd.take_action(parsed_args) self.assertIsNone(ret) - self.sdk_client.delete_keypair.assert_called_with( + self.compute_sdk_client.delete_keypair.assert_called_with( self.keypairs[0].name, user_id=identity_fakes.user_id, ignore_missing=False, @@ -389,10 +389,6 @@ def test_keypair_delete_with_user(self, sm_mock): @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) def test_keypair_delete_with_user_pre_v210(self, sm_mock): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.9' - ) - arglist = ['--user', identity_fakes.user_name, self.keypairs[0].name] verifylist = [ ('user', identity_fakes.user_name), @@ -409,13 +405,13 @@ def test_keypair_delete_with_user_pre_v210(self, sm_mock): class TestKeypairList(TestKeypair): - # Return value of self.sdk_client.keypairs(). + # Return value of self.compute_sdk_client.keypairs(). keypairs = compute_fakes.create_keypairs(count=1) def setUp(self): super().setUp() - self.sdk_client.keypairs.return_value = self.keypairs + self.compute_sdk_client.keypairs.return_value = self.keypairs # Get the command object to test self.cmd = keypair.ListKeypair(self.app, None) @@ -434,7 +430,7 @@ def test_keypair_list_no_options(self, sm_mock): # Set expected values - self.sdk_client.keypairs.assert_called_with() + self.compute_sdk_client.keypairs.assert_called_with() self.assertEqual(('Name', 'Fingerprint'), columns) self.assertEqual( @@ -456,7 +452,7 @@ def test_keypair_list_v22(self, sm_mock): # Set expected values - self.sdk_client.keypairs.assert_called_with() + self.compute_sdk_client.keypairs.assert_called_with() self.assertEqual(('Name', 'Fingerprint', 'Type'), columns) self.assertEqual( @@ -492,7 +488,7 @@ def test_keypair_list_with_user(self, sm_mock): columns, data = self.cmd.take_action(parsed_args) users_mock.get.assert_called_with(identity_fakes.user_name) - self.sdk_client.keypairs.assert_called_with( + self.compute_sdk_client.keypairs.assert_called_with( user_id=identity_fakes.user_id, ) @@ -554,7 +550,7 @@ def test_keypair_list_with_project(self, sm_mock): projects_mock.get.assert_called_with(identity_fakes.project_name) users_mock.list.assert_called_with(tenant_id=identity_fakes.project_id) - self.sdk_client.keypairs.assert_called_with( + self.compute_sdk_client.keypairs.assert_called_with( user_id=identity_fakes.user_id, ) @@ -614,7 +610,7 @@ def test_keypair_list_with_limit(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.sdk_client.keypairs.assert_called_with(limit=1) + self.compute_sdk_client.keypairs.assert_called_with(limit=1) @mock.patch.object( sdk_utils, 'supports_microversion', new=mock.Mock(return_value=False) @@ -652,7 +648,7 @@ def test_keypair_list_with_marker(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.sdk_client.keypairs.assert_called_with(marker='test_kp') + self.compute_sdk_client.keypairs.assert_called_with(marker='test_kp') @mock.patch.object( sdk_utils, 'supports_microversion', new=mock.Mock(return_value=False) @@ -708,7 +704,7 @@ def test_keypair_show_no_options(self): def test_keypair_show(self): self.keypair = compute_fakes.create_one_keypair() - self.sdk_client.find_keypair.return_value = self.keypair + self.compute_sdk_client.find_keypair.return_value = self.keypair self.data = ( self.keypair.created_at, @@ -727,7 +723,7 @@ def test_keypair_show(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.find_keypair.assert_called_with( + self.compute_sdk_client.find_keypair.assert_called_with( self.keypair.name, ignore_missing=False ) @@ -736,7 +732,7 @@ def test_keypair_show(self): def test_keypair_show_public(self): self.keypair = compute_fakes.create_one_keypair() - self.sdk_client.find_keypair.return_value = self.keypair + self.compute_sdk_client.find_keypair.return_value = self.keypair arglist = ['--public-key', self.keypair.name] verifylist = [('public_key', True), ('name', self.keypair.name)] @@ -751,7 +747,7 @@ def test_keypair_show_public(self): @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) def test_keypair_show_with_user(self, sm_mock): self.keypair = compute_fakes.create_one_keypair() - self.sdk_client.find_keypair.return_value = self.keypair + self.compute_sdk_client.find_keypair.return_value = self.keypair self.data = ( self.keypair.created_at, @@ -778,7 +774,7 @@ def test_keypair_show_with_user(self, sm_mock): columns, data = self.cmd.take_action(parsed_args) self.users_mock.get.assert_called_with(identity_fakes.user_name) - self.sdk_client.find_keypair.assert_called_with( + self.compute_sdk_client.find_keypair.assert_called_with( self.keypair.name, ignore_missing=False, user_id=identity_fakes.user_id, diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index e0e3fd06e..ea29c1b78 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -67,28 +67,28 @@ def setUp(self): super(TestServer, self).setUp() # Get a shortcut to the compute client ServerManager Mock - self.servers_mock = self.app.client_manager.compute.servers + self.servers_mock = self.compute_client.servers self.servers_mock.reset_mock() self.app.client_manager.sdk_connection.compute = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.compute + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) # Get a shortcut to the compute client ServerMigrationsManager Mock - self.server_migrations_mock = ( - self.app.client_manager.compute.server_migrations - ) + self.server_migrations_mock = self.compute_client.server_migrations self.server_migrations_mock.reset_mock() # Get a shortcut to the compute client VolumeManager mock - self.servers_volumes_mock = self.app.client_manager.compute.volumes + self.servers_volumes_mock = self.compute_client.volumes self.servers_volumes_mock.reset_mock() # Get a shortcut to the compute client MigrationManager mock - self.migrations_mock = self.app.client_manager.compute.migrations + self.migrations_mock = self.compute_client.migrations self.migrations_mock.reset_mock() # Get a shortcut to the compute client FlavorManager Mock - self.flavors_mock = self.app.client_manager.compute.flavors + self.flavors_mock = self.compute_client.flavors self.flavors_mock.reset_mock() # Get a shortcut to the volume client VolumeManager Mock @@ -111,7 +111,7 @@ def setUp(self): self.addCleanup(patcher.stop) self.supports_microversion_mock = patcher.start() self._set_mock_microversion( - self.app.client_manager.compute.api_version.get_string() + self.compute_client.api_version.get_string() ) def _set_mock_microversion(self, mock_v): @@ -151,7 +151,7 @@ def setup_sdk_servers_mock(self, count): ) # This is the return value for compute_client.find_server() - self.sdk_client.find_server.side_effect = servers + self.compute_sdk_client.find_server.side_effect = servers return servers @@ -175,7 +175,7 @@ def run_method_with_sdk_servers(self, method_name, server_count): result = self.cmd.take_action(parsed_args) calls = [call(s.id) for s in servers] - method = getattr(self.sdk_client, method_name) + method = getattr(self.compute_sdk_client, method_name) method.assert_has_calls(calls) self.assertIsNone(result) @@ -233,7 +233,9 @@ def test_server_add_fixed_ip(self, sm_mock): servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() interface = compute_fakes.create_one_server_interface() - self.sdk_client.create_server_interface.return_value = interface + self.compute_sdk_client.create_server_interface.return_value = ( + interface + ) with mock.patch.object( self.app.client_manager, @@ -268,7 +270,7 @@ def test_server_add_fixed_ip(self, sm_mock): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, tuple(data)) - self.sdk_client.create_server_interface.assert_called_once_with( + self.compute_sdk_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'] ) @@ -279,7 +281,9 @@ def test_server_add_fixed_ip_with_fixed_ip(self, sm_mock): servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() interface = compute_fakes.create_one_server_interface() - self.sdk_client.create_server_interface.return_value = interface + self.compute_sdk_client.create_server_interface.return_value = ( + interface + ) with mock.patch.object( self.app.client_manager, @@ -320,7 +324,7 @@ def test_server_add_fixed_ip_with_fixed_ip(self, sm_mock): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, tuple(data)) - self.sdk_client.create_server_interface.assert_called_once_with( + self.compute_sdk_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'], fixed_ips=[{'ip_address': '5.6.7.8'}], @@ -333,7 +337,9 @@ def test_server_add_fixed_ip_with_tag(self, sm_mock): servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() interface = compute_fakes.create_one_server_interface() - self.sdk_client.create_server_interface.return_value = interface + self.compute_sdk_client.create_server_interface.return_value = ( + interface + ) with mock.patch.object( self.app.client_manager, @@ -379,7 +385,7 @@ def test_server_add_fixed_ip_with_tag(self, sm_mock): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, tuple(data)) - self.sdk_client.create_server_interface.assert_called_once_with( + self.compute_sdk_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'], fixed_ips=[{'ip_address': '5.6.7.8'}], @@ -393,7 +399,9 @@ def test_server_add_fixed_ip_with_fixed_ip_with_tag(self, sm_mock): servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() interface = compute_fakes.create_one_server_interface() - self.sdk_client.create_server_interface.return_value = interface + self.compute_sdk_client.create_server_interface.return_value = ( + interface + ) with mock.patch.object( self.app.client_manager, @@ -439,7 +447,7 @@ def test_server_add_fixed_ip_with_fixed_ip_with_tag(self, sm_mock): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, tuple(data)) - self.sdk_client.create_server_interface.assert_called_once_with( + self.compute_sdk_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'], fixed_ips=[{'ip_address': '5.6.7.8'}], @@ -745,7 +753,7 @@ def _test_server_add_port(self, port_id): result = self.cmd.take_action(parsed_args) - self.sdk_client.create_server_interface.assert_called_once_with( + self.compute_sdk_client.create_server_interface.assert_called_once_with( servers[0], port_id=port_id ) self.assertIsNone(result) @@ -763,10 +771,6 @@ def test_server_add_port_no_neutron(self): @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) def test_server_add_port_with_tag(self, sm_mock): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.49' - ) - servers = self.setup_sdk_servers_mock(count=1) self.find_port.return_value.id = 'fake-port' arglist = [ @@ -785,16 +789,12 @@ def test_server_add_port_with_tag(self, sm_mock): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.sdk_client.create_server_interface.assert_called_once_with( + self.compute_sdk_client.create_server_interface.assert_called_once_with( servers[0], port_id='fake-port', tag='tag1' ) @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) def test_server_add_port_with_tag_pre_v249(self, sm_mock): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.48' - ) - servers = self.setup_servers_mock(count=1) self.find_port.return_value.id = 'fake-port' arglist = [ @@ -837,7 +837,7 @@ def setUp(self): attrs=attrs ) - self.sdk_client.create_volume_attachment.return_value = ( + self.compute_sdk_client.create_volume_attachment.return_value = ( self.volume_attachment ) @@ -877,7 +877,7 @@ def test_server_add_volume(self, sm_mock): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) - self.sdk_client.create_volume_attachment.assert_called_once_with( + self.compute_sdk_client.create_volume_attachment.assert_called_once_with( self.servers[0], volumeId=self.volumes[0].id, device='/dev/sdb' ) @@ -920,7 +920,7 @@ def side_effect(compute_client, version): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) - self.sdk_client.create_volume_attachment.assert_called_once_with( + self.compute_sdk_client.create_volume_attachment.assert_called_once_with( self.servers[0], volumeId=self.volumes[0].id, device='/dev/sdb', @@ -991,7 +991,7 @@ def test_server_add_volume_with_enable_delete_on_termination( columns, data = self.cmd.take_action(parsed_args) self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) - self.sdk_client.create_volume_attachment.assert_called_once_with( + self.compute_sdk_client.create_volume_attachment.assert_called_once_with( self.servers[0], volumeId=self.volumes[0].id, device='/dev/sdb', @@ -1042,7 +1042,7 @@ def test_server_add_volume_with_disable_delete_on_termination( self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) - self.sdk_client.create_volume_attachment.assert_called_once_with( + self.compute_sdk_client.create_volume_attachment.assert_called_once_with( self.servers[0], volumeId=self.volumes[0].id, device='/dev/sdb', @@ -1169,7 +1169,7 @@ def test_server_remove_volume(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.sdk_client.delete_volume_attachment.assert_called_once_with( + self.compute_sdk_client.delete_volume_attachment.assert_called_once_with( self.volumes[0], self.servers[0], ignore_missing=False, @@ -1204,7 +1204,7 @@ def _test_server_add_network(self, net_id): result = self.cmd.take_action(parsed_args) - self.sdk_client.create_server_interface.assert_called_once_with( + self.compute_sdk_client.create_server_interface.assert_called_once_with( servers[0], net_id=net_id ) self.assertIsNone(result) @@ -1222,10 +1222,6 @@ def test_server_add_network_no_neutron(self): @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) def test_server_add_network_with_tag(self, sm_mock): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.49' - ) - servers = self.setup_sdk_servers_mock(count=1) self.find_network.return_value.id = 'fake-network' @@ -1245,16 +1241,12 @@ def test_server_add_network_with_tag(self, sm_mock): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.sdk_client.create_server_interface.assert_called_once_with( + self.compute_sdk_client.create_server_interface.assert_called_once_with( servers[0], net_id='fake-network', tag='tag1' ) @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) def test_server_add_network_with_tag_pre_v249(self, sm_mock): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.48' - ) - servers = self.setup_sdk_servers_mock(count=1) self.find_network.return_value.id = 'fake-network' @@ -1578,7 +1570,7 @@ def test_server_create_with_security_group_in_nova_network(self): return_value=False, ): with mock.patch.object( - self.app.client_manager.compute.api, + self.compute_client.api, 'security_group_find', return_value={'name': 'fake_sg'}, ) as mock_find: @@ -1784,9 +1776,7 @@ def test_server_create_with_network(self): self.assertEqual(self.datalist(), data) def test_server_create_with_network_tag(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.43' - ) + self.compute_client.api_version = api_versions.APIVersion('2.43') arglist = [ '--image', @@ -1862,9 +1852,7 @@ def test_server_create_with_network_tag(self): self.app.client_manager.network.find_network.assert_called_once() def test_server_create_with_network_tag_pre_v243(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.42' - ) + self.compute_client.api_version = api_versions.APIVersion('2.42') arglist = [ '--image', @@ -1900,9 +1888,7 @@ def test_server_create_with_network_tag_pre_v243(self): def _test_server_create_with_auto_network(self, arglist): # requires API microversion 2.37 or later - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.37' - ) + self.compute_client.api_version = api_versions.APIVersion('2.37') verifylist = [ ('image', 'image1'), @@ -1968,9 +1954,7 @@ def test_server_create_with_auto_network(self): def test_server_create_with_auto_network_pre_v237(self): # use an API microversion that's too old - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.36' - ) + self.compute_client.api_version = api_versions.APIVersion('2.36') arglist = [ '--image', @@ -2006,9 +1990,7 @@ def test_server_create_with_auto_network_pre_v237(self): def test_server_create_with_auto_network_default_v2_37(self): """Tests creating a server without specifying --nic using 2.37.""" # requires API microversion 2.37 or later - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.37' - ) + self.compute_client.api_version = api_versions.APIVersion('2.37') arglist = [ '--image', @@ -2054,9 +2036,7 @@ def test_server_create_with_auto_network_default_v2_37(self): def _test_server_create_with_none_network(self, arglist): # requires API microversion 2.37 or later - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.37' - ) + self.compute_client.api_version = api_versions.APIVersion('2.37') verifylist = [ ('image', 'image1'), @@ -2122,9 +2102,7 @@ def test_server_create_with_none_network(self): def test_server_create_with_none_network_pre_v237(self): # use an API microversion that's too old - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.36' - ) + self.compute_client.api_version = api_versions.APIVersion('2.36') arglist = [ '--image', @@ -2607,9 +2585,7 @@ def test_server_create_with_block_device(self): self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_full(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.67' - ) + self.compute_client.api_version = api_versions.APIVersion('2.67') block_device = ( f'uuid={self.volume.id},source_type=volume,' @@ -2710,9 +2686,7 @@ def test_server_create_with_block_device_full(self): self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_from_file(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.67' - ) + self.compute_client.api_version = api_versions.APIVersion('2.67') block_device = { 'uuid': self.volume.id, @@ -2866,9 +2840,7 @@ def test_server_create_with_block_device_invalid_shutdown(self): ) def test_server_create_with_block_device_tag_pre_v242(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.41' - ) + self.compute_client.api_version = api_versions.APIVersion('2.41') block_device = f'uuid={self.volume.name},tag=foo' arglist = [ @@ -2889,9 +2861,7 @@ def test_server_create_with_block_device_tag_pre_v242(self): ) def test_server_create_with_block_device_volume_type_pre_v267(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.66' - ) + self.compute_client.api_version = api_versions.APIVersion('2.66') block_device = f'uuid={self.volume.name},volume_type=foo' arglist = [ @@ -3828,7 +3798,7 @@ def test_server_create_invalid_hint(self): def test_server_create_with_description_api_newer(self): # Description is supported for nova api version 2.19 or above - self.app.client_manager.compute.api_version = 2.19 + self.compute_client.api_version = 2.19 arglist = [ '--image', @@ -3884,7 +3854,7 @@ def test_server_create_with_description_api_newer(self): def test_server_create_with_description_api_older(self): # Description is not supported for nova api version below 2.19 - self.app.client_manager.compute.api_version = 2.18 + self.compute_client.api_version = 2.18 arglist = [ '--image', @@ -3910,9 +3880,7 @@ def test_server_create_with_description_api_older(self): ) def test_server_create_with_tag(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.52' - ) + self.compute_client.api_version = api_versions.APIVersion('2.52') arglist = [ '--image', @@ -3965,9 +3933,7 @@ def test_server_create_with_tag(self): self.assertFalse(self.flavors_mock.called) def test_server_create_with_tag_pre_v252(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.51' - ) + self.compute_client.api_version = api_versions.APIVersion('2.51') arglist = [ '--image', @@ -3998,7 +3964,7 @@ def test_server_create_with_tag_pre_v252(self): def test_server_create_with_host_v274(self): # Explicit host is supported for nova api version 2.74 or above - self.app.client_manager.compute.api_version = 2.74 + self.compute_client.api_version = 2.74 arglist = [ '--image', @@ -4054,7 +4020,7 @@ def test_server_create_with_host_v274(self): def test_server_create_with_host_pre_v274(self): # Host is not supported for nova api version below 2.74 - self.app.client_manager.compute.api_version = 2.73 + self.compute_client.api_version = 2.73 arglist = [ '--image', @@ -4082,7 +4048,7 @@ def test_server_create_with_host_pre_v274(self): def test_server_create_with_hypervisor_hostname_v274(self): # Explicit hypervisor_hostname is supported for nova api version # 2.74 or above - self.app.client_manager.compute.api_version = 2.74 + self.compute_client.api_version = 2.74 arglist = [ '--image', @@ -4138,7 +4104,7 @@ def test_server_create_with_hypervisor_hostname_v274(self): def test_server_create_with_hypervisor_hostname_pre_v274(self): # Hypervisor_hostname is not supported for nova api version below 2.74 - self.app.client_manager.compute.api_version = 2.73 + self.compute_client.api_version = 2.73 arglist = [ '--image', @@ -4166,7 +4132,7 @@ def test_server_create_with_hypervisor_hostname_pre_v274(self): def test_server_create_with_host_and_hypervisor_hostname_v274(self): # Explicit host and hypervisor_hostname is supported for nova api # version 2.74 or above - self.app.client_manager.compute.api_version = 2.74 + self.compute_client.api_version = 2.74 arglist = [ '--image', @@ -4225,9 +4191,7 @@ def test_server_create_with_host_and_hypervisor_hostname_v274(self): self.assertFalse(self.flavors_mock.called) def test_server_create_with_hostname_v290(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.90' - ) + self.compute_client.api_version = api_versions.APIVersion('2.90') arglist = [ '--image', @@ -4276,9 +4240,7 @@ def test_server_create_with_hostname_v290(self): self.assertFalse(self.flavors_mock.called) def test_server_create_with_hostname_pre_v290(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.89' - ) + self.compute_client.api_version = api_versions.APIVersion('2.89') arglist = [ '--image', @@ -4303,9 +4265,7 @@ def test_server_create_with_hostname_pre_v290(self): ) def test_server_create_with_trusted_image_cert(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.63' - ) + self.compute_client.api_version = api_versions.APIVersion('2.63') arglist = [ '--image', @@ -4356,9 +4316,7 @@ def test_server_create_with_trusted_image_cert(self): self.assertFalse(self.flavors_mock.called) def test_server_create_with_trusted_image_cert_prev263(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.62' - ) + self.compute_client.api_version = api_versions.APIVersion('2.62') arglist = [ '--image', @@ -4385,9 +4343,7 @@ def test_server_create_with_trusted_image_cert_prev263(self): ) def test_server_create_with_trusted_image_cert_from_volume(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.63' - ) + self.compute_client.api_version = api_versions.APIVersion('2.63') arglist = [ '--volume', 'volume1', @@ -4413,9 +4369,7 @@ def test_server_create_with_trusted_image_cert_from_volume(self): ) def test_server_create_with_trusted_image_cert_from_snapshot(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.63' - ) + self.compute_client.api_version = api_versions.APIVersion('2.63') arglist = [ '--snapshot', 'snapshot1', @@ -4441,9 +4395,7 @@ def test_server_create_with_trusted_image_cert_from_snapshot(self): ) def test_server_create_with_trusted_image_cert_boot_from_volume(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.63' - ) + self.compute_client.api_version = api_versions.APIVersion('2.63') arglist = [ '--image', 'image1', @@ -4629,7 +4581,9 @@ def run_test_server_dump(self, server_count): self.assertIsNone(result) for s in servers: - s.trigger_crash_dump.assert_called_once_with(self.sdk_client) + s.trigger_crash_dump.assert_called_once_with( + self.compute_sdk_client + ) def test_server_dump_one_server(self): self.run_test_server_dump(1) @@ -4704,12 +4658,12 @@ def setUp(self): self.image_client.get_image.return_value = self.image self.flavor = compute_fakes.create_one_flavor() - self.sdk_client.find_flavor.return_value = self.flavor + self.compute_sdk_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.sdk_client.servers.return_value = self.servers + self.compute_sdk_client.servers.return_value = self.servers # Get the command object to test self.cmd = server.ListServer(self.app, None) @@ -4728,7 +4682,7 @@ def setUp(self): ] Flavor = collections.namedtuple('Flavor', 'id name') - self.sdk_client.flavors.return_value = [ + self.compute_sdk_client.flavors.return_value = [ Flavor(id=s.flavor['id'], name=self.flavor.name) for s in self.servers ] @@ -4758,9 +4712,9 @@ def test_server_list_no_option(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_called() - self.sdk_client.flavors.assert_called() + self.compute_sdk_client.flavors.assert_called() # we did not pass image or flavor, so gets on those must be absent self.assertFalse(self.flavors_mock.get.call_count) self.assertFalse(self.image_client.get_image.call_count) @@ -4775,14 +4729,14 @@ def test_server_list_no_servers(self): ('deleted', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.sdk_client.servers.return_value = [] + self.compute_sdk_client.servers.return_value = [] self.data = () columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.sdk_client.flavors.assert_not_called() + self.compute_sdk_client.flavors.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4816,12 +4770,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.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_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.sdk_client.flavors.assert_called_once_with(is_public=None) + self.compute_sdk_client.flavors.assert_called_once_with(is_public=None) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data, tuple(data)) @@ -4858,7 +4812,7 @@ def test_server_list_column_option(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertIn('Project ID', columns) self.assertIn('User ID', columns) self.assertIn('Created At', columns) @@ -4897,9 +4851,9 @@ def test_server_list_no_name_lookup_option(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.sdk_client.flavors.assert_not_called() + self.compute_sdk_client.flavors.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4928,9 +4882,9 @@ def test_server_list_n_option(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.sdk_client.flavors.assert_not_called() + self.compute_sdk_client.flavors.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4945,11 +4899,11 @@ def test_server_list_name_lookup_one_by_one(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.sdk_client.flavors.assert_not_called() + self.compute_sdk_client.flavors.assert_not_called() self.image_client.get_image.assert_called() - self.sdk_client.find_flavor.assert_called() + self.compute_sdk_client.find_flavor.assert_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4966,9 +4920,9 @@ def test_server_list_with_image(self): ) self.kwargs['image'] = self.image.id - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_not_called() - self.sdk_client.flavors.assert_called_once() + self.compute_sdk_client.flavors.assert_called_once() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -4980,14 +4934,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.sdk_client.find_flavor.assert_has_calls( + self.compute_sdk_client.find_flavor.assert_has_calls( [mock.call(self.flavor.id)] ) self.kwargs['flavor'] = self.flavor.id - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_called_once() - self.sdk_client.flavors.assert_not_called() + self.compute_sdk_client.flavors.assert_not_called() self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -5004,7 +4958,7 @@ def test_server_list_with_changes_since(self): self.kwargs['changes-since'] = '2016-03-04T06:27:59Z' self.kwargs['deleted'] = True - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -5047,7 +5001,7 @@ def test_server_list_with_tag(self): self.kwargs['tags'] = 'tag1,tag2' - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -5090,7 +5044,7 @@ def test_server_list_with_not_tag(self): self.kwargs['not-tags'] = 'tag1,tag2' - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -5129,7 +5083,7 @@ def test_server_list_with_availability_zone(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['availability_zone'] = 'test-az' - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5146,7 +5100,7 @@ def test_server_list_with_key_name(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['key_name'] = 'test-key' - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5162,7 +5116,7 @@ def test_server_list_with_config_drive(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['config_drive'] = True - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5178,7 +5132,7 @@ def test_server_list_with_no_config_drive(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['config_drive'] = False - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5195,7 +5149,7 @@ def test_server_list_with_progress(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['progress'] = '100' - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5226,7 +5180,7 @@ def test_server_list_with_vm_state(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['vm_state'] = 'active' - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5243,7 +5197,7 @@ def test_server_list_with_task_state(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['task_state'] = 'deleting' - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5260,7 +5214,7 @@ def test_server_list_with_power_state(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['power_state'] = 1 - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -5296,18 +5250,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.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_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.sdk_client.servers.reset_mock() + self.compute_sdk_client.servers.reset_mock() self.attrs['host_status'] = 'UP' servers = self.setup_sdk_servers_mock(3) - self.sdk_client.servers.return_value = servers + self.compute_sdk_client.servers.return_value = servers # Make sure the returned image and flavor IDs match the servers. Image = collections.namedtuple('Image', 'id name') @@ -5343,7 +5297,7 @@ def test_server_list_long_with_host_status_v216(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertEqual(columns_long, columns) self.assertEqual(tuple(self.data2), tuple(data)) @@ -5391,7 +5345,7 @@ def setUp(self): # The servers to be listed. self.servers = self.setup_sdk_servers_mock(3) - self.sdk_client.servers.return_value = self.servers + self.compute_sdk_client.servers.return_value = self.servers Image = collections.namedtuple('Image', 'id name') self.image_client.images.return_value = [ @@ -5403,7 +5357,7 @@ def setUp(self): # The flavor information is embedded, so now reason for this to be # called - self.sdk_client.flavors = mock.NonCallableMock() + self.compute_sdk_client.flavors = mock.NonCallableMock() self.data = tuple( ( @@ -5439,7 +5393,7 @@ def test_server_list_with_locked(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['locked'] = True - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, tuple(data)) @@ -5454,7 +5408,7 @@ def test_server_list_with_unlocked_v273(self): columns, data = self.cmd.take_action(parsed_args) self.kwargs['locked'] = False - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, tuple(data)) @@ -5487,7 +5441,7 @@ def test_server_list_with_changes_before(self): self.kwargs['changes-before'] = '2016-03-05T06:27:59Z' self.kwargs['deleted'] = True - self.sdk_client.servers.assert_called_with(**self.kwargs) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, tuple(data)) @@ -5577,8 +5531,8 @@ def setUp(self): self.server = compute_fakes.create_one_sdk_server() - self.sdk_client.find_server.return_value = self.server - self.sdk_client.lock_server.return_value = None + self.compute_sdk_client.find_server.return_value = self.server + self.compute_sdk_client.lock_server.return_value = None # Get the command object to test self.cmd = server.LockServer(self.app, None) @@ -5607,11 +5561,11 @@ def test_server_lock_with_reason(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.server.id, ignore_missing=False, ) - self.sdk_client.lock_server.assert_called_with( + self.compute_sdk_client.lock_server.assert_called_with( self.server.id, locked_reason="blah", ) @@ -5632,12 +5586,12 @@ def test_server_lock_with_reason_multi_servers(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.assertEqual(2, self.sdk_client.find_server.call_count) - self.sdk_client.lock_server.assert_called_with( + 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.assertEqual(2, self.sdk_client.lock_server.call_count) + self.assertEqual(2, self.compute_sdk_client.lock_server.call_count) @mock.patch.object(sdk_utils, 'supports_microversion') def test_server_lock_with_reason_pre_v273(self, sm_mock): @@ -5719,9 +5673,7 @@ def test_server_migrate_with_host_2_56(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.56' - ) + self.compute_client.api_version = api_versions.APIVersion('2.56') result = self.cmd.take_action(parsed_args) @@ -5845,9 +5797,7 @@ def test_server_live_migrate_with_host(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.30' - ) + self.compute_client.api_version = api_versions.APIVersion('2.30') result = self.cmd.take_action(parsed_args) @@ -5907,9 +5857,7 @@ def test_server_block_live_migrate(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.24' - ) + self.compute_client.api_version = api_versions.APIVersion('2.24') result = self.cmd.take_action(parsed_args) @@ -5934,9 +5882,7 @@ def test_server_live_migrate_with_disk_overcommit(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.24' - ) + self.compute_client.api_version = api_versions.APIVersion('2.24') result = self.cmd.take_action(parsed_args) @@ -5961,9 +5907,7 @@ def test_server_live_migrate_with_disk_overcommit_post_v224(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.25' - ) + self.compute_client.api_version = api_versions.APIVersion('2.25') with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) @@ -6028,7 +5972,7 @@ class TestServerReboot(TestServer): def setUp(self): super().setUp() - self.sdk_client.reboot_server.return_value = None + self.compute_sdk_client.reboot_server.return_value = None self.cmd = server.RebootServer(self.app, None) @@ -6047,7 +5991,7 @@ def test_server_reboot(self): result = self.cmd.take_action(parsed_args) - self.sdk_client.reboot_server.assert_called_once_with( + self.compute_sdk_client.reboot_server.assert_called_once_with( servers[0].id, 'SOFT', ) @@ -6069,7 +6013,7 @@ def test_server_reboot_with_hard(self): result = self.cmd.take_action(parsed_args) - self.sdk_client.reboot_server.assert_called_once_with( + self.compute_sdk_client.reboot_server.assert_called_once_with( servers[0].id, 'HARD', ) @@ -6093,12 +6037,12 @@ def test_server_reboot_with_wait(self, mock_wait_for_status): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.sdk_client.reboot_server.assert_called_once_with( + self.compute_sdk_client.reboot_server.assert_called_once_with( servers[0].id, 'SOFT', ) mock_wait_for_status.assert_called_once_with( - self.sdk_client.get_server, + self.compute_sdk_client.get_server, servers[0].id, callback=mock.ANY, ) @@ -6126,12 +6070,12 @@ def test_server_reboot_with_wait_fails( self.assertRaises(SystemExit, self.cmd.take_action, parsed_args) self.assertIn('Error rebooting server', mock_log.call_args[0][0]) - self.sdk_client.reboot_server.assert_called_once_with( + self.compute_sdk_client.reboot_server.assert_called_once_with( servers[0].id, 'SOFT', ) mock_wait_for_status.assert_called_once_with( - self.sdk_client.get_server, + self.compute_sdk_client.get_server, servers[0].id, callback=mock.ANY, ) @@ -6316,9 +6260,7 @@ def test_rebuild_with_password(self): self.server.rebuild.assert_called_with(self.image, password) def test_rebuild_with_description(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.19' - ) + self.compute_client.api_version = api_versions.APIVersion('2.19') description = 'description1' arglist = [self.server.id, '--description', description] @@ -6334,9 +6276,7 @@ def test_rebuild_with_description(self): ) def test_rebuild_with_description_pre_v219(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.18' - ) + self.compute_client.api_version = api_versions.APIVersion('2.18') description = 'description1' arglist = [self.server.id, '--description', description] @@ -6504,9 +6444,7 @@ def test_rebuild_with_property(self): ) def test_rebuild_with_keypair_name(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.54' - ) + self.compute_client.api_version = api_versions.APIVersion('2.54') self.server.key_name = 'mykey' arglist = [ @@ -6529,9 +6467,7 @@ def test_rebuild_with_keypair_name(self): ) def test_rebuild_with_keypair_name_pre_v254(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.53' - ) + self.compute_client.api_version = api_versions.APIVersion('2.53') self.server.key_name = 'mykey' arglist = [ @@ -6550,9 +6486,7 @@ def test_rebuild_with_keypair_name_pre_v254(self): ) def test_rebuild_with_no_keypair_name(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.54' - ) + self.compute_client.api_version = api_versions.APIVersion('2.54') self.server.key_name = 'mykey' arglist = [ @@ -6591,9 +6525,7 @@ def test_rebuild_with_keypair_name_and_unset(self): @mock.patch('openstackclient.compute.v2.server.io.open') def test_rebuild_with_user_data(self, mock_open): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.57' - ) + self.compute_client.api_version = api_versions.APIVersion('2.57') mock_file = mock.Mock(name='File') mock_open.return_value = mock_file @@ -6627,9 +6559,7 @@ def test_rebuild_with_user_data(self, mock_open): ) def test_rebuild_with_user_data_pre_v257(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.56' - ) + self.compute_client.api_version = api_versions.APIVersion('2.56') arglist = [ self.server.id, @@ -6647,9 +6577,7 @@ def test_rebuild_with_user_data_pre_v257(self): ) def test_rebuild_with_no_user_data(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.54' - ) + self.compute_client.api_version = api_versions.APIVersion('2.54') self.server.key_name = 'mykey' arglist = [ @@ -6668,9 +6596,7 @@ def test_rebuild_with_no_user_data(self): self.server.rebuild.assert_called_with(self.image, None, userdata=None) def test_rebuild_with_no_user_data_pre_v254(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.53' - ) + self.compute_client.api_version = api_versions.APIVersion('2.53') arglist = [ self.server.id, @@ -6698,9 +6624,7 @@ def test_rebuild_with_user_data_and_unset(self): ) def test_rebuild_with_trusted_image_cert(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.63' - ) + self.compute_client.api_version = api_versions.APIVersion('2.63') arglist = [ self.server.id, @@ -6724,9 +6648,7 @@ def test_rebuild_with_trusted_image_cert(self): ) def test_rebuild_with_trusted_image_cert_pre_v263(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.62' - ) + self.compute_client.api_version = api_versions.APIVersion('2.62') arglist = [ self.server.id, @@ -6746,9 +6668,7 @@ def test_rebuild_with_trusted_image_cert_pre_v263(self): ) def test_rebuild_with_no_trusted_image_cert(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.63' - ) + self.compute_client.api_version = api_versions.APIVersion('2.63') arglist = [ self.server.id, @@ -6768,9 +6688,7 @@ def test_rebuild_with_no_trusted_image_cert(self): ) def test_rebuild_with_no_trusted_image_cert_pre_v263(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.62' - ) + self.compute_client.api_version = api_versions.APIVersion('2.62') arglist = [ self.server.id, @@ -6787,9 +6705,7 @@ def test_rebuild_with_no_trusted_image_cert_pre_v263(self): ) def test_rebuild_with_hostname(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.90' - ) + self.compute_client.api_version = api_versions.APIVersion('2.90') arglist = [self.server.id, '--hostname', 'new-hostname'] verifylist = [('server', self.server.id), ('hostname', 'new-hostname')] @@ -6804,9 +6720,7 @@ def test_rebuild_with_hostname(self): ) def test_rebuild_with_hostname_pre_v290(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.89' - ) + self.compute_client.api_version = api_versions.APIVersion('2.89') arglist = [ self.server.id, @@ -6851,9 +6765,7 @@ def setUp(self): self.cmd = server.RebuildServer(self.app, None) def test_rebuild_with_reimage_boot_volume(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.93' - ) + self.compute_client.api_version = api_versions.APIVersion('2.93') arglist = [ self.server.id, @@ -6874,9 +6786,7 @@ def test_rebuild_with_reimage_boot_volume(self): self.server.rebuild.assert_called_with(self.new_image, None) def test_rebuild_with_no_reimage_boot_volume(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.93' - ) + self.compute_client.api_version = api_versions.APIVersion('2.93') arglist = [ self.server.id, @@ -6897,9 +6807,7 @@ def test_rebuild_with_no_reimage_boot_volume(self): self.assertIn('--reimage-boot-volume is required', str(exc)) def test_rebuild_with_reimage_boot_volume_pre_v293(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.92' - ) + self.compute_client.api_version = api_versions.APIVersion('2.92') arglist = [ self.server.id, @@ -6992,9 +6900,7 @@ def test_evacuate_with_password(self): self._test_evacuate(args, verify_args, evac_args) def test_evacuate_with_host(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.29' - ) + self.compute_client.api_version = api_versions.APIVersion('2.29') host = 'target-host' args = [ @@ -7011,9 +6917,7 @@ def test_evacuate_with_host(self): self._test_evacuate(args, verify_args, evac_args) def test_evacuate_with_host_pre_v229(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.28' - ) + self.compute_client.api_version = api_versions.APIVersion('2.28') args = [ self.server.id, @@ -7031,9 +6935,7 @@ def test_evacuate_with_host_pre_v229(self): ) def test_evacuate_without_share_storage(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.13' - ) + self.compute_client.api_version = api_versions.APIVersion('2.13') args = [self.server.id, '--shared-storage'] verify_args = [ @@ -7048,9 +6950,7 @@ def test_evacuate_without_share_storage(self): self._test_evacuate(args, verify_args, evac_args) def test_evacuate_without_share_storage_post_v213(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.14' - ) + self.compute_client.api_version = api_versions.APIVersion('2.14') args = [self.server.id, '--shared-storage'] verify_args = [ @@ -7303,7 +7203,7 @@ def _test_server_remove_port(self, port_id): result = self.cmd.take_action(parsed_args) - self.sdk_client.delete_server_interface.assert_called_with( + self.compute_sdk_client.delete_server_interface.assert_called_with( port_id, server=servers[0], ignore_missing=False ) self.assertIsNone(result) @@ -7336,7 +7236,9 @@ def setUp(self): self.find_network = mock.Mock() self.app.client_manager.network.find_network = self.find_network - self.sdk_client.server_interfaces.return_value = [self.fake_inf] + self.compute_sdk_client.server_interfaces.return_value = [ + self.fake_inf + ] def _test_server_remove_network(self, network_id): self.fake_inf.net_id = network_id @@ -7356,8 +7258,10 @@ def _test_server_remove_network(self, network_id): result = self.cmd.take_action(parsed_args) - self.sdk_client.server_interfaces.assert_called_once_with(servers[0]) - self.sdk_client.delete_server_interface.assert_called_once_with( + self.compute_sdk_client.server_interfaces.assert_called_once_with( + servers[0] + ) + self.compute_sdk_client.delete_server_interface.assert_called_once_with( 'fake-port', server=servers[0] ) self.assertIsNone(result) @@ -8133,8 +8037,8 @@ def setUp(self): attrs={'status': 'ACTIVE'}, ) - self.sdk_client.find_server.return_value = self.server - self.sdk_client.shelve_server.return_value = None + self.compute_sdk_client.find_server.return_value = self.server + self.compute_sdk_client.shelve_server.return_value = None # Get the command object to test self.cmd = server.ShelveServer(self.app, None) @@ -8151,12 +8055,14 @@ def test_shelve(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.server.name, ignore_missing=False, ) - self.sdk_client.shelve_server.assert_called_with(self.server.id) - self.sdk_client.shelve_offload_server.assert_not_called() + self.compute_sdk_client.shelve_server.assert_called_with( + self.server.id + ) + self.compute_sdk_client.shelve_offload_server.assert_not_called() def test_shelve_already_shelved(self): self.server.status = 'SHELVED' @@ -8172,12 +8078,12 @@ def test_shelve_already_shelved(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.server.name, ignore_missing=False, ) - self.sdk_client.shelve_server.assert_not_called() - self.sdk_client.shelve_offload_server.assert_not_called() + self.compute_sdk_client.shelve_server.assert_not_called() + self.compute_sdk_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): @@ -8192,14 +8098,16 @@ def test_shelve_with_wait(self, mock_wait_for_status): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.server.name, ignore_missing=False, ) - self.sdk_client.shelve_server.assert_called_with(self.server.id) - self.sdk_client.shelve_offload_server.assert_not_called() + self.compute_sdk_client.shelve_server.assert_called_with( + self.server.id + ) + self.compute_sdk_client.shelve_offload_server.assert_not_called() mock_wait_for_status.assert_called_once_with( - self.sdk_client.get_server, + self.compute_sdk_client.get_server, self.server.id, callback=mock.ANY, success_status=('shelved', 'shelved_offloaded'), @@ -8220,18 +8128,20 @@ def test_shelve_offload(self, mock_wait_for_status): # two calls - one to retrieve the server state before shelving and # another to do this before offloading - self.sdk_client.find_server.assert_has_calls( + self.compute_sdk_client.find_server.assert_has_calls( [ mock.call(self.server.name, ignore_missing=False), mock.call(self.server.name, ignore_missing=False), ] ) - self.sdk_client.shelve_server.assert_called_with(self.server.id) - self.sdk_client.shelve_offload_server.assert_called_once_with( + self.compute_sdk_client.shelve_server.assert_called_with( + self.server.id + ) + self.compute_sdk_client.shelve_offload_server.assert_called_once_with( self.server.id, ) mock_wait_for_status.assert_called_once_with( - self.sdk_client.get_server, + self.compute_sdk_client.get_server, self.server.id, callback=mock.ANY, success_status=('shelved', 'shelved_offloaded'), @@ -8254,7 +8164,9 @@ def setUp(self): 'tenant_id': 'tenant-id-xxx', 'networks': {'public': ['10.20.30.40', '2001:db8::f']}, } - self.sdk_client.get_server_diagnostics.return_value = {'test': 'test'} + self.compute_sdk_client.get_server_diagnostics.return_value = { + 'test': 'test' + } server_method = { 'fetch_topology': self.topology, } @@ -8263,7 +8175,7 @@ def setUp(self): ) # This is the return value for utils.find_resource() - self.sdk_client.get_server.return_value = self.server + self.compute_sdk_client.get_server.return_value = self.server self.image_client.get_image.return_value = self.image self.flavors_mock.get.return_value = self.flavor @@ -8558,7 +8470,7 @@ def test_server_start_with_all_projects(self): self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_once_with( + self.compute_sdk_client.find_server.assert_called_once_with( servers[0].id, ignore_missing=False, details=False, @@ -8593,7 +8505,7 @@ def test_server_start_with_all_projects(self): self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_once_with( + self.compute_sdk_client.find_server.assert_called_once_with( servers[0].id, ignore_missing=False, details=False, @@ -8685,7 +8597,7 @@ def test_server_unset_with_property(self): def test_server_unset_with_description_api_newer(self): # Description is supported for nova api version 2.19 or above - self.app.client_manager.compute.api_version = 2.19 + self.compute_client.api_version = 2.19 arglist = [ '--description', @@ -8706,7 +8618,7 @@ def test_server_unset_with_description_api_newer(self): def test_server_unset_with_description_api_older(self): # Description is not supported for nova api version below 2.19 - self.app.client_manager.compute.api_version = 2.18 + self.compute_client.api_version = api_versions.APIVersion('2.18') arglist = [ '--description', @@ -8718,15 +8630,15 @@ def test_server_unset_with_description_api_older(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object(api_versions, 'APIVersion', return_value=2.19): - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) + ex = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + '--os-compute-api-version 2.19 or greater is required', str(ex) + ) def test_server_unset_with_tag(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.26' - ) + self.compute_client.api_version = api_versions.APIVersion('2.26') arglist = [ '--tag', @@ -8752,9 +8664,7 @@ def test_server_unset_with_tag(self): ) def test_server_unset_with_tag_pre_v226(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.25' - ) + self.compute_client.api_version = api_versions.APIVersion('2.25') arglist = [ '--tag', @@ -8785,8 +8695,8 @@ def setUp(self): attrs={'status': 'SHELVED'}, ) - self.sdk_client.find_server.return_value = self.server - self.sdk_client.unshelve_server.return_value = None + self.compute_sdk_client.find_server.return_value = self.server + self.compute_sdk_client.unshelve_server.return_value = None # Get the command object to test self.cmd = server.UnshelveServer(self.app, None) @@ -8802,11 +8712,13 @@ def test_unshelve(self): self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_once_with( + self.compute_sdk_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, ) - self.sdk_client.unshelve_server.assert_called_once_with(self.server.id) + self.compute_sdk_client.unshelve_server.assert_called_once_with( + self.server.id + ) def test_unshelve_with_az(self): self._set_mock_microversion('2.77') @@ -8824,11 +8736,11 @@ def test_unshelve_with_az(self): self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_once_with( + self.compute_sdk_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, ) - self.sdk_client.unshelve_server.assert_called_once_with( + self.compute_sdk_client.unshelve_server.assert_called_once_with( self.server.id, availability_zone='foo-az', ) @@ -8870,11 +8782,11 @@ def test_unshelve_with_host(self): self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_once_with( + self.compute_sdk_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, ) - self.sdk_client.unshelve_server.assert_called_once_with( + self.compute_sdk_client.unshelve_server.assert_called_once_with( self.server.id, host='server1', ) @@ -8916,11 +8828,11 @@ def test_unshelve_with_no_az(self): self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_once_with( + self.compute_sdk_client.find_server.assert_called_once_with( self.server.id, ignore_missing=False, ) - self.sdk_client.unshelve_server.assert_called_once_with( + self.compute_sdk_client.unshelve_server.assert_called_once_with( self.server.id, availability_zone=None, ) @@ -8992,13 +8904,15 @@ def test_unshelve_with_wait(self, mock_wait_for_status): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.server.name, ignore_missing=False, ) - self.sdk_client.unshelve_server.assert_called_with(self.server.id) + self.compute_sdk_client.unshelve_server.assert_called_with( + self.server.id + ) mock_wait_for_status.assert_called_once_with( - self.sdk_client.get_server, + self.compute_sdk_client.get_server, self.server.id, callback=mock.ANY, success_status=('active', 'shutoff'), @@ -9124,7 +9038,7 @@ def test_prep_server_detail(self, find_resource): # Call _prep_server_detail(). server_detail = server._prep_server_detail( - self.app.client_manager.compute, + self.compute_client, self.image_client, _server, ) diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py index e5ad8c5b8..507567266 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_backup.py +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -28,7 +28,9 @@ def setUp(self): # Get a shortcut to the compute client ServerManager Mock self.app.client_manager.sdk_connection.compute = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.compute + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) # Set object attributes to be tested. Could be overwritten in subclass. self.attrs = {} @@ -40,7 +42,7 @@ def setup_servers_mock(self, count): ) # This is the return value for compute_client.find_server() - self.sdk_client.find_server = compute_fakes.get_servers( + self.compute_sdk_client.find_server = compute_fakes.get_servers( servers, 0, ) @@ -121,7 +123,7 @@ def test_server_backup_defaults(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.backup_server.assert_called_with( + self.compute_sdk_client.backup_server.assert_called_with( servers[0].id, servers[0].name, '', @@ -157,7 +159,7 @@ def test_server_backup_create_options(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.backup_server.assert_called_with( + self.compute_sdk_client.backup_server.assert_called_with( servers[0].id, 'image', 'daily', @@ -197,7 +199,7 @@ def test_server_backup_wait_fail(self, mock_wait_for_status): parsed_args, ) - self.sdk_client.backup_server.assert_called_with( + self.compute_sdk_client.backup_server.assert_called_with( servers[0].id, 'image', 'daily', @@ -238,7 +240,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.sdk_client.backup_server.assert_called_with( + self.compute_sdk_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 9d64762d2..5598c822e 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_event.py +++ b/openstackclient/tests/unit/compute/v2/test_server_event.py @@ -30,11 +30,13 @@ def setUp(self): super(TestServerEvent, self).setUp() self.app.client_manager.sdk_connection.compute = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.compute - self.sdk_client.find_server = mock.Mock() - self.sdk_client.server_actions = mock.Mock() - self.sdk_client.get_server_action = mock.Mock() - self.sdk_client.reset_mock() + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) + self.compute_sdk_client.find_server = mock.Mock() + self.compute_sdk_client.server_actions = mock.Mock() + self.compute_sdk_client.get_server_action = mock.Mock() + self.compute_sdk_client.reset_mock() patcher = mock.patch.object( sdk_utils, 'supports_microversion', return_value=True @@ -42,7 +44,7 @@ def setUp(self): self.addCleanup(patcher.stop) self.supports_microversion_mock = patcher.start() self._set_mock_microversion( - self.app.client_manager.compute.api_version.get_string() + self.compute_client.api_version.get_string() ) def _set_mock_microversion(self, mock_v): @@ -97,8 +99,8 @@ class TestListServerEvent(TestServerEvent): def setUp(self): super().setUp() - self.sdk_client.find_server.return_value = self.fake_server - self.sdk_client.server_actions.return_value = [ + self.compute_sdk_client.find_server.return_value = self.fake_server + self.compute_sdk_client.server_actions.return_value = [ self.fake_event, ] @@ -116,11 +118,13 @@ def test_server_event_list(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.sdk_client.server_actions.assert_called_with(self.fake_server.id) + self.compute_sdk_client.server_actions.assert_called_with( + self.fake_server.id + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -138,11 +142,13 @@ 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.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.sdk_client.server_actions.assert_called_with(self.fake_server.id) + self.compute_sdk_client.server_actions.assert_called_with( + self.fake_server.id + ) self.assertEqual(self.long_columns, columns) self.assertEqual(self.long_data, tuple(data)) @@ -163,11 +169,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.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.sdk_client.server_actions.assert_called_with( + self.compute_sdk_client.server_actions.assert_called_with( self.fake_server.id, changes_since='2016-03-04T06:27:59Z', ) @@ -243,11 +249,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.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.sdk_client.server_actions.assert_called_with( + self.compute_sdk_client.server_actions.assert_called_with( self.fake_server.id, changes_before='2016-03-04T06:27:59Z', ) @@ -319,7 +325,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.sdk_client.server_actions.assert_called_with( + self.compute_sdk_client.server_actions.assert_called_with( self.fake_server.id, limit=1, paginated=False, @@ -366,7 +372,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.sdk_client.server_actions.assert_called_with( + self.compute_sdk_client.server_actions.assert_called_with( self.fake_server.id, marker='test_event', ) @@ -420,8 +426,10 @@ class TestShowServerEvent(TestServerEvent): def setUp(self): super().setUp() - self.sdk_client.find_server.return_value = self.fake_server - self.sdk_client.get_server_action.return_value = self.fake_event + self.compute_sdk_client.find_server.return_value = self.fake_server + self.compute_sdk_client.get_server_action.return_value = ( + self.fake_event + ) self.cmd = server_event.ShowServerEvent(self.app, None) @@ -438,11 +446,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.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.fake_server.name, ignore_missing=False, ) - self.sdk_client.get_server_action.assert_called_with( + self.compute_sdk_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 ef6f13e0c..44fa083f1 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_group.py +++ b/openstackclient/tests/unit/compute/v2/test_server_group.py @@ -51,15 +51,17 @@ def setUp(self): super().setUp() # Create and get a shortcut to the compute client mock - self.sdk_client = self.app.client_manager.sdk_connection.compute - self.sdk_client.reset_mock() + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) + self.compute_sdk_client.reset_mock() class TestServerGroupCreate(TestServerGroup): def setUp(self): super().setUp() - self.sdk_client.create_server_group.return_value = ( + self.compute_sdk_client.create_server_group.return_value = ( self.fake_server_group ) self.cmd = server_group.CreateServerGroup(self.app, None) @@ -77,7 +79,7 @@ def test_server_group_create(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_server_group.assert_called_once_with( + self.compute_sdk_client.create_server_group.assert_called_once_with( name=parsed_args.name, policy=parsed_args.policy, ) @@ -98,7 +100,7 @@ def test_server_group_create_with_soft_policies(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_server_group.assert_called_once_with( + self.compute_sdk_client.create_server_group.assert_called_once_with( name=parsed_args.name, policy=parsed_args.policy, ) @@ -141,7 +143,7 @@ def test_server_group_create_with_rules(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_server_group.assert_called_once_with( + self.compute_sdk_client.create_server_group.assert_called_once_with( name=parsed_args.name, policy=parsed_args.policy, rules=parsed_args.rules, @@ -180,7 +182,9 @@ class TestServerGroupDelete(TestServerGroup): def setUp(self): super().setUp() - self.sdk_client.find_server_group.return_value = self.fake_server_group + self.compute_sdk_client.find_server_group.return_value = ( + self.fake_server_group + ) self.cmd = server_group.DeleteServerGroup(self.app, None) def test_server_group_delete(self): @@ -192,10 +196,10 @@ def test_server_group_delete(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.sdk_client.find_server_group.assert_called_once_with( + self.compute_sdk_client.find_server_group.assert_called_once_with( 'affinity_group' ) - self.sdk_client.delete_server_group.assert_called_once_with( + self.compute_sdk_client.delete_server_group.assert_called_once_with( self.fake_server_group.id ) self.assertIsNone(result) @@ -207,15 +211,21 @@ def test_server_group_multiple_delete(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.sdk_client.find_server_group.assert_any_call('affinity_group') - self.sdk_client.find_server_group.assert_any_call( + self.compute_sdk_client.find_server_group.assert_any_call( + 'affinity_group' + ) + self.compute_sdk_client.find_server_group.assert_any_call( 'anti_affinity_group' ) - self.sdk_client.delete_server_group.assert_called_with( + self.compute_sdk_client.delete_server_group.assert_called_with( self.fake_server_group.id ) - self.assertEqual(2, self.sdk_client.find_server_group.call_count) - self.assertEqual(2, self.sdk_client.delete_server_group.call_count) + 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.assertIsNone(result) def test_server_group_delete_no_input(self): @@ -236,7 +246,7 @@ def test_server_group_multiple_delete_with_exception(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.sdk_client.find_server_group.side_effect = [ + self.compute_sdk_client.find_server_group.side_effect = [ self.fake_server_group, exceptions.CommandError, ] @@ -246,12 +256,16 @@ 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.sdk_client.find_server_group.assert_any_call('affinity_group') - self.sdk_client.find_server_group.assert_any_call( + self.compute_sdk_client.find_server_group.assert_any_call( + 'affinity_group' + ) + self.compute_sdk_client.find_server_group.assert_any_call( 'anti_affinity_group' ) - self.assertEqual(2, self.sdk_client.find_server_group.call_count) - self.sdk_client.delete_server_group.assert_called_once_with( + self.assertEqual( + 2, self.compute_sdk_client.find_server_group.call_count + ) + self.compute_sdk_client.delete_server_group.assert_called_once_with( self.fake_server_group.id ) @@ -336,7 +350,9 @@ class TestServerGroupList(TestServerGroup): def setUp(self): super().setUp() - self.sdk_client.server_groups.return_value = [self.fake_server_group] + self.compute_sdk_client.server_groups.return_value = [ + self.fake_server_group + ] self.cmd = server_group.ListServerGroup(self.app, None) @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) @@ -351,7 +367,7 @@ def test_server_group_list(self, sm_mock): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.server_groups.assert_called_once_with() + self.compute_sdk_client.server_groups.assert_called_once_with() self.assertCountEqual(self.list_columns, columns) self.assertCountEqual(self.list_data, tuple(data)) @@ -370,7 +386,7 @@ def test_server_group_list_with_all_projects_and_long(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.server_groups.assert_called_once_with( + self.compute_sdk_client.server_groups.assert_called_once_with( all_projects=True ) @@ -393,7 +409,7 @@ def test_server_group_list_with_limit(self, sm_mock): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.sdk_client.server_groups.assert_called_once_with(limit=1) + self.compute_sdk_client.server_groups.assert_called_once_with(limit=1) @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) def test_server_group_list_with_offset(self, sm_mock): @@ -411,7 +427,7 @@ def test_server_group_list_with_offset(self, sm_mock): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.sdk_client.server_groups.assert_called_once_with(offset=5) + self.compute_sdk_client.server_groups.assert_called_once_with(offset=5) @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) def test_server_group_list_v264(self, sm_mock): @@ -422,7 +438,7 @@ def test_server_group_list_v264(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.server_groups.assert_called_once_with() + 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)) @@ -439,7 +455,7 @@ def test_server_group_list_with_all_projects_and_long_v264(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.server_groups.assert_called_once_with( + self.compute_sdk_client.server_groups.assert_called_once_with( all_projects=True ) @@ -451,7 +467,9 @@ class TestServerGroupShow(TestServerGroup): def setUp(self): super().setUp() - self.sdk_client.find_server_group.return_value = self.fake_server_group + self.compute_sdk_client.find_server_group.return_value = ( + self.fake_server_group + ) self.cmd = server_group.ShowServerGroup(self.app, None) @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py index 83afe3519..317c055e8 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_image.py +++ b/openstackclient/tests/unit/compute/v2/test_server_image.py @@ -27,7 +27,9 @@ def setUp(self): # Get a shortcut to the compute client ServerManager Mock self.app.client_manager.sdk_connection.compute = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.compute + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) # Set object attributes to be tested. Could be overwritten in subclass. self.attrs = {} @@ -39,7 +41,7 @@ def setup_servers_mock(self, count): ) # This is the return value for compute_client.find_server() - self.sdk_client.find_server = compute_fakes.get_servers( + self.compute_sdk_client.find_server = compute_fakes.get_servers( servers, 0, ) @@ -96,7 +98,7 @@ def setup_images_mock(self, count, servers=None): ) self.image_client.find_image = mock.Mock(side_effect=images) - self.sdk_client.create_server_image = mock.Mock( + self.compute_sdk_client.create_server_image = mock.Mock( return_value=images[0], ) return images @@ -118,7 +120,7 @@ def test_server_image_create_defaults(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_server_image.assert_called_with( + self.compute_sdk_client.create_server_image.assert_called_with( servers[0].id, servers[0].name, None, @@ -150,7 +152,7 @@ def test_server_image_create_options(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.create_server_image.assert_called_with( + self.compute_sdk_client.create_server_image.assert_called_with( servers[0].id, 'img-nam', {'key': 'value'}, @@ -180,7 +182,7 @@ def test_server_create_image_wait_fail(self, mock_wait_for_status): parsed_args, ) - self.sdk_client.create_server_image.assert_called_with( + self.compute_sdk_client.create_server_image.assert_called_with( servers[0].id, servers[0].name, None, @@ -210,7 +212,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.sdk_client.create_server_image.assert_called_with( + self.compute_sdk_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 4031c6ae5..236e28b45 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_migration.py +++ b/openstackclient/tests/unit/compute/v2/test_server_migration.py @@ -27,17 +27,17 @@ def setUp(self): super().setUp() # Get a shortcut to the compute client ServerManager Mock - self.servers_mock = self.app.client_manager.compute.servers + self.servers_mock = self.compute_client.servers self.servers_mock.reset_mock() # Get a shortcut to the compute client ServerMigrationsManager Mock - self.server_migrations_mock = ( - self.app.client_manager.compute.server_migrations - ) + self.server_migrations_mock = self.compute_client.server_migrations self.server_migrations_mock.reset_mock() self.app.client_manager.sdk_connection.compute = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.compute + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) patcher = mock.patch.object( sdk_utils, 'supports_microversion', return_value=True @@ -91,10 +91,10 @@ def setUp(self): self._set_mock_microversion('2.1') self.server = compute_fakes.create_one_sdk_server() - self.sdk_client.find_server.return_value = self.server + self.compute_sdk_client.find_server.return_value = self.server self.migrations = compute_fakes.create_migrations(count=3) - self.sdk_client.migrations.return_value = self.migrations + self.compute_sdk_client.migrations.return_value = self.migrations self.data = ( common_utils.get_item_properties(s, self.MIGRATION_FIELDS) @@ -114,7 +114,7 @@ def test_server_migration_list_no_options(self): # Set expected values kwargs = {} - self.sdk_client.migrations.assert_called_with(**kwargs) + self.compute_sdk_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -146,8 +146,8 @@ def test_server_migration_list(self): 'migration_type': 'migration', } - self.sdk_client.find_server.assert_called_with('server1') - self.sdk_client.migrations.assert_called_with(**kwargs) + self.compute_sdk_client.find_server.assert_called_with('server1') + self.compute_sdk_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -205,7 +205,7 @@ def test_server_migration_list(self): 'status': 'migrating', } - self.sdk_client.migrations.assert_called_with(**kwargs) + self.compute_sdk_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -283,7 +283,7 @@ def test_server_migration_list(self): 'changes_since': '2019-08-09T08:03:25Z', } - self.sdk_client.migrations.assert_called_with(**kwargs) + self.compute_sdk_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -409,7 +409,7 @@ def test_server_migration_list_with_changes_before(self): 'changes_before': '2019-08-09T08:03:25Z', } - self.sdk_client.migrations.assert_called_with(**kwargs) + self.compute_sdk_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) @@ -527,7 +527,7 @@ def test_server_migration_list_with_project(self): 'changes_before': "2019-08-09T08:03:25Z", } - self.sdk_client.migrations.assert_called_with(**kwargs) + self.compute_sdk_client.migrations.assert_called_with(**kwargs) self.MIGRATION_COLUMNS.insert( len(self.MIGRATION_COLUMNS) - 2, "Project" @@ -603,7 +603,7 @@ def test_server_migration_list_with_user(self): 'changes_before': "2019-08-09T08:03:25Z", } - self.sdk_client.migrations.assert_called_with(**kwargs) + self.compute_sdk_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") @@ -674,7 +674,7 @@ def test_server_migration_list_with_project_and_user(self): 'changes_before': "2019-08-09T08:03:25Z", } - self.sdk_client.migrations.assert_called_with(**kwargs) + self.compute_sdk_client.migrations.assert_called_with(**kwargs) self.MIGRATION_COLUMNS.insert( len(self.MIGRATION_COLUMNS) - 2, "Project" @@ -724,13 +724,13 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.sdk_client.find_server.return_value = self.server + self.compute_sdk_client.find_server.return_value = self.server self.server_migration = compute_fakes.create_one_server_migration() - self.sdk_client.get_server_migration.return_value = ( + self.compute_sdk_client.get_server_migration.return_value = ( self.server_migration ) - self.sdk_client.server_migrations.return_value = iter( + self.compute_sdk_client.server_migrations.return_value = iter( [self.server_migration] ) @@ -788,10 +788,10 @@ def _test_server_migration_show(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.sdk_client.get_server_migration.assert_called_with( + self.compute_sdk_client.get_server_migration.assert_called_with( self.server.id, '2', ignore_missing=False ) @@ -840,7 +840,7 @@ def test_server_migration_show_pre_v224(self): def test_server_migration_show_by_uuid(self): self._set_mock_microversion('2.59') - self.sdk_client.server_migrations.return_value = iter( + self.compute_sdk_client.server_migrations.return_value = iter( [self.server_migration] ) @@ -859,15 +859,17 @@ def test_server_migration_show_by_uuid(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.sdk_client.server_migrations.assert_called_with(self.server.id) - self.sdk_client.get_server_migration.assert_not_called() + self.compute_sdk_client.server_migrations.assert_called_with( + self.server.id + ) + self.compute_sdk_client.get_server_migration.assert_not_called() def test_server_migration_show_by_uuid_no_matches(self): self._set_mock_microversion('2.59') - self.sdk_client.server_migrations.return_value = iter([]) + self.compute_sdk_client.server_migrations.return_value = iter([]) arglist = [ self.server.id, @@ -926,7 +928,7 @@ def setUp(self): self.server = compute_fakes.create_one_sdk_server() # Return value for utils.find_resource for server. - self.sdk_client.find_server.return_value = self.server + self.compute_sdk_client.find_server.return_value = self.server # Get the command object to test self.cmd = server_migration.AbortMigration(self.app, None) @@ -943,10 +945,10 @@ def test_migration_abort(self): result = self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.sdk_client.abort_server_migration.assert_called_with( + self.compute_sdk_client.abort_server_migration.assert_called_with( '2', self.server.id, ignore_missing=False ) self.assertIsNone(result) @@ -972,7 +974,7 @@ def test_server_migration_abort_by_uuid(self): self._set_mock_microversion('2.59') self.server_migration = compute_fakes.create_one_server_migration() - self.sdk_client.server_migrations.return_value = iter( + self.compute_sdk_client.server_migrations.return_value = iter( [self.server_migration] ) @@ -985,11 +987,13 @@ def test_server_migration_abort_by_uuid(self): result = self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.sdk_client.server_migrations.assert_called_with(self.server.id) - self.sdk_client.abort_server_migration.assert_called_with( + self.compute_sdk_client.server_migrations.assert_called_with( + self.server.id + ) + self.compute_sdk_client.abort_server_migration.assert_called_with( self.server_migration.id, self.server.id, ignore_missing=False ) self.assertIsNone(result) @@ -997,7 +1001,7 @@ def test_server_migration_abort_by_uuid(self): def test_server_migration_abort_by_uuid_no_matches(self): self._set_mock_microversion('2.59') - self.sdk_client.server_migrations.return_value = iter([]) + self.compute_sdk_client.server_migrations.return_value = iter([]) arglist = [ self.server.id, @@ -1039,7 +1043,7 @@ def setUp(self): self.server = compute_fakes.create_one_sdk_server() # Return value for utils.find_resource for server. - self.sdk_client.find_server.return_value = self.server + self.compute_sdk_client.find_server.return_value = self.server # Get the command object to test self.cmd = server_migration.ForceCompleteMigration(self.app, None) @@ -1056,10 +1060,10 @@ def test_migration_force_complete(self): result = self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.sdk_client.force_complete_server_migration.assert_called_with( + self.compute_sdk_client.force_complete_server_migration.assert_called_with( '2', self.server.id ) self.assertIsNone(result) @@ -1085,7 +1089,7 @@ def test_server_migration_force_complete_by_uuid(self): self._set_mock_microversion('2.59') self.server_migration = compute_fakes.create_one_server_migration() - self.sdk_client.server_migrations.return_value = iter( + self.compute_sdk_client.server_migrations.return_value = iter( [self.server_migration] ) @@ -1098,11 +1102,13 @@ def test_server_migration_force_complete_by_uuid(self): result = self.cmd.take_action(parsed_args) - self.sdk_client.find_server.assert_called_with( + self.compute_sdk_client.find_server.assert_called_with( self.server.id, ignore_missing=False ) - self.sdk_client.server_migrations.assert_called_with(self.server.id) - self.sdk_client.force_complete_server_migration.assert_called_with( + self.compute_sdk_client.server_migrations.assert_called_with( + self.server.id + ) + self.compute_sdk_client.force_complete_server_migration.assert_called_with( self.server_migration.id, self.server.id ) self.assertIsNone(result) @@ -1110,7 +1116,7 @@ def test_server_migration_force_complete_by_uuid(self): def test_server_migration_force_complete_by_uuid_no_matches(self): self._set_mock_microversion('2.59') - self.sdk_client.server_migrations.return_value = iter([]) + self.compute_sdk_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 1ef726016..316d6de77 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_volume.py +++ b/openstackclient/tests/unit/compute/v2/test_server_volume.py @@ -9,11 +9,9 @@ # 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 novaclient import api_versions from openstack import utils as sdk_utils from osc_lib import exceptions @@ -27,7 +25,9 @@ def setUp(self): super().setUp() self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_client = self.app.client_manager.sdk_connection.compute + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) class TestServerVolumeList(TestServerVolume): @@ -37,8 +37,8 @@ def setUp(self): self.server = compute_fakes.create_one_sdk_server() self.volume_attachments = compute_fakes.create_volume_attachments() - self.compute_client.find_server.return_value = self.server - self.compute_client.volume_attachments.return_value = ( + self.compute_sdk_client.find_server.return_value = self.server + self.compute_sdk_client.volume_attachments.return_value = ( self.volume_attachments ) @@ -47,9 +47,6 @@ def setUp(self): @mock.patch.object(sdk_utils, 'supports_microversion') def test_server_volume_list(self, sm_mock): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.1' - ) sm_mock.side_effect = [False, False, False, False] arglist = [ @@ -80,7 +77,7 @@ def test_server_volume_list(self, sm_mock): ), tuple(data), ) - self.compute_client.volume_attachments.assert_called_once_with( + self.compute_sdk_client.volume_attachments.assert_called_once_with( self.server, ) @@ -127,7 +124,7 @@ def test_server_volume_list_with_tags(self, sm_mock): ), tuple(data), ) - self.compute_client.volume_attachments.assert_called_once_with( + self.compute_sdk_client.volume_attachments.assert_called_once_with( self.server, ) @@ -176,7 +173,7 @@ def test_server_volume_list_with_delete_on_attachment(self, sm_mock): ), tuple(data), ) - self.compute_client.volume_attachments.assert_called_once_with( + self.compute_sdk_client.volume_attachments.assert_called_once_with( self.server, ) @@ -228,7 +225,7 @@ def test_server_volume_list_with_attachment_ids(self, sm_mock): ), tuple(data), ) - self.compute_client.volume_attachments.assert_called_once_with( + self.compute_sdk_client.volume_attachments.assert_called_once_with( self.server, ) @@ -238,7 +235,7 @@ def setUp(self): super().setUp() self.server = compute_fakes.create_one_sdk_server() - self.compute_client.find_server.return_value = self.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 @@ -261,7 +258,7 @@ def test_server_volume_update(self): result = self.cmd.take_action(parsed_args) # This is a no-op - self.compute_client.update_volume_attachment.assert_not_called() + self.compute_sdk_client.update_volume_attachment.assert_not_called() self.assertIsNone(result) @mock.patch.object(sdk_utils, 'supports_microversion') @@ -282,7 +279,7 @@ def test_server_volume_update_with_delete_on_termination(self, sm_mock): result = self.cmd.take_action(parsed_args) - self.compute_client.update_volume_attachment.assert_called_once_with( + self.compute_sdk_client.update_volume_attachment.assert_called_once_with( self.server, self.volume, delete_on_termination=True, @@ -307,7 +304,7 @@ def test_server_volume_update_with_preserve_on_termination(self, sm_mock): result = self.cmd.take_action(parsed_args) - self.compute_client.update_volume_attachment.assert_called_once_with( + self.compute_sdk_client.update_volume_attachment.assert_called_once_with( self.server, self.volume, delete_on_termination=False ) self.assertIsNone(result) @@ -336,7 +333,7 @@ def test_server_volume_update_with_delete_on_termination_pre_v285( self.cmd.take_action, parsed_args, ) - self.compute_client.update_volume_attachment.assert_not_called() + self.compute_sdk_client.update_volume_attachment.assert_not_called() @mock.patch.object(sdk_utils, 'supports_microversion') def test_server_volume_update_with_preserve_on_termination_pre_v285( @@ -362,4 +359,4 @@ def test_server_volume_update_with_preserve_on_termination_pre_v285( self.cmd.take_action, parsed_args, ) - self.compute_client.update_volume_attachment.assert_not_called() + self.compute_sdk_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 97ac27523..31b7cf82c 100644 --- a/openstackclient/tests/unit/compute/v2/test_service.py +++ b/openstackclient/tests/unit/compute/v2/test_service.py @@ -11,12 +11,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. -# from unittest import mock from unittest.mock import call -from novaclient import api_versions from openstack import utils as sdk_utils from osc_lib import exceptions @@ -29,7 +27,9 @@ def setUp(self): super(TestService, self).setUp() self.app.client_manager.sdk_connection.compute = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.compute + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) class TestServiceDelete(TestService): @@ -38,7 +38,7 @@ class TestServiceDelete(TestService): def setUp(self): super(TestServiceDelete, self).setUp() - self.sdk_client.delete_service.return_value = None + self.compute_sdk_client.delete_service.return_value = None # Get the command object to test self.cmd = service.DeleteService(self.app, None) @@ -54,7 +54,7 @@ def test_service_delete(self): result = self.cmd.take_action(parsed_args) - self.sdk_client.delete_service.assert_called_with( + self.compute_sdk_client.delete_service.assert_called_with( self.services[0].binary, ignore_missing=False ) self.assertIsNone(result) @@ -73,7 +73,7 @@ def test_multi_services_delete(self): calls = [] for s in self.services: calls.append(call(s.binary, ignore_missing=False)) - self.sdk_client.delete_service.assert_has_calls(calls) + self.compute_sdk_client.delete_service.assert_has_calls(calls) self.assertIsNone(result) def test_multi_services_delete_with_exception(self): @@ -85,7 +85,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.sdk_client.delete_service = mock.Mock( + self.compute_sdk_client.delete_service = mock.Mock( side_effect=delete_mock_result ) @@ -97,10 +97,10 @@ def test_multi_services_delete_with_exception(self): '1 of 2 compute services failed to delete.', str(e) ) - self.sdk_client.delete_service.assert_any_call( + self.compute_sdk_client.delete_service.assert_any_call( self.services[0].binary, ignore_missing=False ) - self.sdk_client.delete_service.assert_any_call( + self.compute_sdk_client.delete_service.assert_any_call( 'unexist_service', ignore_missing=False ) @@ -135,7 +135,7 @@ class TestServiceList(TestService): def setUp(self): super(TestServiceList, self).setUp() - self.sdk_client.services.return_value = [self.service] + self.compute_sdk_client.services.return_value = [self.service] # Get the command object to test self.cmd = service.ListService(self.app, None) @@ -158,7 +158,7 @@ def test_service_list(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.services.assert_called_with( + self.compute_sdk_client.services.assert_called_with( host=self.service.host, binary=self.service.binary, ) @@ -189,7 +189,7 @@ def test_service_list_with_long_option(self, sm_mock): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.services.assert_called_with( + self.compute_sdk_client.services.assert_called_with( host=self.service.host, binary=self.service.binary, ) @@ -214,16 +214,13 @@ def test_service_list_with_long_option_2_11(self, sm_mock): ('long', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.11' - ) # 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.sdk_client.services.assert_called_with( + self.compute_sdk_client.services.assert_called_with( host=self.service.host, binary=self.service.binary, ) @@ -242,8 +239,8 @@ def setUp(self): self.service = compute_fakes.create_one_service() - self.sdk_client.enable_service.return_value = self.service - self.sdk_client.disable_service.return_value = self.service + self.compute_sdk_client.enable_service.return_value = self.service + self.compute_sdk_client.disable_service.return_value = self.service self.cmd = service.SetService(self.app, None) @@ -261,8 +258,8 @@ def test_set_nothing(self, sm_mock): parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.sdk_client.enable_service.assert_not_called() - self.sdk_client.disable_service.assert_not_called() + self.compute_sdk_client.enable_service.assert_not_called() + self.compute_sdk_client.disable_service.assert_not_called() self.assertIsNone(result) @mock.patch.object(sdk_utils, 'supports_microversion') @@ -282,7 +279,7 @@ def test_service_set_enable(self, sm_mock): result = self.cmd.take_action(parsed_args) - self.sdk_client.enable_service.assert_called_with( + self.compute_sdk_client.enable_service.assert_called_with( None, self.service.host, self.service.binary ) self.assertIsNone(result) @@ -304,7 +301,7 @@ def test_service_set_disable(self, sm_mock): result = self.cmd.take_action(parsed_args) - self.sdk_client.disable_service.assert_called_with( + self.compute_sdk_client.disable_service.assert_called_with( None, self.service.host, self.service.binary, None ) self.assertIsNone(result) @@ -330,7 +327,7 @@ def test_service_set_disable_with_reason(self, sm_mock): result = self.cmd.take_action(parsed_args) - self.sdk_client.disable_service.assert_called_with( + self.compute_sdk_client.disable_service.assert_called_with( None, self.service.host, self.service.binary, reason ) self.assertIsNone(result) @@ -404,11 +401,11 @@ def test_service_set_state_up(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_sdk_client.update_service_forced_down.assert_called_once_with( None, self.service.host, self.service.binary, False ) - self.assertNotCalled(self.sdk_client.enable_service) - self.assertNotCalled(self.sdk_client.disable_service) + self.assertNotCalled(self.compute_sdk_client.enable_service) + self.assertNotCalled(self.compute_sdk_client.disable_service) self.assertIsNone(result) @mock.patch.object(sdk_utils, 'supports_microversion') @@ -426,11 +423,11 @@ def test_service_set_state_down(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_sdk_client.update_service_forced_down.assert_called_once_with( None, self.service.host, self.service.binary, True ) - self.assertNotCalled(self.sdk_client.enable_service) - self.assertNotCalled(self.sdk_client.disable_service) + self.assertNotCalled(self.compute_sdk_client.enable_service) + self.assertNotCalled(self.compute_sdk_client.disable_service) self.assertIsNone(result) @mock.patch.object(sdk_utils, 'supports_microversion') @@ -450,10 +447,10 @@ def test_service_set_enable_and_state_down(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.sdk_client.enable_service.assert_called_once_with( + self.compute_sdk_client.enable_service.assert_called_once_with( None, self.service.host, self.service.binary ) - self.sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_sdk_client.update_service_forced_down.assert_called_once_with( None, self.service.host, self.service.binary, True ) self.assertIsNone(result) @@ -476,12 +473,12 @@ def test_service_set_enable_and_state_down_with_exception(self, sm_mock): parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch.object( - self.sdk_client, 'enable_service', side_effect=Exception() + self.compute_sdk_client, 'enable_service', side_effect=Exception() ): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_sdk_client.update_service_forced_down.assert_called_once_with( None, self.service.host, self.service.binary, True ) @@ -504,12 +501,14 @@ def test_service_set_2_53_disable_down(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c' - self.sdk_client.services.return_value = [mock.Mock(id=service_id)] + self.compute_sdk_client.services.return_value = [ + mock.Mock(id=service_id) + ] result = self.cmd.take_action(parsed_args) - self.sdk_client.disable_service.assert_called_once_with( + self.compute_sdk_client.disable_service.assert_called_once_with( service_id, self.service.host, self.service.binary, None ) - self.sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_sdk_client.update_service_forced_down.assert_called_once_with( service_id, self.service.host, self.service.binary, True ) self.assertIsNone(result) @@ -535,9 +534,11 @@ def test_service_set_2_53_disable_reason(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c' - self.sdk_client.services.return_value = [mock.Mock(id=service_id)] + self.compute_sdk_client.services.return_value = [ + mock.Mock(id=service_id) + ] result = self.cmd.take_action(parsed_args) - self.sdk_client.disable_service.assert_called_once_with( + self.compute_sdk_client.disable_service.assert_called_once_with( service_id, self.service.host, self.service.binary, reason ) self.assertIsNone(result) @@ -561,23 +562,25 @@ def test_service_set_2_53_enable_up(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) service_id = '339478d0-0b95-4a94-be63-d5be05dfeb1c' - self.sdk_client.services.return_value = [mock.Mock(id=service_id)] + self.compute_sdk_client.services.return_value = [ + mock.Mock(id=service_id) + ] result = self.cmd.take_action(parsed_args) - self.sdk_client.enable_service.assert_called_once_with( + self.compute_sdk_client.enable_service.assert_called_once_with( service_id, self.service.host, self.service.binary ) - self.sdk_client.update_service_forced_down.assert_called_once_with( + self.compute_sdk_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.sdk_client.services.return_value = [] + self.compute_sdk_client.services.return_value = [] ex = self.assertRaises( exceptions.CommandError, self.cmd._find_service_by_host_and_binary, - self.sdk_client, + self.compute_sdk_client, 'fake-host', 'nova-compute', ) @@ -589,11 +592,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.sdk_client.services.return_value = [mock.Mock(), mock.Mock()] + self.compute_sdk_client.services.return_value = [ + mock.Mock(), + mock.Mock(), + ] ex = self.assertRaises( exceptions.CommandError, self.cmd._find_service_by_host_and_binary, - self.sdk_client, + self.compute_sdk_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 76b70117a..9ddc6c82d 100644 --- a/openstackclient/tests/unit/compute/v2/test_usage.py +++ b/openstackclient/tests/unit/compute/v2/test_usage.py @@ -23,7 +23,9 @@ def setUp(self): super(TestUsage, self).setUp() self.app.client_manager.sdk_connection.compute = mock.Mock() - self.sdk_client = self.app.client_manager.sdk_connection.compute + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.compute + ) self.projects_mock = self.app.client_manager.identity.projects self.projects_mock.reset_mock() @@ -57,7 +59,7 @@ class TestUsageList(TestUsage): def setUp(self): super(TestUsageList, self).setUp() - self.sdk_client.usages.return_value = self.usages + self.compute_sdk_client.usages.return_value = self.usages self.projects_mock.list.return_value = [self.project] # Get the command object to test @@ -96,7 +98,7 @@ def test_usage_list_with_options(self): columns, data = self.cmd.take_action(parsed_args) self.projects_mock.list.assert_called_with() - self.sdk_client.usages.assert_called_with( + self.compute_sdk_client.usages.assert_called_with( start='2016-11-11T00:00:00', end='2016-12-20T00:00:00', detailed=True, @@ -117,7 +119,7 @@ def test_usage_list_with_pagination(self): columns, data = self.cmd.take_action(parsed_args) self.projects_mock.list.assert_called_with() - self.sdk_client.usages.assert_has_calls( + self.compute_sdk_client.usages.assert_has_calls( [mock.call(start=mock.ANY, end=mock.ANY, detailed=True)] ) self.assertCountEqual(self.columns, columns) @@ -148,7 +150,7 @@ class TestUsageShow(TestUsage): def setUp(self): super(TestUsageShow, self).setUp() - self.sdk_client.get_usage.return_value = self.usage + self.compute_sdk_client.get_usage.return_value = self.usage self.projects_mock.get.return_value = self.project # Get the command object to test @@ -191,7 +193,7 @@ def test_usage_show_with_options(self): columns, data = self.cmd.take_action(parsed_args) - self.sdk_client.get_usage.assert_called_with( + self.compute_sdk_client.get_usage.assert_called_with( project=self.project.id, start='2016-11-11T00:00:00', end='2016-12-20T00:00:00', diff --git a/openstackclient/tests/unit/network/test_common.py b/openstackclient/tests/unit/network/test_common.py index edc315a28..40d78e6bc 100644 --- a/openstackclient/tests/unit/network/test_common.py +++ b/openstackclient/tests/unit/network/test_common.py @@ -138,8 +138,8 @@ def setUp(self): ) self.app.client_manager.compute = mock.Mock() - self.compute = self.app.client_manager.compute - self.compute.compute_action = mock.Mock( + self.compute_client = self.app.client_manager.compute + self.compute_client.compute_action = mock.Mock( return_value='take_action_compute' ) @@ -161,7 +161,7 @@ def test_take_action_compute(self): self.app.client_manager.network_endpoint_enabled = False parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.compute.compute_action.assert_called_with(parsed_args) + self.compute_client.compute_action.assert_called_with(parsed_args) self.assertEqual('take_action_compute', result) 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 238a2c6b2..1385b09df 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py @@ -29,7 +29,7 @@ def setUp(self): super(TestFloatingIPCompute, self).setUp() # Get a shortcut to the compute client - self.compute = self.app.client_manager.compute + self.compute_client = self.app.client_manager.compute @mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_create') @@ -58,7 +58,7 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - # self.compute.floating_ips.create.return_value = self.floating_ip + # self.compute_client.floating_ips.create.return_value = self.floating_ip # Get the command object to test self.cmd = fip.CreateFloatingIP(self.app, None) 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 6c3d4ee79..289a58bed 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 @@ -25,7 +25,7 @@ def setUp(self): super(TestFloatingIPPoolCompute, self).setUp() # Get a shortcut to the compute client - self.compute = self.app.client_manager.compute + self.compute_client = self.app.client_manager.compute @mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_pool_list') diff --git a/openstackclient/tests/unit/network/v2/test_network_compute.py b/openstackclient/tests/unit/network/v2/test_network_compute.py index ada1d7237..775ee4e91 100644 --- a/openstackclient/tests/unit/network/v2/test_network_compute.py +++ b/openstackclient/tests/unit/network/v2/test_network_compute.py @@ -28,7 +28,7 @@ def setUp(self): super(TestNetworkCompute, self).setUp() # Get a shortcut to the compute client - self.compute = self.app.client_manager.compute + self.compute_client = self.app.client_manager.compute @mock.patch('openstackclient.api.compute_v2.APIv2.network_create') @@ -182,7 +182,7 @@ def setUp(self): self._networks = compute_fakes.create_networks(count=3) # Return value of utils.find_resource() - self.compute.api.network_find = compute_fakes.get_networks( + self.compute_client.api.network_find = compute_fakes.get_networks( networks=self._networks ) diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index c897a1afa..460a49904 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -1200,8 +1200,6 @@ class TestListPort(TestPort): def setUp(self): super(TestListPort, self).setUp() - # Get the command object to test - self.cmd = port.ListPort(self.app, self.namespace) self.network_client.ports = mock.Mock(return_value=self._ports) fake_router = network_fakes.FakeRouter.create_one_router( { @@ -1215,7 +1213,12 @@ def setUp(self): ) self.network_client.find_router = mock.Mock(return_value=fake_router) self.network_client.find_network = mock.Mock(return_value=fake_network) + self.app.client_manager.compute = mock.Mock() + self.compute_client = self.app.client_manager.compute + + # Get the command object to test + self.cmd = port.ListPort(self.app, self.namespace) def test_port_list_no_options(self): arglist = [] 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 57defbd3e..06f508592 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_compute.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_compute.py @@ -27,7 +27,7 @@ def setUp(self): super(TestSecurityGroupCompute, self).setUp() # Get a shortcut to the compute client - self.compute = self.app.client_manager.compute + self.compute_client = self.app.client_manager.compute @mock.patch('openstackclient.api.compute_v2.APIv2.security_group_create') @@ -119,7 +119,7 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - self.compute.api.security_group_find = ( + self.compute_client.api.security_group_find = ( compute_fakes.get_security_groups(self._security_groups) ) @@ -279,7 +279,7 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - self.compute.api.security_group_find = mock.Mock( + self.compute_client.api.security_group_find = mock.Mock( return_value=self._security_group ) 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 2a1609bac..1fb4831ec 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 @@ -28,7 +28,7 @@ def setUp(self): super(TestSecurityGroupRuleCompute, self).setUp() # Get a shortcut to the network client - self.compute = self.app.client_manager.compute + self.compute_client = self.app.client_manager.compute @mock.patch('openstackclient.api.compute_v2.APIv2.security_group_rule_create') @@ -59,7 +59,7 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - self.compute.api.security_group_find = mock.Mock( + self.compute_client.api.security_group_find = mock.Mock( return_value=self._security_group, ) @@ -168,7 +168,7 @@ def test_security_group_rule_create_default_rule(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) # TODO(dtroyer): save this for the security group rule changes - # self.compute.api.security_group_rule_create.assert_called_once_with( + # self.compute_client.api.security_group_rule_create.assert_called_once_with( sgr_mock.assert_called_once_with( security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], @@ -212,7 +212,7 @@ def test_security_group_rule_create_remote_group(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) # TODO(dtroyer): save this for the security group rule changes - # self.compute.api.security_group_rule_create.assert_called_once_with( + # self.compute_client.api.security_group_rule_create.assert_called_once_with( sgr_mock.assert_called_once_with( security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], @@ -251,7 +251,7 @@ def test_security_group_rule_create_remote_ip(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) # TODO(dtroyer): save this for the security group rule changes - # self.compute.api.security_group_rule_create.assert_called_once_with( + # self.compute_client.api.security_group_rule_create.assert_called_once_with( sgr_mock.assert_called_once_with( security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], @@ -291,7 +291,7 @@ def test_security_group_rule_create_proto_option(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) # TODO(dtroyer): save this for the security group rule changes - # self.compute.api.security_group_rule_create.assert_called_once_with( + # self.compute_client.api.security_group_rule_create.assert_called_once_with( sgr_mock.assert_called_once_with( security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], @@ -440,10 +440,10 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - self.compute.api.security_group_find = mock.Mock( + self.compute_client.api.security_group_find = mock.Mock( return_value=self._security_group, ) - self.compute.api.security_group_list = mock.Mock( + self.compute_client.api.security_group_list = mock.Mock( return_value=[self._security_group], ) @@ -454,7 +454,7 @@ def test_security_group_rule_list_default(self): parsed_args = self.check_parser(self.cmd, [], []) columns, data = self.cmd.take_action(parsed_args) - self.compute.api.security_group_list.assert_called_once_with( + self.compute_client.api.security_group_list.assert_called_once_with( search_opts={'all_tenants': False} ) self.assertEqual(self.expected_columns_no_group, columns) @@ -470,7 +470,7 @@ def test_security_group_rule_list_with_group(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute.api.security_group_find.assert_called_once_with( + self.compute_client.api.security_group_find.assert_called_once_with( self._security_group['id'] ) self.assertEqual(self.expected_columns_with_group, columns) @@ -486,7 +486,7 @@ def test_security_group_rule_list_all_projects(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute.api.security_group_list.assert_called_once_with( + self.compute_client.api.security_group_list.assert_called_once_with( search_opts={'all_tenants': True} ) self.assertEqual(self.expected_columns_no_group, columns) @@ -502,7 +502,7 @@ def test_security_group_rule_list_with_ignored_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute.api.security_group_list.assert_called_once_with( + self.compute_client.api.security_group_list.assert_called_once_with( search_opts={'all_tenants': False} ) self.assertEqual(self.expected_columns_no_group, columns) @@ -525,7 +525,7 @@ def setUp(self): # Build a security group fake customized for this test. security_group_rules = [self._security_group_rule] security_group = {'rules': security_group_rules} - self.compute.api.security_group_list = mock.Mock( + self.compute_client.api.security_group_list = mock.Mock( return_value=[security_group], ) @@ -548,6 +548,6 @@ def test_security_group_rule_show_all_options(self): columns, data = self.cmd.take_action(parsed_args) - self.compute.api.security_group_list.assert_called_once_with() + self.compute_client.api.security_group_list.assert_called_once_with() 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 ea8f54566..dcd8b11f3 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -89,10 +89,12 @@ def setUp(self): self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN ) + self.app.client_manager.compute = compute_fakes.FakeComputev2Client( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN, ) + self.compute_client = self.app.client_manager.compute # TODO(stephenfin): Check if the responses are actually the same diff --git a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py index 3655c8bfb..c6fbbe58a 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py @@ -33,7 +33,8 @@ def setUp(self): self.projects_mock = self.app.client_manager.identity.projects self.projects_mock.reset_mock() - self.servers_mock = self.app.client_manager.compute.servers + self.compute_client = self.app.client_manager.compute + self.servers_mock = self.compute_client.servers self.servers_mock.reset_mock() From 912a21a8fd6ba0582c1196ced0095afd149dfe3a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 6 Sep 2023 11:42:20 +0100 Subject: [PATCH 009/403] tests: Add compute v2 FakeClientMixin This ensures we are speccing the compute proxy API. Change-Id: I7adbf2666d71f222fbd9c1479216f72cb9893348 Signed-off-by: Stephen Finucane --- .../unit/common/test_availability_zone.py | 16 ++--------- .../tests/unit/common/test_limits.py | 1 - .../tests/unit/common/test_quota.py | 1 - .../tests/unit/compute/v2/fakes.py | 28 +++++++++++++++---- .../tests/unit/compute/v2/test_aggregate.py | 17 ----------- .../tests/unit/compute/v2/test_console.py | 17 ++--------- .../tests/unit/compute/v2/test_flavor.py | 21 +------------- .../tests/unit/compute/v2/test_host.py | 18 ++---------- .../tests/unit/compute/v2/test_hypervisor.py | 15 ++-------- .../unit/compute/v2/test_hypervisor_stats.py | 5 ---- .../tests/unit/compute/v2/test_keypair.py | 9 ------ .../tests/unit/compute/v2/test_server.py | 5 ---- .../unit/compute/v2/test_server_backup.py | 13 --------- .../unit/compute/v2/test_server_event.py | 9 ------ .../unit/compute/v2/test_server_group.py | 9 ------ .../unit/compute/v2/test_server_image.py | 13 --------- .../unit/compute/v2/test_server_migration.py | 5 ---- .../unit/compute/v2/test_server_volume.py | 14 ++-------- .../tests/unit/compute/v2/test_service.py | 16 ++--------- .../tests/unit/compute/v2/test_usage.py | 5 ---- .../network/v2/test_floating_ip_compute.py | 16 +++-------- .../v2/test_floating_ip_pool_compute.py | 10 +------ .../unit/network/v2/test_network_compute.py | 14 +++------- .../network/v2/test_security_group_compute.py | 18 ++++-------- .../v2/test_security_group_rule_compute.py | 16 +++-------- .../unit/volume/v3/test_volume_attachment.py | 1 - 26 files changed, 55 insertions(+), 257 deletions(-) diff --git a/openstackclient/tests/unit/common/test_availability_zone.py b/openstackclient/tests/unit/common/test_availability_zone.py index 1e5cbf9d3..0965c01ce 100644 --- a/openstackclient/tests/unit/common/test_availability_zone.py +++ b/openstackclient/tests/unit/common/test_availability_zone.py @@ -10,8 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock - from openstackclient.common import availability_zone from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes from openstackclient.tests.unit.network.v2 import fakes as network_fakes @@ -78,22 +76,12 @@ def _build_network_az_datalist(network_az, long_datalist=False): return (datalist,) -class TestAvailabilityZone( +class TestAvailabilityZoneList( network_fakes.FakeClientMixin, volume_fakes.FakeClientMixin, + compute_fakes.FakeClientMixin, utils.TestCommand, ): - def setUp(self): - super().setUp() - - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - self.compute_sdk_client.availability_zones = mock.Mock() - - -class TestAvailabilityZoneList(TestAvailabilityZone): compute_azs = compute_fakes.create_availability_zones() volume_azs = volume_fakes.create_availability_zones(count=1) network_azs = network_fakes.create_availability_zones() diff --git a/openstackclient/tests/unit/common/test_limits.py b/openstackclient/tests/unit/common/test_limits.py index ecbbe534a..86b17b182 100644 --- a/openstackclient/tests/unit/common/test_limits.py +++ b/openstackclient/tests/unit/common/test_limits.py @@ -27,7 +27,6 @@ class TestComputeLimits(compute_fakes.TestComputev2): def setUp(self): super().setUp() self.app.client_manager.volume_endpoint_enabled = False - self.compute_client = self.app.client_manager.compute self.fake_limits = compute_fakes.FakeLimits() self.compute_client.limits.get.return_value = self.fake_limits diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index 8a15fb4c4..1884321a3 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -48,7 +48,6 @@ def setUp(self): self.projects_mock.reset_mock() self.projects_mock.get.return_value = self.projects[0] - self.compute_client = self.app.client_manager.compute self.compute_quotas_mock = self.compute_client.quotas self.compute_quotas_mock.reset_mock() self.compute_quotas_class_mock = self.compute_client.quota_classes diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 563245863..3e9f4ad9b 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -19,6 +19,7 @@ import uuid from novaclient import api_versions +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 @@ -148,12 +149,7 @@ def __init__(self, **kwargs): self.api_version = api_versions.APIVersion('2.1') -class TestComputev2( - network_fakes.FakeClientMixin, - image_fakes.FakeClientMixin, - volume_fakes.FakeClientMixin, - utils.TestCommand, -): +class FakeClientMixin: def setUp(self): super().setUp() @@ -168,6 +164,26 @@ def setUp(self): endpoint=fakes.AUTH_URL, ) + # 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 + ) + + +class TestComputev2( + network_fakes.FakeClientMixin, + image_fakes.FakeClientMixin, + volume_fakes.FakeClientMixin, + FakeClientMixin, + utils.TestCommand, +): + def setUp(self): + super().setUp() + self.app.client_manager.identity = identity_fakes.FakeIdentityv2Client( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN, diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index 52f6759a8..13a643720 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -55,23 +55,6 @@ class TestAggregate(compute_fakes.TestComputev2): fake_ag.uuid, ) - def setUp(self): - super(TestAggregate, self).setUp() - - # Get a shortcut to the AggregateManager Mock - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - self.compute_sdk_client.aggregates = mock.Mock() - self.compute_sdk_client.find_aggregate = mock.Mock() - self.compute_sdk_client.create_aggregate = mock.Mock() - self.compute_sdk_client.update_aggregate = mock.Mock() - self.compute_sdk_client.update_aggregate = mock.Mock() - self.compute_sdk_client.set_aggregate_metadata = mock.Mock() - self.compute_sdk_client.add_host_to_aggregate = mock.Mock() - self.compute_sdk_client.remove_host_from_aggregate = mock.Mock() - class TestAggregateAddHost(TestAggregate): def setUp(self): diff --git a/openstackclient/tests/unit/compute/v2/test_console.py b/openstackclient/tests/unit/compute/v2/test_console.py index 07772fa44..c04f55cb0 100644 --- a/openstackclient/tests/unit/compute/v2/test_console.py +++ b/openstackclient/tests/unit/compute/v2/test_console.py @@ -20,20 +20,7 @@ from openstackclient.tests.unit import utils -class TestConsole(compute_fakes.TestComputev2): - def setUp(self): - super(TestConsole, self).setUp() - - # SDK mock - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - self.compute_sdk_client.find_server = mock.Mock() - self.compute_sdk_client.get_server_console_output = mock.Mock() - - -class TestConsoleLog(TestConsole): +class TestConsoleLog(compute_fakes.TestComputev2): _server = compute_fakes.create_one_server() def setUp(self): @@ -89,7 +76,7 @@ def test_show_lines(self): ) -class TestConsoleUrlShow(TestConsole): +class TestConsoleUrlShow(compute_fakes.TestComputev2): _server = compute_fakes.create_one_server() def setUp(self): diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py index 1f2854563..e81d2bf70 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -30,25 +30,6 @@ class TestFlavor(compute_fakes.TestComputev2): def setUp(self): super(TestFlavor, self).setUp() - # SDK mock - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - self.compute_sdk_client.flavors = mock.Mock() - self.compute_sdk_client.find_flavor = mock.Mock() - self.compute_sdk_client.delete_flavor = mock.Mock() - self.compute_sdk_client.update_flavor = mock.Mock() - self.compute_sdk_client.flavor_add_tenant_access = mock.Mock() - self.compute_sdk_client.flavor_remove_tenant_access = mock.Mock() - self.compute_sdk_client.create_flavor_extra_specs = mock.Mock() - self.compute_sdk_client.update_flavor_extra_specs_property = ( - mock.Mock() - ) - self.compute_sdk_client.delete_flavor_extra_specs_property = ( - mock.Mock() - ) - self.projects_mock = self.app.client_manager.identity.projects self.projects_mock.reset_mock() @@ -1193,7 +1174,7 @@ def test_flavor_unset_project(self): self.flavor.id, self.project.id, ) - self.compute_sdk_client.delete_flavor_extra_specs_proerty.assert_not_called() + self.compute_sdk_client.delete_flavor_extra_specs_property.assert_not_called() self.assertIsNone(result) def test_flavor_unset_no_project(self): diff --git a/openstackclient/tests/unit/compute/v2/test_host.py b/openstackclient/tests/unit/compute/v2/test_host.py index 6fbce9afa..8a899ea2b 100644 --- a/openstackclient/tests/unit/compute/v2/test_host.py +++ b/openstackclient/tests/unit/compute/v2/test_host.py @@ -21,20 +21,8 @@ from openstackclient.tests.unit import utils as tests_utils -class TestHost(compute_fakes.TestComputev2): - def setUp(self): - super(TestHost, self).setUp() - - # Get a shortcut to the compute client - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - self.compute_sdk_client.get = mock.Mock() - - @mock.patch('openstackclient.api.compute_v2.APIv2.host_list') -class TestHostList(TestHost): +class TestHostList(compute_fakes.TestComputev2): _host = compute_fakes.create_one_host() def setUp(self): @@ -93,7 +81,7 @@ def test_host_list_with_option(self, h_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.host_set') -class TestHostSet(TestHost): +class TestHostSet(compute_fakes.TestComputev2): def setUp(self): super(TestHostSet, self).setUp() @@ -143,7 +131,7 @@ def test_host_set(self, h_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.host_show') -class TestHostShow(TestHost): +class TestHostShow(compute_fakes.TestComputev2): _host = compute_fakes.create_one_host() def setUp(self): diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor.py b/openstackclient/tests/unit/compute/v2/test_hypervisor.py index 1651b04a1..0499ae59e 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor.py @@ -25,18 +25,7 @@ from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -class TestHypervisor(compute_fakes.TestComputev2): - def setUp(self): - super().setUp() - - # Create and get a shortcut to the compute client mock - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - self.compute_sdk_client.reset_mock() - - -class TestHypervisorList(TestHypervisor): +class TestHypervisorList(compute_fakes.TestComputev2): def setUp(self): super().setUp() @@ -294,7 +283,7 @@ def test_hypervisor_list_with_marker_pre_v233(self, sm_mock): ) -class TestHypervisorShow(TestHypervisor): +class TestHypervisorShow(compute_fakes.TestComputev2): def setUp(self): super().setUp() diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py index b35475cfc..bb755310a 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py @@ -23,11 +23,6 @@ class TestHypervisorStats(compute_fakes.TestComputev2): def setUp(self): super(TestHypervisorStats, self).setUp() - # Get a shortcut to the compute client hypervisors mock - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) self.compute_sdk_client.get = mock.Mock() diff --git a/openstackclient/tests/unit/compute/v2/test_keypair.py b/openstackclient/tests/unit/compute/v2/test_keypair.py index 7fd56d4e8..39d5739d1 100644 --- a/openstackclient/tests/unit/compute/v2/test_keypair.py +++ b/openstackclient/tests/unit/compute/v2/test_keypair.py @@ -40,15 +40,6 @@ def setUp(self): loaded=True, ) - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - self.compute_sdk_client.keypairs = mock.Mock() - self.compute_sdk_client.create_keypair = mock.Mock() - self.compute_sdk_client.delete_keypair = mock.Mock() - self.compute_sdk_client.find_keypair = mock.Mock() - class TestKeypairCreate(TestKeypair): def setUp(self): diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index ea29c1b78..db6dfc969 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -70,11 +70,6 @@ def setUp(self): self.servers_mock = self.compute_client.servers self.servers_mock.reset_mock() - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - # Get a shortcut to the compute client ServerMigrationsManager Mock self.server_migrations_mock = self.compute_client.server_migrations self.server_migrations_mock.reset_mock() diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py index 507567266..a72f96f16 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_backup.py +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -23,21 +23,8 @@ class TestServerBackup(compute_fakes.TestComputev2): - def setUp(self): - super(TestServerBackup, self).setUp() - - # Get a shortcut to the compute client ServerManager Mock - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - - # Set object attributes to be tested. Could be overwritten in subclass. - self.attrs = {} - def setup_servers_mock(self, count): servers = compute_fakes.create_sdk_servers( - attrs=self.attrs, count=count, ) diff --git a/openstackclient/tests/unit/compute/v2/test_server_event.py b/openstackclient/tests/unit/compute/v2/test_server_event.py index 5598c822e..ce9345bb0 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_event.py +++ b/openstackclient/tests/unit/compute/v2/test_server_event.py @@ -29,15 +29,6 @@ class TestServerEvent(compute_fakes.TestComputev2): def setUp(self): super(TestServerEvent, self).setUp() - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - self.compute_sdk_client.find_server = mock.Mock() - self.compute_sdk_client.server_actions = mock.Mock() - self.compute_sdk_client.get_server_action = mock.Mock() - self.compute_sdk_client.reset_mock() - patcher = mock.patch.object( sdk_utils, 'supports_microversion', return_value=True ) diff --git a/openstackclient/tests/unit/compute/v2/test_server_group.py b/openstackclient/tests/unit/compute/v2/test_server_group.py index 44fa083f1..3356218ec 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_group.py +++ b/openstackclient/tests/unit/compute/v2/test_server_group.py @@ -47,15 +47,6 @@ class TestServerGroup(compute_fakes.TestComputev2): fake_server_group.user_id, ) - def setUp(self): - super().setUp() - - # Create and get a shortcut to the compute client mock - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - self.compute_sdk_client.reset_mock() - class TestServerGroupCreate(TestServerGroup): def setUp(self): diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py index 317c055e8..8ba6d1262 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_image.py +++ b/openstackclient/tests/unit/compute/v2/test_server_image.py @@ -22,21 +22,8 @@ class TestServerImage(compute_fakes.TestComputev2): - def setUp(self): - super().setUp() - - # Get a shortcut to the compute client ServerManager Mock - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - - # Set object attributes to be tested. Could be overwritten in subclass. - self.attrs = {} - def setup_servers_mock(self, count): servers = compute_fakes.create_sdk_servers( - attrs=self.attrs, count=count, ) diff --git a/openstackclient/tests/unit/compute/v2/test_server_migration.py b/openstackclient/tests/unit/compute/v2/test_server_migration.py index 236e28b45..8cb4df4fe 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_migration.py +++ b/openstackclient/tests/unit/compute/v2/test_server_migration.py @@ -34,11 +34,6 @@ def setUp(self): self.server_migrations_mock = self.compute_client.server_migrations self.server_migrations_mock.reset_mock() - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - patcher = mock.patch.object( sdk_utils, 'supports_microversion', return_value=True ) diff --git a/openstackclient/tests/unit/compute/v2/test_server_volume.py b/openstackclient/tests/unit/compute/v2/test_server_volume.py index 316d6de77..439ff048d 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_volume.py +++ b/openstackclient/tests/unit/compute/v2/test_server_volume.py @@ -20,17 +20,7 @@ from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes -class TestServerVolume(compute_fakes.TestComputev2): - def setUp(self): - super().setUp() - - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - - -class TestServerVolumeList(TestServerVolume): +class TestServerVolumeList(compute_fakes.TestComputev2): def setUp(self): super().setUp() @@ -230,7 +220,7 @@ def test_server_volume_list_with_attachment_ids(self, sm_mock): ) -class TestServerVolumeUpdate(TestServerVolume): +class TestServerVolumeUpdate(compute_fakes.TestComputev2): def setUp(self): super().setUp() diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py index 31b7cf82c..d658b1507 100644 --- a/openstackclient/tests/unit/compute/v2/test_service.py +++ b/openstackclient/tests/unit/compute/v2/test_service.py @@ -22,17 +22,7 @@ from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -class TestService(compute_fakes.TestComputev2): - def setUp(self): - super(TestService, self).setUp() - - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - - -class TestServiceDelete(TestService): +class TestServiceDelete(compute_fakes.TestComputev2): services = compute_fakes.create_services(count=2) def setUp(self): @@ -105,7 +95,7 @@ def test_multi_services_delete_with_exception(self): ) -class TestServiceList(TestService): +class TestServiceList(compute_fakes.TestComputev2): service = compute_fakes.create_one_service() columns = ( @@ -233,7 +223,7 @@ def test_service_list_with_long_option_2_11(self, sm_mock): self.assertEqual(data_long, list(data)) -class TestServiceSet(TestService): +class TestServiceSet(compute_fakes.TestComputev2): def setUp(self): super(TestServiceSet, self).setUp() diff --git a/openstackclient/tests/unit/compute/v2/test_usage.py b/openstackclient/tests/unit/compute/v2/test_usage.py index 9ddc6c82d..ad8a8b504 100644 --- a/openstackclient/tests/unit/compute/v2/test_usage.py +++ b/openstackclient/tests/unit/compute/v2/test_usage.py @@ -22,11 +22,6 @@ class TestUsage(compute_fakes.TestComputev2): def setUp(self): super(TestUsage, self).setUp() - self.app.client_manager.sdk_connection.compute = mock.Mock() - self.compute_sdk_client = ( - self.app.client_manager.sdk_connection.compute - ) - self.projects_mock = self.app.client_manager.identity.projects self.projects_mock.reset_mock() 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 1385b09df..ed6ec12dc 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py @@ -24,16 +24,8 @@ # Tests for Nova network -class TestFloatingIPCompute(compute_fakes.TestComputev2): - def setUp(self): - super(TestFloatingIPCompute, self).setUp() - - # Get a shortcut to the compute client - self.compute_client = self.app.client_manager.compute - - @mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_create') -class TestCreateFloatingIPCompute(TestFloatingIPCompute): +class TestCreateFloatingIPCompute(compute_fakes.TestComputev2): # The floating ip to be deleted. _floating_ip = compute_fakes.create_one_floating_ip() @@ -93,7 +85,7 @@ def test_floating_ip_create_default(self, fip_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_delete') -class TestDeleteFloatingIPCompute(TestFloatingIPCompute): +class TestDeleteFloatingIPCompute(compute_fakes.TestComputev2): # The floating ips to be deleted. _floating_ips = compute_fakes.create_floating_ips(count=2) @@ -169,7 +161,7 @@ def test_floating_ip_delete_multi_exception(self, fip_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_list') -class TestListFloatingIPCompute(TestFloatingIPCompute): +class TestListFloatingIPCompute(compute_fakes.TestComputev2): # The floating ips to be list up _floating_ips = compute_fakes.create_floating_ips(count=3) @@ -215,7 +207,7 @@ def test_floating_ip_list(self, fip_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_find') -class TestShowFloatingIPCompute(TestFloatingIPCompute): +class TestShowFloatingIPCompute(compute_fakes.TestComputev2): # The floating ip to display. _floating_ip = compute_fakes.create_one_floating_ip() 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 289a58bed..cc07d5e69 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 @@ -20,16 +20,8 @@ # Tests for Compute network -class TestFloatingIPPoolCompute(compute_fakes.TestComputev2): - def setUp(self): - super(TestFloatingIPPoolCompute, self).setUp() - - # Get a shortcut to the compute client - self.compute_client = self.app.client_manager.compute - - @mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_pool_list') -class TestListFloatingIPPoolCompute(TestFloatingIPPoolCompute): +class TestListFloatingIPPoolCompute(compute_fakes.TestComputev2): # The floating ip pools to list up _floating_ip_pools = compute_fakes.create_floating_ip_pools(count=3) diff --git a/openstackclient/tests/unit/network/v2/test_network_compute.py b/openstackclient/tests/unit/network/v2/test_network_compute.py index 775ee4e91..ebe37243a 100644 --- a/openstackclient/tests/unit/network/v2/test_network_compute.py +++ b/openstackclient/tests/unit/network/v2/test_network_compute.py @@ -23,16 +23,10 @@ # Tests for Nova network # -class TestNetworkCompute(compute_fakes.TestComputev2): - def setUp(self): - super(TestNetworkCompute, self).setUp() - - # Get a shortcut to the compute client - self.compute_client = self.app.client_manager.compute @mock.patch('openstackclient.api.compute_v2.APIv2.network_create') -class TestCreateNetworkCompute(TestNetworkCompute): +class TestCreateNetworkCompute(compute_fakes.TestComputev2): # The network to create. _network = compute_fakes.create_one_network() @@ -172,7 +166,7 @@ def test_network_create_default_options(self, net_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.network_delete') -class TestDeleteNetworkCompute(TestNetworkCompute): +class TestDeleteNetworkCompute(compute_fakes.TestComputev2): def setUp(self): super(TestDeleteNetworkCompute, self).setUp() @@ -252,7 +246,7 @@ def test_network_delete_multi_with_exception(self, net_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.network_list') -class TestListNetworkCompute(TestNetworkCompute): +class TestListNetworkCompute(compute_fakes.TestComputev2): # The networks going to be listed up. _networks = compute_fakes.create_networks(count=3) @@ -297,7 +291,7 @@ def test_network_list_no_options(self, net_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.network_find') -class TestShowNetworkCompute(TestNetworkCompute): +class TestShowNetworkCompute(compute_fakes.TestComputev2): # The network to show. _network = compute_fakes.create_one_network() 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 06f508592..f9ea640ff 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_compute.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_compute.py @@ -22,16 +22,8 @@ from openstackclient.tests.unit import utils as tests_utils -class TestSecurityGroupCompute(compute_fakes.TestComputev2): - def setUp(self): - super(TestSecurityGroupCompute, self).setUp() - - # Get a shortcut to the compute client - self.compute_client = self.app.client_manager.compute - - @mock.patch('openstackclient.api.compute_v2.APIv2.security_group_create') -class TestCreateSecurityGroupCompute(TestSecurityGroupCompute): +class TestCreateSecurityGroupCompute(compute_fakes.TestComputev2): project = identity_fakes.FakeProject.create_one_project() domain = identity_fakes.FakeDomain.create_one_domain() @@ -110,7 +102,7 @@ def test_security_group_create_all_options(self, sg_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.security_group_delete') -class TestDeleteSecurityGroupCompute(TestSecurityGroupCompute): +class TestDeleteSecurityGroupCompute(compute_fakes.TestComputev2): # The security groups to be deleted. _security_groups = compute_fakes.create_security_groups() @@ -192,7 +184,7 @@ def test_security_group_multi_delete_with_exception(self, sg_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.security_group_list') -class TestListSecurityGroupCompute(TestSecurityGroupCompute): +class TestListSecurityGroupCompute(compute_fakes.TestComputev2): # The security group to be listed. _security_groups = compute_fakes.create_security_groups(count=3) @@ -270,7 +262,7 @@ def test_security_group_list_all_projects(self, sg_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.security_group_set') -class TestSetSecurityGroupCompute(TestSecurityGroupCompute): +class TestSetSecurityGroupCompute(compute_fakes.TestComputev2): # The security group to be set. _security_group = compute_fakes.create_one_security_group() @@ -337,7 +329,7 @@ def test_security_group_set_all_options(self, sg_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.security_group_find') -class TestShowSecurityGroupCompute(TestSecurityGroupCompute): +class TestShowSecurityGroupCompute(compute_fakes.TestComputev2): # The security group rule to be shown with the group. _security_group_rule = compute_fakes.create_one_security_group_rule() 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 1fb4831ec..5fff5b431 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 @@ -23,16 +23,8 @@ from openstackclient.tests.unit import utils as tests_utils -class TestSecurityGroupRuleCompute(compute_fakes.TestComputev2): - def setUp(self): - super(TestSecurityGroupRuleCompute, self).setUp() - - # Get a shortcut to the network client - self.compute_client = self.app.client_manager.compute - - @mock.patch('openstackclient.api.compute_v2.APIv2.security_group_rule_create') -class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): +class TestCreateSecurityGroupRuleCompute(compute_fakes.TestComputev2): project = identity_fakes.FakeProject.create_one_project() domain = identity_fakes.FakeDomain.create_one_domain() @@ -305,7 +297,7 @@ def test_security_group_rule_create_proto_option(self, sgr_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.security_group_rule_delete') -class TestDeleteSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): +class TestDeleteSecurityGroupRuleCompute(compute_fakes.TestComputev2): # The security group rule to be deleted. _security_group_rules = compute_fakes.create_security_group_rules(count=2) @@ -373,7 +365,7 @@ def test_security_group_rule_delete_multi_with_exception(self, sgr_mock): sgr_mock.assert_any_call('unexist_rule') -class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): +class TestListSecurityGroupRuleCompute(compute_fakes.TestComputev2): # The security group to hold the rules. _security_group = compute_fakes.create_one_security_group() @@ -509,7 +501,7 @@ def test_security_group_rule_list_with_ignored_options(self): self.assertEqual(self.expected_data_no_group, list(data)) -class TestShowSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): +class TestShowSecurityGroupRuleCompute(compute_fakes.TestComputev2): # The security group rule to be shown. _security_group_rule = compute_fakes.create_one_security_group_rule() diff --git a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py index c6fbbe58a..91b27eb2c 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py @@ -33,7 +33,6 @@ def setUp(self): self.projects_mock = self.app.client_manager.identity.projects self.projects_mock.reset_mock() - self.compute_client = self.app.client_manager.compute self.servers_mock = self.compute_client.servers self.servers_mock.reset_mock() From c628c2dcd3efb6cde7d6322ce7fa405508e2e487 Mon Sep 17 00:00:00 2001 From: Mridula Joshi Date: Mon, 27 Mar 2023 07:02:15 +0000 Subject: [PATCH 010/403] image: Add support for cache commands Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/874372 Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/874940 Change-Id: I96b95cb93d298602b6d4b0cd35a213478babff5f --- doc/source/cli/data/glance.csv | 8 +- openstackclient/image/v2/cache.py | 218 ++++++++++++++++++ openstackclient/tests/unit/image/v2/fakes.py | 23 ++ .../tests/unit/image/v2/test_cache.py | 214 +++++++++++++++++ .../add-cache-commands-a6f046348a3a0b1f.yaml | 5 + setup.cfg | 6 + 6 files changed, 470 insertions(+), 4 deletions(-) create mode 100644 openstackclient/image/v2/cache.py create mode 100644 openstackclient/tests/unit/image/v2/test_cache.py create mode 100644 releasenotes/notes/add-cache-commands-a6f046348a3a0b1f.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index 1481610ec..c2c324894 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -1,7 +1,7 @@ -cache-clear,,"Clear all images from cache, queue or both." -cache-delete,,Delete image from cache/caching queue. -cache-list,,Get cache state. -cache-queue,,Queue image(s) for caching. +cache-clear,cached image list,"Clear all images from cache, queue or both." +cache-delete,cached image delete,Delete image from cache/caching queue. +cache-list,cached image list,Get cache state. +cache-queue,cached image queue,Queue image(s) for caching. explain,WONTFIX,Describe a specific model. image-create,image create,Create a new image. image-create-via-import, image create --import,"EXPERIMENTAL: Create a new image via image import using glance-direct import method. Missing support for web-download, copy-image and glance-download import methods. The OSC command is also missing support for importing image to specified store as well as all stores (--store, --stores, --all-stores) and skip or stop processing if import fails to one of the store (--allow-failure)" diff --git a/openstackclient/image/v2/cache.py b/openstackclient/image/v2/cache.py new file mode 100644 index 000000000..ebb4e5b20 --- /dev/null +++ b/openstackclient/image/v2/cache.py @@ -0,0 +1,218 @@ +# Copyright 2023 Red Hat. +# All Rights Reserved. +# +# 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 datetime +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__) + + +def _format_image_cache(cached_images): + """Format image cache to make it more consistent with OSC operations.""" + + image_list = [] + for item in cached_images: + if item == "cached_images": + for image in cached_images[item]: + image_obj = copy.deepcopy(image) + image_obj['state'] = 'cached' + image_obj[ + 'last_accessed' + ] = datetime.datetime.utcfromtimestamp( + image['last_accessed'] + ).isoformat() + image_obj[ + 'last_modified' + ] = datetime.datetime.utcfromtimestamp( + image['last_modified'] + ).isoformat() + image_list.append(image_obj) + elif item == "queued_images": + for image in cached_images[item]: + image = {'image_id': image} + image.update( + { + 'state': 'queued', + 'last_accessed': 'N/A', + 'last_modified': 'N/A', + 'size': 'N/A', + 'hits': 'N/A', + } + ) + image_list.append(image) + return image_list + + +class ListCachedImage(command.Lister): + _description = _("Get Cache State") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + # List of Cache data received + data = _format_image_cache(dict(image_client.get_image_cache())) + columns = [ + 'image_id', + 'state', + 'last_accessed', + 'last_modified', + 'size', + 'hits', + ] + column_headers = [ + "ID", + "State", + "Last Accessed (UTC)", + "Last Modified (UTC)", + "Size", + "Hits", + ] + + return ( + column_headers, + ( + utils.get_dict_properties( + image, + columns, + ) + for image in data + ), + ) + + +class QueueCachedImage(command.Command): + _description = _("Queue image(s) for caching.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "images", + metavar="", + nargs="+", + help=_("Image to display (name or ID)"), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + failures = 0 + for image in parsed_args.images: + try: + image_obj = image_client.find_image( + image, + ignore_missing=False, + ) + image_client.queue_image(image_obj.id) + except Exception as e: + failures += 1 + msg = _( + "Failed to queue image with name or " + "ID '%(image)s': %(e)s" + ) + LOG.error(msg, {'image': image, 'e': e}) + + if failures > 0: + total = len(parsed_args.images) + msg = _("Failed to queue %(failures)s of %(total)s images") % { + 'failures': failures, + 'total': total, + } + raise exceptions.CommandError(msg) + + +class DeleteCachedImage(command.Command): + _description = _("Delete image(s) from cache") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "images", + metavar="", + nargs="+", + help=_("Image(s) to delete (name or ID)"), + ) + return parser + + def take_action(self, parsed_args): + failures = 0 + image_client = self.app.client_manager.image + for image in parsed_args.images: + try: + image_obj = image_client.find_image( + image, + ignore_missing=False, + ) + image_client.cache_delete_image(image_obj.id) + except Exception as e: + failures += 1 + msg = _( + "Failed to delete image with name or " + "ID '%(image)s': %(e)s" + ) + LOG.error(msg, {'image': image, 'e': e}) + + if failures > 0: + total = len(parsed_args.images) + msg = _("Failed to delete %(failures)s of %(total)s images.") % { + 'failures': failures, + 'total': total, + } + raise exceptions.CommandError(msg) + + +class ClearCachedImage(command.Command): + _description = _("Clear all images from cache, queue or both") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "--cache", + action="store_const", + const="cache", + dest="target", + help=_("Clears all the cached images"), + ) + parser.add_argument( + "--queue", + action="store_const", + const="queue", + dest="target", + help=_("Clears all the queued images"), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + target = parsed_args.target + try: + image_client.clear_cache(target) + except Exception: + msg = _("Failed to clear image cache") + LOG.error(msg) + raise exceptions.CommandError(msg) diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py index 1fcfb50e9..45af0f47c 100644 --- a/openstackclient/tests/unit/image/v2/fakes.py +++ b/openstackclient/tests/unit/image/v2/fakes.py @@ -17,6 +17,7 @@ import uuid from openstack.image.v2 import _proxy +from openstack.image.v2 import cache from openstack.image.v2 import image from openstack.image.v2 import member from openstack.image.v2 import metadef_namespace @@ -238,6 +239,28 @@ def create_tasks(attrs=None, count=2): return tasks +def create_cache(attrs=None): + attrs = attrs or {} + cache_info = { + 'cached_images': [ + { + 'hits': 0, + 'image_id': '1a56983c-f71f-490b-a7ac-6b321a18935a', + 'last_accessed': 1671699579.444378, + 'last_modified': 1671699579.444378, + 'size': 0, + }, + ], + 'queued_images': [ + '3a4560a1-e585-443e-9b39-553b46ec92d1', + '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', + ], + } + cache_info.update(attrs) + + return cache.Cache(**cache_info) + + def create_one_metadef_namespace(attrs=None): """Create a fake MetadefNamespace member. diff --git a/openstackclient/tests/unit/image/v2/test_cache.py b/openstackclient/tests/unit/image/v2/test_cache.py new file mode 100644 index 000000000..abb0b3732 --- /dev/null +++ b/openstackclient/tests/unit/image/v2/test_cache.py @@ -0,0 +1,214 @@ +# Copyright 2023 Red Hat. +# All Rights Reserved. +# +# 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.mock import call + +from openstack import exceptions as sdk_exceptions +from osc_lib import exceptions + +from openstackclient.image.v2 import cache +from openstackclient.tests.unit.image.v2 import fakes + + +class TestCacheList(fakes.TestImagev2): + _cache = fakes.create_cache() + columns = [ + "ID", + "State", + "Last Accessed (UTC)", + "Last Modified (UTC)", + "Size", + "Hits", + ] + + cache_list = cache._format_image_cache(dict(fakes.create_cache())) + datalist = ( + ( + image['image_id'], + image['state'], + image['last_accessed'], + image['last_modified'], + image['size'], + image['hits'], + ) + for image in cache_list + ) + + def setUp(self): + super().setUp() + + # Get the command object to test + self.image_client.get_image_cache.return_value = self._cache + self.cmd = cache.ListCachedImage(self.app, None) + + def test_image_cache_list(self): + arglist = [] + parsed_args = self.check_parser(self.cmd, arglist, []) + columns, data = self.cmd.take_action(parsed_args) + + self.image_client.get_image_cache.assert_called() + self.assertEqual(self.columns, columns) + self.assertEqual(tuple(self.datalist), tuple(data)) + + +class TestQueueCache(fakes.TestImagev2): + def setUp(self): + super().setUp() + + self.image_client.queue_image.return_value = None + self.cmd = cache.QueueCachedImage(self.app, None) + + def test_cache_queue(self): + images = fakes.create_images(count=1) + arglist = [ + images[0].id, + ] + + verifylist = [ + ('images', [images[0].id]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.image_client.find_image.side_effect = images + + self.cmd.take_action(parsed_args) + + self.image_client.queue_image.assert_called_once_with(images[0].id) + + def test_cache_queue_multiple_images(self): + images = fakes.create_images(count=3) + arglist = [i.id for i in images] + + verifylist = [ + ('images', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.image_client.find_image.side_effect = images + + self.cmd.take_action(parsed_args) + calls = [call(i.id) for i in images] + self.image_client.queue_image.assert_has_calls(calls) + + +class TestCacheDelete(fakes.TestImagev2): + def setUp(self): + super().setUp() + + self.image_client.cache_delete_image.return_value = None + self.cmd = cache.DeleteCachedImage(self.app, None) + + def test_cache_delete(self): + images = fakes.create_images(count=1) + arglist = [ + images[0].id, + ] + + verifylist = [ + ('images', [images[0].id]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.image_client.find_image.side_effect = images + + self.cmd.take_action(parsed_args) + + self.image_client.find_image.assert_called_once_with( + images[0].id, ignore_missing=False + ) + self.image_client.cache_delete_image.assert_called_once_with( + images[0].id + ) + + def test_cache_delete_multiple_images(self): + images = fakes.create_images(count=3) + arglist = [i.id for i in images] + + verifylist = [ + ('images', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.image_client.find_image.side_effect = images + + self.cmd.take_action(parsed_args) + calls = [call(i.id) for i in images] + self.image_client.cache_delete_image.assert_has_calls(calls) + + def test_cache_delete_multiple_images_exception(self): + images = fakes.create_images(count=2) + arglist = [ + images[0].id, + images[1].id, + 'x-y-x', + ] + verifylist = [ + ('images', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + ret_find = [images[0], images[1], sdk_exceptions.ResourceNotFound()] + + self.image_client.find_image.side_effect = ret_find + + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + calls = [call(i.id) for i in images] + self.image_client.cache_delete_image.assert_has_calls(calls) + + +class TestCacheClear(fakes.TestImagev2): + def setUp(self): + super().setUp() + + self.image_client.clear_cache.return_value = None + self.cmd = cache.ClearCachedImage(self.app, None) + + def test_cache_clear_no_option(self): + arglist = [] + + verifylist = [('target', None)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.assertIsNone( + self.image_client.clear_cache.assert_called_with(None) + ) + + def test_cache_clear_queue_option(self): + arglist = ['--queue'] + + verifylist = [('target', 'queue')] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.image_client.clear_cache.assert_called_once_with('queue') + + def test_cache_clear_cache_option(self): + arglist = ['--cache'] + + verifylist = [('target', 'cache')] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.cmd.take_action(parsed_args) + + self.image_client.clear_cache.assert_called_once_with('cache') diff --git a/releasenotes/notes/add-cache-commands-a6f046348a3a0b1f.yaml b/releasenotes/notes/add-cache-commands-a6f046348a3a0b1f.yaml new file mode 100644 index 000000000..81243cfa9 --- /dev/null +++ b/releasenotes/notes/add-cache-commands-a6f046348a3a0b1f.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add commands for the image Cache API, to list, queue, + delete and clear images in the cache. diff --git a/setup.cfg b/setup.cfg index de1350756..dc770b006 100644 --- a/setup.cfg +++ b/setup.cfg @@ -399,6 +399,12 @@ openstack.image.v2 = image_metadef_resource_type_list = openstackclient.image.v2.metadef_resource_types:ListMetadefResourceTypes + 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 From 39a084f91cd580be6adf46e8e752233112f6ec74 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Wed, 20 Sep 2023 23:41:39 +0530 Subject: [PATCH 011/403] Migrate volume backend commands to SDK This patch migrates following volume backend commands to SDK: volume backend capability show volume backend pool list Change-Id: Idded38a5f87d51574426c4ac10c806af52984b7c --- openstackclient/tests/unit/volume/v2/fakes.py | 22 +++++++------ .../unit/volume/v2/test_volume_backend.py | 32 ++++++------------- openstackclient/volume/v2/volume_backend.py | 28 ++++++---------- ...ume-backend-commands-259e553e213c71b0.yaml | 7 ++++ 4 files changed, 40 insertions(+), 49 deletions(-) create mode 100644 releasenotes/notes/migrate-volume-backend-commands-259e553e213c71b0.yaml diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index 2af5cf0de..4d04fcdbf 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -21,6 +21,8 @@ from cinderclient import api_versions from openstack.block_storage.v2 import _proxy as block_storage_v2_proxy from openstack.block_storage.v3 import backup as _backup +from openstack.block_storage.v3 import capabilities as _capabilities +from openstack.block_storage.v3 import stats as _stats from openstack.block_storage.v3 import volume as _volume from openstack.image.v2 import _proxy as image_v2_proxy from osc_lib.cli import format_columns @@ -301,7 +303,7 @@ def create_one_capability(attrs=None): # Overwrite default attributes if there are some attributes set capability_info.update(attrs or {}) - capability = fakes.FakeResource(None, capability_info, loaded=True) + capability = _capabilities.Capabilities(**capability_info) return capability @@ -317,19 +319,21 @@ def create_one_pool(attrs=None): # Set default attribute pool_info = { 'name': 'host@lvmdriver-1#lvmdriver-1', - 'storage_protocol': 'iSCSI', - 'thick_provisioning_support': False, - 'thin_provisioning_support': True, - 'total_volumes': 99, - 'total_capacity_gb': 1000.00, - 'allocated_capacity_gb': 100, - 'max_over_subscription_ratio': 200.0, + 'capabilities': { + 'storage_protocol': 'iSCSI', + 'thick_provisioning_support': False, + 'thin_provisioning_support': True, + 'total_volumes': 99, + 'total_capacity_gb': 1000.00, + 'allocated_capacity_gb': 100, + 'max_over_subscription_ratio': 200.0, + }, } # Overwrite default attributes if there are some attributes set pool_info.update(attrs or {}) - pool = fakes.FakeResource(None, pool_info, loaded=True) + pool = _stats.Pools(**pool_info) return pool diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backend.py b/openstackclient/tests/unit/volume/v2/test_volume_backend.py index 1652581b7..8a11f01c5 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backend.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backend.py @@ -12,6 +12,8 @@ # under the License. # +from osc_lib.cli import format_columns + from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes from openstackclient.volume.v2 import volume_backend @@ -25,9 +27,8 @@ class TestShowVolumeCapability(volume_fakes.TestVolume): def setUp(self): super().setUp() - # Get a shortcut to the capability Mock - self.capability_mock = self.volume_client.capabilities - self.capability_mock.get.return_value = self.capability + # Assign return value to capabilities mock + self.volume_sdk_client.get_capabilities.return_value = self.capability # Get the command object to test self.cmd = volume_backend.ShowCapability(self.app, None) @@ -68,7 +69,7 @@ def test_capability_show(self): self.assertIn(cap[0], capabilities) # checking if proper call was made to get capabilities - self.capability_mock.get.assert_called_with( + self.volume_sdk_client.get_capabilities.assert_called_with( 'fake', ) @@ -82,8 +83,7 @@ class TestListVolumePool(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.pool_mock = self.volume_client.pools - self.pool_mock.list.return_value = [self.pools] + self.volume_sdk_client.backend_pools.return_value = [self.pools] # Get the command object to test self.cmd = volume_backend.ListPool(self.app, None) @@ -111,7 +111,7 @@ def test_pool_list(self): self.assertEqual(datalist, tuple(data)) # checking if proper call was made to list pools - self.pool_mock.list.assert_called_with( + self.volume_sdk_client.backend_pools.assert_called_with( detailed=False, ) @@ -131,13 +131,7 @@ def test_service_list_with_long_option(self): expected_columns = [ 'Name', - 'Protocol', - 'Thick', - 'Thin', - 'Volumes', - 'Capacity', - 'Allocated', - 'Max Over Ratio', + 'Capabilities', ] # confirming if all expected columns are present in the result. @@ -146,19 +140,13 @@ def test_service_list_with_long_option(self): datalist = ( ( self.pools.name, - self.pools.storage_protocol, - self.pools.thick_provisioning_support, - self.pools.thin_provisioning_support, - self.pools.total_volumes, - self.pools.total_capacity_gb, - self.pools.allocated_capacity_gb, - self.pools.max_over_subscription_ratio, + format_columns.DictColumn(self.pools.capabilities), ), ) # confirming if all expected values are present in the result. self.assertEqual(datalist, tuple(data)) - self.pool_mock.list.assert_called_with( + self.volume_sdk_client.backend_pools.assert_called_with( detailed=True, ) diff --git a/openstackclient/volume/v2/volume_backend.py b/openstackclient/volume/v2/volume_backend.py index 5e9eaf485..a0125032c 100644 --- a/openstackclient/volume/v2/volume_backend.py +++ b/openstackclient/volume/v2/volume_backend.py @@ -14,6 +14,7 @@ """Storage backend action implementations""" +from osc_lib.cli import format_columns from osc_lib.command import command from osc_lib import utils @@ -33,7 +34,7 @@ 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 columns = [ 'Title', @@ -42,7 +43,7 @@ def take_action(self, parsed_args): 'Description', ] - data = volume_client.capabilities.get(parsed_args.host) + data = volume_client.get_capabilities(parsed_args.host) # The get capabilities API is... interesting. We only want the names of # the capabilities that can set for a backend through extra specs, so @@ -83,28 +84,17 @@ 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 parsed_args.long: columns = [ 'name', - 'storage_protocol', - 'thick_provisioning_support', - 'thin_provisioning_support', - 'total_volumes', - 'total_capacity_gb', - 'allocated_capacity_gb', - 'max_over_subscription_ratio', + 'capabilities', ] + headers = [ 'Name', - 'Protocol', - 'Thick', - 'Thin', - 'Volumes', - 'Capacity', - 'Allocated', - 'Max Over Ratio', + 'Capabilities', ] else: columns = [ @@ -112,13 +102,15 @@ def take_action(self, parsed_args): ] headers = columns - data = volume_client.pools.list(detailed=parsed_args.long) + data = volume_client.backend_pools(detailed=parsed_args.long) + formatters = {'capabilities': format_columns.DictColumn} return ( headers, ( utils.get_item_properties( s, columns, + formatters=formatters, ) for s in data ), diff --git a/releasenotes/notes/migrate-volume-backend-commands-259e553e213c71b0.yaml b/releasenotes/notes/migrate-volume-backend-commands-259e553e213c71b0.yaml new file mode 100644 index 000000000..266f69565 --- /dev/null +++ b/releasenotes/notes/migrate-volume-backend-commands-259e553e213c71b0.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Migrated following volume backends commands to SDK. + + * Capability Show + * Pool List From 5fb922e46953963ffbdadeef3e4854e7ae3c37b5 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Mon, 9 Oct 2023 23:33:02 +0000 Subject: [PATCH 012/403] Add "image metadef property list" command. Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/857727 Change-Id: I8f6692e779c4721225052d348b4b947a545bd6c0 --- doc/source/cli/data/glance.csv | 2 +- .../image/v2/metadef_properties.py | 46 +++++++++++++++++++ openstackclient/tests/unit/image/v2/fakes.py | 15 ++++++ .../unit/image/v2/test_metadef_properties.py | 42 +++++++++++++++++ ...etadef-property-list-fe89ae8ff9780002.yaml | 5 ++ setup.cfg | 2 + 6 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 openstackclient/image/v2/metadef_properties.py create mode 100644 openstackclient/tests/unit/image/v2/test_metadef_properties.py create mode 100644 releasenotes/notes/add-metadef-property-list-fe89ae8ff9780002.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index 1481610ec..a8b342867 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -40,7 +40,7 @@ md-object-show,,Describe a specific metadata definitions object inside a namespa md-object-update,,Update metadata definitions object inside a namespace. md-property-create,,Create a new metadata definitions property inside a namespace. md-property-delete,,Delete a specific metadata definitions property inside a namespace. -md-property-list,,List metadata definitions properties inside a specific namespace. +md-property-list,image metadef property list,List metadata definitions properties inside a specific namespace. md-property-show,,Describe a specific metadata definitions property inside a namespace. md-property-update,,Update metadata definitions property inside a namespace. md-resource-type-associate,,Associate resource type with a metadata definitions namespace. diff --git a/openstackclient/image/v2/metadef_properties.py b/openstackclient/image/v2/metadef_properties.py new file mode 100644 index 000000000..89452667f --- /dev/null +++ b/openstackclient/image/v2/metadef_properties.py @@ -0,0 +1,46 @@ +# Copyright 2023 Red Hat, 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 osc_lib.command import command +from osc_lib import utils + +from openstackclient.i18n import _ + + +class ListMetadefProperties(command.Lister): + _description = _("List metadef properties") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "namespace_name", + metavar="", + help=_("An identifier (a name) for the namespace"), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + props = image_client.metadef_properties(parsed_args.namespace_name) + columns = ['name', 'title', 'type'] + return ( + columns, + ( + utils.get_item_properties( + prop, + columns, + ) + for prop in props + ), + ) diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py index 1fcfb50e9..a63d4c525 100644 --- a/openstackclient/tests/unit/image/v2/fakes.py +++ b/openstackclient/tests/unit/image/v2/fakes.py @@ -20,6 +20,7 @@ from openstack.image.v2 import image from openstack.image.v2 import member from openstack.image.v2 import metadef_namespace +from openstack.image.v2 import metadef_property from openstack.image.v2 import metadef_resource_type from openstack.image.v2 import service_info as _service_info from openstack.image.v2 import task @@ -263,6 +264,20 @@ def create_one_metadef_namespace(attrs=None): return metadef_namespace.MetadefNamespace(**metadef_namespace_list) +def create_one_metadef_property(attrs=None): + attrs = attrs or {} + + metadef_property_list = { + 'name': 'cpu_cores', + 'title': 'vCPU Cores', + 'type': 'integer', + } + + # Overwrite default attributes if there are some attributes set + metadef_property_list.update(attrs) + return metadef_property.MetadefProperty(**metadef_property_list) + + def create_one_resource_type(attrs=None): """Create a fake MetadefResourceType member. diff --git a/openstackclient/tests/unit/image/v2/test_metadef_properties.py b/openstackclient/tests/unit/image/v2/test_metadef_properties.py new file mode 100644 index 000000000..57b1bb6cd --- /dev/null +++ b/openstackclient/tests/unit/image/v2/test_metadef_properties.py @@ -0,0 +1,42 @@ +# Copyright 2023 Red Hat, 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 openstackclient.image.v2 import metadef_properties +from openstackclient.tests.unit.image.v2 import fakes as image_fakes + + +class TestMetadefPropertyList(image_fakes.TestImagev2): + _metadef_property = [image_fakes.create_one_metadef_property()] + + columns = ['name', 'title', 'type'] + + def setUp(self): + super().setUp() + + self.image_client.metadef_properties.side_effect = [ + self._metadef_property, + [], + ] + + self.cmd = metadef_properties.ListMetadefProperties(self.app, None) + self.datalist = self._metadef_property + + def test_metadef_property_list(self): + arglist = ['my-namespace'] + parsed_args = self.check_parser(self.cmd, arglist, []) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(getattr(self.datalist[0], 'name'), next(data)[0]) diff --git a/releasenotes/notes/add-metadef-property-list-fe89ae8ff9780002.yaml b/releasenotes/notes/add-metadef-property-list-fe89ae8ff9780002.yaml new file mode 100644 index 000000000..643c2c5e3 --- /dev/null +++ b/releasenotes/notes/add-metadef-property-list-fe89ae8ff9780002.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``image metadef property list`` command to list the + metadata definitions properties inside a specific namespace. diff --git a/setup.cfg b/setup.cfg index 26b5ae309..3c994fd0e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -397,6 +397,8 @@ openstack.image.v2 = image_metadef_namespace_set = openstackclient.image.v2.metadef_namespaces:SetMetadefNameSpace image_metadef_namespace_show = openstackclient.image.v2.metadef_namespaces:ShowMetadefNameSpace + image_metadef_property_list = openstackclient.image.v2.metadef_properties:ListMetadefProperties + image_metadef_resource_type_list = openstackclient.image.v2.metadef_resource_types:ListMetadefResourceTypes openstack.network.v2 = From 190f06a9633f2804a0aeff5d3effac8b7255d26c Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Thu, 12 Oct 2023 02:51:17 +0000 Subject: [PATCH 013/403] Add "image metadef property show" command Change-Id: I326735014dfcb4477b109a99aa31f71b07b91c7b --- doc/source/cli/data/glance.csv | 2 +- .../image/v2/metadef_properties.py | 52 +++++++++++++++++++ .../unit/image/v2/test_metadef_properties.py | 33 ++++++++++++ ...etadef-property-show-8bf2ec421f74cb2d.yaml | 5 ++ setup.cfg | 1 + 5 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-metadef-property-show-8bf2ec421f74cb2d.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index a8b342867..5cd78e5a5 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -41,7 +41,7 @@ md-object-update,,Update metadata definitions object inside a namespace. md-property-create,,Create a new metadata definitions property inside a namespace. md-property-delete,,Delete a specific metadata definitions property inside a namespace. md-property-list,image metadef property list,List metadata definitions properties inside a specific namespace. -md-property-show,,Describe a specific metadata definitions property inside a namespace. +md-property-show,image metadef property show,Describe a specific metadata definitions property inside a namespace. md-property-update,,Update metadata definitions property inside a namespace. md-resource-type-associate,,Associate resource type with a metadata definitions namespace. md-resource-type-deassociate,,Deassociate resource type with a metadata definitions namespace. diff --git a/openstackclient/image/v2/metadef_properties.py b/openstackclient/image/v2/metadef_properties.py index 89452667f..bf34964bb 100644 --- a/openstackclient/image/v2/metadef_properties.py +++ b/openstackclient/image/v2/metadef_properties.py @@ -44,3 +44,55 @@ def take_action(self, parsed_args): for prop in props ), ) + + +class ShowMetadefProperty(command.ShowOne): + _description = _("Describe a specific property from a namespace") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "namespace_name", + metavar="", + help=_("Namespace (name) for the namespace"), + ) + parser.add_argument( + "property_name", + metavar="", + help=_("Property to show"), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + data = image_client.get_metadef_property( + parsed_args.property_name, parsed_args.namespace_name + ) + data = data.to_dict(ignore_none=True, original_names=True) + info = { + key: data[key] + for key in [ + 'namespace_name', + 'name', + 'type', + 'title', + 'description', + 'operators', + 'default', + 'is_readonly', + 'minimum', + 'maximum', + 'enum', + 'pattern', + 'min_length', + 'max_length', + 'items', + 'require_unique_items', + 'min_items', + 'max_items', + 'allow_additional_items', + ] + if key in data + } + + return zip(*sorted(info.items())) diff --git a/openstackclient/tests/unit/image/v2/test_metadef_properties.py b/openstackclient/tests/unit/image/v2/test_metadef_properties.py index 57b1bb6cd..c3c8de961 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_properties.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_properties.py @@ -40,3 +40,36 @@ def test_metadef_property_list(self): self.assertEqual(self.columns, columns) self.assertEqual(getattr(self.datalist[0], 'name'), next(data)[0]) + + +class TestMetadefPropertyShow(image_fakes.TestImagev2): + _metadef_property = image_fakes.create_one_metadef_property() + + expected_columns = ( + 'name', + 'title', + 'type', + ) + expected_data = ( + _metadef_property.name, + _metadef_property.title, + _metadef_property.type, + ) + + def setUp(self): + super().setUp() + + self.image_client.get_metadef_property.return_value = ( + self._metadef_property + ) + + self.cmd = metadef_properties.ShowMetadefProperty(self.app, None) + + def test_metadef_property_show(self): + arglist = ['my-namespace', 'my-property'] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) diff --git a/releasenotes/notes/add-metadef-property-show-8bf2ec421f74cb2d.yaml b/releasenotes/notes/add-metadef-property-show-8bf2ec421f74cb2d.yaml new file mode 100644 index 000000000..9411a533c --- /dev/null +++ b/releasenotes/notes/add-metadef-property-show-8bf2ec421f74cb2d.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``image metadef property show`` command to show details + about a metadef property inside a specific namespace. diff --git a/setup.cfg b/setup.cfg index 3c994fd0e..48781ffd3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -398,6 +398,7 @@ openstack.image.v2 = image_metadef_namespace_show = openstackclient.image.v2.metadef_namespaces:ShowMetadefNameSpace image_metadef_property_list = openstackclient.image.v2.metadef_properties:ListMetadefProperties + image_metadef_property_show = openstackclient.image.v2.metadef_properties:ShowMetadefProperty image_metadef_resource_type_list = openstackclient.image.v2.metadef_resource_types:ListMetadefResourceTypes From d9c4c43a409977ed593169011b103b618302cc08 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Sun, 15 Oct 2023 20:21:38 +0000 Subject: [PATCH 014/403] Add "image metadef property create" command Change-Id: Icb4fab0aef13b28212771da3a3b7c4a0775bb38e --- doc/source/cli/data/glance.csv | 2 +- .../image/v2/metadef_properties.py | 116 ++++++++++++++---- .../unit/image/v2/test_metadef_properties.py | 69 +++++++++++ ...adef-property-create-c9a4ec2bced892af.yaml | 5 + setup.cfg | 1 + 5 files changed, 166 insertions(+), 27 deletions(-) create mode 100644 releasenotes/notes/add-metadef-property-create-c9a4ec2bced892af.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index 5cd78e5a5..90f7aeb15 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -38,7 +38,7 @@ md-object-list,,List metadata definitions objects inside a specific namespace. md-object-property-show,,Describe a specific metadata definitions property inside an object. md-object-show,,Describe a specific metadata definitions object inside a namespace. md-object-update,,Update metadata definitions object inside a namespace. -md-property-create,,Create a new metadata definitions property inside a namespace. +md-property-create,image metadef property create,Create a new metadata definitions property inside a namespace. md-property-delete,,Delete a specific metadata definitions property inside a namespace. md-property-list,image metadef property list,List metadata definitions properties inside a specific namespace. md-property-show,image metadef property show,Describe a specific metadata definitions property inside a namespace. diff --git a/openstackclient/image/v2/metadef_properties.py b/openstackclient/image/v2/metadef_properties.py index bf34964bb..2de7ac00b 100644 --- a/openstackclient/image/v2/metadef_properties.py +++ b/openstackclient/image/v2/metadef_properties.py @@ -12,12 +12,101 @@ # License for the specific language governing permissions and limitations # under the License. +import json + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils from openstackclient.i18n import _ +def _format_property(prop): + prop = prop.to_dict(ignore_none=True, original_names=True) + return { + key: prop[key] + for key in [ + 'namespace_name', + 'name', + 'type', + 'title', + 'description', + 'operators', + 'default', + 'is_readonly', + 'minimum', + 'maximum', + 'enum', + 'pattern', + 'min_length', + 'max_length', + 'items', + 'require_unique_items', + 'min_items', + 'max_items', + 'allow_additional_items', + ] + if key in prop + } + + +class CreateMetadefProperty(command.ShowOne): + _description = _("Create a metadef property") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "--name", + required=True, + help=_("Internal name of the property"), + ) + parser.add_argument( + "--title", + required=True, + help=_("Property name displayed to the user"), + ) + parser.add_argument( + "--type", + required=True, + help=_("Property type"), + ) + parser.add_argument( + "--schema", + required=True, + help=_("Valid JSON schema of the property"), + ) + parser.add_argument( + "namespace_name", + help=_("Name of namespace the property will belong."), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + kwargs = { + 'name': parsed_args.name, + 'title': parsed_args.title, + 'type': parsed_args.type, + } + try: + kwargs.update(json.loads(parsed_args.schema)) + except json.JSONDecodeError as e: + raise exceptions.CommandError( + _("Failed to load JSON schema: %(e)s") + % { + 'e': e, + } + ) + + data = image_client.create_metadef_property( + parsed_args.namespace_name, **kwargs + ) + info = _format_property(data) + + return zip(*sorted(info.items())) + + class ListMetadefProperties(command.Lister): _description = _("List metadef properties") @@ -68,31 +157,6 @@ def take_action(self, parsed_args): data = image_client.get_metadef_property( parsed_args.property_name, parsed_args.namespace_name ) - data = data.to_dict(ignore_none=True, original_names=True) - info = { - key: data[key] - for key in [ - 'namespace_name', - 'name', - 'type', - 'title', - 'description', - 'operators', - 'default', - 'is_readonly', - 'minimum', - 'maximum', - 'enum', - 'pattern', - 'min_length', - 'max_length', - 'items', - 'require_unique_items', - 'min_items', - 'max_items', - 'allow_additional_items', - ] - if key in data - } + info = _format_property(data) return zip(*sorted(info.items())) diff --git a/openstackclient/tests/unit/image/v2/test_metadef_properties.py b/openstackclient/tests/unit/image/v2/test_metadef_properties.py index c3c8de961..b1fad7f6c 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_properties.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_properties.py @@ -12,8 +12,77 @@ # License for the specific language governing permissions and limitations # under the License. +from osc_lib import exceptions + from openstackclient.image.v2 import metadef_properties from openstackclient.tests.unit.image.v2 import fakes as image_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestMetadefPropertyCreate(image_fakes.TestImagev2): + _metadef_namespace = image_fakes.create_one_metadef_namespace() + _metadef_property = image_fakes.create_one_metadef_property() + expected_columns = ( + 'name', + 'title', + 'type', + ) + expected_data = ( + _metadef_property.name, + _metadef_property.title, + _metadef_property.type, + ) + + def setUp(self): + super().setUp() + self.image_client.create_metadef_property.return_value = ( + self._metadef_property + ) + self.cmd = metadef_properties.CreateMetadefProperty(self.app, None) + + def test_metadef_property_create_missing_arguments(self): + self.assertRaises( + tests_utils.ParserException, self.check_parser, self.cmd, [], [] + ) + + def test_metadef_property_create(self): + arglist = [ + '--name', + 'cpu_cores', + '--schema', + '{}', + '--title', + 'vCPU Cores', + '--type', + 'integer', + self._metadef_namespace.namespace, + ] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_metadef_property_create_invalid_schema(self): + arglist = [ + '--name', + 'cpu_cores', + '--schema', + '{invalid}', + '--title', + 'vCPU Cores', + '--type', + 'integer', + self._metadef_namespace.namespace, + ] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) class TestMetadefPropertyList(image_fakes.TestImagev2): diff --git a/releasenotes/notes/add-metadef-property-create-c9a4ec2bced892af.yaml b/releasenotes/notes/add-metadef-property-create-c9a4ec2bced892af.yaml new file mode 100644 index 000000000..11862e167 --- /dev/null +++ b/releasenotes/notes/add-metadef-property-create-c9a4ec2bced892af.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``image metadef property create`` command to create a new + metadef property inside a specific namespace. diff --git a/setup.cfg b/setup.cfg index 48781ffd3..0a579a796 100644 --- a/setup.cfg +++ b/setup.cfg @@ -397,6 +397,7 @@ openstack.image.v2 = image_metadef_namespace_set = openstackclient.image.v2.metadef_namespaces:SetMetadefNameSpace image_metadef_namespace_show = openstackclient.image.v2.metadef_namespaces:ShowMetadefNameSpace + image_metadef_property_create = openstackclient.image.v2.metadef_properties:CreateMetadefProperty image_metadef_property_list = openstackclient.image.v2.metadef_properties:ListMetadefProperties image_metadef_property_show = openstackclient.image.v2.metadef_properties:ShowMetadefProperty From 9094e540b582073f62635c64d762b3a19db7a8e5 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Mon, 16 Oct 2023 11:27:54 +0000 Subject: [PATCH 015/403] Add "image metadef property delete" command Change-Id: I2e13b26139424c421ae609804c546a6b42add5a4 --- doc/source/cli/data/glance.csv | 2 +- .../image/v2/metadef_properties.py | 38 ++++++++++++++ .../unit/image/v2/test_metadef_properties.py | 49 +++++++++++++++++++ ...adef-property-delete-ebb999d92a588ad4.yaml | 5 ++ setup.cfg | 1 + 5 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-metadef-property-delete-ebb999d92a588ad4.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index 90f7aeb15..706a894ba 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -39,7 +39,7 @@ md-object-property-show,,Describe a specific metadata definitions property insid md-object-show,,Describe a specific metadata definitions object inside a namespace. md-object-update,,Update metadata definitions object inside a namespace. md-property-create,image metadef property create,Create a new metadata definitions property inside a namespace. -md-property-delete,,Delete a specific metadata definitions property inside a namespace. +md-property-delete,image metadef property delete,Delete a specific metadata definitions property inside a namespace. md-property-list,image metadef property list,List metadata definitions properties inside a specific namespace. md-property-show,image metadef property show,Describe a specific metadata definitions property inside a namespace. md-property-update,,Update metadata definitions property inside a namespace. diff --git a/openstackclient/image/v2/metadef_properties.py b/openstackclient/image/v2/metadef_properties.py index 2de7ac00b..007e018ae 100644 --- a/openstackclient/image/v2/metadef_properties.py +++ b/openstackclient/image/v2/metadef_properties.py @@ -107,6 +107,44 @@ def take_action(self, parsed_args): return zip(*sorted(info.items())) +class DeleteMetadefProperty(command.Command): + _description = _("Delete a metadef property") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "namespace_name", + help=_("An identifier (a name) for the namespace"), + ) + parser.add_argument( + "property_name", + help=_("Property to delete"), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + try: + image_client.delete_metadef_property( + parsed_args.property_name, + parsed_args.namespace_name, + ignore_missing=False, + ) + except Exception as e: + raise exceptions.CommandError( + _( + "Failed to delete property with name or " + "ID '%(property)s' from namespace '%(namespace)s': %(e)s" + ) + % { + 'property': parsed_args.property_name, + 'namespace': parsed_args.namespace_name, + 'e': e, + } + ) + + class ListMetadefProperties(command.Lister): _description = _("List metadef properties") diff --git a/openstackclient/tests/unit/image/v2/test_metadef_properties.py b/openstackclient/tests/unit/image/v2/test_metadef_properties.py index b1fad7f6c..fa59c29cd 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_properties.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_properties.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +from openstack import exceptions as sdk_exceptions from osc_lib import exceptions from openstackclient.image.v2 import metadef_properties @@ -85,6 +86,54 @@ def test_metadef_property_create_invalid_schema(self): ) +class TestMetadefPropertyDelete(image_fakes.TestImagev2): + def setUp(self): + super().setUp() + + self.cmd = metadef_properties.DeleteMetadefProperty(self.app, None) + + def test_metadef_property_delete(self): + arglist = ['namespace', 'property'] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + def test_metadef_property_delete_missing_arguments(self): + arglist = [] + self.assertRaises( + tests_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + [], + ) + + arglist = ['namespace'] + self.assertRaises( + tests_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + [], + ) + + 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 + ) + + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + + class TestMetadefPropertyList(image_fakes.TestImagev2): _metadef_property = [image_fakes.create_one_metadef_property()] diff --git a/releasenotes/notes/add-metadef-property-delete-ebb999d92a588ad4.yaml b/releasenotes/notes/add-metadef-property-delete-ebb999d92a588ad4.yaml new file mode 100644 index 000000000..69e43dd00 --- /dev/null +++ b/releasenotes/notes/add-metadef-property-delete-ebb999d92a588ad4.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``image metadef property delete`` command to delete a + metadef property inside a specific namespace. diff --git a/setup.cfg b/setup.cfg index 0a579a796..9f1f254e6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -398,6 +398,7 @@ openstack.image.v2 = image_metadef_namespace_show = openstackclient.image.v2.metadef_namespaces:ShowMetadefNameSpace 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_show = openstackclient.image.v2.metadef_properties:ShowMetadefProperty From 705ecef7a949ef6df0401fe98b66d1c8d96de1d6 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Wed, 18 Oct 2023 11:55:49 +0000 Subject: [PATCH 016/403] Add "image metadef property set" command Change-Id: I8fbe8ef5b5119fb500df0ed1b6e645ea00eadf01 --- doc/source/cli/data/glance.csv | 2 +- .../image/v2/metadef_properties.py | 62 +++++++++++++++++++ .../unit/image/v2/test_metadef_properties.py | 34 ++++++++++ ...metadef-property-set-ab9cdcb73adf6397.yaml | 5 ++ setup.cfg | 1 + 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-metadef-property-set-ab9cdcb73adf6397.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index 706a894ba..5b1bef0b2 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -42,7 +42,7 @@ md-property-create,image metadef property create,Create a new metadata definitio md-property-delete,image metadef property delete,Delete a specific metadata definitions property inside a namespace. md-property-list,image metadef property list,List metadata definitions properties inside a specific namespace. md-property-show,image metadef property show,Describe a specific metadata definitions property inside a namespace. -md-property-update,,Update metadata definitions property inside a namespace. +md-property-update,image metadef property set,Update metadata definitions property inside a namespace. md-resource-type-associate,,Associate resource type with a metadata definitions namespace. md-resource-type-deassociate,,Deassociate resource type with a metadata definitions namespace. md-resource-type-list,,List available resource type names. diff --git a/openstackclient/image/v2/metadef_properties.py b/openstackclient/image/v2/metadef_properties.py index 007e018ae..d2fa4abd4 100644 --- a/openstackclient/image/v2/metadef_properties.py +++ b/openstackclient/image/v2/metadef_properties.py @@ -173,6 +173,68 @@ def take_action(self, parsed_args): ) +class SetMetadefProperty(command.Command): + _description = _("Update metadef namespace property") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "--name", + help=_("Internal name of the property"), + ) + parser.add_argument( + "--title", + help=_("Property name displayed to the user"), + ) + parser.add_argument( + "--type", + help=_("Property type"), + ) + parser.add_argument( + "--schema", + help=_("Valid JSON schema of the property"), + ) + parser.add_argument( + "namespace_name", + help=_("Namespace of the namespace to which the property belongs"), + ) + parser.add_argument( + "property_name", + help=_("Property to update"), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + # We need to pass the values for *all* attributes as kwargs to + # update_metadef_property(), otherwise the attributes that are not + # listed will be reset. + data = image_client.get_metadef_property( + parsed_args.property_name, parsed_args.namespace_name + ) + kwargs = _format_property(data) + for key in ['name', 'title', 'type']: + argument = getattr(parsed_args, key, None) + if argument is not None: + kwargs[key] = argument + + if parsed_args.schema: + try: + kwargs.update(json.loads(parsed_args.schema)) + except json.JSONDecodeError as e: + raise exceptions.CommandError( + _("Failed to load JSON schema: %(e)s") + % { + 'e': e, + } + ) + + image_client.update_metadef_property( + parsed_args.property_name, parsed_args.namespace_name, **kwargs + ) + + class ShowMetadefProperty(command.ShowOne): _description = _("Describe a specific property from a namespace") diff --git a/openstackclient/tests/unit/image/v2/test_metadef_properties.py b/openstackclient/tests/unit/image/v2/test_metadef_properties.py index fa59c29cd..274ed6635 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_properties.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_properties.py @@ -160,6 +160,40 @@ def test_metadef_property_list(self): self.assertEqual(getattr(self.datalist[0], 'name'), next(data)[0]) +class TestMetadefPropertySet(image_fakes.TestImagev2): + _metadef_property = image_fakes.create_one_metadef_property() + + def setUp(self): + super().setUp() + + self.cmd = metadef_properties.SetMetadefProperty(self.app, None) + self.image_client.get_metadef_property.return_value = ( + self._metadef_property + ) + + def test_metadef_property_set(self): + arglist = ['--title', 'new title', 'namespace', 'property'] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.assertIsNone(result) + + def test_metadef_property_set_invalid_schema(self): + arglist = [ + '--title', + 'new title', + '--schema', + '{invalid}', + 'namespace', + 'property', + ] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + + class TestMetadefPropertyShow(image_fakes.TestImagev2): _metadef_property = image_fakes.create_one_metadef_property() diff --git a/releasenotes/notes/add-metadef-property-set-ab9cdcb73adf6397.yaml b/releasenotes/notes/add-metadef-property-set-ab9cdcb73adf6397.yaml new file mode 100644 index 000000000..cf07d527d --- /dev/null +++ b/releasenotes/notes/add-metadef-property-set-ab9cdcb73adf6397.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``image metadef property set`` command to update a + metadef property inside a specific namespace. diff --git a/setup.cfg b/setup.cfg index 9f1f254e6..1212ad9fe 100644 --- a/setup.cfg +++ b/setup.cfg @@ -400,6 +400,7 @@ openstack.image.v2 = 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 From 7b99b5716544bccf455ad0bf6d8a0ccfe2788331 Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Fri, 30 Jun 2023 12:04:02 +0200 Subject: [PATCH 017/403] Add support for default security group rule CRUDs Change-Id: I1c18c2ec5eb4923e1ab8b3fc6199ef6f329b4a4d --- .../default-security-group-rule.rst | 11 + openstackclient/network/utils.py | 100 ++ .../network/v2/default_security_group_rule.py | 399 ++++++ .../network/v2/security_group_rule.py | 129 +- .../v2/test_default_security_group_rule.py | 69 + .../v2/test_default_security_group_rule.py | 1133 +++++++++++++++++ .../v2/test_security_group_rule_compute.py | 4 +- .../v2/test_security_group_rule_network.py | 9 +- ...rity-group-rule-CRUD-2916568f829ea38c.yaml | 8 + requirements.txt | 2 +- setup.cfg | 5 + 11 files changed, 1749 insertions(+), 120 deletions(-) create mode 100644 doc/source/cli/command-objects/default-security-group-rule.rst create mode 100644 openstackclient/network/v2/default_security_group_rule.py create mode 100644 openstackclient/tests/functional/network/v2/test_default_security_group_rule.py create mode 100644 openstackclient/tests/unit/network/v2/test_default_security_group_rule.py create mode 100644 releasenotes/notes/Add-default-security-group-rule-CRUD-2916568f829ea38c.yaml diff --git a/doc/source/cli/command-objects/default-security-group-rule.rst b/doc/source/cli/command-objects/default-security-group-rule.rst new file mode 100644 index 000000000..1905614bf --- /dev/null +++ b/doc/source/cli/command-objects/default-security-group-rule.rst @@ -0,0 +1,11 @@ +=========================== +default security group rule +=========================== + +A **default security group rule** specifies the template of the security group +rules which will be used by neutron to create rules in every new security group. + +Network v2 + +.. autoprogram-cliff:: openstack.network.v2 + :command: default security group rule * diff --git a/openstackclient/network/utils.py b/openstackclient/network/utils.py index 4d4d18e47..85a4f5ac1 100644 --- a/openstackclient/network/utils.py +++ b/openstackclient/network/utils.py @@ -81,3 +81,103 @@ def str2dict(strdict): key, sep, value = kv.partition(':') result[key] = value return result + + +def format_security_group_rule_show(obj): + data = transform_compute_security_group_rule(obj) + return zip(*sorted(data.items())) + + +def format_network_port_range(rule): + # Display port range or ICMP type and code. For example: + # - ICMP type: 'type=3' + # - ICMP type and code: 'type=3:code=0' + # - ICMP code: Not supported + # - Matching port range: '443:443' + # - Different port range: '22:24' + # - Single port: '80:80' + # - No port range: '' + port_range = '' + if is_icmp_protocol(rule['protocol']): + if rule['port_range_min']: + port_range += 'type=' + str(rule['port_range_min']) + if rule['port_range_max']: + port_range += ':code=' + str(rule['port_range_max']) + elif rule['port_range_min'] or rule['port_range_max']: + port_range_min = str(rule['port_range_min']) + port_range_max = str(rule['port_range_max']) + if rule['port_range_min'] is None: + port_range_min = port_range_max + if rule['port_range_max'] is None: + port_range_max = port_range_min + port_range = port_range_min + ':' + port_range_max + return port_range + + +def format_remote_ip_prefix(rule): + remote_ip_prefix = rule['remote_ip_prefix'] + if remote_ip_prefix is None: + ethertype = rule['ether_type'] + if ethertype == 'IPv4': + remote_ip_prefix = '0.0.0.0/0' + elif ethertype == 'IPv6': + remote_ip_prefix = '::/0' + return remote_ip_prefix + + +def convert_ipvx_case(string): + if string.lower() == 'ipv4': + return 'IPv4' + if string.lower() == 'ipv6': + return 'IPv6' + return string + + +def is_icmp_protocol(protocol): + # NOTE(rtheis): Neutron has deprecated protocol icmpv6. + # However, while the OSC CLI doesn't document the protocol, + # the code must still handle it. In addition, handle both + # protocol names and numbers. + if protocol in ['icmp', 'icmpv6', 'ipv6-icmp', '1', '58']: + return True + else: + return False + + +def convert_to_lowercase(string): + return string.lower() + + +def get_protocol(parsed_args, default_protocol='any'): + protocol = default_protocol + if parsed_args.protocol is not None: + protocol = parsed_args.protocol + if hasattr(parsed_args, "proto") and parsed_args.proto is not None: + protocol = parsed_args.proto + if protocol == 'any': + protocol = None + return protocol + + +def get_ethertype(parsed_args, protocol): + ethertype = 'IPv4' + if parsed_args.ethertype is not None: + ethertype = parsed_args.ethertype + elif is_ipv6_protocol(protocol): + ethertype = 'IPv6' + return ethertype + + +def is_ipv6_protocol(protocol): + # NOTE(rtheis): Neutron has deprecated protocol icmpv6. + # However, while the OSC CLI doesn't document the protocol, + # the code must still handle it. In addition, handle both + # protocol names and numbers. + if ( + protocol is not None + and protocol.startswith('ipv6-') + or protocol in ['icmpv6', '41', '43', '44', '58', '59', '60'] + ): + return True + else: + return False diff --git a/openstackclient/network/v2/default_security_group_rule.py b/openstackclient/network/v2/default_security_group_rule.py new file mode 100644 index 000000000..b8232a252 --- /dev/null +++ b/openstackclient/network/v2/default_security_group_rule.py @@ -0,0 +1,399 @@ +# 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. +# + +"""Default Security Group Rule action implementations""" + +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.i18n import _ +from openstackclient.network import common +from openstackclient.network import utils as network_utils + +LOG = logging.getLogger(__name__) + + +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 + ) + + +class CreateDefaultSecurityGroupRule( + command.ShowOne, common.NeutronCommandWithExtraArgs +): + _description = _("Create a new default security group rule") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + + parser.add_argument( + '--description', + metavar='', + help=_("Set default security group rule description"), + ) + parser.add_argument( + '--icmp-type', + metavar='', + type=int, + help=_("ICMP type for ICMP IP protocols"), + ) + parser.add_argument( + '--icmp-code', + metavar='', + type=int, + help=_("ICMP code for ICMP IP protocols"), + ) + direction_group = parser.add_mutually_exclusive_group() + direction_group.add_argument( + '--ingress', + action='store_true', + help=_("Rule will apply to incoming network traffic (default)"), + ) + direction_group.add_argument( + '--egress', + action='store_true', + help=_("Rule will apply to outgoing network traffic"), + ) + parser.add_argument( + '--ethertype', + metavar='', + choices=['IPv4', 'IPv6'], + type=network_utils.convert_ipvx_case, + help=_( + "Ethertype of network traffic " + "(IPv4, IPv6; default: based on IP protocol)" + ), + ) + remote_group = parser.add_mutually_exclusive_group() + remote_group.add_argument( + "--remote-ip", + metavar="", + help=_( + "Remote IP address block (may use CIDR notation; " + "default for IPv4 rule: 0.0.0.0/0, " + "default for IPv6 rule: ::/0)" + ), + ) + remote_group.add_argument( + "--remote-group", + metavar="", + help=_("Remote security group (ID)"), + ) + remote_group.add_argument( + "--remote-address-group", + metavar="", + help=_("Remote address group (ID)"), + ) + + parser.add_argument( + '--dst-port', + metavar='', + action=parseractions.RangeAction, + help=_( + "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 protocols." + ), + ) + parser.add_argument( + '--protocol', + metavar='', + type=network_utils.convert_to_lowercase, + help=_( + "IP protocol (ah, dccp, egp, esp, gre, icmp, igmp, " + "ipv66-encap, ipv6-frag, ipv6-icmp, ipv6-nonxt, ipv6-opts, " + "ipv6-route, ospf, pgm, rsvp, sctp, tcp, udp, udplite, vrrp " + "and integer representations [0-255] or any; " + "default: any (all protocols))" + ), + ) + parser.add_argument( + '--for-default-sg', + action='store_true', + default=False, + help=_( + "Set this default security group rule to be used in all " + "default security groups created automatically for each " + "project" + ), + ) + parser.add_argument( + '--for-custom-sg', + action='store_true', + default=True, + help=_( + "Set this default security group rule to be used in all " + "custom security groups created manually by users" + ), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.sdk_connection.network + # Build the create attributes. + attrs = {} + attrs['protocol'] = network_utils.get_protocol(parsed_args) + + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + + # NOTE: A direction must be specified and ingress + # is the default. + if parsed_args.ingress or not parsed_args.egress: + attrs['direction'] = 'ingress' + if parsed_args.egress: + attrs['direction'] = 'egress' + + # NOTE(rtheis): Use ethertype specified else default based + # on IP protocol. + attrs['ethertype'] = network_utils.get_ethertype( + parsed_args, attrs['protocol'] + ) + + # NOTE(rtheis): Validate the port range and ICMP type and code. + # It would be ideal if argparse could do this. + if parsed_args.dst_port and ( + parsed_args.icmp_type or parsed_args.icmp_code + ): + msg = _( + 'Argument --dst-port not allowed with arguments ' + '--icmp-type and --icmp-code' + ) + raise exceptions.CommandError(msg) + if parsed_args.icmp_type is None and parsed_args.icmp_code is not None: + msg = _('Argument --icmp-type required with argument --icmp-code') + raise exceptions.CommandError(msg) + is_icmp_protocol = network_utils.is_icmp_protocol(attrs['protocol']) + if not is_icmp_protocol and ( + parsed_args.icmp_type or parsed_args.icmp_code + ): + msg = _( + 'ICMP IP protocol required with arguments ' + '--icmp-type and --icmp-code' + ) + raise exceptions.CommandError(msg) + # NOTE(rtheis): For backwards compatibility, continue ignoring + # the destination port range when an ICMP IP protocol is specified. + if parsed_args.dst_port and not is_icmp_protocol: + attrs['port_range_min'] = parsed_args.dst_port[0] + attrs['port_range_max'] = parsed_args.dst_port[1] + if parsed_args.icmp_type is not None and parsed_args.icmp_type >= 0: + attrs['port_range_min'] = parsed_args.icmp_type + if parsed_args.icmp_code is not None and parsed_args.icmp_code >= 0: + attrs['port_range_max'] = parsed_args.icmp_code + + if parsed_args.remote_group is not None: + attrs['remote_group_id'] = parsed_args.remote_group + elif parsed_args.remote_address_group is not None: + attrs['remote_address_group_id'] = parsed_args.remote_address_group + elif parsed_args.remote_ip is not None: + attrs['remote_ip_prefix'] = parsed_args.remote_ip + elif attrs['ethertype'] == 'IPv4': + attrs['remote_ip_prefix'] = '0.0.0.0/0' + elif attrs['ethertype'] == 'IPv6': + attrs['remote_ip_prefix'] = '::/0' + + attrs['used_in_default_sg'] = parsed_args.for_default_sg + attrs['used_in_non_default_sg'] = parsed_args.for_custom_sg + + attrs.update( + self._parse_extra_properties(parsed_args.extra_properties) + ) + + # Create and show the security group rule. + obj = client.create_default_security_group_rule(**attrs) + display_columns, columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns) + return (display_columns, data) + + +class DeleteDefaultSecurityGroupRule(command.Command): + _description = _("Delete default security group rule(s)") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'rule', + metavar='', + nargs="+", + help=_("Default security group rule(s) to delete (ID only)"), + ) + return parser + + def take_action(self, parsed_args): + result = 0 + client = self.app.client_manager.sdk_connection.network + for r in parsed_args.rule: + try: + obj = client.find_default_security_group_rule( + r, ignore_missing=False + ) + client.delete_default_security_group_rule(obj) + except Exception as e: + result += 1 + LOG.error( + _( + "Failed to delete default SG rule with " + "ID '%(rule_id)s': %(e)s" + ), + {'rule_id': r, 'e': e}, + ) + + if result > 0: + total = len(parsed_args.rule) + msg = _( + "%(result)s of %(total)s default rules failed to delete." + ) % {'result': result, 'total': total} + raise exceptions.CommandError(msg) + + +class ListDefaultSecurityGroupRule(command.Lister): + _description = _("List default security group rules") + + def _format_network_security_group_rule(self, rule): + """Transform the SDK DefaultSecurityGroupRule object to a dict + + The SDK object gets in the way of reformatting columns... + Create port_range column from port_range_min and port_range_max + """ + rule = rule.to_dict() + rule['port_range'] = network_utils.format_network_port_range(rule) + rule['remote_ip_prefix'] = network_utils.format_remote_ip_prefix(rule) + return rule + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + + parser.add_argument( + '--protocol', + metavar='', + type=network_utils.convert_to_lowercase, + help=_( + "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] or any; " + "default: any (all protocols))" + ), + ) + parser.add_argument( + '--ethertype', + metavar='', + type=network_utils.convert_to_lowercase, + help=_("List default rules by the Ethertype (IPv4 or IPv6)"), + ) + direction_group = parser.add_mutually_exclusive_group() + direction_group.add_argument( + '--ingress', + action='store_true', + help=_( + "List default rules which will be applied to incoming " + "network traffic" + ), + ) + direction_group.add_argument( + '--egress', + action='store_true', + help=_( + "List default rules which will be applied to outgoing " + "network traffic" + ), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.sdk_connection.network + column_headers = ( + 'ID', + 'IP Protocol', + 'Ethertype', + 'IP Range', + 'Port Range', + 'Direction', + 'Remote Security Group', + 'Remote Address Group', + 'Used in default Security Group', + 'Used in custom Security Group', + ) + columns = ( + 'id', + 'protocol', + 'ether_type', + 'remote_ip_prefix', + 'port_range', + 'direction', + 'remote_group_id', + 'remote_address_group_id', + 'used_in_default_sg', + 'used_in_non_default_sg', + ) + + # Get the security group rules using the requested query. + query = {} + if parsed_args.ingress: + query['direction'] = 'ingress' + if parsed_args.egress: + query['direction'] = 'egress' + if parsed_args.protocol is not None: + query['protocol'] = parsed_args.protocol + + rules = [ + self._format_network_security_group_rule(r) + for r in client.default_security_group_rules(**query) + ] + + return ( + column_headers, + ( + utils.get_dict_properties( + s, + columns, + ) + for s in rules + ), + ) + + +class ShowDefaultSecurityGroupRule(command.ShowOne): + _description = _("Display default security group rule details") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'rule', + metavar="", + help=_("Default security group rule to display (ID only)"), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.sdk_connection.network + obj = client.find_default_security_group_rule( + parsed_args.rule, ignore_missing=False + ) + # necessary for old rules that have None in this field + if not obj['remote_ip_prefix']: + obj['remote_ip_prefix'] = network_utils.format_remote_ip_prefix( + obj + ) + display_columns, columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns) + return (display_columns, data) diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index 24a1fcfb0..5e0de120b 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -28,48 +28,6 @@ LOG = logging.getLogger(__name__) -def _format_security_group_rule_show(obj): - data = network_utils.transform_compute_security_group_rule(obj) - return zip(*sorted(data.items())) - - -def _format_network_port_range(rule): - # Display port range or ICMP type and code. For example: - # - ICMP type: 'type=3' - # - ICMP type and code: 'type=3:code=0' - # - ICMP code: Not supported - # - Matching port range: '443:443' - # - Different port range: '22:24' - # - Single port: '80:80' - # - No port range: '' - port_range = '' - if _is_icmp_protocol(rule['protocol']): - if rule['port_range_min']: - port_range += 'type=' + str(rule['port_range_min']) - if rule['port_range_max']: - port_range += ':code=' + str(rule['port_range_max']) - elif rule['port_range_min'] or rule['port_range_max']: - port_range_min = str(rule['port_range_min']) - port_range_max = str(rule['port_range_max']) - if rule['port_range_min'] is None: - port_range_min = port_range_max - if rule['port_range_max'] is None: - port_range_max = port_range_min - port_range = port_range_min + ':' + port_range_max - return port_range - - -def _format_remote_ip_prefix(rule): - remote_ip_prefix = rule['remote_ip_prefix'] - if remote_ip_prefix is None: - ethertype = rule['ether_type'] - if ethertype == 'IPv4': - remote_ip_prefix = '0.0.0.0/0' - elif ethertype == 'IPv6': - remote_ip_prefix = '::/0' - return remote_ip_prefix - - def _get_columns(item): column_map = {} hidden_columns = ['location', 'tenant_id'] @@ -78,29 +36,6 @@ def _get_columns(item): ) -def _convert_to_lowercase(string): - return string.lower() - - -def _convert_ipvx_case(string): - if string.lower() == 'ipv4': - return 'IPv4' - if string.lower() == 'ipv6': - return 'IPv6' - return string - - -def _is_icmp_protocol(protocol): - # NOTE(rtheis): Neutron has deprecated protocol icmpv6. - # However, while the OSC CLI doesn't document the protocol, - # the code must still handle it. In addition, handle both - # protocol names and numbers. - if protocol in ['icmp', 'icmpv6', 'ipv6-icmp', '1', '58']: - return True - else: - return False - - # TODO(abhiraut): Use the SDK resource mapped attribute names once the # OSC minimum requirements include SDK 1.0. class CreateSecurityGroupRule( @@ -188,7 +123,7 @@ def update_parser_common(self, parser): protocol_group.add_argument( '--protocol', metavar='', - type=_convert_to_lowercase, + type=network_utils.convert_to_lowercase, help=protocol_help, **proto_choices ) @@ -196,7 +131,7 @@ def update_parser_common(self, parser): protocol_group.add_argument( '--proto', metavar='', - type=_convert_to_lowercase, + type=network_utils.convert_to_lowercase, help=argparse.SUPPRESS, **proto_choices ) @@ -246,7 +181,7 @@ def update_parser_network(self, parser): '--ethertype', metavar='', choices=['IPv4', 'IPv6'], - type=_convert_ipvx_case, + type=network_utils.convert_ipvx_case, help=self.enhance_help_neutron( _( "Ethertype of network traffic " @@ -264,38 +199,6 @@ def update_parser_network(self, parser): ) return parser - def _get_protocol(self, parsed_args, default_protocol='any'): - protocol = default_protocol - if parsed_args.protocol is not None: - protocol = parsed_args.protocol - if parsed_args.proto is not None: - protocol = parsed_args.proto - if protocol == 'any': - protocol = None - return protocol - - def _get_ethertype(self, parsed_args, protocol): - ethertype = 'IPv4' - if parsed_args.ethertype is not None: - ethertype = parsed_args.ethertype - elif self._is_ipv6_protocol(protocol): - ethertype = 'IPv6' - return ethertype - - def _is_ipv6_protocol(self, protocol): - # NOTE(rtheis): Neutron has deprecated protocol icmpv6. - # However, while the OSC CLI doesn't document the protocol, - # the code must still handle it. In addition, handle both - # protocol names and numbers. - if ( - protocol is not None - and protocol.startswith('ipv6-') - or protocol in ['icmpv6', '41', '43', '44', '58', '59', '60'] - ): - return True - else: - return False - def take_action_network(self, client, parsed_args): # Get the security group ID to hold the rule. security_group_id = client.find_security_group( @@ -304,7 +207,7 @@ def take_action_network(self, client, parsed_args): # Build the create attributes. attrs = {} - attrs['protocol'] = self._get_protocol(parsed_args) + attrs['protocol'] = network_utils.get_protocol(parsed_args) if parsed_args.description is not None: attrs['description'] = parsed_args.description @@ -318,7 +221,7 @@ def take_action_network(self, client, parsed_args): # NOTE(rtheis): Use ethertype specified else default based # on IP protocol. - attrs['ethertype'] = self._get_ethertype( + attrs['ethertype'] = network_utils.get_ethertype( parsed_args, attrs['protocol'] ) @@ -335,7 +238,7 @@ def take_action_network(self, client, parsed_args): if parsed_args.icmp_type is None and parsed_args.icmp_code is not None: msg = _('Argument --icmp-type required with argument --icmp-code') raise exceptions.CommandError(msg) - is_icmp_protocol = _is_icmp_protocol(attrs['protocol']) + is_icmp_protocol = network_utils.is_icmp_protocol(attrs['protocol']) if not is_icmp_protocol and ( parsed_args.icmp_type or parsed_args.icmp_code ): @@ -390,7 +293,9 @@ def take_action_network(self, client, parsed_args): def take_action_compute(self, client, parsed_args): group = client.api.security_group_find(parsed_args.group) - protocol = self._get_protocol(parsed_args, default_protocol='tcp') + protocol = network_utils.get_protocol( + parsed_args, default_protocol='tcp' + ) if protocol == 'icmp': from_port, to_port = -1, -1 else: @@ -414,7 +319,7 @@ def take_action_compute(self, client, parsed_args): remote_ip=remote_ip, remote_group=parsed_args.remote_group, ) - return _format_security_group_rule_show(obj) + return network_utils.format_security_group_rule_show(obj) class DeleteSecurityGroupRule(common.NetworkAndComputeDelete): @@ -451,8 +356,8 @@ def _format_network_security_group_rule(self, rule): Create port_range column from port_range_min and port_range_max """ rule = rule.to_dict() - rule['port_range'] = _format_network_port_range(rule) - rule['remote_ip_prefix'] = _format_remote_ip_prefix(rule) + rule['port_range'] = network_utils.format_network_port_range(rule) + rule['remote_ip_prefix'] = network_utils.format_remote_ip_prefix(rule) return rule def update_parser_common(self, parser): @@ -478,7 +383,7 @@ def update_parser_network(self, parser): parser.add_argument( '--protocol', metavar='', - type=_convert_to_lowercase, + type=network_utils.convert_to_lowercase, help=self.enhance_help_neutron( _( "List rules by the IP protocol (ah, dhcp, egp, esp, gre, " @@ -493,7 +398,7 @@ def update_parser_network(self, parser): parser.add_argument( '--ethertype', metavar='', - type=_convert_to_lowercase, + type=network_utils.convert_to_lowercase, help=self.enhance_help_neutron( _("List rules by the Ethertype (IPv4 or IPv6)") ), @@ -677,7 +582,9 @@ def take_action_network(self, client, parsed_args): ) # necessary for old rules that have None in this field if not obj['remote_ip_prefix']: - obj['remote_ip_prefix'] = _format_remote_ip_prefix(obj) + obj['remote_ip_prefix'] = network_utils.format_remote_ip_prefix( + obj + ) display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns) return (display_columns, data) @@ -704,4 +611,4 @@ def take_action_compute(self, client, parsed_args): raise exceptions.CommandError(msg) # NOTE(rtheis): Format security group rule - return _format_security_group_rule_show(obj) + return network_utils.format_security_group_rule_show(obj) 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 new file mode 100644 index 000000000..76c8053be --- /dev/null +++ b/openstackclient/tests/functional/network/v2/test_default_security_group_rule.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. + +import random + +from openstackclient.tests.functional.network.v2 import common + + +class SecurityGroupRuleTests(common.NetworkTests): + """Functional tests for security group rule""" + + def setUp(self): + super(SecurityGroupRuleTests, self).setUp() + # Nothing in this class works with Nova Network + if not self.haz_network: + self.skipTest("No Network service present") + if not self.is_extension_enabled("security-groups-default-rules"): + self.skipTest("No security-groups-default-rules extension present") + + self.port = random.randint(1, 65535) + self.protocol = random.choice(["tcp", "udp"]) + self.direction = random.choice(["ingress", "egress"]) + # 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, + }, + parse_output=True, + ) + self.addCleanup( + self.openstack, + 'default security group rule delete ' + cmd_output['id'], + ) + self.DEFAULT_SG_RULE_ID = cmd_output['id'] + + def test_security_group_rule_list(self): + cmd_output = self.openstack( + 'default security group rule list ', + parse_output=True, + ) + self.assertIn( + self.DEFAULT_SG_RULE_ID, [rule['ID'] for rule in cmd_output] + ) + + def test_security_group_rule_show(self): + cmd_output = self.openstack( + 'default security group rule show ' + self.DEFAULT_SG_RULE_ID, + parse_output=True, + ) + self.assertEqual(self.DEFAULT_SG_RULE_ID, cmd_output['id']) + self.assertEqual(self.protocol, cmd_output['protocol']) + self.assertEqual(self.port, cmd_output['port_range_min']) + self.assertEqual(self.port, cmd_output['port_range_max']) + self.assertEqual(self.direction, cmd_output['direction']) 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 new file mode 100644 index 000000000..a7a6ec69b --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_default_security_group_rule.py @@ -0,0 +1,1133 @@ +# 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 +import uuid + +from openstack.network.v2 import _proxy +from openstack.network.v2 import ( + default_security_group_rule as _default_security_group_rule, +) +from openstack.test import fakes as sdk_fakes +from osc_lib import exceptions + +from openstackclient.network import utils as network_utils +from openstackclient.network.v2 import default_security_group_rule +from openstackclient.tests.unit.network.v2 import fakes as network_fakes +from openstackclient.tests.unit import utils as tests_utils + + +class TestDefaultSecurityGroupRule(network_fakes.TestNetworkV2): + def setUp(self): + super(TestDefaultSecurityGroupRule, self).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): + expected_columns = ( + 'description', + 'direction', + 'ether_type', + 'id', + 'port_range_max', + 'port_range_min', + 'protocol', + 'remote_address_group_id', + 'remote_group_id', + 'remote_ip_prefix', + 'used_in_default_sg', + 'used_in_non_default_sg', + ) + + expected_data = None + + def _setup_default_security_group_rule(self, attrs=None): + default_security_group_rule_attrs = { + 'description': 'default-security-group-rule-description-' + + uuid.uuid4().hex, + 'direction': 'ingress', + 'ether_type': 'IPv4', + 'id': 'default-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', + 'location': 'MUNCHMUNCHMUNCH', + 'used_in_default_sg': False, + 'used_in_non_default_sg': True, + } + attrs = attrs or {} + # Overwrite default attributes. + 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 + ) + + self.sdk_client.create_default_security_group_rule.return_value = ( + self._default_sg_rule + ) + self.expected_data = ( + self._default_sg_rule.description, + self._default_sg_rule.direction, + self._default_sg_rule.ether_type, + self._default_sg_rule.id, + self._default_sg_rule.port_range_max, + self._default_sg_rule.port_range_min, + self._default_sg_rule.protocol, + self._default_sg_rule.remote_address_group_id, + self._default_sg_rule.remote_group_id, + self._default_sg_rule.remote_ip_prefix, + self._default_sg_rule.used_in_default_sg, + self._default_sg_rule.used_in_non_default_sg, + ) + + def setUp(self): + super(TestCreateDefaultSecurityGroupRule, self).setUp() + + # Get the command object to test + self.cmd = default_security_group_rule.CreateDefaultSecurityGroupRule( + self.app, self.namespace + ) + + def test_create_all_remote_options(self): + arglist = [ + '--remote-ip', + '10.10.0.0/24', + '--remote-group', + 'test-remote-group-id', + '--remote-address-group', + 'test-remote-address-group-id', + ] + self.assertRaises( + tests_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + [], + ) + + def test_create_bad_ethertype(self): + arglist = [ + '--ethertype', + 'foo', + ] + self.assertRaises( + tests_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + [], + ) + + def test_lowercase_ethertype(self): + arglist = [ + '--ethertype', + 'ipv4', + ] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.assertEqual('IPv4', parsed_args.ethertype) + + def test_lowercase_v6_ethertype(self): + arglist = [ + '--ethertype', + 'ipv6', + ] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.assertEqual('IPv6', parsed_args.ethertype) + + def test_proper_case_ethertype(self): + arglist = [ + '--ethertype', + 'IPv6', + ] + parsed_args = self.check_parser(self.cmd, arglist, []) + self.assertEqual('IPv6', parsed_args.ethertype) + + def test_create_all_port_range_options(self): + arglist = [ + '--dst-port', + '80:80', + '--icmp-type', + '3', + '--icmp-code', + '1', + ] + verifylist = [ + ('dst_port', (80, 80)), + ('icmp_type', 3), + ('icmp_code', 1), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + + def test_create_default_rule(self): + self._setup_default_security_group_rule( + { + 'protocol': 'tcp', + 'port_range_max': 443, + 'port_range_min': 443, + } + ) + arglist = [ + '--protocol', + 'tcp', + '--dst-port', + str(self._default_sg_rule.port_range_min), + ] + verifylist = [ + ( + 'dst_port', + ( + self._default_sg_rule.port_range_min, + self._default_sg_rule.port_range_max, + ), + ), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'direction': self._default_sg_rule.direction, + 'ethertype': self._default_sg_rule.ether_type, + 'port_range_max': self._default_sg_rule.port_range_max, + 'port_range_min': self._default_sg_rule.port_range_min, + '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': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_protocol_any(self): + self._setup_default_security_group_rule( + { + 'protocol': None, + 'remote_ip_prefix': '10.0.2.0/24', + } + ) + arglist = [ + '--protocol', + 'any', + '--remote-ip', + self._default_sg_rule.remote_ip_prefix, + ] + verifylist = [ + ('protocol', 'any'), + ('remote_ip', self._default_sg_rule.remote_ip_prefix), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'direction': self._default_sg_rule.direction, + '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': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_remote_address_group(self): + self._setup_default_security_group_rule( + { + 'protocol': 'icmp', + 'remote_address_group_id': 'remote-address-group-id', + } + ) + arglist = [ + '--protocol', + 'icmp', + '--remote-address-group', + self._default_sg_rule.remote_address_group_id, + ] + verifylist = [ + ( + 'remote_address_group', + self._default_sg_rule.remote_address_group_id, + ), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'direction': self._default_sg_rule.direction, + 'ethertype': self._default_sg_rule.ether_type, + 'protocol': self._default_sg_rule.protocol, + 'remote_address_group_id': self._default_sg_rule.remote_address_group_id, + 'used_in_default_sg': False, + 'used_in_non_default_sg': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_remote_group(self): + self._setup_default_security_group_rule( + { + 'protocol': 'tcp', + 'port_range_max': 22, + 'port_range_min': 22, + } + ) + arglist = [ + '--protocol', + 'tcp', + '--dst-port', + str(self._default_sg_rule.port_range_min), + '--ingress', + '--remote-group', + 'remote-group-id', + ] + verifylist = [ + ( + 'dst_port', + ( + self._default_sg_rule.port_range_min, + self._default_sg_rule.port_range_max, + ), + ), + ('ingress', True), + ('remote_group', 'remote-group-id'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'direction': self._default_sg_rule.direction, + 'ethertype': self._default_sg_rule.ether_type, + 'port_range_max': self._default_sg_rule.port_range_max, + 'port_range_min': self._default_sg_rule.port_range_min, + 'protocol': self._default_sg_rule.protocol, + 'remote_group_id': 'remote-group-id', + 'used_in_default_sg': False, + 'used_in_non_default_sg': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_source_group(self): + self._setup_default_security_group_rule( + { + 'remote_group_id': 'remote-group-id', + } + ) + arglist = [ + '--ingress', + '--remote-group', + 'remote-group-id', + ] + verifylist = [ + ('ingress', True), + ('remote_group', 'remote-group-id'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'direction': self._default_sg_rule.direction, + 'ethertype': self._default_sg_rule.ether_type, + 'protocol': self._default_sg_rule.protocol, + 'remote_group_id': 'remote-group-id', + 'used_in_default_sg': False, + 'used_in_non_default_sg': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_source_ip(self): + self._setup_default_security_group_rule( + { + 'protocol': 'icmp', + 'remote_ip_prefix': '10.0.2.0/24', + } + ) + arglist = [ + '--protocol', + self._default_sg_rule.protocol, + '--remote-ip', + self._default_sg_rule.remote_ip_prefix, + ] + verifylist = [ + ('protocol', self._default_sg_rule.protocol), + ('remote_ip', self._default_sg_rule.remote_ip_prefix), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'direction': self._default_sg_rule.direction, + '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': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_remote_ip(self): + self._setup_default_security_group_rule( + { + 'protocol': 'icmp', + 'remote_ip_prefix': '10.0.2.0/24', + } + ) + arglist = [ + '--protocol', + self._default_sg_rule.protocol, + '--remote-ip', + self._default_sg_rule.remote_ip_prefix, + ] + verifylist = [ + ('protocol', self._default_sg_rule.protocol), + ('remote_ip', self._default_sg_rule.remote_ip_prefix), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'direction': self._default_sg_rule.direction, + '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': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_tcp_with_icmp_type(self): + arglist = [ + '--protocol', + 'tcp', + '--icmp-type', + '15', + ] + verifylist = [ + ('protocol', 'tcp'), + ('icmp_type', 15), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + + def test_create_icmp_code(self): + arglist = [ + '--protocol', + '1', + '--icmp-code', + '1', + ] + verifylist = [ + ('protocol', '1'), + ('icmp_code', 1), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + + def test_create_icmp_code_zero(self): + self._setup_default_security_group_rule( + { + 'port_range_min': 15, + 'port_range_max': 0, + 'protocol': 'icmp', + 'remote_ip_prefix': '0.0.0.0/0', + } + ) + arglist = [ + '--protocol', + self._default_sg_rule.protocol, + '--icmp-type', + str(self._default_sg_rule.port_range_min), + '--icmp-code', + str(self._default_sg_rule.port_range_max), + ] + verifylist = [ + ('protocol', self._default_sg_rule.protocol), + ('icmp_code', self._default_sg_rule.port_range_max), + ('icmp_type', self._default_sg_rule.port_range_min), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_icmp_code_greater_than_zero(self): + self._setup_default_security_group_rule( + { + 'port_range_min': 15, + 'port_range_max': 18, + 'protocol': 'icmp', + 'remote_ip_prefix': '0.0.0.0/0', + } + ) + arglist = [ + '--protocol', + self._default_sg_rule.protocol, + '--icmp-type', + str(self._default_sg_rule.port_range_min), + '--icmp-code', + str(self._default_sg_rule.port_range_max), + ] + verifylist = [ + ('protocol', self._default_sg_rule.protocol), + ('icmp_type', self._default_sg_rule.port_range_min), + ('icmp_code', self._default_sg_rule.port_range_max), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_icmp_code_negative_value(self): + self._setup_default_security_group_rule( + { + 'port_range_min': 15, + 'port_range_max': None, + 'protocol': 'icmp', + 'remote_ip_prefix': '0.0.0.0/0', + } + ) + arglist = [ + '--protocol', + self._default_sg_rule.protocol, + '--icmp-type', + str(self._default_sg_rule.port_range_min), + '--icmp-code', + '-2', + ] + verifylist = [ + ('protocol', self._default_sg_rule.protocol), + ('icmp_type', self._default_sg_rule.port_range_min), + ('icmp_code', -2), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_icmp_type(self): + self._setup_default_security_group_rule( + { + 'port_range_min': 15, + 'protocol': 'icmp', + 'remote_ip_prefix': '0.0.0.0/0', + } + ) + arglist = [ + '--icmp-type', + str(self._default_sg_rule.port_range_min), + '--protocol', + self._default_sg_rule.protocol, + ] + verifylist = [ + ('dst_port', None), + ('icmp_type', self._default_sg_rule.port_range_min), + ('icmp_code', None), + ('protocol', self._default_sg_rule.protocol), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'direction': self._default_sg_rule.direction, + 'ethertype': self._default_sg_rule.ether_type, + 'port_range_min': self._default_sg_rule.port_range_min, + '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': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_icmp_type_zero(self): + self._setup_default_security_group_rule( + { + 'port_range_min': 0, + 'protocol': 'icmp', + 'remote_ip_prefix': '0.0.0.0/0', + } + ) + arglist = [ + '--icmp-type', + str(self._default_sg_rule.port_range_min), + '--protocol', + self._default_sg_rule.protocol, + ] + verifylist = [ + ('dst_port', None), + ('icmp_type', self._default_sg_rule.port_range_min), + ('icmp_code', None), + ('protocol', self._default_sg_rule.protocol), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'direction': self._default_sg_rule.direction, + 'ethertype': self._default_sg_rule.ether_type, + 'port_range_min': self._default_sg_rule.port_range_min, + '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': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_icmp_type_greater_than_zero(self): + self._setup_default_security_group_rule( + { + 'port_range_min': 13, # timestamp + 'protocol': 'icmp', + 'remote_ip_prefix': '0.0.0.0/0', + } + ) + arglist = [ + '--icmp-type', + str(self._default_sg_rule.port_range_min), + '--protocol', + self._default_sg_rule.protocol, + ] + verifylist = [ + ('dst_port', None), + ('icmp_type', self._default_sg_rule.port_range_min), + ('icmp_code', None), + ('protocol', self._default_sg_rule.protocol), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'direction': self._default_sg_rule.direction, + 'ethertype': self._default_sg_rule.ether_type, + 'port_range_min': self._default_sg_rule.port_range_min, + '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': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_icmp_type_negative_value(self): + self._setup_default_security_group_rule( + { + 'port_range_min': None, # timestamp + 'protocol': 'icmp', + 'remote_ip_prefix': '0.0.0.0/0', + } + ) + arglist = [ + '--icmp-type', + '-13', + '--protocol', + self._default_sg_rule.protocol, + ] + verifylist = [ + ('dst_port', None), + ('icmp_type', -13), + ('icmp_code', None), + ('protocol', self._default_sg_rule.protocol), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'direction': self._default_sg_rule.direction, + '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': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_ipv6_icmp_type_code(self): + self._setup_default_security_group_rule( + { + 'ether_type': 'IPv6', + 'port_range_min': 139, + 'port_range_max': 2, + 'protocol': 'ipv6-icmp', + 'remote_ip_prefix': '::/0', + } + ) + arglist = [ + '--icmp-type', + str(self._default_sg_rule.port_range_min), + '--icmp-code', + str(self._default_sg_rule.port_range_max), + '--protocol', + self._default_sg_rule.protocol, + ] + verifylist = [ + ('dst_port', None), + ('icmp_type', self._default_sg_rule.port_range_min), + ('icmp_code', self._default_sg_rule.port_range_max), + ('protocol', self._default_sg_rule.protocol), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'direction': self._default_sg_rule.direction, + 'ethertype': self._default_sg_rule.ether_type, + 'port_range_min': self._default_sg_rule.port_range_min, + 'port_range_max': self._default_sg_rule.port_range_max, + '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': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_icmpv6_type(self): + self._setup_default_security_group_rule( + { + 'ether_type': 'IPv6', + 'port_range_min': 139, + 'protocol': 'icmpv6', + 'remote_ip_prefix': '::/0', + } + ) + arglist = [ + '--icmp-type', + str(self._default_sg_rule.port_range_min), + '--protocol', + self._default_sg_rule.protocol, + ] + verifylist = [ + ('dst_port', None), + ('icmp_type', self._default_sg_rule.port_range_min), + ('icmp_code', None), + ('protocol', self._default_sg_rule.protocol), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'direction': self._default_sg_rule.direction, + 'ethertype': self._default_sg_rule.ether_type, + 'port_range_min': self._default_sg_rule.port_range_min, + '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': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_create_with_description(self): + self._setup_default_security_group_rule( + { + 'description': 'Setting SGR', + } + ) + arglist = [ + '--description', + self._default_sg_rule.description, + ] + verifylist = [ + ('description', self._default_sg_rule.description), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.create_default_security_group_rule.assert_called_once_with( + **{ + 'description': self._default_sg_rule.description, + 'direction': self._default_sg_rule.direction, + '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': True, + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + +class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): + # The default security group rules to be deleted. + default_security_group_rule_attrs = { + 'direction': 'ingress', + 'ether_type': 'IPv4', + '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', + 'location': 'MUNCHMUNCHMUNCH', + 'used_in_default_sg': False, + 'used_in_non_default_sg': True, + } + _default_sg_rules = list( + sdk_fakes.generate_fake_resources( + _default_security_group_rule.DefaultSecurityGroupRule, + count=2, + attrs=default_security_group_rule_attrs, + ) + ) + + def setUp(self): + super(TestDeleteDefaultSecurityGroupRule, self).setUp() + + self.sdk_client.delete_default_security_group_rule.return_value = None + + # Get the command object to test + self.cmd = default_security_group_rule.DeleteDefaultSecurityGroupRule( + self.app, self.namespace + ) + + def test_default_security_group_rule_delete(self): + arglist = [ + self._default_sg_rules[0].id, + ] + verifylist = [ + ('rule', [self._default_sg_rules[0].id]), + ] + self.sdk_client.find_default_security_group_rule.return_value = ( + self._default_sg_rules[0] + ) + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.sdk_client.delete_default_security_group_rule.assert_called_once_with( + self._default_sg_rules[0] + ) + self.assertIsNone(result) + + def test_multi_default_security_group_rules_delete(self): + arglist = [] + verifylist = [] + + for s in self._default_sg_rules: + arglist.append(s.id) + verifylist = [ + ('rule', arglist), + ] + self.sdk_client.find_default_security_group_rule.side_effect = ( + self._default_sg_rules + ) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for s in self._default_sg_rules: + calls.append(call(s)) + self.sdk_client.delete_default_security_group_rule.assert_has_calls( + calls + ) + self.assertIsNone(result) + + def test_multi_default_security_group_rules_delete_with_exception(self): + arglist = [ + self._default_sg_rules[0].id, + 'unexist_rule', + ] + verifylist = [ + ('rule', [self._default_sg_rules[0].id, 'unexist_rule']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [ + self._default_sg_rules[0], + exceptions.CommandError, + ] + self.sdk_client.find_default_security_group_rule = mock.Mock( + side_effect=find_mock_result + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 default rules failed to delete.', str(e)) + + self.sdk_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( + 'unexist_rule', ignore_missing=False + ) + self.sdk_client.delete_default_security_group_rule.assert_called_once_with( + self._default_sg_rules[0] + ) + + +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} + ) + _default_sg_rule_icmp = sdk_fakes.generate_fake_resource( + _default_security_group_rule.DefaultSecurityGroupRule, + **{'protocol': 'icmp', 'remote_ip_prefix': '10.0.2.0/24'} + ) + _default_sg_rules = [ + _default_sg_rule_tcp, + _default_sg_rule_icmp, + ] + + expected_columns = ( + 'ID', + 'IP Protocol', + 'Ethertype', + 'IP Range', + 'Port Range', + 'Direction', + 'Remote Security Group', + 'Remote Address Group', + 'Used in default Security Group', + 'Used in custom Security Group', + ) + + expected_data = [] + expected_data_no_group = [] + for _default_sg_rule in _default_sg_rules: + expected_data.append( + ( + _default_sg_rule.id, + _default_sg_rule.protocol, + _default_sg_rule.ether_type, + _default_sg_rule.remote_ip_prefix, + network_utils.format_network_port_range(_default_sg_rule), + _default_sg_rule.direction, + _default_sg_rule.remote_group_id, + _default_sg_rule.remote_address_group_id, + _default_sg_rule.used_in_default_sg, + _default_sg_rule.used_in_non_default_sg, + ) + ) + + def setUp(self): + super(TestListDefaultSecurityGroupRule, self).setUp() + + self.sdk_client.default_security_group_rules.return_value = ( + self._default_sg_rules + ) + + # Get the command object to test + self.cmd = default_security_group_rule.ListDefaultSecurityGroupRule( + self.app, self.namespace + ) + + def test_list_default(self): + self._default_sg_rule_tcp.port_range_min = 80 + parsed_args = self.check_parser(self.cmd, [], []) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.default_security_group_rules.assert_called_once_with( + **{} + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, list(data)) + + def test_list_with_protocol(self): + self._default_sg_rule_tcp.port_range_min = 80 + arglist = [ + '--protocol', + 'tcp', + ] + verifylist = [ + ('protocol', 'tcp'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.default_security_group_rules.assert_called_once_with( + **{ + 'protocol': 'tcp', + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, list(data)) + + def test_list_with_ingress(self): + self._default_sg_rule_tcp.port_range_min = 80 + arglist = [ + '--ingress', + ] + verifylist = [ + ('ingress', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.default_security_group_rules.assert_called_once_with( + **{ + 'direction': 'ingress', + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, list(data)) + + def test_list_with_wrong_egress(self): + self._default_sg_rule_tcp.port_range_min = 80 + arglist = [ + '--egress', + ] + verifylist = [ + ('egress', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.default_security_group_rules.assert_called_once_with( + **{ + 'direction': 'egress', + } + ) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, list(data)) + + +class TestShowDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): + # The default security group rule to be shown. + _default_sg_rule = sdk_fakes.generate_fake_resource( + _default_security_group_rule.DefaultSecurityGroupRule + ) + + columns = ( + 'description', + 'direction', + 'ether_type', + 'id', + 'port_range_max', + 'port_range_min', + 'protocol', + 'remote_address_group_id', + 'remote_group_id', + 'remote_ip_prefix', + 'used_in_default_sg', + 'used_in_non_default_sg', + ) + + data = ( + _default_sg_rule.description, + _default_sg_rule.direction, + _default_sg_rule.ether_type, + _default_sg_rule.id, + _default_sg_rule.port_range_max, + _default_sg_rule.port_range_min, + _default_sg_rule.protocol, + _default_sg_rule.remote_address_group_id, + _default_sg_rule.remote_group_id, + _default_sg_rule.remote_ip_prefix, + _default_sg_rule.used_in_default_sg, + _default_sg_rule.used_in_non_default_sg, + ) + + def setUp(self): + super(TestShowDefaultSecurityGroupRule, self).setUp() + + self.sdk_client.find_default_security_group_rule.return_value = ( + self._default_sg_rule + ) + + # Get the command object to test + self.cmd = default_security_group_rule.ShowDefaultSecurityGroupRule( + self.app, self.namespace + ) + + def test_show_no_options(self): + self.assertRaises( + tests_utils.ParserException, self.check_parser, self.cmd, [], [] + ) + + def test_show_all_options(self): + arglist = [ + self._default_sg_rule.id, + ] + verifylist = [ + ('rule', self._default_sg_rule.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.sdk_client.find_default_security_group_rule.assert_called_once_with( + self._default_sg_rule.id, ignore_missing=False + ) + self.assertEqual(self.columns, columns) + self.assertEqual(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 2a1609bac..853920439 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 @@ -49,7 +49,7 @@ def _setup_security_group_rule(self, attrs=None): ( expected_columns, expected_data, - ) = security_group_rule._format_security_group_rule_show( + ) = network_utils.format_security_group_rule_show( self._security_group_rule ) return expected_columns, expected_data @@ -513,7 +513,7 @@ class TestShowSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): # The security group rule to be shown. _security_group_rule = compute_fakes.create_one_security_group_rule() - columns, data = security_group_rule._format_security_group_rule_show( + columns, data = network_utils.format_security_group_rule_show( _security_group_rule ) 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 675ba9bd8..7b28ae345 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 @@ -16,6 +16,7 @@ from osc_lib import exceptions +from openstackclient.network import utils as network_utils from openstackclient.network.v2 import security_group_rule from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit.network.v2 import fakes as network_fakes @@ -1124,9 +1125,7 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): _security_group_rule.protocol, _security_group_rule.ether_type, _security_group_rule.remote_ip_prefix, - security_group_rule._format_network_port_range( - _security_group_rule - ), + network_utils.format_network_port_range(_security_group_rule), _security_group_rule.direction, _security_group_rule.remote_group_id, _security_group_rule.remote_address_group_id, @@ -1138,9 +1137,7 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): _security_group_rule.protocol, _security_group_rule.ether_type, _security_group_rule.remote_ip_prefix, - security_group_rule._format_network_port_range( - _security_group_rule - ), + network_utils.format_network_port_range(_security_group_rule), _security_group_rule.direction, _security_group_rule.remote_group_id, _security_group_rule.remote_address_group_id, diff --git a/releasenotes/notes/Add-default-security-group-rule-CRUD-2916568f829ea38c.yaml b/releasenotes/notes/Add-default-security-group-rule-CRUD-2916568f829ea38c.yaml new file mode 100644 index 000000000..955d50155 --- /dev/null +++ b/releasenotes/notes/Add-default-security-group-rule-CRUD-2916568f829ea38c.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Add ``default security group rule create``, ``default security group rule + delete``, ``default security group rule list`` and ``default security group + rule show`` commands to support Neutron Default Security Group Rule CRUD + operations. + [Bug `1983053 `_] diff --git a/requirements.txt b/requirements.txt index a6b67dcc3..a1b87a042 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>=1.4.0 # Apache-2.0 +openstacksdk>=2.0.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 de1350756..26b5ae309 100644 --- a/setup.cfg +++ b/setup.cfg @@ -571,6 +571,11 @@ openstack.network.v2 = 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 From 5d1d6b5a06419d55d38279c0ce8794e7d2fafe8c Mon Sep 17 00:00:00 2001 From: gtema Date: Wed, 25 Oct 2023 18:54:22 +0200 Subject: [PATCH 018/403] Switch back to Launchpad Change the links to issue tracker. Change-Id: Iaf93c892d211fd7465395d8830f56c2977a88f8b --- CONTRIBUTING.rst | 2 +- doc/source/cli/man/openstack.rst | 4 ++-- doc/source/conf.py | 1 - doc/source/index.rst | 7 +++---- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index f8732b721..9d764b057 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -11,7 +11,7 @@ to set up and use Gerrit: Bugs should be filed on StoryBoard: - https://storyboard.openstack.org/#!/project/openstack/python-openstackclient + https://launchpad.net/python-openstackclient Developers should also join the discussion on the mailing list, at: diff --git a/doc/source/cli/man/openstack.rst b/doc/source/cli/man/openstack.rst index 29db06419..8f7124dd6 100644 --- a/doc/source/cli/man/openstack.rst +++ b/doc/source/cli/man/openstack.rst @@ -593,8 +593,8 @@ The following environment variables can be set to alter the behaviour of :progra BUGS ==== -Bug reports are accepted at the python-openstackclient StoryBoard project -"https://storyboard.openstack.org/#!/project/975". +Bug reports are accepted at the python-openstackclient Launchpad project +"https://bugs.launchpad.net/python-openstackclient". AUTHORS diff --git a/doc/source/conf.py b/doc/source/conf.py index 500bdacc8..0500d482d 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -31,7 +31,6 @@ # openstackdocstheme options openstackdocs_repo_name = 'openstack/python-openstackclient' -openstackdocs_use_storyboard = True openstackdocs_auto_name = False # Add project 'foo' to this list to enable the :foo-doc: role diff --git a/doc/source/index.rst b/doc/source/index.rst index 7675d6c37..21ae8efc3 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -54,16 +54,15 @@ Contributing ============ OpenStackClient utilizes all of the usual OpenStack processes and requirements for -contributions. The code is hosted `on OpenStack's Git server`_. `Bug reports`_ -may be submitted to the :code:`python-openstackclient` `Storyboard project`_. +contributions. The code is hosted `on OpenStack's Git server`_. Bug reports +may be submitted to the :code:`python-openstackclient` `Launchpad project`_. Code may be submitted to the :code:`openstack/python-openstackclient` project using `Gerrit`_. Developers may also be found in the `IRC channel`_ ``#openstack-sdks``. .. _`on OpenStack's Git server`: https://opendev.org/openstack/python-openstackclient/ -.. _`Storyboard project`: https://storyboard.openstack.org/#!/project/openstack/python-openstackclient +.. _`Launchpad project`: https://bugs.launchpad.net/python-openstackclient .. _Gerrit: http://docs.openstack.org/infra/manual/developers.html#development-workflow -.. _Bug reports: https://storyboard.openstack.org/#!/project/975 .. _PyPi: https://pypi.org/project/python-openstackclient .. _tarball: http://tarballs.openstack.org/python-openstackclient .. _IRC channel: https://wiki.openstack.org/wiki/IRC From 67bec7785ce2e1af745a55d1838315dc6e849eaa Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 29 May 2023 18:12:30 +0100 Subject: [PATCH 019/403] volume: Allow filtering volume types by properties Starting in volume API microversion 3.52, it is possible to filter volume types by extra specs (a.k.a. properties). Even non-admins can do this for so-called "user visible" extra specs. Make it possible to do this. While we're here, the test file is renamed to match the name of the module under test. Change-Id: Ia9acc06c0c615bf24e12b63146ea97ac2505f596 Signed-off-by: Stephen Finucane --- .../tests/unit/volume/v2/test_volume_type.py | 77 +++++++++++++++++-- openstackclient/volume/v2/volume_type.py | 47 +++++++++-- ...st-properties-filter-8532f96d16733915.yaml | 6 ++ 3 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 releasenotes/notes/volume-type-list-properties-filter-8532f96d16733915.yaml diff --git a/openstackclient/tests/unit/volume/v2/test_volume_type.py b/openstackclient/tests/unit/volume/v2/test_volume_type.py index ebcff45fa..7f235e17a 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_type.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_type.py @@ -15,6 +15,7 @@ from unittest import mock from unittest.mock import call +from cinderclient import api_versions from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib import utils @@ -329,7 +330,9 @@ def test_type_list_without_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_types_mock.list.assert_called_once_with(is_public=None) + self.volume_types_mock.list.assert_called_once_with( + search_opts={}, is_public=None + ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) @@ -346,7 +349,9 @@ def test_type_list_with_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_types_mock.list.assert_called_once_with(is_public=True) + self.volume_types_mock.list.assert_called_once_with( + search_opts={}, is_public=True + ) self.assertEqual(self.columns_long, columns) self.assertCountEqual(self.data_long, list(data)) @@ -362,7 +367,9 @@ def test_type_list_with_private_option(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_types_mock.list.assert_called_once_with(is_public=False) + self.volume_types_mock.list.assert_called_once_with( + search_opts={}, is_public=False + ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) @@ -383,6 +390,60 @@ def test_type_list_with_default_option(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data_with_default_type, list(data)) + def test_type_list_with_property_option(self): + self.app.client_manager.volume.api_version = api_versions.APIVersion( + '3.52' + ) + + arglist = [ + "--property", + "multiattach= True", + ] + verifylist = [ + ("encryption_type", False), + ("long", False), + ("is_public", None), + ("default", False), + ("properties", {"multiattach": " True"}), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volume_types_mock.list.assert_called_once_with( + search_opts={"extra_specs": {"multiattach": " True"}}, + is_public=None, + ) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, list(data)) + + def test_type_list_with_property_option_pre_v352(self): + self.app.client_manager.volume.api_version = api_versions.APIVersion( + '3.51' + ) + + arglist = [ + "--property", + "multiattach= True", + ] + verifylist = [ + ("encryption_type", False), + ("long", False), + ("is_public", None), + ("default", False), + ("properties", {"multiattach": " True"}), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertIn( + '--os-volume-api-version 3.52 or greater is required', + str(exc), + ) + 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}, @@ -428,7 +489,9 @@ def test_type_list_with_encryption(self): columns, data = self.cmd.take_action(parsed_args) self.volume_encryption_types_mock.list.assert_called_once_with() - self.volume_types_mock.list.assert_called_once_with(is_public=None) + self.volume_types_mock.list.assert_called_once_with( + search_opts={}, is_public=None + ) self.assertEqual(encryption_columns, columns) self.assertCountEqual(encryption_data, list(data)) @@ -461,7 +524,7 @@ def test_type_set(self): verifylist = [ ('name', 'new_name'), ('description', 'new_description'), - ('property', None), + ('properties', None), ('volume_type', self.volume_type.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -491,7 +554,7 @@ def test_type_set_property(self): verifylist = [ ('name', None), ('description', None), - ('property', {'myprop': 'myvalue'}), + ('properties', {'myprop': 'myvalue'}), ('volume_type', self.volume_type.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -859,7 +922,7 @@ def test_type_unset(self): self.volume_type.id, ] verifylist = [ - ('property', ['property', 'multi_property']), + ('properties', ['property', 'multi_property']), ('volume_type', self.volume_type.id), ] diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index 3d8a50105..bf10a2e54 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -17,6 +17,7 @@ import functools import logging +from cinderclient import api_versions from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions @@ -139,6 +140,7 @@ def get_parser(self, prog_name): '--property', metavar='', action=parseractions.KeyValueAction, + dest='properties', help=_( 'Set a property on this volume type ' '(repeat option to set multiple properties)' @@ -232,11 +234,13 @@ def take_action(self, parsed_args): "type: %(e)s" ) LOG.error(msg % {'project': parsed_args.project, 'e': e}) - if parsed_args.property: - result = volume_type.set_keys(parsed_args.property) + + if parsed_args.properties: + result = volume_type.set_keys(parsed_args.properties) volume_type._info.update( {'properties': format_columns.DictColumn(result)} ) + if ( parsed_args.encryption_provider or parsed_args.encryption_cipher @@ -261,6 +265,7 @@ def take_action(self, parsed_args): 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())) @@ -348,6 +353,18 @@ def get_parser(self, prog_name): "(admin only)" ), ) + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + dest='properties', + help=_( + 'Filter by a property on the volume types ' + '(repeat option to filter by multiple properties) ' + '(admin only except for user-visible extra specs) ' + '(supported by --os-volume-api-version 3.52 or above)' + ), + ) return parser def take_action(self, parsed_args): @@ -375,8 +392,22 @@ def take_action(self, parsed_args): if parsed_args.default: data = [volume_client.volume_types.default()] else: + search_opts = {} + + if parsed_args.properties: + if volume_client.api_version < api_versions.APIVersion('3.52'): + msg = _( + "--os-volume-api-version 3.52 or greater is required " + "to use the '--property' option" + ) + raise exceptions.CommandError(msg) + + # we pass this through as-is + search_opts['extra_specs'] = parsed_args.properties + data = volume_client.volume_types.list( - is_public=parsed_args.is_public + search_opts=search_opts, + is_public=parsed_args.is_public, ) formatters = {'Extra Specs': format_columns.DictColumn} @@ -445,6 +476,7 @@ def get_parser(self, prog_name): '--property', metavar='', action=parseractions.KeyValueAction, + dest='properties', help=_( 'Set a property on this volume type ' '(repeat option to set multiple properties)' @@ -555,9 +587,9 @@ def take_action(self, parsed_args): ) result += 1 - if parsed_args.property: + if parsed_args.properties: try: - volume_type.set_keys(parsed_args.property) + volume_type.set_keys(parsed_args.properties) except Exception as e: LOG.error(_("Failed to set volume type property: %s"), e) result += 1 @@ -689,6 +721,7 @@ def get_parser(self, prog_name): '--property', metavar='', action='append', + dest='properties', help=_( 'Remove a property from this volume type ' '(repeat option to remove multiple properties)' @@ -723,9 +756,9 @@ def take_action(self, parsed_args): ) result = 0 - if parsed_args.property: + if parsed_args.properties: try: - volume_type.unset_keys(parsed_args.property) + volume_type.unset_keys(parsed_args.properties) except Exception as e: LOG.error(_("Failed to unset volume type property: %s"), e) result += 1 diff --git a/releasenotes/notes/volume-type-list-properties-filter-8532f96d16733915.yaml b/releasenotes/notes/volume-type-list-properties-filter-8532f96d16733915.yaml new file mode 100644 index 000000000..178622c47 --- /dev/null +++ b/releasenotes/notes/volume-type-list-properties-filter-8532f96d16733915.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + The ``volume type list`` command now accepts a ``--property =`` + option, allowing users to filter volume types by their extra spec + properties. From e0c7cef434e90bb8b971040b0c1a47964c6d89fb Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 29 May 2023 16:31:21 +0100 Subject: [PATCH 020/403] volume: Add aliases for common volume type props Add aliases for the enabling multiattach, caching and replication via extra spec properties. These are useful since the syntax for setting each of them is a bit weird and frankly not very discoverable. Change-Id: I08b51224c66a34a9dcfcffc95847e61d9aac0e7e Signed-off-by: Stephen Finucane --- .../tests/unit/volume/v2/test_volume_type.py | 104 ++++++++++--- openstackclient/volume/v2/volume_type.py | 143 ++++++++++++++++-- ...ume-type-extra-specs-22a22fcb6e269832.yaml | 7 + 3 files changed, 223 insertions(+), 31 deletions(-) create mode 100644 releasenotes/notes/volume-type-extra-specs-22a22fcb6e269832.yaml diff --git a/openstackclient/tests/unit/volume/v2/test_volume_type.py b/openstackclient/tests/unit/volume/v2/test_volume_type.py index 7f235e17a..e2b503b9b 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_type.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_type.py @@ -48,18 +48,19 @@ def setUp(self): class TestTypeCreate(TestType): - project = identity_fakes.FakeProject.create_one_project() - columns = ( - 'description', - 'id', - 'is_public', - 'name', - ) - def setUp(self): super().setUp() - self.new_volume_type = volume_fakes.create_one_volume_type() + self.new_volume_type = volume_fakes.create_one_volume_type( + methods={'set_keys': None}, + ) + self.project = identity_fakes.FakeProject.create_one_project() + self.columns = ( + 'description', + 'id', + 'is_public', + 'name', + ) self.data = ( self.new_volume_type.description, self.new_volume_type.id, @@ -123,7 +124,45 @@ def test_type_create_private(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) - def test_public_type_create_with_project(self): + def test_type_create_with_properties(self): + arglist = [ + '--property', + 'myprop=myvalue', + # this combination isn't viable server-side but is okay for testing + '--multiattach', + '--cacheable', + '--replicated', + self.new_volume_type.name, + ] + verifylist = [ + ('properties', {'myprop': 'myvalue'}), + ('multiattach', True), + ('cacheable', True), + ('replicated', True), + ('name', self.new_volume_type.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volume_types_mock.create.assert_called_with( + self.new_volume_type.name, description=None + ) + self.new_volume_type.set_keys.assert_called_once_with( + { + 'myprop': 'myvalue', + 'multiattach': ' True', + 'cacheable': ' True', + 'replication_enabled': ' True', + } + ) + + self.columns += ('properties',) + self.data += (format_columns.DictColumn(None),) + + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_public_type_create_with_project_public(self): arglist = [ '--project', self.project.id, @@ -136,7 +175,9 @@ def test_public_type_create_with_project(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args + exceptions.CommandError, + self.cmd.take_action, + parsed_args, ) def test_type_create_with_encryption(self): @@ -390,47 +431,60 @@ def test_type_list_with_default_option(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data_with_default_type, list(data)) - def test_type_list_with_property_option(self): + def test_type_list_with_properties(self): self.app.client_manager.volume.api_version = api_versions.APIVersion( '3.52' ) arglist = [ "--property", - "multiattach= True", + "foo=bar", + "--multiattach", + "--cacheable", + "--replicated", ] verifylist = [ ("encryption_type", False), ("long", False), ("is_public", None), ("default", False), - ("properties", {"multiattach": " True"}), + ("properties", {"foo": "bar"}), + ("multiattach", True), + ("cacheable", True), + ("replicated", True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volume_types_mock.list.assert_called_once_with( - search_opts={"extra_specs": {"multiattach": " True"}}, + search_opts={ + "extra_specs": { + "foo": "bar", + "multiattach": " True", + "cacheable": " True", + "replication_enabled": " True", + } + }, is_public=None, ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) - def test_type_list_with_property_option_pre_v352(self): + def test_type_list_with_properties_pre_v352(self): self.app.client_manager.volume.api_version = api_versions.APIVersion( '3.51' ) arglist = [ "--property", - "multiattach= True", + "foo=bar", ] verifylist = [ ("encryption_type", False), ("long", False), ("is_public", None), ("default", False), - ("properties", {"multiattach": " True"}), + ("properties", {"foo": "bar"}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -549,12 +603,19 @@ def test_type_set_property(self): arglist = [ '--property', 'myprop=myvalue', + # this combination isn't viable server-side but is okay for testing + '--multiattach', + '--cacheable', + '--replicated', self.volume_type.id, ] verifylist = [ ('name', None), ('description', None), ('properties', {'myprop': 'myvalue'}), + ('multiattach', True), + ('cacheable', True), + ('replicated', True), ('volume_type', self.volume_type.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -563,7 +624,12 @@ def test_type_set_property(self): self.assertIsNone(result) self.volume_type.set_keys.assert_called_once_with( - {'myprop': 'myvalue'} + { + 'myprop': 'myvalue', + 'multiattach': ' True', + 'cacheable': ' True', + 'replication_enabled': ' True', + } ) self.volume_type_access_mock.add_project_access.assert_not_called() self.volume_encryption_types_mock.update.assert_not_called() diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index bf10a2e54..c8e562743 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -146,14 +146,45 @@ def get_parser(self, prog_name): '(repeat option to set multiple properties)' ), ) + parser.add_argument( + '--multiattach', + action='store_true', + default=False, + help=_( + "Enable multi-attach for this volume type " + "(this is an alias for '--property multiattach= True') " + "(requires driver support)" + ), + ) + parser.add_argument( + '--cacheable', + action='store_true', + default=False, + help=_( + "Enable caching for this volume type " + "(this is an alias for '--property cacheable= True') " + "(requires driver support)" + ), + ) + parser.add_argument( + '--replicated', + action='store_true', + default=False, + help=_( + "Enabled replication for this volume type " + "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(requires driver support)" + ), + ) parser.add_argument( '--project', metavar='', help=_( "Allow to access private type (name or ID) " - "(Must be used with --private option)" + "(must be used with --private option)" ), ) + identity_common.add_project_domain_option_to_parser(parser) # TODO(Huanxuan Ao): Add choices for each "--encryption-*" option. parser.add_argument( '--encryption-provider', @@ -161,8 +192,8 @@ def get_parser(self, prog_name): 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 ' + '(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")' ), @@ -198,7 +229,6 @@ def get_parser(self, prog_name): '"--encryption-provider")' ), ) - identity_common.add_project_domain_option_to_parser(parser) return parser def take_action(self, parsed_args): @@ -235,8 +265,17 @@ def take_action(self, parsed_args): ) LOG.error(msg % {'project': parsed_args.project, 'e': e}) + properties = {} if parsed_args.properties: - result = volume_type.set_keys(parsed_args.properties) + properties.update(parsed_args.properties) + if parsed_args.multiattach: + properties['multiattach'] = ' True' + if parsed_args.cacheable: + properties['cacheable'] = ' True' + if parsed_args.replicated: + properties['replication_enabled'] = ' True' + if properties: + result = volume_type.set_keys(properties) volume_type._info.update( {'properties': format_columns.DictColumn(result)} ) @@ -365,6 +404,37 @@ def get_parser(self, prog_name): '(supported by --os-volume-api-version 3.52 or above)' ), ) + parser.add_argument( + '--multiattach', + action='store_true', + default=False, + help=_( + "List only volume types with multi-attach enabled " + "(this is an alias for '--property multiattach= True') " + "(supported by --os-volume-api-version 3.52 or above)" + ), + ) + parser.add_argument( + '--cacheable', + action='store_true', + default=False, + help=_( + "List only volume types with caching enabled " + "(this is an alias for '--property cacheable= True') " + "(admin only) " + "(supported by --os-volume-api-version 3.52 or above)" + ), + ) + parser.add_argument( + '--replicated', + action='store_true', + default=False, + help=_( + "List only volume types with replication enabled " + "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(supported by --os-volume-api-version 3.52 or above)" + ), + ) return parser def take_action(self, parsed_args): @@ -393,17 +463,25 @@ def take_action(self, parsed_args): data = [volume_client.volume_types.default()] else: search_opts = {} - + properties = {} if parsed_args.properties: + properties.update(parsed_args.properties) + if parsed_args.multiattach: + properties['multiattach'] = ' True' + if parsed_args.cacheable: + properties['cacheable'] = ' True' + if parsed_args.replicated: + properties['replication_enabled'] = ' True' + if properties: if volume_client.api_version < api_versions.APIVersion('3.52'): msg = _( "--os-volume-api-version 3.52 or greater is required " - "to use the '--property' option" + "to use the '--property' option or any of the alias " + "options" ) raise exceptions.CommandError(msg) - # we pass this through as-is - search_opts['extra_specs'] = parsed_args.properties + search_opts['extra_specs'] = properties data = volume_client.volume_types.list( search_opts=search_opts, @@ -482,6 +560,36 @@ def get_parser(self, prog_name): '(repeat option to set multiple properties)' ), ) + parser.add_argument( + '--multiattach', + action='store_true', + default=False, + help=_( + "Enable multi-attach for this volume type " + "(this is an alias for '--property multiattach= True') " + "(requires driver support)" + ), + ) + parser.add_argument( + '--cacheable', + action='store_true', + default=False, + help=_( + "Enable caching for this volume type " + "(this is an alias for '--property cacheable= True') " + "(requires driver support)" + ), + ) + parser.add_argument( + '--replicated', + action='store_true', + default=False, + help=_( + "Enabled replication for this volume type " + "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(requires driver support)" + ), + ) parser.add_argument( '--project', metavar='', @@ -587,11 +695,22 @@ def take_action(self, parsed_args): ) result += 1 + properties = {} + + properties = {} if parsed_args.properties: + properties.update(parsed_args.properties) + if parsed_args.multiattach: + properties['multiattach'] = ' True' + if parsed_args.cacheable: + properties['cacheable'] = ' True' + if parsed_args.replicated: + properties['replication_enabled'] = ' True' + if properties: try: - volume_type.set_keys(parsed_args.properties) + volume_type.set_keys(properties) except Exception as e: - LOG.error(_("Failed to set volume type property: %s"), e) + LOG.error(_("Failed to set volume type properties: %s"), e) result += 1 if parsed_args.project: @@ -760,7 +879,7 @@ def take_action(self, parsed_args): try: volume_type.unset_keys(parsed_args.properties) except Exception as e: - LOG.error(_("Failed to unset volume type property: %s"), e) + LOG.error(_("Failed to unset volume type properties: %s"), e) result += 1 if parsed_args.project: diff --git a/releasenotes/notes/volume-type-extra-specs-22a22fcb6e269832.yaml b/releasenotes/notes/volume-type-extra-specs-22a22fcb6e269832.yaml new file mode 100644 index 000000000..fb30f98cf --- /dev/null +++ b/releasenotes/notes/volume-type-extra-specs-22a22fcb6e269832.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + The ``volume type create``, ``volume type set``, ``volume type list`` + commands now accept three new options - ``--multiattach``, ``--cacheable``, + and ``--replicated`` - which are short cuts for setting or filtering on + the relevant properties on the volume type. From a3410cd4f785ec188973d85509448a320e882514 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 29 May 2023 18:01:22 +0100 Subject: [PATCH 021/403] volume: Add alias for volume type AZs Another quality of life improvements. The key for this one is weird and the whole thing is a little more involved, hence why it's kept separate. Change-Id: I75aa85f27905104dc84fffe823c01b4c90a6a822 Signed-off-by: Stephen Finucane --- .../tests/unit/volume/v2/test_volume_type.py | 12 ++++++ openstackclient/volume/v2/volume_type.py | 42 +++++++++++++++++++ ...ume-type-extra-specs-22a22fcb6e269832.yaml | 6 +-- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_type.py b/openstackclient/tests/unit/volume/v2/test_volume_type.py index e2b503b9b..2157eabcc 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_type.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_type.py @@ -132,6 +132,8 @@ def test_type_create_with_properties(self): '--multiattach', '--cacheable', '--replicated', + '--availability-zone', + 'az1', self.new_volume_type.name, ] verifylist = [ @@ -139,6 +141,7 @@ def test_type_create_with_properties(self): ('multiattach', True), ('cacheable', True), ('replicated', True), + ('availability_zones', ['az1']), ('name', self.new_volume_type.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -153,6 +156,7 @@ def test_type_create_with_properties(self): 'multiattach': ' True', 'cacheable': ' True', 'replication_enabled': ' True', + 'RESKEY:availability_zones': 'az1', } ) @@ -442,6 +446,8 @@ def test_type_list_with_properties(self): "--multiattach", "--cacheable", "--replicated", + "--availability-zone", + "az1", ] verifylist = [ ("encryption_type", False), @@ -452,6 +458,7 @@ def test_type_list_with_properties(self): ("multiattach", True), ("cacheable", True), ("replicated", True), + ("availability_zones", ["az1"]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -463,6 +470,7 @@ def test_type_list_with_properties(self): "multiattach": " True", "cacheable": " True", "replication_enabled": " True", + "RESKEY:availability_zones": "az1", } }, is_public=None, @@ -607,6 +615,8 @@ def test_type_set_property(self): '--multiattach', '--cacheable', '--replicated', + '--availability-zone', + 'az1', self.volume_type.id, ] verifylist = [ @@ -616,6 +626,7 @@ def test_type_set_property(self): ('multiattach', True), ('cacheable', True), ('replicated', True), + ('availability_zones', ['az1']), ('volume_type', self.volume_type.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -629,6 +640,7 @@ def test_type_set_property(self): 'multiattach': ' True', 'cacheable': ' True', 'replication_enabled': ' True', + 'RESKEY:availability_zones': 'az1', } ) self.volume_type_access_mock.add_project_access.assert_not_called() diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index c8e562743..ebb364b5d 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -176,6 +176,16 @@ def get_parser(self, prog_name): "(requires driver support)" ), ) + parser.add_argument( + '--availability-zone', + action='append', + dest='availability_zones', + help=_( + "Set an availability zone for this volume type " + "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(repeat option to set multiple availabilty zones)" + ), + ) parser.add_argument( '--project', metavar='', @@ -274,6 +284,10 @@ def take_action(self, parsed_args): properties['cacheable'] = ' True' if parsed_args.replicated: properties['replication_enabled'] = ' True' + if parsed_args.availability_zones: + properties['RESKEY:availability_zones'] = ','.join( + parsed_args.availability_zones + ) if properties: result = volume_type.set_keys(properties) volume_type._info.update( @@ -435,6 +449,16 @@ def get_parser(self, prog_name): "(supported by --os-volume-api-version 3.52 or above)" ), ) + parser.add_argument( + '--availability-zone', + action='append', + dest='availability_zones', + help=_( + "List only volume types with this availability configured " + "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(repeat option to filter on multiple availabilty zones)" + ), + ) return parser def take_action(self, parsed_args): @@ -472,6 +496,10 @@ def take_action(self, parsed_args): properties['cacheable'] = ' True' if parsed_args.replicated: properties['replication_enabled'] = ' True' + if parsed_args.availability_zones: + properties['RESKEY:availability_zones'] = ','.join( + parsed_args.availability_zones + ) if properties: if volume_client.api_version < api_versions.APIVersion('3.52'): msg = _( @@ -590,6 +618,16 @@ def get_parser(self, prog_name): "(requires driver support)" ), ) + parser.add_argument( + '--availability-zone', + action='append', + dest='availability_zones', + help=_( + "Set an availability zone for this volume type " + "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(repeat option to set multiple availabilty zones)" + ), + ) parser.add_argument( '--project', metavar='', @@ -706,6 +744,10 @@ def take_action(self, parsed_args): properties['cacheable'] = ' True' if parsed_args.replicated: properties['replication_enabled'] = ' True' + if parsed_args.availability_zones: + properties['RESKEY:availability_zones'] = ','.join( + parsed_args.availability_zones + ) if properties: try: volume_type.set_keys(properties) diff --git a/releasenotes/notes/volume-type-extra-specs-22a22fcb6e269832.yaml b/releasenotes/notes/volume-type-extra-specs-22a22fcb6e269832.yaml index fb30f98cf..aab21d00a 100644 --- a/releasenotes/notes/volume-type-extra-specs-22a22fcb6e269832.yaml +++ b/releasenotes/notes/volume-type-extra-specs-22a22fcb6e269832.yaml @@ -2,6 +2,6 @@ features: - | The ``volume type create``, ``volume type set``, ``volume type list`` - commands now accept three new options - ``--multiattach``, ``--cacheable``, - and ``--replicated`` - which are short cuts for setting or filtering on - the relevant properties on the volume type. + commands now accept four new options - ``--multiattach``, ``--cacheable``, + ``--replicated``, and ``--availability-zone`` - which are short cuts for + setting or filtering on the relevant properties on the volume type. From 2a2a6e17817a8482770e2be9d3604b9cd45b46f8 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 29 May 2023 16:38:44 +0100 Subject: [PATCH 022/403] trivial: Make better use of argparse Change-Id: Ib76c0d18bf5e44bfb4dcd9d729d9a1c5635cdba7 Signed-off-by: Stephen Finucane --- openstackclient/tests/unit/volume/v2/test_volume_type.py | 1 + openstackclient/volume/v2/volume_type.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_type.py b/openstackclient/tests/unit/volume/v2/test_volume_type.py index 2157eabcc..ca6971e53 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_type.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_type.py @@ -173,6 +173,7 @@ def test_public_type_create_with_project_public(self): self.new_volume_type.name, ] verifylist = [ + ('is_public', None), ('project', self.project.id), ('name', self.new_volume_type.name), ] diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index ebb364b5d..f865c8247 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -250,11 +250,14 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) kwargs = {} + if parsed_args.is_public is not None: kwargs['is_public'] = parsed_args.is_public volume_type = volume_client.volume_types.create( - parsed_args.name, description=parsed_args.description, **kwargs + parsed_args.name, + description=parsed_args.description, + **kwargs, ) volume_type._info.pop('extra_specs') From 6b9f40576d689797dfa2d244f3f670b95cc73404 Mon Sep 17 00:00:00 2001 From: cw0306-lee Date: Mon, 30 Oct 2023 14:17:39 +0900 Subject: [PATCH 023/403] Removed start, end time format. Before fix, openstack usage list command resulted 'str' object has no attribute 'isoformat' error. story: 2010943 task: 48951 Change-Id: I9ee3384cc6df9ca768ac664f01472244dd8e3267 --- openstackclient/compute/v2/usage.py | 10 ++++------ openstackclient/tests/unit/compute/v2/test_usage.py | 9 +++++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index 3b8bda2d1..c89296794 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -153,7 +153,6 @@ def _format_project(project): ) date_cli_format = "%Y-%m-%d" - date_api_format = "%Y-%m-%dT%H:%M:%S" now = datetime.datetime.utcnow() if parsed_args.start: @@ -170,8 +169,8 @@ def _format_project(project): usage_list = list( compute_client.usages( - start=start.strftime(date_api_format), - end=end.strftime(date_api_format), + start=start, + end=end, detailed=True, ) ) @@ -239,7 +238,6 @@ 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" - date_api_format = "%Y-%m-%dT%H:%M:%S" now = datetime.datetime.utcnow() if parsed_args.start: @@ -265,8 +263,8 @@ def take_action(self, parsed_args): usage = compute_client.get_usage( project=project, - start=start.strftime(date_api_format), - end=end.strftime(date_api_format), + start=start, + end=end, ) if parsed_args.formatter == 'table': diff --git a/openstackclient/tests/unit/compute/v2/test_usage.py b/openstackclient/tests/unit/compute/v2/test_usage.py index ad8a8b504..9d0ea45cc 100644 --- a/openstackclient/tests/unit/compute/v2/test_usage.py +++ b/openstackclient/tests/unit/compute/v2/test_usage.py @@ -11,6 +11,7 @@ # under the License. # +import datetime from unittest import mock from openstackclient.compute.v2 import usage as usage_cmds @@ -94,8 +95,8 @@ def test_usage_list_with_options(self): self.projects_mock.list.assert_called_with() self.compute_sdk_client.usages.assert_called_with( - start='2016-11-11T00:00:00', - end='2016-12-20T00:00:00', + start=datetime.datetime(2016, 11, 11, 0, 0), + end=datetime.datetime(2016, 12, 20, 0, 0), detailed=True, ) @@ -190,8 +191,8 @@ def test_usage_show_with_options(self): self.compute_sdk_client.get_usage.assert_called_with( project=self.project.id, - start='2016-11-11T00:00:00', - end='2016-12-20T00:00:00', + start=datetime.datetime(2016, 11, 11, 0, 0), + end=datetime.datetime(2016, 12, 20, 0, 0), ) self.assertEqual(self.columns, columns) From ae10851a682adb1db7d3dca2794415437c2eaa79 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Wed, 6 Sep 2023 00:16:01 +0530 Subject: [PATCH 024/403] Migrate resource filter commands to SDK This patch migrates ``block storage resource filter list`` and ``block storage resource filter show`` commands to SDK. Change-Id: Ibc50963954418aab0be35e120f8dcfadbe4020b8 --- openstackclient/tests/unit/volume/v3/fakes.py | 3 +- .../v3/test_block_storage_resource_filter.py | 47 +++++++++++++------ .../v3/block_storage_resource_filter.py | 44 ++++++++++++----- ...urce-filter-commands-2a353edb965723d1.yaml | 5 ++ 4 files changed, 73 insertions(+), 26 deletions(-) create mode 100644 releasenotes/notes/migrate-resource-filter-commands-2a353edb965723d1.yaml diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index dcd8b11f3..cd914e7d9 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -18,6 +18,7 @@ from openstack.block_storage.v3 import _proxy from openstack.block_storage.v3 import availability_zone as _availability_zone from openstack.block_storage.v3 import extension as _extension +from openstack.block_storage.v3 import resource_filter as _filters from openstack.block_storage.v3 import volume as _volume from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes @@ -243,7 +244,7 @@ def create_one_resource_filter(attrs=None): # Overwrite default attributes if there are some attributes set resource_filter_info.update(attrs) - return fakes.FakeResource(None, resource_filter_info, loaded=True) + return _filters.ResourceFilter(**resource_filter_info) def create_resource_filters(attrs=None, count=2): diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py b/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py index a5c0da3d3..70d8911c4 100644 --- a/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py +++ b/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py @@ -10,7 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. +from unittest import mock + from cinderclient import api_versions +from openstack import utils as sdk_utils +from osc_lib.cli import format_columns from osc_lib import exceptions from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes @@ -21,9 +25,22 @@ class TestBlockStorageResourceFilter(volume_fakes.TestVolume): def setUp(self): super().setUp() - # Get a shortcut to the ResourceFilterManager Mock - self.resource_filter_mock = self.volume_client.resource_filters - self.resource_filter_mock.reset_mock() + patcher = mock.patch.object( + sdk_utils, 'supports_microversion', return_value=True + ) + self.addCleanup(patcher.stop) + self.supports_microversion_mock = patcher.start() + self._set_mock_microversion( + self.app.client_manager.volume.api_version.get_string() + ) + + def _set_mock_microversion(self, mock_v): + """Set a specific microversion for the mock supports_microversion().""" + self.supports_microversion_mock.reset_mock(return_value=True) + self.supports_microversion_mock.side_effect = ( + lambda _, v: api_versions.APIVersion(v) + <= api_versions.APIVersion(mock_v) + ) class TestBlockStorageResourceFilterList(TestBlockStorageResourceFilter): @@ -33,7 +50,7 @@ class TestBlockStorageResourceFilterList(TestBlockStorageResourceFilter): def setUp(self): super().setUp() - self.resource_filter_mock.list.return_value = ( + self.volume_sdk_client.resource_filters.return_value = ( self.fake_resource_filters ) @@ -45,7 +62,7 @@ def setUp(self): ) def test_resource_filter_list(self): - self.volume_client.api_version = api_versions.APIVersion('3.33') + self._set_mock_microversion('3.33') arglist = [] verifylist = [] @@ -55,7 +72,7 @@ def test_resource_filter_list(self): expected_data = tuple( ( resource_filter.resource, - resource_filter.filters, + format_columns.ListColumn(resource_filter.filters), ) for resource_filter in self.fake_resource_filters ) @@ -65,10 +82,10 @@ def test_resource_filter_list(self): self.assertEqual(expected_data, tuple(data)) # checking if proper call was made to list clusters - self.resource_filter_mock.list.assert_called_with() + self.volume_sdk_client.resource_filters.assert_called_with() def test_resource_filter_list_pre_v333(self): - self.volume_client.api_version = api_versions.APIVersion('3.32') + self._set_mock_microversion('3.32') arglist = [] verifylist = [] @@ -89,7 +106,7 @@ class TestBlockStorageResourceFilterShow(TestBlockStorageResourceFilter): def setUp(self): super().setUp() - self.resource_filter_mock.list.return_value = iter( + self.volume_sdk_client.resource_filters.return_value = iter( [self.fake_resource_filter] ) @@ -101,7 +118,7 @@ def setUp(self): ) def test_resource_filter_show(self): - self.volume_client.api_version = api_versions.APIVersion('3.33') + self._set_mock_microversion('3.33') arglist = [ self.fake_resource_filter.resource, @@ -111,10 +128,10 @@ def test_resource_filter_show(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - expected_columns = ('filters', 'resource') + expected_columns = ('Resource', 'Filters') expected_data = ( - self.fake_resource_filter.filters, self.fake_resource_filter.resource, + format_columns.ListColumn(self.fake_resource_filter.filters), ) columns, data = self.cmd.take_action(parsed_args) @@ -122,10 +139,12 @@ def test_resource_filter_show(self): self.assertEqual(expected_data, data) # checking if proper call was made to list clusters - self.resource_filter_mock.list.assert_called_with(resource='volume') + self.volume_sdk_client.resource_filters.assert_called_with( + resource='volume' + ) def test_resource_filter_show_pre_v333(self): - self.volume_client.api_version = api_versions.APIVersion('3.32') + self._set_mock_microversion('3.32') arglist = [ self.fake_resource_filter.resource, diff --git a/openstackclient/volume/v3/block_storage_resource_filter.py b/openstackclient/volume/v3/block_storage_resource_filter.py index 4963fd4c7..494d9cbe1 100644 --- a/openstackclient/volume/v3/block_storage_resource_filter.py +++ b/openstackclient/volume/v3/block_storage_resource_filter.py @@ -12,7 +12,8 @@ """Volume V3 Resource Filters implementations""" -from cinderclient import api_versions +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 @@ -24,9 +25,9 @@ class ListBlockStorageResourceFilter(command.Lister): _description = _('List block storage resource filters') 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.33'): + if not sdk_utils.supports_microversion(volume_client, '3.33'): msg = _( "--os-volume-api-version 3.33 or greater is required to " "support the 'block storage resource filter list' command" @@ -37,12 +38,20 @@ def take_action(self, parsed_args): 'Resource', 'Filters', ) + columns = ( + 'resource', + 'filters', + ) - data = volume_client.resource_filters.list() + data = volume_client.resource_filters() + formatters = {'filters': format_columns.ListColumn} return ( column_headers, - (utils.get_item_properties(s, column_headers) for s in data), + ( + utils.get_item_properties(s, columns, formatters=formatters) + for s in data + ), ) @@ -60,18 +69,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 - if volume_client.api_version < api_versions.APIVersion('3.33'): + if not sdk_utils.supports_microversion(volume_client, '3.33'): msg = _( "--os-volume-api-version 3.33 or greater is required to " "support the 'block storage resource filter show' command" ) raise exceptions.CommandError(msg) - data = volume_client.resource_filters.list( - resource=parsed_args.resource - ) + data = volume_client.resource_filters(resource=parsed_args.resource) if not data: msg = _( "No resource filter with a name of {parsed_args.resource}' " @@ -80,4 +87,19 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) resource_filter = next(data) - return zip(*sorted(resource_filter._info.items())) + column_headers = ( + 'Resource', + 'Filters', + ) + columns = ( + 'resource', + 'filters', + ) + formatters = {'filters': format_columns.ListColumn} + + return ( + column_headers, + utils.get_dict_properties( + resource_filter, columns, formatters=formatters + ), + ) diff --git a/releasenotes/notes/migrate-resource-filter-commands-2a353edb965723d1.yaml b/releasenotes/notes/migrate-resource-filter-commands-2a353edb965723d1.yaml new file mode 100644 index 000000000..bf5ce3b60 --- /dev/null +++ b/releasenotes/notes/migrate-resource-filter-commands-2a353edb965723d1.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Migrated ``block storage resource filters list`` and + ``block storage resource filters show`` commands to SDK. From 60a0e379a4fa629b6997699582d1ad8b102600ca Mon Sep 17 00:00:00 2001 From: Johannes Kulik Date: Fri, 22 Sep 2023 16:31:57 +0200 Subject: [PATCH 025/403] volume: Support same_host, different_host hint as list When creating a volume, the scheduler hints can be supplied as strings. The "same_host" and "different_host" hints can also be supplied as a list if affinity/anti-affinity to multiple volumes is requested [0] The previously-used `KeyValueAction` only supplies strings as values - the last one if multiple --hint contain the same key. An alternative already used in `CreateServer` would be `KeyValueAppendAction`, but only a subset of the scheduler hints accept lists, so we cannot use that in general. Therefore, we create `KeyValueHintAction`. It contains both a `KeyValueAction` and a `KeyValueAppendAction` object and calls the appropriate action based on the beginning of the given values as defined in `APPEND_KEYS`. [0] https://github.com/sapcc/cinder/blob/d96b705774e8ca85c75d3d0292722da8fe8cb14f/cinder/api/schemas/scheduler_hints.py#L31-L65 Change-Id: Ida7f4662dc9fea24510758541fd4f2622b73bf31 --- .../tests/unit/volume/v2/test_volume.py | 68 +++++++++++++++++++ openstackclient/volume/v2/volume.py | 31 ++++++++- 2 files changed, 96 insertions(+), 3 deletions(-) diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index 992b99742..ba92f5b33 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -686,6 +686,74 @@ def test_volume_create_with_multi_source(self): verifylist, ) + def test_volume_create_hints(self): + """--hint needs to behave differently based on the given hint + + different_host and same_host need to append to a list if given multiple + times. All other parameter are strings. + """ + arglist = [ + '--size', + str(self.new_volume.size), + '--hint', + 'k=v', + '--hint', + 'k=v2', + '--hint', + 'same_host=v3', + '--hint', + 'same_host=v4', + '--hint', + 'different_host=v5', + '--hint', + 'local_to_instance=v6', + '--hint', + 'different_host=v7', + self.new_volume.name, + ] + verifylist = [ + ('size', self.new_volume.size), + ( + 'hint', + { + 'k': 'v2', + 'same_host': ['v3', 'v4'], + 'local_to_instance': 'v6', + 'different_host': ['v5', 'v7'], + }, + ), + ('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=None, + source_volid=None, + consistencygroup_id=None, + scheduler_hints={ + 'k': 'v2', + 'same_host': ['v3', 'v4'], + 'local_to_instance': 'v6', + 'different_host': ['v5', 'v7'], + }, + backup_id=None, + ) + + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.datalist, data) + class TestVolumeDelete(TestVolume): def setUp(self): diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 43a64664b..096305543 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -33,6 +33,29 @@ LOG = logging.getLogger(__name__) +class KeyValueHintAction(argparse.Action): + """Uses KeyValueAction or KeyValueAppendAction based on the given key""" + + APPEND_KEYS = ('same_host', 'different_host') + + def __init__(self, *args, **kwargs): + self._key_value_action = parseractions.KeyValueAction(*args, **kwargs) + self._key_value_append_action = parseractions.KeyValueAppendAction( + *args, **kwargs + ) + super().__init__(*args, **kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + if values.startswith(self.APPEND_KEYS): + self._key_value_append_action( + parser, namespace, values, option_string=option_string + ) + else: + self._key_value_action( + parser, namespace, values, option_string=option_string + ) + + class AttachmentsColumn(cliff_columns.FormattableColumn): """Formattable column for attachments column. @@ -162,10 +185,12 @@ def get_parser(self, prog_name): parser.add_argument( "--hint", metavar="", - action=parseractions.KeyValueAction, + action=KeyValueHintAction, help=_( - "Arbitrary scheduler hint key-value pairs to help boot " - "an instance (repeat option to set multiple hints)" + "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() From 692ee752b6bff65f6105e9a7c6c01f2d8fd3fcf2 Mon Sep 17 00:00:00 2001 From: "Dr. Jens Harbott" Date: Tue, 31 Oct 2023 10:19:43 +0100 Subject: [PATCH 026/403] Update from storyboard to launchpad pt. 2 This was missed in [0]. Also update the mailing list link after it moved to mailman3. [0] Iaf93c892d211fd7465395d8830f56c2977a88f8b Change-Id: I91168713a854be0c4348bab3fa34f4b1a924f451 --- CONTRIBUTING.rst | 6 +++--- README.rst | 4 ++-- releasenotes/source/conf.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 9d764b057..74b442ef6 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -9,13 +9,13 @@ to set up and use Gerrit: https://docs.openstack.org/contributors/code-and-documentation/quick-start.html -Bugs should be filed on StoryBoard: +Bugs should be filed on Launchpad: - https://launchpad.net/python-openstackclient + https://bugs.launchpad.net/python-openstackclient Developers should also join the discussion on the mailing list, at: - http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss + https://lists.openstack.org/mailman3/lists/openstack-discuss.lists.openstack.org/ or join the IRC channel on diff --git a/README.rst b/README.rst index 7f31bcdbe..f526ec6ad 100644 --- a/README.rst +++ b/README.rst @@ -24,7 +24,7 @@ language to describe operations in OpenStack. * `PyPi`_ - package installation * `Online Documentation`_ -* `Storyboard project`_ - bugs and feature requests +* `Launchpad project`_ - bugs and feature requests * `Blueprints`_ - feature specifications (historical only) * `Source`_ * `Developer`_ - getting started as a developer @@ -36,7 +36,7 @@ language to describe operations in OpenStack. .. _PyPi: https://pypi.org/project/python-openstackclient .. _Online Documentation: https://docs.openstack.org/python-openstackclient/latest/ .. _Blueprints: https://blueprints.launchpad.net/python-openstackclient -.. _`Storyboard project`: https://storyboard.openstack.org/#!/project/openstack/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 diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index 3302cd3a7..2ad39ab62 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -53,7 +53,7 @@ # openstackdocstheme options openstackdocs_repo_name = 'openstack/python-openstackclient' -openstackdocs_use_storyboard = True +openstackdocs_use_storyboard = False openstackdocs_auto_name = False # Set aliases for extlinks From 4673c8915402726f38eacce89bbae4a59e34c23d Mon Sep 17 00:00:00 2001 From: Clark Boylan Date: Thu, 12 Oct 2023 08:24:29 -0700 Subject: [PATCH 027/403] Update the docker image to python3.11 OpenDev is trying to get consumers of these images to update to newer iterations so that old images can be dropped from the build list. OSC is successfully running voting python3.11 unittest jobs at this point. This should make it safe to update the Docker container image for OSC to python3.11 without risk of regressions. Making this update should bring some (small) performance updates as py311 is generally quicker than py310. As mentioned before it will also allow OpenDev to reduce the set of images that are being built. Change-Id: I01e3c9e27f92205979ea6562b23f0f7f3b431728 --- .zuul.yaml | 8 ++++---- Dockerfile | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.zuul.yaml b/.zuul.yaml index a41dd17f1..4915f9f85 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -161,8 +161,8 @@ description: Build Docker images. allowed-projects: openstack/python-openstackclient requires: - - python-builder-3.10-bookworm-container-image - - python-base-3.10-bookworm-container-image + - python-builder-3.11-bookworm-container-image + - python-base-3.11-bookworm-container-image provides: osc-container-image vars: &osc_image_vars docker_images: @@ -175,8 +175,8 @@ description: Build Docker images and upload to Docker Hub. allowed-projects: openstack/python-openstackclient requires: - - python-builder-3.10-bookworm-container-image - - python-base-3.10-bookworm-container-image + - python-builder-3.11-bookworm-container-image + - python-base-3.11-bookworm-container-image provides: osc-container-image secrets: - name: docker_credentials diff --git a/Dockerfile b/Dockerfile index 9f6dcd807..420fdb5ea 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.10-bookworm as builder +FROM docker.io/opendevorg/python-builder:3.11-bookworm as builder COPY . /tmp/src RUN assemble -FROM docker.io/opendevorg/python-base:3.10-bookworm +FROM docker.io/opendevorg/python-base:3.11-bookworm COPY --from=builder /output/ /output RUN /output/install-from-bindep From f8c708900c7c522d12b2ca95e0c335219db75bf8 Mon Sep 17 00:00:00 2001 From: Mridula Joshi Date: Tue, 18 Jul 2023 10:51:33 +0000 Subject: [PATCH 028/403] Adds command ``image metadef object create`` Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/858350 Change-Id: Ie74231c823d6128d149d5f01c90a66ed3afa3d1a --- doc/source/cli/data/glance.csv | 2 +- openstackclient/image/v2/metadef_objects.py | 75 +++++++++++++++++++ openstackclient/tests/unit/image/v2/fakes.py | 44 +++++++++++ .../unit/image/v2/test_metadef_objects.py | 62 +++++++++++++++ ...etadef-object-create-3939ee1453585484.yaml | 5 ++ setup.cfg | 2 + 6 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 openstackclient/image/v2/metadef_objects.py create mode 100644 openstackclient/tests/unit/image/v2/test_metadef_objects.py create mode 100644 releasenotes/notes/add-metadef-object-create-3939ee1453585484.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index 90f7aeb15..349e90ea4 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -32,7 +32,7 @@ md-namespace-resource-type-list,image metadef resource type list,List resource t 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. md-namespace-update,,Update an existing metadata definitions namespace. -md-object-create,,Create a new metadata definitions object inside a namespace. +md-object-create,image metadef object create,Create a new metadata definitions object inside a namespace. md-object-delete,,Delete a specific metadata definitions object inside a namespace. md-object-list,,List metadata definitions objects inside a specific namespace. md-object-property-show,,Describe a specific metadata definitions property inside an object. diff --git a/openstackclient/image/v2/metadef_objects.py b/openstackclient/image/v2/metadef_objects.py new file mode 100644 index 000000000..7e83bdcb2 --- /dev/null +++ b/openstackclient/image/v2/metadef_objects.py @@ -0,0 +1,75 @@ +# 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. +# + +"""Image V2 Action Implementations""" + + +from osc_lib.command import command +from osc_lib import utils + +from openstackclient.i18n import _ + + +def _format_object(md_object): + fields_to_show = ( + 'created_at', + 'description', + 'name', + 'namespace_name', + 'properties', + 'required', + 'updated_at', + ) + + return ( + fields_to_show, + utils.get_item_properties( + md_object, + fields_to_show, + ), + ) + + +class CreateMetadefObjects(command.ShowOne): + _description = _("Create a metadef object") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "--namespace", + metavar="", + help=_("Metadef namespace to create the metadef object in (name)"), + ) + parser.add_argument( + "name", + metavar='', + help=_('New metadef object name'), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + namespace = image_client.get_metadef_namespace( + parsed_args.namespace, + ) + data = image_client.create_metadef_object( + namespace=namespace.namespace, + name=parsed_args.name, + ) + + fields, value = _format_object(data) + + return fields, value diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py index a63d4c525..37aacb048 100644 --- a/openstackclient/tests/unit/image/v2/fakes.py +++ b/openstackclient/tests/unit/image/v2/fakes.py @@ -20,6 +20,7 @@ from openstack.image.v2 import image from openstack.image.v2 import member from openstack.image.v2 import metadef_namespace +from openstack.image.v2 import metadef_object from openstack.image.v2 import metadef_property from openstack.image.v2 import metadef_resource_type from openstack.image.v2 import service_info as _service_info @@ -314,3 +315,46 @@ def create_resource_types(attrs=None, count=2): metadef_resource_types.append(create_one_resource_type(attrs)) return metadef_resource_types + + +def create_one_metadef_object(attrs=None): + """Create a fake MetadefNamespace member. + + :param attrs: A dictionary with all attributes of metadef_namespace member + :type attrs: dict + :return: a list of MetadefNamespace objects + :rtype: list of `metadef_namespace.MetadefNamespace` + """ + attrs = attrs or {} + + metadef_objects_list = { + 'created_at': '2014-09-19T18:20:56Z', + 'description': 'The CPU limits with control parameters.', + 'name': 'CPU Limits', + 'properties': { + 'quota:cpu_period': { + 'description': 'The enforcement interval', + 'maximum': 1000000, + 'minimum': 1000, + 'title': 'Quota: CPU Period', + 'type': 'integer', + }, + 'quota:cpu_quota': { + 'description': 'The maximum allowed bandwidth', + 'title': 'Quota: CPU Quota', + 'type': 'integer', + }, + 'quota:cpu_shares': { + 'description': 'The proportional weighted', + 'title': 'Quota: CPU Shares', + 'type': 'integer', + }, + }, + 'required': [], + 'schema': '/v2/schemas/metadefs/object', + 'updated_at': '2014-09-19T18:20:56Z', + } + + # Overwrite default attributes if there are some attributes set + metadef_objects_list.update(attrs) + return metadef_object.MetadefObject(**metadef_objects_list) diff --git a/openstackclient/tests/unit/image/v2/test_metadef_objects.py b/openstackclient/tests/unit/image/v2/test_metadef_objects.py new file mode 100644 index 000000000..b33256f16 --- /dev/null +++ b/openstackclient/tests/unit/image/v2/test_metadef_objects.py @@ -0,0 +1,62 @@ +# 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. + +from openstackclient.image.v2 import metadef_objects +from openstackclient.tests.unit.image.v2 import fakes + + +class TestMetadefObjectsCreate(fakes.TestImagev2): + _metadef_namespace = fakes.create_one_metadef_namespace() + _metadef_objects = fakes.create_one_metadef_object() + + expected_columns = ( + 'created_at', + 'description', + 'name', + 'namespace_name', + 'properties', + 'required', + 'updated_at', + ) + expected_data = ( + _metadef_objects.created_at, + _metadef_objects.description, + _metadef_objects.name, + _metadef_objects.namespace_name, + _metadef_objects.properties, + _metadef_objects.required, + _metadef_objects.updated_at, + ) + + def setUp(self): + super().setUp() + + self.image_client.create_metadef_object.return_value = ( + self._metadef_objects + ) + self.cmd = metadef_objects.CreateMetadefObjects(self.app, None) + + def test_namespace_create(self): + arglist = [ + '--namespace', + self._metadef_namespace.namespace, + self._metadef_objects.name, + ] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) diff --git a/releasenotes/notes/add-metadef-object-create-3939ee1453585484.yaml b/releasenotes/notes/add-metadef-object-create-3939ee1453585484.yaml new file mode 100644 index 000000000..250a1452a --- /dev/null +++ b/releasenotes/notes/add-metadef-object-create-3939ee1453585484.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``image metadef object show`` command to create the + metadata definitions objects inside a specific namespace diff --git a/setup.cfg b/setup.cfg index 0a579a796..a8c3e5e60 100644 --- a/setup.cfg +++ b/setup.cfg @@ -397,6 +397,8 @@ openstack.image.v2 = 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_property_create = openstackclient.image.v2.metadef_properties:CreateMetadefProperty image_metadef_property_list = openstackclient.image.v2.metadef_properties:ListMetadefProperties image_metadef_property_show = openstackclient.image.v2.metadef_properties:ShowMetadefProperty From 7708106cf06cd590ca581802d55094418bdc1feb Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 2 Nov 2023 17:46:24 +0000 Subject: [PATCH 029/403] compute: Add 'server create --server-group' option Add an alias for requesting a server group. This is more syntactic sugar, though it comes with the added bonus of letting users request a server group by name instead of just ID. Change-Id: I3d9a7ce04a02fdf374b7a8082618eccdea8c3217 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 18 +++++++++++++++++- .../tests/unit/compute/v2/test_server.py | 16 +++++++++++----- ...r-create-server-group-a5b630f2a64de28d.yaml | 5 +++++ 3 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/server-create-server-group-a5b630f2a64de28d.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 5b563a483..ba24b2b65 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1335,10 +1335,19 @@ def get_parser(self, prog_name): '(supported by --os-compute-api-version 2.74 or above)' ), ) + parser.add_argument( + '--server-group', + metavar='', + help=_( + "Server group to create the server within " + "(this is an alias for '--hint group=')" + ), + ) parser.add_argument( '--hint', metavar='', action=parseractions.KeyValueAppendAction, + dest='hints', default={}, help=_('Hints for the scheduler'), ) @@ -1857,13 +1866,20 @@ def _match_image(image_api, wanted_properties): security_group_names.append(sg['name']) hints = {} - for key, values in parsed_args.hint.items(): + for key, values in parsed_args.hints.items(): # only items with multiple values will result in a list if len(values) == 1: hints[key] = values[0] else: hints[key] = values + if parsed_args.server_group: + server_group_obj = utils.find_resource( + compute_client.server_groups, + parsed_args.server_group, + ) + hints['group'] = server_group_obj.id + if isinstance(parsed_args.config_drive, bool): # NOTE(stephenfin): The API doesn't accept False as a value :'( config_drive = parsed_args.config_drive or None diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 03237e90f..b145fb85f 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -1447,6 +1447,8 @@ def test_server_create_with_options(self): 'a=b', '--hint', 'a=c', + '--server-group', + 'servergroup', self.new_server.name, ] verifylist = [ @@ -1455,22 +1457,26 @@ def test_server_create_with_options(self): ('key_name', 'keyname'), ('properties', {'Beta': 'b'}), ('security_group', ['securitygroup']), - ('hint', {'a': ['b', 'c']}), + ('hints', {'a': ['b', 'c']}), + ('server_group', 'servergroup'), ('config_drive', True), ('password', 'passw0rd'), ('server_name', self.new_server.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. + fake_server_group = compute_fakes.create_one_server_group() + self.compute_client.server_groups.get.return_value = fake_server_group + fake_sg = network_fakes.FakeSecurityGroup.create_security_groups() mock_find_sg = network_fakes.FakeSecurityGroup.get_security_groups( fake_sg ) self.app.client_manager.network.find_security_group = mock_find_sg + # 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) mock_find_sg.assert_called_once_with( @@ -1490,7 +1496,7 @@ def test_server_create_with_options(self): admin_pass='passw0rd', block_device_mapping_v2=[], nics=[], - scheduler_hints={'a': ['b', 'c']}, + scheduler_hints={'a': ['b', 'c'], 'group': fake_server_group.id}, config_drive=True, ) # ServerManager.create(name, image, flavor, **kwargs) diff --git a/releasenotes/notes/server-create-server-group-a5b630f2a64de28d.yaml b/releasenotes/notes/server-create-server-group-a5b630f2a64de28d.yaml new file mode 100644 index 000000000..f9aaf4b46 --- /dev/null +++ b/releasenotes/notes/server-create-server-group-a5b630f2a64de28d.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + The ``server create`` command now accepts a new option, ``--server-group``, + which is a shortcut for configuring the ``group`` scheduler hint. From c7e3529dea48d010f1fd5a6631638ea754b130dd Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 27 Jul 2021 11:00:06 +0100 Subject: [PATCH 030/403] Add pagination helpers Add some pagination helpers to configure pagination parameters for various commands. Two pagination schemes are supported, based on what we currently support across OSC commands: marker-based pagination and offset-based pagination. Change-Id: I551bb4c3ff0568c6df5244a1d0f0669497bee58f Signed-off-by: Stephen Finucane --- openstackclient/common/pagination.py | 82 +++++++++++++++++++ openstackclient/compute/v2/flavor.py | 18 +--- openstackclient/compute/v2/hypervisor.py | 23 +----- openstackclient/compute/v2/keypair.py | 11 +-- openstackclient/compute/v2/server.py | 25 +----- openstackclient/compute/v2/server_event.py | 17 +--- openstackclient/compute/v2/server_group.py | 25 +----- .../compute/v2/server_migration.py | 24 +----- openstackclient/image/v2/image.py | 20 +---- openstackclient/object/v1/container.py | 14 +--- openstackclient/object/v1/object.py | 13 +-- openstackclient/volume/v1/volume.py | 16 +--- openstackclient/volume/v2/volume.py | 14 +--- openstackclient/volume/v2/volume_backup.py | 15 +--- openstackclient/volume/v2/volume_snapshot.py | 14 +--- .../volume/v3/volume_attachment.py | 16 +--- openstackclient/volume/v3/volume_message.py | 15 +--- 17 files changed, 115 insertions(+), 247 deletions(-) create mode 100644 openstackclient/common/pagination.py diff --git a/openstackclient/common/pagination.py b/openstackclient/common/pagination.py new file mode 100644 index 000000000..b6a11c5f5 --- /dev/null +++ b/openstackclient/common/pagination.py @@ -0,0 +1,82 @@ +# 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.cli import parseractions + +from openstackclient.i18n import _ + + +# TODO(stephenfin): Consider moving these to osc-lib since they're broadly +# useful + + +def add_marker_pagination_option_to_parser(parser): + """Add marker-based pagination options to the parser. + + APIs that use marker-based paging use the marker and limit query parameters + to paginate through items in a collection. + + Marker-based pagination is often used in cases where the length of the + total set of items is either changing frequently, or where the total length + might not be known upfront. + """ + parser.add_argument( + '--limit', + metavar='', + type=int, + action=parseractions.NonNegativeAction, + help=_( + 'The maximum number of entries to return. If the value exceeds ' + 'the server-defined maximum, then the maximum value will be used.' + ), + ) + parser.add_argument( + '--marker', + metavar='', + default=None, + help=_( + 'The first position in the collection to return results from. ' + 'This should be a value that was returned in a previous request.' + ), + ) + + +def add_offset_pagination_option_to_parser(parser): + """Add offset-based pagination options to the parser. + + APIs that use offset-based paging use the offset and limit query parameters + to paginate through items in a collection. + + Offset-based pagination is often used where the list of items is of a fixed + and predetermined length. + """ + parser.add_argument( + '--limit', + metavar='', + type=int, + action=parseractions.NonNegativeAction, + help=_( + 'The maximum number of entries to return. If the value exceeds ' + 'the server-defined maximum, then the maximum value will be used.' + ), + ) + parser.add_argument( + '--offset', + metavar='', + type=int, + action=parseractions.NonNegativeAction, + default=None, + help=_( + 'The (zero-based) offset of the first item in the collection to ' + 'return.' + ), + ) diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index d670720ca..aa8c010f1 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -25,6 +25,7 @@ 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 @@ -292,22 +293,7 @@ def get_parser(self, prog_name): default=False, help=_("List additional fields in output"), ) - parser.add_argument( - '--marker', - metavar="", - help=_("The last flavor ID of the previous page"), - ) - parser.add_argument( - '--limit', - type=int, - metavar='', - help=_( - 'Maximum number of flavors to display. This is also ' - 'configurable on the server. The actual limit used will be ' - 'the lower of the user-supplied value and the server ' - 'configuration-derived value' - ), - ) + pagination.add_marker_pagination_option_to_parser(parser) return parser def take_action(self, parsed_args): diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py index ddfd3e8d9..a118913b6 100644 --- a/openstackclient/compute/v2/hypervisor.py +++ b/openstackclient/compute/v2/hypervisor.py @@ -25,6 +25,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import pagination from openstackclient.i18n import _ @@ -80,27 +81,7 @@ def get_parser(self, prog_name): "when using microversion 2.52 or lower" ), ) - parser.add_argument( - '--marker', - metavar='', - help=_( - "The UUID of the last hypervisor of the previous page; " - "displays list of hypervisors after 'marker'. " - "(supported with --os-compute-api-version 2.33 or above)" - ), - ) - parser.add_argument( - '--limit', - metavar='', - type=int, - help=_( - "Maximum number of hypervisors to display. Note that there " - "is a configurable max limit on the server, and the limit " - "that is used will be the minimum of what is requested " - "here and what is configured in the server. " - "(supported with --os-compute-api-version 2.33 or above)" - ), - ) + pagination.add_marker_pagination_option_to_parser(parser) parser.add_argument( '--long', action='store_true', diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index 3e16feabc..5d7815b26 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -27,6 +27,7 @@ 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 @@ -296,15 +297,7 @@ def get_parser(self, prog_name): ), ) identity_common.add_project_domain_option_to_parser(parser) - parser.add_argument( - '--marker', - help=_('The last keypair ID of the previous page'), - ) - parser.add_argument( - '--limit', - type=int, - help=_('Maximum number of keypairs to display'), - ) + pagination.add_marker_pagination_option_to_parser(parser) return parser def take_action(self, parsed_args): diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 5b563a483..4854e91f8 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -33,6 +33,7 @@ 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 from openstackclient.network import common as network_common @@ -2370,29 +2371,7 @@ def get_parser(self, prog_name): 'Mutually exclusive with "--no-name-lookup|-n" option.' ), ) - parser.add_argument( - '--marker', - metavar='', - default=None, - help=_( - 'The last server of the previous page. Display ' - 'list of servers after marker. Display all servers if not ' - 'specified. When used with ``--deleted``, the marker must ' - 'be an ID, otherwise a name or ID can be used.' - ), - ) - parser.add_argument( - '--limit', - metavar='', - type=int, - default=None, - help=_( - "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." - ), - ) + pagination.add_marker_pagination_option_to_parser(parser) parser.add_argument( '--changes-before', metavar='', diff --git a/openstackclient/compute/v2/server_event.py b/openstackclient/compute/v2/server_event.py index 683179524..1fc467210 100644 --- a/openstackclient/compute/v2/server_event.py +++ b/openstackclient/compute/v2/server_event.py @@ -26,6 +26,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import pagination from openstackclient.i18n import _ LOG = logging.getLogger(__name__) @@ -143,21 +144,7 @@ def get_parser(self, prog_name): "(supported with --os-compute-api-version 2.66 or above)" ), ) - parser.add_argument( - '--marker', - help=_( - 'The last server event ID of the previous page ' - '(supported by --os-compute-api-version 2.58 or above)' - ), - ) - parser.add_argument( - '--limit', - type=int, - help=_( - 'Maximum number of server events to display ' - '(supported by --os-compute-api-version 2.58 or above)' - ), - ) + pagination.add_marker_pagination_option_to_parser(parser) return parser def take_action(self, parsed_args): diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py index d1c0bf976..e5a1a4cea 100644 --- a/openstackclient/compute/v2/server_group.py +++ b/openstackclient/compute/v2/server_group.py @@ -24,9 +24,9 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import pagination from openstackclient.i18n import _ - LOG = logging.getLogger(__name__) @@ -191,28 +191,7 @@ def get_parser(self, prog_name): ) # TODO(stephenfin): This should really be a --marker option, but alas # the API doesn't support that for some reason - parser.add_argument( - '--offset', - metavar='', - type=int, - default=None, - help=_( - 'Index from which to start listing servers. This should ' - 'typically be a factor of --limit. Display all servers groups ' - 'if not specified.' - ), - ) - parser.add_argument( - '--limit', - metavar='', - type=int, - default=None, - help=_( - "Maximum number of server groups to display. " - "If limit is greater than 'osapi_max_limit' option of Nova " - "API, 'osapi_max_limit' will be used instead." - ), - ) + pagination.add_offset_pagination_option_to_parser(parser) return parser def take_action(self, parsed_args): diff --git a/openstackclient/compute/v2/server_migration.py b/openstackclient/compute/v2/server_migration.py index 6235ae697..8a4440d6a 100644 --- a/openstackclient/compute/v2/server_migration.py +++ b/openstackclient/compute/v2/server_migration.py @@ -19,6 +19,7 @@ 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 @@ -54,28 +55,7 @@ def get_parser(self, prog_name): ], help=_('Filter migrations by type'), ) - parser.add_argument( - '--marker', - metavar='', - help=_( - "The last migration of the previous page; displays list " - "of migrations after 'marker'. Note that the marker is " - "the migration UUID. " - "(supported with --os-compute-api-version 2.59 or above)" - ), - ) - parser.add_argument( - '--limit', - metavar='', - type=int, - help=_( - "Maximum number of migrations to display. Note that there " - "is a configurable max limit on the server, and the limit " - "that is used will be the minimum of what is requested " - "here and what is configured in the server. " - "(supported with --os-compute-api-version 2.59 or above)" - ), - ) + pagination.add_marker_pagination_option_to_parser(parser) parser.add_argument( '--changes-since', dest='changes_since', diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 48b263926..725a4d3ef 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -31,6 +31,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import pagination from openstackclient.common import progressbar from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -805,9 +806,9 @@ def get_parser(self, prog_name): default=False, help=_('List additional fields in output'), ) - # --page-size has never worked, leave here for silent compatibility # We'll implement limit/marker differently later + # TODO(stephenfin): Remove this in the next major version bump parser.add_argument( "--page-size", metavar="", @@ -823,22 +824,7 @@ def get_parser(self, prog_name): "specified separated by comma" ), ) - parser.add_argument( - "--limit", - metavar="", - type=int, - help=_("Maximum number of images to display."), - ) - parser.add_argument( - '--marker', - metavar='', - default=None, - help=_( - "The last image of the previous page. Display " - "list of images after marker. Display all images if not " - "specified. (name or ID)" - ), - ) + pagination.add_marker_pagination_option_to_parser(parser) return parser def take_action(self, parsed_args): diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py index 4f01c1d92..25108e2c6 100644 --- a/openstackclient/object/v1/container.py +++ b/openstackclient/object/v1/container.py @@ -22,9 +22,9 @@ from osc_lib.command import command from osc_lib import utils +from openstackclient.common import pagination from openstackclient.i18n import _ - LOG = logging.getLogger(__name__) @@ -127,22 +127,12 @@ def get_parser(self, prog_name): metavar="", help=_("Filter list using "), ) - parser.add_argument( - "--marker", - metavar="", - help=_("Anchor for paging"), - ) + pagination.add_marker_pagination_option_to_parser(parser) parser.add_argument( "--end-marker", metavar="", help=_("End anchor for paging"), ) - parser.add_argument( - "--limit", - metavar="", - type=int, - help=_("Limit the number of containers returned"), - ) parser.add_argument( '--long', action='store_true', diff --git a/openstackclient/object/v1/object.py b/openstackclient/object/v1/object.py index e88be1626..24cebb3b8 100644 --- a/openstackclient/object/v1/object.py +++ b/openstackclient/object/v1/object.py @@ -23,6 +23,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import pagination from openstackclient.i18n import _ @@ -140,22 +141,12 @@ def get_parser(self, prog_name): metavar="", help=_("Roll up items with "), ) - parser.add_argument( - "--marker", - metavar="", - help=_("Anchor for paging"), - ) + pagination.add_marker_pagination_option_to_parser(parser) parser.add_argument( "--end-marker", metavar="", help=_("End anchor for paging"), ) - parser.add_argument( - "--limit", - metavar="", - type=int, - help=_("Limit the number of objects returned"), - ) parser.add_argument( '--long', action='store_true', diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index 7c60d6c78..168fed4ab 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -26,6 +26,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import pagination from openstackclient.i18n import _ @@ -372,20 +373,7 @@ def get_parser(self, prog_name): default=False, help=_('List additional fields in output'), ) - parser.add_argument( - '--offset', - type=int, - action=parseractions.NonNegativeAction, - metavar='', - help=_('Index from which to start listing volumes'), - ) - parser.add_argument( - '--limit', - type=int, - action=parseractions.NonNegativeAction, - metavar='', - help=_('Maximum number of volumes to display'), - ) + pagination.add_offset_pagination_option_to_parser(parser) return parser def take_action(self, parsed_args): diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 096305543..4127e23d4 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -26,6 +26,7 @@ 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 @@ -456,18 +457,7 @@ def get_parser(self, prog_name): default=False, help=_('List additional fields in output'), ) - parser.add_argument( - '--marker', - metavar='', - help=_('The last volume ID of the previous page'), - ) - parser.add_argument( - '--limit', - type=int, - action=parseractions.NonNegativeAction, - metavar='', - help=_('Maximum number of volumes to display'), - ) + pagination.add_marker_pagination_option_to_parser(parser) return parser def take_action(self, parsed_args): diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index 6cacc7fa5..72e887156 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -26,9 +26,9 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.common import pagination from openstackclient.i18n import _ - LOG = logging.getLogger(__name__) @@ -272,18 +272,7 @@ def get_parser(self, prog_name): "Filters results by the volume which they backup (name or ID)" ), ) - parser.add_argument( - '--marker', - metavar='', - help=_('The last backup of the previous page (name or ID)'), - ) - parser.add_argument( - '--limit', - type=int, - action=parseractions.NonNegativeAction, - metavar='', - help=_('Maximum number of backups to display'), - ) + pagination.add_marker_pagination_option_to_parser(parser) parser.add_argument( '--all-projects', action='store_true', diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index 376c2b5e1..0ca87144b 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -25,6 +25,7 @@ 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 @@ -228,18 +229,6 @@ def get_parser(self, prog_name): default=False, help=_('List additional fields in output'), ) - parser.add_argument( - '--marker', - metavar='', - help=_('The last snapshot ID of the previous page'), - ) - parser.add_argument( - '--limit', - type=int, - action=parseractions.NonNegativeAction, - metavar='', - help=_('Maximum number of snapshots to display'), - ) parser.add_argument( '--name', metavar='', @@ -268,6 +257,7 @@ def get_parser(self, prog_name): 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): diff --git a/openstackclient/volume/v3/volume_attachment.py b/openstackclient/volume/v3/volume_attachment.py index 652fdf63c..8adbac46a 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 pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -410,20 +411,7 @@ def get_parser(self, prog_name): metavar='', help=_('Filters results by a status. ') + _FILTER_DEPRECATED, ) - parser.add_argument( - '--marker', - metavar='', - help=_( - 'Begin returning volume attachments that appear later in ' - 'volume attachment list than that represented by this ID.' - ), - ) - parser.add_argument( - '--limit', - type=int, - metavar='', - help=_('Maximum number of volume attachments to return.'), - ) + pagination.add_marker_pagination_option_to_parser(parser) # TODO(stephenfin): Add once we have an equivalent command for # 'cinder list-filters' # parser.add_argument( diff --git a/openstackclient/volume/v3/volume_message.py b/openstackclient/volume/v3/volume_message.py index b76ebe326..071a10d45 100644 --- a/openstackclient/volume/v3/volume_message.py +++ b/openstackclient/volume/v3/volume_message.py @@ -21,6 +21,7 @@ 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 @@ -78,19 +79,7 @@ def get_parser(self, prog_name): help=_('Filter results by project (name or ID) (admin only)'), ) identity_common.add_project_domain_option_to_parser(parser) - parser.add_argument( - '--marker', - metavar='', - help=_('The last message ID of the previous page'), - default=None, - ) - parser.add_argument( - '--limit', - type=int, - metavar='', - help=_('Maximum number of messages to display'), - default=None, - ) + pagination.add_marker_pagination_option_to_parser(parser) return parser From 78ef009a3aebe43583d6b93e65f53c49a1d818a6 Mon Sep 17 00:00:00 2001 From: Mridula Joshi Date: Tue, 27 Jun 2023 13:17:57 +0000 Subject: [PATCH 031/403] Adds command ``image metadef object show`` Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/858350 Change-Id: I97bcb0ccee9d25fb26475ec9e10660556e6072a6 --- doc/source/cli/data/glance.csv | 2 +- openstackclient/image/v2/metadef_objects.py | 34 +++++++++++++- .../unit/image/v2/test_metadef_objects.py | 46 ++++++++++++++++++- ...-metadef-object-show-1b05dd33ecf42210.yaml | 5 ++ setup.cfg | 1 + 5 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/add-metadef-object-show-1b05dd33ecf42210.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index 6abb223b4..d8bc984cf 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -36,7 +36,7 @@ md-object-create,image metadef object create,Create a new metadata definitions o md-object-delete,,Delete a specific metadata definitions object inside a namespace. md-object-list,,List metadata definitions objects inside a specific namespace. md-object-property-show,,Describe a specific metadata definitions property inside an object. -md-object-show,,Describe a specific metadata definitions object inside a namespace. +md-object-show,image metadef object show,Describe a specific metadata definitions object inside a namespace. md-object-update,,Update metadata definitions object inside a namespace. md-property-create,image metadef property create,Create a new metadata definitions property inside a namespace. md-property-delete,,Delete a specific metadata definitions property inside a namespace. diff --git a/openstackclient/image/v2/metadef_objects.py b/openstackclient/image/v2/metadef_objects.py index 7e83bdcb2..3792ed8fc 100644 --- a/openstackclient/image/v2/metadef_objects.py +++ b/openstackclient/image/v2/metadef_objects.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack Foundation +# Copyright 2023 Red Hat # # 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 @@ -73,3 +73,35 @@ def take_action(self, parsed_args): fields, value = _format_object(data) return fields, value + + +class ShowMetadefObjects(command.ShowOne): + _description = _( + "Describe a specific metadata definitions" "object inside a namespace" + ) + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "namespace_name", + metavar="", + help=_("Namespace (name) for the namespace"), + ) + parser.add_argument( + "object_name", + metavar="", + help=_("Name of an object."), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + namespace_name = parsed_args.namespace_name + object_name = parsed_args.object_name + + data = image_client.get_metadef_object(object_name, namespace_name) + + fields, value = _format_object(data) + + return fields, value diff --git a/openstackclient/tests/unit/image/v2/test_metadef_objects.py b/openstackclient/tests/unit/image/v2/test_metadef_objects.py index b33256f16..76a26f0ee 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_objects.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_objects.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack Foundation +# Copyright 2023 Red Hat # # 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 @@ -41,7 +41,6 @@ class TestMetadefObjectsCreate(fakes.TestImagev2): def setUp(self): super().setUp() - self.image_client.create_metadef_object.return_value = ( self._metadef_objects ) @@ -55,6 +54,49 @@ def test_namespace_create(self): ] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + +class TestMetadefObjectsShow(fakes.TestImagev2): + _metadef_namespace = fakes.create_one_metadef_namespace() + _metadef_objects = fakes.create_one_metadef_object() + + expected_columns = ( + 'created_at', + 'description', + 'name', + 'namespace_name', + 'properties', + 'required', + 'updated_at', + ) + expected_data = ( + _metadef_objects.created_at, + _metadef_objects.description, + _metadef_objects.name, + _metadef_objects.namespace_name, + _metadef_objects.properties, + _metadef_objects.required, + _metadef_objects.updated_at, + ) + + def setUp(self): + super().setUp() + + self.image_client.get_metadef_object.return_value = ( + self._metadef_objects + ) + self.cmd = metadef_objects.ShowMetadefObjects(self.app, None) + + def test_object_show(self): + arglist = [ + self._metadef_namespace.namespace, + self._metadef_objects.name, + ] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) diff --git a/releasenotes/notes/add-metadef-object-show-1b05dd33ecf42210.yaml b/releasenotes/notes/add-metadef-object-show-1b05dd33ecf42210.yaml new file mode 100644 index 000000000..d3df6f2aa --- /dev/null +++ b/releasenotes/notes/add-metadef-object-show-1b05dd33ecf42210.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``image metadef object show`` command to show the + metadata definitions objects inside a specific namespace diff --git a/setup.cfg b/setup.cfg index b019546dd..2e2acbed3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -398,6 +398,7 @@ openstack.image.v2 = 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_property_create = openstackclient.image.v2.metadef_properties:CreateMetadefProperty image_metadef_property_list = openstackclient.image.v2.metadef_properties:ListMetadefProperties From a3730afe2e24fb2bdc0101ab73e5ec81e50f9308 Mon Sep 17 00:00:00 2001 From: Masayoshi Mizuma Date: Sat, 24 Dec 2022 20:02:27 +0900 Subject: [PATCH 032/403] image: Fix the default description of image visibility The default image visibility was changed to 'shared' in API v2.5. Fix the help information of image visibility, centralizing options in the process. Change-Id: Ib3017fc4f618c1e14e3b26b616ff9374d0e24eaa Co-authored-by: Stephen Finucane --- openstackclient/image/v2/image.py | 163 +++++++++++++----------------- 1 file changed, 73 insertions(+), 90 deletions(-) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 725a4d3ef..be64d2081 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -163,6 +163,67 @@ def get_data_from_stdin(): return None +def _add_is_protected_args(parser): + protected_group = parser.add_mutually_exclusive_group() + protected_group.add_argument( + "--protected", + action="store_true", + dest="is_protected", + default=None, + help=_("Prevent image from being deleted"), + ) + protected_group.add_argument( + "--unprotected", + action="store_false", + dest="is_protected", + default=None, + help=_("Allow image to be deleted (default)"), + ) + + +def _add_visibility_args(parser): + public_group = parser.add_mutually_exclusive_group() + public_group.add_argument( + "--public", + action="store_const", + const="public", + dest="visibility", + help=_("Image is accessible and visisble to all users"), + ) + public_group.add_argument( + "--private", + action="store_const", + const="private", + dest="visibility", + help=_( + "Image is only accessible by the owner " + "(default until --os-image-api-version 2.5)" + ), + ) + public_group.add_argument( + "--community", + action="store_const", + const="community", + dest="visibility", + help=_( + "Image is accessible by all users but does not appear in the " + "default image list of any user except the owner " + "(requires --os-image-api-version 2.5 or later)" + ), + ) + public_group.add_argument( + "--shared", + action="store_const", + const="shared", + dest="visibility", + help=_( + "Image is only accessible by the owner and image members " + "(requires --os-image-api-version 2.5 or later) " + "(default since --os-image-api-version 2.5)" + ), + ) + + class AddProjectToImage(command.ShowOne): _description = _("Associate project with image") @@ -322,50 +383,8 @@ def get_parser(self, prog_name): "Only use in combination with --sign-key-path" ), ) - protected_group = parser.add_mutually_exclusive_group() - protected_group.add_argument( - "--protected", - action="store_true", - dest="is_protected", - default=None, - help=_("Prevent image from being deleted"), - ) - protected_group.add_argument( - "--unprotected", - action="store_false", - dest="is_protected", - default=None, - help=_("Allow image to be deleted (default)"), - ) - public_group = parser.add_mutually_exclusive_group() - public_group.add_argument( - "--public", - action="store_const", - const="public", - dest="visibility", - help=_("Image is accessible to the public"), - ) - public_group.add_argument( - "--private", - action="store_const", - const="private", - dest="visibility", - help=_("Image is inaccessible to the public (default)"), - ) - public_group.add_argument( - "--community", - action="store_const", - const="community", - dest="visibility", - help=_("Image is accessible to the community"), - ) - public_group.add_argument( - "--shared", - action="store_const", - const="shared", - dest="visibility", - help=_("Image can be shared"), - ) + _add_is_protected_args(parser) + _add_visibility_args(parser) parser.add_argument( "--property", dest="properties", @@ -726,14 +745,20 @@ def get_parser(self, prog_name): action="store_const", const="community", dest="visibility", - help=_("List only community images"), + help=_( + "List only community images " + "(requires --os-image-api-version 2.5 or later)" + ), ) public_group.add_argument( "--shared", action="store_const", const="shared", dest="visibility", - help=_("List only shared images"), + help=_( + "List only shared images " + "(requires --os-image-api-version 2.5 or later)" + ), ) public_group.add_argument( "--all", @@ -1073,50 +1098,8 @@ def get_parser(self, prog_name): help=_("Image disk format. The supported options are: %s") % ', '.join(DISK_CHOICES), ) - protected_group = parser.add_mutually_exclusive_group() - protected_group.add_argument( - "--protected", - action="store_true", - dest="is_protected", - default=None, - help=_("Prevent image from being deleted"), - ) - protected_group.add_argument( - "--unprotected", - action="store_false", - dest="is_protected", - default=None, - help=_("Allow image to be deleted (default)"), - ) - public_group = parser.add_mutually_exclusive_group() - public_group.add_argument( - "--public", - action="store_const", - const="public", - dest="visibility", - help=_("Image is accessible to the public"), - ) - public_group.add_argument( - "--private", - action="store_const", - const="private", - dest="visibility", - help=_("Image is inaccessible to the public (default)"), - ) - public_group.add_argument( - "--community", - action="store_const", - const="community", - dest="visibility", - help=_("Image is accessible to the community"), - ) - public_group.add_argument( - "--shared", - action="store_const", - const="shared", - dest="visibility", - help=_("Image can be shared"), - ) + _add_is_protected_args(parser) + _add_visibility_args(parser) parser.add_argument( "--property", dest="properties", From 4bb6efa8f8ec60f288b1e7a246e97dacb0f6ad33 Mon Sep 17 00:00:00 2001 From: Mridula Joshi Date: Fri, 23 Jun 2023 12:27:50 +0000 Subject: [PATCH 033/403] Adds command ``image metadef object list`` Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/858350 Change-Id: I0691f7519e8fb9f01836e6232e0bcebd2c428ac3 --- doc/source/cli/data/glance.csv | 2 +- openstackclient/image/v2/metadef_objects.py | 32 +++++++++++++++++ .../unit/image/v2/test_metadef_objects.py | 35 +++++++++++++++++++ ...-metadef-object-list-c8831e73c696b9d9.yaml | 5 +++ setup.cfg | 3 ++ 5 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-metadef-object-list-c8831e73c696b9d9.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index d8bc984cf..e2e2f7e87 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -34,7 +34,7 @@ md-namespace-tags-delete,,Delete all metadata definitions tags inside a specific md-namespace-update,,Update an existing metadata definitions namespace. md-object-create,image metadef object create,Create a new metadata definitions object inside a namespace. md-object-delete,,Delete a specific metadata definitions object inside a namespace. -md-object-list,,List metadata definitions objects inside a specific namespace. +md-object-list,image metadef object list,List metadata definitions objects inside a specific namespace. md-object-property-show,,Describe a specific metadata definitions property inside an object. md-object-show,image metadef object show,Describe a specific metadata definitions object inside a namespace. md-object-update,,Update metadata definitions object inside a namespace. diff --git a/openstackclient/image/v2/metadef_objects.py b/openstackclient/image/v2/metadef_objects.py index 3792ed8fc..a88c19cd8 100644 --- a/openstackclient/image/v2/metadef_objects.py +++ b/openstackclient/image/v2/metadef_objects.py @@ -105,3 +105,35 @@ def take_action(self, parsed_args): fields, value = _format_object(data) return fields, value + + +class ListMetadefObjects(command.Lister): + _description = _("List metadef objects inside a specific namespace.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "namespace", + metavar="", + help=_("Namespace (name) for the namespace"), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + namespace_name = parsed_args.namespace + columns = ['name', 'description'] + + md_objects = list(image_client.metadef_objects(namespace_name)) + column_headers = columns + return ( + column_headers, + ( + utils.get_item_properties( + md_object, + columns, + ) + for md_object in md_objects + ), + ) diff --git a/openstackclient/tests/unit/image/v2/test_metadef_objects.py b/openstackclient/tests/unit/image/v2/test_metadef_objects.py index 76a26f0ee..7b8c238b9 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_objects.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_objects.py @@ -102,3 +102,38 @@ def test_object_show(self): self.assertEqual(self.expected_columns, columns) self.assertEqual(self.expected_data, data) + + +class TestMetadefObjectList(fakes.TestImagev2): + _metadef_namespace = fakes.create_one_metadef_namespace() + _metadef_objects = [fakes.create_one_metadef_object()] + columns = ['name', 'description'] + + datalist = [] + + def setUp(self): + super().setUp() + + self.image_client.metadef_objects.side_effect = [ + self._metadef_objects, + [], + ] + + # Get the command object to test + self.image_client.metadef_objects.return_value = iter( + self._metadef_objects + ) + self.cmd = metadef_objects.ListMetadefObjects(self.app, None) + self.datalist = self._metadef_objects + + def test_metadef_objects_list(self): + arglist = [self._metadef_namespace.namespace] + parsed_args = self.check_parser(self.cmd, arglist, []) + + # 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(getattr(self.datalist[0], 'name'), next(data)[0]) diff --git a/releasenotes/notes/add-metadef-object-list-c8831e73c696b9d9.yaml b/releasenotes/notes/add-metadef-object-list-c8831e73c696b9d9.yaml new file mode 100644 index 000000000..bfa89036a --- /dev/null +++ b/releasenotes/notes/add-metadef-object-list-c8831e73c696b9d9.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``image metadef object list`` command to list the + metadata definitions objects inside a specific namespace diff --git a/setup.cfg b/setup.cfg index 2e2acbed3..a42fafccf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -397,8 +397,11 @@ openstack.image.v2 = 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_property_create = openstackclient.image.v2.metadef_properties:CreateMetadefProperty image_metadef_property_list = openstackclient.image.v2.metadef_properties:ListMetadefProperties From 20490dcd102ef231ae554ee516fa3ce306b8f431 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 10 Nov 2023 10:06:58 +0000 Subject: [PATCH 034/403] tests: Centralise check for networking service No point duplicating this across functional tests. Change-Id: I9502be8b4e718885c6f854c7f5b19f6cacf51055 Signed-off-by: Stephen Finucane --- .../tests/functional/network/v2/common.py | 8 +++++++- .../network/v2/test_address_group.py | 6 ++---- .../network/v2/test_address_scope.py | 6 ------ .../v2/test_default_security_group_rule.py | 6 ++---- .../functional/network/v2/test_floating_ip.py | 9 +++------ .../network/v2/test_ip_availability.py | 11 +++------- .../network/v2/test_l3_conntrack_helper.py | 7 +++---- .../functional/network/v2/test_local_ip.py | 6 ++---- .../functional/network/v2/test_network.py | 6 ------ .../network/v2/test_network_agent.py | 12 ----------- .../network/v2/test_network_flavor.py | 6 ------ .../network/v2/test_network_flavor_profile.py | 6 ------ .../network/v2/test_network_meter.py | 6 ------ .../network/v2/test_network_meter_rule.py | 10 ++-------- .../network/v2/test_network_ndp_proxy.py | 4 +--- .../network/v2/test_network_qos_policy.py | 6 ------ .../network/v2/test_network_qos_rule.py | 20 ++++--------------- .../network/v2/test_network_qos_rule_type.py | 6 ------ .../network/v2/test_network_rbac.py | 5 +---- .../network/v2/test_network_segment.py | 10 ++-------- .../network/v2/test_network_segment_range.py | 7 +++---- .../v2/test_network_service_provider.py | 6 ++---- .../network/v2/test_network_trunk.py | 3 --- .../tests/functional/network/v2/test_port.py | 10 ++-------- .../functional/network/v2/test_router.py | 6 ------ .../network/v2/test_security_group.py | 5 +---- .../network/v2/test_security_group_rule.py | 5 +---- .../functional/network/v2/test_subnet.py | 10 ++-------- .../functional/network/v2/test_subnet_pool.py | 6 ------ 29 files changed, 43 insertions(+), 171 deletions(-) diff --git a/openstackclient/tests/functional/network/v2/common.py b/openstackclient/tests/functional/network/v2/common.py index afdf5d704..eecfff468 100644 --- a/openstackclient/tests/functional/network/v2/common.py +++ b/openstackclient/tests/functional/network/v2/common.py @@ -20,9 +20,15 @@ class NetworkTests(base.TestCase): @classmethod def setUpClass(cls): - super(NetworkTests, cls).setUpClass() + super().setUpClass() cls.haz_network = cls.is_service_enabled('network') + def setUp(self): + super().setUp() + + if not self.haz_network: + self.skipTest("No Network service present") + class NetworkTagTests(NetworkTests): """Functional tests with tag operation""" diff --git a/openstackclient/tests/functional/network/v2/test_address_group.py b/openstackclient/tests/functional/network/v2/test_address_group.py index e3fb2eaed..8146112a3 100644 --- a/openstackclient/tests/functional/network/v2/test_address_group.py +++ b/openstackclient/tests/functional/network/v2/test_address_group.py @@ -19,10 +19,8 @@ class AddressGroupTests(common.NetworkTests): """Functional tests for address group""" def setUp(self): - super(AddressGroupTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() + if not self.is_extension_enabled('address-group'): self.skipTest("No address-group extension present") diff --git a/openstackclient/tests/functional/network/v2/test_address_scope.py b/openstackclient/tests/functional/network/v2/test_address_scope.py index 48957a7ef..6aabae624 100644 --- a/openstackclient/tests/functional/network/v2/test_address_scope.py +++ b/openstackclient/tests/functional/network/v2/test_address_scope.py @@ -23,12 +23,6 @@ class AddressScopeTests(common.NetworkTests): # has its own needs and there are collisions when running # tests in parallel. - def setUp(self): - super(AddressScopeTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") - def test_address_scope_delete(self): """Test create, delete multiple""" name1 = uuid.uuid4().hex 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 76c8053be..d6ce95c1a 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 @@ -19,10 +19,8 @@ class SecurityGroupRuleTests(common.NetworkTests): """Functional tests for security group rule""" def setUp(self): - super(SecurityGroupRuleTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() + if not self.is_extension_enabled("security-groups-default-rules"): self.skipTest("No security-groups-default-rules extension present") diff --git a/openstackclient/tests/functional/network/v2/test_floating_ip.py b/openstackclient/tests/functional/network/v2/test_floating_ip.py index 9f2982508..a1b11a44a 100644 --- a/openstackclient/tests/functional/network/v2/test_floating_ip.py +++ b/openstackclient/tests/functional/network/v2/test_floating_ip.py @@ -21,7 +21,7 @@ class FloatingIpTests(common.NetworkTests): @classmethod def setUpClass(cls): - common.NetworkTests.setUpClass() + super().setUpClass() if cls.haz_network: # Create common networks that all tests share cls.EXTERNAL_NETWORK_NAME = uuid.uuid4().hex @@ -53,13 +53,10 @@ def tearDownClass(cls): ) cls.assertOutput('', del_output) finally: - super(FloatingIpTests, cls).tearDownClass() + super().tearDownClass() def setUp(self): - super(FloatingIpTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() # Verify setup self.assertIsNotNone(self.external_network_id) diff --git a/openstackclient/tests/functional/network/v2/test_ip_availability.py b/openstackclient/tests/functional/network/v2/test_ip_availability.py index 643f03555..1cdbd487a 100644 --- a/openstackclient/tests/functional/network/v2/test_ip_availability.py +++ b/openstackclient/tests/functional/network/v2/test_ip_availability.py @@ -20,7 +20,8 @@ class IPAvailabilityTests(common.NetworkTests): @classmethod def setUpClass(cls): - common.NetworkTests.setUpClass() + super().setUpClass() + if cls.haz_network: cls.NAME = uuid.uuid4().hex cls.NETWORK_NAME = uuid.uuid4().hex @@ -49,13 +50,7 @@ def tearDownClass(cls): cls.assertOutput('', raw_subnet) cls.assertOutput('', raw_network) finally: - super(IPAvailabilityTests, cls).tearDownClass() - - def setUp(self): - super(IPAvailabilityTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().tearDownClass() def test_ip_availability_list(self): """Test ip availability list""" 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 f899b041f..510ec8b2d 100644 --- a/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py +++ b/openstackclient/tests/functional/network/v2/test_l3_conntrack_helper.py @@ -18,12 +18,11 @@ class L3ConntrackHelperTests(common.NetworkTests): def setUp(self): - super(L3ConntrackHelperTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() + if not self.is_extension_enabled('l3-conntrack-helper'): self.skipTest("No l3-conntrack-helper extension present") + if not self.is_extension_enabled('expose-l3-conntrack-helper'): self.skipTest("No expose-l3-conntrack-helper extension present") diff --git a/openstackclient/tests/functional/network/v2/test_local_ip.py b/openstackclient/tests/functional/network/v2/test_local_ip.py index 921dd13dc..a1553bf3c 100644 --- a/openstackclient/tests/functional/network/v2/test_local_ip.py +++ b/openstackclient/tests/functional/network/v2/test_local_ip.py @@ -21,10 +21,8 @@ class LocalIPTests(common.NetworkTests): """Functional tests for local IP""" def setUp(self): - super(LocalIPTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() + if not self.is_extension_enabled('local-ip'): self.skipTest("No local-ip extension present") diff --git a/openstackclient/tests/functional/network/v2/test_network.py b/openstackclient/tests/functional/network/v2/test_network.py index 16886b62e..5f1e71392 100644 --- a/openstackclient/tests/functional/network/v2/test_network.py +++ b/openstackclient/tests/functional/network/v2/test_network.py @@ -20,12 +20,6 @@ class NetworkTests(common.NetworkTagTests): base_command = 'network' - def setUp(self): - super(NetworkTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") - def test_network_create_compute(self): """Test Nova-net create options, delete""" if self.haz_network: diff --git a/openstackclient/tests/functional/network/v2/test_network_agent.py b/openstackclient/tests/functional/network/v2/test_network_agent.py index 6ff2c0f01..3cfb26ac3 100644 --- a/openstackclient/tests/functional/network/v2/test_network_agent.py +++ b/openstackclient/tests/functional/network/v2/test_network_agent.py @@ -18,12 +18,6 @@ class NetworkAgentTests(common.NetworkTests): """Functional tests for network agent""" - def setUp(self): - super(NetworkAgentTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") - def test_network_agent_list_show_set(self): """Test network agent list, set, show commands @@ -88,12 +82,6 @@ def test_network_agent_list_show_set(self): class NetworkAgentListTests(common.NetworkTests): """Functional test for network agent""" - def setUp(self): - super(NetworkAgentListTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") - def test_network_dhcp_agent_list(self): """Test network agent list""" diff --git a/openstackclient/tests/functional/network/v2/test_network_flavor.py b/openstackclient/tests/functional/network/v2/test_network_flavor.py index 13dc0d5cc..e04d10e80 100644 --- a/openstackclient/tests/functional/network/v2/test_network_flavor.py +++ b/openstackclient/tests/functional/network/v2/test_network_flavor.py @@ -18,12 +18,6 @@ class NetworkFlavorTests(common.NetworkTests): """Functional tests for network flavor""" - def setUp(self): - super(NetworkFlavorTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") - def test_network_flavor_add_remove_profile(self): """Test add and remove network flavor to/from profile""" # Create Flavor diff --git a/openstackclient/tests/functional/network/v2/test_network_flavor_profile.py b/openstackclient/tests/functional/network/v2/test_network_flavor_profile.py index c6f84f824..7a64da768 100644 --- a/openstackclient/tests/functional/network/v2/test_network_flavor_profile.py +++ b/openstackclient/tests/functional/network/v2/test_network_flavor_profile.py @@ -19,12 +19,6 @@ class NetworkFlavorProfileTests(common.NetworkTests): DESCRIPTION = 'fakedescription' METAINFO = 'Extrainfo' - def setUp(self): - super(NetworkFlavorProfileTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") - def test_network_flavor_profile_create(self): json_output = self.openstack( 'network flavor profile create ' diff --git a/openstackclient/tests/functional/network/v2/test_network_meter.py b/openstackclient/tests/functional/network/v2/test_network_meter.py index aa00e25b7..82e94c43f 100644 --- a/openstackclient/tests/functional/network/v2/test_network_meter.py +++ b/openstackclient/tests/functional/network/v2/test_network_meter.py @@ -26,12 +26,6 @@ class TestMeter(common.NetworkTests): # has its own needs and there are collisions when running # tests in parallel. - def setUp(self): - super(TestMeter, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") - def test_meter_delete(self): """Test create, delete multiple""" name1 = uuid.uuid4().hex 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 40838855a..d41102a52 100644 --- a/openstackclient/tests/functional/network/v2/test_network_meter_rule.py +++ b/openstackclient/tests/functional/network/v2/test_network_meter_rule.py @@ -26,7 +26,7 @@ class TestMeterRule(common.NetworkTests): @classmethod def setUpClass(cls): - common.NetworkTests.setUpClass() + super().setUpClass() if cls.haz_network: cls.METER_NAME = uuid.uuid4().hex @@ -45,13 +45,7 @@ def tearDownClass(cls): ) cls.assertOutput('', raw_output) finally: - common.NetworkTests.tearDownClass() - - def setUp(self): - super(TestMeterRule, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().tearDownClass() def test_meter_rule_delete(self): """test create, delete""" 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 62eecf869..d22f29747 100644 --- a/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py +++ b/openstackclient/tests/functional/network/v2/test_network_ndp_proxy.py @@ -16,9 +16,7 @@ class L3NDPProxyTests(common.NetworkTests): def setUp(self): super().setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + if not self.is_extension_enabled('l3-ndp-proxy'): self.skipTest("No l3-ndp-proxy extension present") diff --git a/openstackclient/tests/functional/network/v2/test_network_qos_policy.py b/openstackclient/tests/functional/network/v2/test_network_qos_policy.py index 492f6a0a5..2662a9975 100644 --- a/openstackclient/tests/functional/network/v2/test_network_qos_policy.py +++ b/openstackclient/tests/functional/network/v2/test_network_qos_policy.py @@ -21,12 +21,6 @@ class NetworkQosPolicyTests(common.NetworkTests): """Functional tests for QoS policy""" - def setUp(self): - super(NetworkQosPolicyTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") - def test_qos_rule_create_delete(self): # This is to check the output of qos policy delete policy_name = uuid.uuid4().hex 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 25455b138..9cc4ec9ae 100644 --- a/openstackclient/tests/functional/network/v2/test_network_qos_rule.py +++ b/openstackclient/tests/functional/network/v2/test_network_qos_rule.py @@ -22,10 +22,7 @@ class NetworkQosRuleTestsMinimumBandwidth(common.NetworkTests): """Functional tests for QoS minimum bandwidth rule""" def setUp(self): - super(NetworkQosRuleTestsMinimumBandwidth, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex @@ -100,10 +97,7 @@ class NetworkQosRuleTestsMinimumPacketRate(common.NetworkTests): """Functional tests for QoS minimum packet rate rule""" def setUp(self): - super(NetworkQosRuleTestsMinimumPacketRate, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex @@ -179,10 +173,7 @@ class NetworkQosRuleTestsDSCPMarking(common.NetworkTests): """Functional tests for QoS DSCP marking rule""" def setUp(self): - super(NetworkQosRuleTestsDSCPMarking, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex self.openstack('network qos policy create %s' % self.QOS_POLICY_NAME) @@ -254,10 +245,7 @@ class NetworkQosRuleTestsBandwidthLimit(common.NetworkTests): """Functional tests for QoS bandwidth limit rule""" def setUp(self): - super(NetworkQosRuleTestsBandwidthLimit, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex self.openstack('network qos policy create %s' % self.QOS_POLICY_NAME) 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 77f0d71c4..a595043a3 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 @@ -29,12 +29,6 @@ class NetworkQosRuleTypeTests(common.NetworkTests): 'minimum_packet_rate', ] - def setUp(self): - super(NetworkQosRuleTypeTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") - def test_qos_rule_type_list(self): cmd_output = self.openstack( 'network qos rule type list -f json', diff --git a/openstackclient/tests/functional/network/v2/test_network_rbac.py b/openstackclient/tests/functional/network/v2/test_network_rbac.py index e5130cd10..657175a28 100644 --- a/openstackclient/tests/functional/network/v2/test_network_rbac.py +++ b/openstackclient/tests/functional/network/v2/test_network_rbac.py @@ -24,10 +24,7 @@ class NetworkRBACTests(common.NetworkTests): FIELDS = ['id'] def setUp(self): - super(NetworkRBACTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() self.NET_NAME = uuid.uuid4().hex self.PROJECT_NAME = uuid.uuid4().hex diff --git a/openstackclient/tests/functional/network/v2/test_network_segment.py b/openstackclient/tests/functional/network/v2/test_network_segment.py index f8b5aaf60..f694ed76d 100644 --- a/openstackclient/tests/functional/network/v2/test_network_segment.py +++ b/openstackclient/tests/functional/network/v2/test_network_segment.py @@ -20,7 +20,7 @@ class NetworkSegmentTests(common.NetworkTests): @classmethod def setUpClass(cls): - common.NetworkTests.setUpClass() + super().setUpClass() if cls.haz_network: cls.NETWORK_NAME = uuid.uuid4().hex cls.PHYSICAL_NETWORK_NAME = uuid.uuid4().hex @@ -42,13 +42,7 @@ def tearDownClass(cls): ) cls.assertOutput('', raw_output) finally: - super(NetworkSegmentTests, cls).tearDownClass() - - def setUp(self): - super(NetworkSegmentTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().tearDownClass() def test_network_segment_create_delete(self): name = uuid.uuid4().hex 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 0dd67ead0..604ee3cfa 100644 --- a/openstackclient/tests/functional/network/v2/test_network_segment_range.py +++ b/openstackclient/tests/functional/network/v2/test_network_segment_range.py @@ -23,12 +23,11 @@ class NetworkSegmentRangeTests(common.NetworkTests): """Functional tests for network segment range""" def setUp(self): - super(NetworkSegmentRangeTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() + if not self.is_extension_enabled('network-segment-range'): self.skipTest("No network-segment-range extension present") + self.PROJECT_NAME = uuid.uuid4().hex def test_network_segment_range_create_delete(self): diff --git a/openstackclient/tests/functional/network/v2/test_network_service_provider.py b/openstackclient/tests/functional/network/v2/test_network_service_provider.py index dd1d26e21..9be2827b1 100644 --- a/openstackclient/tests/functional/network/v2/test_network_service_provider.py +++ b/openstackclient/tests/functional/network/v2/test_network_service_provider.py @@ -20,10 +20,8 @@ class TestNetworkServiceProvider(common.NetworkTests): """Functional tests for network service provider""" def setUp(self): - super(TestNetworkServiceProvider, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() + # NOTE(slaweq): # that tests should works only when "standard" Neutron L3 agent is # used, as e.g. OVN L3 plugin don't supports that. diff --git a/openstackclient/tests/functional/network/v2/test_network_trunk.py b/openstackclient/tests/functional/network/v2/test_network_trunk.py index bb50164fe..b2ec305eb 100644 --- a/openstackclient/tests/functional/network/v2/test_network_trunk.py +++ b/openstackclient/tests/functional/network/v2/test_network_trunk.py @@ -23,9 +23,6 @@ class NetworkTrunkTests(common.NetworkTests): def setUp(self): super().setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") network_name = uuid.uuid4().hex subnet_name = uuid.uuid4().hex diff --git a/openstackclient/tests/functional/network/v2/test_port.py b/openstackclient/tests/functional/network/v2/test_port.py index b3d3a109c..700c8dfd4 100644 --- a/openstackclient/tests/functional/network/v2/test_port.py +++ b/openstackclient/tests/functional/network/v2/test_port.py @@ -25,7 +25,7 @@ class PortTests(common.NetworkTagTests): @classmethod def setUpClass(cls): - common.NetworkTests.setUpClass() + super().setUpClass() if cls.haz_network: cls.NAME = uuid.uuid4().hex cls.NETWORK_NAME = uuid.uuid4().hex @@ -42,13 +42,7 @@ def tearDownClass(cls): ) cls.assertOutput('', raw_output) finally: - super(PortTests, cls).tearDownClass() - - def setUp(self): - super(PortTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().tearDownClass() def test_port_delete(self): """Test create, delete multiple""" diff --git a/openstackclient/tests/functional/network/v2/test_router.py b/openstackclient/tests/functional/network/v2/test_router.py index fcd72d9fa..4f7c4c4a5 100644 --- a/openstackclient/tests/functional/network/v2/test_router.py +++ b/openstackclient/tests/functional/network/v2/test_router.py @@ -20,12 +20,6 @@ class RouterTests(common.NetworkTagTests): base_command = 'router' - def setUp(self): - super(RouterTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") - def test_router_create_and_delete(self): """Test create options, delete multiple""" name1 = uuid.uuid4().hex diff --git a/openstackclient/tests/functional/network/v2/test_security_group.py b/openstackclient/tests/functional/network/v2/test_security_group.py index 571c01ee1..1eb03a5a2 100644 --- a/openstackclient/tests/functional/network/v2/test_security_group.py +++ b/openstackclient/tests/functional/network/v2/test_security_group.py @@ -19,10 +19,7 @@ class SecurityGroupTests(common.NetworkTests): """Functional tests for security group""" def setUp(self): - super(SecurityGroupTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() self.NAME = uuid.uuid4().hex self.OTHER_NAME = uuid.uuid4().hex diff --git a/openstackclient/tests/functional/network/v2/test_security_group_rule.py b/openstackclient/tests/functional/network/v2/test_security_group_rule.py index 6fdd041c1..0a55697d3 100644 --- a/openstackclient/tests/functional/network/v2/test_security_group_rule.py +++ b/openstackclient/tests/functional/network/v2/test_security_group_rule.py @@ -19,10 +19,7 @@ class SecurityGroupRuleTests(common.NetworkTests): """Functional tests for security group rule""" def setUp(self): - super(SecurityGroupRuleTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().setUp() self.SECURITY_GROUP_NAME = uuid.uuid4().hex diff --git a/openstackclient/tests/functional/network/v2/test_subnet.py b/openstackclient/tests/functional/network/v2/test_subnet.py index 90eb77299..0aff30b85 100644 --- a/openstackclient/tests/functional/network/v2/test_subnet.py +++ b/openstackclient/tests/functional/network/v2/test_subnet.py @@ -23,7 +23,7 @@ class SubnetTests(common.NetworkTagTests): @classmethod def setUpClass(cls): - common.NetworkTests.setUpClass() + super().setUpClass() if cls.haz_network: cls.NETWORK_NAME = uuid.uuid4().hex @@ -44,13 +44,7 @@ def tearDownClass(cls): ) cls.assertOutput('', raw_output) finally: - super(SubnetTests, cls).tearDownClass() - - def setUp(self): - super(SubnetTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") + super().tearDownClass() def test_subnet_create_and_delete(self): """Test create, delete multiple""" diff --git a/openstackclient/tests/functional/network/v2/test_subnet_pool.py b/openstackclient/tests/functional/network/v2/test_subnet_pool.py index f7cb1d742..89eb6f0ed 100644 --- a/openstackclient/tests/functional/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/functional/network/v2/test_subnet_pool.py @@ -21,12 +21,6 @@ class SubnetPoolTests(common.NetworkTagTests): base_command = 'subnet pool' - def setUp(self): - super(SubnetPoolTests, self).setUp() - # Nothing in this class works with Nova Network - if not self.haz_network: - self.skipTest("No Network service present") - def test_subnet_pool_create_delete(self): """Test create, delete""" name1 = uuid.uuid4().hex From c251cb89a5cd11120106f6b0fb662b7daf815597 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 10 Nov 2023 10:20:03 +0000 Subject: [PATCH 035/403] tests: Fix API extension check The check for extensions was not using a machine readable format for output. This meant an API check could match on a substring, e.g. a check for 'qos' would match on 'qos-specs'. Address this issue by switching our command invocation to use JSON output and migrating the check function from the general base class to the networking base class. Change-Id: Idc6dc54503031ddf3e148f50ed53ad8898f7a7e3 Signed-off-by: Stephen Finucane --- openstackclient/tests/functional/base.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openstackclient/tests/functional/base.py b/openstackclient/tests/functional/base.py index 9b4235bec..86fca8fe8 100644 --- a/openstackclient/tests/functional/base.py +++ b/openstackclient/tests/functional/base.py @@ -119,9 +119,13 @@ def is_service_enabled(cls, service, version=None): return bool(ret) @classmethod - def is_extension_enabled(cls, alias): + def is_extension_enabled(cls, alias, *, service='network'): """Ask client cloud if extension is enabled""" - return alias in cls.openstack('extension list -f value -c Alias') + extensions = cls.openstack( + f'extension list --{service}', + parse_output=True, + ) + return alias in [x['Alias'] for x in extensions] @classmethod def get_openstack_configuration_value(cls, configuration): From 1678f87d7ac72ad2064db1a3d34b66d506db76bd Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 10 Nov 2023 10:25:29 +0000 Subject: [PATCH 036/403] tests: Handle missing extensions in network tests This is mostly a case of skipping tests where the extension is missing, but some tests are updated to remove a reliance on optional tests. Change-Id: I25b8811fe09f2a4a9fc20ca5459f5a404b88a337 Signed-off-by: Stephen Finucane --- .../network/v2/test_network_meter_rule.py | 6 ++++++ .../network/v2/test_network_qos_rule.py | 16 ++++++++++++---- .../network/v2/test_network_segment.py | 13 +++++++------ .../functional/network/v2/test_network_trunk.py | 3 +++ .../tests/functional/network/v2/test_subnet.py | 16 ++-------------- 5 files changed, 30 insertions(+), 24 deletions(-) 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 d41102a52..e0fdcd3f3 100644 --- a/openstackclient/tests/functional/network/v2/test_network_meter_rule.py +++ b/openstackclient/tests/functional/network/v2/test_network_meter_rule.py @@ -47,6 +47,12 @@ def tearDownClass(cls): finally: super().tearDownClass() + def setUp(self): + super().setUp() + + if not self.is_extension_enabled("metering"): + self.skipTest("No metering extension present") + def test_meter_rule_delete(self): """test create, delete""" 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 9cc4ec9ae..0e16a5035 100644 --- a/openstackclient/tests/functional/network/v2/test_network_qos_rule.py +++ b/openstackclient/tests/functional/network/v2/test_network_qos_rule.py @@ -18,7 +18,15 @@ from openstackclient.tests.functional.network.v2 import common -class NetworkQosRuleTestsMinimumBandwidth(common.NetworkTests): +class NetworkQosTests(common.NetworkTests): + def setUp(self): + super().setUp() + + if not self.is_extension_enabled("qos"): + self.skipTest("No qos extension present") + + +class NetworkQosRuleTestsMinimumBandwidth(NetworkQosTests): """Functional tests for QoS minimum bandwidth rule""" def setUp(self): @@ -93,7 +101,7 @@ def test_qos_rule_set(self): self.assertEqual(7500, cmd_output['min_kbps']) -class NetworkQosRuleTestsMinimumPacketRate(common.NetworkTests): +class NetworkQosRuleTestsMinimumPacketRate(NetworkQosTests): """Functional tests for QoS minimum packet rate rule""" def setUp(self): @@ -169,7 +177,7 @@ def test_qos_rule_set(self): self.assertEqual('any', cmd_output['direction']) -class NetworkQosRuleTestsDSCPMarking(common.NetworkTests): +class NetworkQosRuleTestsDSCPMarking(NetworkQosTests): """Functional tests for QoS DSCP marking rule""" def setUp(self): @@ -241,7 +249,7 @@ def test_qos_rule_set(self): self.assertEqual(32, cmd_output['dscp_mark']) -class NetworkQosRuleTestsBandwidthLimit(common.NetworkTests): +class NetworkQosRuleTestsBandwidthLimit(NetworkQosTests): """Functional tests for QoS bandwidth limit rule""" def setUp(self): diff --git a/openstackclient/tests/functional/network/v2/test_network_segment.py b/openstackclient/tests/functional/network/v2/test_network_segment.py index f694ed76d..03f5daf74 100644 --- a/openstackclient/tests/functional/network/v2/test_network_segment.py +++ b/openstackclient/tests/functional/network/v2/test_network_segment.py @@ -44,6 +44,12 @@ def tearDownClass(cls): finally: super().tearDownClass() + def setUp(self): + super().setUp() + + if not self.is_extension_enabled("segment"): + self.skipTest("No segment extension present") + def test_network_segment_create_delete(self): name = uuid.uuid4().hex json_output = self.openstack( @@ -110,12 +116,7 @@ def test_network_segment_set_show(self): ) self.addCleanup(self.openstack, 'network segment delete ' + name) - extension_output = self.openstack( - "extension list ", - parse_output=True, - ) - ext_alias = [x["Alias"] for x in extension_output] - if "standard-attr-segment" in ext_alias: + if self.is_extension_enabled('standard-attr-segment'): self.assertEqual( '', json_output["description"], diff --git a/openstackclient/tests/functional/network/v2/test_network_trunk.py b/openstackclient/tests/functional/network/v2/test_network_trunk.py index b2ec305eb..7c504286d 100644 --- a/openstackclient/tests/functional/network/v2/test_network_trunk.py +++ b/openstackclient/tests/functional/network/v2/test_network_trunk.py @@ -24,6 +24,9 @@ class NetworkTrunkTests(common.NetworkTests): def setUp(self): super().setUp() + if not self.is_extension_enabled("trunk"): + self.skipTest("No trunk extension present") + network_name = uuid.uuid4().hex subnet_name = uuid.uuid4().hex self.parent_port_name = uuid.uuid4().hex diff --git a/openstackclient/tests/functional/network/v2/test_subnet.py b/openstackclient/tests/functional/network/v2/test_subnet.py index 0aff30b85..2ec987e9b 100644 --- a/openstackclient/tests/functional/network/v2/test_subnet.py +++ b/openstackclient/tests/functional/network/v2/test_subnet.py @@ -211,7 +211,6 @@ def test_subnet_set_show_unset(self): + ' --description bbbb ' + '--no-dhcp ' + '--gateway 10.10.11.1 ' - + '--service-type network:floatingip_agent_gateway ' + name ) self.assertOutput('', cmd_output) @@ -236,27 +235,16 @@ def test_subnet_set_show_unset(self): '10.10.11.1', cmd_output["gateway_ip"], ) - self.assertEqual( - ['network:floatingip_agent_gateway'], - cmd_output["service_types"], - ) # Test unset - cmd_output = self.openstack( - 'subnet unset ' - + '--service-type network:floatingip_agent_gateway ' - + new_name - ) + cmd_output = self.openstack('subnet unset --gateway ' + new_name) self.assertOutput('', cmd_output) cmd_output = self.openstack( 'subnet show ' + new_name, parse_output=True, ) - self.assertEqual( - [], - cmd_output["service_types"], - ) + self.assertIsNone(cmd_output["gateway_ip"]) def _subnet_create(self, cmd, name, is_type_ipv4=True): # Try random subnet range for subnet creating From d0d4077e1d88f7f1d4caf58dcfe1b20840c34cd4 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 13 Nov 2023 17:29:35 +0000 Subject: [PATCH 037/403] tests: Remove prints Change-Id: I35adac1199769fd73978b6457044191d02bb5bd4 Signed-off-by: Stephen Finucane --- openstackclient/tests/unit/identity/v3/test_trust.py | 2 -- openstackclient/tests/unit/integ/cli/test_shell.py | 2 -- openstackclient/tests/unit/test_shell.py | 8 +------- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/openstackclient/tests/unit/identity/v3/test_trust.py b/openstackclient/tests/unit/identity/v3/test_trust.py index 67e64cea3..8a7903e08 100644 --- a/openstackclient/tests/unit/identity/v3/test_trust.py +++ b/openstackclient/tests/unit/identity/v3/test_trust.py @@ -294,7 +294,6 @@ def test_trust_list_trustee(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - print(self.trusts_mock.list.call_args_list) self.trusts_mock.list.assert_any_call( trustee_user=self.users_mock.get(), trustor_user=None, @@ -335,7 +334,6 @@ def test_trust_list_trustor(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - print(self.trusts_mock.list.call_args_list) self.trusts_mock.list.assert_any_call( trustor_user=self.users_mock.get(), trustee_user=None, diff --git a/openstackclient/tests/unit/integ/cli/test_shell.py b/openstackclient/tests/unit/integ/cli/test_shell.py index 9cd701345..6951c2afc 100644 --- a/openstackclient/tests/unit/integ/cli/test_shell.py +++ b/openstackclient/tests/unit/integ/cli/test_shell.py @@ -502,7 +502,6 @@ def vendor_mock_return(): ) # +env, -cli, +occ - print("auth_req: %s" % auth_req['auth']) self.assertEqual( test_shell.DEFAULT_USERNAME, auth_req['auth']['identity']['password']['user']['name'], @@ -559,7 +558,6 @@ def vendor_mock_return(): ) # +env, +cli, +occ - print("auth_req: %s" % auth_req['auth']) self.assertEqual( 'zarquon', auth_req['auth']['identity']['password']['user']['name'], diff --git a/openstackclient/tests/unit/test_shell.py b/openstackclient/tests/unit/test_shell.py index 4081b670f..5d4bc6a83 100644 --- a/openstackclient/tests/unit/test_shell.py +++ b/openstackclient/tests/unit/test_shell.py @@ -141,10 +141,6 @@ class TestShell(osc_lib_test_utils.TestShell): # Full name of the OpenStackShell class to test (cliff.app.App subclass) shell_class_name = "openstackclient.shell.OpenStackShell" - # TODO(dtroyer): remove this once the shell_class_patch patch is released - # in osc-lib - app_patch = shell_class_name - def setUp(self): super(TestShell, self).setUp() # TODO(dtroyer): remove this once the shell_class_patch patch is @@ -162,7 +158,6 @@ def _assert_admin_token_auth(self, cmd_options, default_args): ) _cmd = cmd_options + " list role" osc_lib_test_utils.fake_execute(_shell, _cmd) - print("_shell: %s" % _shell) self.app.assert_called_with(["list", "role"]) self.assertEqual( @@ -178,7 +173,7 @@ def _assert_admin_token_auth(self, cmd_options, default_args): def _assert_token_auth(self, cmd_options, default_args): with mock.patch( - self.app_patch + ".initialize_app", + self.shell_class_name + ".initialize_app", self.app, ): _shell = osc_lib_test_utils.make_shell( @@ -186,7 +181,6 @@ def _assert_token_auth(self, cmd_options, default_args): ) _cmd = cmd_options + " list role" osc_lib_test_utils.fake_execute(_shell, _cmd) - print("_shell: %s" % _shell) self.app.assert_called_with(["list", "role"]) self.assertEqual( From 89a8f72960893b492fc5c69b3a150233c06db8b0 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 5 Oct 2022 17:48:32 +0100 Subject: [PATCH 038/403] tests: Enable logging fixture Quieten the output of our test runs significantly. Change-Id: Ie32c919bb987eb0b9bc4c5b2ec54ee20a6841c03 Signed-off-by: Stephen Finucane --- openstackclient/network/v2/local_ip_association.py | 2 +- openstackclient/tests/unit/utils.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/openstackclient/network/v2/local_ip_association.py b/openstackclient/network/v2/local_ip_association.py index d54ac9c90..9f8325be4 100644 --- a/openstackclient/network/v2/local_ip_association.py +++ b/openstackclient/network/v2/local_ip_association.py @@ -124,7 +124,7 @@ def take_action(self, parsed_args): "fixed port " "name or ID '%(fixed_port_id)s': %(e)s" ), - {'fixed port ID': fixed_port_id, 'e': e}, + {'fixed_port_id': fixed_port_id, 'e': e}, ) if result > 0: diff --git a/openstackclient/tests/unit/utils.py b/openstackclient/tests/unit/utils.py index 1691424c6..93f548876 100644 --- a/openstackclient/tests/unit/utils.py +++ b/openstackclient/tests/unit/utils.py @@ -51,6 +51,8 @@ def setUp(self): stderr = self.useFixture(fixtures.StringStream("stderr")).stream self.useFixture(fixtures.MonkeyPatch("sys.stderr", stderr)) + self.log = self.useFixture(fixtures.LoggerFixture()) + def assertNotCalled(self, m, msg=None): """Assert a function was not called""" From 900fad5360524d65f97c148dc89ddd2e94254822 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 14 Nov 2023 12:18:20 +0000 Subject: [PATCH 039/403] tests: Remove unused flag Nothing is setting 'merge_stderr'. It looks like a carry-over from the legacy clients. Change-Id: I32b65830e11b27ba83dfba002bf996af561b0768 Signed-off-by: Stephen Finucane --- openstackclient/tests/functional/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstackclient/tests/functional/base.py b/openstackclient/tests/functional/base.py index 86fca8fe8..18ff65baf 100644 --- a/openstackclient/tests/functional/base.py +++ b/openstackclient/tests/functional/base.py @@ -24,12 +24,12 @@ LOG = logging.getLogger(__name__) -def execute(cmd, fail_ok=False, merge_stderr=False): +def execute(cmd, *, fail_ok=False): """Executes specified command for the given action.""" LOG.debug('Executing: %s', cmd) cmdlist = shlex.split(cmd) stdout = subprocess.PIPE - stderr = subprocess.STDOUT if merge_stderr else subprocess.PIPE + stderr = subprocess.PIPE proc = subprocess.Popen(cmdlist, stdout=stdout, stderr=stderr) From 885f5912eb531bc5594f98f834f2920a8e103c91 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 15 Nov 2023 11:19:14 +0000 Subject: [PATCH 040/403] compute: Address bug in shelve offload logic We were reusing a variable from a previous loop, which meant this would never work with multiple servers. Correct the mistake. Change-Id: I52246e183fb2cf0d855d92058dd305b48783589d Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 28 +++++++++---------- .../tests/unit/compute/v2/test_server.py | 18 +++++++----- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 4854e91f8..5816f3600 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -4416,6 +4416,7 @@ def _show_progress(progress): self.app.stdout.flush() compute_client = self.app.client_manager.sdk_connection.compute + server_ids = [] for server in parsed_args.servers: server_obj = compute_client.find_server( @@ -4425,6 +4426,8 @@ def _show_progress(progress): if server_obj.status.lower() in ('shelved', 'shelved_offloaded'): continue + server_ids.append(server_obj.id) + compute_client.shelve_server(server_obj.id) # if we don't have to wait, either because it was requested explicitly @@ -4432,54 +4435,51 @@ def _show_progress(progress): if not parsed_args.wait and not parsed_args.offload: return - for server in parsed_args.servers: + for server_id in server_ids: # We use osc-lib's wait_for_status since that allows for a callback # TODO(stephenfin): We should wait for these in parallel using e.g. # https://review.opendev.org/c/openstack/osc-lib/+/762503/ if not utils.wait_for_status( compute_client.get_server, - server_obj.id, + server_id, success_status=('shelved', 'shelved_offloaded'), callback=_show_progress, ): - LOG.error(_('Error shelving server: %s'), server_obj.id) + LOG.error(_('Error shelving server: %s'), server_id) self.app.stdout.write( - _('Error shelving server: %s\n') % server_obj.id + _('Error shelving server: %s\n') % server_id ) raise SystemExit if not parsed_args.offload: return - for server in parsed_args.servers: - server_obj = compute_client.find_server( - server, - ignore_missing=False, - ) + for server_id in server_ids: + server_obj = compute_client.get_server(server_id) if server_obj.status.lower() == 'shelved_offloaded': continue - compute_client.shelve_offload_server(server_obj.id) + compute_client.shelve_offload_server(server_id) if not parsed_args.wait: return - for server in parsed_args.servers: + for server_id in server_ids: # We use osc-lib's wait_for_status since that allows for a callback # TODO(stephenfin): We should wait for these in parallel using e.g. # https://review.opendev.org/c/openstack/osc-lib/+/762503/ if not utils.wait_for_status( compute_client.get_server, - server_obj.id, + server_id, success_status=('shelved_offloaded',), callback=_show_progress, ): LOG.error( _('Error offloading shelved server %s'), - server_obj.id, + server_id, ) self.app.stdout.write( - _('Error offloading shelved server: %s\n') % server_obj.id + _('Error offloading shelved server: %s\n') % server_id ) raise SystemExit diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 03237e90f..032560a2d 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -8149,20 +8149,24 @@ def test_shelve_offload(self, mock_wait_for_status): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - # two calls - one to retrieve the server state before shelving and - # another to do this before offloading - self.compute_sdk_client.find_server.assert_has_calls( - [ - mock.call(self.server.name, ignore_missing=False), - mock.call(self.server.name, ignore_missing=False), - ] + # one call to retrieve to retrieve the server state before shelving + self.compute_sdk_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 ) + # one call to shelve the server self.compute_sdk_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.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.server.id, From 432698fea292beb49a0c0d04def5ab81151a2627 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 15 Nov 2023 11:26:31 +0000 Subject: [PATCH 041/403] Use CommandError, not SystemExit, to exit Change-Id: Id2bcc18420b17cf3afed5584ef5104c3ef413830 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 62 +++++++------------ .../tests/unit/compute/v2/test_server.py | 25 +++++--- 2 files changed, 40 insertions(+), 47 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 5816f3600..d923b30c6 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1987,9 +1987,10 @@ def _match_image(image_api, wanted_properties): ): self.app.stdout.write('\n') else: - LOG.error('Error creating server: %s', parsed_args.server_name) - self.app.stdout.write(_('Error creating server\n')) - raise SystemExit + msg = ( + _('Error unshelving server: %s') % parsed_args.server_name + ) + raise exceptions.CommandError(msg) details = _prep_server_detail(compute_client, image_client, server) return zip(*sorted(details.items())) @@ -2081,10 +2082,8 @@ def _show_progress(progress): server_obj.id, callback=_show_progress, ): - msg = _('Error deleting server: %s') - LOG.error(msg, server_obj.id) - self.app.stdout.write(_('Error deleting server\n')) - raise SystemExit + msg = _('Error deleting server: %s') % server_obj.id + raise exceptions.CommandError(msg) def percent_type(x): @@ -3110,9 +3109,8 @@ def _show_progress(progress): ): self.app.stdout.write(_('Complete\n')) else: - LOG.error(_('Error migrating server: %s'), server.id) - self.app.stdout.write(_('Error migrating server\n')) - raise SystemExit + msg = _('Error migrating server: %s') % server.id + raise exceptions.CommandError(msg) class PauseServer(command.Command): @@ -3194,9 +3192,8 @@ def _show_progress(progress): ): self.app.stdout.write(_('Complete\n')) else: - LOG.error(_('Error rebooting server: %s'), server_id) - self.app.stdout.write(_('Error rebooting server\n')) - raise SystemExit + msg = _('Error rebooting server: %s') % server_id + raise exceptions.CommandError(msg) class RebuildServer(command.ShowOne): @@ -3566,9 +3563,8 @@ def _show_progress(progress): ): self.app.stdout.write(_('Complete\n')) else: - LOG.error(_('Error rebuilding server: %s'), server.id) - self.app.stdout.write(_('Error rebuilding server\n')) - raise SystemExit + msg = _('Error rebuilding server: %s') % server.id + raise exceptions.CommandError(msg) details = _prep_server_detail( compute_client, image_client, server, refresh=False @@ -3689,9 +3685,8 @@ def _show_progress(progress): ): self.app.stdout.write(_('Complete\n')) else: - LOG.error(_('Error evacuating server: %s'), server.id) - self.app.stdout.write(_('Error evacuating server\n')) - raise SystemExit + msg = _('Error evacuating server: %s') % server.id + raise exceptions.CommandError(msg) details = _prep_server_detail( compute_client, image_client, server, refresh=True @@ -4042,9 +4037,8 @@ def _show_progress(progress): ): self.app.stdout.write(_('Complete\n')) else: - LOG.error(_('Error resizing server: %s'), server.id) - self.app.stdout.write(_('Error resizing server\n')) - raise SystemExit + msg = _('Error resizing server: %s') % server.id + raise exceptions.CommandError(msg) elif parsed_args.confirm: self.log.warning( _( @@ -4445,11 +4439,8 @@ def _show_progress(progress): success_status=('shelved', 'shelved_offloaded'), callback=_show_progress, ): - LOG.error(_('Error shelving server: %s'), server_id) - self.app.stdout.write( - _('Error shelving server: %s\n') % server_id - ) - raise SystemExit + msg = _('Error shelving server: %s') % server_id + raise exceptions.CommandError(msg) if not parsed_args.offload: return @@ -4474,14 +4465,8 @@ def _show_progress(progress): success_status=('shelved_offloaded',), callback=_show_progress, ): - LOG.error( - _('Error offloading shelved server %s'), - server_id, - ) - self.app.stdout.write( - _('Error offloading shelved server: %s\n') % server_id - ) - raise SystemExit + msg = _('Error offloading shelved server: %s') % server_id + raise exceptions.CommandError(msg) class ShowServer(command.ShowOne): @@ -5073,8 +5058,5 @@ def _show_progress(progress): success_status=('active', 'shutoff'), callback=_show_progress, ): - LOG.error(_('Error unshelving server %s'), server_obj.id) - self.app.stdout.write( - _('Error unshelving server: %s\n') % server_obj.id - ) - raise SystemExit + msg = _('Error unshelving server: %s') % server_obj.id + raise exceptions.CommandError(msg) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 032560a2d..683b46cc2 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -2327,7 +2327,9 @@ def test_server_create_with_wait_fails(self, mock_wait_for_status): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises(SystemExit, self.cmd.take_action, parsed_args) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) mock_wait_for_status.assert_called_once_with( self.servers_mock.get, @@ -4569,7 +4571,9 @@ def test_server_delete_wait_fails(self, mock_wait_for_delete): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises(SystemExit, self.cmd.take_action, parsed_args) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) self.servers_mock.delete.assert_called_with(servers[0].id) mock_wait_for_delete.assert_called_once_with( @@ -5984,7 +5988,9 @@ def test_server_migrate_with_wait_fails(self, mock_wait_for_status): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises(SystemExit, self.cmd.take_action, parsed_args) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) self.servers_mock.get.assert_called_with(self.server.id) self.server.migrate.assert_called_with() @@ -6090,9 +6096,10 @@ def test_server_reboot_with_wait_fails( ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises(SystemExit, self.cmd.take_action, parsed_args) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) - self.assertIn('Error rebooting server', mock_log.call_args[0][0]) self.compute_sdk_client.reboot_server.assert_called_once_with( servers[0].id, 'SOFT', @@ -6351,7 +6358,9 @@ def test_rebuild_with_wait_fails(self, mock_wait_for_status): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises(SystemExit, self.cmd.take_action, parsed_args) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) mock_wait_for_status.assert_called_once_with( self.servers_mock.get, @@ -7519,7 +7528,9 @@ def test_server_resize_with_wait_fails(self, mock_wait_for_status): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises(SystemExit, self.cmd.take_action, parsed_args) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) self.servers_mock.get.assert_called_with( self.server.id, From 3057997cae65261b513a2d59aaf6eb4e87afc8c9 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 15 Nov 2023 11:29:06 +0000 Subject: [PATCH 042/403] Remove unnecessary file This is testing things now found in osc-lib. Remove it. Change-Id: Iccbd540fa340c77a957486d16d352d3fe4c3ddb2 Signed-off-by: Stephen Finucane --- .../tests/unit/common/test_parseractions.py | 233 ------------------ 1 file changed, 233 deletions(-) delete mode 100644 openstackclient/tests/unit/common/test_parseractions.py diff --git a/openstackclient/tests/unit/common/test_parseractions.py b/openstackclient/tests/unit/common/test_parseractions.py deleted file mode 100644 index 7a1b17d74..000000000 --- a/openstackclient/tests/unit/common/test_parseractions.py +++ /dev/null @@ -1,233 +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. -# - -# NOTE(dtroyer): This file is deprecated in Jun 2016, remove after 4.x release -# or Jun 2017. - -import argparse - -from osc_lib.cli import parseractions - -from openstackclient.tests.unit import utils - - -class TestKeyValueAction(utils.TestCase): - def setUp(self): - super(TestKeyValueAction, self).setUp() - - self.parser = argparse.ArgumentParser() - - # Set up our typical usage - self.parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - default={'green': '20%', 'format': '#rgb'}, - help='Property to store for this volume ' - '(repeat option to set multiple properties)', - ) - - def test_good_values(self): - results = self.parser.parse_args( - [ - '--property', - 'red=', - '--property', - 'green=100%', - '--property', - 'blue=50%', - ] - ) - - actual = getattr(results, 'property', {}) - # All should pass through unmolested - expect = {'red': '', 'green': '100%', 'blue': '50%', 'format': '#rgb'} - self.assertEqual(expect, actual) - - def test_error_values(self): - self.assertRaises( - argparse.ArgumentTypeError, - self.parser.parse_args, - [ - '--property', - 'red', - ], - ) - - -class TestMultiKeyValueAction(utils.TestCase): - def setUp(self): - super(TestMultiKeyValueAction, self).setUp() - - self.parser = argparse.ArgumentParser() - - # Set up our typical usage - self.parser.add_argument( - '--test', - metavar='req1=xxx,req2=yyy', - action=parseractions.MultiKeyValueAction, - dest='test', - default=None, - required_keys=['req1', 'req2'], - optional_keys=['opt1', 'opt2'], - help='Test', - ) - - def test_good_values(self): - results = self.parser.parse_args( - [ - '--test', - 'req1=aaa,req2=bbb', - '--test', - 'req1=,req2=', - ] - ) - - actual = getattr(results, 'test', []) - expect = [ - {'req1': 'aaa', 'req2': 'bbb'}, - {'req1': '', 'req2': ''}, - ] - self.assertCountEqual(expect, actual) - - def test_empty_required_optional(self): - self.parser.add_argument( - '--test-empty', - metavar='req1=xxx,req2=yyy', - action=parseractions.MultiKeyValueAction, - dest='test_empty', - default=None, - required_keys=[], - optional_keys=[], - help='Test', - ) - - results = self.parser.parse_args( - [ - '--test-empty', - 'req1=aaa,req2=bbb', - '--test-empty', - 'req1=,req2=', - ] - ) - - actual = getattr(results, 'test_empty', []) - expect = [ - {'req1': 'aaa', 'req2': 'bbb'}, - {'req1': '', 'req2': ''}, - ] - self.assertCountEqual(expect, actual) - - def test_error_values_with_comma(self): - self.assertRaises( - argparse.ArgumentTypeError, - self.parser.parse_args, - [ - '--test', - 'mmm,nnn=zzz', - ], - ) - - def test_error_values_without_comma(self): - self.assertRaises( - argparse.ArgumentTypeError, - self.parser.parse_args, - [ - '--test', - 'mmmnnn', - ], - ) - - def test_missing_key(self): - self.assertRaises( - argparse.ArgumentTypeError, - self.parser.parse_args, - [ - '--test', - 'req2=ddd', - ], - ) - - def test_invalid_key(self): - self.assertRaises( - argparse.ArgumentTypeError, - self.parser.parse_args, - [ - '--test', - 'req1=aaa,req2=bbb,aaa=req1', - ], - ) - - def test_required_keys_not_list(self): - self.assertRaises( - TypeError, - self.parser.add_argument, - '--test-required-dict', - metavar='req1=xxx,req2=yyy', - action=parseractions.MultiKeyValueAction, - dest='test_required_dict', - default=None, - required_keys={'aaa': 'bbb'}, - optional_keys=['opt1', 'opt2'], - help='Test', - ) - - def test_optional_keys_not_list(self): - self.assertRaises( - TypeError, - self.parser.add_argument, - '--test-optional-dict', - metavar='req1=xxx,req2=yyy', - action=parseractions.MultiKeyValueAction, - dest='test_optional_dict', - default=None, - required_keys=['req1', 'req2'], - optional_keys={'aaa': 'bbb'}, - help='Test', - ) - - -class TestNonNegativeAction(utils.TestCase): - def setUp(self): - super(TestNonNegativeAction, self).setUp() - - self.parser = argparse.ArgumentParser() - - # Set up our typical usage - self.parser.add_argument( - '--foo', - metavar='', - type=int, - action=parseractions.NonNegativeAction, - ) - - def test_negative_values(self): - self.assertRaises( - argparse.ArgumentTypeError, - self.parser.parse_args, - "--foo -1".split(), - ) - - def test_zero_values(self): - results = self.parser.parse_args('--foo 0'.split()) - - actual = getattr(results, 'foo', None) - self.assertEqual(actual, 0) - - def test_positive_values(self): - results = self.parser.parse_args('--foo 1'.split()) - - actual = getattr(results, 'foo', None) - self.assertEqual(actual, 1) From c08d6e0391c2e435f2e98918116b968104846327 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 15 Nov 2023 10:21:36 +0000 Subject: [PATCH 043/403] parseactions: Use ArgumentError, not ArgumentTypeError If you use the former, you get a pretty error message when there's a failure. If you use the latter, you get an ugly traceback when used with the '--debug' flag. Without this change: $ openstack flavor create ... --property '' foo ... Traceback (most recent call last): File "/tmp/venv/lib/python3.11/site-packages/cliff/app.py", line 402, in run_subcommand parsed_args = cmd_parser.parse_args(sub_argv) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib64/python3.11/argparse.py", line 1862, in parse_args args, argv = self.parse_known_args(args, namespace) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib64/python3.11/argparse.py", line 1895, in parse_known_args namespace, args = self._parse_known_args(args, namespace) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib64/python3.11/argparse.py", line 2107, in _parse_known_args start_index = consume_optional(start_index) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib64/python3.11/argparse.py", line 2047, in consume_optional take_action(action, args, option_string) File "/usr/lib64/python3.11/argparse.py", line 1971, in take_action action(self, namespace, argument_values, option_string) File "/tmp/venv/lib/python3.11/site-packages/osc_lib/cli/parseractions.py", line 45, in __call__ raise argparse.ArgumentTypeError(msg % str(values)) argparse.ArgumentTypeError: Expected 'key=value' type, but got: clean_up CreateFlavor: Expected 'key=value' type, but got: With this change: $ openstack flavor create ... --property '' foo ... usage: openstack flavor create [-h] [-f {json,shell,table,value,yaml}] [-c COLUMN] [--noindent] [--prefix PREFIX] [--max-width ] [--fit-width] [--print-empty] [--id ] [--ram ] [--disk ] [--ephemeral ] [--swap ] [--vcpus ] [--rxtx-factor ] [--public | --private] [--property ] [--project ] [--description ] [--project-domain ] openstack flavor create: error: argument --property: Expected 'key=value' type, but got: clean_up CreateFlavor: Change-Id: I9e78b35ad9d016d7a33655141ec579397c5344c0 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 69 +++++++++++++++---- openstackclient/network/v2/port.py | 4 +- .../tests/unit/compute/v2/test_server.py | 51 +++++++------- .../unit/network/v2/test_network_trunk.py | 14 ++-- .../tests/unit/network/v2/test_port.py | 34 +++++---- .../tests/unit/network/v2/test_subnet_pool.py | 24 +++---- openstackclient/tests/unit/utils.py | 7 +- .../tests/unit/volume/v1/test_volume.py | 10 ++- .../tests/unit/volume/v2/test_volume.py | 10 ++- .../unit/volume/v2/test_volume_snapshot.py | 8 +-- 10 files changed, 133 insertions(+), 98 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index d923b30c6..ea8e8f907 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -841,7 +841,7 @@ def __call__(self, parser, namespace, values, option_string=None): "Invalid argument %s; characters ',' and '=' are not " "allowed" ) - raise argparse.ArgumentTypeError(msg % values) + raise argparse.ArgumentError(self, msg % values) values = '='.join([self.key, values]) else: @@ -869,7 +869,7 @@ def __call__(self, parser, namespace, values, option_string=None): "'net-id=net-uuid,port-id=port-uuid,v4-fixed-ip=ip-addr," "v6-fixed-ip=ip-addr,tag=tag'" ) - raise argparse.ArgumentTypeError(msg % values) + raise argparse.ArgumentError(self, msg % values) info[k] = v @@ -878,7 +878,7 @@ def __call__(self, parser, namespace, values, option_string=None): 'Invalid argument %s; either network or port should be ' 'specified but not both' ) - raise argparse.ArgumentTypeError(msg % values) + raise argparse.ArgumenteError(self, msg % values) getattr(namespace, self.dest).append(info) @@ -896,7 +896,7 @@ def __call__(self, parser, namespace, values, option_string=None): "Invalid argument %s; argument must be of form " "'dev-name=id[:type[:size[:delete-on-terminate]]]'" ) - raise argparse.ArgumentTypeError(msg % values) + raise argparse.ArgumentError(self, msg % values) mapping = { 'device_name': dev_name, @@ -913,7 +913,7 @@ def __call__(self, parser, namespace, values, option_string=None): "Invalid argument %s; 'type' must be one of: volume, " "snapshot, image" ) - raise argparse.ArgumentTypeError(msg % values) + raise argparse.ArgumentError(self, msg % values) mapping['source_type'] = dev_map[1] @@ -966,12 +966,13 @@ def validate_keys(self, keys): "Invalid keys %(invalid_keys)s specified.\n" "Valid keys are: %(valid_keys)s" ) - raise argparse.ArgumentTypeError( + raise argparse.ArgumentError( + self, msg % { 'invalid_keys': ', '.join(invalid_keys), 'valid_keys': ', '.join(valid_keys), - } + }, ) missing_keys = [k for k in self.required_keys if k not in keys] @@ -980,12 +981,13 @@ def validate_keys(self, keys): "Missing required keys %(missing_keys)s.\n" "Required keys are: %(required_keys)s" ) - raise argparse.ArgumentTypeError( + raise argparse.ArgumentError( + self, msg % { 'missing_keys': ', '.join(missing_keys), 'required_keys': ', '.join(self.required_keys), - } + }, ) def __call__(self, parser, namespace, values, option_string=None): @@ -2086,11 +2088,48 @@ def _show_progress(progress): raise exceptions.CommandError(msg) -def percent_type(x): - x = int(x) - if not 0 < x <= 100: - raise argparse.ArgumentTypeError("Must be between 0 and 100") - return x +class PercentAction(argparse.Action): + def __init__( + self, + option_strings, + dest, + nargs=None, + const=None, + default=None, + type=None, + choices=None, + required=False, + help=None, + metavar=None, + ): + if nargs == 0: + raise ValueError( + 'nargs for store actions must be != 0; if you ' + 'have nothing to store, actions such as store ' + 'true or store const may be more appropriate' + ) + + if const is not None: + raise ValueError('const does not make sense for PercentAction') + + super().__init__( + option_strings=option_strings, + dest=dest, + nargs=nargs, + const=const, + default=default, + type=type, + choices=choices, + required=required, + help=help, + metavar=metavar, + ) + + def __call__(self, parser, namespace, values, option_string=None): + x = int(values) + if not 0 < x <= 100: + raise argparse.ArgumentError(self, "Must be between 0 and 100") + setattr(namespace, self.dest, x) class ListServer(command.Lister): @@ -2243,7 +2282,7 @@ def get_parser(self, prog_name): ) parser.add_argument( '--progress', - type=percent_type, + action=PercentAction, default=None, help=_( 'Search by progress value (%%) ' diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 0612b438d..f688aba69 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -124,7 +124,7 @@ def __call__(self, parser, namespace, values, option_string=None): "%(option)s, but encountered JSON parsing error: " "%(error)s" ) % {"option": option_string, "error": e} - raise argparse.ArgumentTypeError(msg) + raise argparse.ArgumentError(self, msg) def _get_attrs(client_manager, parsed_args): @@ -409,7 +409,7 @@ def _validate_port_hints(hints): {'openvswitch': {'other_config': {'tx-steering': 'hash'}}}, ): msg = _("Invalid value to --hints, see --help for valid values.") - raise argparse.ArgumentTypeError(msg) + raise exceptions.CommandError(msg) # When we have multiple hints, we'll need to refactor this to expand aliases diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 683b46cc2..1a577aeea 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -11,8 +11,7 @@ # 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 argparse + import collections import copy import getpass @@ -33,11 +32,11 @@ from openstackclient.tests.unit.compute.v2 import fakes as compute_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 +from openstackclient.tests.unit import utils as test_utils from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes -class TestPowerStateColumn(utils.TestCase): +class TestPowerStateColumn(test_utils.TestCase): def test_human_readable(self): self.assertEqual( 'NOSTATE', server.PowerStateColumn(0x00).human_readable() @@ -1128,7 +1127,7 @@ def test_server_add_volume_with_disable_and_enable_delete_on_termination( ('disable_delete_on_termination', True), ] ex = self.assertRaises( - utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -1373,7 +1372,7 @@ def test_server_create_no_options(self): ] self.assertRaises( - utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -2193,7 +2192,7 @@ def test_server_create_with_invalid_network_options(self): self.new_server.name, ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -2212,7 +2211,7 @@ def test_server_create_with_invalid_network_key(self): self.new_server.name, ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -2231,7 +2230,7 @@ def test_server_create_with_empty_network_key_value(self): self.new_server.name, ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -2250,7 +2249,7 @@ def test_server_create_with_only_network_key(self): self.new_server.name, ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -3302,7 +3301,7 @@ def test_server_create_with_block_device_mapping_invalid_format(self): self.new_server.name, ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -3320,7 +3319,7 @@ def test_server_create_with_block_device_mapping_invalid_format(self): self.new_server.name, ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -3338,7 +3337,7 @@ def test_server_create_with_block_device_mapping_no_uuid(self): self.new_server.name, ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -3759,7 +3758,7 @@ def test_server_create_with_ephemeral_missing_key(self): self.new_server.name, ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -3777,7 +3776,7 @@ def test_server_create_with_ephemeral_invalid_key(self): self.new_server.name, ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -3796,7 +3795,7 @@ def test_server_create_invalid_hint(self): self.new_server.name, ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -3814,7 +3813,7 @@ def test_server_create_invalid_hint(self): self.new_server.name, ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -5187,7 +5186,7 @@ def test_server_list_with_progress_invalid(self): ] self.assertRaises( - utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -5446,7 +5445,7 @@ def test_server_list_with_locked_and_unlocked(self): verifylist = [('locked', True), ('unlocked', True)] ex = self.assertRaises( - utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -6548,7 +6547,7 @@ def test_rebuild_with_keypair_name_and_unset(self): ('key_name', self.server.key_name), ] self.assertRaises( - utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -6652,7 +6651,11 @@ def test_rebuild_with_user_data_and_unset(self): '--no-user-data', ] self.assertRaises( - utils.ParserException, self.check_parser, self.cmd, arglist, None + test_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + None, ) def test_rebuild_with_trusted_image_cert(self): @@ -7847,7 +7850,7 @@ def test_server_set_with_invalid_state(self): ('server', 'foo_vm'), ] self.assertRaises( - utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -8251,7 +8254,7 @@ def test_show_no_options(self): verifylist = [] self.assertRaises( - utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -8915,7 +8918,7 @@ def test_unshelve_with_no_az_and_az_conflict(self): ] ex = self.assertRaises( - utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, diff --git a/openstackclient/tests/unit/network/v2/test_network_trunk.py b/openstackclient/tests/unit/network/v2/test_network_trunk.py index 82c5fec70..9d23a51da 100644 --- a/openstackclient/tests/unit/network/v2/test_network_trunk.py +++ b/openstackclient/tests/unit/network/v2/test_network_trunk.py @@ -9,9 +9,7 @@ # 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 argparse import copy from unittest import mock from unittest.mock import call @@ -23,7 +21,7 @@ from openstackclient.network.v2 import network_trunk from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 from openstackclient.tests.unit.network.v2 import fakes as network_fakes -from openstackclient.tests.unit import utils as tests_utils +from openstackclient.tests.unit import utils as test_utils # Tests for Neutron trunks @@ -104,7 +102,7 @@ def test_create_no_options(self): verifylist = [] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -289,7 +287,7 @@ def test_create_network_trunk_subports_without_required_key_fail(self): ), ] - with testtools.ExpectedException(argparse.ArgumentTypeError): + with testtools.ExpectedException(test_utils.ParserException): self.check_parser(self.cmd, arglist, verifylist) @@ -432,7 +430,7 @@ def test_show_no_options(self): verifylist = [] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -755,7 +753,7 @@ def test_set_network_trunk_subports_without_required_key_fail(self): ), ] - with testtools.ExpectedException(argparse.ArgumentTypeError): + with testtools.ExpectedException(test_utils.ParserException): self.check_parser(self.cmd, arglist, verifylist) self.network_client.add_trunk_subports.assert_not_called() @@ -949,7 +947,7 @@ def test_unset_subport_no_arguments_fail(self): ('trunk', self._trunk['name']), ] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 9f2e53fa6..e93cc8c48 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -9,9 +9,7 @@ # 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 argparse from unittest import mock from unittest.mock import call @@ -23,7 +21,7 @@ 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.network.v2 import fakes as network_fakes -from openstackclient.tests.unit import utils as tests_utils +from openstackclient.tests.unit import utils as test_utils LIST_FIELDS_TO_RETRIEVE = ('id', 'name', 'mac_address', 'fixed_ips', 'status') @@ -257,7 +255,7 @@ def test_create_invalid_json_binding_profile(self): 'test-port', ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -273,7 +271,7 @@ def test_create_invalid_key_value_binding_profile(self): 'test-port', ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -733,7 +731,7 @@ def _test_create_with_tag(self, add_tags=True, add_tags_in_post=True): ) if add_tags: self.network_client.set_tags.assert_called_once_with( - self._port, tests_utils.CompareBySet(['red', 'blue']) + self._port, test_utils.CompareBySet(['red', 'blue']) ) else: self.assertFalse(self.network_client.set_tags.called) @@ -952,7 +950,7 @@ def test_create_hints_invalid_json(self): 'test-port', ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -976,7 +974,7 @@ def test_create_hints_invalid_alias(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( - argparse.ArgumentTypeError, + exceptions.CommandError, self.cmd.take_action, parsed_args, ) @@ -998,7 +996,7 @@ def test_create_hints_invalid_value(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( - argparse.ArgumentTypeError, + exceptions.CommandError, self.cmd.take_action, parsed_args, ) @@ -1905,7 +1903,7 @@ def test_set_port_invalid_json_binding_profile(self): 'test-port', ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -1919,7 +1917,7 @@ def test_set_port_invalid_key_value_binding_profile(self): 'test-port', ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -2285,7 +2283,7 @@ def test_set_port_invalid_data_plane_status_value(self): 'test-port', ] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -2309,7 +2307,7 @@ def _test_set_tags(self, with_tags=True): self.assertFalse(self.network_client.update_port.called) self.network_client.set_tags.assert_called_once_with( - self._port, tests_utils.CompareBySet(expected_args) + self._port, test_utils.CompareBySet(expected_args) ) self.assertIsNone(result) @@ -2358,7 +2356,7 @@ def test_set_hints_invalid_json(self): 'test-port', ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -2377,7 +2375,7 @@ def test_set_hints_invalid_alias(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( - argparse.ArgumentTypeError, + exceptions.CommandError, self.cmd.take_action, parsed_args, ) @@ -2394,7 +2392,7 @@ def test_set_hints_invalid_value(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( - argparse.ArgumentTypeError, + exceptions.CommandError, self.cmd.take_action, parsed_args, ) @@ -2477,7 +2475,7 @@ def test_show_no_options(self): verifylist = [] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -2756,7 +2754,7 @@ def _test_unset_tags(self, with_tags=True): self.assertFalse(self.network_client.update_port.called) self.network_client.set_tags.assert_called_once_with( - self._testport, tests_utils.CompareBySet(expected_args) + self._testport, test_utils.CompareBySet(expected_args) ) self.assertIsNone(result) diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py index 070cfb734..0035fe8cf 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py @@ -9,9 +9,7 @@ # 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 argparse from unittest import mock from unittest.mock import call @@ -21,7 +19,7 @@ from openstackclient.network.v2 import subnet_pool from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 from openstackclient.tests.unit.network.v2 import fakes as network_fakes -from openstackclient.tests.unit import utils as tests_utils +from openstackclient.tests.unit import utils as test_utils class TestSubnetPool(network_fakes.TestNetworkV2): @@ -98,7 +96,7 @@ def test_create_no_options(self): verifylist = [] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -115,7 +113,7 @@ def test_create_no_pool_prefix(self): ('name', self._subnet_pool.name), ] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -196,7 +194,7 @@ def test_create_len_negative(self): ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -368,7 +366,7 @@ def _test_create_with_tag(self, add_tags=True): ) if add_tags: self.network_client.set_tags.assert_called_once_with( - self._subnet_pool, tests_utils.CompareBySet(['red', 'blue']) + self._subnet_pool, test_utils.CompareBySet(['red', 'blue']) ) else: self.assertFalse(self.network_client.set_tags.called) @@ -837,7 +835,7 @@ def test_set_len_negative(self): ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -902,7 +900,7 @@ def test_set_no_address_scope_conflict(self): # Exclusive arguments will conflict here. self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -963,7 +961,7 @@ def test_set_no_default_conflict(self): # Exclusive arguments will conflict here. self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -1029,7 +1027,7 @@ def _test_set_tags(self, with_tags=True): self.assertFalse(self.network_client.update_subnet_pool.called) self.network_client.set_tags.assert_called_once_with( - self._subnet_pool, tests_utils.CompareBySet(expected_args) + self._subnet_pool, test_utils.CompareBySet(expected_args) ) self.assertIsNone(result) @@ -1093,7 +1091,7 @@ def test_show_no_options(self): verifylist = [] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -1149,7 +1147,7 @@ def _test_unset_tags(self, with_tags=True): self.assertFalse(self.network_client.update_subnet_pool.called) self.network_client.set_tags.assert_called_once_with( - self._subnetpool, tests_utils.CompareBySet(expected_args) + self._subnetpool, test_utils.CompareBySet(expected_args) ) self.assertIsNone(result) diff --git a/openstackclient/tests/unit/utils.py b/openstackclient/tests/unit/utils.py index 1691424c6..3c8c746c3 100644 --- a/openstackclient/tests/unit/utils.py +++ b/openstackclient/tests/unit/utils.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import argparse import io import os @@ -78,7 +79,11 @@ def check_parser(self, cmd, args, verify_args): with fixtures.MonkeyPatch('sys.stderr', stderr): try: parsed_args = cmd_parser.parse_args(args) - except SystemExit: + except ( + SystemExit, + argparse.ArgumentTypeError, + argparse.ArgumentError, + ): raise ParserException( "Argument parse failed: %s" % stderr.getvalue() ) diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py index 85610787d..a4fd50368 100644 --- a/openstackclient/tests/unit/volume/v1/test_volume.py +++ b/openstackclient/tests/unit/volume/v1/test_volume.py @@ -11,9 +11,7 @@ # 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 argparse from unittest import mock from unittest.mock import call @@ -23,7 +21,7 @@ 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 tests_utils +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 @@ -665,7 +663,7 @@ def test_volume_create_with_multi_source(self): ] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -973,7 +971,7 @@ def test_volume_list_negative_limit(self): ("limit", -2), ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -1065,7 +1063,7 @@ def test_volume_migrate_without_host(self): ] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index c506023c4..e30a312dd 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -10,9 +10,7 @@ # 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 argparse from unittest import mock from unittest.mock import call @@ -23,7 +21,7 @@ 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 tests_utils +from openstackclient.tests.unit import utils as test_utils from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes from openstackclient.volume.v2 import volume @@ -675,7 +673,7 @@ def test_volume_create_with_multi_source(self): ] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -1347,7 +1345,7 @@ def test_volume_list_negative_limit(self): ("limit", -2), ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -1458,7 +1456,7 @@ def test_volume_migrate_without_host(self): ] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, diff --git a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py index c4762595b..03f2a33fa 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py @@ -10,9 +10,7 @@ # 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 argparse from unittest import mock from osc_lib.cli import format_columns @@ -20,7 +18,7 @@ from osc_lib import utils from openstackclient.tests.unit.identity.v3 import fakes as project_fakes -from openstackclient.tests.unit import utils as tests_utils +from openstackclient.tests.unit import utils as test_utils from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes from openstackclient.volume.v2 import volume_snapshot @@ -117,7 +115,7 @@ def test_snapshot_create_without_name(self): ("volume", self.new_snapshot.volume_id), ] self.assertRaises( - tests_utils.ParserException, + test_utils.ParserException, self.check_parser, self.cmd, arglist, @@ -491,7 +489,7 @@ def test_snapshot_list_negative_limit(self): ("limit", -2), ] self.assertRaises( - argparse.ArgumentTypeError, + test_utils.ParserException, self.check_parser, self.cmd, arglist, From e9d3a7bb136c6533f15f857b0c07c551378a614f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 14 Nov 2023 12:20:57 +0000 Subject: [PATCH 044/403] test: Ignore 'OS_' environment variables Our functional tests are inherently dependent on 'clouds.yaml' files. The presence of 'OS_*' environment variables can cause weird test failures. Simply don't pass them through to our test environment. Change-Id: I7d24cdff5f1f5798118816b12d7398b87a5f5ed4 Signed-off-by: Stephen Finucane --- openstackclient/tests/functional/base.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openstackclient/tests/functional/base.py b/openstackclient/tests/functional/base.py index 18ff65baf..ea135d8af 100644 --- a/openstackclient/tests/functional/base.py +++ b/openstackclient/tests/functional/base.py @@ -30,8 +30,11 @@ def execute(cmd, *, fail_ok=False): cmdlist = shlex.split(cmd) stdout = subprocess.PIPE stderr = subprocess.PIPE + env = { + k: v for k, v in os.environ.copy().items() if not k.startswith('OS_') + } - proc = subprocess.Popen(cmdlist, stdout=stdout, stderr=stderr) + proc = subprocess.Popen(cmdlist, stdout=stdout, stderr=stderr, env=env) result_out, result_err = proc.communicate() result_out = result_out.decode('utf-8') From 5d1afcee68e01d81019a9195f683507371aebfaf Mon Sep 17 00:00:00 2001 From: Mridula Joshi Date: Thu, 31 Aug 2023 05:49:44 +0000 Subject: [PATCH 045/403] Adds command ``image metadef object delete`` Change-Id: Ib94b7ba625ca0679ae4ae841e217ea251baff371 --- .../cli/command-objects/image-metadef.rst | 21 +++++++ doc/source/cli/data/glance.csv | 4 +- openstackclient/image/v2/metadef_objects.py | 56 ++++++++++++++++++- .../unit/image/v2/test_metadef_objects.py | 25 ++++++++- setup.cfg | 1 + 5 files changed, 103 insertions(+), 4 deletions(-) diff --git a/doc/source/cli/command-objects/image-metadef.rst b/doc/source/cli/command-objects/image-metadef.rst index 0a41d9261..46fe43eda 100644 --- a/doc/source/cli/command-objects/image-metadef.rst +++ b/doc/source/cli/command-objects/image-metadef.rst @@ -21,3 +21,24 @@ Image v2 .. autoprogram-cliff:: openstack.image.v2 :command: image metadef resource type list + +.. autoprogram-cliff:: openstack.image.v2 + :command: image metadef object create + +.. autoprogram-cliff:: openstack.image.v2 + :command: image metadef object show + +.. autoprogram-cliff:: openstack.image.v2 + :command: image metadef object list + +.. autoprogram-cliff:: openstack.image.v2 + :command: image metadef object delete + +.. autoprogram-cliff:: openstack.image.v2 + :command: image metadef property create + +.. autoprogram-cliff:: openstack.image.v2 + :command: image metadef property list + +.. autoprogram-cliff:: openstack.image.v2 + :command: image metadef property show diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index e2e2f7e87..4c46bf8ef 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -33,10 +33,10 @@ md-namespace-show,image metadef namespace show,Describe a specific metadata defi md-namespace-tags-delete,,Delete all metadata definitions tags inside a specific namespace. md-namespace-update,,Update an existing metadata definitions namespace. md-object-create,image metadef object create,Create a new metadata definitions object inside a namespace. -md-object-delete,,Delete a specific metadata definitions object inside a namespace. +md-object-show,image metadef object show,Describe a specific metadata definitions object inside a namespace. md-object-list,image metadef object list,List metadata definitions objects inside a specific namespace. +md-object-delete,image metadef object delete,Delete a specific metadata definitions object inside a namespace. md-object-property-show,,Describe a specific metadata definitions property inside an object. -md-object-show,image metadef object show,Describe a specific metadata definitions object inside a namespace. md-object-update,,Update metadata definitions object inside a namespace. md-property-create,image metadef property create,Create a new metadata definitions property inside a namespace. md-property-delete,,Delete a specific metadata definitions property inside a namespace. diff --git a/openstackclient/image/v2/metadef_objects.py b/openstackclient/image/v2/metadef_objects.py index a88c19cd8..014a953d2 100644 --- a/openstackclient/image/v2/metadef_objects.py +++ b/openstackclient/image/v2/metadef_objects.py @@ -15,13 +15,18 @@ """Image V2 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__) + + def _format_object(md_object): fields_to_show = ( 'created_at', @@ -77,7 +82,7 @@ def take_action(self, parsed_args): class ShowMetadefObjects(command.ShowOne): _description = _( - "Describe a specific metadata definitions" "object inside a namespace" + "Describe a specific metadata definitions object inside a namespace" ) def get_parser(self, prog_name): @@ -107,6 +112,55 @@ def take_action(self, parsed_args): return fields, value +class DeleteMetadefObject(command.Command): + _description = _( + "Delete a specific metadata definitions object inside a namespace" + ) + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "namespace_name", + metavar="", + help=_("Namespace (name) for the namespace"), + ) + parser.add_argument( + "object_name", + metavar="", + nargs="+", + help=_("Name of an object."), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + namespace_name = parsed_args.namespace_name + + result = 0 + for i in parsed_args.object_name: + try: + object = image_client.get_metadef_object(i, namespace_name) + image_client.delete_metadef_object(object, namespace_name) + except Exception as e: + result += 1 + LOG.error( + _( + "Failed to delete object with name or " + "ID '%(object)s': %(e)s" + ), + {'object': i, 'e': e}, + ) + + if result > 0: + total = len(parsed_args.namespace_name) + msg = _("%(result)s of %(total)s object failed to delete.") % { + 'result': result, + 'total': total, + } + raise exceptions.CommandError(msg) + + class ListMetadefObjects(command.Lister): _description = _("List metadef objects inside a specific namespace.") diff --git a/openstackclient/tests/unit/image/v2/test_metadef_objects.py b/openstackclient/tests/unit/image/v2/test_metadef_objects.py index 7b8c238b9..d05b0337b 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_objects.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_objects.py @@ -97,13 +97,36 @@ def test_object_show(self): ] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.expected_columns, columns) self.assertEqual(self.expected_data, data) +class TestMetadefObjectDelete(fakes.TestImagev2): + _metadef_namespace = fakes.create_one_metadef_namespace() + _metadef_objects = fakes.create_one_metadef_object() + + def setUp(self): + super().setUp() + + self.image_client.delete_metadef_object.return_value = None + self.cmd = metadef_objects.DeleteMetadefObject(self.app, None) + + def test_object_delete(self): + arglist = [ + self._metadef_namespace.namespace, + self._metadef_objects.name, + ] + + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + class TestMetadefObjectList(fakes.TestImagev2): _metadef_namespace = fakes.create_one_metadef_namespace() _metadef_objects = [fakes.create_one_metadef_object()] diff --git a/setup.cfg b/setup.cfg index a42fafccf..b2197e737 100644 --- a/setup.cfg +++ b/setup.cfg @@ -401,6 +401,7 @@ openstack.image.v2 = 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_property_create = openstackclient.image.v2.metadef_properties:CreateMetadefProperty From 71839eb5fa4bd4962b261556b3bc574b91c5f530 Mon Sep 17 00:00:00 2001 From: Rajesh Tailor Date: Mon, 20 Nov 2023 18:03:42 +0530 Subject: [PATCH 046/403] [codespell] fix typos in doc,tests and help messages Change-Id: I4823782daa1af3872bc22603147e3073152cc777 --- openstackclient/compute/v2/server.py | 2 +- openstackclient/identity/v3/policy.py | 4 +++- openstackclient/network/v2/ndp_proxy.py | 10 +++++++--- openstackclient/tests/functional/base.py | 2 +- openstackclient/tests/unit/image/v1/fakes.py | 2 +- openstackclient/volume/v2/volume_type.py | 6 +++--- 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 4854e91f8..b3dbddb9e 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2751,7 +2751,7 @@ def take_action(self, parsed_args): try: # some deployments can have *loads* of images so we only # want to list the ones we care about. It would be better - # to only retrun the *fields* we care about (name) but + # to only return the *fields* we care about (name) but # glance doesn't support that # NOTE(stephenfin): This could result in super long URLs # but it seems unlikely to cause issues. Apache supports diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py index 2504a467f..c6642768b 100644 --- a/openstackclient/identity/v3/policy.py +++ b/openstackclient/identity/v3/policy.py @@ -92,7 +92,9 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.policy) - msg = _("%(result)s of %(total)s policys failed " "to delete.") % { + msg = _( + "%(result)s of %(total)s policies failed " "to delete." + ) % { 'result': result, 'total': total, } diff --git a/openstackclient/network/v2/ndp_proxy.py b/openstackclient/network/v2/ndp_proxy.py index 8699d2fbd..3b6caab66 100644 --- a/openstackclient/network/v2/ndp_proxy.py +++ b/openstackclient/network/v2/ndp_proxy.py @@ -129,7 +129,7 @@ def take_action(self, parsed_args): if result > 0: total = len(parsed_args.ndp_proxy) msg = _( - "%(result)s of %(total)s NDP Proxy failed " "to delete." + "%(result)s of %(total)s NDP proxies failed " "to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) @@ -142,12 +142,16 @@ def get_parser(self, prog_name): parser.add_argument( '--router', metavar='', - help=_("List only NDP proxies belong to this router (name or ID)"), + help=_( + "List only NDP proxies belonging to this router (name or ID)" + ), ) parser.add_argument( '--port', metavar='', - help=_("List only NDP proxies assocate to this port (name or ID)"), + help=_( + "List only NDP proxies associated to this port (name or ID)" + ), ) parser.add_argument( '--ip-address', diff --git a/openstackclient/tests/functional/base.py b/openstackclient/tests/functional/base.py index 86fca8fe8..adf42b35b 100644 --- a/openstackclient/tests/functional/base.py +++ b/openstackclient/tests/functional/base.py @@ -65,7 +65,7 @@ def openstack( :param cloud: The cloud to execute against. This can be a string, empty string, or None. A string results in '--os-auth-type $cloud', an empty string results in the '--os-auth-type' option being - omitted, and None resuts in '--os-auth-type none' for legacy + omitted, and None results in '--os-auth-type none' for legacy reasons. :param fail_ok: If failure is permitted. If False (default), a command failure will result in `~tempest.lib.exceptions.CommandFailed` diff --git a/openstackclient/tests/unit/image/v1/fakes.py b/openstackclient/tests/unit/image/v1/fakes.py index 67ebc000f..44845fb82 100644 --- a/openstackclient/tests/unit/image/v1/fakes.py +++ b/openstackclient/tests/unit/image/v1/fakes.py @@ -46,7 +46,7 @@ def create_one_image(attrs=None): """Create a fake image. :param Dictionary attrs: - A dictionary with all attrbutes of image + A dictionary with all attributes of image :return: A FakeResource object with id, name, owner, protected, visibility and tags attrs diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index f865c8247..00821178c 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -183,7 +183,7 @@ def get_parser(self, prog_name): help=_( "Set an availability zone for this volume type " "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 - "(repeat option to set multiple availabilty zones)" + "(repeat option to set multiple availability zones)" ), ) parser.add_argument( @@ -459,7 +459,7 @@ def get_parser(self, prog_name): help=_( "List only volume types with this availability configured " "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 - "(repeat option to filter on multiple availabilty zones)" + "(repeat option to filter on multiple availability zones)" ), ) return parser @@ -628,7 +628,7 @@ def get_parser(self, prog_name): help=_( "Set an availability zone for this volume type " "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 - "(repeat option to set multiple availabilty zones)" + "(repeat option to set multiple availability zones)" ), ) parser.add_argument( From e44d8017eccf8112e1abd8b382517157208f7eb5 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 15 Nov 2023 17:00:35 +0000 Subject: [PATCH 047/403] tests: Check for DHCP agents first in DHCP test If there are no agents with type=dhcp, the neutron-dhcp-agent service is likely not enabled on the deployment. Change-Id: I1253f35e71cf996c559f2a2d8d1d8cde6b41a519 Signed-off-by: Stephen Finucane --- .../functional/network/v2/test_network.py | 29 ++++++++++++------- .../network/v2/test_network_agent.py | 25 +++++++++------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/openstackclient/tests/functional/network/v2/test_network.py b/openstackclient/tests/functional/network/v2/test_network.py index 5f1e71392..fde34c278 100644 --- a/openstackclient/tests/functional/network/v2/test_network.py +++ b/openstackclient/tests/functional/network/v2/test_network.py @@ -323,11 +323,25 @@ def test_network_list(self): def test_network_dhcp_agent(self): if not self.haz_network: self.skipTest("No Network service present") + + if not self.is_extension_enabled("agent"): + self.skipTest("No dhcp_agent_scheduler extension present") + if not self.is_extension_enabled("dhcp_agent_scheduler"): self.skipTest("No dhcp_agent_scheduler extension present") + # Get DHCP Agent ID + cmd_output = self.openstack( + 'network agent list --agent-type dhcp', + parse_output=True, + ) + if not cmd_output: + self.skipTest("No agents with type=dhcp available") + + agent_id = cmd_output[0]['ID'] + name1 = uuid.uuid4().hex - cmd_output1 = self.openstack( + cmd_output = self.openstack( 'network create --description aaaa %s' % name1, parse_output=True, ) @@ -335,14 +349,7 @@ def test_network_dhcp_agent(self): self.addCleanup(self.openstack, 'network delete %s' % name1) # Get network ID - network_id = cmd_output1['id'] - - # Get DHCP Agent ID - cmd_output2 = self.openstack( - 'network agent list --agent-type dhcp', - parse_output=True, - ) - agent_id = cmd_output2[0]['ID'] + network_id = cmd_output['id'] # Add Agent to Network self.openstack( @@ -350,7 +357,7 @@ def test_network_dhcp_agent(self): ) # Test network list --agent - cmd_output3 = self.openstack( + cmd_output = self.openstack( 'network list --agent %s' % agent_id, parse_output=True, ) @@ -363,7 +370,7 @@ def test_network_dhcp_agent(self): ) # Assert - col_name = [x["ID"] for x in cmd_output3] + col_name = [x["ID"] for x in cmd_output] self.assertIn(network_id, col_name) def test_network_set(self): diff --git a/openstackclient/tests/functional/network/v2/test_network_agent.py b/openstackclient/tests/functional/network/v2/test_network_agent.py index 3cfb26ac3..38697905f 100644 --- a/openstackclient/tests/functional/network/v2/test_network_agent.py +++ b/openstackclient/tests/functional/network/v2/test_network_agent.py @@ -88,8 +88,18 @@ def test_network_dhcp_agent_list(self): if not self.is_extension_enabled("dhcp_agent_scheduler"): self.skipTest("No dhcp_agent_scheduler extension present") + # Get DHCP Agent ID + cmd_output = self.openstack( + 'network agent list --agent-type dhcp', + parse_output=True, + ) + if not cmd_output: + self.skipTest("No agents with type=dhcp available") + + agent_id = cmd_output[0]['ID'] + name1 = uuid.uuid4().hex - cmd_output1 = self.openstack( + cmd_output = self.openstack( 'network create --description aaaa %s' % name1, parse_output=True, ) @@ -97,14 +107,7 @@ def test_network_dhcp_agent_list(self): self.addCleanup(self.openstack, 'network delete %s' % name1) # Get network ID - network_id = cmd_output1['id'] - - # Get DHCP Agent ID - cmd_output2 = self.openstack( - 'network agent list --agent-type dhcp', - parse_output=True, - ) - agent_id = cmd_output2[0]['ID'] + network_id = cmd_output['id'] # Add Agent to Network self.openstack( @@ -112,7 +115,7 @@ def test_network_dhcp_agent_list(self): ) # Test network agent list --network - cmd_output3 = self.openstack( + cmd_output = self.openstack( 'network agent list --network %s' % network_id, parse_output=True, ) @@ -125,7 +128,7 @@ def test_network_dhcp_agent_list(self): ) # Assert - col_name = [x["ID"] for x in cmd_output3] + col_name = [x["ID"] for x in cmd_output] self.assertIn(agent_id, col_name) def test_network_agent_list_routers(self): From 6d3490ed7c103b1a3adbad23f63dbba2f17644d8 Mon Sep 17 00:00:00 2001 From: ArtofBugs Date: Tue, 21 Nov 2023 09:50:51 -0800 Subject: [PATCH 048/403] tests: Handle missing extensions in network tests Some tests still assume extensions are enabled; those tests are now skipped if the extension is not enabled. Change I25b8811fe09f2a4a9fc20ca5459f5a404b88a337 addressed some but not all of these. Change-Id: If36550650f143a7efe4190e60961c51a8cd20fb3 --- .../functional/network/v2/test_network_agent.py | 16 ++++++++++++++-- .../functional/network/v2/test_network_meter.py | 6 ++++++ .../network/v2/test_network_meter_rule.py | 11 +++++------ .../network/v2/test_network_qos_policy.py | 6 ++++++ .../network/v2/test_network_qos_rule_type.py | 6 ++++++ 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/openstackclient/tests/functional/network/v2/test_network_agent.py b/openstackclient/tests/functional/network/v2/test_network_agent.py index 38697905f..c9a2ee121 100644 --- a/openstackclient/tests/functional/network/v2/test_network_agent.py +++ b/openstackclient/tests/functional/network/v2/test_network_agent.py @@ -15,9 +15,15 @@ from openstackclient.tests.functional.network.v2 import common -class NetworkAgentTests(common.NetworkTests): +class TestAgent(common.NetworkTests): """Functional tests for network agent""" + def setUp(self): + super().setUp() + + if not self.is_extension_enabled("agent"): + self.skipTest("No agent extension present") + def test_network_agent_list_show_set(self): """Test network agent list, set, show commands @@ -79,9 +85,15 @@ def test_network_agent_list_show_set(self): ) -class NetworkAgentListTests(common.NetworkTests): +class TestAgentList(common.NetworkTests): """Functional test for network agent""" + def setUp(self): + super().setUp() + + if not self.is_extension_enabled("agent"): + self.skipTest("No agent extension present") + def test_network_dhcp_agent_list(self): """Test network agent list""" diff --git a/openstackclient/tests/functional/network/v2/test_network_meter.py b/openstackclient/tests/functional/network/v2/test_network_meter.py index 82e94c43f..edd2c4415 100644 --- a/openstackclient/tests/functional/network/v2/test_network_meter.py +++ b/openstackclient/tests/functional/network/v2/test_network_meter.py @@ -21,6 +21,12 @@ class TestMeter(common.NetworkTests): """Functional tests for network meter""" + def setUp(self): + super().setUp() + + if not self.is_extension_enabled("metering"): + self.skipTest("No metering extension present") + # NOTE(dtroyer): Do not normalize the setup and teardown of the resource # creation and deletion. Little is gained when each test # has its own needs and there are collisions when running 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 e0fdcd3f3..8258d5831 100644 --- a/openstackclient/tests/functional/network/v2/test_network_meter_rule.py +++ b/openstackclient/tests/functional/network/v2/test_network_meter_rule.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import unittest import uuid from openstackclient.tests.functional.network.v2 import common @@ -27,6 +28,10 @@ class TestMeterRule(common.NetworkTests): @classmethod def setUpClass(cls): super().setUpClass() + + if not cls.is_extension_enabled("metering"): + raise unittest.SkipTest("No metering extension present") + if cls.haz_network: cls.METER_NAME = uuid.uuid4().hex @@ -47,12 +52,6 @@ def tearDownClass(cls): finally: super().tearDownClass() - def setUp(self): - super().setUp() - - if not self.is_extension_enabled("metering"): - self.skipTest("No metering extension present") - def test_meter_rule_delete(self): """test create, delete""" json_output = self.openstack( diff --git a/openstackclient/tests/functional/network/v2/test_network_qos_policy.py b/openstackclient/tests/functional/network/v2/test_network_qos_policy.py index 2662a9975..3f59ce35f 100644 --- a/openstackclient/tests/functional/network/v2/test_network_qos_policy.py +++ b/openstackclient/tests/functional/network/v2/test_network_qos_policy.py @@ -21,6 +21,12 @@ class NetworkQosPolicyTests(common.NetworkTests): """Functional tests for QoS policy""" + def setUp(self): + super().setUp() + + if not self.is_extension_enabled("qos"): + self.skipTest("No qos extension present") + def test_qos_rule_create_delete(self): # This is to check the output of qos policy delete policy_name = uuid.uuid4().hex 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 a595043a3..77ce35716 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 @@ -29,6 +29,12 @@ class NetworkQosRuleTypeTests(common.NetworkTests): 'minimum_packet_rate', ] + def setUp(self): + super().setUp() + + if not self.is_extension_enabled("qos"): + self.skipTest("No qos extension present") + def test_qos_rule_type_list(self): cmd_output = self.openstack( 'network qos rule type list -f json', From ce0765facba9c25fcc1fca662d23cc9c28345559 Mon Sep 17 00:00:00 2001 From: Florian Streibelt Date: Thu, 23 Nov 2023 16:04:47 +0100 Subject: [PATCH 049/403] Fix clearing of dns_domain and description on a network by setting to empty strings After setting a default dns_domain on a network openstack net set --dns-domain 'example.com.' the setting could not be reverted back to an empty string using openstack net set --dns-domain '' and the call also does not emit any error. The same is true for the description of a network. Reason was using the parsed argument directly as a condition instead of comparing against None -- dropping the empty string as valid value. The name parameter already accepted an empty string. This change also adds a testcase for dns_domain, description and the network name parameter, checking if the empty string is forwarded. Change-Id: Ia7b9738205002b028c19e4f397411c86469cba1a --- openstackclient/network/v2/network.py | 4 +-- .../tests/unit/network/v2/test_network.py | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 1901e2195..973878b9e 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -107,7 +107,7 @@ def _get_attrs_network(client_manager, parsed_args): attrs['availability_zone_hints'] = parsed_args.availability_zone_hints # set description - if parsed_args.description: + if parsed_args.description is not None: attrs['description'] = parsed_args.description # set mtu @@ -139,7 +139,7 @@ def _get_attrs_network(client_manager, parsed_args): if 'no_qos_policy' in parsed_args and parsed_args.no_qos_policy: attrs['qos_policy_id'] = None # Update DNS network options - if parsed_args.dns_domain: + if parsed_args.dns_domain is not None: attrs['dns_domain'] = parsed_args.dns_domain return attrs diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index f2fd45baa..2dbb0f822 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -1063,6 +1063,39 @@ def test_set_that(self): ) self.assertIsNone(result) + def test_set_to_empty(self): + # Test if empty strings are accepted to clear any of the fields, + # so once they are set to a value its possible to clear them again. + + arglist = [ + self._network.name, + '--name', + '', + '--description', + '', + '--dns-domain', + '', + ] + verifylist = [ + ('network', self._network.name), + ('description', ''), + ('name', ''), + ('dns_domain', ''), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + attrs = { + 'name': '', + 'description': '', + 'dns_domain': '', + } + self.network_client.update_network.assert_called_once_with( + self._network, **attrs + ) + self.assertIsNone(result) + def test_set_nothing(self): arglist = [ self._network.name, From c657047d7e57fc651c33ee11021bb7b94f148551 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Wed, 6 Dec 2023 17:39:03 +0530 Subject: [PATCH 050/403] volume list: Don't call nova if no volume is attached Currently 'openstack volume list' calls nova to resolve server UUIDs to server names. This is not required if: 1. no volume is attached to an instance 2. no volume exists in deployment This patch fixes this by checking volume statuses and, if any volume has status 'in-use', we will call nova to resolve server names. Note that we don't check for 'reserved', 'attaching', 'detaching' states since those are transition states and doesn't guarantee that the volume is actually attached to the instance. Change-Id: Ic4d89db69244d3fba44d4b69c79b3e7632ee3d53 --- openstackclient/volume/v2/volume.py | 35 ++++++++++++++++++----------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 4127e23d4..a7bdb782f 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -490,19 +490,6 @@ def take_action(self, parsed_args): column_headers = copy.deepcopy(columns) column_headers[4] = 'Attached to' - # Cache the server list - server_cache = {} - try: - compute_client = self.app.client_manager.compute - for s in compute_client.servers.list(): - server_cache[s.id] = s - except Exception: - # Just forget it if there's any trouble - pass - AttachmentsColumnWithCache = functools.partial( - AttachmentsColumn, server_cache=server_cache - ) - project_id = None if parsed_args.project: project_id = identity_common.find_project( @@ -533,6 +520,28 @@ def take_action(self, parsed_args): marker=parsed_args.marker, limit=parsed_args.limit, ) + + do_server_list = False + + for vol in data: + if vol.status == 'in-use': + do_server_list = True + break + + # Cache the server list + server_cache = {} + if do_server_list: + try: + compute_client = self.app.client_manager.compute + for s in compute_client.servers.list(): + server_cache[s.id] = s + except Exception: + # Just forget it if there's any trouble + pass + AttachmentsColumnWithCache = functools.partial( + AttachmentsColumn, server_cache=server_cache + ) + column_headers = utils.backward_compat_col_lister( column_headers, parsed_args.columns, {'Display Name': 'Name'} ) From f3207bdf3febe586e8c64b470a4964d0cb0b7b13 Mon Sep 17 00:00:00 2001 From: Pavlo Shchelokovskyy Date: Thu, 7 Dec 2023 16:24:34 +0000 Subject: [PATCH 051/403] Fix availability zone list command there are two problems currently: - SDK's `availability_zones()` returns a generator that raises errors only when actually accessing its items - the error raised is the sdk exception, not nova one, and thus is not being handled correctly As a result, currently nova AZs can not be listed by non-admins. Story: 2010989 Task: 49220 Change-Id: Ia299faea85857d3fc3a9b539800f3483f84ccbc0 --- openstackclient/common/availability_zone.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openstackclient/common/availability_zone.py b/openstackclient/common/availability_zone.py index af6980f1d..5d62ecd12 100644 --- a/openstackclient/common/availability_zone.py +++ b/openstackclient/common/availability_zone.py @@ -16,7 +16,7 @@ import copy import logging -from novaclient import exceptions as nova_exceptions +from openstack import exceptions as sdk_exceptions from osc_lib.command import command from osc_lib import utils @@ -119,8 +119,8 @@ def get_parser(self, prog_name): def _get_compute_availability_zones(self, parsed_args): compute_client = self.app.client_manager.sdk_connection.compute try: - data = compute_client.availability_zones(details=True) - except nova_exceptions.Forbidden: # policy doesn't allow + data = list(compute_client.availability_zones(details=True)) + except sdk_exceptions.ForbiddenException: # policy doesn't allow try: data = compute_client.availability_zones(details=False) except Exception: @@ -135,7 +135,7 @@ def _get_volume_availability_zones(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume data = [] try: - data = volume_client.availability_zones() + data = list(volume_client.availability_zones()) except Exception as e: LOG.debug('Volume availability zone exception: %s', e) if parsed_args.volume: From ea254061c8264eb52a71bfd88230d00a0da429d4 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Wed, 13 Dec 2023 15:07:19 +0000 Subject: [PATCH 052/403] Doc: Fix volume snapshot commands The snapshot commands use the "volume " prefix which was missing from the doc. This patch adds it. Change-Id: I2acf28eff78fb8419a4c4f00395355a6ca44a576 --- doc/source/cli/data/cinder.csv | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/source/cli/data/cinder.csv b/doc/source/cli/data/cinder.csv index 84ea409e2..1b199400b 100644 --- a/doc/source/cli/data/cinder.csv +++ b/doc/source/cli/data/cinder.csv @@ -108,17 +108,17 @@ service-list,volume service list,Lists all services. Filter by host and service service-set-log,block storage log level set,(Supported by API versions 3.32 - 3.latest) set-bootable,volume set --bootable / --not-bootable,Update bootable status of a volume. show,volume show,Shows volume details. -snapshot-create,snapshot create,Creates a snapshot. -snapshot-delete,snapshot delete,Remove one or more snapshots. -snapshot-list,snapshot list,Lists all snapshots. +snapshot-create,volume snapshot create,Creates a snapshot. +snapshot-delete,volume snapshot delete,Remove one or more snapshots. +snapshot-list,volume snapshot list,Lists all snapshots. snapshot-manage,volume snapshot create --remote-source ,Manage an existing snapshot. snapshot-manageable-list,block storage snapshot manageable list,Lists all manageable snapshots. (Supported by API versions 3.8 - 3.latest) -snapshot-metadata,snapshot set --property k=v / snapshot unset --property k,Sets or deletes snapshot metadata. -snapshot-metadata-show,snapshot show,Shows snapshot metadata. -snapshot-metadata-update-all,snapshot set --property k=v,Updates snapshot metadata. -snapshot-rename,snapshot set --name,Renames a snapshot. -snapshot-reset-state,snapshot set --state,Explicitly updates the snapshot state. -snapshot-show,snapshot show,Shows snapshot details. +snapshot-metadata,volume snapshot set --property k=v / snapshot unset --property k,Sets or deletes snapshot metadata. +snapshot-metadata-show,volume snapshot show,Shows snapshot metadata. +snapshot-metadata-update-all,volume snapshot set --property k=v,Updates snapshot metadata. +snapshot-rename,volume snapshot set --name,Renames a snapshot. +snapshot-reset-state,volume snapshot set --state,Explicitly updates the snapshot state. +snapshot-show,volume snapshot show,Shows snapshot details. snapshot-unmanage,volume snapshot delete --remote,Stop managing a snapshot. summary,volume summary,Get volumes summary. (Supported by API versions 3.12 - 3.latest) thaw-host,volume host set --enable,Thaw and enable the specified cinder-volume host. From 1b6b639c9fa0f81f365f4c5baad575dc2eaca200 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 14 Dec 2023 12:30:03 +0000 Subject: [PATCH 053/403] network: Clarify purpose of default sg rules These only apply to newly created default security groups i.e. when you create a new project. They do not apply to existing default security groups. Change-Id: Ie01bf47dd8a0392354d17d984b41c1fad504e659 Signed-off-by: Stephen Finucane --- .../network/v2/default_security_group_rule.py | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/openstackclient/network/v2/default_security_group_rule.py b/openstackclient/network/v2/default_security_group_rule.py index b8232a252..86a2ddc87 100644 --- a/openstackclient/network/v2/default_security_group_rule.py +++ b/openstackclient/network/v2/default_security_group_rule.py @@ -38,7 +38,12 @@ def _get_columns(item): class CreateDefaultSecurityGroupRule( command.ShowOne, common.NeutronCommandWithExtraArgs ): - _description = _("Create a new default security group rule") + """Add a new security group rule to the default security group template. + + These rules will be applied to the default security groups created for any + new project. They will not be applied to any existing default security + groups. + """ def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -225,7 +230,12 @@ def take_action(self, parsed_args): class DeleteDefaultSecurityGroupRule(command.Command): - _description = _("Delete default security group rule(s)") + """Remove security group rule(s) from the default security group template. + + These rules will not longer be applied to the default security groups + created for any new project. They will not be removed from any existing + default security groups. + """ def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -265,7 +275,12 @@ def take_action(self, parsed_args): class ListDefaultSecurityGroupRule(command.Lister): - _description = _("List default security group rules") + """List security group rules used for new default security groups. + + This shows the rules that will be added to any new default security groups + created. These rules may differ for the rules present on existing default + security groups. + """ def _format_network_security_group_rule(self, rule): """Transform the SDK DefaultSecurityGroupRule object to a dict @@ -373,7 +388,11 @@ def take_action(self, parsed_args): class ShowDefaultSecurityGroupRule(command.ShowOne): - _description = _("Display default security group rule details") + """Show a security group rule used for new default security groups. + + This shows a rule that will be added to any new default security groups + created. This rule may not be present on existing default security groups. + """ def get_parser(self, prog_name): parser = super().get_parser(prog_name) From be3d438ed206f45c9d5b058696d6633a47ad112f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 14 Dec 2023 12:31:26 +0000 Subject: [PATCH 054/403] trivial: Place positional opts last Change-Id: I5479e71223c8c224e0bba387348e740c997cc7bf Signed-off-by: Stephen Finucane --- openstackclient/network/v2/port.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index f688aba69..af8c936df 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -1163,10 +1163,6 @@ def get_parser(self, prog_name): "or ID) (repeat option to unset multiple security groups)" ), ) - - parser.add_argument( - 'port', metavar="", help=_("Port to modify (name or ID)") - ) parser.add_argument( '--allowed-address', metavar='ip-address=[,mac-address=]', @@ -1209,8 +1205,12 @@ def get_parser(self, prog_name): default=False, help=_("Clear hints for the port."), ) - _tag.add_tag_option_to_parser_for_unset(parser, _('port')) + parser.add_argument( + 'port', + metavar="", + help=_("Port to modify (name or ID)"), + ) return parser From 93b2e66d2dd245d36774a2725d6786ef325ae35b Mon Sep 17 00:00:00 2001 From: Mridula Joshi Date: Wed, 20 Dec 2023 05:03:40 +0000 Subject: [PATCH 055/403] Adding CLI command for ``glance member-get`` This patch adds a command ``image member get`` which displays a particular member associated to the image. Change-Id: I48d3151f8e204e1eb5cfff67ce1e333d1cfb9322 --- doc/source/cli/data/glance.csv | 2 +- openstackclient/image/v2/image.py | 37 ++++++++++++ .../tests/unit/image/v2/test_image.py | 60 +++++++++++++++++++ ...add-image-member-get-25e913ef2b861bf3.yaml | 6 ++ setup.cfg | 1 + 5 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-image-member-get-25e913ef2b861bf3.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index e554f09fe..7bf9c528e 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -54,7 +54,7 @@ md-tag-show,,Describe a specific metadata definitions tag inside a namespace. md-tag-update,,Rename a metadata definitions tag inside a namespace. member-create,image add project,Create member for a given image. member-delete,image remove project,Delete image member. -member-get,,Show details of an image member +member-get,image member get,Show details of an image member member-list,image member list,Describe sharing permissions by image. member-update,image set --accept --reject --status,Update the status of a member for a given image. stores-delete,image delete --store,Delete image from specific store. diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 725a4d3ef..2ae701060 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -992,6 +992,43 @@ def take_action(self, parsed_args): image_client.remove_member(member=project_id, image=image.id) +class ShowProjectImage(command.ShowOne): + _description = _("Show a particular project associated with image") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "image", + metavar="", + help=_("Image (name or ID)"), + ) + parser.add_argument( + "member", + metavar="", + help=_("Project to show (name or ID)"), + ) + identity_common.add_project_domain_option_to_parser(parser) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + image = image_client.find_image( + parsed_args.image, + ignore_missing=False, + ) + + obj = image_client.get_member( + image=image.id, + member=parsed_args.member, + ) + + display_columns, columns = _get_member_columns(obj) + data = utils.get_item_properties(obj, columns, formatters={}) + + return (display_columns, data) + + class SaveImage(command.Command): _description = _("Save an image locally") diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index 451bb89c0..2a2a654a1 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -1093,6 +1093,66 @@ def test_remove_project_image_with_options(self): self.assertIsNone(result) +class TestShowProjectImage(TestImage): + _image = image_fakes.create_one_image() + new_member = image_fakes.create_one_image_member( + attrs={'image_id': _image.id, 'member_id': 'member1'} + ) + + columns = ( + 'created_at', + 'image_id', + 'member_id', + 'schema', + 'status', + 'updated_at', + ) + + datalist = ( + new_member.created_at, + _image.id, + new_member.member_id, + new_member.schema, + new_member.status, + new_member.updated_at, + ) + + def setUp(self): + super().setUp() + + # This is the return value for utils.find_resource() + self.image_client.find_image.return_value = self._image + + self.image_client.get_member.return_value = self.new_member + # Get the command object to test + self.cmd = _image.ShowProjectImage(self.app, None) + + def test_show_project_image(self): + arglist = [ + self._image.id, + 'member1', + ] + verifylist = [ + ('image', self._image.id), + ('member', 'member1'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.image_client.find_image.assert_called_with( + self._image.id, ignore_missing=False + ) + + self.image_client.get_member.assert_called_with( + member='member1', + image=self._image.id, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + + class TestImageSet(TestImage): project = identity_fakes.FakeProject.create_one_project() domain = identity_fakes.FakeDomain.create_one_domain() diff --git a/releasenotes/notes/add-image-member-get-25e913ef2b861bf3.yaml b/releasenotes/notes/add-image-member-get-25e913ef2b861bf3.yaml new file mode 100644 index 000000000..9293f3c6e --- /dev/null +++ b/releasenotes/notes/add-image-member-get-25e913ef2b861bf3.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add ``image member get`` command which accepts an + image_id and member_id and displays the detail of + the particular meber associated to the image. diff --git a/setup.cfg b/setup.cfg index ceb94adc5..f11f52d6c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -379,6 +379,7 @@ openstack.image.v2 = 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 From e7bc3d9b82283690988f6707be716c94846a862c Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Thu, 10 Aug 2023 17:59:28 -0400 Subject: [PATCH 056/403] Fix --use-prefix-delegation subnet create argument The --use-prefix-delegation argument when creating a subnet should not take an argument of True/False, it should simply trigger the code to set subnetpool_id value to 'prefix_delegation'. Change action to correct this. Added unit test to cover missing checks. Related-bug: #2028159 Change-Id: Ib7ee80100327b8611d4a354c7f4eb0e696c953da --- openstackclient/network/v2/subnet.py | 1 + .../tests/unit/network/v2/test_subnet.py | 92 +++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 32b2493a0..950ad2578 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -306,6 +306,7 @@ def get_parser(self, prog_name): ) subnet_pool_group.add_argument( '--use-prefix-delegation', + action='store_true', help=_( "Use 'prefix-delegation' if IP is IPv6 format " "and IP would be delegated externally" diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py index fb2d510fa..649ba2af7 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet.py +++ b/openstackclient/tests/unit/network/v2/test_subnet.py @@ -108,6 +108,26 @@ def _init_subnet_variables(self): } ) + # An IPv6 subnet to be created with Prefix Delegation options specified + self._subnet_ipv6_pd = network_fakes.FakeSubnet.create_one_subnet( + attrs={ + 'project_id': self.project.id, + 'cidr': '::/64', + 'enable_dhcp': True, + 'allocation_pools': [ + { + 'start': '::1', + 'end': '::ffff:ffff:ffff:ffff', + }, + ], + 'ip_version': 6, + 'gateway_ip': '::', + 'ipv6_address_mode': 'slaac', + 'ipv6_ra_mode': 'slaac', + 'subnetpool_id': 'prefix_delegation', + } + ) + # The network to be returned from find_network self._network = network_fakes.create_one_network( attrs={ @@ -210,6 +230,29 @@ def _init_subnet_variables(self): format_columns.ListColumn(self._subnet_ipv6.tags), ) + self.data_ipv6_pd = ( + subnet_v2.AllocationPoolsColumn( + self._subnet_ipv6_pd.allocation_pools + ), + self._subnet_ipv6_pd.cidr, + self._subnet_ipv6_pd.description, + format_columns.ListColumn(self._subnet_ipv6_pd.dns_nameservers), + self._subnet_ipv6_pd.enable_dhcp, + self._subnet_ipv6_pd.gateway_ip, + subnet_v2.HostRoutesColumn(self._subnet_ipv6_pd.host_routes), + self._subnet_ipv6_pd.id, + self._subnet_ipv6_pd.ip_version, + self._subnet_ipv6_pd.ipv6_address_mode, + self._subnet_ipv6_pd.ipv6_ra_mode, + self._subnet_ipv6_pd.name, + self._subnet_ipv6_pd.network_id, + self._subnet_ipv6_pd.project_id, + self._subnet_ipv6_pd.segment_id, + format_columns.ListColumn(self._subnet_ipv6_pd.service_types), + self._subnet_ipv6_pd.subnetpool_id, + format_columns.ListColumn(self._subnet_ipv6_pd.tags), + ) + def setUp(self): self._init_subnet_variables() super(TestCreateSubnet, self).setUp() @@ -455,6 +498,55 @@ def test_create_options_subnet_range_ipv6(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data_ipv6, data) + def test_create_options_subnet_ipv6_pd(self): + # Mock SDK calls for this test. + self.network_client.create_subnet.return_value = self._subnet_ipv6_pd + self._network.id = self._subnet_ipv6_pd.network_id + + arglist = [ + self._subnet_ipv6_pd.name, + "--network", + self._subnet_ipv6_pd.network_id, + "--ip-version", + str(self._subnet_ipv6_pd.ip_version), + "--ipv6-ra-mode", + self._subnet_ipv6_pd.ipv6_ra_mode, + "--ipv6-address-mode", + self._subnet_ipv6_pd.ipv6_address_mode, + "--dhcp", + "--use-prefix-delegation", + ] + + verifylist = [ + ('name', self._subnet_ipv6_pd.name), + ('network', self._subnet_ipv6_pd.network_id), + ('ip_version', self._subnet_ipv6_pd.ip_version), + ('ipv6_ra_mode', self._subnet_ipv6_pd.ipv6_ra_mode), + ('ipv6_address_mode', self._subnet_ipv6_pd.ipv6_address_mode), + ('dhcp', self._subnet_ipv6_pd.enable_dhcp), + ('use_prefix_delegation', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + # Calling with --use-prefix-delegation will set the subnetpool_id + # to 'prefix_delegation' + self.network_client.create_subnet.assert_called_once_with( + **{ + 'enable_dhcp': self._subnet_ipv6_pd.enable_dhcp, + 'ip_version': self._subnet_ipv6_pd.ip_version, + 'ipv6_address_mode': self._subnet_ipv6_pd.ipv6_address_mode, + 'ipv6_ra_mode': self._subnet_ipv6_pd.ipv6_ra_mode, + 'name': self._subnet_ipv6_pd.name, + 'network_id': self._subnet_ipv6_pd.network_id, + 'subnetpool_id': self._subnet_ipv6_pd.subnetpool_id, + } + ) + self.assertFalse(self.network_client.set_tags.called) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data_ipv6_pd, data) + def test_create_with_network_segment(self): # Mock SDK calls for this test. self._network.id = self._subnet.network_id From cc2a0bab2499a6ac6554cd681c2ed4f60aa7a3c2 Mon Sep 17 00:00:00 2001 From: Bence Romsics Date: Wed, 10 Jan 2024 14:11:52 +0100 Subject: [PATCH 057/403] Correct error message for "create server --wait" Error message should say "Error creating server" and not "unshelving". Change-Id: I49dc6160f47a13d38128b3da1ba16437bd089c86 Closes-Bug: #2048896 --- 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 43557f662..2e7bc694d 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2005,9 +2005,7 @@ def _match_image(image_api, wanted_properties): ): self.app.stdout.write('\n') else: - msg = ( - _('Error unshelving server: %s') % parsed_args.server_name - ) + msg = _('Error creating server: %s') % parsed_args.server_name raise exceptions.CommandError(msg) details = _prep_server_detail(compute_client, image_client, server) From 37b420142852c3875097859c47ceba7c5e4cc0b6 Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Thu, 11 Jan 2024 15:17:15 -0800 Subject: [PATCH 058/403] Update python classifier in setup.cfg As per the current release tested runtime, we test python version from 3.8 to 3.11 so updating the same in python classifier in setup.cfg Change-Id: Ic137b2eecee65d43afeca2c936e195a8652237af --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index ceb94adc5..12960cd07 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,6 +17,8 @@ classifier = Programming Language :: Python :: 3 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 [files] packages = From 1517f4af21fc16c4e98705b086dce944a217ad28 Mon Sep 17 00:00:00 2001 From: Mridula Joshi Date: Tue, 16 Jan 2024 15:14:55 +0000 Subject: [PATCH 059/403] Explicitly specify namespace fields for output Rather than excluding the few fields we don't want, explicitly indicate the ones we do want. Note that this is a problem in the tests for virtually all commands that will be seen as the SDK continues to evolve and new fields are added to existing resources. Change-Id: Ia8d487e1e7804fa177fce46497c0202aed8acb08 --- openstackclient/image/v2/metadef_namespaces.py | 4 +++- .../tests/unit/image/v2/test_metadef_namespaces.py | 14 -------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/openstackclient/image/v2/metadef_namespaces.py b/openstackclient/image/v2/metadef_namespaces.py index eaaae6747..cc0277c54 100644 --- a/openstackclient/image/v2/metadef_namespaces.py +++ b/openstackclient/image/v2/metadef_namespaces.py @@ -42,6 +42,7 @@ def _format_namespace(namespace): 'owner', 'protected', 'schema', + 'updated_at', 'visibility', ] @@ -130,8 +131,9 @@ def take_action(self, parsed_args): kwargs['visibility'] = parsed_args.visibility data = image_client.create_metadef_namespace(**kwargs) + info = _format_namespace(data) - return zip(*sorted(data.items())) + return zip(*sorted(info.items())) class DeleteMetadefNameSpace(command.Command): diff --git a/openstackclient/tests/unit/image/v2/test_metadef_namespaces.py b/openstackclient/tests/unit/image/v2/test_metadef_namespaces.py index 9e629c3c1..0b0ddbe0b 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_namespaces.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_namespaces.py @@ -21,30 +21,16 @@ class TestMetadefNamespaceCreate(image_fakes.TestImagev2): expected_columns = ( 'created_at', - 'description', 'display_name', - 'id', - 'is_protected', - 'location', - 'name', 'namespace', 'owner', - 'resource_type_associations', - 'updated_at', 'visibility', ) expected_data = ( _metadef_namespace.created_at, - _metadef_namespace.description, _metadef_namespace.display_name, - _metadef_namespace.id, - _metadef_namespace.is_protected, - _metadef_namespace.location, - _metadef_namespace.name, _metadef_namespace.namespace, _metadef_namespace.owner, - _metadef_namespace.resource_type_associations, - _metadef_namespace.updated_at, _metadef_namespace.visibility, ) From 810f691904672c30e25e094ea334158ac1577e70 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 21 Jan 2024 03:58:44 +0900 Subject: [PATCH 060/403] doc: Remove Searchlight subcommands ... because the project was retired. These were overlooked during cleanup[1]. [1] a79e7db4aeb6990912497eb0fd313ae3e60adafb Change-Id: I8eeeb3ea8c49ed3c9405f54f0dd3be0402cd2a8a --- doc/source/cli/commands.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/source/cli/commands.rst b/doc/source/cli/commands.rst index 8e1c9a6c0..2dd0d80f8 100644 --- a/doc/source/cli/commands.rst +++ b/doc/source/cli/commands.rst @@ -227,9 +227,6 @@ conflicts when creating new plugins. For a complete list check out * ``queue``: (**Messaging (Zaqar)**) * ``recordset``: (**DNS (Designate)**) * ``rsd``: (**Disaggregated Hardware Resource Management (RSD)**) -* ``search`` (**Search (Searchlight)**) -* ``search facet`` (**Search (Searchlight)**) -* ``search resource type`` (**Search (Searchlight)**) * ``secret``: (**Key Manager (Barbican)**) * ``secret container``: (**Key Manager (Barbican)**) * ``secret order``: (**Key Manager (Barbican)**) From 82c34743231f3b2060de3da389cacbd7b8fe9402 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 21 Jan 2024 22:04:06 +0900 Subject: [PATCH 061/403] doc: Remove RSD subcommand The python-rsdclient library hasn't been updated for 5 years and is no longer maintained. Change-Id: Ia378a9001836bbaa4f679dddf1ed743e4332a72e --- doc/requirements.txt | 1 - doc/source/cli/commands.rst | 1 - doc/source/cli/plugin-commands/index.rst | 1 - doc/source/cli/plugin-commands/rsd.rst | 4 ---- doc/source/contributor/plugins.rst | 1 - 5 files changed, 8 deletions(-) delete mode 100644 doc/source/cli/plugin-commands/rsd.rst diff --git a/doc/requirements.txt b/doc/requirements.txt index 93e4f0466..8b8094a6f 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -21,7 +21,6 @@ python-mistralclient!=3.2.0,>=3.1.0 # Apache-2.0 python-muranoclient>=0.8.2 # Apache-2.0 python-neutronclient>=6.7.0 # Apache-2.0 python-octaviaclient>=1.11.0 # Apache-2.0 -python-rsdclient>=1.0.1 # Apache-2.0 python-saharaclient>=1.4.0 # Apache-2.0 python-senlinclient>=1.1.0 # Apache-2.0 python-troveclient>=3.1.0 # Apache-2.0 diff --git a/doc/source/cli/commands.rst b/doc/source/cli/commands.rst index 2dd0d80f8..bd1b54d90 100644 --- a/doc/source/cli/commands.rst +++ b/doc/source/cli/commands.rst @@ -226,7 +226,6 @@ conflicts when creating new plugins. For a complete list check out * ``ptr record``: (**DNS (Designate)**) * ``queue``: (**Messaging (Zaqar)**) * ``recordset``: (**DNS (Designate)**) -* ``rsd``: (**Disaggregated Hardware Resource Management (RSD)**) * ``secret``: (**Key Manager (Barbican)**) * ``secret container``: (**Key Manager (Barbican)**) * ``secret order``: (**Key Manager (Barbican)**) diff --git a/doc/source/cli/plugin-commands/index.rst b/doc/source/cli/plugin-commands/index.rst index e2e0dfa4d..3f1bd8aeb 100644 --- a/doc/source/cli/plugin-commands/index.rst +++ b/doc/source/cli/plugin-commands/index.rst @@ -19,7 +19,6 @@ Plugin Commands neutron octavia placement - rsd sahara senlin trove diff --git a/doc/source/cli/plugin-commands/rsd.rst b/doc/source/cli/plugin-commands/rsd.rst deleted file mode 100644 index d28cea316..000000000 --- a/doc/source/cli/plugin-commands/rsd.rst +++ /dev/null @@ -1,4 +0,0 @@ -rsd ---- - -.. autoprogram-cliff:: openstack.rsd.v2 diff --git a/doc/source/contributor/plugins.rst b/doc/source/contributor/plugins.rst index 35d8d2070..416b79e7e 100644 --- a/doc/source/contributor/plugins.rst +++ b/doc/source/contributor/plugins.rst @@ -35,7 +35,6 @@ The following is a list of projects that are an OpenStackClient plugin. - python-muranoclient - python-neutronclient\*\*\* - python-octaviaclient -- python-rsdclient - python-saharaclient - python-senlinclient - python-tripleoclient\*\* From d4acd4b6d3f989155400cc1e39bb39b7c904d094 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 21 Jan 2024 22:15:29 +0900 Subject: [PATCH 062/403] doc: Drop tripleoclient The TripleO project has been deprecated and its master is no longer maintained. The complete project retirement is now on-going. Change-Id: If7b390fc2230c16ef138ceb4a7bc5d97dcf790e6 --- doc/source/cli/plugin-commands/index.rst | 5 ----- doc/source/contributor/plugins.rst | 7 ++----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/doc/source/cli/plugin-commands/index.rst b/doc/source/cli/plugin-commands/index.rst index e2e0dfa4d..780953f0d 100644 --- a/doc/source/cli/plugin-commands/index.rst +++ b/doc/source/cli/plugin-commands/index.rst @@ -42,8 +42,3 @@ Plugin Commands .. # the murano docs cause warnings and a broken docs build .. # .. list-plugins:: openstack.application_catalog.v1 .. # :detailed: - -.. tripleo -.. # tripleoclient is not in global-requirements -.. # list-plugins:: openstack.tripleoclient.v1 -.. # :detailed: diff --git a/doc/source/contributor/plugins.rst b/doc/source/contributor/plugins.rst index 35d8d2070..6b28b29f7 100644 --- a/doc/source/contributor/plugins.rst +++ b/doc/source/contributor/plugins.rst @@ -33,20 +33,17 @@ The following is a list of projects that are an OpenStackClient plugin. - python-ironic-inspector-client - python-mistralclient - python-muranoclient -- python-neutronclient\*\*\* +- python-neutronclient\*\* - python-octaviaclient - python-rsdclient - python-saharaclient - python-senlinclient -- python-tripleoclient\*\* - python-troveclient - python-watcherclient - python-zaqarclient - python-zunclient -\*\* Note that some clients are not listed in global-requirements. - -\*\*\* Project contains advanced network services. +\*\* Project contains advanced network services. The following is a list of projects that are not an OpenStackClient plugin. From f2387601ee08adfb5cba0fdb672a596e167ab7d5 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 21 Jan 2024 22:13:56 +0900 Subject: [PATCH 063/403] Revert "Temporarily drop aodhclient from doc build" This reverts commit 860d6360474b2f215097d1aa4018a57070e44924. Reason for revert: The issue with aodhclient and latest pyparsing was fixed by [1]. [1] 3a36ed1774ba0bbb3ad6a3716c6c014246bc9613 Change-Id: I461edafc5be7b3afbde485f1620bff71e178b078 --- doc/source/cli/plugin-commands/aodh.rst | 4 ++++ doc/source/cli/plugin-commands/index.rst | 5 +---- 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 doc/source/cli/plugin-commands/aodh.rst diff --git a/doc/source/cli/plugin-commands/aodh.rst b/doc/source/cli/plugin-commands/aodh.rst new file mode 100644 index 000000000..5d8b4332c --- /dev/null +++ b/doc/source/cli/plugin-commands/aodh.rst @@ -0,0 +1,4 @@ +aodh +---- + +.. autoprogram-cliff:: openstack.alarming.v2 diff --git a/doc/source/cli/plugin-commands/index.rst b/doc/source/cli/plugin-commands/index.rst index e2e0dfa4d..eb607ff6c 100644 --- a/doc/source/cli/plugin-commands/index.rst +++ b/doc/source/cli/plugin-commands/index.rst @@ -7,6 +7,7 @@ Plugin Commands .. toctree:: :maxdepth: 1 + aodh barbican cyborg designate @@ -29,10 +30,6 @@ Plugin Commands .. TODO(efried): Make pages for the following once they're fixed. -.. aodh -.. # aodhclient docs build is failing with recent pyparsing -.. # autoprogram-cliff:: openstack.alarming.v2 - .. cue .. # cueclient is not in global-requirements .. # list-plugins:: openstack.mb.v1 From 03044eaf11444f2a9b3fd099dbbb10ffddeddbfd Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 21 Jan 2024 03:52:11 +0900 Subject: [PATCH 064/403] doc: Add manilaclient as a OSC plugin The manilaclient library now provides OSC plugin for share API access. Change-Id: I0afdf4d48e16191283916065719ba7cf623e7377 --- doc/source/cli/commands.rst | 26 ++++++++++++++++++++++++++ doc/source/contributor/plugins.rst | 1 + 2 files changed, 27 insertions(+) diff --git a/doc/source/cli/commands.rst b/doc/source/cli/commands.rst index 8e1c9a6c0..18237c878 100644 --- a/doc/source/cli/commands.rst +++ b/doc/source/cli/commands.rst @@ -233,6 +233,32 @@ conflicts when creating new plugins. For a complete list check out * ``secret``: (**Key Manager (Barbican)**) * ``secret container``: (**Key Manager (Barbican)**) * ``secret order``: (**Key Manager (Barbican)**) +* ``share``: (**Share (Manila)**) +* ``share access``: (**Share (Manila)**) +* ``share availability zone``: (**Share (Manila)**) +* ``share backup``: (**Share (Manila)**) +* ``share export location``: (**Share (Manila)**) +* ``share group``: (**Share (Manila)**) +* ``share group snapshot``: (**Share (Manila)**) +* ``share group type``: (**Share (Manila)**) +* ``share instance``: (**Share (Manila)**) +* ``share limits show``: (**Share (Manila)**) +* ``share lock``: (**Share (Manila)**) +* ``share message``: (**Share (Manila)**) +* ``share migration``: (**Share (Manila)**) +* ``share network``: (**Share (Manila)**) +* ``share quota``: (**Share (Manila)**) +* ``share replica``: (**Share (Manila)**) +* ``share security service``: (**Share (Manila)**) +* ``share server``: (**Share (Manila)**) +* ``share server migration``: (**Share (Manila)**) +* ``share service``: (**Share (Manila)**) +* ``share snapshot``: (**Share (Manila)**) +* ``share snapshot access``: (**Share (Manila)**) +* ``share snapshot export location``: (**Share (Manila)**) +* ``share snapshot instance``: (**Share (Manila)**) +* ``share transfer``: (**Share (Manila)**) +* ``share type``: (**Share (Manila)**) * ``software config``: (**Orchestration (Heat)**) * ``software deployment``: (**Orchestration (Heat)**) * ``stack event``: (**Orchestration (Heat)**) diff --git a/doc/source/contributor/plugins.rst b/doc/source/contributor/plugins.rst index 35d8d2070..8a2d3b55a 100644 --- a/doc/source/contributor/plugins.rst +++ b/doc/source/contributor/plugins.rst @@ -31,6 +31,7 @@ The following is a list of projects that are an OpenStackClient plugin. - python-heatclient - python-ironicclient - python-ironic-inspector-client +- python-manilaclient - python-mistralclient - python-muranoclient - python-neutronclient\*\*\* From 9c2e5d56486f1e31fc4c4c7ca3f6882cc1fa9101 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 23 Jan 2024 11:47:52 +0000 Subject: [PATCH 065/403] pre-commit: Bump linter versions We also migrate to the native hacking pre-commit hook. Change-Id: Idb5a825c20cb7f189997a4d6c3a9a88218fdb335 Signed-off-by: Stephen Finucane --- .pre-commit-config.yaml | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7905abdda..9afa612f2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ default_language_version: python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: trailing-whitespace - id: mixed-line-ending @@ -18,23 +18,18 @@ repos: files: .*\.(yaml|yml)$ args: ['--unsafe'] - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 23.12.1 hooks: - id: black args: ['-S', '-l', '79'] - repo: https://github.com/PyCQA/bandit - rev: 1.7.5 + rev: 1.7.6 hooks: - id: bandit args: ['-x', 'tests', '-s', 'B105,B106,B107,B401,B404,B603,B606,B607,B110,B605,B101'] - - repo: local + - repo: https://opendev.org/openstack/hacking + rev: 6.1.0 hooks: - - id: flake8 - name: flake8 - additional_dependencies: - - hacking>=6.0.1,<6.1.0 - - flake8-import-order>=0.18.2,<0.19.0 - language: python - entry: flake8 - files: '^.*\.py$' + - id: hacking + additional_dependencies: [] exclude: '^(doc|releasenotes|tools)/.*$' From a1f7bd28e67e27ffb2b845c01d210b709fa94023 Mon Sep 17 00:00:00 2001 From: Rajesh Tailor Date: Tue, 2 Jan 2024 18:13:36 +0530 Subject: [PATCH 066/403] Add support for showing requested az in output This change adds support for showing the availability zone requested during instance create in server show and server list --long output. Depends-On: https://review.opendev.org/c/openstack/nova/+/904568 Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/904490 Change-Id: I1772e06b4f043ef3118f036f3908ec70515144bd --- openstackclient/compute/v2/server.py | 9 +++++++++ openstackclient/tests/unit/compute/v2/test_server.py | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index bab303ebd..be1beb1ff 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -180,6 +180,7 @@ 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', } # Some columns returned by openstacksdk should not be shown because they're # either irrelevant or duplicates @@ -2753,11 +2754,13 @@ def take_action(self, parsed_args): if parsed_args.long: columns += ( 'availability_zone', + 'pinned_availability_zone', 'hypervisor_hostname', 'metadata', ) column_headers += ( 'Availability Zone', + 'Pinned Availability Zone', 'Host', 'Properties', ) @@ -2792,6 +2795,12 @@ def take_action(self, parsed_args): if c in ('Availability Zone', "availability_zone"): columns += ('availability_zone',) column_headers += ('Availability Zone',) + if c in ( + 'pinned_availability_zone', + "Pinned Availability Zone", + ): + 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 ea944f9b3..e6d3a1da6 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4644,6 +4644,7 @@ class _TestServerList(TestServer): 'Flavor Name', 'Flavor ID', 'Availability Zone', + 'Pinned Availability Zone', 'Host', 'Properties', ) @@ -4785,6 +4786,7 @@ 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), ) @@ -4830,6 +4832,8 @@ def test_server_list_column_option(self): '-c', 'Availability Zone', '-c', + 'Pinned Availability Zone', + '-c', 'Host', '-c', 'Properties', @@ -4852,6 +4856,7 @@ 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.assertCountEqual(columns, set(columns)) @@ -5264,6 +5269,7 @@ 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), ) @@ -5318,6 +5324,7 @@ 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), s.host_status, @@ -5354,6 +5361,7 @@ class TestServerListV273(_TestServerList): 'Image ID', 'Flavor', 'Availability Zone', + 'Pinned Availability Zone', 'Host', 'Properties', ) From 0725bb474c138c84cbe1131cbf5ab5d877f9d964 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Wed, 23 Aug 2023 16:18:04 +0000 Subject: [PATCH 067/403] Add "hardware_offload_type" attribute to "port" Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/892771 Related-Bug: #2013228 Change-Id: I2c6fd434be4ae8cc41edf45fefe150a41cbfe0bd --- openstackclient/network/v2/port.py | 16 +++++++ .../tests/unit/network/v2/fakes.py | 1 + .../tests/unit/network/v2/test_port.py | 44 +++++++++++++++++++ ...ardware-offload-type-011c98ab748357d7.yaml | 5 +++ 4 files changed, 66 insertions(+) create mode 100644 releasenotes/notes/add-port-hardware-offload-type-011c98ab748357d7.yaml diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index af8c936df..49118b1d9 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -74,6 +74,7 @@ def _get_columns(item): 'dns_name': 'dns_name', 'extra_dhcp_opts': 'extra_dhcp_opts', 'fixed_ips': 'fixed_ips', + 'hardware_offload_type': 'hardware_offload_type', 'hints': 'hints', 'id': 'id', 'ip_allocation': 'ip_allocation', @@ -212,6 +213,12 @@ def _get_attrs(client_manager, parsed_args): if 'device_profile' in parsed_args and parsed_args.device_profile: attrs['device_profile'] = parsed_args.device_profile + if ( + 'hardware_offload_type' in parsed_args + and parsed_args.hardware_offload_type + ): + attrs['hardware_offload_type'] = parsed_args.hardware_offload_type + return attrs @@ -560,6 +567,15 @@ def get_parser(self, prog_name): metavar='', help=_('Cyborg port device profile'), ) + parser.add_argument( + '--hardware-offload-type', + metavar='', + dest='hardware_offload_type', + help=_( + 'Hardware offload type this port will request when ' + 'attached to the network backend' + ), + ) _tag.add_tag_option_to_parser_for_create(parser, _('port')) return parser diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index fcfa376f1..ac02d96ad 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -1761,6 +1761,7 @@ def create_one_port(attrs=None): 'subnet_id': 'subnet-id-' + uuid.uuid4().hex, } ], + 'hardware_offload_type': None, 'hints': {}, 'id': 'port-id-' + uuid.uuid4().hex, 'mac_address': 'fa:16:3e:a9:4e:72', diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index e93cc8c48..f09fe1e51 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -56,6 +56,7 @@ def _get_common_cols_data(fake_port): 'dns_name', 'extra_dhcp_opts', 'fixed_ips', + 'hardware_offload_type', 'hints', 'id', 'ip_allocation', @@ -96,6 +97,7 @@ def _get_common_cols_data(fake_port): fake_port.dns_name, format_columns.ListDictColumn(fake_port.extra_dhcp_opts), format_columns.ListDictColumn(fake_port.fixed_ips), + fake_port.hardware_offload_type, fake_port.hints, fake_port.id, fake_port.ip_allocation, @@ -1068,6 +1070,48 @@ def test_create_hints_valid_json(self): self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) + def _test_create_with_hardware_offload_type(self, hwol_type=None): + arglist = [ + '--network', + self._port.network_id, + 'test-port', + ] + if hwol_type: + arglist += ['--hardware-offload-type', hwol_type] + + hardware_offload_type = None if not hwol_type else hwol_type + verifylist = [ + ( + 'network', + self._port.network_id, + ), + ('name', 'test-port'), + ] + if hwol_type: + verifylist.append(('hardware_offload_type', hwol_type)) + + 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', + } + if hwol_type: + create_args['hardware_offload_type'] = hardware_offload_type + 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_hardware_offload_type_switchdev(self): + self._test_create_with_hardware_offload_type(hwol_type='switchdev') + + def test_create_with_hardware_offload_type_null(self): + self._test_create_with_hardware_offload_type() + class TestDeletePort(TestPort): # Ports to delete. diff --git a/releasenotes/notes/add-port-hardware-offload-type-011c98ab748357d7.yaml b/releasenotes/notes/add-port-hardware-offload-type-011c98ab748357d7.yaml new file mode 100644 index 000000000..87a72cdbd --- /dev/null +++ b/releasenotes/notes/add-port-hardware-offload-type-011c98ab748357d7.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add the port hardware offload attribute to the ``port create`` command. + Once defined, the value cannot be modified. From 2ed10e9a72524717e11b96b00a19d10d06280460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Weing=C3=A4rtner?= Date: Fri, 26 Jan 2024 18:22:53 -0300 Subject: [PATCH 068/403] Introduce `schema_version` in the federated attribute mapping API Depends-On: https://review.opendev.org/c/openstack/keystone/+/739966 Change-Id: I276ebd49094368dd823e50ff11a6e65fa2a6dcfb --- openstackclient/identity/v3/mapping.py | 28 +++++++++++++++++-- .../tests/unit/identity/v3/test_mappings.py | 13 ++++++--- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/openstackclient/identity/v3/mapping.py b/openstackclient/identity/v3/mapping.py index 0a5925de6..ea5e7d70a 100644 --- a/openstackclient/identity/v3/mapping.py +++ b/openstackclient/identity/v3/mapping.py @@ -81,6 +81,21 @@ def _read_rules(self, path): else: return rules + @staticmethod + def add_federated_schema_version_option(parser): + parser.add_argument( + '--schema-version', + metavar='', + required=False, + default=None, + help=_( + "The federated attribute mapping schema version. The " + "default value on the client side is 'None'; however, that " + "will lead the backend to set the default according to " + "'attribute_mapping_default_schema_version' option." + ), + ) + class CreateMapping(command.ShowOne, _RulesReader): _description = _("Create new mapping") @@ -98,6 +113,7 @@ def get_parser(self, prog_name): required=True, help=_('Filename that contains a set of mapping rules (required)'), ) + _RulesReader.add_federated_schema_version_option(parser) return parser def take_action(self, parsed_args): @@ -105,7 +121,9 @@ def take_action(self, parsed_args): rules = self._read_rules(parsed_args.rules) mapping = identity_client.federation.mappings.create( - mapping_id=parsed_args.mapping, rules=rules + mapping_id=parsed_args.mapping, + rules=rules, + schema_version=parsed_args.schema_version, ) mapping._info.pop('links', None) @@ -158,7 +176,7 @@ def take_action(self, parsed_args): # rules, (s)he should show specific ones. identity_client = self.app.client_manager.identity data = identity_client.federation.mappings.list() - columns = ('ID',) + columns = ('ID', 'schema_version') items = [utils.get_item_properties(s, columns) for s in data] return (columns, items) @@ -178,6 +196,8 @@ def get_parser(self, prog_name): metavar='', help=_('Filename that contains a new set of mapping rules'), ) + + _RulesReader.add_federated_schema_version_option(parser) return parser def take_action(self, parsed_args): @@ -186,7 +206,9 @@ def take_action(self, parsed_args): rules = self._read_rules(parsed_args.rules) mapping = identity_client.federation.mappings.update( - mapping=parsed_args.mapping, rules=rules + mapping=parsed_args.mapping, + rules=rules, + schema_version=parsed_args.schema_version, ) mapping._info.pop('links', None) diff --git a/openstackclient/tests/unit/identity/v3/test_mappings.py b/openstackclient/tests/unit/identity/v3/test_mappings.py index e594a96ba..dea0b87c4 100644 --- a/openstackclient/tests/unit/identity/v3/test_mappings.py +++ b/openstackclient/tests/unit/identity/v3/test_mappings.py @@ -63,6 +63,7 @@ def test_create_mapping(self): self.mapping_mock.create.assert_called_with( mapping_id=identity_fakes.mapping_id, rules=identity_fakes.MAPPING_RULES, + schema_version=None, ) collist = ('id', 'rules') @@ -106,12 +107,12 @@ def setUp(self): self.mapping_mock.list.return_value = [ fakes.FakeResource( None, - {'id': identity_fakes.mapping_id}, + {'id': identity_fakes.mapping_id, 'schema_version': '1.0'}, loaded=True, ), fakes.FakeResource( None, - {'id': 'extra_mapping'}, + {'id': 'extra_mapping', 'schema_version': '2.0'}, loaded=True, ), ] @@ -128,10 +129,13 @@ def test_mapping_list(self): self.mapping_mock.list.assert_called_with() - collist = ('ID',) + collist = ('ID', 'schema_version') self.assertEqual(collist, columns) - datalist = [(identity_fakes.mapping_id,), ('extra_mapping',)] + datalist = [ + (identity_fakes.mapping_id, '1.0'), + ('extra_mapping', '2.0'), + ] self.assertEqual(datalist, data) @@ -173,6 +177,7 @@ def test_set_new_rules(self): self.mapping_mock.update.assert_called_with( mapping=identity_fakes.mapping_id, rules=identity_fakes.MAPPING_RULES_2, + schema_version=None, ) self.assertIsNone(result) From 8a63b51039ababa3327900a3b46f2392351e14a6 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 29 Jan 2024 13:34:54 +0000 Subject: [PATCH 069/403] image: Trivial fixes Mostly stylistic, with the exception that we now allow deleting multiple metadef properties in a given namespace. Change-Id: Ib0c243f0d647ce74c0165ee666beed6eb5d5c5a7 Signed-off-by: Stephen Finucane --- .../image/v2/metadef_namespaces.py | 42 ++++---- openstackclient/image/v2/metadef_objects.py | 56 +++++------ .../image/v2/metadef_properties.py | 98 ++++++++++++------- .../unit/image/v2/test_metadef_namespaces.py | 10 +- setup.cfg | 14 +-- 5 files changed, 118 insertions(+), 102 deletions(-) diff --git a/openstackclient/image/v2/metadef_namespaces.py b/openstackclient/image/v2/metadef_namespaces.py index cc0277c54..8294daa02 100644 --- a/openstackclient/image/v2/metadef_namespaces.py +++ b/openstackclient/image/v2/metadef_namespaces.py @@ -62,7 +62,7 @@ def _format_namespace(namespace): return info -class CreateMetadefNameSpace(command.ShowOne): +class CreateMetadefNamespace(command.ShowOne): _description = _("Create a metadef namespace") def get_parser(self, prog_name): @@ -136,16 +136,16 @@ def take_action(self, parsed_args): return zip(*sorted(info.items())) -class DeleteMetadefNameSpace(command.Command): +class DeleteMetadefNamespace(command.Command): _description = _("Delete metadef namespace") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( - "namespace_name", - metavar="", + "namespace", + metavar="", nargs="+", - help=_("An identifier (a name) for the namespace"), + help=_("Metadef namespace(s) to delete (name)"), ) return parser @@ -153,9 +153,9 @@ def take_action(self, parsed_args): image_client = self.app.client_manager.image result = 0 - for i in parsed_args.namespace_name: + for ns in parsed_args.namespace: try: - namespace = image_client.get_metadef_namespace(i) + namespace = image_client.get_metadef_namespace(ns) image_client.delete_metadef_namespace(namespace.id) except Exception as e: result += 1 @@ -164,18 +164,18 @@ def take_action(self, parsed_args): "Failed to delete namespace with name or " "ID '%(namespace)s': %(e)s" ), - {'namespace': i, 'e': e}, + {'namespace': ns, 'e': e}, ) if result > 0: - total = len(parsed_args.namespace_name) + total = len(parsed_args.namespace) msg = _( "%(result)s of %(total)s namespace failed " "to delete." ) % {'result': result, 'total': total} raise exceptions.CommandError(msg) -class ListMetadefNameSpaces(command.Lister): +class ListMetadefNamespace(command.Lister): _description = _("List metadef namespaces") def get_parser(self, prog_name): @@ -217,7 +217,7 @@ def take_action(self, parsed_args): ) -class SetMetadefNameSpace(command.Command): +class SetMetadefNamespace(command.Command): _description = _("Set metadef namespace properties") def get_parser(self, prog_name): @@ -225,7 +225,7 @@ def get_parser(self, prog_name): parser.add_argument( "namespace", metavar="", - help=_("Namespace (name) for the namespace"), + help=_("Metadef namespace to modify (name)"), ) parser.add_argument( "--display-name", @@ -243,14 +243,16 @@ def get_parser(self, prog_name): action="store_const", const="public", dest="visibility", - help=_("Set namespace visibility 'public'"), + help=_("Metadef namespace is accessible to the public"), ) visibility_group.add_argument( "--private", action="store_const", const="private", dest="visibility", - help=_("Set namespace visibility 'private'"), + help=_( + "Metadef namespace is inaccessible to the public (default)" + ), ) protected_group = parser.add_mutually_exclusive_group() protected_group.add_argument( @@ -291,24 +293,24 @@ def take_action(self, parsed_args): image_client.update_metadef_namespace(namespace, **kwargs) -class ShowMetadefNameSpace(command.ShowOne): +class ShowMetadefNamespace(command.ShowOne): _description = _("Show a metadef namespace") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( - "namespace_name", - metavar="", - help=_("Namespace (name) for the namespace"), + "namespace", + metavar="", + help=_("Metadef namespace to show (name)"), ) return parser def take_action(self, parsed_args): image_client = self.app.client_manager.image - namespace_name = parsed_args.namespace_name + namespace = parsed_args.namespace - data = image_client.get_metadef_namespace(namespace_name) + data = image_client.get_metadef_namespace(namespace) info = _format_namespace(data) return zip(*sorted(info.items())) diff --git a/openstackclient/image/v2/metadef_objects.py b/openstackclient/image/v2/metadef_objects.py index 014a953d2..88a70f684 100644 --- a/openstackclient/image/v2/metadef_objects.py +++ b/openstackclient/image/v2/metadef_objects.py @@ -55,7 +55,7 @@ def get_parser(self, prog_name): parser.add_argument( "--namespace", metavar="", - help=_("Metadef namespace to create the metadef object in (name)"), + help=_("Metadef namespace to create the object in (name)"), ) parser.add_argument( "name", @@ -81,31 +81,29 @@ def take_action(self, parsed_args): class ShowMetadefObjects(command.ShowOne): - _description = _( - "Describe a specific metadata definitions object inside a namespace" - ) + _description = _("Show a particular metadef object") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( - "namespace_name", - metavar="", - help=_("Namespace (name) for the namespace"), + "namespace", + metavar="", + help=_("Metadef namespace of the object (name)"), ) parser.add_argument( - "object_name", - metavar="", - help=_("Name of an object."), + "object", + metavar="", + help=_("Metadef object to show"), ) return parser def take_action(self, parsed_args): image_client = self.app.client_manager.image - namespace_name = parsed_args.namespace_name - object_name = parsed_args.object_name + namespace = parsed_args.namespace + object = parsed_args.object - data = image_client.get_metadef_object(object_name, namespace_name) + data = image_client.get_metadef_object(object, namespace) fields, value = _format_object(data) @@ -113,35 +111,33 @@ def take_action(self, parsed_args): class DeleteMetadefObject(command.Command): - _description = _( - "Delete a specific metadata definitions object inside a namespace" - ) + _description = _("Delete metadata definitions object(s)") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( - "namespace_name", - metavar="", - help=_("Namespace (name) for the namespace"), + "namespace", + metavar="", + help=_("Metadef namespace of the object (name)"), ) parser.add_argument( - "object_name", - metavar="", + "objects", + metavar="", nargs="+", - help=_("Name of an object."), + help=_("Metadef object(s) to delete (name)"), ) return parser def take_action(self, parsed_args): image_client = self.app.client_manager.image - namespace_name = parsed_args.namespace_name + namespace = parsed_args.namespace result = 0 - for i in parsed_args.object_name: + for obj in parsed_args.objects: try: - object = image_client.get_metadef_object(i, namespace_name) - image_client.delete_metadef_object(object, namespace_name) + object = image_client.get_metadef_object(obj, namespace) + image_client.delete_metadef_object(object, namespace) except Exception as e: result += 1 LOG.error( @@ -149,11 +145,11 @@ def take_action(self, parsed_args): "Failed to delete object with name or " "ID '%(object)s': %(e)s" ), - {'object': i, 'e': e}, + {'object': obj, 'e': e}, ) if result > 0: - total = len(parsed_args.namespace_name) + total = len(parsed_args.namespace) msg = _("%(result)s of %(total)s object failed to delete.") % { 'result': result, 'total': total, @@ -176,10 +172,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): image_client = self.app.client_manager.image - namespace_name = parsed_args.namespace + namespace = parsed_args.namespace columns = ['name', 'description'] - md_objects = list(image_client.metadef_objects(namespace_name)) + md_objects = list(image_client.metadef_objects(namespace)) column_headers = columns return ( column_headers, diff --git a/openstackclient/image/v2/metadef_properties.py b/openstackclient/image/v2/metadef_properties.py index d2fa4abd4..40440c029 100644 --- a/openstackclient/image/v2/metadef_properties.py +++ b/openstackclient/image/v2/metadef_properties.py @@ -13,6 +13,7 @@ # under the License. import json +import logging from osc_lib.command import command from osc_lib import exceptions @@ -21,6 +22,9 @@ from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + def _format_property(prop): prop = prop.to_dict(ignore_none=True, original_names=True) return { @@ -76,7 +80,7 @@ def get_parser(self, prog_name): help=_("Valid JSON schema of the property"), ) parser.add_argument( - "namespace_name", + "namespace", help=_("Name of namespace the property will belong."), ) return parser @@ -100,7 +104,7 @@ def take_action(self, parsed_args): ) data = image_client.create_metadef_property( - parsed_args.namespace_name, **kwargs + parsed_args.namespace, **kwargs ) info = _format_property(data) @@ -108,41 +112,55 @@ def take_action(self, parsed_args): class DeleteMetadefProperty(command.Command): - _description = _("Delete a metadef property") + _description = _("Delete metadef propert(ies)") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( - "namespace_name", - help=_("An identifier (a name) for the namespace"), + "namespace", + metavar="", + help=_("Metadef namespace of the property (name)"), ) parser.add_argument( - "property_name", - help=_("Property to delete"), + "properties", + metavar="", + nargs="+", + help=_("Metadef propert(ies) to delete (name)"), ) return parser def take_action(self, parsed_args): image_client = self.app.client_manager.image - try: - image_client.delete_metadef_property( - parsed_args.property_name, - parsed_args.namespace_name, - ignore_missing=False, - ) - except Exception as e: - raise exceptions.CommandError( - _( - "Failed to delete property with name or " - "ID '%(property)s' from namespace '%(namespace)s': %(e)s" + result = 0 + for prop in parsed_args.properties: + try: + image_client.delete_metadef_property( + prop, + parsed_args.namespace, + ignore_missing=False, ) - % { - 'property': parsed_args.property_name, - 'namespace': parsed_args.namespace_name, - 'e': e, - } - ) + except Exception as e: + result += 1 + LOG.error( + _( + "Failed to delete property with name or ID " + "'%(property)s' from namespace '%(namespace)s': %(e)s" + ), + { + 'property': prop, + 'namespace': parsed_args.namespace, + 'e': e, + }, + ) + + if result > 0: + total = len(parsed_args.namespace) + msg = _("%(result)s of %(total)s properties failed to delete.") % { + 'result': result, + 'total': total, + } + raise exceptions.CommandError(msg) class ListMetadefProperties(command.Lister): @@ -151,15 +169,15 @@ class ListMetadefProperties(command.Lister): def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( - "namespace_name", - metavar="", + "namespace", + metavar="", help=_("An identifier (a name) for the namespace"), ) return parser def take_action(self, parsed_args): image_client = self.app.client_manager.image - props = image_client.metadef_properties(parsed_args.namespace_name) + props = image_client.metadef_properties(parsed_args.namespace) columns = ['name', 'title', 'type'] return ( columns, @@ -195,11 +213,11 @@ def get_parser(self, prog_name): help=_("Valid JSON schema of the property"), ) parser.add_argument( - "namespace_name", + "namespace", help=_("Namespace of the namespace to which the property belongs"), ) parser.add_argument( - "property_name", + "property", help=_("Property to update"), ) return parser @@ -211,7 +229,8 @@ def take_action(self, parsed_args): # update_metadef_property(), otherwise the attributes that are not # listed will be reset. data = image_client.get_metadef_property( - parsed_args.property_name, parsed_args.namespace_name + parsed_args.property, + parsed_args.namespace, ) kwargs = _format_property(data) for key in ['name', 'title', 'type']: @@ -231,23 +250,25 @@ def take_action(self, parsed_args): ) image_client.update_metadef_property( - parsed_args.property_name, parsed_args.namespace_name, **kwargs + parsed_args.property, + parsed_args.namespace, + **kwargs, ) class ShowMetadefProperty(command.ShowOne): - _description = _("Describe a specific property from a namespace") + _description = _("Show a particular metadef property") def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( - "namespace_name", - metavar="", - help=_("Namespace (name) for the namespace"), + "namespace", + metavar="", + help=_("Metadef namespace of the property (name)"), ) parser.add_argument( - "property_name", - metavar="", + "property", + metavar="", help=_("Property to show"), ) return parser @@ -255,7 +276,8 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): image_client = self.app.client_manager.image data = image_client.get_metadef_property( - parsed_args.property_name, parsed_args.namespace_name + parsed_args.property, + parsed_args.namespace, ) info = _format_property(data) diff --git a/openstackclient/tests/unit/image/v2/test_metadef_namespaces.py b/openstackclient/tests/unit/image/v2/test_metadef_namespaces.py index 0b0ddbe0b..68b3076d5 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_namespaces.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_namespaces.py @@ -40,7 +40,7 @@ def setUp(self): self.image_client.create_metadef_namespace.return_value = ( self._metadef_namespace ) - self.cmd = metadef_namespaces.CreateMetadefNameSpace(self.app, None) + self.cmd = metadef_namespaces.CreateMetadefNamespace(self.app, None) self.datalist = self._metadef_namespace def test_namespace_create(self): @@ -64,7 +64,7 @@ def setUp(self): self.image_client.delete_metadef_namespace.return_value = ( self._metadef_namespace ) - self.cmd = metadef_namespaces.DeleteMetadefNameSpace(self.app, None) + self.cmd = metadef_namespaces.DeleteMetadefNamespace(self.app, None) self.datalist = self._metadef_namespace def test_namespace_create(self): @@ -97,7 +97,7 @@ def setUp(self): self.image_client.metadef_namespaces.return_value = iter( self._metadef_namespace ) - self.cmd = metadef_namespaces.ListMetadefNameSpaces(self.app, None) + self.cmd = metadef_namespaces.ListMetadefNamespace(self.app, None) self.datalist = self._metadef_namespace def test_namespace_list_no_options(self): @@ -122,7 +122,7 @@ def setUp(self): self.image_client.update_metadef_namespace.return_value = ( self._metadef_namespace ) - self.cmd = metadef_namespaces.SetMetadefNameSpace(self.app, None) + self.cmd = metadef_namespaces.SetMetadefNamespace(self.app, None) self.datalist = self._metadef_namespace def test_namespace_set_no_options(self): @@ -162,7 +162,7 @@ def setUp(self): self.image_client.get_metadef_namespace.return_value = ( self._metadef_namespace ) - self.cmd = metadef_namespaces.ShowMetadefNameSpace(self.app, None) + self.cmd = metadef_namespaces.ShowMetadefNamespace(self.app, None) def test_namespace_show_no_options(self): arglist = [self._metadef_namespace.namespace] diff --git a/setup.cfg b/setup.cfg index ac1c7d67e..e19c46488 100644 --- a/setup.cfg +++ b/setup.cfg @@ -393,20 +393,17 @@ openstack.image.v2 = 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:ListMetadefNameSpaces - image_metadef_namespace_set = openstackclient.image.v2.metadef_namespaces:SetMetadefNameSpace - image_metadef_namespace_show = openstackclient.image.v2.metadef_namespaces:ShowMetadefNameSpace - + 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_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 @@ -420,7 +417,6 @@ openstack.image.v2 = 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 From 49ad528266053d1c75f13908a248a04220c342a3 Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 21 Jan 2024 03:44:02 +0900 Subject: [PATCH 070/403] doc: magnumclient provides a OSC plugin The magnumclient library now provides an OSC plugin. So update the document accordingly. Change-Id: Ie828f005a38a511bb0f34ab86cbf97e4a3aae2fe --- doc/requirements.txt | 1 + doc/source/cli/commands.rst | 6 ++++++ doc/source/cli/plugin-commands/index.rst | 1 + doc/source/cli/plugin-commands/magnum.rst | 4 ++++ doc/source/contributor/plugins.rst | 2 +- 5 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 doc/source/cli/plugin-commands/magnum.rst diff --git a/doc/requirements.txt b/doc/requirements.txt index 93e4f0466..aa84f9ef2 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -16,6 +16,7 @@ python-designateclient>=2.7.0 # Apache-2.0 python-heatclient>=1.10.0 # Apache-2.0 python-ironicclient>=2.3.0 # Apache-2.0 python-ironic-inspector-client>=1.5.0 # Apache-2.0 +python-magnumclient>=2.3.0 # Apache-2.0 python-manilaclient>=2.0.0 # Apache-2.0 python-mistralclient!=3.2.0,>=3.1.0 # Apache-2.0 python-muranoclient>=0.8.2 # Apache-2.0 diff --git a/doc/source/cli/commands.rst b/doc/source/cli/commands.rst index 18237c878..f1aea487c 100644 --- a/doc/source/cli/commands.rst +++ b/doc/source/cli/commands.rst @@ -202,6 +202,12 @@ conflicts when creating new plugins. For a complete list check out * ``cluster profile``: (**Clustering (Senlin)**) * ``cluster profile type``: (**Clustering (Senlin)**) * ``cluster receiver``: (**Clustering (Senlin)**) +* ``coe ca``: (**Container Orchestration Engine (Magnum)**) +* ``coe cluster``: (**Container Orchestration Engine (Magnum)**) +* ``coe cluster template``: (**Container Orchestration Engine (Magnum)**) +* ``coe quotas``: (**Container Orchestration Engine (Magnum)**) +* ``coe service``: (**Container Orchestration Engine (Magnum)**) +* ``coe stats``: (**Container Orchestration Engine (Magnum)**) * ``cron trigger``: (**Workflow Engine (Mistral)**) * ``database flavor``: (**Database (Trove)**) * ``dataprocessing data source``: (**Data Processing (Sahara)**) diff --git a/doc/source/cli/plugin-commands/index.rst b/doc/source/cli/plugin-commands/index.rst index e2e0dfa4d..479752765 100644 --- a/doc/source/cli/plugin-commands/index.rst +++ b/doc/source/cli/plugin-commands/index.rst @@ -14,6 +14,7 @@ Plugin Commands heat ironic ironic-inspector + magnum manila mistral neutron diff --git a/doc/source/cli/plugin-commands/magnum.rst b/doc/source/cli/plugin-commands/magnum.rst new file mode 100644 index 000000000..9b3f9f7e8 --- /dev/null +++ b/doc/source/cli/plugin-commands/magnum.rst @@ -0,0 +1,4 @@ +magnum +------ + +.. autoprogram-cliff:: openstack.container_infra.v1 diff --git a/doc/source/contributor/plugins.rst b/doc/source/contributor/plugins.rst index 8a2d3b55a..1ec2e31fb 100644 --- a/doc/source/contributor/plugins.rst +++ b/doc/source/contributor/plugins.rst @@ -31,6 +31,7 @@ The following is a list of projects that are an OpenStackClient plugin. - python-heatclient - python-ironicclient - python-ironic-inspector-client +- python-magnumclient - python-manilaclient - python-mistralclient - python-muranoclient @@ -51,7 +52,6 @@ The following is a list of projects that are an OpenStackClient plugin. The following is a list of projects that are not an OpenStackClient plugin. -- python-magnumclient - python-monascaclient - python-solumclient From d0f81f57177b3eb5cc1effed1e984dcb3ae9999f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 2 Feb 2024 10:14:29 +0000 Subject: [PATCH 071/403] trivial: Fix typo Change-Id: I936cc07af38bdd5d389cc5edafa93861f8fac3a6 Signed-off-by: Stephen Finucane --- openstackclient/image/v2/image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index be64d2081..b980b9bca 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -188,7 +188,7 @@ def _add_visibility_args(parser): action="store_const", const="public", dest="visibility", - help=_("Image is accessible and visisble to all users"), + help=_("Image is accessible and visible to all users"), ) public_group.add_argument( "--private", From 07c05d8ae555c140fc1510765d99095d644fda30 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Mon, 5 Feb 2024 16:14:41 +0000 Subject: [PATCH 072/403] reno: Update master for unmaintained/yoga Update the yoga release notes configuration to build from unmaintained/yoga. Change-Id: Id5ecff72f0a7ebdcab32964480dc351b96beb8b0 --- releasenotes/source/yoga.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/yoga.rst b/releasenotes/source/yoga.rst index 7cd5e908a..43cafdea8 100644 --- a/releasenotes/source/yoga.rst +++ b/releasenotes/source/yoga.rst @@ -3,4 +3,4 @@ Yoga Series Release Notes ========================= .. release-notes:: - :branch: stable/yoga + :branch: unmaintained/yoga From c128ae19694eb3b4871481ec180bca8c8467f6a1 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 13 Feb 2024 12:08:08 +0000 Subject: [PATCH 073/403] trivial: Don't ignore missing resources An openstacksdk 'find_foo' proxy method will return None by default if a resource is not found. You can change this behavior by setting 'ignore_missing=False'. We were doing this in most, but not all cases: correct the issue. In the event of calling 'image delete' with multiple images, it will no longer fail on the first missing image and will instead attempt to delete remaining images before failing. Change-Id: I1e01d3c096dcaab731c28e496a182dd911229227 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 19 ++++++---- openstackclient/compute/v2/server_backup.py | 4 +- openstackclient/compute/v2/server_group.py | 8 +++- .../compute/v2/server_migration.py | 7 ++-- openstackclient/image/v1/image.py | 37 ++++++++++++++++--- openstackclient/image/v2/image.py | 18 ++++----- openstackclient/network/v2/network_flavor.py | 2 +- .../tests/unit/compute/v2/test_server.py | 6 ++- .../unit/compute/v2/test_server_group.py | 10 ++--- .../unit/compute/v2/test_server_migration.py | 4 +- .../tests/unit/image/v1/test_image.py | 4 +- openstackclient/volume/v2/volume_backup.py | 6 +-- 12 files changed, 82 insertions(+), 43 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index b34e20f22..d5d60c4b1 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2555,10 +2555,9 @@ def take_action(self, parsed_args): # flavor name is given, map it to ID. flavor_id = None if parsed_args.flavor: - flavor = compute_client.find_flavor(parsed_args.flavor) - if flavor is None: - msg = _('Unable to find flavor: %s') % parsed_args.flavor - raise exceptions.CommandError(msg) + flavor = compute_client.find_flavor( + parsed_args.flavor, ignore_missing=False + ) flavor_id = flavor.id # Nova only supports list servers searching by image ID. So if a @@ -2811,7 +2810,9 @@ def take_action(self, parsed_args): if parsed_args.deleted: marker_id = parsed_args.marker else: - marker_id = compute_client.find_server(parsed_args.marker).id + marker_id = compute_client.find_server( + parsed_args.marker, ignore_missing=False + ).id search_opts['marker'] = marker_id data = list(compute_client.servers(**search_opts)) @@ -2871,7 +2872,9 @@ def take_action(self, parsed_args): # "Flavor Name" is not crucial, so we swallow any # exceptions try: - flavors[f_id] = compute_client.find_flavor(f_id) + flavors[f_id] = compute_client.find_flavor( + f_id, ignore_missing=False + ) except Exception: pass else: @@ -4039,7 +4042,9 @@ def take_action(self, parsed_args): image = None if parsed_args.image: - image = image_client.find_image(parsed_args.image) + image = image_client.find_image( + parsed_args.image, ignore_missing=False + ) utils.find_resource( compute_client.servers, diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py index c88a0b6ca..ae2154756 100644 --- a/openstackclient/compute/v2/server_backup.py +++ b/openstackclient/compute/v2/server_backup.py @@ -73,7 +73,9 @@ def _show_progress(progress): compute_client = self.app.client_manager.sdk_connection.compute - server = compute_client.find_server(parsed_args.server) + server = compute_client.find_server( + parsed_args.server, ignore_missing=False + ) # Set sane defaults as this API wants all mouths to be fed if parsed_args.name is None: diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py index e5a1a4cea..046057600 100644 --- a/openstackclient/compute/v2/server_group.py +++ b/openstackclient/compute/v2/server_group.py @@ -157,7 +157,9 @@ def take_action(self, parsed_args): result = 0 for group in parsed_args.server_group: try: - group_obj = compute_client.find_server_group(group) + group_obj = compute_client.find_server_group( + group, ignore_missing=False + ) compute_client.delete_server_group(group_obj.id) # Catch all exceptions in order to avoid to block the next deleting except Exception as e: @@ -263,7 +265,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): compute_client = self.app.client_manager.sdk_connection.compute - group = compute_client.find_server_group(parsed_args.server_group) + group = compute_client.find_server_group( + parsed_args.server_group, ignore_missing=False + ) display_columns, columns = _get_server_group_columns( group, compute_client, diff --git a/openstackclient/compute/v2/server_migration.py b/openstackclient/compute/v2/server_migration.py index 8a4440d6a..307303793 100644 --- a/openstackclient/compute/v2/server_migration.py +++ b/openstackclient/compute/v2/server_migration.py @@ -166,10 +166,9 @@ def take_action(self, parsed_args): search_opts['status'] = parsed_args.status if parsed_args.server: - server = compute_client.find_server(parsed_args.server) - if server is None: - msg = _('Unable to find server: %s') % parsed_args.server - raise exceptions.CommandError(msg) + server = compute_client.find_server( + parsed_args.server, ignore_missing=False + ) search_opts['instance_uuid'] = server.id if parsed_args.type: diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 9edec48d1..10f997cd1 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -26,6 +26,7 @@ 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 _ @@ -372,10 +373,30 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + result = 0 image_client = self.app.client_manager.image for image in parsed_args.images: - image_obj = image_client.find_image(image) - image_client.delete_image(image_obj.id) + try: + image_obj = image_client.find_image( + image, + ignore_missing=False, + ) + image_client.delete_image(image_obj.id) + except Exception as e: + result += 1 + msg = _( + "Failed to delete image with name or " + "ID '%(image)s': %(e)s" + ) + LOG.error(msg, {'image': image, 'e': e}) + + total = len(parsed_args.images) + if result > 0: + msg = _("Failed to delete %(result)s of %(total)s images.") % { + 'result': result, + 'total': total, + } + raise exceptions.CommandError(msg) class ListImage(command.Lister): @@ -528,7 +549,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): image_client = self.app.client_manager.image - image = image_client.find_image(parsed_args.image) + image = image_client.find_image( + parsed_args.image, ignore_missing=False + ) output_file = parsed_args.file if output_file is None: @@ -719,7 +742,9 @@ def take_action(self, parsed_args): # Wrap the call to catch exceptions in order to close files try: - image = image_client.find_image(parsed_args.image) + image = image_client.find_image( + parsed_args.image, ignore_missing=False + ) if not parsed_args.location and not parsed_args.copy_from: if parsed_args.volume: @@ -800,7 +825,9 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): image_client = self.app.client_manager.image - image = image_client.find_image(parsed_args.image) + image = image_client.find_image( + parsed_args.image, ignore_missing=False + ) if parsed_args.human_readable: _formatters['size'] = HumanReadableSizeColumn diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 2cf0e40ea..46d374e9e 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -683,7 +683,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - del_result = 0 + result = 0 image_client = self.app.client_manager.image for image in parsed_args.images: try: @@ -691,10 +691,6 @@ def take_action(self, parsed_args): image, ignore_missing=False, ) - except sdk_exceptions.ResourceNotFound as e: - msg = _("Unable to process request: %(e)s") % {'e': e} - raise exceptions.CommandError(msg) - try: image_client.delete_image( image_obj.id, store=parsed_args.store, @@ -704,7 +700,7 @@ def take_action(self, parsed_args): msg = _("Multi Backend support not enabled.") raise exceptions.CommandError(msg) except Exception as e: - del_result += 1 + result += 1 msg = _( "Failed to delete image with name or " "ID '%(image)s': %(e)s" @@ -712,9 +708,9 @@ def take_action(self, parsed_args): LOG.error(msg, {'image': image, 'e': e}) total = len(parsed_args.images) - if del_result > 0: - msg = _("Failed to delete %(dresult)s of %(total)s images.") % { - 'dresult': del_result, + if result > 0: + msg = _("Failed to delete %(result)s of %(total)s images.") % { + 'result': result, 'total': total, } raise exceptions.CommandError(msg) @@ -1797,7 +1793,9 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - image = image_client.find_image(parsed_args.image) + image = image_client.find_image( + parsed_args.image, ignore_missing=False + ) if not image.container_format and not image.disk_format: msg = _( diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py index 7c088d509..2f308bed4 100644 --- a/openstackclient/network/v2/network_flavor.py +++ b/openstackclient/network/v2/network_flavor.py @@ -175,7 +175,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, } diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index ea944f9b3..fb7715a70 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4965,7 +4965,7 @@ def test_server_list_with_flavor(self): columns, data = self.cmd.take_action(parsed_args) self.compute_sdk_client.find_flavor.assert_has_calls( - [mock.call(self.flavor.id)] + [mock.call(self.flavor.id, ignore_missing=False)] ) self.kwargs['flavor'] = self.flavor.id @@ -7119,7 +7119,9 @@ def test_rescue_with_new_image(self): self.cmd.take_action(parsed_args) self.servers_mock.get.assert_called_with(self.server.id) - self.image_client.find_image.assert_called_with(new_image.id) + self.image_client.find_image.assert_called_with( + new_image.id, ignore_missing=False + ) self.server.rescue.assert_called_with(image=new_image, password=None) def test_rescue_with_current_image_and_password(self): diff --git a/openstackclient/tests/unit/compute/v2/test_server_group.py b/openstackclient/tests/unit/compute/v2/test_server_group.py index 3356218ec..ab27288ea 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_group.py +++ b/openstackclient/tests/unit/compute/v2/test_server_group.py @@ -188,7 +188,7 @@ 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( - 'affinity_group' + 'affinity_group', ignore_missing=False ) self.compute_sdk_client.delete_server_group.assert_called_once_with( self.fake_server_group.id @@ -203,10 +203,10 @@ 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( - 'affinity_group' + 'affinity_group', ignore_missing=False ) self.compute_sdk_client.find_server_group.assert_any_call( - 'anti_affinity_group' + 'anti_affinity_group', ignore_missing=False ) self.compute_sdk_client.delete_server_group.assert_called_with( self.fake_server_group.id @@ -248,10 +248,10 @@ def test_server_group_multiple_delete_with_exception(self): self.assertEqual('1 of 2 server groups failed to delete.', str(e)) self.compute_sdk_client.find_server_group.assert_any_call( - 'affinity_group' + 'affinity_group', ignore_missing=False ) self.compute_sdk_client.find_server_group.assert_any_call( - 'anti_affinity_group' + 'anti_affinity_group', ignore_missing=False ) self.assertEqual( 2, self.compute_sdk_client.find_server_group.call_count diff --git a/openstackclient/tests/unit/compute/v2/test_server_migration.py b/openstackclient/tests/unit/compute/v2/test_server_migration.py index 8cb4df4fe..cff5917d1 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_migration.py +++ b/openstackclient/tests/unit/compute/v2/test_server_migration.py @@ -141,7 +141,9 @@ def test_server_migration_list(self): 'migration_type': 'migration', } - self.compute_sdk_client.find_server.assert_called_with('server1') + self.compute_sdk_client.find_server.assert_called_with( + 'server1', ignore_missing=False + ) self.compute_sdk_client.migrations.assert_called_with(**kwargs) self.assertEqual(self.MIGRATION_COLUMNS, columns) diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py index 2edbe1e2a..bf62d10c2 100644 --- a/openstackclient/tests/unit/image/v1/test_image.py +++ b/openstackclient/tests/unit/image/v1/test_image.py @@ -731,7 +731,7 @@ def test_image_show(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) self.image_client.find_image.assert_called_with( - self._image.id, + self._image.id, ignore_missing=False ) self.assertEqual(self.columns, columns) @@ -753,7 +753,7 @@ def test_image_show_human_readable(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) self.image_client.find_image.assert_called_with( - self._image.id, + self._image.id, ignore_missing=False ) size_index = columns.index('size') diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index 72e887156..64c786512 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -203,10 +203,10 @@ def take_action(self, parsed_args): volume_client = self.app.client_manager.sdk_connection.volume result = 0 - for i in parsed_args.backups: + for backup in parsed_args.backups: try: backup_id = volume_client.find_backup( - i, ignore_missing=False + backup, ignore_missing=False ).id volume_client.delete_backup( backup_id, @@ -220,7 +220,7 @@ def take_action(self, parsed_args): "Failed to delete backup with " "name or ID '%(backup)s': %(e)s" ) - % {'backup': i, 'e': e} + % {'backup': backup, 'e': e} ) if result > 0: From 3cd5ad2c1babc9bddc85c1faf5005728a909725c Mon Sep 17 00:00:00 2001 From: Mridula Joshi Date: Wed, 20 Dec 2023 07:35:33 +0000 Subject: [PATCH 074/403] Adds CLI support for ``glance md-object-update`` This patch adds a command "image metadef object update" which update metadata definitions object inside a namespace. Change-Id: I2dd8f54f9224abda5adc7a1b6a1c270c49d473a7 --- .../cli/command-objects/image-metadef.rst | 3 ++ doc/source/cli/data/glance.csv | 2 +- openstackclient/image/v2/metadef_objects.py | 36 ++++++++++++++++++ .../unit/image/v2/test_metadef_objects.py | 38 +++++++++++++++++++ ...etadef-object-update-f4880e423bf4faba.yaml | 5 +++ setup.cfg | 2 + 6 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-image-metadef-object-update-f4880e423bf4faba.yaml diff --git a/doc/source/cli/command-objects/image-metadef.rst b/doc/source/cli/command-objects/image-metadef.rst index 46fe43eda..9fbe75f38 100644 --- a/doc/source/cli/command-objects/image-metadef.rst +++ b/doc/source/cli/command-objects/image-metadef.rst @@ -34,6 +34,9 @@ Image v2 .. autoprogram-cliff:: openstack.image.v2 :command: image metadef object delete +.. autoprogram-cliff:: openstack.image.v2 + :command: image metadef object update + .. autoprogram-cliff:: openstack.image.v2 :command: image metadef property create diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index 7bf9c528e..c0a2f9602 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -37,7 +37,7 @@ md-object-show,image metadef object show,Describe a specific metadata definition md-object-list,image metadef object list,List metadata definitions objects inside a specific namespace. md-object-delete,image metadef object delete,Delete a specific metadata definitions object inside a namespace. md-object-property-show,,Describe a specific metadata definitions property inside an object. -md-object-update,,Update metadata definitions object inside a namespace. +md-object-update,image metadef object update,Update metadata definitions object inside a namespace. md-property-create,image metadef property create,Create a new metadata definitions property inside a namespace. md-property-delete,image metadef property delete,Delete a specific metadata definitions property inside a namespace. md-property-list,image metadef property list,List metadata definitions properties inside a specific namespace. diff --git a/openstackclient/image/v2/metadef_objects.py b/openstackclient/image/v2/metadef_objects.py index 88a70f684..614f5c0c4 100644 --- a/openstackclient/image/v2/metadef_objects.py +++ b/openstackclient/image/v2/metadef_objects.py @@ -187,3 +187,39 @@ def take_action(self, parsed_args): for md_object in md_objects ), ) + + +class SetMetadefObject(command.Command): + _description = _("Update a metadef object") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "namespace", + metavar="", + help=_("Metadef namespace name"), + ) + parser.add_argument( + "object", + metavar="", + help=_('Metadef object to be updated'), + ) + parser.add_argument( + "--name", + help=_("New name of the object"), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + object = image_client.get_metadef_object( + parsed_args.object, parsed_args.namespace + ) + kwargs = {} + if parsed_args.name: + kwargs['name'] = parsed_args.name + + image_client.update_metadef_object( + object, parsed_args.namespace, **kwargs + ) diff --git a/openstackclient/tests/unit/image/v2/test_metadef_objects.py b/openstackclient/tests/unit/image/v2/test_metadef_objects.py index d05b0337b..5533d555b 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_objects.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_objects.py @@ -160,3 +160,41 @@ def test_metadef_objects_list(self): self.assertEqual(self.columns, columns) self.assertEqual(getattr(self.datalist[0], 'name'), next(data)[0]) + + +class TestMetadefObjectSet(fakes.TestImagev2): + _metadef_namespace = fakes.create_one_metadef_namespace() + _metadef_objects = fakes.create_one_metadef_object() + new_metadef_object = fakes.create_one_metadef_object( + attrs={'name': 'new_object_name'} + ) + + def setUp(self): + super().setUp() + + self.image_client.update_metadef_object.return_value = None + self.cmd = metadef_objects.SetMetadefObject(self.app, None) + + def test_object_set_no_options(self): + arglist = [ + self._metadef_namespace.namespace, + self._metadef_objects.name, + ] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + def test_object_set(self): + arglist = [ + self._metadef_namespace.namespace, + self._metadef_objects.name, + '--name', + 'new_object_name', + ] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) diff --git a/releasenotes/notes/add-image-metadef-object-update-f4880e423bf4faba.yaml b/releasenotes/notes/add-image-metadef-object-update-f4880e423bf4faba.yaml new file mode 100644 index 000000000..6cb8efe05 --- /dev/null +++ b/releasenotes/notes/add-image-metadef-object-update-f4880e423bf4faba.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``image metadef object update`` command which + updates the attributes of an object. diff --git a/setup.cfg b/setup.cfg index e19c46488..1ea56efb2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -403,6 +403,8 @@ openstack.image.v2 = 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_property_create = openstackclient.image.v2.metadef_properties:CreateMetadefProperty image_metadef_property_delete = openstackclient.image.v2.metadef_properties:DeleteMetadefProperty From 75ed315885b9f6f433a791bb3e55cf657d01de83 Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Wed, 14 Feb 2024 17:28:04 -0500 Subject: [PATCH 075/403] Do not sort subnet dns_nameservers field When using table output format, the dns_nameservers field of a subnet is sorted, but it should not be as the order is important. Created an UnsortedListColumn() class in subnet.py so the output is correct. Updated the unit test accordingly to verify the order is correct when an entry is removed. Change-Id: I60a15a944f83549738305dd025db38ff8e165be7 Closes-bug: #2053201 --- openstackclient/network/v2/subnet.py | 9 ++++++- .../tests/unit/network/v2/test_subnet.py | 24 +++++++++++-------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 950ad2578..a9af74bba 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -60,9 +60,16 @@ def human_readable(self): ) +class UnsortedListColumn(cliff_columns.FormattableColumn): + # format_columns.ListColumn sorts the output, but for things like + # DNS server addresses the order matters + def human_readable(self): + return ', '.join(self._value) + + _formatters = { 'allocation_pools': AllocationPoolsColumn, - 'dns_nameservers': format_columns.ListColumn, + 'dns_nameservers': UnsortedListColumn, 'host_routes': HostRoutesColumn, 'service_types': format_columns.ListColumn, 'tags': format_columns.ListColumn, diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py index 649ba2af7..816026410 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet.py +++ b/openstackclient/tests/unit/network/v2/test_subnet.py @@ -167,7 +167,7 @@ def _init_subnet_variables(self): subnet_v2.AllocationPoolsColumn(self._subnet.allocation_pools), self._subnet.cidr, self._subnet.description, - format_columns.ListColumn(self._subnet.dns_nameservers), + subnet_v2.UnsortedListColumn(self._subnet.dns_nameservers), self._subnet.enable_dhcp, self._subnet.gateway_ip, subnet_v2.HostRoutesColumn(self._subnet.host_routes), @@ -190,7 +190,9 @@ def _init_subnet_variables(self): ), self._subnet_from_pool.cidr, self._subnet_from_pool.description, - format_columns.ListColumn(self._subnet_from_pool.dns_nameservers), + subnet_v2.UnsortedListColumn( + self._subnet_from_pool.dns_nameservers + ), self._subnet_from_pool.enable_dhcp, self._subnet_from_pool.gateway_ip, subnet_v2.HostRoutesColumn(self._subnet_from_pool.host_routes), @@ -213,7 +215,7 @@ def _init_subnet_variables(self): ), self._subnet_ipv6.cidr, self._subnet_ipv6.description, - format_columns.ListColumn(self._subnet_ipv6.dns_nameservers), + subnet_v2.UnsortedListColumn(self._subnet_ipv6.dns_nameservers), self._subnet_ipv6.enable_dhcp, self._subnet_ipv6.gateway_ip, subnet_v2.HostRoutesColumn(self._subnet_ipv6.host_routes), @@ -236,7 +238,7 @@ def _init_subnet_variables(self): ), self._subnet_ipv6_pd.cidr, self._subnet_ipv6_pd.description, - format_columns.ListColumn(self._subnet_ipv6_pd.dns_nameservers), + subnet_v2.UnsortedListColumn(self._subnet_ipv6_pd.dns_nameservers), self._subnet_ipv6_pd.enable_dhcp, self._subnet_ipv6_pd.gateway_ip, subnet_v2.HostRoutesColumn(self._subnet_ipv6_pd.host_routes), @@ -837,7 +839,7 @@ class TestListSubnet(TestSubnet): subnet.cidr, subnet.project_id, subnet.enable_dhcp, - format_columns.ListColumn(subnet.dns_nameservers), + subnet_v2.UnsortedListColumn(subnet.dns_nameservers), subnet_v2.AllocationPoolsColumn(subnet.allocation_pools), subnet_v2.HostRoutesColumn(subnet.host_routes), subnet.ip_version, @@ -1489,7 +1491,7 @@ class TestShowSubnet(TestSubnet): subnet_v2.AllocationPoolsColumn(_subnet.allocation_pools), _subnet.cidr, _subnet.description, - format_columns.ListColumn(_subnet.dns_nameservers), + subnet_v2.UnsortedListColumn(_subnet.dns_nameservers), _subnet.enable_dhcp, _subnet.gateway_ip, subnet_v2.HostRoutesColumn(_subnet.host_routes), @@ -1550,9 +1552,10 @@ def test_show_all_options(self): class TestUnsetSubnet(TestSubnet): def setUp(self): super(TestUnsetSubnet, self).setUp() + # Add three dns_nameserver entries so we can verify ordering self._testsubnet = network_fakes.FakeSubnet.create_one_subnet( { - 'dns_nameservers': ['8.8.8.8', '8.8.8.4'], + 'dns_nameservers': ['8.8.8.8', '8.8.8.4', '8.8.4.4'], 'host_routes': [ {'destination': '10.20.20.0/24', 'nexthop': '10.20.20.1'}, {'destination': '10.30.30.30/24', 'nexthop': '10.30.30.1'}, @@ -1578,9 +1581,10 @@ def setUp(self): self.cmd = subnet_v2.UnsetSubnet(self.app, self.namespace) def test_unset_subnet_params(self): + # Remove just the middle dns_nameserver entry, verify still in order arglist = [ '--dns-nameserver', - '8.8.8.8', + '8.8.8.4', '--host-route', 'destination=10.30.30.30/24,gateway=10.30.30.1', '--allocation-pool', @@ -1591,7 +1595,7 @@ def test_unset_subnet_params(self): self._testsubnet.name, ] verifylist = [ - ('dns_nameservers', ['8.8.8.8']), + ('dns_nameservers', ['8.8.8.4']), ( 'host_routes', [{"destination": "10.30.30.30/24", "gateway": "10.30.30.1"}], @@ -1605,7 +1609,7 @@ def test_unset_subnet_params(self): result = self.cmd.take_action(parsed_args) attrs = { - 'dns_nameservers': ['8.8.8.4'], + 'dns_nameservers': ['8.8.8.8', '8.8.4.4'], 'host_routes': [ {"destination": "10.20.20.0/24", "nexthop": "10.20.20.1"} ], From 2a90a6f07b52760b9442493d4c382d7e6ee1ced2 Mon Sep 17 00:00:00 2001 From: Mridula Joshi Date: Wed, 20 Dec 2023 08:43:44 +0000 Subject: [PATCH 076/403] Adds CLI support for ``glance md-object-property-show`` This patch adds a command "image metadef object property show" which describe a specific metadata definitions property inside an object. Change-Id: I738e2e3c27c9819290d5a2a8781878b81f03b5f9 --- .../cli/command-objects/image-metadef.rst | 3 + doc/source/cli/data/glance.csv | 2 +- openstackclient/image/v2/metadef_objects.py | 44 +++++++++++++ .../unit/image/v2/test_metadef_objects.py | 62 +++++++++++++++++++ ...object-property-show-4ab2c957451ea230.yaml | 5 ++ setup.cfg | 2 +- 6 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/add-image-metadef-object-property-show-4ab2c957451ea230.yaml diff --git a/doc/source/cli/command-objects/image-metadef.rst b/doc/source/cli/command-objects/image-metadef.rst index 9fbe75f38..ed36d95f7 100644 --- a/doc/source/cli/command-objects/image-metadef.rst +++ b/doc/source/cli/command-objects/image-metadef.rst @@ -37,6 +37,9 @@ Image v2 .. autoprogram-cliff:: openstack.image.v2 :command: image metadef object update +.. autoprogram-cliff:: openstack.image.v2 + :command: image metadef object property show + .. autoprogram-cliff:: openstack.image.v2 :command: image metadef property create diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index c0a2f9602..8852e2d00 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -36,7 +36,7 @@ md-object-create,image metadef object create,Create a new metadata definitions o md-object-show,image metadef object show,Describe a specific metadata definitions object inside a namespace. md-object-list,image metadef object list,List metadata definitions objects inside a specific namespace. md-object-delete,image metadef object delete,Delete a specific metadata definitions object inside a namespace. -md-object-property-show,,Describe a specific metadata definitions property inside an object. +md-object-property-show,image metadef object property show,Describe a specific metadata definitions property inside an object. md-object-update,image metadef object update,Update metadata definitions object inside a namespace. md-property-create,image metadef property create,Create a new metadata definitions property inside a namespace. md-property-delete,image metadef property delete,Delete a specific metadata definitions property inside a namespace. diff --git a/openstackclient/image/v2/metadef_objects.py b/openstackclient/image/v2/metadef_objects.py index 614f5c0c4..2dc33968a 100644 --- a/openstackclient/image/v2/metadef_objects.py +++ b/openstackclient/image/v2/metadef_objects.py @@ -223,3 +223,47 @@ def take_action(self, parsed_args): image_client.update_metadef_object( object, parsed_args.namespace, **kwargs ) + + +class ShowMetadefObjectProperty(command.ShowOne): + _description = _( + "Describe a specific metadata definitions property inside an object." + ) + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "namespace", + metavar="", + help=_("Namespace (name) for the namespace"), + ) + parser.add_argument( + "object", + metavar="", + help=_("Name of an object."), + ) + parser.add_argument( + "property", + help=_("Name of the property."), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + namespace_name = parsed_args.namespace + object_name = parsed_args.object + + obj = image_client.get_metadef_object(object_name, namespace_name) + try: + prop = obj['properties'][parsed_args.property] + prop['name'] = parsed_args.property + + except KeyError: + msg = _('Property %s not found in object %s.') % ( + parsed_args.property, + parsed_args.object, + ) + raise exceptions.CommandError(msg) + + return zip(*sorted(prop.items())) diff --git a/openstackclient/tests/unit/image/v2/test_metadef_objects.py b/openstackclient/tests/unit/image/v2/test_metadef_objects.py index 5533d555b..323d8ea15 100644 --- a/openstackclient/tests/unit/image/v2/test_metadef_objects.py +++ b/openstackclient/tests/unit/image/v2/test_metadef_objects.py @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations # under the License. +from osc_lib import exceptions + from openstackclient.image.v2 import metadef_objects from openstackclient.tests.unit.image.v2 import fakes @@ -198,3 +200,63 @@ def test_object_set(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) + + +class TestMetadefObjectPropertyShow(fakes.TestImagev2): + _metadef_namespace = fakes.create_one_metadef_namespace() + _metadef_objects = fakes.create_one_metadef_object() + md_property = _metadef_objects['properties']['quota:cpu_quota'] + md_property['name'] = 'quota:cpu_quota' + + expected_columns = ( + 'description', + 'name', + 'title', + 'type', + ) + expected_data = ( + md_property['description'], + md_property['name'], + md_property['title'], + md_property['type'], + ) + + def setUp(self): + super().setUp() + + self.image_client.get_metadef_object.return_value = ( + self._metadef_objects + ) + self.cmd = metadef_objects.ShowMetadefObjectProperty(self.app, None) + + def test_object_property_show(self): + arglist = [ + self._metadef_namespace.namespace, + self._metadef_objects.name, + 'quota:cpu_quota', + ] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + + def test_neg_object_property_show(self): + arglist = [ + self._metadef_namespace.namespace, + self._metadef_objects.name, + 'prop1', + ] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertIn( + 'Property %s not found in object %s.' + % (parsed_args.property, parsed_args.object), + str(exc), + ) diff --git a/releasenotes/notes/add-image-metadef-object-property-show-4ab2c957451ea230.yaml b/releasenotes/notes/add-image-metadef-object-property-show-4ab2c957451ea230.yaml new file mode 100644 index 000000000..32b259c12 --- /dev/null +++ b/releasenotes/notes/add-image-metadef-object-property-show-4ab2c957451ea230.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``image metadef object property show`` command which + shows a particular property inside metadef object. diff --git a/setup.cfg b/setup.cfg index 1ea56efb2..c8ff01ee5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -404,7 +404,7 @@ openstack.image.v2 = 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 From 93b73e9b2968fe2a8a488bc68edc38dd5f07354b Mon Sep 17 00:00:00 2001 From: James Black Date: Wed, 21 Feb 2024 19:50:28 -0800 Subject: [PATCH 077/403] Bug Fix, Default SG Rule Custom SG Currently the Default setting for CustomSG Rule is set to True, this means all new SGs inherit these rules, with no way for user to override this behavior. Closes Bug: #2054629 Change-Id: Icef7c91654dbced76a5492437e723c4e2a0f3102 --- .../network/v2/default_security_group_rule.py | 2 +- .../v2/test_default_security_group_rule.py | 32 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/openstackclient/network/v2/default_security_group_rule.py b/openstackclient/network/v2/default_security_group_rule.py index 86a2ddc87..9c39b3f9c 100644 --- a/openstackclient/network/v2/default_security_group_rule.py +++ b/openstackclient/network/v2/default_security_group_rule.py @@ -142,7 +142,7 @@ def get_parser(self, prog_name): parser.add_argument( '--for-custom-sg', action='store_true', - default=True, + default=False, help=_( "Set this default security group rule to be used in all " "custom security groups created manually by users" 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 a7a6ec69b..aded5abff 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 @@ -72,7 +72,7 @@ def _setup_default_security_group_rule(self, attrs=None): 'remote_ip_prefix': '0.0.0.0/0', 'location': 'MUNCHMUNCHMUNCH', 'used_in_default_sg': False, - 'used_in_non_default_sg': True, + 'used_in_non_default_sg': False, } attrs = attrs or {} # Overwrite default attributes. @@ -217,7 +217,7 @@ def test_create_default_rule(self): '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': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -251,7 +251,7 @@ def test_create_protocol_any(self): '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': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -287,7 +287,7 @@ def test_create_remote_address_group(self): 'protocol': self._default_sg_rule.protocol, 'remote_address_group_id': self._default_sg_rule.remote_address_group_id, 'used_in_default_sg': False, - 'used_in_non_default_sg': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -334,7 +334,7 @@ def test_create_remote_group(self): 'protocol': self._default_sg_rule.protocol, 'remote_group_id': 'remote-group-id', 'used_in_default_sg': False, - 'used_in_non_default_sg': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -366,7 +366,7 @@ def test_create_source_group(self): 'protocol': self._default_sg_rule.protocol, 'remote_group_id': 'remote-group-id', 'used_in_default_sg': False, - 'used_in_non_default_sg': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -400,7 +400,7 @@ def test_create_source_ip(self): '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': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -434,7 +434,7 @@ def test_create_remote_ip(self): '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': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -585,7 +585,7 @@ def test_create_icmp_type(self): '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': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -623,7 +623,7 @@ def test_create_icmp_type_zero(self): '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': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -661,7 +661,7 @@ def test_create_icmp_type_greater_than_zero(self): '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': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -698,7 +698,7 @@ def test_create_icmp_type_negative_value(self): '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': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -741,7 +741,7 @@ def test_create_ipv6_icmp_type_code(self): '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': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -780,7 +780,7 @@ def test_create_icmpv6_type(self): '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': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -811,7 +811,7 @@ def test_create_with_description(self): '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': True, + 'used_in_non_default_sg': False, } ) self.assertEqual(self.expected_columns, columns) @@ -831,7 +831,7 @@ class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): 'remote_ip_prefix': '0.0.0.0/0', 'location': 'MUNCHMUNCHMUNCH', 'used_in_default_sg': False, - 'used_in_non_default_sg': True, + 'used_in_non_default_sg': False, } _default_sg_rules = list( sdk_fakes.generate_fake_resources( From 966051cfe2e35c2a9c8a497bf5a6b626ba192b0c Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Sat, 24 Feb 2024 11:15:45 +0000 Subject: [PATCH 078/403] Add NUMA affinity policy options "socket" Added a new port NUMA affinity policy option called "socket". Depends-On: https://review.opendev.org/c/openstack/neutron/+/910594 Related-Bug: #2052786 Change-Id: Icff9070db68ba83b47b344d56ee93b235383b2f2 --- openstackclient/network/v2/port.py | 9 +++++++++ ...ort-numa-affinity-policy-socket-5a986b14033e0f6e.yaml | 5 +++++ 2 files changed, 14 insertions(+) create mode 100644 releasenotes/notes/add-port-numa-affinity-policy-socket-5a986b14033e0f6e.yaml diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 49118b1d9..0e5fe7605 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -205,6 +205,10 @@ def _get_attrs(client_manager, parsed_args): and parsed_args.numa_policy_preferred ): attrs['numa_affinity_policy'] = 'preferred' + elif ( + 'numa_policy_socket' in parsed_args and parsed_args.numa_policy_socket + ): + attrs['numa_affinity_policy'] = 'socket' elif ( 'numa_policy_legacy' in parsed_args and parsed_args.numa_policy_legacy ): @@ -354,6 +358,11 @@ def _add_updatable_args(parser): action='store_true', help=_("NUMA affinity policy preferred to schedule this port"), ) + numa_affinity_policy_group.add_argument( + '--numa-policy-socket', + action='store_true', + help=_("NUMA affinity policy socket to schedule this port"), + ) numa_affinity_policy_group.add_argument( '--numa-policy-legacy', action='store_true', diff --git a/releasenotes/notes/add-port-numa-affinity-policy-socket-5a986b14033e0f6e.yaml b/releasenotes/notes/add-port-numa-affinity-policy-socket-5a986b14033e0f6e.yaml new file mode 100644 index 000000000..d464992ec --- /dev/null +++ b/releasenotes/notes/add-port-numa-affinity-policy-socket-5a986b14033e0f6e.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add a new NUMA affinity policy option: "socket". That applies to any new + port (using ``port create``) or any existing port (using ``port set``). From f696aee81d8b20ffdaa11c786c7e55cd7aa9cbb8 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Tue, 5 Mar 2024 11:15:23 +0100 Subject: [PATCH 079/403] Parse external-gateway argument in separate helper This is to prepare for subsequent patches that will add support for managing multiple gateways. Related-Bug: #2002687 Change-Id: Ic088dca0b7cd83bd7568d775b4e70285ce72411d Signed-off-by: Frode Nordahl --- openstackclient/network/v2/router.py | 62 ++++++++++++++++------------ 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index fdb7102bb..b44301533 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -85,6 +85,39 @@ def _get_columns(item): ) +def _get_external_gateway_attrs(client_manager, parsed_args): + attrs = {} + + if parsed_args.external_gateway: + gateway_info = {} + n_client = client_manager.network + network = n_client.find_network( + parsed_args.external_gateway, ignore_missing=False + ) + gateway_info['network_id'] = network.id + if parsed_args.disable_snat: + gateway_info['enable_snat'] = False + if parsed_args.enable_snat: + gateway_info['enable_snat'] = True + if parsed_args.fixed_ip: + ips = [] + for ip_spec in parsed_args.fixed_ip: + if ip_spec.get('subnet', False): + subnet_name_id = ip_spec.pop('subnet') + if subnet_name_id: + subnet = n_client.find_subnet( + subnet_name_id, ignore_missing=False + ) + ip_spec['subnet_id'] = subnet.id + if ip_spec.get('ip-address', False): + ip_spec['ip_address'] = ip_spec.pop('ip-address') + ips.append(ip_spec) + gateway_info['external_fixed_ips'] = ips + attrs['external_gateway_info'] = gateway_info + + return attrs + + def _get_attrs(client_manager, parsed_args): attrs = {} if parsed_args.name is not None: @@ -113,32 +146,9 @@ def _get_attrs(client_manager, parsed_args): parsed_args.project_domain, ).id attrs['project_id'] = project_id - if parsed_args.external_gateway: - gateway_info = {} - n_client = client_manager.network - network = n_client.find_network( - parsed_args.external_gateway, ignore_missing=False - ) - gateway_info['network_id'] = network.id - if parsed_args.disable_snat: - gateway_info['enable_snat'] = False - if parsed_args.enable_snat: - gateway_info['enable_snat'] = True - if parsed_args.fixed_ip: - ips = [] - for ip_spec in parsed_args.fixed_ip: - if ip_spec.get('subnet', False): - subnet_name_id = ip_spec.pop('subnet') - if subnet_name_id: - subnet = n_client.find_subnet( - subnet_name_id, ignore_missing=False - ) - ip_spec['subnet_id'] = subnet.id - if ip_spec.get('ip-address', False): - ip_spec['ip_address'] = ip_spec.pop('ip-address') - ips.append(ip_spec) - gateway_info['external_fixed_ips'] = ips - attrs['external_gateway_info'] = gateway_info + + attrs.update(_get_external_gateway_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: attrs['flavor_id'] = parsed_args.flavor_id From 58ad3cefa7870320d9d9835b202f7b9661b1d825 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Tue, 5 Mar 2024 15:56:49 +0100 Subject: [PATCH 080/403] router: Use plural form for storage of ``--fixed_ip`` argument The variable already takes multiple values, let's make it obvious just by reading the code. Related-Bug: #2002687 Change-Id: I294ee710d989d7a3a54331fca424e84708a2faab Signed-off-by: Frode Nordahl --- openstackclient/network/v2/router.py | 10 ++++++---- openstackclient/tests/unit/network/v2/test_router.py | 10 +++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index b44301533..21170ffa1 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -99,9 +99,9 @@ def _get_external_gateway_attrs(client_manager, parsed_args): gateway_info['enable_snat'] = False if parsed_args.enable_snat: gateway_info['enable_snat'] = True - if parsed_args.fixed_ip: + if parsed_args.fixed_ips: ips = [] - for ip_spec in parsed_args.fixed_ip: + for ip_spec in parsed_args.fixed_ips: if ip_spec.get('subnet', False): subnet_name_id = ip_spec.pop('subnet') if subnet_name_id: @@ -379,6 +379,7 @@ def get_parser(self, prog_name): metavar='subnet=,ip-address=', action=parseractions.MultiKeyValueAction, optional_keys=['subnet', 'ip-address'], + dest='fixed_ips', help=_( "Desired IP and/or subnet (name or ID) " "on external gateway: " @@ -449,7 +450,7 @@ def take_action(self, parsed_args): if ( parsed_args.disable_snat or parsed_args.enable_snat - or parsed_args.fixed_ip + or parsed_args.fixed_ips ) and not parsed_args.external_gateway: msg = _( "You must specify '--external-gateway' in order " @@ -797,6 +798,7 @@ def get_parser(self, prog_name): metavar='subnet=,ip-address=', action=parseractions.MultiKeyValueAction, optional_keys=['subnet', 'ip-address'], + dest='fixed_ips', help=_( "Desired IP and/or subnet (name or ID) " "on external gateway: " @@ -870,7 +872,7 @@ def take_action(self, parsed_args): if ( parsed_args.disable_snat or parsed_args.enable_snat - or parsed_args.fixed_ip + or parsed_args.fixed_ips ) and not parsed_args.external_gateway: msg = _( "You must specify '--external-gateway' in order " diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 9935db4d9..fb12e3f09 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -230,7 +230,7 @@ def test_create_with_gateway(self): ('ha', False), ('external_gateway', _network.name), ('enable_snat', True), - ('fixed_ip', [{'ip-address': '2001:db8::1'}]), + ('fixed_ips', [{'ip-address': '2001:db8::1'}]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1297,7 +1297,7 @@ def test_wrong_gateway_params(self): self._router.id, ] verifylist = [ - ('fixed_ip', [{'subnet': "'abc'"}]), + ('fixed_ips', [{'subnet': "'abc'"}]), ('router', self._router.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1336,7 +1336,7 @@ def test_set_gateway_options_subnet_only(self): verifylist = [ ('router', self._router.id), ('external_gateway', self._network.id), - ('fixed_ip', [{'subnet': "'abc'"}]), + ('fixed_ips', [{'subnet': "'abc'"}]), ('enable_snat', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1370,7 +1370,7 @@ def test_set_gateway_option_ipaddress_only(self): verifylist = [ ('router', self._router.id), ('external_gateway', self._network.id), - ('fixed_ip', [{'ip-address': "10.0.1.1"}]), + ('fixed_ips', [{'ip-address': "10.0.1.1"}]), ('enable_snat', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1404,7 +1404,7 @@ def test_set_gateway_options_subnet_ipaddress(self): verifylist = [ ('router', self._router.id), ('external_gateway', self._network.id), - ('fixed_ip', [{'subnet': "'abc'", 'ip-address': "10.0.1.1"}]), + ('fixed_ips', [{'subnet': "'abc'", 'ip-address': "10.0.1.1"}]), ('enable_snat', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) From 16c695045c5618a733574fd2a5558d672a2df553 Mon Sep 17 00:00:00 2001 From: Dmitrii Shcherbakov Date: Fri, 7 Jul 2023 19:08:33 +0300 Subject: [PATCH 081/403] Add support for managing external gateways This change implements the logic to call the new API for managing external gateways. Relevant Neutron core change: https://review.opendev.org/c/openstack/neutron/+/873593 Co-Authored-by: Frode Nordahl Related-Bug: #2002687 Change-Id: Ib45f30f552934a0a5c035c3b7fadfc0d522219ba --- openstackclient/network/v2/router.py | 313 ++++++++- .../tests/unit/network/v2/test_router.py | 616 +++++++++++++++++- setup.cfg | 2 + 3 files changed, 885 insertions(+), 46 deletions(-) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 21170ffa1..52b7d25fc 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -13,6 +13,7 @@ """Router action implementations""" +import collections import copy import json import logging @@ -85,23 +86,67 @@ def _get_columns(item): ) +def is_multiple_gateways_supported(n_client): + return n_client.find_extension("external-gateway-multihoming") is not None + + +def _passed_multiple_gateways(extension_supported, external_gateways): + passed_multiple_gws = len(external_gateways) > 1 + if passed_multiple_gws and not extension_supported: + msg = _( + 'Supplying --external-gateway option multiple times is not ' + 'supported due to the lack of external-gateway-multihoming ' + 'extension at the Neutron side.' + ) + raise exceptions.CommandError(msg) + return passed_multiple_gws + + def _get_external_gateway_attrs(client_manager, parsed_args): attrs = {} - if parsed_args.external_gateway: - gateway_info = {} + if parsed_args.external_gateways: + external_gateways: collections.defaultdict[ + str, list[dict] + ] = collections.defaultdict(list) n_client = client_manager.network - network = n_client.find_network( - parsed_args.external_gateway, ignore_missing=False - ) - gateway_info['network_id'] = network.id - if parsed_args.disable_snat: - gateway_info['enable_snat'] = False - if parsed_args.enable_snat: - gateway_info['enable_snat'] = True + first_network_id = None + + 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: + first_network_id = gw_net.id + gateway_info['network_id'] = gw_net.id + if 'disable_snat' in parsed_args and parsed_args.disable_snat: + gateway_info['enable_snat'] = False + if 'enable_snat' in parsed_args and parsed_args.enable_snat: + gateway_info['enable_snat'] = True + + # This option was added before multiple gateways were supported, so + # it does not have a per-gateway port granularity so just pass it + # along in gw info in case it is specified. + if 'qos_policy' in parsed_args and parsed_args.qos_policy: + qos_id = n_client.find_qos_policy( + parsed_args.qos_policy, ignore_missing=False + ).id + gateway_info['qos_policy_id'] = qos_id + if 'no_qos_policy' in parsed_args and parsed_args.no_qos_policy: + gateway_info['qos_policy_id'] = None + + external_gateways[gw_net.id].append(gateway_info) + + multiple_gws_supported = is_multiple_gateways_supported(n_client) + # Parse the external fixed IP specs and match them to specific gateway + # ports if needed. if parsed_args.fixed_ips: - ips = [] 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 + if ip_spec.get('subnet', False): subnet_name_id = ip_spec.pop('subnet') if subnet_name_id: @@ -109,12 +154,45 @@ def _get_external_gateway_attrs(client_manager, parsed_args): subnet_name_id, ignore_missing=False ) ip_spec['subnet_id'] = subnet.id + ip_net_id = subnet.network_id if ip_spec.get('ip-address', False): ip_spec['ip_address'] = ip_spec.pop('ip-address') - ips.append(ip_spec) - gateway_info['external_fixed_ips'] = ips - attrs['external_gateway_info'] = gateway_info - + # Finally, add an ip_spec to the specific gateway identified + # by a network from the spec. + if ( + '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']) + raise exceptions.CommandError(msg) + for gw_info in external_gateways[ip_net_id]: + if 'external_fixed_ips' not in gw_info: + gw_info['external_fixed_ips'] = [ip_spec] + break + else: + # The end user has requested more fixed IPs than there are + # gateways, add multiple fixed IPs to single gateway to + # retain current behavior. + for gw_info in external_gateways[ip_net_id]: + gw_info['external_fixed_ips'].append(ip_spec) + break + + # Use the newer API whenever it is supported regardless of whether one + # or multiple gateways are passed as arguments. + if multiple_gws_supported: + gateway_list = [] + # Now merge the per-network-id lists of external gateway info + # dicts into one list. + for gw_info_list in external_gateways.values(): + gateway_list.extend(gw_info_list) + attrs['external_gateways'] = gateway_list + else: + attrs['external_gateway_info'] = external_gateways[ + first_network_id + ][0] return attrs @@ -372,7 +450,13 @@ def get_parser(self, prog_name): parser.add_argument( '--external-gateway', metavar="", - help=_("External Network used as router's gateway (name or ID)"), + action='append', + help=_( + "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)." + ), + dest='external_gateways', ) parser.add_argument( '--fixed-ip', @@ -384,7 +468,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() @@ -433,7 +517,7 @@ def take_action(self, parsed_args): self._parse_extra_properties(parsed_args.extra_properties) ) - if parsed_args.enable_ndp_proxy and not parsed_args.external_gateway: + if parsed_args.enable_ndp_proxy and not parsed_args.external_gateways: msg = _( "You must specify '--external-gateway' in order " "to enable router's NDP proxy" @@ -443,15 +527,24 @@ def take_action(self, parsed_args): if parsed_args.enable_ndp_proxy is not None: attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy + external_gateways = attrs.pop('external_gateways', None) obj = client.create_router(**attrs) # tags cannot be set when created, so tags need to be set later. _tag.update_tags_for_set(client, obj, parsed_args) + # If the multiple external gateways API is intended to be used, + # do a separate API call to set the desired external gateways as the + # router creation API supports adding only one. + if external_gateways: + client.update_external_gateways( + obj, body={'router': {'external_gateways': external_gateways}} + ) + if ( parsed_args.disable_snat or parsed_args.enable_snat or parsed_args.fixed_ips - ) and not parsed_args.external_gateway: + ) and not parsed_args.external_gateways: msg = _( "You must specify '--external-gateway' in order " "to specify SNAT or fixed-ip values" @@ -791,7 +884,13 @@ def get_parser(self, prog_name): parser.add_argument( '--external-gateway', metavar="", - help=_("External Network used as router's gateway (name or ID)"), + action='append', + help=_( + "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)." + ), + dest='external_gateways', ) parser.add_argument( '--fixed-ip', @@ -803,7 +902,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() @@ -873,7 +972,7 @@ def take_action(self, parsed_args): parsed_args.disable_snat or parsed_args.enable_snat or parsed_args.fixed_ips - ) and not parsed_args.external_gateway: + ) and not parsed_args.external_gateways: msg = _( "You must specify '--external-gateway' in order " "to update the SNAT or fixed-ip values" @@ -882,7 +981,7 @@ def take_action(self, parsed_args): if ( parsed_args.qos_policy or parsed_args.no_qos_policy - ) and not parsed_args.external_gateway: + ) and not parsed_args.external_gateways: try: original_net_id = obj.external_gateway_info['network_id'] except (KeyError, TypeError): @@ -893,17 +992,21 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) else: - if not attrs.get('external_gateway_info'): + if not attrs.get('external_gateway_info') and not attrs.get( + 'external_gateways' + ): attrs['external_gateway_info'] = {} attrs['external_gateway_info']['network_id'] = original_net_id if parsed_args.qos_policy: check_qos_id = client.find_qos_policy( parsed_args.qos_policy, ignore_missing=False ).id - attrs['external_gateway_info']['qos_policy_id'] = check_qos_id + if not attrs.get('external_gateways'): + attrs['external_gateway_info']['qos_policy_id'] = check_qos_id if 'no_qos_policy' in parsed_args and parsed_args.no_qos_policy: - attrs['external_gateway_info']['qos_policy_id'] = None + if not attrs.get('external_gateways'): + attrs['external_gateway_info']['qos_policy_id'] = None attrs.update( self._parse_extra_properties(parsed_args.extra_properties) @@ -913,7 +1016,16 @@ def take_action(self, parsed_args): attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy if attrs: + external_gateways = attrs.pop('external_gateways', None) client.update_router(obj, **attrs) + # If the multiple external gateways API is intended to be used, + # do a separate API call to set external gateways. + if external_gateways: + client.update_external_gateways( + obj, + body={'router': {'external_gateways': external_gateways}}, + ) + # tags is a subresource and it needs to be updated separately. _tag.update_tags_for_set(client, obj, parsed_args) @@ -973,11 +1085,15 @@ def get_parser(self, prog_name): "(repeat option to unset multiple routes)" ), ) + # NOTE(dmitriis): This was not extended to support selective removal + # of external gateways due to a cpython bug in argparse: + # https://github.com/python/cpython/issues/53584 parser.add_argument( '--external-gateway', action='store_true', default=False, help=_("Remove external gateway information from the router"), + dest='external_gateways', ) parser.add_argument( '--qos-policy', @@ -1024,7 +1140,7 @@ def take_action(self, parsed_args): 'qos_policy_id': None, } - if parsed_args.external_gateway: + if parsed_args.external_gateways: attrs['external_gateway_info'] = {} attrs.update( @@ -1032,6 +1148,149 @@ def take_action(self, parsed_args): ) if attrs: + # If removing multiple gateways per router are supported, + # use the relevant API to remove them all. + if is_multiple_gateways_supported(client): + client.remove_external_gateways( + obj, + body={'router': {'external_gateways': {}}}, + ) + client.update_router(obj, **attrs) # tags is a subresource and it needs to be updated separately. _tag.update_tags_for_unset(client, obj, parsed_args) + + +class AddGatewayToRouter(command.ShowOne): + _description = _("Add router gateway") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'router', + metavar="", + help=_("Router to modify (name or ID)."), + ) + parser.add_argument( + metavar="", + help=_( + "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 + # common attribute parsing code. + nargs=1, + ) + parser.add_argument( + '--fixed-ip', + metavar='subnet=,ip-address=', + action=parseractions.MultiKeyValueAction, + optional_keys=['subnet', 'ip-address'], + dest='fixed_ips', + help=_( + "Desired IP and/or subnet (name or ID) " + "on external gateway: " + "subnet=,ip-address= " + "(repeat option to set multiple fixed IP addresses)." + ), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + if not is_multiple_gateways_supported(client): + msg = _( + 'The external-gateway-multihoming extension is not enabled at ' + 'the Neutron side.' + ) + raise exceptions.CommandError(msg) + + router_obj = client.find_router( + parsed_args.router, ignore_missing=False + ) + + # Get the common attributes. + attrs = _get_external_gateway_attrs( + self.app.client_manager, parsed_args + ) + + if attrs: + external_gateways = attrs.pop('external_gateways') + router_obj = client.add_external_gateways( + router_obj, + body={'router': {'external_gateways': external_gateways}}, + ) + + display_columns, columns = _get_columns(router_obj) + data = utils.get_item_properties( + router_obj, columns, formatters=_formatters + ) + return (display_columns, data) + + +class RemoveGatewayFromRouter(command.ShowOne): + _description = _("Remove router gateway") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'router', + metavar="", + help=_("Router to modify (name or ID)."), + ) + parser.add_argument( + metavar="", + help=_( + "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 + # common attribute parsing code. + nargs=1, + ) + parser.add_argument( + '--fixed-ip', + metavar='subnet=,ip-address=', + action=parseractions.MultiKeyValueAction, + optional_keys=['subnet', 'ip-address'], + dest='fixed_ips', + help=_( + "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=." + ), + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + if not is_multiple_gateways_supported(client): + msg = _( + 'The external-gateway-multihoming extension is not enabled at ' + 'the Neutron side.' + ) + raise exceptions.CommandError(msg) + + router_obj = client.find_router( + parsed_args.router, ignore_missing=False + ) + + # Get the common attributes. + attrs = _get_external_gateway_attrs( + self.app.client_manager, parsed_args + ) + if attrs: + external_gateways = attrs.pop('external_gateways') + router_obj = client.remove_external_gateways( + router_obj, + body={'router': {'external_gateways': external_gateways}}, + ) + + display_columns, columns = _get_columns(router_obj) + data = utils.get_item_properties( + router_obj, columns, formatters=_formatters + ) + return (display_columns, data) diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index fb12e3f09..33956b3ec 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -75,7 +75,7 @@ def test_add_port_required_options(self): self._router, **{ 'port_id': self._router.port, - } + }, ) self.assertIsNone(result) @@ -130,6 +130,7 @@ def test_add_subnet_required_options(self): class TestCreateRouter(TestRouter): # The new router created. new_router = network_fakes.FakeRouter.create_one_router() + _extensions = {'fake': network_fakes.create_one_extension()} columns = ( 'admin_state_up', @@ -169,7 +170,9 @@ def setUp(self): 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) + ) # Get the command object to test self.cmd = router.CreateRouter(self.app, self.namespace) @@ -228,7 +231,7 @@ def test_create_with_gateway(self): ('enable', True), ('distributed', False), ('ha', False), - ('external_gateway', _network.name), + ('external_gateways', [_network.name]), ('enable_snat', True), ('fixed_ips', [{'ip-address': '2001:db8::1'}]), ] @@ -1100,10 +1103,13 @@ class TestSetRouter(TestRouter): # The router to set. _default_route = {'destination': '10.20.20.0/24', 'nexthop': '10.20.30.1'} _network = network_fakes.create_one_network() - _subnet = network_fakes.FakeSubnet.create_one_subnet() + _subnet = network_fakes.FakeSubnet.create_one_subnet( + attrs={'network_id': _network.id} + ) _router = network_fakes.FakeRouter.create_one_router( attrs={'routes': [_default_route], 'tags': ['green', 'red']} ) + _extensions = {'fake': network_fakes.create_one_extension()} def setUp(self): super(TestSetRouter, self).setUp() @@ -1114,7 +1120,9 @@ def setUp(self): 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) + ) # Get the command object to test self.cmd = router.SetRouter(self.app, self.namespace) @@ -1312,7 +1320,7 @@ def test_set_gateway_network_only(self): self._router.id, ] verifylist = [ - ('external_gateway', self._network.id), + ('external_gateways', [self._network.id]), ('router', self._router.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1320,7 +1328,7 @@ def test_set_gateway_network_only(self): result = self.cmd.take_action(parsed_args) self.network_client.update_router.assert_called_with( self._router, - **{'external_gateway_info': {'network_id': self._network.id}} + **{'external_gateway_info': {'network_id': self._network.id}}, ) self.assertIsNone(result) @@ -1335,7 +1343,7 @@ def test_set_gateway_options_subnet_only(self): ] verifylist = [ ('router', self._router.id), - ('external_gateway', self._network.id), + ('external_gateways', [self._network.id]), ('fixed_ips', [{'subnet': "'abc'"}]), ('enable_snat', True), ] @@ -1354,7 +1362,7 @@ def test_set_gateway_options_subnet_only(self): ], 'enable_snat': True, } - } + }, ) self.assertIsNone(result) @@ -1369,7 +1377,7 @@ def test_set_gateway_option_ipaddress_only(self): ] verifylist = [ ('router', self._router.id), - ('external_gateway', self._network.id), + ('external_gateways', [self._network.id]), ('fixed_ips', [{'ip-address': "10.0.1.1"}]), ('enable_snat', True), ] @@ -1388,7 +1396,7 @@ def test_set_gateway_option_ipaddress_only(self): ], 'enable_snat': True, } - } + }, ) self.assertIsNone(result) @@ -1403,7 +1411,7 @@ def test_set_gateway_options_subnet_ipaddress(self): ] verifylist = [ ('router', self._router.id), - ('external_gateway', self._network.id), + ('external_gateways', [self._network.id]), ('fixed_ips', [{'subnet': "'abc'", 'ip-address': "10.0.1.1"}]), ('enable_snat', True), ] @@ -1423,7 +1431,7 @@ def test_set_gateway_options_subnet_ipaddress(self): ], 'enable_snat': True, } - } + }, ) self.assertIsNone(result) @@ -1468,7 +1476,7 @@ def test_set_gateway_ip_qos(self): ] verifylist = [ ('router', self._router.id), - ('external_gateway', self._network.id), + ('external_gateways', [self._network.id]), ('qos_policy', qos_policy.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1481,7 +1489,7 @@ def test_set_gateway_ip_qos(self): 'network_id': self._network.id, 'qos_policy_id': qos_policy.id, } - } + }, ) self.assertIsNone(result) @@ -1494,7 +1502,7 @@ def test_unset_gateway_ip_qos(self): ] verifylist = [ ('router', self._router.id), - ('external_gateway', self._network.id), + ('external_gateways', [self._network.id]), ('no_qos_policy', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1507,7 +1515,7 @@ def test_unset_gateway_ip_qos(self): 'network_id': self._network.id, 'qos_policy_id': None, } - } + }, ) self.assertIsNone(result) @@ -1526,7 +1534,7 @@ def test_set_unset_gateway_ip_qos(self): ] verifylist = [ ('router', self._router.id), - ('external_gateway', self._network.id), + ('external_gateways', [self._network.id]), ('qos_policy', qos_policy.id), ('no_qos_policy', True), ] @@ -1747,6 +1755,13 @@ def setUp(self): ) self.network_client.update_router = mock.Mock(return_value=None) self.network_client.set_tags = mock.Mock(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 + ) # Get the command object to test self.cmd = router.UnsetRouter(self.app, self.namespace) @@ -1799,7 +1814,7 @@ def test_unset_router_external_gateway(self): '--external-gateway', self._testrouter.name, ] - verifylist = [('external_gateway', True)] + verifylist = [('external_gateways', True)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) attrs = {'external_gateway_info': {}} @@ -1808,6 +1823,33 @@ def test_unset_router_external_gateway(self): ) self.assertIsNone(result) + def test_unset_router_external_gateway_multiple_supported(self): + # Add the relevant extension in order to test the alternate behavior. + self._extensions = { + 'external-gateway-multihoming': network_fakes.create_one_extension( + attrs={'name': 'external-gateway-multihoming'} + ) + } + arglist = [ + '--external-gateway', + self._testrouter.name, + ] + verifylist = [('external_gateways', True)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + # The removal of all gateways should be requested using the multiple + # gateways API. + self.network_client.remove_external_gateways.assert_called_once_with( + self._testrouter, body={'router': {'external_gateways': {}}} + ) + # The compatibility API will also be called in order to potentially + # unset other parameters along with external_gateway_info which + # should already be empty at that point anyway. + self.network_client.update_router.assert_called_once_with( + self._testrouter, **{'external_gateway_info': {}} + ) + self.assertIsNone(result) + def _test_unset_tags(self, with_tags=True): if with_tags: arglist = ['--tag', 'red', '--tag', 'blue'] @@ -1895,3 +1937,539 @@ def test_unset_gateway_ip_qos_no_qos(self): self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + + +class TestGatewayOps(TestRouter): + def setUp(self): + super().setUp() + self._networks = [] + self._network = network_fakes.create_one_network() + self._networks.append(self._network) + + self._router = network_fakes.FakeRouter.create_one_router( + { + 'external_gateway_info': { + 'network_id': self._network.id, + }, + } + ) + self._subnet = network_fakes.FakeSubnet.create_one_subnet( + attrs={'network_id': self._network.id} + ) + self._extensions = { + 'external-gateway-multihoming': network_fakes.create_one_extension( + attrs={'name': 'external-gateway-multihoming'} + ) + } + self.network_client.find_extension = mock.Mock( + side_effect=lambda name: self._extensions.get(name) + ) + self.network_client.find_router = mock.Mock(return_value=self._router) + + def _find_network(name_or_id, ignore_missing): + for network in self._networks: + if name_or_id in (network.id, network.name): + return network + if 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_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 + ) + + +class TestCreateMultipleGateways(TestGatewayOps): + _columns = ( + 'admin_state_up', + 'availability_zone_hints', + 'availability_zones', + 'description', + 'distributed', + 'external_gateway_info', + 'ha', + 'id', + 'name', + 'project_id', + 'routes', + 'status', + 'tags', + ) + + def setUp(self): + super().setUp() + 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._data = ( + router.AdminStateColumn(self._router.admin_state_up), + format_columns.ListColumn(self._router.availability_zone_hints), + format_columns.ListColumn(self._router.availability_zones), + self._router.description, + self._router.distributed, + router.RouterInfoColumn(self._router.external_gateway_info), + self._router.ha, + self._router.id, + self._router.name, + self._router.project_id, + router.RoutesColumn(self._router.routes), + self._router.status, + format_columns.ListColumn(self._router.tags), + ) + self.cmd = router.CreateRouter(self.app, self.namespace) + + def test_create_one_gateway(self): + arglist = [ + "--external-gateway", + self._network.id, + self._router.name, + ] + verifylist = [ + ('name', self._router.name), + ('external_gateways', [self._network.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.network_client.update_external_gateways.assert_called_with( + self._router, + body={ + 'router': { + 'external_gateways': [ + { + 'network_id': self._network.id, + } + ] + } + }, + ) + self.assertEqual(self._columns, columns) + self.assertCountEqual(self._data, data) + + def test_create_multiple_gateways(self): + arglist = [ + self._router.name, + "--external-gateway", + self._network.id, + "--external-gateway", + self._network.id, + "--external-gateway", + self._second_network.id, + '--fixed-ip', + 'subnet={},ip-address=10.0.1.1'.format(self._subnet.id), + '--fixed-ip', + 'subnet={},ip-address=10.0.1.2'.format(self._subnet.id), + ] + verifylist = [ + ('name', self._router.name), + ( + 'external_gateways', + [self._network.id, self._network.id, self._second_network.id], + ), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + # The router will not have a gateway after the create call, but it + # will be added after the update call. + self.network_client.create_router.assert_called_once_with( + **{ + 'admin_state_up': True, + 'name': self._router.name, + } + ) + self.network_client.update_external_gateways.assert_called_with( + self._router, + body={ + 'router': { + 'external_gateways': [ + { + 'network_id': self._network.id, + 'external_fixed_ips': [ + { + 'subnet_id': self._subnet.id, + 'ip_address': '10.0.1.1', + } + ], + }, + { + 'network_id': self._network.id, + 'external_fixed_ips': [ + { + 'subnet_id': self._subnet.id, + 'ip_address': '10.0.1.2', + } + ], + }, + { + 'network_id': self._second_network.id, + }, + ] + } + }, + ) + self.assertEqual(self._columns, columns) + self.assertCountEqual(self._data, data) + + +class TestUpdateMultipleGateways(TestGatewayOps): + def setUp(self): + super().setUp() + 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.cmd = router.SetRouter(self.app, self.namespace) + + def test_update_one_gateway(self): + arglist = [ + "--external-gateway", + self._network.id, + "--no-qos-policy", + self._router.name, + ] + verifylist = [ + ('router', self._router.name), + ('external_gateways', [self._network.id]), + ('no_qos_policy', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.network_client.update_external_gateways.assert_called_with( + self._router, + body={ + 'router': { + 'external_gateways': [ + {'network_id': self._network.id, 'qos_policy_id': None} + ] + } + }, + ) + self.assertIsNone(result) + + def test_update_multiple_gateways(self): + arglist = [ + self._router.name, + "--external-gateway", + self._network.id, + "--external-gateway", + self._network.id, + "--external-gateway", + self._second_network.id, + '--fixed-ip', + 'subnet={},ip-address=10.0.1.1'.format(self._subnet.id), + '--fixed-ip', + 'subnet={},ip-address=10.0.1.2'.format(self._subnet.id), + "--no-qos-policy", + ] + verifylist = [ + ('router', self._router.name), + ( + 'external_gateways', + [self._network.id, self._network.id, self._second_network.id], + ), + ('no_qos_policy', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.network_client.update_external_gateways.assert_called_with( + self._router, + body={ + 'router': { + 'external_gateways': [ + { + 'network_id': self._network.id, + 'external_fixed_ips': [ + { + 'subnet_id': self._subnet.id, + 'ip_address': '10.0.1.1', + } + ], + 'qos_policy_id': None, + }, + { + 'network_id': self._network.id, + 'external_fixed_ips': [ + { + 'subnet_id': self._subnet.id, + 'ip_address': '10.0.1.2', + } + ], + 'qos_policy_id': None, + }, + { + 'network_id': self._second_network.id, + 'qos_policy_id': None, + }, + ] + } + }, + ) + self.assertIsNone(result) + + +class TestAddGatewayRouter(TestGatewayOps): + def setUp(self): + super().setUp() + # Get the command object to test + self.cmd = router.AddGatewayToRouter(self.app, self.namespace) + + self.network_client.add_external_gateways.return_value = self._router + + def test_add_gateway_network_only(self): + arglist = [ + self._router.name, + self._network.id, + ] + verifylist = [ + ('router', self._router.name), + ('external_gateways', [self._network.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.network_client.add_external_gateways.assert_called_with( + self._router, + body={ + 'router': { + 'external_gateways': [{'network_id': self._network.id}] + } + }, + ) + self.assertEqual(result[1][result[0].index('id')], self._router.id) + + def test_add_gateway_network_fixed_ip(self): + arglist = [ + self._router.name, + self._network.id, + '--fixed-ip', + 'subnet={},ip-address=10.0.1.1'.format(self._subnet.id), + ] + verifylist = [ + ('router', self._router.name), + ('external_gateways', [self._network.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.network_client.add_external_gateways.assert_called_with( + self._router, + body={ + 'router': { + 'external_gateways': [ + { + 'network_id': self._network.id, + 'external_fixed_ips': [ + { + 'subnet_id': self._subnet.id, + 'ip_address': '10.0.1.1', + } + ], + } + ] + } + }, + ) + self.assertEqual(result[1][result[0].index('id')], self._router.id) + + def test_add_gateway_network_multiple_fixed_ips(self): + arglist = [ + self._router.name, + self._network.id, + '--fixed-ip', + 'subnet={},ip-address=10.0.1.1'.format(self._subnet.id), + '--fixed-ip', + 'subnet={},ip-address=10.0.1.2'.format(self._subnet.id), + ] + verifylist = [ + ('router', self._router.name), + ('external_gateways', [self._network.id]), + ( + 'fixed_ips', + [ + {'ip-address': '10.0.1.1', 'subnet': self._subnet.id}, + {'ip-address': '10.0.1.2', 'subnet': self._subnet.id}, + ], + ), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.network_client.add_external_gateways.assert_called_with( + self._router, + body={ + 'router': { + 'external_gateways': [ + { + 'network_id': self._network.id, + 'external_fixed_ips': [ + { + 'subnet_id': self._subnet.id, + 'ip_address': '10.0.1.1', + }, + { + 'subnet_id': self._subnet.id, + 'ip_address': '10.0.1.2', + }, + ], + } + ] + } + }, + ) + self.assertEqual(result[1][result[0].index('id')], self._router.id) + + def test_add_gateway_network_only_no_extension(self): + self._extensions = {} + arglist = [ + self._router.name, + self._network.id, + ] + verifylist = [ + ('router', self._router.name), + ('external_gateways', [self._network.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + + +class TestRemoveGatewayRouter(TestGatewayOps): + def setUp(self): + super().setUp() + # Get the command object to test + self.cmd = router.RemoveGatewayFromRouter(self.app, self.namespace) + + self.network_client.remove_external_gateways.return_value = ( + self._router + ) + + def test_remove_gateway_network_only(self): + arglist = [ + self._router.name, + self._network.id, + ] + verifylist = [ + ('router', self._router.name), + ('external_gateways', [self._network.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.network_client.remove_external_gateways.assert_called_with( + self._router, + body={ + 'router': { + 'external_gateways': [{'network_id': self._network.id}] + } + }, + ) + self.assertEqual(result[1][result[0].index('id')], self._router.id) + + def test_remove_gateway_network_fixed_ip(self): + arglist = [ + self._router.name, + self._network.id, + '--fixed-ip', + 'subnet={},ip-address=10.0.1.1'.format(self._subnet.id), + ] + verifylist = [ + ('router', self._router.name), + ('external_gateways', [self._network.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.network_client.remove_external_gateways.assert_called_with( + self._router, + body={ + 'router': { + 'external_gateways': [ + { + 'network_id': self._network.id, + 'external_fixed_ips': [ + { + 'subnet_id': self._subnet.id, + 'ip_address': '10.0.1.1', + } + ], + } + ] + } + }, + ) + self.assertEqual(result[1][result[0].index('id')], self._router.id) + + def test_remove_gateway_network_multiple_fixed_ips(self): + arglist = [ + self._router.name, + self._network.id, + '--fixed-ip', + 'subnet={},ip-address=10.0.1.1'.format(self._subnet.id), + '--fixed-ip', + 'subnet={},ip-address=10.0.1.2'.format(self._subnet.id), + ] + verifylist = [ + ('router', self._router.name), + ('external_gateways', [self._network.id]), + ( + 'fixed_ips', + [ + {'ip-address': '10.0.1.1', 'subnet': self._subnet.id}, + {'ip-address': '10.0.1.2', 'subnet': self._subnet.id}, + ], + ), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + self.network_client.remove_external_gateways.assert_called_with( + self._router, + body={ + 'router': { + 'external_gateways': [ + { + 'network_id': self._network.id, + 'external_fixed_ips': [ + { + 'subnet_id': self._subnet.id, + 'ip_address': '10.0.1.1', + }, + { + 'subnet_id': self._subnet.id, + 'ip_address': '10.0.1.2', + }, + ], + } + ] + } + }, + ) + self.assertEqual(result[1][result[0].index('id')], self._router.id) + + def test_remove_gateway_network_only_no_extension(self): + self._extensions = {} + arglist = [ + self._router.name, + self._network.id, + ] + verifylist = [ + ('router', self._router.name), + ('external_gateways', [self._network.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) diff --git a/setup.cfg b/setup.cfg index 1ea56efb2..c5b859aba 100644 --- a/setup.cfg +++ b/setup.cfg @@ -560,12 +560,14 @@ openstack.network.v2 = 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 From 7184e876a5b79ea226c75a267af2dfca7c011f62 Mon Sep 17 00:00:00 2001 From: Frode Nordahl Date: Thu, 26 Oct 2023 11:50:55 +0200 Subject: [PATCH 082/403] Add router default route BFD/ECMP options Add the `--enable-default-route-bfd`, `--disable-default-route-bfd` `--enable-default-route-ecmp` and `--disable-default-route-ecmp` options for `router create` and `router set` commands. Related-Bug: #2002687 Signed-off-by: Frode Nordahl Change-Id: Ia5a196daa87d29445dc5514dcb91544f9d470795 --- openstackclient/network/v2/router.py | 69 +++++++++++++++++++ .../tests/unit/network/v2/test_router.py | 68 ++++++++++++++++++ 2 files changed, 137 insertions(+) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 52b7d25fc..51e2aba42 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -231,9 +231,72 @@ def _get_attrs(client_manager, parsed_args): if 'flavor_id' in parsed_args and parsed_args.flavor_id is not None: attrs['flavor_id'] = parsed_args.flavor_id + for attr in ('enable_default_route_bfd', 'enable_default_route_ecmp'): + value = getattr(parsed_args, attr, None) + if value is not None: + attrs[attr] = value + return attrs +def _parser_add_bfd_ecmp_arguments(parser): + """Helper to add BFD and ECMP args for CreateRouter and SetRouter.""" + parser.add_argument( + '--enable-default-route-bfd', + dest='enable_default_route_bfd', + default=None, + action='store_true', + help=_( + "Enable BFD sessions for default routes inferred from " + "the external gateway port subnets for this router." + ), + ) + parser.add_argument( + '--disable-default-route-bfd', + dest='enable_default_route_bfd', + default=None, + action='store_false', + help=_( + "Disable BFD sessions for default routes inferred from " + "the external gateway port subnets for this router." + ), + ) + parser.add_argument( + '--enable-default-route-ecmp', + dest='enable_default_route_ecmp', + default=None, + action='store_true', + help=_( + "Add ECMP default routes if multiple are available via " + "different gateway ports." + ), + ) + parser.add_argument( + '--disable-default-route-ecmp', + dest='enable_default_route_ecmp', + default=None, + action='store_false', + help=_("Add default route only for first gateway port."), + ) + + +def _command_check_bfd_ecmp_supported(attrs, client): + """Helper to check for server side support when bfd/ecmp attrs provided. + + :raises: exceptions.CommandError + """ + if ( + 'enable_default_route_bfd' in attrs + or 'enable_default_route_ecmp' in attrs + ) and not is_multiple_gateways_supported(client): + msg = _( + 'The external-gateway-multihoming extension is not enabled at ' + 'the Neutron side, cannot use --enable-default-route-bfd or ' + '--enable-default-route-ecmp arguments.' + ) + raise exceptions.CommandError(msg) + + class AddPortToRouter(command.Command): _description = _("Add a port to a router") @@ -502,6 +565,7 @@ def get_parser(self, prog_name): metavar='', help=_("Associate the router to a flavor by ID"), ) + _parser_add_bfd_ecmp_arguments(parser) return parser @@ -527,6 +591,8 @@ def take_action(self, parsed_args): if parsed_args.enable_ndp_proxy is not None: attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy + _command_check_bfd_ecmp_supported(attrs, client) + external_gateways = attrs.pop('external_gateways', None) obj = client.create_router(**attrs) # tags cannot be set when created, so tags need to be set later. @@ -943,6 +1009,7 @@ def get_parser(self, prog_name): help=_("Remove QoS policy from router gateway IPs"), ) _tag.add_tag_option_to_parser_for_set(parser, _('router')) + _parser_add_bfd_ecmp_arguments(parser) return parser def take_action(self, parsed_args): @@ -1015,6 +1082,8 @@ def take_action(self, parsed_args): if parsed_args.enable_ndp_proxy is not None: attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy + _command_check_bfd_ecmp_supported(attrs, client) + if attrs: external_gateways = attrs.pop('external_gateways', None) client.update_router(obj, **attrs) diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 33956b3ec..256fd9b70 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -410,6 +410,74 @@ def test_create_with_flavor_id(self): self.assertEqual(self.columns, columns) 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( + attrs={'name': 'external-gateway-multihoming'} + ) + ) + arglist = [self.new_router.name, '--enable-default-route-bfd'] + verifylist = [ + ('name', self.new_router.name), + ('enable_default_route_bfd', True), + ] + 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( + name=self.new_router.name, + admin_state_up=True, + enable_default_route_bfd=True, + ) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_create_with_enable_default_route_bfd_no_extension(self): + arglist = [self.new_router.name, '--enable-default-route-bfd'] + verifylist = [ + ('name', self.new_router.name), + ('enable_default_route_bfd', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + def test_create_with_enable_default_route_ecmp(self): + self.network_client.find_extension = mock.Mock( + return_value=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), + ('enable_default_route_ecmp', True), + ] + 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( + name=self.new_router.name, + admin_state_up=True, + enable_default_route_ecmp=True, + ) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_create_with_enable_default_route_ecmp_no_extension(self): + arglist = [self.new_router.name, '--enable-default-route-ecmp'] + verifylist = [ + ('name', self.new_router.name), + ('enable_default_route_ecmp', True), + ] + 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. From da09a7c519f8c812648742aa21ac0db0e35ae6b6 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 7 Mar 2024 08:44:17 +0000 Subject: [PATCH 083/403] reno: Update master for unmaintained/victoria Update the victoria release notes configuration to build from unmaintained/victoria. Change-Id: Ic41670b3f6c9a1b78074319f445e468827590b68 --- releasenotes/source/victoria.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/victoria.rst b/releasenotes/source/victoria.rst index 4efc7b6f3..8ce933419 100644 --- a/releasenotes/source/victoria.rst +++ b/releasenotes/source/victoria.rst @@ -3,4 +3,4 @@ Victoria Series Release Notes ============================= .. release-notes:: - :branch: stable/victoria + :branch: unmaintained/victoria From ff4737c1e55dfb335c120f6645914598e53c37c0 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 7 Mar 2024 08:45:23 +0000 Subject: [PATCH 084/403] reno: Update master for unmaintained/wallaby Update the wallaby release notes configuration to build from unmaintained/wallaby. Change-Id: I925f6c38bcfe315edfed129dea76d092ec9110b9 --- releasenotes/source/wallaby.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/wallaby.rst b/releasenotes/source/wallaby.rst index d77b56599..bcf35c5f8 100644 --- a/releasenotes/source/wallaby.rst +++ b/releasenotes/source/wallaby.rst @@ -3,4 +3,4 @@ Wallaby Series Release Notes ============================ .. release-notes:: - :branch: stable/wallaby + :branch: unmaintained/wallaby From 772c27658cbc258e07125eed531dcc35a676e9e8 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Thu, 7 Mar 2024 08:46:19 +0000 Subject: [PATCH 085/403] reno: Update master for unmaintained/xena Update the xena release notes configuration to build from unmaintained/xena. Change-Id: I5ffcd8daa4822fddaf48877fc1201847ec5e9340 --- releasenotes/source/xena.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/xena.rst b/releasenotes/source/xena.rst index 1be85be3e..d19eda488 100644 --- a/releasenotes/source/xena.rst +++ b/releasenotes/source/xena.rst @@ -3,4 +3,4 @@ Xena Series Release Notes ========================= .. release-notes:: - :branch: stable/xena + :branch: unmaintained/xena From 9e4dbd1d82ee58f12cc34a397a78cad9b193d454 Mon Sep 17 00:00:00 2001 From: Youngjun Date: Tue, 12 Mar 2024 16:37:49 +0900 Subject: [PATCH 086/403] refectory: remove unreachable code Change-Id: If12a550451f5aafe3e2fb5aaa0257319ea908fe3 Signed-off-by: Youngjun --- openstackclient/compute/v2/server_migration.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openstackclient/compute/v2/server_migration.py b/openstackclient/compute/v2/server_migration.py index 307303793..18aaf407b 100644 --- a/openstackclient/compute/v2/server_migration.py +++ b/openstackclient/compute/v2/server_migration.py @@ -252,7 +252,6 @@ def _get_migration_by_uuid(compute_client, server_id, migration_uuid): for migration in compute_client.server_migrations(server_id): if migration.uuid == migration_uuid: return migration - break else: msg = _('In-progress live migration %s is not found for server %s.') raise exceptions.CommandError(msg % (migration_uuid, server_id)) From 76f234eba9ebd3046ccc9d06eb949c2ffe4b6de9 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Tue, 12 Mar 2024 19:58:33 +0100 Subject: [PATCH 087/403] Fix typo in the list of Glanceclient/OSC commands Change-Id: I9c5cf24e51fa8d4ab1428280f25408391c8556c8 --- 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 c0a2f9602..029661fcf 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -1,4 +1,4 @@ -cache-clear,cached image list,"Clear all images from cache, queue or both." +cache-clear,cached image clear,"Clear all images from cache, queue or both." cache-delete,cached image delete,Delete image from cache/caching queue. cache-list,cached image list,Get cache state. cache-queue,cached image queue,Queue image(s) for caching. From a4db11c62e63808cfbe3742cca10c864539c1cae Mon Sep 17 00:00:00 2001 From: Youngjun Date: Tue, 12 Mar 2024 16:39:01 +0900 Subject: [PATCH 088/403] refectoring: remove duplicate declaration Change-Id: I0d9e3d25bc42df56be31defaea13bbe7aaa21caa Signed-off-by: Youngjun --- openstackclient/network/v2/network.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 973878b9e..10409cade 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -61,7 +61,6 @@ def _get_columns_network(item): 'tags': 'tags', } hidden_columns = ['location', 'tenant_id'] - hidden_columns = ['location'] return utils.get_osc_show_columns_for_sdk_resource( item, column_map, hidden_columns ) From 0970dd4096ab86ce0efec68772100d29e7996517 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Thu, 14 Mar 2024 04:03:08 +0100 Subject: [PATCH 089/403] image cache clear: fix value of default target When using the "openstack image cache clear" command, the "clear_cache" method from the OpenStack SDK is used. It expects its only argument to be one of "both", "cache" or "queue". However, when passing neither "--cache" nor "--queue", it is currently passed None as a value. Fix this by specifying "both" as the default value to be passed. Change-Id: I17c6e3d435a84b4ba453845086ff3fe272b54f58 --- openstackclient/image/v2/cache.py | 2 ++ openstackclient/tests/unit/image/v2/test_cache.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openstackclient/image/v2/cache.py b/openstackclient/image/v2/cache.py index ebb4e5b20..bf27b5e63 100644 --- a/openstackclient/image/v2/cache.py +++ b/openstackclient/image/v2/cache.py @@ -194,6 +194,7 @@ def get_parser(self, prog_name): "--cache", action="store_const", const="cache", + default="both", dest="target", help=_("Clears all the cached images"), ) @@ -201,6 +202,7 @@ def get_parser(self, prog_name): "--queue", action="store_const", const="queue", + default="both", dest="target", help=_("Clears all the queued images"), ) diff --git a/openstackclient/tests/unit/image/v2/test_cache.py b/openstackclient/tests/unit/image/v2/test_cache.py index abb0b3732..3624d7867 100644 --- a/openstackclient/tests/unit/image/v2/test_cache.py +++ b/openstackclient/tests/unit/image/v2/test_cache.py @@ -184,13 +184,13 @@ def setUp(self): def test_cache_clear_no_option(self): arglist = [] - verifylist = [('target', None)] + verifylist = [('target', 'both')] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.assertIsNone( - self.image_client.clear_cache.assert_called_with(None) + self.image_client.clear_cache.assert_called_with('both') ) def test_cache_clear_queue_option(self): From cb539bab240c807c09800b5c88c52f4d7a3fd793 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Mon, 18 Mar 2024 14:04:27 +0000 Subject: [PATCH 090/403] Update master for stable/2024.1 Add file to the reno documentation build to show release notes for stable/2024.1. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/2024.1. Sem-Ver: feature Change-Id: I13bb45d30d3dff73ca48ad8a2ed8bdc982b62d86 --- releasenotes/source/2024.1.rst | 6 ++++++ releasenotes/source/index.rst | 1 + 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/2024.1.rst diff --git a/releasenotes/source/2024.1.rst b/releasenotes/source/2024.1.rst new file mode 100644 index 000000000..4977a4f1a --- /dev/null +++ b/releasenotes/source/2024.1.rst @@ -0,0 +1,6 @@ +=========================== +2024.1 Series Release Notes +=========================== + +.. release-notes:: + :branch: stable/2024.1 diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 80d8440a5..e25b52dec 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ OpenStackClient Release Notes :maxdepth: 1 unreleased + 2024.1 2023.2 2023.1 zed From c86b9d8cc7d6435e3f73be732c69c9886301dfa3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 14 Feb 2024 17:44:04 +0000 Subject: [PATCH 091/403] tests: Add identity v2, v3 FakeClientMixin This ensures we are speccing the identity proxy API, as we did previously for other services. Change-Id: I4d090bab001f9b7e1d83ca8fee9e7e1117844cd8 Signed-off-by: Stephen Finucane --- .../tests/unit/compute/v2/fakes.py | 9 +--- .../tests/unit/identity/v2_0/fakes.py | 22 +++++++++- .../tests/unit/identity/v3/fakes.py | 44 ++++++++++++++++++- openstackclient/tests/unit/image/v2/fakes.py | 15 +++---- .../tests/unit/network/v2/fakes.py | 15 +++---- openstackclient/tests/unit/volume/v1/fakes.py | 11 +++-- openstackclient/tests/unit/volume/v2/fakes.py | 10 ++--- openstackclient/tests/unit/volume/v3/fakes.py | 10 ++--- 8 files changed, 91 insertions(+), 45 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 3e9f4ad9b..5806bf434 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -178,16 +178,11 @@ class TestComputev2( network_fakes.FakeClientMixin, image_fakes.FakeClientMixin, volume_fakes.FakeClientMixin, + identity_fakes.FakeClientMixin, FakeClientMixin, utils.TestCommand, ): - def setUp(self): - super().setUp() - - self.app.client_manager.identity = identity_fakes.FakeIdentityv2Client( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, - ) + ... def create_one_aggregate(attrs=None): diff --git a/openstackclient/tests/unit/identity/v2_0/fakes.py b/openstackclient/tests/unit/identity/v2_0/fakes.py index 708f9a835..70017c58a 100644 --- a/openstackclient/tests/unit/identity/v2_0/fakes.py +++ b/openstackclient/tests/unit/identity/v2_0/fakes.py @@ -19,6 +19,7 @@ from keystoneauth1 import access from keystoneauth1 import fixture +from openstack.identity.v2 import _proxy from openstackclient.tests.unit import fakes from openstackclient.tests.unit import utils @@ -183,14 +184,31 @@ def __getattr__(self, name): raise AttributeError(name) -class TestIdentityv2(utils.TestCommand): +class FakeClientMixin: def setUp(self): - super(TestIdentityv2, self).setUp() + super().setUp() self.app.client_manager.identity = FakeIdentityv2Client( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN, ) + self.identity_client = self.app.client_manager.identity + + # TODO(stephenfin): Rename to 'identity_client' once all commands are + # migrated to SDK + self.app.client_manager.sdk_connection.identity = mock.Mock( + _proxy.Proxy + ) + self.identity_sdk_client = ( + self.app.client_manager.sdk_connection.identity + ) + + +class TestIdentityv2( + FakeClientMixin, + utils.TestCommand, +): + ... class FakeExtension(object): diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py index 0626df874..27d98b067 100644 --- a/openstackclient/tests/unit/identity/v3/fakes.py +++ b/openstackclient/tests/unit/identity/v3/fakes.py @@ -20,6 +20,7 @@ from keystoneauth1 import access from keystoneauth1 import fixture +from openstack.identity.v3 import _proxy from osc_lib.cli import format_columns from openstackclient.tests.unit import fakes @@ -666,16 +667,34 @@ def __init__(self, **kwargs): self.request_tokens.resource_class = fakes.FakeResource(None, {}) -class TestIdentityv3(utils.TestCommand): +class FakeClientMixin: def setUp(self): - super(TestIdentityv3, self).setUp() + super().setUp() self.app.client_manager.identity = FakeIdentityv3Client( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN, ) + self.identity_client = self.app.client_manager.identity + + # TODO(stephenfin): Rename to 'identity_client' once all commands are + # migrated to SDK + self.app.client_manager.sdk_connection.identity = mock.Mock( + _proxy.Proxy + ) + self.identity_sdk_client = ( + self.app.client_manager.sdk_connection.identity + ) +class TestIdentityv3( + FakeClientMixin, + utils.TestCommand, +): + ... + + +# We don't use FakeClientMixin since we want a different fake legacy client class TestFederatedIdentity(utils.TestCommand): def setUp(self): super(TestFederatedIdentity, self).setUp() @@ -683,8 +702,19 @@ def setUp(self): self.app.client_manager.identity = FakeFederatedClient( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN ) + self.identity_client = self.app.client_manager.identity + + # TODO(stephenfin): Rename to 'identity_client' once all commands are + # migrated to SDK + self.app.client_manager.sdk_connection.identity = mock.Mock( + _proxy.Proxy + ) + self.identity_sdk_client = ( + self.app.client_manager.sdk_connection.identity + ) +# We don't use FakeClientMixin since we want a different fake legacy client class TestOAuth1(utils.TestCommand): def setUp(self): super(TestOAuth1, self).setUp() @@ -692,6 +722,16 @@ def setUp(self): self.app.client_manager.identity = FakeOAuth1Client( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN ) + self.identity_client = self.app.client_manager.identity + + # TODO(stephenfin): Rename to 'identity_client' once all commands are + # migrated to SDK + self.app.client_manager.sdk_connection.identity = mock.Mock( + _proxy.Proxy + ) + self.identity_sdk_client = ( + self.app.client_manager.sdk_connection.identity + ) class FakeProject(object): diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py index 6565495fb..e152da972 100644 --- a/openstackclient/tests/unit/image/v2/fakes.py +++ b/openstackclient/tests/unit/image/v2/fakes.py @@ -27,7 +27,6 @@ from openstack.image.v2 import service_info as _service_info from openstack.image.v2 import task -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit import utils @@ -40,14 +39,12 @@ def setUp(self): self.image_client = self.app.client_manager.image -class TestImagev2(FakeClientMixin, utils.TestCommand): - def setUp(self): - super().setUp() - - self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, - ) +class TestImagev2( + identity_fakes.FakeClientMixin, + FakeClientMixin, + utils.TestCommand, +): + ... def create_one_image(attrs=None): diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index ac02d96ad..fdd41b0a8 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -38,7 +38,7 @@ from openstack.network.v2 import trunk as _trunk from openstackclient.tests.unit import fakes -from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit import utils @@ -101,19 +101,16 @@ def setUp(self): self.network_client = self.app.client_manager.network -class TestNetworkV2(FakeClientMixin, utils.TestCommand): +class TestNetworkV2( + identity_fakes.FakeClientMixin, + FakeClientMixin, + utils.TestCommand, +): def setUp(self): super().setUp() self.namespace = argparse.Namespace() - self.app.client_manager.identity = ( - identity_fakes_v3.FakeIdentityv3Client( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, - ) - ) - def create_one_extension(attrs=None): """Create a fake extension. diff --git a/openstackclient/tests/unit/volume/v1/fakes.py b/openstackclient/tests/unit/volume/v1/fakes.py index b9d9bf264..9b4dc126d 100644 --- a/openstackclient/tests/unit/volume/v1/fakes.py +++ b/openstackclient/tests/unit/volume/v1/fakes.py @@ -64,15 +64,14 @@ def setUp(self): self.volume_client = self.app.client_manager.volume -class TestVolumev1(FakeClientMixin, utils.TestCommand): +class TestVolumev1( + identity_fakes.FakeClientMixin, + FakeClientMixin, + utils.TestCommand, +): def setUp(self): super().setUp() - self.app.client_manager.identity = identity_fakes.FakeIdentityv2Client( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, - ) - # 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) diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index 4d04fcdbf..00e7618c7 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -108,14 +108,14 @@ def setUp(self): self.volume_sdk_client = self.app.client_manager.sdk_connection.volume -class TestVolume(FakeClientMixin, utils.TestCommand): +class TestVolume( + identity_fakes.FakeClientMixin, + FakeClientMixin, + utils.TestCommand, +): def setUp(self): super().setUp() - self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client( - endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN - ) - # avoid circular imports by defining this manually rather than using # openstackclient.tests.unit.image.v2.fakes.FakeClientMixin self.app.client_manager.image = mock.Mock(spec=image_v2_proxy.Proxy) diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index b1366a613..8c786bc72 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -82,14 +82,14 @@ def setUp(self): self.volume_sdk_client = self.app.client_manager.sdk_connection.volume -class TestVolume(FakeClientMixin, utils.TestCommand): +class TestVolume( + identity_fakes.FakeClientMixin, + FakeClientMixin, + utils.TestCommand, +): def setUp(self): super().setUp() - self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client( - endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN - ) - # avoid circular imports from openstackclient.tests.unit.compute.v2 import ( fakes as compute_fakes, From 680e3e301137c832568a93a05f99bd3c1ced8ba2 Mon Sep 17 00:00:00 2001 From: ArtofBugs Date: Wed, 14 Feb 2024 09:27:13 -0800 Subject: [PATCH 092/403] identity: Migrate 'user' commands to SDK Change-Id: I06f3848812bce60c65909f1311f36b70eba427d4 --- openstackclient/identity/v3/user.py | 206 +++--- .../tests/functional/identity/v3/common.py | 1 - .../tests/unit/identity/v3/test_user.py | 610 +++++++++--------- 3 files changed, 425 insertions(+), 392 deletions(-) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index dfc66b3de..6837340c3 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -18,7 +18,7 @@ import copy 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 @@ -30,6 +30,33 @@ LOG = logging.getLogger(__name__) +def _format_user(user): + columns = ( + 'default_project_id', + 'domain_id', + 'email', + 'is_enabled', + 'id', + 'name', + 'description', + 'password_expires_at', + ) + column_headers = ( + 'default_project_id', + 'domain_id', + 'email', + 'enabled', + 'id', + 'name', + 'description', + 'password_expires_at', + ) + return ( + column_headers, + utils.get_item_properties(user, columns), + ) + + def _get_options_for_user(identity_client, parsed_args): options = {} if parsed_args.ignore_lockout_failure_attempts: @@ -220,25 +247,26 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - - project_id = None - if parsed_args.project: - project_id = common.find_project( - identity_client, - parsed_args.project, - parsed_args.project_domain, - ).id + 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 + domain_id = identity_client.find_domain( + name_or_id=parsed_args.domain, + ignore_missing=False, ).id - enabled = True + project_id = None + if parsed_args.project: + project_id = identity_client.find_project( + name_or_id=parsed_args.project, + ignore_missing=False, + domain_id=domain_id, + ).id + + is_enabled = True if parsed_args.disable: - enabled = False + is_enabled = False if parsed_args.password_prompt: parsed_args.password = utils.get_password(self.app.stdin) @@ -252,29 +280,28 @@ def take_action(self, parsed_args): options = _get_options_for_user(identity_client, parsed_args) try: - user = identity_client.users.create( + 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, - domain=domain_id, - default_project=project_id, password=parsed_args.password, - email=parsed_args.email, - description=parsed_args.description, - enabled=enabled, options=options, ) - except ks_exc.Conflict: + except sdk_exc.ConflictException: if parsed_args.or_show: - user = utils.find_resource( - identity_client.users, - parsed_args.name, + user = identity_client.find_user( + name_or_id=parsed_args.name, domain_id=domain_id, + ignore_missing=False, ) LOG.info(_('Returning existing user %s'), user.name) else: raise - user._info.pop('links') - return zip(*sorted(user._info.items())) + return _format_user(user) class DeleteUser(command.Command): @@ -296,21 +323,28 @@ 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) + domain = identity_client.find_domain( + name_or_id=parsed_args.domain, + ignore_missing=True, + ) errors = 0 for user in parsed_args.users: try: if domain is not None: - user_obj = utils.find_resource( - identity_client.users, user, domain_id=domain.id + user_obj = identity_client.find_user( + name_or_id=user, + domain_id=domain.id, + ignore_missing=False, ) else: - user_obj = utils.find_resource(identity_client.users, user) - identity_client.users.delete(user_obj.id) + user_obj = identity_client.find_user( + name_or_id=user, ignore_missing=False + ) + identity_client.delete_user(user_obj.id, ignore_missing=False) except Exception as e: errors += 1 LOG.error( @@ -360,32 +394,36 @@ 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 = identity_client.find_domain( + name_or_id=parsed_args.domain, + ).id group = None if parsed_args.group: - group = common.find_group( - identity_client, parsed_args.group, parsed_args.domain + group = identity_client.find_group( + name_or_id=parsed_args.group, + domain_id=parsed_args.domain, + ignore_missing=False, ).id if parsed_args.project: if domain is not None: - project = utils.find_resource( - identity_client.projects, - parsed_args.project, + project = identity_client.find_project( + name_or_id=parsed_args.project, domain_id=domain, + ignore_missing=False, ).id else: - project = utils.find_resource( - identity_client.projects, - parsed_args.project, + project = identity_client.find_project( + name_or_id=parsed_args.project, + ignore_missing=False, ).id - assignments = identity_client.role_assignments.list( + assignments = identity_client.role_assignments_filter( project=project ) @@ -394,19 +432,19 @@ def take_action(self, parsed_args): # are looking for any role, let's just track unique user IDs. user_ids = set() for assignment in assignments: - if hasattr(assignment, 'user'): + if assignment.user: user_ids.add(assignment.user['id']) # NOTE(stevemar): Call find_resource once we have unique IDs, so # it's fewer trips to the Identity API, then collect the data. data = [] for user_id in user_ids: - user = utils.find_resource(identity_client.users, user_id) + user = identity_client.find_user(user_id, ignore_missing=False) data.append(user) else: - data = identity_client.users.list( - domain=domain, + data = identity_client.users( + domain_id=domain, group=group, ) @@ -419,11 +457,12 @@ def take_action(self, parsed_args): 'Domain Id', 'Description', 'Email', - 'Enabled', + 'Is Enabled', ] column_headers = copy.deepcopy(columns) column_headers[2] = 'Project' column_headers[3] = 'Domain' + column_headers[6] = 'Enabled' else: columns = ['ID', 'Name'] column_headers = columns @@ -507,7 +546,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 parsed_args.password_prompt: parsed_args.password = utils.get_password(self.app.stdin) @@ -524,14 +563,19 @@ def take_action(self, parsed_args): identity_client, 'user', parsed_args.user, parsed_args.domain ) if parsed_args.domain: - domain = common.find_domain(identity_client, parsed_args.domain) - user = utils.find_resource( - identity_client.users, user_str, domain_id=domain.id + domain = identity_client.find_domain( + name_or_id=parsed_args.domain, + ignore_missing=False, + ) + user = identity_client.find_user( + name_or_id=user_str, + domain_id=domain.id, + ignore_missing=False, ) else: - user = utils.find_resource( - identity_client.users, - parsed_args.user, + user = identity_client.find_user( + name_or_id=parsed_args.user, + ignore_missing=False, ) kwargs = {} @@ -544,23 +588,27 @@ def take_action(self, parsed_args): if parsed_args.description: kwargs['description'] = parsed_args.description if parsed_args.project: - project_id = common.find_project( - identity_client, - parsed_args.project, - parsed_args.project_domain, + project_domain_id = identity_client.find_domain( + name_or_id=parsed_args.project_domain, + ignore_missing=False, ).id - kwargs['default_project'] = project_id - kwargs['enabled'] = user.enabled + project_id = identity_client.find_project( + name_or_id=parsed_args.project, + ignore_missing=False, + domain_id=project_domain_id, + ).id + kwargs['default_project_id'] = project_id + kwargs['is_enabled'] = user.is_enabled if parsed_args.enable: - kwargs['enabled'] = True + kwargs['is_enabled'] = True if parsed_args.disable: - kwargs['enabled'] = False + kwargs['is_enabled'] = False options = _get_options_for_user(identity_client, parsed_args) if options: kwargs['options'] = options - identity_client.users.update(user.id, **kwargs) + identity_client.update_user(user=user, **kwargs) class SetPasswordUser(command.Command): @@ -583,7 +631,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 # FIXME(gyee): there are two scenarios: # @@ -625,7 +673,9 @@ def take_action(self, parsed_args): ) ) - identity_client.users.update_password(current_password, password) + identity_client.update_user( + current_password=current_password, password=password + ) class ShowUser(command.ShowOne): @@ -646,18 +696,28 @@ 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_str = common._get_token_resource( identity_client, 'user', parsed_args.user, parsed_args.domain ) + + domain = None if parsed_args.domain: - domain = common.find_domain(identity_client, parsed_args.domain) - user = utils.find_resource( - identity_client.users, user_str, domain_id=domain.id + domain = identity_client.find_domain( + name_or_id=parsed_args.domain, + ignore_missing=True, + ) + if domain: + user = identity_client.find_user( + name_or_id=user_str, + domain_id=domain.id, + ignore_missing=False, ) else: - user = utils.find_resource(identity_client.users, user_str) + user = identity_client.find_user( + name_or_id=user_str, + ignore_missing=False, + ) - user._info.pop('links') - return zip(*sorted(user._info.items())) + return _format_user(user) diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py index 67eccddda..b3d55e654 100644 --- a/openstackclient/tests/functional/identity/v3/common.py +++ b/openstackclient/tests/functional/identity/v3/common.py @@ -33,7 +33,6 @@ class IdentityTests(base.TestCase): 'enabled', 'id', 'name', - 'name', 'domain_id', 'default_project_id', 'description', diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index 15ebd67ed..822abd682 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -17,43 +17,23 @@ from unittest import mock from osc_lib import exceptions -from osc_lib import utils + +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_assignment as _role_assignment +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes from openstackclient.identity import common from openstackclient.identity.v3 import user from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestUser(identity_fakes.TestIdentityv3): - def setUp(self): - super(TestUser, self).setUp() - - # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains - self.domains_mock.reset_mock() - - # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects - self.projects_mock.reset_mock() - - # Get a shortcut to the GroupManager Mock - self.groups_mock = self.app.client_manager.identity.groups - self.groups_mock.reset_mock() - - # Get a shortcut to the UserManager Mock - self.users_mock = self.app.client_manager.identity.users - self.users_mock.reset_mock() - - # Shortcut for RoleAssignmentManager Mock - self.role_assignments_mock = ( - self.app.client_manager.identity.role_assignments - ) - self.role_assignments_mock.reset_mock() - - -class TestUserCreate(TestUser): - domain = identity_fakes.FakeDomain.create_one_domain() - project = identity_fakes.FakeProject.create_one_project() +class TestUserCreate(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + project = sdk_fakes.generate_fake_resource(_project.Project) columns = ( 'default_project_id', @@ -62,16 +42,17 @@ class TestUserCreate(TestUser): 'enabled', 'id', 'name', + 'description', + 'password_expires_at', ) def setUp(self): - super(TestUserCreate, self).setUp() + super().setUp() - self.user = identity_fakes.FakeUser.create_one_user( - attrs={ - 'domain_id': self.domain.id, - 'default_project_id': self.project.id, - } + self.user = sdk_fakes.generate_fake_resource( + resource_type=_user.User, + domain_id=self.domain.id, + default_project_id=self.project.id, ) self.datalist = ( self.project.id, @@ -80,11 +61,13 @@ def setUp(self): True, self.user.id, self.user.name, + self.user.description, + self.user.password_expires_at, ) - self.domains_mock.get.return_value = self.domain - self.projects_mock.get.return_value = self.project - self.users_mock.create.return_value = self.user + self.identity_sdk_client.find_domain.return_value = self.domain + self.identity_sdk_client.find_project.return_value = self.project + self.identity_sdk_client.create_user.return_value = self.user # Get the command object to test self.cmd = user.CreateUser(self.app, None) @@ -108,18 +91,15 @@ def test_user_create_no_options(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, 'options': {}, - 'enabled': True, + 'is_enabled': True, 'password': None, } - - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -147,17 +127,16 @@ def test_user_create_password(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, 'options': {}, - 'enabled': True, + 'is_enabled': True, 'password': 'secret', } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) + self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -186,17 +165,15 @@ def test_user_create_password_prompt(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, 'options': {}, - 'enabled': True, + 'is_enabled': True, 'password': 'abc123', } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -223,17 +200,15 @@ def test_user_create_email(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': 'barney@example.com', - 'enabled': True, + 'is_enabled': True, 'options': {}, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -260,17 +235,15 @@ def test_user_create_project(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': self.project.id, + 'default_project_id': self.project.id, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, - 'enabled': True, + 'is_enabled': True, 'options': {}, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( @@ -280,6 +253,8 @@ def test_user_create_project(self): True, self.user.id, self.user.name, + self.user.description, + self.user.password_expires_at, ) self.assertEqual(datalist, data) @@ -308,17 +283,15 @@ def test_user_create_project_domain(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': self.project.id, + 'default_project_id': self.project.id, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, 'options': {}, - 'enabled': True, + 'is_enabled': True, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) datalist = ( @@ -328,6 +301,8 @@ def test_user_create_project_domain(self): True, self.user.id, self.user.name, + self.user.description, + self.user.password_expires_at, ) self.assertEqual(datalist, data) @@ -353,17 +328,15 @@ def test_user_create_domain(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': self.domain.id, + 'domain_id': self.domain.id, 'email': None, 'options': {}, - 'enabled': True, + 'is_enabled': True, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -388,17 +361,15 @@ def test_user_create_enable(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, 'options': {}, - 'enabled': True, + 'is_enabled': True, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -423,16 +394,16 @@ def test_user_create_disable(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, 'options': {}, - 'enabled': False, + 'is_enabled': False, 'password': None, } - # users.create(name=, password, email, tenant_id=None, enabled=True) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) + self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -457,17 +428,15 @@ def test_user_create_ignore_lockout_failure_attempts(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, - 'enabled': True, + 'is_enabled': True, 'options': {'ignore_lockout_failure_attempts': True}, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -493,17 +462,15 @@ def test_user_create_no_ignore_lockout_failure_attempts(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, - 'enabled': True, + 'is_enabled': True, 'options': {'ignore_lockout_failure_attempts': False}, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -529,17 +496,15 @@ def test_user_create_ignore_password_expiry(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, - 'enabled': True, + 'is_enabled': True, 'options': {'ignore_password_expiry': True}, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -565,17 +530,15 @@ def test_user_create_no_ignore_password_expiry(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, - 'enabled': True, + 'is_enabled': True, 'options': {'ignore_password_expiry': False}, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -601,17 +564,15 @@ def test_user_create_ignore_change_password_upon_first_use(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, - 'enabled': True, + 'is_enabled': True, 'options': {'ignore_change_password_upon_first_use': True}, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -637,17 +598,15 @@ def test_user_create_no_ignore_change_password_upon_first_use(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, - 'enabled': True, + 'is_enabled': True, 'options': {'ignore_change_password_upon_first_use': False}, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -673,17 +632,15 @@ def test_user_create_enables_lock_password(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, - 'enabled': True, + 'is_enabled': True, 'options': {'lock_password': True}, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -709,17 +666,15 @@ def test_user_create_disables_lock_password(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, - 'enabled': True, + 'is_enabled': True, 'options': {'lock_password': False}, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -745,17 +700,15 @@ def test_user_create_enable_multi_factor_auth(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, - 'enabled': True, + 'is_enabled': True, 'options': {'multi_factor_auth_enabled': True}, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -781,17 +734,15 @@ def test_user_create_disable_multi_factor_auth(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, - 'enabled': True, + 'is_enabled': True, 'options': {'multi_factor_auth_enabled': False}, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -823,19 +774,17 @@ def test_user_create_option_with_multi_factor_auth_rule(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, - 'enabled': True, + 'is_enabled': True, 'options': { 'multi_factor_auth_rules': [["password", "totp"], ["password"]] }, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) @@ -866,11 +815,11 @@ def test_user_create_with_multiple_options(self): # Set expected values kwargs = { 'name': self.user.name, - 'default_project': None, + 'default_project_id': None, 'description': None, - 'domain': None, + 'domain_id': None, 'email': None, - 'enabled': True, + 'is_enabled': True, 'options': { 'ignore_password_expiry': True, 'multi_factor_auth_enabled': False, @@ -878,23 +827,20 @@ def test_user_create_with_multiple_options(self): }, 'password': None, } - # UserManager.create(name=, domain=, project=, password=, email=, - # description=, enabled=, default_project=) - self.users_mock.create.assert_called_with(**kwargs) + self.identity_sdk_client.create_user.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) -class TestUserDelete(TestUser): - user = identity_fakes.FakeUser.create_one_user() +class TestUserDelete(identity_fakes.TestIdentityv3): + user = sdk_fakes.generate_fake_resource(_user.User) def setUp(self): - super(TestUserDelete, self).setUp() + super().setUp() - # This is the return value for utils.find_resource() - self.users_mock.get.return_value = self.user - self.users_mock.delete.return_value = None + self.identity_sdk_client.find_user.return_value = self.user + self.identity_sdk_client.delete_user.return_value = None # Get the command object to test self.cmd = user.DeleteUser(self.app, None) @@ -910,14 +856,18 @@ def test_user_delete_no_options(self): result = self.cmd.take_action(parsed_args) - self.users_mock.delete.assert_called_with( + self.identity_sdk_client.delete_user.assert_called_with( self.user.id, + ignore_missing=False, ) self.assertIsNone(result) - @mock.patch.object(utils, 'find_resource') + @mock.patch.object(_user.User, 'find') def test_delete_multi_users_with_exception(self, find_mock): - find_mock.side_effect = [self.user, exceptions.CommandError] + self.identity_sdk_client.find_user.side_effect = [ + self.user, + sdk_exc.ResourceNotFound, + ] arglist = [ self.user.id, 'unexist_user', @@ -933,24 +883,30 @@ def test_delete_multi_users_with_exception(self, find_mock): except exceptions.CommandError as e: self.assertEqual('1 of 2 users failed to delete.', str(e)) - find_mock.assert_any_call(self.users_mock, self.user.id) - find_mock.assert_any_call(self.users_mock, 'unexist_user') + self.identity_sdk_client.find_user.assert_has_calls( + [ + mock.call(name_or_id=self.user.id, ignore_missing=False), + mock.call(name_or_id='unexist_user', ignore_missing=False), + ] + ) - self.assertEqual(2, find_mock.call_count) - self.users_mock.delete.assert_called_once_with(self.user.id) + self.assertEqual(2, self.identity_sdk_client.find_user.call_count) + self.identity_sdk_client.delete_user.assert_called_once_with( + self.user.id, ignore_missing=False + ) -class TestUserList(TestUser): - domain = identity_fakes.FakeDomain.create_one_domain() - project = identity_fakes.FakeProject.create_one_project() - user = identity_fakes.FakeUser.create_one_user( - attrs={'domain_id': domain.id, 'default_project_id': project.id} +class TestUserList(identity_fakes.TestIdentityv3): + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + project = sdk_fakes.generate_fake_resource(_project.Project) + user = sdk_fakes.generate_fake_resource( + resource_type=_user.User, + domain_id=domain.id, + default_project_id=project.id, ) - group = identity_fakes.FakeGroup.create_one_group() - role_assignment = ( - identity_fakes.FakeRoleAssignment.create_one_role_assignment( - attrs={'user': {'id': user.id}} - ) + group = sdk_fakes.generate_fake_resource(_group.Group) + role_assignment = sdk_fakes.generate_fake_resource( + resource_type=_role_assignment.RoleAssignment, user={'id': user.id} ) columns = ['ID', 'Name'] @@ -964,12 +920,14 @@ class TestUserList(TestUser): def setUp(self): super(TestUserList, self).setUp() - self.users_mock.get.return_value = self.user - self.users_mock.list.return_value = [self.user] - self.domains_mock.get.return_value = self.domain - self.groups_mock.get.return_value = self.group - self.projects_mock.get.return_value = self.project - self.role_assignments_mock.list.return_value = [self.role_assignment] + self.identity_sdk_client.find_user.return_value = self.user + self.identity_sdk_client.users.return_value = [self.user] + 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.role_assignment + ] # Get the command object to test self.cmd = user.ListUser(self.app, None) @@ -986,11 +944,11 @@ def test_user_list_no_options(self): # Set expected values kwargs = { - 'domain': None, + 'domain_id': None, 'group': None, } - self.users_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.users.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) @@ -1012,11 +970,11 @@ def test_user_list_domain(self): # Set expected values kwargs = { - 'domain': self.domain.id, + 'domain_id': self.domain.id, 'group': None, } - self.users_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.users.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) @@ -1038,11 +996,11 @@ def test_user_list_group(self): # Set expected values kwargs = { - 'domain': None, + 'domain_id': None, 'group': self.group.id, } - self.users_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.users.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) @@ -1063,11 +1021,11 @@ def test_user_list_long(self): # Set expected values kwargs = { - 'domain': None, + 'domain_id': None, 'group': None, } - self.users_mock.list.assert_called_with(**kwargs) + self.identity_sdk_client.users.assert_called_with(**kwargs) collist = [ 'ID', @@ -1085,7 +1043,7 @@ def test_user_list_long(self): self.user.name, self.project.id, self.domain.id, - '', + self.user.description, self.user.email, True, ), @@ -1111,29 +1069,32 @@ def test_user_list_project(self): 'project': self.project.id, } - self.role_assignments_mock.list.assert_called_with(**kwargs) - self.users_mock.get.assert_called_with(self.user.id) + self.identity_sdk_client.role_assignments_filter.assert_called_with( + **kwargs + ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) -class TestUserSet(TestUser): - project = identity_fakes.FakeProject.create_one_project() - domain = identity_fakes.FakeDomain.create_one_domain() - user = identity_fakes.FakeUser.create_one_user( - attrs={'default_project_id': project.id} +class TestUserSet(identity_fakes.TestIdentityv3): + project = sdk_fakes.generate_fake_resource(_project.Project) + domain = sdk_fakes.generate_fake_resource(_domain.Domain) + user = sdk_fakes.generate_fake_resource( + resource_type=_user.User, default_project_id=project.id ) - user2 = identity_fakes.FakeUser.create_one_user( - attrs={'default_project_id': project.id, 'domain_id': domain.id} + user2 = sdk_fakes.generate_fake_resource( + resource_type=_user.User, + default_project_id=project.id, + domain_id=domain.id, ) def setUp(self): super(TestUserSet, self).setUp() - self.projects_mock.get.return_value = self.project - self.users_mock.get.return_value = self.user - self.users_mock.update.return_value = self.user + self.identity_sdk_client.find_project.return_value = self.project + self.identity_sdk_client.find_user.return_value = self.user + self.identity_sdk_client.update_user.return_value = self.user # Get the command object to test self.cmd = user.SetUser(self.app, None) @@ -1178,12 +1139,12 @@ def test_user_set_name(self): # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'name': 'qwerty', } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_specify_domain(self): @@ -1208,9 +1169,11 @@ def test_user_set_specify_domain(self): result = self.cmd.take_action(parsed_args) - kwargs = {'enabled': True, 'name': 'qwerty'} + kwargs = {'is_enabled': True, 'name': 'qwerty'} - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_password(self): @@ -1235,12 +1198,12 @@ def test_user_set_password(self): # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'password': 'secret', } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_password_prompt(self): @@ -1267,12 +1230,12 @@ def test_user_set_password_prompt(self): # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'password': 'abc123', } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_email(self): @@ -1296,12 +1259,12 @@ def test_user_set_email(self): # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'email': 'barney@example.com', } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_project(self): @@ -1325,12 +1288,12 @@ def test_user_set_project(self): # Set expected values kwargs = { - 'enabled': True, - 'default_project': self.project.id, + 'is_enabled': True, + 'default_project_id': self.project.id, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_project_domain(self): @@ -1357,12 +1320,12 @@ def test_user_set_project_domain(self): # Set expected values kwargs = { - 'enabled': True, - 'default_project': self.project.id, + 'is_enabled': True, + 'default_project_id': self.project.id, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_enable(self): @@ -1385,11 +1348,11 @@ def test_user_set_enable(self): # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_disable(self): @@ -1412,11 +1375,11 @@ def test_user_set_disable(self): # Set expected values kwargs = { - 'enabled': False, + 'is_enabled': False, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_ignore_lockout_failure_attempts(self): @@ -1439,12 +1402,12 @@ def test_user_set_ignore_lockout_failure_attempts(self): result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'options': {'ignore_lockout_failure_attempts': True}, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_no_ignore_lockout_failure_attempts(self): @@ -1467,12 +1430,12 @@ def test_user_set_no_ignore_lockout_failure_attempts(self): result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'options': {'ignore_lockout_failure_attempts': False}, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_ignore_password_expiry(self): @@ -1495,12 +1458,12 @@ def test_user_set_ignore_password_expiry(self): result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'options': {'ignore_password_expiry': True}, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_no_ignore_password_expiry(self): @@ -1523,12 +1486,12 @@ def test_user_set_no_ignore_password_expiry(self): result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'options': {'ignore_password_expiry': False}, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_ignore_change_password_upon_first_use(self): @@ -1551,12 +1514,12 @@ def test_user_set_ignore_change_password_upon_first_use(self): result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'options': {'ignore_change_password_upon_first_use': True}, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_no_ignore_change_password_upon_first_use(self): @@ -1579,12 +1542,12 @@ def test_user_set_no_ignore_change_password_upon_first_use(self): result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'options': {'ignore_change_password_upon_first_use': False}, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_enable_lock_password(self): @@ -1607,12 +1570,12 @@ def test_user_set_enable_lock_password(self): result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'options': {'lock_password': True}, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_disable_lock_password(self): @@ -1635,12 +1598,12 @@ def test_user_set_disable_lock_password(self): result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'options': {'lock_password': False}, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_enable_multi_factor_auth(self): @@ -1663,12 +1626,12 @@ def test_user_set_enable_multi_factor_auth(self): result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'options': {'multi_factor_auth_enabled': True}, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_disable_multi_factor_auth(self): @@ -1691,12 +1654,12 @@ def test_user_set_disable_multi_factor_auth(self): result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'options': {'multi_factor_auth_enabled': False}, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_option_multi_factor_auth_rule(self): @@ -1720,13 +1683,13 @@ def test_user_set_option_multi_factor_auth_rule(self): result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'options': {'multi_factor_auth_rules': [["password", "totp"]]}, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) def test_user_set_with_multiple_options(self): @@ -1754,7 +1717,7 @@ def test_user_set_with_multiple_options(self): result = self.cmd.take_action(parsed_args) # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, 'options': { 'ignore_password_expiry': True, 'multi_factor_auth_enabled': True, @@ -1762,13 +1725,13 @@ def test_user_set_with_multiple_options(self): }, } - # UserManager.update(user, name=, domain=, project=, password=, - # email=, description=, enabled=, default_project=) - self.users_mock.update.assert_called_with(self.user.id, **kwargs) + self.identity_sdk_client.update_user.assert_called_with( + user=self.user, **kwargs + ) self.assertIsNone(result) -class TestUserSetPassword(TestUser): +class TestUserSetPassword(identity_fakes.TestIdentityv3): def setUp(self): super(TestUserSetPassword, self).setUp() self.cmd = user.SetPasswordUser(self.app, None) @@ -1796,8 +1759,8 @@ def test_user_password_change(self): with self._mock_get_password(current_pass): result = self.cmd.take_action(parsed_args) - self.users_mock.update_password.assert_called_with( - current_pass, new_pass + self.identity_sdk_client.update_user.assert_called_with( + current_password=current_pass, password=new_pass ) self.assertIsNone(result) @@ -1810,8 +1773,8 @@ def test_user_create_password_prompt(self): with self._mock_get_password(current_pass, new_pass): result = self.cmd.take_action(parsed_args) - self.users_mock.update_password.assert_called_with( - current_pass, new_pass + self.identity_sdk_client.update_user.assert_called_with( + current_password=current_pass, password=new_pass ) self.assertIsNone(result) @@ -1832,19 +1795,19 @@ def test_user_password_change_no_prompt(self): result = self.cmd.take_action(parsed_args) - self.users_mock.update_password.assert_called_with( - current_pass, new_pass + self.identity_sdk_client.update_user.assert_called_with( + current_password=current_pass, password=new_pass ) self.assertIsNone(result) -class TestUserShow(TestUser): - user = identity_fakes.FakeUser.create_one_user() +class TestUserShow(identity_fakes.TestIdentityv3): + user = sdk_fakes.generate_fake_resource(_user.User) def setUp(self): super(TestUserShow, self).setUp() - self.users_mock.get.return_value = self.user + self.identity_sdk_client.find_user.return_value = self.user # Get the command object to test self.cmd = user.ShowUser(self.app, None) @@ -1854,7 +1817,7 @@ def setUp(self): self.app.client_manager.identity.tokens.get_token_data.return_value = { 'token': { 'user': { - 'domain': {'id': self.user.domain_id}, + 'domain_id': {'id': self.user.domain_id}, 'id': self.user.id, 'name': self.user.name, } @@ -1875,7 +1838,9 @@ def test_user_show(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.users_mock.get.assert_called_with(self.user.id) + self.identity_sdk_client.find_user.assert_called_with( + name_or_id=self.user.id, ignore_missing=False + ) collist = ( 'default_project_id', @@ -1884,6 +1849,8 @@ def test_user_show(self): 'enabled', 'id', 'name', + 'description', + 'password_expires_at', ) self.assertEqual(collist, columns) datalist = ( @@ -1893,14 +1860,15 @@ def test_user_show(self): True, self.user.id, self.user.name, + self.user.description, + self.user.password_expires_at, ) self.assertEqual(datalist, data) def test_user_show_with_domain(self): - user = identity_fakes.FakeUser.create_one_user( - {"name": self.user.name} + user = sdk_fakes.generate_fake_resource( + resource_type=_user.User, name=self.user.name ) - identity_client = self.app.client_manager.identity arglist = [ "--domain", @@ -1914,9 +1882,12 @@ def test_user_show_with_domain(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) user_str = common._get_token_resource( - identity_client, 'user', parsed_args.user, parsed_args.domain + self.identity_sdk_client, + 'user', + parsed_args.user, + parsed_args.domain, ) - self.assertEqual(self.user.id, user_str) + self.assertEqual(self.user.name, user_str) arglist = [ "--domain", @@ -1930,6 +1901,9 @@ def test_user_show_with_domain(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) user_str = common._get_token_resource( - identity_client, 'user', parsed_args.user, parsed_args.domain + self.identity_sdk_client, + 'user', + parsed_args.user, + parsed_args.domain, ) self.assertEqual(user.name, user_str) From a45a3642b5ecb1ce1593b5902957328034b52c01 Mon Sep 17 00:00:00 2001 From: ArtofBugs Date: Thu, 4 Apr 2024 17:09:28 -0700 Subject: [PATCH 093/403] Tox: Fix install commands for unit-tips and functional-tips Change-Id: I352ea2b18a8bdb57cd3027c2024834db091a5643 --- tox.ini | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tox.ini b/tox.ini index 0cfed1712..ece6be2fa 100644 --- a/tox.ini +++ b/tox.ini @@ -50,10 +50,10 @@ commands = [testenv:unit-tips] commands = - python -m pip install -q -U -e "git+file://{toxinidir}/../cliff#egg=cliff" - python -m pip install -q -U -e "git+file://{toxinidir}/../keystoneauth#egg=keystoneauth" - python -m pip install -q -U -e "git+file://{toxinidir}/../osc-lib#egg=osc_lib" - pythom -m pip install -q -e "git+file://{toxinidir}/../openstacksdk#egg=openstacksdk" + python -m pip install -q -U -e {toxinidir}/../cliff#egg=cliff + python -m pip install -q -U -e {toxinidir}/../keystoneauth#egg=keystoneauth + python -m pip install -q -U -e {toxinidir}/../osc-lib#egg=osc_lib + python -m pip install -q -U -e {toxinidir}/../openstacksdk#egg=openstacksdk python -m pip freeze stestr run {posargs} @@ -71,10 +71,10 @@ setenv = passenv = OS_* commands = - python -m pip install -q -U -e "git+file://{toxinidir}/../cliff#egg=cliff" - python -m pip install -q -U -e "git+file://{toxinidir}/../keystoneauth#egg=keystoneauth1" - python -m pip install -q -U -e "git+file://{toxinidir}/../osc-lib#egg=osc_lib" - python -m pip install -q -U -e "git+file://{toxinidir}/../openstacksdk#egg=openstacksdk" + python -m pip install -q -U -e {toxinidir}/../cliff#egg=cliff + python -m pip install -q -U -e {toxinidir}/../keystoneauth#egg=keystoneauth1 + python -m pip install -q -U -e {toxinidir}/../osc-lib#egg=osc_lib + python -m pip install -q -U -e {toxinidir}/../openstacksdk#egg=openstacksdk python -m pip freeze stestr run {posargs} From 5a18f995a8ef31efb9ab324e2b5c9e572e75c2d1 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Thu, 7 Dec 2023 19:31:46 +0530 Subject: [PATCH 094/403] volume: Deprecate '--retype-policy' in favor of '--migration-policy' The '--retype-policy' option is used in the 'volume set' command to specify the migration policy during the retype operation. The '--retype-policy' option does not convey the correct meaning of its usage. The migration policy determines whether we are going to perform the migration in the retype operation or not and is not related to the actual retype which just changes the volume type of the volume. Change-Id: I2ea8fd3f5277bb3422ccae915d05e8ad44ff1912 --- openstackclient/volume/v2/volume.py | 32 +++++++++++++++---- ...me-set-retype-policy-6bacb7dd92f1ad82.yaml | 8 +++++ 2 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 releasenotes/notes/rename-volume-set-retype-policy-6bacb7dd92f1ad82.yaml diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 4127e23d4..2073e0a53 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -711,6 +711,12 @@ def get_parser(self, prog_name): '--retype-policy', metavar='', choices=['never', 'on-demand'], + help=argparse.SUPPRESS, + ) + parser.add_argument( + '--migration-policy', + metavar='', + choices=['never', 'on-demand'], help=_( 'Migration policy while re-typing volume ' '("never" or "on-demand", default is "never" ) ' @@ -746,6 +752,15 @@ def take_action(self, parsed_args): volume = utils.find_resource(volume_client.volumes, parsed_args.volume) result = 0 + if parsed_args.retype_policy: + msg = _( + "The '--retype-policy' option has been deprecated in favor " + "of '--migration-policy' option. The '--retype-policy' option " + "will be removed in a future release. Please use " + "'--migration-policy' instead." + ) + self.log.warning(msg) + if parsed_args.size: try: if parsed_args.size <= volume.size: @@ -839,11 +854,12 @@ 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 migration_policy = 'never' - if parsed_args.retype_policy: - migration_policy = parsed_args.retype_policy + if policy: + migration_policy = policy try: # find the volume type volume_type = utils.find_resource( @@ -856,12 +872,14 @@ def take_action(self, parsed_args): except Exception as e: LOG.error(_("Failed to set volume type: %s"), e) result += 1 - elif parsed_args.retype_policy: - # If the "--retype-policy" is specified without "--type" + elif policy: + # If the "--migration-policy" is specified without "--type" LOG.warning( - _( - "'--retype-policy' option will not work " - "without '--type' option" + _("'%s' option will not work without '--type' option") + % ( + '--migration-policy' + if parsed_args.migration_policy + else '--retype-policy' ) ) diff --git a/releasenotes/notes/rename-volume-set-retype-policy-6bacb7dd92f1ad82.yaml b/releasenotes/notes/rename-volume-set-retype-policy-6bacb7dd92f1ad82.yaml new file mode 100644 index 000000000..93380c1c2 --- /dev/null +++ b/releasenotes/notes/rename-volume-set-retype-policy-6bacb7dd92f1ad82.yaml @@ -0,0 +1,8 @@ +--- +upgrade: + - | + The ``volume set --retype-policy`` parameter has been renamed to + ``--migration-policy`` to better convey the correct meaning of the options + usage. The migration policy determines whether we are going to perform the + migration in the retype opearation or not and is not related to the actual + retype which just changes the volume type of the volume. From 948b97d09e27a7dd92ec98edfc5faa9aeffe7898 Mon Sep 17 00:00:00 2001 From: ArtofBugs Date: Tue, 9 Apr 2024 12:15:00 -0700 Subject: [PATCH 095/403] Identity: Properly list users in a group in 'user list' commands Change-Id: Ia4457eaea5016b2e0325c6eb704d1592058e455e --- openstackclient/identity/v3/user.py | 6 +++++- openstackclient/tests/unit/identity/v3/test_user.py | 6 ++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 6837340c3..365240674 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -442,10 +442,14 @@ def take_action(self, parsed_args): user = identity_client.find_user(user_id, ignore_missing=False) data.append(user) + elif parsed_args.group: + data = identity_client.group_users( + domain_id=domain, + group=group, + ) else: data = identity_client.users( domain_id=domain, - group=group, ) # Column handling diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index 822abd682..ede5b85c0 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -922,6 +922,7 @@ def setUp(self): self.identity_sdk_client.find_user.return_value = self.user self.identity_sdk_client.users.return_value = [self.user] + self.identity_sdk_client.group_users.return_value = [self.user] 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 @@ -945,7 +946,6 @@ def test_user_list_no_options(self): # Set expected values kwargs = { 'domain_id': None, - 'group': None, } self.identity_sdk_client.users.assert_called_with(**kwargs) @@ -971,7 +971,6 @@ def test_user_list_domain(self): # Set expected values kwargs = { 'domain_id': self.domain.id, - 'group': None, } self.identity_sdk_client.users.assert_called_with(**kwargs) @@ -1000,7 +999,7 @@ def test_user_list_group(self): 'group': self.group.id, } - self.identity_sdk_client.users.assert_called_with(**kwargs) + self.identity_sdk_client.group_users.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) @@ -1022,7 +1021,6 @@ def test_user_list_long(self): # Set expected values kwargs = { 'domain_id': None, - 'group': None, } self.identity_sdk_client.users.assert_called_with(**kwargs) From ea202fe77c28f291d9a78a5ccd411f8fd3cf685f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 14 Feb 2024 18:02:31 +0000 Subject: [PATCH 096/403] tests: Use consistent shortcut to fake identity client Change-Id: I72e016b8146e38948b6ae857bf7ec0b18f4e3663 Signed-off-by: Stephen Finucane --- .../tests/unit/common/test_quota.py | 2 +- .../tests/unit/compute/v2/test_flavor.py | 2 +- .../tests/unit/compute/v2/test_keypair.py | 8 +++---- .../unit/compute/v2/test_server_migration.py | 4 ++-- .../tests/unit/compute/v2/test_usage.py | 2 +- .../tests/unit/identity/v2_0/test_endpoint.py | 4 ++-- .../tests/unit/identity/v2_0/test_project.py | 2 +- .../tests/unit/identity/v2_0/test_role.py | 6 ++--- .../identity/v2_0/test_role_assignment.py | 6 ++--- .../tests/unit/identity/v2_0/test_service.py | 2 +- .../tests/unit/identity/v2_0/test_token.py | 2 +- .../tests/unit/identity/v2_0/test_user.py | 4 ++-- .../unit/identity/v3/test_access_rule.py | 2 +- .../v3/test_application_credential.py | 2 +- .../tests/unit/identity/v3/test_consumer.py | 3 +-- .../tests/unit/identity/v3/test_credential.py | 6 ++--- .../tests/unit/identity/v3/test_domain.py | 4 ++-- .../tests/unit/identity/v3/test_endpoint.py | 10 ++++---- .../unit/identity/v3/test_endpoint_group.py | 12 ++++------ .../tests/unit/identity/v3/test_group.py | 6 ++--- .../identity/v3/test_identity_provider.py | 4 ++-- .../unit/identity/v3/test_implied_role.py | 2 +- .../tests/unit/identity/v3/test_limit.py | 2 +- .../tests/unit/identity/v3/test_mappings.py | 2 +- .../tests/unit/identity/v3/test_oauth.py | 2 +- .../tests/unit/identity/v3/test_project.py | 16 ++++++------- .../tests/unit/identity/v3/test_protocol.py | 2 +- .../tests/unit/identity/v3/test_region.py | 2 +- .../unit/identity/v3/test_registered_limit.py | 7 +++--- .../tests/unit/identity/v3/test_role.py | 10 ++++---- .../unit/identity/v3/test_role_assignment.py | 14 +++++------ .../tests/unit/identity/v3/test_service.py | 2 +- .../unit/identity/v3/test_service_provider.py | 2 +- .../tests/unit/identity/v3/test_token.py | 2 +- .../tests/unit/identity/v3/test_trust.py | 8 +++---- .../unit/identity/v3/test_unscoped_saml.py | 2 +- .../tests/unit/identity/v3/test_user.py | 4 ++-- .../tests/unit/image/v2/test_image.py | 4 ++-- .../unit/network/v2/test_address_group.py | 4 ++-- .../unit/network/v2/test_address_scope.py | 4 ++-- .../network/v2/test_floating_ip_network.py | 4 ++-- .../unit/network/v2/test_ip_availability.py | 2 +- .../tests/unit/network/v2/test_local_ip.py | 4 ++-- .../tests/unit/network/v2/test_ndp_proxy.py | 4 ++-- .../tests/unit/network/v2/test_network.py | 23 ++++++++----------- .../test_network_auto_allocated_topology.py | 2 +- .../unit/network/v2/test_network_flavor.py | 4 ++-- .../network/v2/test_network_flavor_profile.py | 4 ++-- .../unit/network/v2/test_network_meter.py | 4 ++-- .../network/v2/test_network_meter_rule.py | 4 ++-- .../unit/network/v2/test_network_rbac.py | 2 +- .../unit/network/v2/test_network_trunk.py | 4 ++-- .../tests/unit/network/v2/test_port.py | 2 +- .../tests/unit/network/v2/test_router.py | 2 +- .../network/v2/test_security_group_network.py | 4 ++-- .../v2/test_security_group_rule_network.py | 4 ++-- .../tests/unit/network/v2/test_subnet.py | 4 ++-- .../tests/unit/network/v2/test_subnet_pool.py | 4 ++-- .../tests/unit/volume/v1/test_volume.py | 4 ++-- .../tests/unit/volume/v2/test_volume.py | 4 ++-- .../unit/volume/v2/test_volume_snapshot.py | 2 +- .../tests/unit/volume/v2/test_volume_type.py | 2 +- .../unit/volume/v3/test_volume_attachment.py | 2 +- .../unit/volume/v3/test_volume_message.py | 2 +- 64 files changed, 135 insertions(+), 146 deletions(-) diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index 1884321a3..d99508abe 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -44,7 +44,7 @@ def setUp(self): # Set up common projects self.projects = identity_fakes_v3.FakeProject.create_projects(count=2) - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() self.projects_mock.get.return_value = self.projects[0] diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py index e81d2bf70..5312d7051 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -30,7 +30,7 @@ class TestFlavor(compute_fakes.TestComputev2): def setUp(self): super(TestFlavor, self).setUp() - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() diff --git a/openstackclient/tests/unit/compute/v2/test_keypair.py b/openstackclient/tests/unit/compute/v2/test_keypair.py index 39d5739d1..d944c216f 100644 --- a/openstackclient/tests/unit/compute/v2/test_keypair.py +++ b/openstackclient/tests/unit/compute/v2/test_keypair.py @@ -32,7 +32,7 @@ def setUp(self): super(TestKeypair, self).setUp() # Initialize the user mock - self.users_mock = self.app.client_manager.identity.users + self.users_mock = self.identity_client.users self.users_mock.reset_mock() self.users_mock.get.return_value = fakes.FakeResource( None, @@ -459,7 +459,7 @@ def test_keypair_list_v22(self, sm_mock): @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) def test_keypair_list_with_user(self, sm_mock): - users_mock = self.app.client_manager.identity.users + users_mock = self.identity_client.users users_mock.reset_mock() users_mock.get.return_value = fakes.FakeResource( None, @@ -515,7 +515,7 @@ def test_keypair_list_with_user_pre_v210(self, sm_mock): @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) def test_keypair_list_with_project(self, sm_mock): - projects_mock = self.app.client_manager.identity.tenants + projects_mock = self.identity_client.tenants projects_mock.reset_mock() projects_mock.get.return_value = fakes.FakeResource( None, @@ -523,7 +523,7 @@ def test_keypair_list_with_project(self, sm_mock): loaded=True, ) - users_mock = self.app.client_manager.identity.users + users_mock = self.identity_client.users users_mock.reset_mock() users_mock.list.return_value = [ fakes.FakeResource( diff --git a/openstackclient/tests/unit/compute/v2/test_server_migration.py b/openstackclient/tests/unit/compute/v2/test_server_migration.py index cff5917d1..6638ca168 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_migration.py +++ b/openstackclient/tests/unit/compute/v2/test_server_migration.py @@ -476,10 +476,10 @@ class TestListMigrationV280(TestListMigration): def setUp(self): super().setUp() - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() - self.users_mock = self.app.client_manager.identity.users + self.users_mock = self.identity_client.users self.users_mock.reset_mock() self.projects_mock.get.return_value = self.project diff --git a/openstackclient/tests/unit/compute/v2/test_usage.py b/openstackclient/tests/unit/compute/v2/test_usage.py index 9d0ea45cc..e47c044ae 100644 --- a/openstackclient/tests/unit/compute/v2/test_usage.py +++ b/openstackclient/tests/unit/compute/v2/test_usage.py @@ -23,7 +23,7 @@ class TestUsage(compute_fakes.TestComputev2): def setUp(self): super(TestUsage, self).setUp() - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v2_0/test_endpoint.py b/openstackclient/tests/unit/identity/v2_0/test_endpoint.py index 5de396c44..84ed6c9a4 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_endpoint.py +++ b/openstackclient/tests/unit/identity/v2_0/test_endpoint.py @@ -27,11 +27,11 @@ def setUp(self): super(TestEndpoint, self).setUp() # Get a shortcut to the EndpointManager Mock - self.endpoints_mock = self.app.client_manager.identity.endpoints + self.endpoints_mock = self.identity_client.endpoints self.endpoints_mock.reset_mock() # Get a shortcut to the ServiceManager Mock - self.services_mock = self.app.client_manager.identity.services + self.services_mock = self.identity_client.services self.services_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v2_0/test_project.py b/openstackclient/tests/unit/identity/v2_0/test_project.py index 68ec9f02e..cfd826337 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_project.py +++ b/openstackclient/tests/unit/identity/v2_0/test_project.py @@ -59,7 +59,7 @@ def setUp(self): super(TestProject, self).setUp() # Get a shortcut to the TenantManager Mock - self.projects_mock = self.app.client_manager.identity.tenants + self.projects_mock = self.identity_client.tenants self.projects_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v2_0/test_role.py b/openstackclient/tests/unit/identity/v2_0/test_role.py index 738982dbe..6556c6d7d 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_role.py +++ b/openstackclient/tests/unit/identity/v2_0/test_role.py @@ -43,15 +43,15 @@ def setUp(self): super(TestRole, self).setUp() # Get a shortcut to the TenantManager Mock - self.projects_mock = self.app.client_manager.identity.tenants + self.projects_mock = self.identity_client.tenants self.projects_mock.reset_mock() # Get a shortcut to the UserManager Mock - self.users_mock = self.app.client_manager.identity.users + self.users_mock = self.identity_client.users self.users_mock.reset_mock() # Get a shortcut to the RoleManager Mock - self.roles_mock = self.app.client_manager.identity.roles + self.roles_mock = self.identity_client.roles self.roles_mock.reset_mock() auth_ref = identity_fakes.fake_auth_ref( 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 248f689e4..9cc7d5e75 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py +++ b/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py @@ -37,15 +37,15 @@ def setUp(self): super(TestRoleAssignment, self).setUp() # Get a shortcut to the UserManager Mock - self.users_mock = self.app.client_manager.identity.users + self.users_mock = self.identity_client.users self.users_mock.reset_mock() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() # Get a shortcut to the RoleManager Mock - self.roles_mock = self.app.client_manager.identity.roles + self.roles_mock = self.identity_client.roles self.roles_mock.reset_mock() self.projects_mock.get.return_value = fakes.FakeResource( diff --git a/openstackclient/tests/unit/identity/v2_0/test_service.py b/openstackclient/tests/unit/identity/v2_0/test_service.py index 844f395be..b5ec353f9 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_service.py +++ b/openstackclient/tests/unit/identity/v2_0/test_service.py @@ -27,7 +27,7 @@ def setUp(self): super(TestService, self).setUp() # Get a shortcut to the ServiceManager Mock - self.services_mock = self.app.client_manager.identity.services + self.services_mock = self.identity_client.services self.services_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v2_0/test_token.py b/openstackclient/tests/unit/identity/v2_0/test_token.py index 33cb0d9c3..56e59f4ad 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_token.py +++ b/openstackclient/tests/unit/identity/v2_0/test_token.py @@ -96,7 +96,7 @@ class TestTokenRevoke(TestToken): def setUp(self): super(TestTokenRevoke, self).setUp() - self.tokens_mock = self.app.client_manager.identity.tokens + self.tokens_mock = self.identity_client.tokens self.tokens_mock.reset_mock() self.tokens_mock.delete.return_value = True self.cmd = token.RevokeToken(self.app, None) diff --git a/openstackclient/tests/unit/identity/v2_0/test_user.py b/openstackclient/tests/unit/identity/v2_0/test_user.py index a8baf61a0..3355e4cb5 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_user.py +++ b/openstackclient/tests/unit/identity/v2_0/test_user.py @@ -34,11 +34,11 @@ def setUp(self): super(TestUser, self).setUp() # Get a shortcut to the TenantManager Mock - self.projects_mock = self.app.client_manager.identity.tenants + self.projects_mock = self.identity_client.tenants self.projects_mock.reset_mock() # Get a shortcut to the UserManager Mock - self.users_mock = self.app.client_manager.identity.users + self.users_mock = self.identity_client.users self.users_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v3/test_access_rule.py b/openstackclient/tests/unit/identity/v3/test_access_rule.py index b62f283f3..b6ef20be7 100644 --- a/openstackclient/tests/unit/identity/v3/test_access_rule.py +++ b/openstackclient/tests/unit/identity/v3/test_access_rule.py @@ -27,7 +27,7 @@ class TestAccessRule(identity_fakes.TestIdentityv3): def setUp(self): super(TestAccessRule, self).setUp() - identity_manager = self.app.client_manager.identity + 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 diff --git a/openstackclient/tests/unit/identity/v3/test_application_credential.py b/openstackclient/tests/unit/identity/v3/test_application_credential.py index 68ed750d8..129b62cad 100644 --- a/openstackclient/tests/unit/identity/v3/test_application_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py @@ -29,7 +29,7 @@ class TestApplicationCredential(identity_fakes.TestIdentityv3): def setUp(self): super(TestApplicationCredential, self).setUp() - identity_manager = self.app.client_manager.identity + identity_manager = self.identity_client self.app_creds_mock = identity_manager.application_credentials self.app_creds_mock.reset_mock() self.roles_mock = identity_manager.roles diff --git a/openstackclient/tests/unit/identity/v3/test_consumer.py b/openstackclient/tests/unit/identity/v3/test_consumer.py index fd13ef4ab..8067965ae 100644 --- a/openstackclient/tests/unit/identity/v3/test_consumer.py +++ b/openstackclient/tests/unit/identity/v3/test_consumer.py @@ -20,8 +20,7 @@ class TestOAuth1(identity_fakes.TestOAuth1): def setUp(self): super(TestOAuth1, self).setUp() - identity_client = self.app.client_manager.identity - self.consumers_mock = identity_client.oauth1.consumers + self.consumers_mock = self.identity_client.oauth1.consumers self.consumers_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v3/test_credential.py b/openstackclient/tests/unit/identity/v3/test_credential.py index e6c2be678..47af5053b 100644 --- a/openstackclient/tests/unit/identity/v3/test_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_credential.py @@ -25,15 +25,15 @@ def setUp(self): super(TestCredential, self).setUp() # Get a shortcut to the CredentialManager Mock - self.credentials_mock = self.app.client_manager.identity.credentials + self.credentials_mock = self.identity_client.credentials self.credentials_mock.reset_mock() # Get a shortcut to the UserManager Mock - self.users_mock = self.app.client_manager.identity.users + self.users_mock = self.identity_client.users self.users_mock.reset_mock() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v3/test_domain.py b/openstackclient/tests/unit/identity/v3/test_domain.py index 08fbbaac9..76e784827 100644 --- a/openstackclient/tests/unit/identity/v3/test_domain.py +++ b/openstackclient/tests/unit/identity/v3/test_domain.py @@ -19,7 +19,7 @@ def setUp(self): super(TestDomain, self).setUp() # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains self.domains_mock.reset_mock() @@ -493,7 +493,7 @@ def test_domain_show(self): ('domain', self.domain.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.identity.tokens.get_token_data.return_value = { + self.identity_client.tokens.get_token_data.return_value = { 'token': {'project': {'domain': {'id': 'd1', 'name': 'd1'}}} } diff --git a/openstackclient/tests/unit/identity/v3/test_endpoint.py b/openstackclient/tests/unit/identity/v3/test_endpoint.py index 39159432f..02c125eb9 100644 --- a/openstackclient/tests/unit/identity/v3/test_endpoint.py +++ b/openstackclient/tests/unit/identity/v3/test_endpoint.py @@ -19,21 +19,21 @@ def setUp(self): super(TestEndpoint, self).setUp() # Get a shortcut to the EndpointManager Mock - self.endpoints_mock = self.app.client_manager.identity.endpoints + self.endpoints_mock = self.identity_client.endpoints self.endpoints_mock.reset_mock() - self.ep_filter_mock = self.app.client_manager.identity.endpoint_filter + self.ep_filter_mock = self.identity_client.endpoint_filter self.ep_filter_mock.reset_mock() # Get a shortcut to the ServiceManager Mock - self.services_mock = self.app.client_manager.identity.services + self.services_mock = self.identity_client.services self.services_mock.reset_mock() # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains self.domains_mock.reset_mock() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v3/test_endpoint_group.py b/openstackclient/tests/unit/identity/v3/test_endpoint_group.py index 66d06dd9c..00208b48a 100644 --- a/openstackclient/tests/unit/identity/v3/test_endpoint_group.py +++ b/openstackclient/tests/unit/identity/v3/test_endpoint_group.py @@ -22,23 +22,21 @@ def setUp(self): super(TestEndpointGroup, self).setUp() # Get a shortcut to the EndpointManager Mock - self.endpoint_groups_mock = ( - self.app.client_manager.identity.endpoint_groups - ) + self.endpoint_groups_mock = self.identity_client.endpoint_groups self.endpoint_groups_mock.reset_mock() - self.epf_mock = self.app.client_manager.identity.endpoint_filter + self.epf_mock = self.identity_client.endpoint_filter self.epf_mock.reset_mock() # Get a shortcut to the ServiceManager Mock - self.services_mock = self.app.client_manager.identity.services + self.services_mock = self.identity_client.services self.services_mock.reset_mock() # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains self.domains_mock.reset_mock() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v3/test_group.py b/openstackclient/tests/unit/identity/v3/test_group.py index 3c95931cf..646a43203 100644 --- a/openstackclient/tests/unit/identity/v3/test_group.py +++ b/openstackclient/tests/unit/identity/v3/test_group.py @@ -27,15 +27,15 @@ def setUp(self): super(TestGroup, self).setUp() # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains self.domains_mock.reset_mock() # Get a shortcut to the GroupManager Mock - self.groups_mock = self.app.client_manager.identity.groups + self.groups_mock = self.identity_client.groups self.groups_mock.reset_mock() # Get a shortcut to the UserManager Mock - self.users_mock = self.app.client_manager.identity.users + self.users_mock = self.identity_client.users self.users_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v3/test_identity_provider.py b/openstackclient/tests/unit/identity/v3/test_identity_provider.py index f45f53a3d..4426e096e 100644 --- a/openstackclient/tests/unit/identity/v3/test_identity_provider.py +++ b/openstackclient/tests/unit/identity/v3/test_identity_provider.py @@ -28,12 +28,12 @@ def setUp(self): super(TestIdentityProvider, self).setUp() # Identity Provider mocks - federation_lib = self.app.client_manager.identity.federation + federation_lib = self.identity_client.federation self.identity_providers_mock = federation_lib.identity_providers self.identity_providers_mock.reset_mock() # Domain mocks - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains self.domains_mock.reset_mock() self.domain = identity_fakes.FakeDomain.create_one_domain( identity_fakes.DOMAIN diff --git a/openstackclient/tests/unit/identity/v3/test_implied_role.py b/openstackclient/tests/unit/identity/v3/test_implied_role.py index ea9c75965..7b53de5eb 100644 --- a/openstackclient/tests/unit/identity/v3/test_implied_role.py +++ b/openstackclient/tests/unit/identity/v3/test_implied_role.py @@ -24,7 +24,7 @@ class TestRole(identity_fakes.TestIdentityv3): def setUp(self): super(TestRole, self).setUp() - identity_client = self.app.client_manager.identity + identity_client = self.identity_client # Get a shortcut to the UserManager Mock self.users_mock = identity_client.users diff --git a/openstackclient/tests/unit/identity/v3/test_limit.py b/openstackclient/tests/unit/identity/v3/test_limit.py index d5895afeb..1fb660b1a 100644 --- a/openstackclient/tests/unit/identity/v3/test_limit.py +++ b/openstackclient/tests/unit/identity/v3/test_limit.py @@ -24,7 +24,7 @@ class TestLimit(identity_fakes.TestIdentityv3): def setUp(self): super(TestLimit, self).setUp() - identity_manager = self.app.client_manager.identity + identity_manager = self.identity_client self.limit_mock = identity_manager.limits diff --git a/openstackclient/tests/unit/identity/v3/test_mappings.py b/openstackclient/tests/unit/identity/v3/test_mappings.py index dea0b87c4..50386609e 100644 --- a/openstackclient/tests/unit/identity/v3/test_mappings.py +++ b/openstackclient/tests/unit/identity/v3/test_mappings.py @@ -26,7 +26,7 @@ class TestMapping(identity_fakes.TestFederatedIdentity): def setUp(self): super(TestMapping, self).setUp() - federation_lib = self.app.client_manager.identity.federation + federation_lib = self.identity_client.federation self.mapping_mock = federation_lib.mappings self.mapping_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v3/test_oauth.py b/openstackclient/tests/unit/identity/v3/test_oauth.py index 0a4a61556..020a00232 100644 --- a/openstackclient/tests/unit/identity/v3/test_oauth.py +++ b/openstackclient/tests/unit/identity/v3/test_oauth.py @@ -20,7 +20,7 @@ class TestOAuth1(identity_fakes.TestOAuth1): def setUp(self): super(TestOAuth1, self).setUp() - identity_client = self.app.client_manager.identity + identity_client = self.identity_client self.access_tokens_mock = identity_client.oauth1.access_tokens self.access_tokens_mock.reset_mock() self.request_tokens_mock = identity_client.oauth1.request_tokens diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py index 70608c44e..d9427e37c 100644 --- a/openstackclient/tests/unit/identity/v3/test_project.py +++ b/openstackclient/tests/unit/identity/v3/test_project.py @@ -29,11 +29,11 @@ def setUp(self): super(TestProject, self).setUp() # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains self.domains_mock.reset_mock() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() @@ -1234,7 +1234,7 @@ def test_project_show(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.identity.tokens.get_token_data.return_value = { + self.identity_client.tokens.get_token_data.return_value = { 'token': { 'project': { 'domain': {}, @@ -1293,7 +1293,7 @@ def test_project_show_parents(self): ('children', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.identity.tokens.get_token_data.return_value = { + self.identity_client.tokens.get_token_data.return_value = { 'token': { 'project': { 'domain': {}, @@ -1360,7 +1360,7 @@ def test_project_show_subtree(self): ('children', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.identity.tokens.get_token_data.return_value = { + self.identity_client.tokens.get_token_data.return_value = { 'token': { 'project': { 'domain': {}, @@ -1428,7 +1428,7 @@ def test_project_show_parents_and_children(self): ('children', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.app.client_manager.identity.tokens.get_token_data.return_value = { + self.identity_client.tokens.get_token_data.return_value = { 'token': { 'project': { 'domain': {}, @@ -1482,7 +1482,7 @@ def test_project_show_with_domain(self): {"name": self.project.name} ) - self.app.client_manager.identity.tokens.get_token_data.return_value = { + self.identity_client.tokens.get_token_data.return_value = { 'token': { 'project': { 'domain': {"id": self.project.domain_id}, @@ -1492,7 +1492,7 @@ def test_project_show_with_domain(self): } } - identity_client = self.app.client_manager.identity + identity_client = self.identity_client arglist = [ "--domain", self.domain.id, diff --git a/openstackclient/tests/unit/identity/v3/test_protocol.py b/openstackclient/tests/unit/identity/v3/test_protocol.py index bb27910c3..d40f916e5 100644 --- a/openstackclient/tests/unit/identity/v3/test_protocol.py +++ b/openstackclient/tests/unit/identity/v3/test_protocol.py @@ -23,7 +23,7 @@ class TestProtocol(identity_fakes.TestFederatedIdentity): def setUp(self): super(TestProtocol, self).setUp() - federation_lib = self.app.client_manager.identity.federation + federation_lib = self.identity_client.federation self.protocols_mock = federation_lib.protocols self.protocols_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v3/test_region.py b/openstackclient/tests/unit/identity/v3/test_region.py index 9e83b7a86..be28f9448 100644 --- a/openstackclient/tests/unit/identity/v3/test_region.py +++ b/openstackclient/tests/unit/identity/v3/test_region.py @@ -23,7 +23,7 @@ def setUp(self): super(TestRegion, self).setUp() # Get a shortcut to the RegionManager Mock - self.regions_mock = self.app.client_manager.identity.regions + self.regions_mock = self.identity_client.regions self.regions_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v3/test_registered_limit.py b/openstackclient/tests/unit/identity/v3/test_registered_limit.py index 1ba6737e7..ad1a69c81 100644 --- a/openstackclient/tests/unit/identity/v3/test_registered_limit.py +++ b/openstackclient/tests/unit/identity/v3/test_registered_limit.py @@ -24,13 +24,12 @@ class TestRegisteredLimit(identity_fakes.TestIdentityv3): def setUp(self): super(TestRegisteredLimit, self).setUp() - identity_manager = self.app.client_manager.identity - self.registered_limit_mock = identity_manager.registered_limits + self.registered_limit_mock = self.identity_client.registered_limits - self.services_mock = identity_manager.services + self.services_mock = self.identity_client.services self.services_mock.reset_mock() - self.regions_mock = identity_manager.regions + self.regions_mock = self.identity_client.regions self.regions_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v3/test_role.py b/openstackclient/tests/unit/identity/v3/test_role.py index 12fd1611b..6d9a2ff09 100644 --- a/openstackclient/tests/unit/identity/v3/test_role.py +++ b/openstackclient/tests/unit/identity/v3/test_role.py @@ -30,23 +30,23 @@ def setUp(self): super(TestRole, self).setUp() # Get a shortcut to the UserManager Mock - self.users_mock = self.app.client_manager.identity.users + self.users_mock = self.identity_client.users self.users_mock.reset_mock() # Get a shortcut to the UserManager Mock - self.groups_mock = self.app.client_manager.identity.groups + self.groups_mock = self.identity_client.groups self.groups_mock.reset_mock() # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains self.domains_mock.reset_mock() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() # Get a shortcut to the RoleManager Mock - self.roles_mock = self.app.client_manager.identity.roles + self.roles_mock = self.identity_client.roles self.roles_mock.reset_mock() def _is_inheritance_testcase(self): diff --git a/openstackclient/tests/unit/identity/v3/test_role_assignment.py b/openstackclient/tests/unit/identity/v3/test_role_assignment.py index 04cb68cb2..afc7ac5b7 100644 --- a/openstackclient/tests/unit/identity/v3/test_role_assignment.py +++ b/openstackclient/tests/unit/identity/v3/test_role_assignment.py @@ -39,28 +39,26 @@ def setUp(self): super(TestRoleAssignment, self).setUp() # Get a shortcut to the UserManager Mock - self.users_mock = self.app.client_manager.identity.users + self.users_mock = self.identity_client.users self.users_mock.reset_mock() # Get a shortcut to the GroupManager Mock - self.groups_mock = self.app.client_manager.identity.groups + self.groups_mock = self.identity_client.groups self.groups_mock.reset_mock() # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains self.domains_mock.reset_mock() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() # Get a shortcut to the RoleManager Mock - self.roles_mock = self.app.client_manager.identity.roles + self.roles_mock = self.identity_client.roles self.roles_mock.reset_mock() - self.role_assignments_mock = ( - self.app.client_manager.identity.role_assignments - ) + self.role_assignments_mock = self.identity_client.role_assignments self.role_assignments_mock.reset_mock() # Get the command object to test diff --git a/openstackclient/tests/unit/identity/v3/test_service.py b/openstackclient/tests/unit/identity/v3/test_service.py index 8143ddd56..36669d7d5 100644 --- a/openstackclient/tests/unit/identity/v3/test_service.py +++ b/openstackclient/tests/unit/identity/v3/test_service.py @@ -25,7 +25,7 @@ def setUp(self): super(TestService, self).setUp() # Get a shortcut to the ServiceManager Mock - self.services_mock = self.app.client_manager.identity.services + self.services_mock = self.identity_client.services self.services_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v3/test_service_provider.py b/openstackclient/tests/unit/identity/v3/test_service_provider.py index 59117da93..8954c2a26 100644 --- a/openstackclient/tests/unit/identity/v3/test_service_provider.py +++ b/openstackclient/tests/unit/identity/v3/test_service_provider.py @@ -23,7 +23,7 @@ class TestServiceProvider(service_fakes.TestFederatedIdentity): def setUp(self): super(TestServiceProvider, self).setUp() - federation_lib = self.app.client_manager.identity.federation + federation_lib = self.identity_client.federation self.service_providers_mock = federation_lib.service_providers self.service_providers_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v3/test_token.py b/openstackclient/tests/unit/identity/v3/test_token.py index 80d5dc195..ba1a026c8 100644 --- a/openstackclient/tests/unit/identity/v3/test_token.py +++ b/openstackclient/tests/unit/identity/v3/test_token.py @@ -119,7 +119,7 @@ class TestTokenRevoke(TestToken): def setUp(self): super(TestTokenRevoke, self).setUp() - self.tokens_mock = self.app.client_manager.identity.tokens + self.tokens_mock = self.identity_client.tokens self.tokens_mock.reset_mock() self.tokens_mock.revoke_token.return_value = True self.cmd = token.RevokeToken(self.app, None) diff --git a/openstackclient/tests/unit/identity/v3/test_trust.py b/openstackclient/tests/unit/identity/v3/test_trust.py index 8a7903e08..6ca90721e 100644 --- a/openstackclient/tests/unit/identity/v3/test_trust.py +++ b/openstackclient/tests/unit/identity/v3/test_trust.py @@ -26,13 +26,13 @@ class TestTrust(identity_fakes.TestIdentityv3): def setUp(self): super(TestTrust, self).setUp() - self.trusts_mock = self.app.client_manager.identity.trusts + self.trusts_mock = self.identity_client.trusts self.trusts_mock.reset_mock() - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() - self.users_mock = self.app.client_manager.identity.users + self.users_mock = self.identity_client.users self.users_mock.reset_mock() - self.roles_mock = self.app.client_manager.identity.roles + self.roles_mock = self.identity_client.roles self.roles_mock.reset_mock() diff --git a/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py b/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py index fcc72bd6d..882dd0862 100644 --- a/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py +++ b/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py @@ -21,7 +21,7 @@ class TestUnscopedSAML(identity_fakes.TestFederatedIdentity): def setUp(self): super(TestUnscopedSAML, self).setUp() - federation_lib = self.app.client_manager.identity.federation + federation_lib = self.identity_client.federation self.projects_mock = federation_lib.projects self.projects_mock.reset_mock() self.domains_mock = federation_lib.domains diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index 822abd682..c57f03af0 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -1811,10 +1811,10 @@ def setUp(self): # Get the command object to test self.cmd = user.ShowUser(self.app, None) - self.app.client_manager.identity.auth.client.get_user_id.return_value = ( # noqa: E501 + self.identity_client.auth.client.get_user_id.return_value = ( # noqa: E501 self.user.id ) - self.app.client_manager.identity.tokens.get_token_data.return_value = { + self.identity_client.tokens.get_token_data.return_value = { 'token': { 'user': { 'domain_id': {'id': self.user.domain_id}, diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index 2a2a654a1..f71986270 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -33,9 +33,9 @@ def setUp(self): super().setUp() # Get shortcut to the Mocks in identity client - self.project_mock = self.app.client_manager.identity.projects + self.project_mock = self.identity_client.projects self.project_mock.reset_mock() - self.domain_mock = self.app.client_manager.identity.domains + self.domain_mock = self.identity_client.domains self.domain_mock.reset_mock() self.volumes_mock = self.volume_client.volumes fake_body = { diff --git a/openstackclient/tests/unit/network/v2/test_address_group.py b/openstackclient/tests/unit/network/v2/test_address_group.py index 11ea4f2dd..075ac8753 100644 --- a/openstackclient/tests/unit/network/v2/test_address_group.py +++ b/openstackclient/tests/unit/network/v2/test_address_group.py @@ -27,9 +27,9 @@ def setUp(self): super(TestAddressGroup, self).setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains class TestCreateAddressGroup(TestAddressGroup): diff --git a/openstackclient/tests/unit/network/v2/test_address_scope.py b/openstackclient/tests/unit/network/v2/test_address_scope.py index 4a6652012..f548e256d 100644 --- a/openstackclient/tests/unit/network/v2/test_address_scope.py +++ b/openstackclient/tests/unit/network/v2/test_address_scope.py @@ -27,9 +27,9 @@ def setUp(self): super(TestAddressScope, self).setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains class TestCreateAddressScope(TestAddressScope): 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 caf9afaaf..bd3da8e8b 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py @@ -27,9 +27,9 @@ def setUp(self): super(TestFloatingIPNetwork, self).setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains class TestCreateFloatingIPNetwork(TestFloatingIPNetwork): diff --git a/openstackclient/tests/unit/network/v2/test_ip_availability.py b/openstackclient/tests/unit/network/v2/test_ip_availability.py index f2aaad95e..1e70994a7 100644 --- a/openstackclient/tests/unit/network/v2/test_ip_availability.py +++ b/openstackclient/tests/unit/network/v2/test_ip_availability.py @@ -26,7 +26,7 @@ def setUp(self): super(TestIPAvailability, self).setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.project = identity_fakes.FakeProject.create_one_project() self.projects_mock.get.return_value = self.project diff --git a/openstackclient/tests/unit/network/v2/test_local_ip.py b/openstackclient/tests/unit/network/v2/test_local_ip.py index 5b293c94c..bfa7062d6 100644 --- a/openstackclient/tests/unit/network/v2/test_local_ip.py +++ b/openstackclient/tests/unit/network/v2/test_local_ip.py @@ -29,9 +29,9 @@ def setUp(self): super().setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains class TestCreateLocalIP(TestLocalIP): diff --git a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py index e1635a63f..fcf93b247 100644 --- a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py +++ b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py @@ -26,9 +26,9 @@ class TestNDPProxy(network_fakes.TestNetworkV2): def setUp(self): super(TestNDPProxy, self).setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains self.router = network_fakes.FakeRouter.create_one_router( {'id': 'fake-router-id'} diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index 2dbb0f822..5e06b18f4 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -19,7 +19,6 @@ from osc_lib import exceptions from openstackclient.network.v2 import network -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes_v2 from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3 from openstackclient.tests.unit.network.v2 import fakes as network_fakes @@ -33,9 +32,9 @@ def setUp(self): super(TestNetwork, self).setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains class TestCreateNetworkIdentityV3(TestNetwork): @@ -311,7 +310,11 @@ def test_create_with_no_tag(self): self._test_create_with_tag(add_tags=False) -class TestCreateNetworkIdentityV2(TestNetwork): +class TestCreateNetworkIdentityV2( + identity_fakes_v2.FakeClientMixin, + network_fakes.FakeClientMixin, + tests_utils.TestCommand, +): project = identity_fakes_v2.FakeProject.create_one_project() # The new network created. _network = network_fakes.create_one_network( @@ -386,18 +389,10 @@ def setUp(self): self.network_client.set_tags = mock.Mock(return_value=None) # Get the command object to test - self.cmd = network.CreateNetwork(self.app, self.namespace) - - # Set identity client v2. And get a shortcut to Identity client. - identity_client = identity_fakes_v2.FakeIdentityv2Client( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, - ) - self.app.client_manager.identity = identity_client - self.identity = self.app.client_manager.identity + self.cmd = network.CreateNetwork(self.app, None) # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.identity.tenants + self.projects_mock = self.identity_client.tenants self.projects_mock.get.return_value = self.project # There is no DomainManager Mock in fake identity v2. 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 51959fd9e..1e9bf4873 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 @@ -23,7 +23,7 @@ class TestAutoAllocatedTopology(network_fakes.TestNetworkV2): def setUp(self): super(TestAutoAllocatedTopology, self).setUp() - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects class TestCreateAutoAllocatedTopology(TestAutoAllocatedTopology): diff --git a/openstackclient/tests/unit/network/v2/test_network_flavor.py b/openstackclient/tests/unit/network/v2/test_network_flavor.py index 15322189e..6aaee7fdc 100644 --- a/openstackclient/tests/unit/network/v2/test_network_flavor.py +++ b/openstackclient/tests/unit/network/v2/test_network_flavor.py @@ -29,9 +29,9 @@ def setUp(self): super(TestNetworkFlavor, self).setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains class TestAddNetworkFlavorToProfile(TestNetworkFlavor): 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 1fe82ce8b..fdceff8fe 100644 --- a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py +++ b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py @@ -24,9 +24,9 @@ def setUp(self): super(TestFlavorProfile, self).setUp() # Get the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects # Get the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains class TestCreateFlavorProfile(TestFlavorProfile): diff --git a/openstackclient/tests/unit/network/v2/test_network_meter.py b/openstackclient/tests/unit/network/v2/test_network_meter.py index d1e3db65a..67ce7ccb9 100644 --- a/openstackclient/tests/unit/network/v2/test_network_meter.py +++ b/openstackclient/tests/unit/network/v2/test_network_meter.py @@ -28,8 +28,8 @@ class TestMeter(network_fakes.TestNetworkV2): def setUp(self): super(TestMeter, self).setUp() - self.projects_mock = self.app.client_manager.identity.projects - self.domains_mock = self.app.client_manager.identity.domains + self.projects_mock = self.identity_client.projects + self.domains_mock = self.identity_client.domains class TestCreateMeter(TestMeter): 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 47c130f67..045b0e3c2 100644 --- a/openstackclient/tests/unit/network/v2/test_network_meter_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_meter_rule.py @@ -28,8 +28,8 @@ class TestMeterRule(network_fakes.TestNetworkV2): def setUp(self): super(TestMeterRule, self).setUp() - self.projects_mock = self.app.client_manager.identity.projects - self.domains_mock = self.app.client_manager.identity.domains + self.projects_mock = self.identity_client.projects + self.domains_mock = self.identity_client.domains class TestCreateMeterRule(TestMeterRule): diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py index 222fd7672..ab4590535 100644 --- a/openstackclient/tests/unit/network/v2/test_network_rbac.py +++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py @@ -28,7 +28,7 @@ def setUp(self): super(TestNetworkRBAC, self).setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects @ddt.ddt diff --git a/openstackclient/tests/unit/network/v2/test_network_trunk.py b/openstackclient/tests/unit/network/v2/test_network_trunk.py index 9d23a51da..d4631a7d3 100644 --- a/openstackclient/tests/unit/network/v2/test_network_trunk.py +++ b/openstackclient/tests/unit/network/v2/test_network_trunk.py @@ -31,9 +31,9 @@ def setUp(self): super().setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains class TestCreateNetworkTrunk(TestNetworkTrunk): diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index f09fe1e51..62e2c8ef8 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -33,7 +33,7 @@ def setUp(self): super(TestPort, self).setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects @staticmethod def _get_common_cols_data(fake_port): diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 256fd9b70..e84d17030 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -27,7 +27,7 @@ class TestRouter(network_fakes.TestNetworkV2): def setUp(self): super(TestRouter, self).setUp() - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects class TestAddPortToRouter(TestRouter): 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 03bcd9776..062612dc6 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py @@ -27,9 +27,9 @@ def setUp(self): super(TestSecurityGroupNetwork, self).setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): 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 7b28ae345..2ac6cf99a 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 @@ -28,9 +28,9 @@ def setUp(self): super(TestSecurityGroupRuleNetwork, self).setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py index 649ba2af7..fc8920919 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet.py +++ b/openstackclient/tests/unit/network/v2/test_subnet.py @@ -28,9 +28,9 @@ def setUp(self): super(TestSubnet, self).setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains class TestCreateSubnet(TestSubnet): diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py index 0035fe8cf..bf441af9b 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py @@ -27,9 +27,9 @@ def setUp(self): super(TestSubnetPool, self).setUp() # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects # Get a shortcut to the DomainManager Mock - self.domains_mock = self.app.client_manager.identity.domains + self.domains_mock = self.identity_client.domains class TestCreateSubnetPool(TestSubnetPool): diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py index a4fd50368..6e3a5cdd8 100644 --- a/openstackclient/tests/unit/volume/v1/test_volume.py +++ b/openstackclient/tests/unit/volume/v1/test_volume.py @@ -35,11 +35,11 @@ def setUp(self): self.volumes_mock.reset_mock() # Get a shortcut to the TenantManager Mock - self.projects_mock = self.app.client_manager.identity.tenants + self.projects_mock = self.identity_client.tenants self.projects_mock.reset_mock() # Get a shortcut to the UserManager Mock - self.users_mock = self.app.client_manager.identity.users + self.users_mock = self.identity_client.users self.users_mock.reset_mock() def setup_volumes_mock(self, count): diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index e30a312dd..130bf7003 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -33,10 +33,10 @@ def setUp(self): self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() - self.users_mock = self.app.client_manager.identity.users + self.users_mock = self.identity_client.users self.users_mock.reset_mock() self.snapshots_mock = self.volume_client.volume_snapshots diff --git a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py index 03f2a33fa..6e7e02ae0 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py @@ -31,7 +31,7 @@ def setUp(self): self.snapshots_mock.reset_mock() self.volumes_mock = self.volume_client.volumes self.volumes_mock.reset_mock() - self.project_mock = self.app.client_manager.identity.projects + self.project_mock = self.identity_client.projects self.project_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v2/test_volume_type.py b/openstackclient/tests/unit/volume/v2/test_volume_type.py index f3d159b12..8b5096ce4 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_type.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_type.py @@ -41,7 +41,7 @@ def setUp(self): ) self.volume_encryption_types_mock.reset_mock() - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() diff --git a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py index 91b27eb2c..10d6a9862 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py @@ -30,7 +30,7 @@ def setUp(self): self.volume_attachments_mock = self.volume_client.attachments self.volume_attachments_mock.reset_mock() - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() self.servers_mock = self.compute_client.servers diff --git a/openstackclient/tests/unit/volume/v3/test_volume_message.py b/openstackclient/tests/unit/volume/v3/test_volume_message.py index aa3fc8058..63349f49d 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_message.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_message.py @@ -24,7 +24,7 @@ class TestVolumeMessage(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.projects_mock = self.app.client_manager.identity.projects + self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() self.volume_messages_mock = self.volume_client.messages From bb6b1e38cb7ab8b84ceee4d65859ebdfcb60d3cf Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 14 Feb 2024 18:11:34 +0000 Subject: [PATCH 097/403] tests: Remove use of unnecessary fake argparse Namespace Change-Id: I135c24d0f6450d5af4e1860218bf3a65c09e36cc Signed-off-by: Stephen Finucane --- .../tests/unit/compute/v2/test_server.py | 4 +- .../tests/unit/network/test_common.py | 17 ++---- .../tests/unit/network/v2/fakes.py | 6 +- .../unit/network/v2/test_address_group.py | 12 ++-- .../unit/network/v2/test_address_scope.py | 10 ++-- .../v2/test_default_security_group_rule.py | 8 +-- .../network/v2/test_floating_ip_network.py | 12 ++-- .../v2/test_floating_ip_pool_network.py | 4 +- .../v2/test_floating_ip_port_forwarding.py | 10 ++-- .../unit/network/v2/test_ip_availability.py | 4 +- .../network/v2/test_l3_conntrack_helper.py | 20 ++----- .../tests/unit/network/v2/test_local_ip.py | 10 ++-- .../network/v2/test_local_ip_association.py | 8 +-- .../tests/unit/network/v2/test_ndp_proxy.py | 10 ++-- .../tests/unit/network/v2/test_network.py | 12 ++-- .../unit/network/v2/test_network_agent.py | 20 +++---- .../test_network_auto_allocated_topology.py | 6 +- .../unit/network/v2/test_network_flavor.py | 16 +++-- .../network/v2/test_network_flavor_profile.py | 10 ++-- .../unit/network/v2/test_network_meter.py | 8 +-- .../network/v2/test_network_meter_rule.py | 8 +-- .../network/v2/test_network_qos_policy.py | 20 ++----- .../unit/network/v2/test_network_qos_rule.py | 60 ++++++------------- .../network/v2/test_network_qos_rule_type.py | 8 +-- .../unit/network/v2/test_network_rbac.py | 10 ++-- .../unit/network/v2/test_network_segment.py | 14 ++--- .../network/v2/test_network_segment_range.py | 12 ++-- .../v2/test_network_service_provider.py | 4 +- .../unit/network/v2/test_network_trunk.py | 14 ++--- .../tests/unit/network/v2/test_port.py | 12 ++-- .../tests/unit/network/v2/test_router.py | 32 +++++----- .../network/v2/test_security_group_network.py | 12 ++-- .../v2/test_security_group_rule_network.py | 16 ++--- .../tests/unit/network/v2/test_subnet.py | 12 ++-- .../tests/unit/network/v2/test_subnet_pool.py | 12 ++-- 35 files changed, 183 insertions(+), 270 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index fb7715a70..2e53eb8a6 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -513,7 +513,7 @@ def setUp(self): self.network_client.update_ip = mock.Mock(return_value=None) # Get the command object to test - self.cmd = server.AddFloatingIP(self.app, self.namespace) + self.cmd = server.AddFloatingIP(self.app, None) def test_server_add_floating_ip(self): _server = compute_fakes.create_one_server() @@ -7182,7 +7182,7 @@ def setUp(self): self.network_client.update_ip = mock.Mock(return_value=None) # Get the command object to test - self.cmd = server.RemoveFloatingIP(self.app, self.namespace) + self.cmd = server.RemoveFloatingIP(self.app, None) def test_server_remove_floating_ip_default(self): _server = compute_fakes.create_one_server() diff --git a/openstackclient/tests/unit/network/test_common.py b/openstackclient/tests/unit/network/test_common.py index 40d78e6bc..6bdbadef7 100644 --- a/openstackclient/tests/unit/network/test_common.py +++ b/openstackclient/tests/unit/network/test_common.py @@ -11,7 +11,6 @@ # under the License. # -import argparse from unittest import mock import openstack @@ -126,8 +125,6 @@ class TestNetworkAndCompute(utils.TestCommand): def setUp(self): super().setUp() - self.namespace = argparse.Namespace() - # Create client mocks. Note that we intentionally do not use specced # mocks since we want to test fake methods. @@ -143,7 +140,7 @@ def setUp(self): return_value='take_action_compute' ) - self.cmd = FakeNetworkAndComputeCommand(self.app, self.namespace) + self.cmd = FakeNetworkAndComputeCommand(self.app, None) def test_take_action_network(self): arglist = ['common', 'network'] @@ -168,19 +165,19 @@ def test_take_action_compute(self): class TestNetworkAndComputeCommand(TestNetworkAndCompute): def setUp(self): super(TestNetworkAndComputeCommand, self).setUp() - self.cmd = FakeNetworkAndComputeCommand(self.app, self.namespace) + self.cmd = FakeNetworkAndComputeCommand(self.app, None) class TestNetworkAndComputeLister(TestNetworkAndCompute): def setUp(self): super(TestNetworkAndComputeLister, self).setUp() - self.cmd = FakeNetworkAndComputeLister(self.app, self.namespace) + self.cmd = FakeNetworkAndComputeLister(self.app, None) class TestNetworkAndComputeShowOne(TestNetworkAndCompute): def setUp(self): super(TestNetworkAndComputeShowOne, self).setUp() - self.cmd = FakeNetworkAndComputeShowOne(self.app, self.namespace) + self.cmd = FakeNetworkAndComputeShowOne(self.app, None) def test_take_action_with_http_exception(self): with mock.patch.object(self.cmd, 'take_action_network') as m_action: @@ -207,8 +204,6 @@ class TestNeutronCommandWithExtraArgs(utils.TestCommand): def setUp(self): super(TestNeutronCommandWithExtraArgs, self).setUp() - self.namespace = argparse.Namespace() - # Create client mocks. Note that we intentionally do not use specced # mocks since we want to test fake methods. @@ -217,9 +212,7 @@ def setUp(self): self.network_client.test_create_action = mock.Mock() # Subclasses can override the command object to test. - self.cmd = FakeCreateNeutronCommandWithExtraArgs( - self.app, self.namespace - ) + self.cmd = FakeCreateNeutronCommandWithExtraArgs(self.app, None) def test_create_extra_attributes_default_type(self): arglist = [ diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index fdd41b0a8..a35716851 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -import argparse import copy from random import choice from random import randint @@ -106,10 +105,7 @@ class TestNetworkV2( FakeClientMixin, utils.TestCommand, ): - def setUp(self): - super().setUp() - - self.namespace = argparse.Namespace() + ... def create_one_extension(attrs=None): diff --git a/openstackclient/tests/unit/network/v2/test_address_group.py b/openstackclient/tests/unit/network/v2/test_address_group.py index 075ac8753..f2e8fb0d5 100644 --- a/openstackclient/tests/unit/network/v2/test_address_group.py +++ b/openstackclient/tests/unit/network/v2/test_address_group.py @@ -63,7 +63,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = address_group.CreateAddressGroup(self.app, self.namespace) + self.cmd = address_group.CreateAddressGroup(self.app, None) self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -153,7 +153,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = address_group.DeleteAddressGroup(self.app, self.namespace) + self.cmd = address_group.DeleteAddressGroup(self.app, None) def test_address_group_delete(self): arglist = [ @@ -256,7 +256,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = address_group.ListAddressGroup(self.app, self.namespace) + self.cmd = address_group.ListAddressGroup(self.app, None) def test_address_group_list(self): arglist = [] @@ -341,7 +341,7 @@ def setUp(self): return_value=self._address_group ) # Get the command object to test - self.cmd = address_group.SetAddressGroup(self.app, self.namespace) + self.cmd = address_group.SetAddressGroup(self.app, None) def test_set_nothing(self): arglist = [ @@ -447,7 +447,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = address_group.ShowAddressGroup(self.app, self.namespace) + self.cmd = address_group.ShowAddressGroup(self.app, None) def test_show_no_options(self): arglist = [] @@ -493,7 +493,7 @@ def setUp(self): return_value=self._address_group ) # Get the command object to test - self.cmd = address_group.UnsetAddressGroup(self.app, self.namespace) + self.cmd = address_group.UnsetAddressGroup(self.app, None) def test_unset_nothing(self): arglist = [ diff --git a/openstackclient/tests/unit/network/v2/test_address_scope.py b/openstackclient/tests/unit/network/v2/test_address_scope.py index f548e256d..308ee2343 100644 --- a/openstackclient/tests/unit/network/v2/test_address_scope.py +++ b/openstackclient/tests/unit/network/v2/test_address_scope.py @@ -57,7 +57,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = address_scope.CreateAddressScope(self.app, self.namespace) + self.cmd = address_scope.CreateAddressScope(self.app, None) self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -168,7 +168,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = address_scope.DeleteAddressScope(self.app, self.namespace) + self.cmd = address_scope.DeleteAddressScope(self.app, None) def test_address_scope_delete(self): arglist = [ @@ -272,7 +272,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = address_scope.ListAddressScope(self.app, self.namespace) + self.cmd = address_scope.ListAddressScope(self.app, None) def test_address_scope_list(self): arglist = [] @@ -404,7 +404,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = address_scope.SetAddressScope(self.app, self.namespace) + self.cmd = address_scope.SetAddressScope(self.app, None) def test_set_nothing(self): arglist = [ @@ -493,7 +493,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = address_scope.ShowAddressScope(self.app, self.namespace) + self.cmd = address_scope.ShowAddressScope(self.app, None) def test_show_no_options(self): arglist = [] 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 aded5abff..1d8e22c13 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 @@ -105,7 +105,7 @@ def setUp(self): # Get the command object to test self.cmd = default_security_group_rule.CreateDefaultSecurityGroupRule( - self.app, self.namespace + self.app, None ) def test_create_all_remote_options(self): @@ -848,7 +848,7 @@ def setUp(self): # Get the command object to test self.cmd = default_security_group_rule.DeleteDefaultSecurityGroupRule( - self.app, self.namespace + self.app, None ) def test_default_security_group_rule_delete(self): @@ -985,7 +985,7 @@ def setUp(self): # Get the command object to test self.cmd = default_security_group_rule.ListDefaultSecurityGroupRule( - self.app, self.namespace + self.app, None ) def test_list_default(self): @@ -1107,7 +1107,7 @@ def setUp(self): # Get the command object to test self.cmd = default_security_group_rule.ShowDefaultSecurityGroupRule( - self.app, self.namespace + self.app, None ) def test_show_no_options(self): 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 bd3da8e8b..0cdd8f3ae 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py @@ -95,7 +95,7 @@ def setUp(self): self.network_client.find_port = mock.Mock(return_value=self.port) # Get the command object to test - self.cmd = fip.CreateFloatingIP(self.app, self.namespace) + self.cmd = fip.CreateFloatingIP(self.app, None) def test_create_no_options(self): arglist = [] @@ -306,7 +306,7 @@ def setUp(self): self.network_client.delete_ip = mock.Mock(return_value=None) # Get the command object to test - self.cmd = fip.DeleteFloatingIP(self.app, self.namespace) + self.cmd = fip.DeleteFloatingIP(self.app, None) def test_floating_ip_delete(self): self.network_client.find_ip.side_effect = [ @@ -479,7 +479,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = fip.ListFloatingIP(self.app, self.namespace) + self.cmd = fip.ListFloatingIP(self.app, None) def test_floating_ip_list(self): arglist = [] @@ -747,7 +747,7 @@ def setUp(self): self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) # Get the command object to test - self.cmd = fip.ShowFloatingIP(self.app, self.namespace) + self.cmd = fip.ShowFloatingIP(self.app, None) def test_floating_ip_show(self): arglist = [ @@ -791,7 +791,7 @@ def setUp(self): self.network_client.set_tags = mock.Mock(return_value=None) # Get the command object to test - self.cmd = fip.SetFloatingIP(self.app, self.namespace) + self.cmd = fip.SetFloatingIP(self.app, None) def test_port_option(self): arglist = [ @@ -1041,7 +1041,7 @@ def setUp(self): self.network_client.set_tags = mock.Mock(return_value=None) # Get the command object to test - self.cmd = fip.UnsetFloatingIP(self.app, self.namespace) + self.cmd = fip.UnsetFloatingIP(self.app, None) def test_floating_ip_unset_port(self): arglist = [ diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py b/openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py index 700b8c5db..5fe53e34d 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py @@ -27,9 +27,7 @@ def setUp(self): super(TestListFloatingIPPoolNetwork, self).setUp() # Get the command object to test - self.cmd = floating_ip_pool.ListFloatingIPPool( - self.app, self.namespace - ) + self.cmd = floating_ip_pool.ListFloatingIPPool(self.app, None) def test_floating_ip_list(self): arglist = [] 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 f26dfb780..b4560f7b3 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 @@ -62,7 +62,7 @@ def setUp(self): # Get the command object to test self.cmd = floating_ip_port_forwarding.CreateFloatingIPPortForwarding( - self.app, self.namespace + self.app, None ) self.columns = ( @@ -358,7 +358,7 @@ def setUp(self): self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) # Get the command object to test self.cmd = floating_ip_port_forwarding.DeleteFloatingIPPortForwarding( - self.app, self.namespace + self.app, None ) def test_port_forwarding_delete(self): @@ -496,7 +496,7 @@ def setUp(self): self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) # Get the command object to test self.cmd = floating_ip_port_forwarding.ListFloatingIPPortForwarding( - self.app, self.namespace + self.app, None ) def test_port_forwarding_list(self): @@ -567,7 +567,7 @@ def setUp(self): self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) # Get the command object to test self.cmd = floating_ip_port_forwarding.SetFloatingIPPortForwarding( - self.app, self.namespace + self.app, None ) def test_set_nothing(self): @@ -696,7 +696,7 @@ def setUp(self): self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) # Get the command object to test self.cmd = floating_ip_port_forwarding.ShowFloatingIPPortForwarding( - self.app, self.namespace + self.app, None ) def test_show_no_options(self): diff --git a/openstackclient/tests/unit/network/v2/test_ip_availability.py b/openstackclient/tests/unit/network/v2/test_ip_availability.py index 1e70994a7..0175f3546 100644 --- a/openstackclient/tests/unit/network/v2/test_ip_availability.py +++ b/openstackclient/tests/unit/network/v2/test_ip_availability.py @@ -54,7 +54,7 @@ class TestListIPAvailability(TestIPAvailability): def setUp(self): super(TestListIPAvailability, self).setUp() - self.cmd = ip_availability.ListIPAvailability(self.app, self.namespace) + self.cmd = ip_availability.ListIPAvailability(self.app, None) self.network_client.network_ip_availabilities = mock.Mock( return_value=self._ip_availability ) @@ -142,7 +142,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = ip_availability.ShowIPAvailability(self.app, self.namespace) + self.cmd = ip_availability.ShowIPAvailability(self.app, None) def test_show_no_option(self): arglist = [] 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 415d87e07..1f3d34e69 100644 --- a/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +++ b/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py @@ -51,9 +51,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = l3_conntrack_helper.CreateConntrackHelper( - self.app, self.namespace - ) + self.cmd = l3_conntrack_helper.CreateConntrackHelper(self.app, None) def test_create_no_options(self): arglist = [] @@ -126,9 +124,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = l3_conntrack_helper.DeleteConntrackHelper( - self.app, self.namespace - ) + self.cmd = l3_conntrack_helper.DeleteConntrackHelper(self.app, None) def test_delete(self): arglist = [self.ct_helper.router_id, self.ct_helper.id] @@ -190,9 +186,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = l3_conntrack_helper.ListConntrackHelper( - self.app, self.namespace - ) + self.cmd = l3_conntrack_helper.ListConntrackHelper(self.app, None) def test_conntrack_helpers_list(self): arglist = [self.router.id] @@ -227,9 +221,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = l3_conntrack_helper.SetConntrackHelper( - self.app, self.namespace - ) + self.cmd = l3_conntrack_helper.SetConntrackHelper(self.app, None) def test_set_nothing(self): arglist = [ @@ -294,9 +286,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = l3_conntrack_helper.ShowConntrackHelper( - self.app, self.namespace - ) + self.cmd = l3_conntrack_helper.ShowConntrackHelper(self.app, None) def test_show_no_options(self): arglist = [] diff --git a/openstackclient/tests/unit/network/v2/test_local_ip.py b/openstackclient/tests/unit/network/v2/test_local_ip.py index bfa7062d6..cf4105b02 100644 --- a/openstackclient/tests/unit/network/v2/test_local_ip.py +++ b/openstackclient/tests/unit/network/v2/test_local_ip.py @@ -86,7 +86,7 @@ def setUp(self): self.network_client.find_port = mock.Mock(return_value=self.port) # Get the command object to test - self.cmd = local_ip.CreateLocalIP(self.app, self.namespace) + self.cmd = local_ip.CreateLocalIP(self.app, None) self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -155,7 +155,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = local_ip.DeleteLocalIP(self.app, self.namespace) + self.cmd = local_ip.DeleteLocalIP(self.app, None) def test_local_ip_delete(self): arglist = [ @@ -264,7 +264,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = local_ip.ListLocalIP(self.app, self.namespace) + self.cmd = local_ip.ListLocalIP(self.app, None) def test_local_ip_list(self): arglist = [] @@ -408,7 +408,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = local_ip.SetLocalIP(self.app, self.namespace) + self.cmd = local_ip.SetLocalIP(self.app, None) def test_set_nothing(self): arglist = [ @@ -487,7 +487,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = local_ip.ShowLocalIP(self.app, self.namespace) + self.cmd = local_ip.ShowLocalIP(self.app, None) def test_show_no_options(self): arglist = [] 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 e294f0308..35a104543 100644 --- a/openstackclient/tests/unit/network/v2/test_local_ip_association.py +++ b/openstackclient/tests/unit/network/v2/test_local_ip_association.py @@ -53,7 +53,7 @@ def setUp(self): # Get the command object to test self.cmd = local_ip_association.CreateLocalIPAssociation( - self.app, self.namespace + self.app, None ) self.columns = ( @@ -137,7 +137,7 @@ def setUp(self): ) # Get the command object to test self.cmd = local_ip_association.DeleteLocalIPAssociation( - self.app, self.namespace + self.app, None ) def test_local_ip_association_delete(self): @@ -270,9 +270,7 @@ def setUp(self): ) self.network_client.find_port = mock.Mock(return_value=self.fixed_port) # Get the command object to test - self.cmd = local_ip_association.ListLocalIPAssociation( - self.app, self.namespace - ) + self.cmd = local_ip_association.ListLocalIPAssociation(self.app, None) def test_local_ip_association_list(self): arglist = [self.local_ip.id] diff --git a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py index fcf93b247..ef7549a00 100644 --- a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py +++ b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py @@ -73,7 +73,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = ndp_proxy.CreateNDPProxy(self.app, self.namespace) + self.cmd = ndp_proxy.CreateNDPProxy(self.app, None) def test_create_no_options(self): arglist = [] @@ -135,7 +135,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = ndp_proxy.DeleteNDPProxy(self.app, self.namespace) + self.cmd = ndp_proxy.DeleteNDPProxy(self.app, None) def test_delete(self): arglist = [self.ndp_proxy.id] @@ -208,7 +208,7 @@ def setUp(self): self.network_client.ndp_proxies = mock.Mock(return_value=ndp_proxies) # Get the command object to test - self.cmd = ndp_proxy.ListNDPProxy(self.app, self.namespace) + self.cmd = ndp_proxy.ListNDPProxy(self.app, None) def test_ndp_proxy_list(self): arglist = [] @@ -348,7 +348,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = ndp_proxy.SetNDPProxy(self.app, self.namespace) + self.cmd = ndp_proxy.SetNDPProxy(self.app, None) def test_set_nothing(self): arglist = [ @@ -444,7 +444,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = ndp_proxy.ShowNDPProxy(self.app, self.namespace) + self.cmd = ndp_proxy.ShowNDPProxy(self.app, None) def test_show_no_options(self): arglist = [] diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index 5e06b18f4..fd67da7ba 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -120,7 +120,7 @@ def setUp(self): self.network_client.set_tags = mock.Mock(return_value=None) # Get the command object to test - self.cmd = network.CreateNetwork(self.app, self.namespace) + self.cmd = network.CreateNetwork(self.app, None) self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -465,7 +465,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network.DeleteNetwork(self.app, self.namespace) + self.cmd = network.DeleteNetwork(self.app, None) def test_delete_one_network(self): arglist = [ @@ -594,7 +594,7 @@ def setUp(self): super(TestListNetwork, self).setUp() # Get the command object to test - self.cmd = network.ListNetwork(self.app, self.namespace) + self.cmd = network.ListNetwork(self.app, None) self.network_client.networks = mock.Mock(return_value=self._network) @@ -962,7 +962,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network.SetNetwork(self.app, self.namespace) + self.cmd = network.SetNetwork(self.app, None) def test_set_this(self): arglist = [ @@ -1205,7 +1205,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network.ShowNetwork(self.app, self.namespace) + self.cmd = network.ShowNetwork(self.app, None) def test_show_no_options(self): arglist = [] @@ -1259,7 +1259,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network.UnsetNetwork(self.app, self.namespace) + self.cmd = network.UnsetNetwork(self.app, None) def test_unset_nothing(self): arglist = [ diff --git a/openstackclient/tests/unit/network/v2/test_network_agent.py b/openstackclient/tests/unit/network/v2/test_network_agent.py index 7322a0a6c..97748eeaf 100644 --- a/openstackclient/tests/unit/network/v2/test_network_agent.py +++ b/openstackclient/tests/unit/network/v2/test_network_agent.py @@ -38,7 +38,7 @@ def setUp(self): self.network_client.find_network = mock.Mock(return_value=self.net) self.network_client.name = self.network_client.find_network.name - self.cmd = network_agent.AddNetworkToAgent(self.app, self.namespace) + self.cmd = network_agent.AddNetworkToAgent(self.app, None) def test_show_no_options(self): arglist = [] @@ -79,7 +79,7 @@ def setUp(self): self.network_client.get_agent = mock.Mock(return_value=self._agent) self.network_client.find_router = mock.Mock(return_value=self._router) - self.cmd = network_agent.AddRouterToAgent(self.app, self.namespace) + self.cmd = network_agent.AddRouterToAgent(self.app, None) def test_add_no_options(self): arglist = [] @@ -123,7 +123,7 @@ def setUp(self): self.network_client.delete_agent = mock.Mock(return_value=None) # Get the command object to test - self.cmd = network_agent.DeleteNetworkAgent(self.app, self.namespace) + self.cmd = network_agent.DeleteNetworkAgent(self.app, None) def test_network_agent_delete(self): arglist = [ @@ -245,7 +245,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_agent.ListNetworkAgent(self.app, self.namespace) + self.cmd = network_agent.ListNetworkAgent(self.app, None) def test_network_agents_list(self): arglist = [] @@ -375,9 +375,7 @@ def setUp(self): self.network_client.find_network = mock.Mock(return_value=self.net) self.network_client.name = self.network_client.find_network.name - self.cmd = network_agent.RemoveNetworkFromAgent( - self.app, self.namespace - ) + self.cmd = network_agent.RemoveNetworkFromAgent(self.app, None) def test_show_no_options(self): arglist = [] @@ -433,9 +431,7 @@ def setUp(self): self.network_client.get_agent = mock.Mock(return_value=self._agent) self.network_client.find_router = mock.Mock(return_value=self._router) - self.cmd = network_agent.RemoveRouterFromAgent( - self.app, self.namespace - ) + self.cmd = network_agent.RemoveRouterFromAgent(self.app, None) def test_remove_no_options(self): arglist = [] @@ -482,7 +478,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_agent.SetNetworkAgent(self.app, self.namespace) + self.cmd = network_agent.SetNetworkAgent(self.app, None) def test_set_nothing(self): arglist = [ @@ -597,7 +593,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_agent.ShowNetworkAgent(self.app, self.namespace) + self.cmd = network_agent.ShowNetworkAgent(self.app, None) def test_show_no_options(self): arglist = [] 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 1e9bf4873..76305869b 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 @@ -48,7 +48,7 @@ def setUp(self): super(TestCreateAutoAllocatedTopology, self).setUp() self.cmd = network_auto_allocated_topology.CreateAutoAllocatedTopology( - self.app, self.namespace + self.app, None ) self.network_client.get_auto_allocated_topology = mock.Mock( return_value=self.topology @@ -153,7 +153,7 @@ def setUp(self): super(TestValidateAutoAllocatedTopology, self).setUp() self.cmd = network_auto_allocated_topology.CreateAutoAllocatedTopology( - self.app, self.namespace + self.app, None ) self.network_client.validate_auto_allocated_topology = mock.Mock( return_value=self.topology @@ -226,7 +226,7 @@ def setUp(self): super(TestDeleteAutoAllocatedTopology, self).setUp() self.cmd = network_auto_allocated_topology.DeleteAutoAllocatedTopology( - self.app, self.namespace + self.app, None ) self.network_client.delete_auto_allocated_topology = mock.Mock( return_value=None diff --git a/openstackclient/tests/unit/network/v2/test_network_flavor.py b/openstackclient/tests/unit/network/v2/test_network_flavor.py index 6aaee7fdc..a7973dd54 100644 --- a/openstackclient/tests/unit/network/v2/test_network_flavor.py +++ b/openstackclient/tests/unit/network/v2/test_network_flavor.py @@ -48,9 +48,7 @@ def setUp(self): return_value=self.service_profile ) - self.cmd = network_flavor.AddNetworkFlavorToProfile( - self.app, self.namespace - ) + self.cmd = network_flavor.AddNetworkFlavorToProfile(self.app, None) def test_show_no_options(self): arglist = [] @@ -109,7 +107,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_flavor.CreateNetworkFlavor(self.app, self.namespace) + self.cmd = network_flavor.CreateNetworkFlavor(self.app, None) self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -226,7 +224,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_flavor.DeleteNetworkFlavor(self.app, self.namespace) + self.cmd = network_flavor.DeleteNetworkFlavor(self.app, None) def test_network_flavor_delete(self): arglist = [ @@ -330,7 +328,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_flavor.ListNetworkFlavor(self.app, self.namespace) + self.cmd = network_flavor.ListNetworkFlavor(self.app, None) def test_network_flavor_list(self): arglist = [] @@ -361,7 +359,7 @@ def setUp(self): ) self.cmd = network_flavor.RemoveNetworkFlavorFromProfile( - self.app, self.namespace + self.app, None ) def test_show_no_options(self): @@ -419,7 +417,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_flavor.ShowNetworkFlavor(self.app, self.namespace) + self.cmd = network_flavor.ShowNetworkFlavor(self.app, None) def test_show_no_options(self): arglist = [] @@ -464,7 +462,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_flavor.SetNetworkFlavor(self.app, self.namespace) + self.cmd = network_flavor.SetNetworkFlavor(self.app, None) def test_set_nothing(self): arglist = [ 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 fdceff8fe..a54045aa6 100644 --- a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py +++ b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py @@ -60,7 +60,7 @@ def setUp(self): self.projects_mock.get.return_value = self.project # Get the command object to test self.cmd = network_flavor_profile.CreateNetworkFlavorProfile( - self.app, self.namespace + self.app, None ) def test_create_all_options(self): @@ -239,7 +239,7 @@ def setUp(self): # Get the command object to test self.cmd = network_flavor_profile.DeleteNetworkFlavorProfile( - self.app, self.namespace + self.app, None ) def test_network_flavor_profile_delete(self): @@ -353,7 +353,7 @@ def setUp(self): # Get the command object to test self.cmd = network_flavor_profile.ListNetworkFlavorProfile( - self.app, self.namespace + self.app, None ) def test_network_flavor_profile_list(self): @@ -396,7 +396,7 @@ def setUp(self): # Get the command object to test self.cmd = network_flavor_profile.ShowNetworkFlavorProfile( - self.app, self.namespace + self.app, None ) def test_show_all_options(self): @@ -432,7 +432,7 @@ def setUp(self): # Get the command object to test self.cmd = network_flavor_profile.SetNetworkFlavorProfile( - self.app, self.namespace + self.app, None ) def test_set_nothing(self): diff --git a/openstackclient/tests/unit/network/v2/test_network_meter.py b/openstackclient/tests/unit/network/v2/test_network_meter.py index 67ce7ccb9..af0513678 100644 --- a/openstackclient/tests/unit/network/v2/test_network_meter.py +++ b/openstackclient/tests/unit/network/v2/test_network_meter.py @@ -59,7 +59,7 @@ def setUp(self): return_value=self.new_meter ) self.projects_mock.get.return_value = self.project - self.cmd = network_meter.CreateMeter(self.app, self.namespace) + self.cmd = network_meter.CreateMeter(self.app, None) def test_create_no_options(self): arglist = [] @@ -140,7 +140,7 @@ def setUp(self): network_fakes.FakeNetworkMeter.get_meter(meter=self.meter_list) ) - self.cmd = network_meter.DeleteMeter(self.app, self.namespace) + self.cmd = network_meter.DeleteMeter(self.app, None) def test_delete_one_meter(self): arglist = [ @@ -244,7 +244,7 @@ def setUp(self): return_value=self.meter_list ) - self.cmd = network_meter.ListMeter(self.app, self.namespace) + self.cmd = network_meter.ListMeter(self.app, None) def test_meter_list(self): arglist = [] @@ -280,7 +280,7 @@ class TestShowMeter(TestMeter): def setUp(self): super(TestShowMeter, self).setUp() - self.cmd = network_meter.ShowMeter(self.app, self.namespace) + self.cmd = network_meter.ShowMeter(self.app, None) self.network_client.find_metering_label = mock.Mock( return_value=self.new_meter 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 045b0e3c2..8ee020809 100644 --- a/openstackclient/tests/unit/network/v2/test_network_meter_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_meter_rule.py @@ -69,7 +69,7 @@ def setUp(self): return_value=self.new_rule ) self.projects_mock.get.return_value = self.project - self.cmd = network_meter_rule.CreateMeterRule(self.app, self.namespace) + self.cmd = network_meter_rule.CreateMeterRule(self.app, None) self.network_client.find_metering_label = mock.Mock( return_value=fake_meter ) @@ -156,7 +156,7 @@ def setUp(self): ) ) - self.cmd = network_meter_rule.DeleteMeterRule(self.app, self.namespace) + self.cmd = network_meter_rule.DeleteMeterRule(self.app, None) def test_delete_one_rule(self): arglist = [ @@ -266,7 +266,7 @@ def setUp(self): return_value=self.rule_list ) - self.cmd = network_meter_rule.ListMeterRule(self.app, self.namespace) + self.cmd = network_meter_rule.ListMeterRule(self.app, None) def test_rule_list(self): arglist = [] @@ -309,7 +309,7 @@ class TestShowMeterRule(TestMeterRule): def setUp(self): super(TestShowMeterRule, self).setUp() - self.cmd = network_meter_rule.ShowMeterRule(self.app, self.namespace) + self.cmd = network_meter_rule.ShowMeterRule(self.app, None) self.network_client.find_metering_label_rule = mock.Mock( return_value=self.new_rule 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 fee2bc241..8129ff202 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_policy.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py @@ -67,9 +67,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_policy.CreateNetworkQosPolicy( - self.app, self.namespace - ) + self.cmd = network_qos_policy.CreateNetworkQosPolicy(self.app, None) self.projects_mock.get.return_value = self.project @@ -174,9 +172,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_policy.DeleteNetworkQosPolicy( - self.app, self.namespace - ) + self.cmd = network_qos_policy.DeleteNetworkQosPolicy(self.app, None) def test_qos_policy_delete(self): arglist = [ @@ -278,9 +274,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_policy.ListNetworkQosPolicy( - self.app, self.namespace - ) + self.cmd = network_qos_policy.ListNetworkQosPolicy(self.app, None) def test_qos_policy_list(self): arglist = [] @@ -361,9 +355,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_policy.SetNetworkQosPolicy( - self.app, self.namespace - ) + self.cmd = network_qos_policy.SetNetworkQosPolicy(self.app, None) def test_set_nothing(self): arglist = [ @@ -463,9 +455,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_policy.ShowNetworkQosPolicy( - self.app, self.namespace - ) + self.cmd = network_qos_policy.ShowNetworkQosPolicy(self.app, None) def test_show_no_options(self): arglist = [] 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 17eb3866d..8ecf4fa8b 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py @@ -97,9 +97,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.CreateNetworkQosRule( - self.app, self.namespace - ) + self.cmd = network_qos_rule.CreateNetworkQosRule(self.app, None) def test_create_no_options(self): arglist = [] @@ -206,9 +204,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.CreateNetworkQosRule( - self.app, self.namespace - ) + self.cmd = network_qos_rule.CreateNetworkQosRule(self.app, None) def test_create_no_options(self): arglist = [] @@ -313,9 +309,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.CreateNetworkQosRule( - self.app, self.namespace - ) + self.cmd = network_qos_rule.CreateNetworkQosRule(self.app, None) def test_create_no_options(self): arglist = [] @@ -417,9 +411,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.CreateNetworkQosRule( - self.app, self.namespace - ) + self.cmd = network_qos_rule.CreateNetworkQosRule(self.app, None) def test_create_no_options(self): arglist = [] @@ -567,9 +559,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.DeleteNetworkQosRule( - self.app, self.namespace - ) + self.cmd = network_qos_rule.DeleteNetworkQosRule(self.app, None) def test_qos_policy_delete(self): arglist = [ @@ -636,9 +626,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.DeleteNetworkQosRule( - self.app, self.namespace - ) + self.cmd = network_qos_rule.DeleteNetworkQosRule(self.app, None) def test_qos_policy_delete(self): arglist = [ @@ -705,9 +693,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.DeleteNetworkQosRule( - self.app, self.namespace - ) + self.cmd = network_qos_rule.DeleteNetworkQosRule(self.app, None) def test_qos_policy_delete(self): arglist = [ @@ -774,9 +760,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.DeleteNetworkQosRule( - self.app, self.namespace - ) + self.cmd = network_qos_rule.DeleteNetworkQosRule(self.app, None) def test_qos_policy_delete(self): arglist = [ @@ -844,7 +828,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.SetNetworkQosRule(self.app, self.namespace) + self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) def test_set_nothing(self): arglist = [ @@ -948,7 +932,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.SetNetworkQosRule(self.app, self.namespace) + self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) def test_set_nothing(self): arglist = [ @@ -1052,7 +1036,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.SetNetworkQosRule(self.app, self.namespace) + self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) def test_set_nothing(self): arglist = [ @@ -1156,7 +1140,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.SetNetworkQosRule(self.app, self.namespace) + self.cmd = network_qos_rule.SetNetworkQosRule(self.app, None) def test_set_nothing(self): arglist = [ @@ -1380,9 +1364,7 @@ def setUp(self): ) ) # Get the command object to test - self.cmd = network_qos_rule.ListNetworkQosRule( - self.app, self.namespace - ) + self.cmd = network_qos_rule.ListNetworkQosRule(self.app, None) def test_qos_rule_list(self): arglist = [self.qos_policy.id] @@ -1436,9 +1418,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.ShowNetworkQosRule( - self.app, self.namespace - ) + self.cmd = network_qos_rule.ShowNetworkQosRule(self.app, None) def test_show_no_options(self): arglist = [] @@ -1506,9 +1486,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.ShowNetworkQosRule( - self.app, self.namespace - ) + self.cmd = network_qos_rule.ShowNetworkQosRule(self.app, None) def test_show_no_options(self): arglist = [] @@ -1574,9 +1552,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.ShowNetworkQosRule( - self.app, self.namespace - ) + self.cmd = network_qos_rule.ShowNetworkQosRule(self.app, None) def test_show_no_options(self): arglist = [] @@ -1646,9 +1622,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_qos_rule.ShowNetworkQosRule( - self.app, self.namespace - ) + self.cmd = network_qos_rule.ShowNetworkQosRule(self.app, None) def test_show_no_options(self): arglist = [] 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 215b75825..c42a00af7 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 @@ -41,9 +41,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = _qos_rule_type.ShowNetworkQosRuleType( - self.app, self.namespace - ) + self.cmd = _qos_rule_type.ShowNetworkQosRuleType(self.app, None) def test_show_no_options(self): arglist = [] @@ -93,9 +91,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = _qos_rule_type.ListNetworkQosRuleType( - self.app, self.namespace - ) + self.cmd = _qos_rule_type.ListNetworkQosRuleType(self.app, None) def test_qos_rule_type_list(self): arglist = [] diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py index ab4590535..e40c1df41 100644 --- a/openstackclient/tests/unit/network/v2/test_network_rbac.py +++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py @@ -70,7 +70,7 @@ def setUp(self): super(TestCreateNetworkRBAC, self).setUp() # Get the command object to test - self.cmd = network_rbac.CreateNetworkRBAC(self.app, self.namespace) + self.cmd = network_rbac.CreateNetworkRBAC(self.app, None) self.network_client.create_rbac_policy = mock.Mock( return_value=self.rbac_policy @@ -349,7 +349,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_rbac.DeleteNetworkRBAC(self.app, self.namespace) + self.cmd = network_rbac.DeleteNetworkRBAC(self.app, None) def test_network_rbac_delete(self): arglist = [ @@ -460,7 +460,7 @@ def setUp(self): super(TestListNetworkRABC, self).setUp() # Get the command object to test - self.cmd = network_rbac.ListNetworkRBAC(self.app, self.namespace) + self.cmd = network_rbac.ListNetworkRBAC(self.app, None) self.network_client.rbac_policies = mock.Mock( return_value=self.rbac_policies @@ -562,7 +562,7 @@ def setUp(self): super(TestSetNetworkRBAC, self).setUp() # Get the command object to test - self.cmd = network_rbac.SetNetworkRBAC(self.app, self.namespace) + self.cmd = network_rbac.SetNetworkRBAC(self.app, None) self.network_client.find_rbac_policy = mock.Mock( return_value=self.rbac_policy @@ -637,7 +637,7 @@ def setUp(self): super(TestShowNetworkRBAC, self).setUp() # Get the command object to test - self.cmd = network_rbac.ShowNetworkRBAC(self.app, self.namespace) + self.cmd = network_rbac.ShowNetworkRBAC(self.app, None) self.network_client.find_rbac_policy = mock.Mock( return_value=self.rbac_policy diff --git a/openstackclient/tests/unit/network/v2/test_network_segment.py b/openstackclient/tests/unit/network/v2/test_network_segment.py index 39e694e74..38342a2c3 100644 --- a/openstackclient/tests/unit/network/v2/test_network_segment.py +++ b/openstackclient/tests/unit/network/v2/test_network_segment.py @@ -66,9 +66,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_segment.CreateNetworkSegment( - self.app, self.namespace - ) + self.cmd = network_segment.CreateNetworkSegment(self.app, None) def test_create_no_options(self): # Missing required args should bail here @@ -180,9 +178,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_segment.DeleteNetworkSegment( - self.app, self.namespace - ) + self.cmd = network_segment.DeleteNetworkSegment(self.app, None) def test_delete(self): arglist = [ @@ -293,7 +289,7 @@ def setUp(self): super(TestListNetworkSegment, self).setUp() # Get the command object to test - self.cmd = network_segment.ListNetworkSegment(self.app, self.namespace) + self.cmd = network_segment.ListNetworkSegment(self.app, None) self.network_client.find_network = mock.Mock( return_value=self._network @@ -364,7 +360,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_segment.SetNetworkSegment(self.app, self.namespace) + self.cmd = network_segment.SetNetworkSegment(self.app, None) def test_set_no_options(self): arglist = [ @@ -441,7 +437,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_segment.ShowNetworkSegment(self.app, self.namespace) + self.cmd = network_segment.ShowNetworkSegment(self.app, None) def test_show_no_options(self): # Missing required args should bail here 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 4c65bb7f1..e33d4d592 100644 --- a/openstackclient/tests/unit/network/v2/test_network_segment_range.py +++ b/openstackclient/tests/unit/network/v2/test_network_segment_range.py @@ -84,7 +84,7 @@ def setUp(self): # Get the command object to test self.cmd = network_segment_range.CreateNetworkSegmentRange( - self.app, self.namespace + self.app, None ) def test_create_no_options(self): @@ -360,7 +360,7 @@ def setUp(self): # Get the command object to test self.cmd = network_segment_range.DeleteNetworkSegmentRange( - self.app, self.namespace + self.app, None ) def test_delete(self): @@ -499,7 +499,7 @@ def setUp(self): # Get the command object to test self.cmd = network_segment_range.ListNetworkSegmentRange( - self.app, self.namespace + self.app, None ) def test_list_no_option(self): @@ -572,9 +572,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_segment_range.SetNetworkSegmentRange( - self.app, self.namespace - ) + self.cmd = network_segment_range.SetNetworkSegmentRange(self.app, None) def test_set_no_options(self): arglist = [ @@ -670,7 +668,7 @@ def setUp(self): # Get the command object to test self.cmd = network_segment_range.ShowNetworkSegmentRange( - self.app, self.namespace + self.app, None ) def test_show_no_options(self): 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 1714931d2..c08064fe7 100644 --- a/openstackclient/tests/unit/network/v2/test_network_service_provider.py +++ b/openstackclient/tests/unit/network/v2/test_network_service_provider.py @@ -56,9 +56,7 @@ def setUp(self): return_value=self.provider_list ) - self.cmd = service_provider.ListNetworkServiceProvider( - self.app, self.namespace - ) + self.cmd = service_provider.ListNetworkServiceProvider(self.app, None) def test_network_service_provider_list(self): arglist = [] diff --git a/openstackclient/tests/unit/network/v2/test_network_trunk.py b/openstackclient/tests/unit/network/v2/test_network_trunk.py index d4631a7d3..47952d988 100644 --- a/openstackclient/tests/unit/network/v2/test_network_trunk.py +++ b/openstackclient/tests/unit/network/v2/test_network_trunk.py @@ -92,7 +92,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_trunk.CreateNetworkTrunk(self.app, self.namespace) + self.cmd = network_trunk.CreateNetworkTrunk(self.app, None) self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -329,7 +329,7 @@ def setUp(self): self.domains_mock.get.return_value = self.domain # Get the command object to test - self.cmd = network_trunk.DeleteNetworkTrunk(self.app, self.namespace) + self.cmd = network_trunk.DeleteNetworkTrunk(self.app, None) def test_delete_trunkx(self): arglist = [ @@ -423,7 +423,7 @@ def setUp(self): self.domains_mock.get.return_value = self.domain # Get the command object to test - self.cmd = network_trunk.ShowNetworkTrunk(self.app, self.namespace) + self.cmd = network_trunk.ShowNetworkTrunk(self.app, None) def test_show_no_options(self): arglist = [] @@ -495,7 +495,7 @@ def setUp(self): self.domains_mock.get.return_value = self.domain # Get the command object to test - self.cmd = network_trunk.ListNetworkTrunk(self.app, self.namespace) + self.cmd = network_trunk.ListNetworkTrunk(self.app, None) def test_trunk_list_no_option(self): arglist = [] @@ -581,7 +581,7 @@ def setUp(self): self.domains_mock.get.return_value = self.domain # Get the command object to test - self.cmd = network_trunk.SetNetworkTrunk(self.app, self.namespace) + self.cmd = network_trunk.SetNetworkTrunk(self.app, None) def _test_set_network_trunk_attr(self, attr, value): arglist = [ @@ -842,7 +842,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_trunk.ListNetworkSubport(self.app, self.namespace) + self.cmd = network_trunk.ListNetworkSubport(self.app, None) def test_subport_list(self): arglist = [ @@ -915,7 +915,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = network_trunk.UnsetNetworkTrunk(self.app, self.namespace) + self.cmd = network_trunk.UnsetNetworkTrunk(self.app, None) def test_unset_network_trunk_subport(self): subport = self._trunk['sub_ports'][0] diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 62e2c8ef8..5a331635f 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -143,7 +143,7 @@ def setUp(self): ) self.network_client.find_extension = mock.Mock(return_value=[]) # Get the command object to test - self.cmd = port.CreatePort(self.app, self.namespace) + self.cmd = port.CreatePort(self.app, None) def test_create_default_options(self): arglist = [ @@ -1125,7 +1125,7 @@ def setUp(self): ports=self._ports ) # Get the command object to test - self.cmd = port.DeletePort(self.app, self.namespace) + self.cmd = port.DeletePort(self.app, None) def test_port_delete(self): arglist = [ @@ -1260,7 +1260,7 @@ def setUp(self): self.compute_client = self.app.client_manager.compute # Get the command object to test - self.cmd = port.ListPort(self.app, self.namespace) + self.cmd = port.ListPort(self.app, None) def test_port_list_no_options(self): arglist = [] @@ -1730,7 +1730,7 @@ def setUp(self): self.network_client.set_tags = mock.Mock(return_value=None) # Get the command object to test - self.cmd = port.SetPort(self.app, self.namespace) + self.cmd = port.SetPort(self.app, None) def test_set_port_defaults(self): arglist = [ @@ -2512,7 +2512,7 @@ def setUp(self): self.network_client.find_port = mock.Mock(return_value=self._port) # Get the command object to test - self.cmd = port.ShowPort(self.app, self.namespace) + self.cmd = port.ShowPort(self.app, None) def test_show_no_options(self): arglist = [] @@ -2577,7 +2577,7 @@ def setUp(self): self.network_client.update_port = mock.Mock(return_value=None) self.network_client.set_tags = mock.Mock(return_value=None) # Get the command object to test - self.cmd = port.UnsetPort(self.app, self.namespace) + self.cmd = port.UnsetPort(self.app, None) def test_unset_port_parameters(self): arglist = [ diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index e84d17030..6e645996f 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -44,7 +44,7 @@ def setUp(self): self.network_client.find_router = mock.Mock(return_value=self._router) self.network_client.find_port = mock.Mock(return_value=self._port) - self.cmd = router.AddPortToRouter(self.app, self.namespace) + self.cmd = router.AddPortToRouter(self.app, None) def test_add_port_no_option(self): arglist = [] @@ -94,7 +94,7 @@ def setUp(self): self.network_client.find_router = mock.Mock(return_value=self._router) self.network_client.find_subnet = mock.Mock(return_value=self._subnet) - self.cmd = router.AddSubnetToRouter(self.app, self.namespace) + self.cmd = router.AddSubnetToRouter(self.app, None) def test_add_subnet_no_option(self): arglist = [] @@ -174,7 +174,7 @@ def setUp(self): side_effect=lambda name: self._extensions.get(name) ) # Get the command object to test - self.cmd = router.CreateRouter(self.app, self.namespace) + self.cmd = router.CreateRouter(self.app, None) def test_create_no_options(self): arglist = [] @@ -493,7 +493,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = router.DeleteRouter(self.app, self.namespace) + self.cmd = router.DeleteRouter(self.app, None) def test_router_delete(self): arglist = [ @@ -645,7 +645,7 @@ def setUp(self): super(TestListRouter, self).setUp() # Get the command object to test - self.cmd = router.ListRouter(self.app, self.namespace) + self.cmd = router.ListRouter(self.app, None) self.network_client.agent_hosted_routers = mock.Mock( return_value=self.routers @@ -910,7 +910,7 @@ def setUp(self): self.network_client.find_router = mock.Mock(return_value=self._router) self.network_client.find_port = mock.Mock(return_value=self._port) - self.cmd = router.RemovePortFromRouter(self.app, self.namespace) + self.cmd = router.RemovePortFromRouter(self.app, None) def test_remove_port_no_option(self): arglist = [] @@ -957,7 +957,7 @@ def setUp(self): self.network_client.find_router = mock.Mock(return_value=self._router) self.network_client.find_subnet = mock.Mock(return_value=self._subnet) - self.cmd = router.RemoveSubnetFromRouter(self.app, self.namespace) + self.cmd = router.RemoveSubnetFromRouter(self.app, None) def test_remove_subnet_no_option(self): arglist = [] @@ -997,7 +997,7 @@ def setUp(self): self.network_client.add_extra_routes_to_router = mock.Mock( return_value=self._router ) - self.cmd = router.AddExtraRoutesToRouter(self.app, self.namespace) + self.cmd = router.AddExtraRoutesToRouter(self.app, None) self.network_client.find_router = mock.Mock(return_value=self._router) def test_add_no_extra_route(self): @@ -1086,7 +1086,7 @@ def setUp(self): self.network_client.remove_extra_routes_from_router = mock.Mock( return_value=self._router ) - self.cmd = router.RemoveExtraRoutesFromRouter(self.app, self.namespace) + self.cmd = router.RemoveExtraRoutesFromRouter(self.app, None) self.network_client.find_router = mock.Mock(return_value=self._router) def test_remove_no_extra_route(self): @@ -1192,7 +1192,7 @@ def setUp(self): side_effect=lambda name: self._extensions.get(name) ) # Get the command object to test - self.cmd = router.SetRouter(self.app, self.namespace) + self.cmd = router.SetRouter(self.app, None) def test_set_this(self): arglist = [ @@ -1716,7 +1716,7 @@ def setUp(self): self.network_client.ports = mock.Mock(return_value=[self._port]) # Get the command object to test - self.cmd = router.ShowRouter(self.app, self.namespace) + self.cmd = router.ShowRouter(self.app, None) def test_show_no_options(self): arglist = [] @@ -1831,7 +1831,7 @@ def setUp(self): return_value=None ) # Get the command object to test - self.cmd = router.UnsetRouter(self.app, self.namespace) + self.cmd = router.UnsetRouter(self.app, None) def test_unset_router_params(self): arglist = [ @@ -2098,7 +2098,7 @@ def setUp(self): self._router.status, format_columns.ListColumn(self._router.tags), ) - self.cmd = router.CreateRouter(self.app, self.namespace) + self.cmd = router.CreateRouter(self.app, None) def test_create_one_gateway(self): arglist = [ @@ -2204,7 +2204,7 @@ def setUp(self): self.network_client.update_external_gateways = mock.Mock( return_value=None ) - self.cmd = router.SetRouter(self.app, self.namespace) + self.cmd = router.SetRouter(self.app, None) def test_update_one_gateway(self): arglist = [ @@ -2297,7 +2297,7 @@ class TestAddGatewayRouter(TestGatewayOps): def setUp(self): super().setUp() # Get the command object to test - self.cmd = router.AddGatewayToRouter(self.app, self.namespace) + self.cmd = router.AddGatewayToRouter(self.app, None) self.network_client.add_external_gateways.return_value = self._router @@ -2421,7 +2421,7 @@ class TestRemoveGatewayRouter(TestGatewayOps): def setUp(self): super().setUp() # Get the command object to test - self.cmd = router.RemoveGatewayFromRouter(self.app, self.namespace) + self.cmd = router.RemoveGatewayFromRouter(self.app, None) self.network_client.remove_external_gateways.return_value = ( self._router 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 062612dc6..927a05df7 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py @@ -72,7 +72,7 @@ def setUp(self): self.network_client.set_tags = mock.Mock(return_value=None) # Get the command object to test - self.cmd = security_group.CreateSecurityGroup(self.app, self.namespace) + self.cmd = security_group.CreateSecurityGroup(self.app, None) def test_create_no_options(self): self.assertRaises( @@ -190,7 +190,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = security_group.DeleteSecurityGroup(self.app, self.namespace) + self.cmd = security_group.DeleteSecurityGroup(self.app, None) def test_security_group_delete(self): arglist = [ @@ -296,7 +296,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = security_group.ListSecurityGroup(self.app, self.namespace) + self.cmd = security_group.ListSecurityGroup(self.app, None) def test_security_group_list_no_options(self): arglist = [] @@ -431,7 +431,7 @@ def setUp(self): self.network_client.set_tags = mock.Mock(return_value=None) # Get the command object to test - self.cmd = security_group.SetSecurityGroup(self.app, self.namespace) + self.cmd = security_group.SetSecurityGroup(self.app, None) def test_set_no_options(self): self.assertRaises( @@ -556,7 +556,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = security_group.ShowSecurityGroup(self.app, self.namespace) + self.cmd = security_group.ShowSecurityGroup(self.app, None) def test_show_no_options(self): self.assertRaises( @@ -602,7 +602,7 @@ def setUp(self): self.network_client.set_tags = mock.Mock(return_value=None) # Get the command object to test - self.cmd = security_group.UnsetSecurityGroup(self.app, self.namespace) + self.cmd = security_group.UnsetSecurityGroup(self.app, None) def test_set_no_options(self): self.assertRaises( 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 2ac6cf99a..fb9e0799c 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 @@ -103,9 +103,7 @@ def setUp(self): self.domains_mock.get.return_value = self.domain # Get the command object to test - self.cmd = security_group_rule.CreateSecurityGroupRule( - self.app, self.namespace - ) + self.cmd = security_group_rule.CreateSecurityGroupRule(self.app, None) def test_create_no_options(self): self.assertRaises( @@ -985,9 +983,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = security_group_rule.DeleteSecurityGroupRule( - self.app, self.namespace - ) + self.cmd = security_group_rule.DeleteSecurityGroupRule(self.app, None) def test_security_group_rule_delete(self): arglist = [ @@ -1156,9 +1152,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = security_group_rule.ListSecurityGroupRule( - self.app, self.namespace - ) + self.cmd = security_group_rule.ListSecurityGroupRule(self.app, None) def test_list_default(self): self._security_group_rule_tcp.port_range_min = 80 @@ -1312,9 +1306,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = security_group_rule.ShowSecurityGroupRule( - self.app, self.namespace - ) + self.cmd = security_group_rule.ShowSecurityGroupRule(self.app, None) def test_show_no_options(self): self.assertRaises( diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py index fc8920919..802f440bc 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet.py +++ b/openstackclient/tests/unit/network/v2/test_subnet.py @@ -258,7 +258,7 @@ def setUp(self): super(TestCreateSubnet, self).setUp() # Get the command object to test - self.cmd = subnet_v2.CreateSubnet(self.app, self.namespace) + self.cmd = subnet_v2.CreateSubnet(self.app, None) self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain @@ -726,7 +726,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = subnet_v2.DeleteSubnet(self.app, self.namespace) + self.cmd = subnet_v2.DeleteSubnet(self.app, None) def test_subnet_delete(self): arglist = [ @@ -851,7 +851,7 @@ def setUp(self): super(TestListSubnet, self).setUp() # Get the command object to test - self.cmd = subnet_v2.ListSubnet(self.app, self.namespace) + self.cmd = subnet_v2.ListSubnet(self.app, None) self.network_client.subnets = mock.Mock(return_value=self._subnet) @@ -1183,7 +1183,7 @@ def setUp(self): 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.cmd = subnet_v2.SetSubnet(self.app, self.namespace) + self.cmd = subnet_v2.SetSubnet(self.app, None) def test_set_this(self): arglist = [ @@ -1510,7 +1510,7 @@ def setUp(self): super(TestShowSubnet, self).setUp() # Get the command object to test - self.cmd = subnet_v2.ShowSubnet(self.app, self.namespace) + self.cmd = subnet_v2.ShowSubnet(self.app, None) self.network_client.find_subnet = mock.Mock(return_value=self._subnet) @@ -1575,7 +1575,7 @@ def setUp(self): self.network_client.update_subnet = mock.Mock(return_value=None) self.network_client.set_tags = mock.Mock(return_value=None) # Get the command object to test - self.cmd = subnet_v2.UnsetSubnet(self.app, self.namespace) + self.cmd = subnet_v2.UnsetSubnet(self.app, None) def test_unset_subnet_params(self): arglist = [ diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py index bf441af9b..1221a9a75 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py @@ -82,7 +82,7 @@ def setUp(self): self.network_client.set_tags = mock.Mock(return_value=None) # Get the command object to test - self.cmd = subnet_pool.CreateSubnetPool(self.app, self.namespace) + self.cmd = subnet_pool.CreateSubnetPool(self.app, None) self.network_client.find_address_scope = mock.Mock( return_value=self._address_scope @@ -394,7 +394,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = subnet_pool.DeleteSubnetPool(self.app, self.namespace) + self.cmd = subnet_pool.DeleteSubnetPool(self.app, None) def test_subnet_pool_delete(self): arglist = [ @@ -512,7 +512,7 @@ def setUp(self): super(TestListSubnetPool, self).setUp() # Get the command object to test - self.cmd = subnet_pool.ListSubnetPool(self.app, self.namespace) + self.cmd = subnet_pool.ListSubnetPool(self.app, None) self.network_client.subnet_pools = mock.Mock( return_value=self._subnet_pools @@ -746,7 +746,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = subnet_pool.SetSubnetPool(self.app, self.namespace) + self.cmd = subnet_pool.SetSubnetPool(self.app, None) def test_set_this(self): arglist = [ @@ -1084,7 +1084,7 @@ def setUp(self): ) # Get the command object to test - self.cmd = subnet_pool.ShowSubnetPool(self.app, self.namespace) + self.cmd = subnet_pool.ShowSubnetPool(self.app, None) def test_show_no_options(self): arglist = [] @@ -1128,7 +1128,7 @@ def setUp(self): self.network_client.update_subnet_pool = mock.Mock(return_value=None) self.network_client.set_tags = mock.Mock(return_value=None) # Get the command object to test - self.cmd = subnet_pool.UnsetSubnetPool(self.app, self.namespace) + self.cmd = subnet_pool.UnsetSubnetPool(self.app, None) def _test_unset_tags(self, with_tags=True): if with_tags: From 276dbb6f5630cf96f260c8e347e85293b86a876f Mon Sep 17 00:00:00 2001 From: Antonia Gaete Date: Tue, 6 Jun 2023 16:46:56 +0000 Subject: [PATCH 098/403] Add image metadef resource type association commands 'create', 'list', 'delete' Change-Id: I2c860427b0b2693076cfe57841f0e512ad1f6388 --- doc/source/cli/data/glance.csv | 8 +- .../v2/metadef_resource_type_association.py | 189 ++++++++++++++++++ openstackclient/tests/unit/image/v2/fakes.py | 41 ++++ .../test_metadef_resource_type_association.py | 131 ++++++++++++ ...association-commands-4d373d7d8eca5d55.yaml | 17 ++ setup.cfg | 3 + 6 files changed, 385 insertions(+), 4 deletions(-) create mode 100644 openstackclient/image/v2/metadef_resource_type_association.py create mode 100644 openstackclient/tests/unit/image/v2/test_metadef_resource_type_association.py create mode 100644 releasenotes/notes/add-image-metadef-resource-type-association-commands-4d373d7d8eca5d55.yaml diff --git a/doc/source/cli/data/glance.csv b/doc/source/cli/data/glance.csv index e554f09fe..905e282b0 100644 --- a/doc/source/cli/data/glance.csv +++ b/doc/source/cli/data/glance.csv @@ -28,7 +28,7 @@ md-namespace-import,,Import a metadata definitions namespace from file or standa 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-properties-delete,,Delete all metadata definitions property inside a specific namespace. -md-namespace-resource-type-list,image metadef resource type list,List resource types associated to 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. md-namespace-update,,Update an existing metadata definitions namespace. @@ -43,9 +43,9 @@ md-property-delete,image metadef property delete,Delete a specific metadata defi md-property-list,image metadef property list,List metadata definitions properties inside a specific namespace. md-property-show,image metadef property show,Describe a specific metadata definitions property inside a namespace. md-property-update,image metadef property set,Update metadata definitions property inside a namespace. -md-resource-type-associate,,Associate resource type with a metadata definitions namespace. -md-resource-type-deassociate,,Deassociate resource type with a metadata definitions namespace. -md-resource-type-list,,List available resource type names. +md-resource-type-associate,image metadef resource type association create,Associate resource type with a metadata definitions namespace. +md-resource-type-deassociate,image metadef resource type association delete,Deassociate resource type with a metadata definitions namespace. +md-resource-type-list,image metadef resource type list,List available resource type names. md-tag-create,,Add a new metadata definitions tag inside a namespace. md-tag-create-multiple,,Create new metadata definitions tags inside a namespace. md-tag-delete,,Delete a specific metadata definitions tag inside a namespace. diff --git a/openstackclient/image/v2/metadef_resource_type_association.py b/openstackclient/image/v2/metadef_resource_type_association.py new file mode 100644 index 000000000..e6461e5da --- /dev/null +++ b/openstackclient/image/v2/metadef_resource_type_association.py @@ -0,0 +1,189 @@ +# 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.command import command +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.i18n import _ + +LOG = logging.getLogger(__name__) + + +def _get_columns(item): + column_map = {} + hidden_columns = ['location'] + return utils.get_osc_show_columns_for_sdk_resource( + item, column_map, hidden_columns + ) + + +class CreateMetadefResourceTypeAssociation(command.ShowOne): + _description = _("Create metadef resource type association") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "namespace", + metavar="", + help=_( + "The name of the namespace you want to create the " + "resource type association in" + ), + ) + parser.add_argument( + "name", + metavar="", + help=_("A name of the new resource type"), + ) + parser.add_argument( + "--properties-target", + metavar="", + help=_( + "Some resource types allow more than one " + "key/value pair per instance." + ), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + kwargs = {} + + kwargs['namespace'] = parsed_args.namespace + kwargs['name'] = parsed_args.name + + if parsed_args.properties_target: + kwargs['properties_target'] = parsed_args.properties_target + + obj = image_client.create_metadef_resource_type_association( + parsed_args.namespace, **kwargs + ) + + display_columns, columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns, formatters={}) + + return (display_columns, data) + + +class DeleteMetadefResourceTypeAssociation(command.Command): + _description = _("Delete metadef resource type association") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "metadef_namespace", + metavar="", + help=_("The name of the namespace whose details you want to see"), + ) + parser.add_argument( + "name", + metavar="", + nargs="+", + help=_( + "The name of the resource type(s) (repeat option to delete" + "multiple metadef resource type associations)" + ), + ) + parser.add_argument( + "--force", + dest='force', + action='store_true', + default=False, + help=_( + "Force delete the resource type association if the" + "namespace is protected" + ), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + + result = 0 + for resource_type in parsed_args.name: + try: + metadef_namespace = image_client.get_metadef_namespace( + parsed_args.metadef_namespace + ) + + kwargs = {} + is_initially_protected = ( + True if metadef_namespace.is_protected else False + ) + if is_initially_protected and parsed_args.force: + kwargs['is_protected'] = False + + image_client.update_metadef_namespace( + metadef_namespace.namespace, **kwargs + ) + + try: + image_client.delete_metadef_resource_type_association( + resource_type, metadef_namespace, ignore_missing=False + ) + finally: + if is_initially_protected: + kwargs['is_protected'] = True + image_client.update_metadef_namespace( + metadef_namespace.namespace, **kwargs + ) + + except Exception as e: + result += 1 + LOG.error( + _( + "Failed to delete resource type with name or " + "ID '%(resource_type)s': %(e)s" + ), + {'resource_type': resource_type, 'e': e}, + ) + + if result > 0: + total = len(parsed_args.metadef_namespace) + msg = _( + "%(result)s of %(total)s resource type failed to delete." + ) % {'result': result, 'total': total} + raise exceptions.CommandError(msg) + + +class ListMetadefResourceTypeAssociations(command.Lister): + _description = _("List metadef resource type associations") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "metadef_namespace", + metavar="", + help=_("The name of the namespace whose details you want to see"), + ) + return parser + + def take_action(self, parsed_args): + image_client = self.app.client_manager.image + data = image_client.metadef_resource_type_associations( + parsed_args.metadef_namespace, + ) + columns = ['Name'] + column_headers = columns + return ( + column_headers, + ( + utils.get_item_properties( + s, + columns, + ) + for s in data + ), + ) diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py index 6565495fb..13ff77d1b 100644 --- a/openstackclient/tests/unit/image/v2/fakes.py +++ b/openstackclient/tests/unit/image/v2/fakes.py @@ -381,3 +381,44 @@ def create_one_metadef_object(attrs=None): # Overwrite default attributes if there are some attributes set metadef_objects_list.update(attrs) return metadef_object.MetadefObject(**metadef_objects_list) + + +def create_one_resource_type_association(attrs=None): + """Create a fake MetadefResourceTypeAssociation. + + :param attrs: A dictionary with all attributes of + metadef_resource_type_association member + :type attrs: dict + :return: A fake MetadefResourceTypeAssociation object + :rtype: A `metadef_resource_type_association. + MetadefResourceTypeAssociation` + """ + attrs = attrs or {} + + metadef_resource_type_association_info = { + 'namespace_name': 'OS::Compute::Quota', + 'name': 'OS::Nova::Flavor', + } + + metadef_resource_type_association_info.update(attrs) + return metadef_resource_type.MetadefResourceTypeAssociation( + **metadef_resource_type_association_info + ) + + +def create_resource_type_associations(attrs=None, count=2): + """Create mutiple fake resource type associations/ + + :param attrs: A dictionary with all attributes of + metadef_resource_type_association member + :type attrs: dict + :return: A list of fake MetadefResourceTypeAssociation objects + :rtype: list + """ + resource_type_associations = [] + for n in range(0, count): + resource_type_associations.append( + create_one_resource_type_association(attrs) + ) + + return resource_type_associations 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 new file mode 100644 index 000000000..ac1d65008 --- /dev/null +++ b/openstackclient/tests/unit/image/v2/test_metadef_resource_type_association.py @@ -0,0 +1,131 @@ +# 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.image.v2 import metadef_resource_type_association +from openstackclient.tests.unit.image.v2 import fakes as resource_type_fakes + + +class TestMetadefResourceTypeAssociationCreate( + resource_type_fakes.TestImagev2 +): + resource_type_association = ( + resource_type_fakes.create_one_resource_type_association() + ) + + columns = ( + 'created_at', + 'id', + 'name', + 'prefix', + 'properties_target', + 'updated_at', + ) + + data = ( + resource_type_association.created_at, + resource_type_association.id, + resource_type_association.name, + resource_type_association.prefix, + resource_type_association.properties_target, + resource_type_association.updated_at, + ) + + def setUp(self): + super().setUp() + + self.image_client.create_metadef_resource_type_association.return_value = ( + self.resource_type_association + ) + self.cmd = metadef_resource_type_association.CreateMetadefResourceTypeAssociation( + self.app, None + ) + + def test_resource_type_association_create(self): + arglist = [ + self.resource_type_association.namespace_name, + self.resource_type_association.name, + ] + + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestMetadefResourceTypeAssociationDelete( + resource_type_fakes.TestImagev2 +): + resource_type_association = ( + resource_type_fakes.create_one_resource_type_association() + ) + + def setUp(self): + super().setUp() + + self.image_client.delete_metadef_resource_type_association.return_value = ( + self.resource_type_association + ) + self.cmd = metadef_resource_type_association.DeleteMetadefResourceTypeAssociation( + self.app, None + ) + + def test_resource_type_association_delete(self): + arglist = [ + self.resource_type_association.namespace_name, + self.resource_type_association.name, + ] + + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + + +class TestMetadefResourceTypeAssociationList(resource_type_fakes.TestImagev2): + resource_type_associations = ( + resource_type_fakes.create_resource_type_associations() + ) + + columns = ['Name'] + + datalist = [ + (resource_type_association.name,) + for resource_type_association in resource_type_associations + ] + + def setUp(self): + super().setUp() + + self.image_client.metadef_resource_type_associations.side_effect = [ + self.resource_type_associations, + [], + ] + + self.cmd = metadef_resource_type_association.ListMetadefResourceTypeAssociations( + self.app, None + ) + + def test_resource_type_association_list(self): + arglist = [ + self.resource_type_associations[0].namespace_name, + ] + parsed_args = self.check_parser(self.cmd, arglist, []) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.datalist, data) diff --git a/releasenotes/notes/add-image-metadef-resource-type-association-commands-4d373d7d8eca5d55.yaml b/releasenotes/notes/add-image-metadef-resource-type-association-commands-4d373d7d8eca5d55.yaml new file mode 100644 index 000000000..eb03566fa --- /dev/null +++ b/releasenotes/notes/add-image-metadef-resource-type-association-commands-4d373d7d8eca5d55.yaml @@ -0,0 +1,17 @@ +--- +features: + - | + Added ``image metadef resource type association list`` + to list resource type associations for the image service. + This is equivalent to the + ``md-namespace-resource-type-list`` command in glance. + - | + Added ``image metadef resource type association create`` + to create a resource type association for the image service. + This is equivalent to the + ``md-resource-type-associate`` command in glance. + - | + Added ``image metadef resource type association delete`` + to delete a resource type association for the image service. + This is equivalent to the + ``md-resource-type-deassociate`` command in glance. diff --git a/setup.cfg b/setup.cfg index ceb94adc5..d49bb6bcc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -411,6 +411,9 @@ openstack.image.v2 = 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 From bbe686109d3648b9ed3509bf8c7baeb413941054 Mon Sep 17 00:00:00 2001 From: Bence Romsics Date: Tue, 16 Apr 2024 10:58:42 +0200 Subject: [PATCH 099/403] Improve output of 'server migrate --wait' We have seen users interpret the current output ('Complete') of 'server migrate --wait' as if it meant success as well and be surprised when later they learned that the migration was complete and failed. This change adds a pointer to the that output, how to actually check the success/failure of a migration, hoping to eliminate this user confusion. Change-Id: I09030705a39405366d6202a5ac743cc4d1ddd63c --- openstackclient/compute/v2/server.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 11ca9a36d..3b888043b 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -3211,7 +3211,12 @@ def _show_progress(progress): success_status=['active', 'verify_resize'], callback=_show_progress, ): - self.app.stdout.write(_('Complete\n')) + self.app.stdout.write( + _( + 'Complete, check success/failure by ' + 'openstack server migration/event list/show\n' + ) + ) else: msg = _('Error migrating server: %s') % server.id raise exceptions.CommandError(msg) From ee23995004086cc3129e453be9ca5595ef02cd1f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 23 Apr 2024 12:22:26 +0100 Subject: [PATCH 100/403] pre-commit: Bump versions We fold in the new black changes also. Change-Id: I326a0529b6b9f2aa9fbc33862567131839460797 Signed-off-by: Stephen Finucane --- .pre-commit-config.yaml | 6 +++--- openstackclient/compute/v2/server.py | 6 +++--- openstackclient/image/v2/cache.py | 20 +++++++++---------- .../network/v2/network_segment_range.py | 6 ++++-- openstackclient/network/v2/router.py | 6 +++--- .../tests/unit/compute/v2/fakes.py | 3 +-- .../tests/unit/identity/v2_0/fakes.py | 3 +-- .../tests/unit/identity/v3/fakes.py | 3 +-- .../unit/identity/v3/test_registered_limit.py | 6 +++--- openstackclient/tests/unit/image/v2/fakes.py | 3 +-- .../tests/unit/image/v2/test_image.py | 2 +- .../tests/unit/network/v2/fakes.py | 3 +-- tox.ini | 3 ++- 13 files changed, 34 insertions(+), 36 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9afa612f2..a65c70b42 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ default_language_version: python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: trailing-whitespace - id: mixed-line-ending @@ -18,12 +18,12 @@ repos: files: .*\.(yaml|yml)$ args: ['--unsafe'] - repo: https://github.com/psf/black - rev: 23.12.1 + rev: 24.4.0 hooks: - id: black args: ['-S', '-l', '79'] - repo: https://github.com/PyCQA/bandit - rev: 1.7.6 + rev: 1.7.8 hooks: - id: bandit args: ['-x', 'tests', '-s', 'B105,B106,B107,B401,B404,B603,B606,B607,B110,B605,B101'] diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 11ca9a36d..ce0a75550 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1991,9 +1991,9 @@ def _match_image(image_api, wanted_properties): ) raise exceptions.CommandError(msg) - boot_kwargs[ - 'hypervisor_hostname' - ] = parsed_args.hypervisor_hostname + boot_kwargs['hypervisor_hostname'] = ( + parsed_args.hypervisor_hostname + ) if parsed_args.hostname: if compute_client.api_version < api_versions.APIVersion("2.90"): diff --git a/openstackclient/image/v2/cache.py b/openstackclient/image/v2/cache.py index bf27b5e63..c69b7b592 100644 --- a/openstackclient/image/v2/cache.py +++ b/openstackclient/image/v2/cache.py @@ -36,16 +36,16 @@ def _format_image_cache(cached_images): for image in cached_images[item]: image_obj = copy.deepcopy(image) image_obj['state'] = 'cached' - image_obj[ - 'last_accessed' - ] = datetime.datetime.utcfromtimestamp( - image['last_accessed'] - ).isoformat() - image_obj[ - 'last_modified' - ] = datetime.datetime.utcfromtimestamp( - image['last_modified'] - ).isoformat() + image_obj['last_accessed'] = ( + datetime.datetime.utcfromtimestamp( + image['last_accessed'] + ).isoformat() + ) + image_obj['last_modified'] = ( + datetime.datetime.utcfromtimestamp( + image['last_modified'] + ).isoformat() + ) image_list.append(image_obj) elif item == "queued_images": for image in cached_images[item]: diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py index 96a03cf6c..59957617b 100644 --- a/openstackclient/network/v2/network_segment_range.py +++ b/openstackclient/network/v2/network_segment_range.py @@ -43,8 +43,10 @@ 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) - yield "%s-%s" % (b[0][1], b[-1][1]) if b[0][1] != b[-1][1] else str( - b[0][1] + yield ( + "%s-%s" % (b[0][1], b[-1][1]) + if b[0][1] != b[-1][1] + else str(b[0][1]) ) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 51e2aba42..e72d3e2f5 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -106,9 +106,9 @@ def _get_external_gateway_attrs(client_manager, parsed_args): attrs = {} if parsed_args.external_gateways: - external_gateways: collections.defaultdict[ - str, list[dict] - ] = collections.defaultdict(list) + external_gateways: collections.defaultdict[str, list[dict]] = ( + collections.defaultdict(list) + ) n_client = client_manager.network first_network_id = None diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 5806bf434..f8a816b6a 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -181,8 +181,7 @@ class TestComputev2( identity_fakes.FakeClientMixin, FakeClientMixin, utils.TestCommand, -): - ... +): ... def create_one_aggregate(attrs=None): diff --git a/openstackclient/tests/unit/identity/v2_0/fakes.py b/openstackclient/tests/unit/identity/v2_0/fakes.py index 70017c58a..b9364e7f0 100644 --- a/openstackclient/tests/unit/identity/v2_0/fakes.py +++ b/openstackclient/tests/unit/identity/v2_0/fakes.py @@ -207,8 +207,7 @@ def setUp(self): class TestIdentityv2( FakeClientMixin, utils.TestCommand, -): - ... +): ... class FakeExtension(object): diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py index 27d98b067..b56762497 100644 --- a/openstackclient/tests/unit/identity/v3/fakes.py +++ b/openstackclient/tests/unit/identity/v3/fakes.py @@ -690,8 +690,7 @@ def setUp(self): class TestIdentityv3( FakeClientMixin, utils.TestCommand, -): - ... +): ... # We don't use FakeClientMixin since we want a different fake legacy client diff --git a/openstackclient/tests/unit/identity/v3/test_registered_limit.py b/openstackclient/tests/unit/identity/v3/test_registered_limit.py index ad1a69c81..2f51b9772 100644 --- a/openstackclient/tests/unit/identity/v3/test_registered_limit.py +++ b/openstackclient/tests/unit/identity/v3/test_registered_limit.py @@ -246,9 +246,9 @@ def setUp(self): def test_registered_limit_set_description(self): registered_limit = copy.deepcopy(identity_fakes.REGISTERED_LIMIT) - registered_limit[ - 'description' - ] = identity_fakes.registered_limit_description + registered_limit['description'] = ( + identity_fakes.registered_limit_description + ) self.registered_limit_mock.update.return_value = fakes.FakeResource( None, registered_limit, loaded=True ) diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py index 52d3ec042..27f4777bc 100644 --- a/openstackclient/tests/unit/image/v2/fakes.py +++ b/openstackclient/tests/unit/image/v2/fakes.py @@ -43,8 +43,7 @@ class TestImagev2( identity_fakes.FakeClientMixin, FakeClientMixin, utils.TestCommand, -): - ... +): ... def create_one_image(attrs=None): diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index f71986270..e8aafa8cf 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -894,7 +894,7 @@ def test_image_list_limit_option(self): columns, data = self.cmd.take_action(parsed_args) self.image_client.images.assert_called_with( limit=ret_limit, - paginated=False + paginated=False, # marker=None ) diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index a35716851..538878e77 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -104,8 +104,7 @@ class TestNetworkV2( identity_fakes.FakeClientMixin, FakeClientMixin, utils.TestCommand, -): - ... +): ... def create_one_extension(attrs=None): diff --git a/tox.ini b/tox.ini index ece6be2fa..9d3b9def9 100644 --- a/tox.ini +++ b/tox.ini @@ -124,8 +124,9 @@ 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 # 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,H301,W503,W504 +ignore = E203, E501, E701, H301, W503, W504 import-order-style = pep8 application_import_names = openstackclient From 648d8df57865575cc0a9acb265878ca9204775fd Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 20 Apr 2023 11:17:07 +0100 Subject: [PATCH 101/403] tox: Add functional-pyNN jobs Let's us test with specific Python versions. We also merge the 'functional-tips' target in. Change-Id: I08e1b3e2f4be57aec0c1cd01274d86dfec769666 Signed-off-by: Stephen Finucane --- tox.ini | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/tox.ini b/tox.ini index 9d3b9def9..369b7c7d8 100644 --- a/tox.ini +++ b/tox.ini @@ -57,26 +57,18 @@ commands = python -m pip freeze stestr run {posargs} -[testenv:functional] +[testenv:functional{,-tips,-py38,-py39,-py310,-py311,-py312}] setenv = OS_TEST_PATH=./openstackclient/tests/functional passenv = OS_* commands = - stestr run {posargs} - -[testenv:functional-tips] -setenv = - OS_TEST_PATH=./openstackclient/tests/functional -passenv = - OS_* -commands = - python -m pip install -q -U -e {toxinidir}/../cliff#egg=cliff - python -m pip install -q -U -e {toxinidir}/../keystoneauth#egg=keystoneauth1 - python -m pip install -q -U -e {toxinidir}/../osc-lib#egg=osc_lib - python -m pip install -q -U -e {toxinidir}/../openstacksdk#egg=openstacksdk - python -m pip freeze - stestr run {posargs} + tips: python -m pip install -q -U -e {toxinidir}/../cliff#egg=cliff + tips: python -m pip install -q -U -e {toxinidir}/../keystoneauth#egg=keystoneauth1 + tips: python -m pip install -q -U -e {toxinidir}/../osc-lib#egg=osc_lib + tips: python -m pip install -q -U -e {toxinidir}/../openstacksdk#egg=openstacksdk + tips: python -m pip freeze + {[testenv]commands} [testenv:venv] deps = From 0646f9b4e4296dbbf14de3f887ea87350e773212 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 26 Apr 2024 12:51:18 +0100 Subject: [PATCH 102/403] tox: Remove bandit skips, run via pre-commit Most of these skips were unnecessary. The few that did generate warnings could be skipped. We also set 'skip_install' since there's no reason to build the package for linting purposes. Change-Id: I9644e5c19720b9c41c60e0a5882b7cd7f6a71f7b Signed-off-by: Stephen Finucane --- .pre-commit-config.yaml | 2 +- openstackclient/common/module.py | 2 +- openstackclient/compute/v2/server.py | 23 +++++++++----- openstackclient/compute/v2/usage.py | 2 +- openstackclient/identity/v2_0/user.py | 2 +- openstackclient/volume/v1/volume.py | 2 +- openstackclient/volume/v1/volume_backup.py | 2 +- openstackclient/volume/v1/volume_snapshot.py | 2 +- openstackclient/volume/v2/volume.py | 2 +- openstackclient/volume/v2/volume_backup.py | 2 +- openstackclient/volume/v2/volume_snapshot.py | 2 +- tox.ini | 33 +++++--------------- 12 files changed, 32 insertions(+), 44 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a65c70b42..9c384867d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,7 +26,7 @@ repos: rev: 1.7.8 hooks: - id: bandit - args: ['-x', 'tests', '-s', 'B105,B106,B107,B401,B404,B603,B606,B607,B110,B605,B101'] + args: ['-x', 'tests'] - repo: https://opendev.org/openstack/hacking rev: 6.1.0 hooks: diff --git a/openstackclient/common/module.py b/openstackclient/common/module.py index 486a27cc1..585fdf823 100644 --- a/openstackclient/common/module.py +++ b/openstackclient/common/module.py @@ -113,6 +113,6 @@ def take_action(self, parsed_args): data[k] = mods[k].__version__ except Exception: # Catch all exceptions, just skip it - pass + pass # nosec: B110 return zip(*sorted(data.items())) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index ce0a75550..1d97c349c 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2843,11 +2843,12 @@ def take_action(self, parsed_args): # there are infra failures if parsed_args.name_lookup_one_by_one or image_id: for image_id in image_ids: - # "Image Name" is not crucial, so we swallow any exceptions try: images[image_id] = image_client.get_image(image_id) except Exception: - pass + # retrieving image names is not crucial, so we swallow + # any exceptions + pass # nosec: B110 else: try: # some deployments can have *loads* of images so we only @@ -2866,7 +2867,9 @@ def take_action(self, parsed_args): for i in images_list: images[i.id] = i except Exception: - pass + # retrieving image names is not crucial, so we swallow any + # exceptions + pass # nosec: B110 # 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 @@ -2878,21 +2881,23 @@ def take_action(self, parsed_args): for s in data if s.flavor and s.flavor.get('id') ): - # "Flavor Name" is not crucial, so we swallow any - # exceptions try: flavors[f_id] = compute_client.find_flavor( f_id, ignore_missing=False ) except Exception: - pass + # retrieving flavor names is not crucial, so we swallow + # any exceptions + pass # nosec: B110 else: try: flavors_list = compute_client.flavors(is_public=None) for i in flavors_list: flavors[i.id] = i except Exception: - pass + # retrieving flavor names is not crucial, so we swallow any + # exceptions + pass # nosec: B110 # Populate image_name, image_id, flavor_name and flavor_id attributes # of server objects so that we can display those columns. @@ -4805,7 +4810,9 @@ def take_action(self, parsed_args): cmd = ' '.join(['ssh', ip_address] + args) LOG.debug("ssh command: {cmd}".format(cmd=cmd)) - os.system(cmd) + # we intentionally pass through user-provided arguments and run this in + # the user's shell + os.system(cmd) # nosec: B605 class StartServer(command.Command): diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index c89296794..0d62bc6a0 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -182,7 +182,7 @@ def _format_project(project): project_cache[p.id] = p except Exception: # Just forget it if there's any trouble - pass + pass # nosec: B110 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 7bcbc4515..10d2f18c3 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -252,7 +252,7 @@ def take_action(self, parsed_args): project_cache[p.id] = p except Exception: # Just forget it if there's any trouble - pass + pass # nosec: B110 formatters['tenantId'] = functools.partial( ProjectColumn, project_cache=project_cache ) diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index 168fed4ab..0248642b0 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -424,7 +424,7 @@ def take_action(self, parsed_args): server_cache[s.id] = s except Exception: # Just forget it if there's any trouble - pass + pass # nosec: B110 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 241fd3010..c90dec195 100644 --- a/openstackclient/volume/v1/volume_backup.py +++ b/openstackclient/volume/v1/volume_backup.py @@ -217,7 +217,7 @@ def take_action(self, parsed_args): volume_cache[s.id] = s except Exception: # Just forget it if there's any trouble - pass + pass # nosec: B110 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 81960635a..fd4bb774a 100644 --- a/openstackclient/volume/v1/volume_snapshot.py +++ b/openstackclient/volume/v1/volume_snapshot.py @@ -244,7 +244,7 @@ def take_action(self, parsed_args): volume_cache[s.id] = s except Exception: # Just forget it if there's any trouble - pass + pass # nosec: B110 VolumeIdColumnWithCache = functools.partial( VolumeIdColumn, volume_cache=volume_cache ) diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index f530b8508..98f4a5cab 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -537,7 +537,7 @@ def take_action(self, parsed_args): server_cache[s.id] = s except Exception: # Just forget it if there's any trouble - pass + pass # nosec: B110 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 64c786512..4454d17b0 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -325,7 +325,7 @@ def take_action(self, parsed_args): volume_cache[s.id] = s except Exception: # Just forget it if there's any trouble - pass + pass # nosec: B110 _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 0ca87144b..93560f53c 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -289,7 +289,7 @@ def take_action(self, parsed_args): volume_cache[s.id] = s except Exception: # Just forget it if there's any trouble - pass + pass # nosec: B110 _VolumeIdColumn = functools.partial( VolumeIdColumn, volume_cache=volume_cache ) diff --git a/tox.ini b/tox.ini index 369b7c7d8..b97f72ace 100644 --- a/tox.ini +++ b/tox.ini @@ -16,37 +16,18 @@ commands = stestr run {posargs} [testenv:pep8] +skip_install = true deps = - pre-commit + pre-commit commands = - pre-commit run --all-files --show-diff-on-failure + pre-commit run --all-files --show-diff-on-failure [testenv:bandit] -# This command runs the bandit security linter against the openstackclient -# codebase minus the tests directory. Some tests are being excluded to -# reduce the number of positives before a team inspection, and to ensure a -# passing gate job for initial addition. The excluded tests are: -# B105-B107: hardcoded password checks - likely to generate false positives -# in a gate environment -# B401: import subprocess - not necessarily a security issue; this plugin is -# mainly used for penetration testing workflow -# B603,B606: process without shell - not necessarily a security issue; this -# plugin is mainly used for penetration testing workflow -# B607: start process with a partial path - this should be a project level -# decision -# NOTE(elmiko): The following tests are being excluded specifically for -# python-openstackclient, they are being excluded to ensure that voting jobs -# in the project and in bandit integration tests continue to pass. These -# tests have generated issue within the project and should be investigated -# by the project. -# B110: try, except, pass detected - possible security issue; this should be -# investigated by the project for possible exploitation -# B605: process with a shell - possible security issue; this should be -# investigated by the project for possible exploitation -# B101: use of assert - this code will be removed when compiling to optimized -# byte code +skip_install = true +deps = + pre-commit commands = - bandit -r openstackclient -x tests -s B105,B106,B107,B401,B404,B603,B606,B607,B110,B605,B101 + pre-commit run --all-files --show-diff-on-failure bandit [testenv:unit-tips] commands = From c5b772db76c071e493a81105c7d8c0def08b2264 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 23 Apr 2024 12:24:23 +0100 Subject: [PATCH 103/403] trivial: Prepare for pyupgrade pre-commit hook This change is entirely automated save for the update of some mocks from 'io.open' to '__builtins__.open'). We are keeping this change separate from addition of the actual hook so that we can ignore the commit later. Change-Id: I0a9d8736632084473b57b57b693322447d7be519 Signed-off-by: Stephen Finucane --- doc/source/conf.py | 1 - doc/source/contributor/command-errors.rst | 2 +- openstackclient/api/api.py | 8 +- openstackclient/api/compute_v2.py | 24 ++--- openstackclient/api/image_v1.py | 2 +- openstackclient/api/object_store_v1.py | 7 +- openstackclient/common/availability_zone.py | 2 +- openstackclient/common/clientmanager.py | 8 +- openstackclient/common/configuration.py | 2 +- openstackclient/common/extension.py | 2 +- openstackclient/common/limits.py | 2 +- openstackclient/common/module.py | 4 +- openstackclient/common/progressbar.py | 4 +- openstackclient/common/project_cleanup.py | 2 +- openstackclient/common/quota.py | 8 +- openstackclient/common/versions.py | 2 +- 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 | 2 +- openstackclient/compute/v2/keypair.py | 15 ++-- openstackclient/compute/v2/server.py | 89 +++++++++---------- openstackclient/compute/v2/server_backup.py | 2 +- openstackclient/compute/v2/server_group.py | 8 +- openstackclient/compute/v2/server_image.py | 2 +- .../compute/v2/server_migration.py | 6 +- openstackclient/compute/v2/service.py | 6 +- openstackclient/compute/v2/usage.py | 4 +- openstackclient/identity/v2_0/catalog.py | 4 +- openstackclient/identity/v2_0/ec2creds.py | 8 +- openstackclient/identity/v2_0/endpoint.py | 8 +- openstackclient/identity/v2_0/project.py | 12 +-- openstackclient/identity/v2_0/role.py | 10 +-- .../identity/v2_0/role_assignment.py | 2 +- openstackclient/identity/v2_0/service.py | 8 +- openstackclient/identity/v2_0/token.py | 4 +- openstackclient/identity/v2_0/user.py | 12 +-- openstackclient/identity/v3/access_rule.py | 6 +- .../identity/v3/application_credential.py | 10 +-- openstackclient/identity/v3/catalog.py | 4 +- openstackclient/identity/v3/consumer.py | 8 +- openstackclient/identity/v3/credential.py | 10 +-- openstackclient/identity/v3/domain.py | 10 +-- openstackclient/identity/v3/ec2creds.py | 8 +- openstackclient/identity/v3/endpoint.py | 14 +-- openstackclient/identity/v3/endpoint_group.py | 18 ++-- .../identity/v3/federation_protocol.py | 10 +-- openstackclient/identity/v3/group.py | 16 ++-- .../identity/v3/identity_provider.py | 10 +-- openstackclient/identity/v3/implied_role.py | 6 +- openstackclient/identity/v3/limit.py | 10 +-- openstackclient/identity/v3/mapping.py | 10 +-- openstackclient/identity/v3/policy.py | 10 +-- openstackclient/identity/v3/project.py | 10 +-- openstackclient/identity/v3/region.py | 10 +-- .../identity/v3/registered_limit.py | 10 +-- openstackclient/identity/v3/role.py | 14 +-- .../identity/v3/role_assignment.py | 2 +- openstackclient/identity/v3/service.py | 10 +-- .../identity/v3/service_provider.py | 8 +- openstackclient/identity/v3/token.py | 10 +-- openstackclient/identity/v3/trust.py | 6 +- openstackclient/identity/v3/user.py | 12 +-- openstackclient/image/v1/image.py | 17 ++-- openstackclient/image/v2/task.py | 2 +- openstackclient/network/common.py | 4 +- openstackclient/network/utils.py | 4 +- openstackclient/network/v2/address_group.py | 12 +-- openstackclient/network/v2/address_scope.py | 10 +-- openstackclient/network/v2/floating_ip.py | 4 +- .../network/v2/floating_ip_port_forwarding.py | 18 ++-- openstackclient/network/v2/ip_availability.py | 4 +- .../network/v2/l3_conntrack_helper.py | 10 +-- openstackclient/network/v2/network.py | 4 +- openstackclient/network/v2/network_agent.py | 16 ++-- .../v2/network_auto_allocated_topology.py | 4 +- openstackclient/network/v2/network_flavor.py | 14 ++- .../network/v2/network_flavor_profile.py | 8 +- openstackclient/network/v2/network_meter.py | 6 +- .../network/v2/network_meter_rule.py | 6 +- .../network/v2/network_qos_policy.py | 10 +-- .../network/v2/network_qos_rule.py | 18 ++-- .../network/v2/network_qos_rule_type.py | 2 +- openstackclient/network/v2/network_rbac.py | 10 +-- openstackclient/network/v2/network_segment.py | 10 +-- .../network/v2/network_segment_range.py | 14 ++- openstackclient/network/v2/network_trunk.py | 14 +-- openstackclient/network/v2/port.py | 12 +-- openstackclient/network/v2/router.py | 24 ++--- openstackclient/network/v2/security_group.py | 2 +- openstackclient/network/v2/subnet.py | 14 +-- openstackclient/network/v2/subnet_pool.py | 12 +-- openstackclient/object/v1/account.py | 4 +- openstackclient/object/v1/container.py | 14 +-- openstackclient/object/v1/object.py | 14 +-- openstackclient/shell.py | 10 +-- openstackclient/tests/functional/base.py | 2 +- .../tests/functional/common/test_extension.py | 2 +- .../tests/functional/common/test_help.py | 4 +- .../tests/functional/common/test_quota.py | 2 +- .../tests/functional/compute/v2/common.py | 4 +- .../functional/compute/v2/test_flavor.py | 4 +- .../functional/compute/v2/test_keypair.py | 2 +- .../functional/compute/v2/test_server.py | 4 +- .../compute/v2/test_server_event.py | 2 +- .../tests/functional/identity/v2/common.py | 6 +- .../tests/functional/identity/v3/common.py | 6 +- .../v3/test_application_credential.py | 18 ++-- .../functional/identity/v3/test_domain.py | 4 +- .../functional/identity/v3/test_endpoint.py | 2 +- .../tests/functional/identity/v3/test_idp.py | 2 +- .../functional/identity/v3/test_region.py | 4 +- .../tests/functional/identity/v3/test_role.py | 4 +- .../functional/identity/v3/test_service.py | 4 +- .../identity/v3/test_service_provider.py | 4 +- .../tests/functional/image/base.py | 2 +- .../tests/functional/image/v2/test_image.py | 2 +- .../tests/functional/image/v2/test_info.py | 2 +- .../tests/functional/network/v2/common.py | 10 +-- .../functional/network/v2/test_network.py | 8 +- .../network/v2/test_network_agent.py | 12 +-- .../network/v2/test_network_qos_rule.py | 8 +- .../tests/functional/network/v2/test_port.py | 18 ++-- .../functional/network/v2/test_router.py | 4 +- .../tests/functional/object/v1/common.py | 2 +- .../functional/object/v1/test_container.py | 6 +- .../tests/functional/object/v1/test_object.py | 22 ++--- .../tests/functional/volume/base.py | 2 +- .../functional/volume/v1/test_service.py | 2 +- .../functional/volume/v1/test_snapshot.py | 4 +- .../volume/v1/test_transfer_request.py | 4 +- .../functional/volume/v1/test_volume_type.py | 2 +- .../functional/volume/v2/test_service.py | 4 +- .../volume/v2/test_volume_backup.py | 4 +- .../volume/v2/test_volume_snapshot.py | 4 +- .../functional/volume/v2/test_volume_type.py | 2 +- .../volume/v3/test_volume_snapshot.py | 4 +- .../functional/volume/v3/test_volume_type.py | 2 +- openstackclient/tests/unit/api/fakes.py | 2 +- openstackclient/tests/unit/api/test_api.py | 4 +- .../tests/unit/api/test_compute_v2.py | 2 +- .../tests/unit/api/test_image_v1.py | 2 +- .../tests/unit/api/test_image_v2.py | 2 +- .../tests/unit/api/test_object_store_v1.py | 8 +- .../tests/unit/common/test_logs.py | 4 +- .../tests/unit/common/test_module.py | 4 +- .../tests/unit/common/test_project_cleanup.py | 2 +- .../tests/unit/common/test_quota.py | 6 +- .../tests/unit/compute/v2/fakes.py | 8 +- .../tests/unit/compute/v2/test_agent.py | 10 +-- .../tests/unit/compute/v2/test_aggregate.py | 18 ++-- .../tests/unit/compute/v2/test_console.py | 4 +- .../tests/unit/compute/v2/test_flavor.py | 14 +-- .../tests/unit/compute/v2/test_host.py | 6 +- .../unit/compute/v2/test_hypervisor_stats.py | 4 +- .../tests/unit/compute/v2/test_keypair.py | 18 ++-- .../tests/unit/compute/v2/test_server.py | 76 ++++++++-------- .../unit/compute/v2/test_server_backup.py | 2 +- .../unit/compute/v2/test_server_event.py | 2 +- .../tests/unit/compute/v2/test_service.py | 6 +- .../tests/unit/compute/v2/test_usage.py | 6 +- openstackclient/tests/unit/fakes.py | 22 ++--- .../tests/unit/identity/v2_0/fakes.py | 16 ++-- .../tests/unit/identity/v2_0/test_catalog.py | 6 +- .../tests/unit/identity/v2_0/test_endpoint.py | 10 +-- .../tests/unit/identity/v2_0/test_project.py | 14 +-- .../tests/unit/identity/v2_0/test_role.py | 14 +-- .../identity/v2_0/test_role_assignment.py | 2 +- .../tests/unit/identity/v2_0/test_service.py | 10 +-- .../tests/unit/identity/v2_0/test_token.py | 6 +- .../tests/unit/identity/v2_0/test_user.py | 12 +-- .../tests/unit/identity/v3/fakes.py | 36 ++++---- .../unit/identity/v3/test_access_rule.py | 8 +- .../v3/test_application_credential.py | 10 +-- .../tests/unit/identity/v3/test_catalog.py | 6 +- .../tests/unit/identity/v3/test_consumer.py | 12 +-- .../tests/unit/identity/v3/test_credential.py | 12 +-- .../tests/unit/identity/v3/test_domain.py | 12 +-- .../tests/unit/identity/v3/test_endpoint.py | 16 ++-- .../unit/identity/v3/test_endpoint_group.py | 14 +-- .../tests/unit/identity/v3/test_group.py | 18 ++-- .../identity/v3/test_identity_provider.py | 12 +-- .../unit/identity/v3/test_implied_role.py | 8 +- .../tests/unit/identity/v3/test_limit.py | 12 +-- .../tests/unit/identity/v3/test_mappings.py | 12 +-- .../tests/unit/identity/v3/test_oauth.py | 8 +- .../tests/unit/identity/v3/test_project.py | 16 ++-- .../tests/unit/identity/v3/test_protocol.py | 12 +-- .../tests/unit/identity/v3/test_region.py | 12 +-- .../unit/identity/v3/test_registered_limit.py | 12 +-- .../tests/unit/identity/v3/test_role.py | 16 ++-- .../unit/identity/v3/test_role_assignment.py | 2 +- .../tests/unit/identity/v3/test_service.py | 12 +-- .../unit/identity/v3/test_service_provider.py | 12 +-- .../tests/unit/identity/v3/test_token.py | 6 +- .../tests/unit/identity/v3/test_trust.py | 10 +-- .../unit/identity/v3/test_unscoped_saml.py | 6 +- .../tests/unit/identity/v3/test_user.py | 8 +- .../tests/unit/image/v1/test_image.py | 12 +-- openstackclient/tests/unit/integ/base.py | 2 +- .../tests/unit/integ/cli/test_project.py | 8 +- .../tests/unit/integ/cli/test_shell.py | 14 +-- .../tests/unit/network/test_common.py | 12 ++- .../tests/unit/network/v2/fakes.py | 32 +++---- .../unit/network/v2/test_address_group.py | 14 +-- .../unit/network/v2/test_address_scope.py | 12 +-- .../v2/test_default_security_group_rule.py | 10 +-- .../network/v2/test_floating_ip_compute.py | 8 +- .../network/v2/test_floating_ip_network.py | 14 +-- .../v2/test_floating_ip_pool_compute.py | 2 +- .../v2/test_floating_ip_pool_network.py | 4 +- .../v2/test_floating_ip_port_forwarding.py | 12 +-- .../unit/network/v2/test_ip_availability.py | 6 +- .../network/v2/test_l3_conntrack_helper.py | 12 +-- .../tests/unit/network/v2/test_ndp_proxy.py | 12 +-- .../tests/unit/network/v2/test_network.py | 16 ++-- .../unit/network/v2/test_network_agent.py | 18 ++-- .../test_network_auto_allocated_topology.py | 8 +- .../unit/network/v2/test_network_compute.py | 8 +- .../unit/network/v2/test_network_flavor.py | 16 ++-- .../network/v2/test_network_flavor_profile.py | 12 +-- .../unit/network/v2/test_network_meter.py | 10 +-- .../network/v2/test_network_meter_rule.py | 10 +-- .../network/v2/test_network_qos_policy.py | 12 +-- .../unit/network/v2/test_network_qos_rule.py | 68 +++++++------- .../network/v2/test_network_qos_rule_type.py | 6 +- .../unit/network/v2/test_network_rbac.py | 12 +-- .../unit/network/v2/test_network_segment.py | 12 +-- .../network/v2/test_network_segment_range.py | 12 +-- .../v2/test_network_service_provider.py | 4 +- .../unit/network/v2/test_network_trunk.py | 4 +- .../tests/unit/network/v2/test_port.py | 16 ++-- .../tests/unit/network/v2/test_router.py | 46 +++++----- .../network/v2/test_security_group_compute.py | 10 +-- .../network/v2/test_security_group_network.py | 14 +-- .../v2/test_security_group_rule_compute.py | 8 +- .../v2/test_security_group_rule_network.py | 10 +-- .../tests/unit/network/v2/test_subnet.py | 14 +-- .../tests/unit/network/v2/test_subnet_pool.py | 14 +-- openstackclient/tests/unit/object/v1/fakes.py | 2 +- .../tests/unit/object/v1/test_container.py | 10 +-- .../unit/object/v1/test_container_all.py | 12 +-- .../tests/unit/object/v1/test_object.py | 6 +- .../tests/unit/object/v1/test_object_all.py | 10 +-- openstackclient/tests/unit/test_shell.py | 10 +-- openstackclient/tests/unit/utils.py | 2 +- .../tests/unit/volume/test_find_resource.py | 4 +- .../tests/unit/volume/v1/test_volume.py | 5 +- openstackclient/tests/unit/volume/v2/fakes.py | 6 +- .../v2/test_consistency_group_snapshot.py | 10 +-- .../tests/unit/volume/v2/test_qos_specs.py | 18 ++-- .../tests/unit/volume/v2/test_volume.py | 5 +- openstackclient/volume/v1/qos_specs.py | 14 +-- openstackclient/volume/v1/service.py | 4 +- openstackclient/volume/v1/volume.py | 18 ++-- openstackclient/volume/v1/volume_backup.py | 12 +-- openstackclient/volume/v1/volume_snapshot.py | 14 +-- .../volume/v1/volume_transfer_request.py | 10 +-- openstackclient/volume/v1/volume_type.py | 14 +-- openstackclient/volume/v2/backup_record.py | 4 +- .../volume/v2/consistency_group.py | 16 ++-- .../volume/v2/consistency_group_snapshot.py | 16 +--- openstackclient/volume/v2/qos_specs.py | 14 +-- openstackclient/volume/v2/service.py | 4 +- openstackclient/volume/v2/volume.py | 16 ++-- openstackclient/volume/v2/volume_backend.py | 4 +- openstackclient/volume/v2/volume_backup.py | 10 +-- openstackclient/volume/v2/volume_host.py | 4 +- openstackclient/volume/v2/volume_snapshot.py | 14 +-- .../volume/v2/volume_transfer_request.py | 10 +-- openstackclient/volume/v2/volume_type.py | 14 +-- openstackclient/volume/v3/volume_message.py | 2 +- releasenotes/source/conf.py | 1 - 274 files changed, 1291 insertions(+), 1325 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 0500d482d..6ee314555 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # OpenStack Command Line Client documentation build configuration file, created # by sphinx-quickstart on Wed May 16 12:05:58 2012. diff --git a/doc/source/contributor/command-errors.rst b/doc/source/contributor/command-errors.rst index c4adb7d19..453ae3a6e 100644 --- a/doc/source/contributor/command-errors.rst +++ b/doc/source/contributor/command-errors.rst @@ -45,7 +45,7 @@ raised that includes the name of the file that was attempted to be opened. public_key = parsed_args.public_key if public_key: try: - with io.open( + with open( os.path.expanduser(parsed_args.public_key), "rb" ) as p: diff --git a/openstackclient/api/api.py b/openstackclient/api/api.py index f02c87dcc..15dcdf32f 100644 --- a/openstackclient/api/api.py +++ b/openstackclient/api/api.py @@ -21,7 +21,7 @@ from openstackclient.i18n import _ -class KeystoneSession(object): +class KeystoneSession: """Wrapper for the Keystone Session Restore some requests.session.Session compatibility; @@ -40,7 +40,7 @@ def __init__(self, session=None, endpoint=None, **kwargs): requests on this API. """ - super(KeystoneSession, self).__init__() + super().__init__() # a requests.Session-style interface self.session = session @@ -95,7 +95,7 @@ def __init__( requests on this API. """ - super(BaseAPI, self).__init__(session=session, endpoint=endpoint) + super().__init__(session=session, endpoint=endpoint) self.service_type = service_type @@ -303,7 +303,7 @@ def find( """ try: - ret = self._request('GET', "/%s/%s" % (path, value)).json() + ret = self._request('GET', f"/{path}/{value}").json() except ks_exceptions.NotFound: kwargs = {attr: value} try: diff --git a/openstackclient/api/compute_v2.py b/openstackclient/api/compute_v2.py index aa0554b17..1ad572b13 100644 --- a/openstackclient/api/compute_v2.py +++ b/openstackclient/api/compute_v2.py @@ -30,7 +30,7 @@ class APIv2(api.BaseAPI): """Compute v2 API""" def __init__(self, **kwargs): - super(APIv2, self).__init__(**kwargs) + super().__init__(**kwargs) # Overrides @@ -76,7 +76,7 @@ def find( """ try: - ret = self._request('GET', "/%s/%s" % (path, value)).json() + ret = self._request('GET', f"/{path}/{value}").json() if isinstance(ret, dict): # strip off the enclosing dict key = list(ret.keys())[0] @@ -136,7 +136,7 @@ def floating_ip_add( return self._request( "POST", - "/%s/%s/action" % (url, server['id']), + "/{}/{}/action".format(url, server['id']), json={'addFloatingIp': body}, ) @@ -180,7 +180,7 @@ def floating_ip_delete( url = "/os-floating-ips" if floating_ip_id is not None: - return self.delete('/%s/%s' % (url, floating_ip_id)) + return self.delete(f'/{url}/{floating_ip_id}') return None @@ -248,7 +248,7 @@ def floating_ip_remove( return self._request( "POST", - "/%s/%s/action" % (url, server['id']), + "/{}/{}/action".format(url, server['id']), json={'removeFloatingIp': body}, ) @@ -316,7 +316,7 @@ def host_set( else: return self._request( "PUT", - "/%s/%s" % (url, host), + f"/{url}/{host}", json=params, ).json() @@ -398,7 +398,7 @@ def network_delete( value=network, )['id'] if network is not None: - return self.delete('/%s/%s' % (url, network)) + return self.delete(f'/{url}/{network}') return None @@ -487,7 +487,7 @@ def security_group_delete( value=security_group, )['id'] if security_group is not None: - return self.delete('/%s/%s' % (url, security_group)) + return self.delete(f'/{url}/{security_group}') return None @@ -535,7 +535,7 @@ def security_group_list( params = {} if search_opts is not None: - params = dict((k, v) for (k, v) in search_opts.items() if v) + params = {k: v for (k, v) in search_opts.items() if v} if limit: params['limit'] = limit if marker: @@ -549,7 +549,7 @@ def security_group_set( security_group=None, # name=None, # description=None, - **params + **params, ): """Update a security group @@ -579,7 +579,7 @@ def security_group_set( security_group[k] = v return self._request( "PUT", - "/%s/%s" % (url, security_group['id']), + "/{}/{}".format(url, security_group['id']), json={'security_group': security_group}, ).json()['security_group'] return None @@ -648,6 +648,6 @@ def security_group_rule_delete( url = "/os-security-group-rules" if security_group_rule_id is not None: - return self.delete('/%s/%s' % (url, security_group_rule_id)) + return self.delete(f'/{url}/{security_group_rule_id}') return None diff --git a/openstackclient/api/image_v1.py b/openstackclient/api/image_v1.py index 1bd15069b..a8b61aca9 100644 --- a/openstackclient/api/image_v1.py +++ b/openstackclient/api/image_v1.py @@ -22,7 +22,7 @@ class APIv1(api.BaseAPI): _endpoint_suffix = '/v1' def __init__(self, endpoint=None, **kwargs): - super(APIv1, self).__init__(endpoint=endpoint, **kwargs) + super().__init__(endpoint=endpoint, **kwargs) self.endpoint = self.endpoint.rstrip('/') self._munge_url() diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py index ac40cf6c5..bc49ac850 100644 --- a/openstackclient/api/object_store_v1.py +++ b/openstackclient/api/object_store_v1.py @@ -13,7 +13,6 @@ """Object Store v1 API Library""" -import io import logging import os import sys @@ -33,7 +32,7 @@ class APIv1(api.BaseAPI): """Object Store v1 API""" def __init__(self, **kwargs): - super(APIv1, self).__init__(**kwargs) + super().__init__(**kwargs) def container_create( self, container=None, public=False, storage_policy=None @@ -257,11 +256,11 @@ def object_create( # object's name in the container. object_name_str = name if name else object - full_url = "%s/%s" % ( + full_url = "{}/{}".format( urllib.parse.quote(container), urllib.parse.quote(object_name_str), ) - with io.open(object, 'rb') as f: + with open(object, 'rb') as f: response = self.create( full_url, method='PUT', diff --git a/openstackclient/common/availability_zone.py b/openstackclient/common/availability_zone.py index 5d62ecd12..acc661a8d 100644 --- a/openstackclient/common/availability_zone.py +++ b/openstackclient/common/availability_zone.py @@ -47,7 +47,7 @@ def _xform_compute_availability_zone(az, include_extra): for svc, state in services.items(): info = copy.deepcopy(host_info) info['service_name'] = svc - info['service_status'] = '%s %s %s' % ( + info['service_status'] = '{} {} {}'.format( 'enabled' if state['active'] else 'disabled', ':-)' if state['available'] else 'XXX', state['updated_at'], diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 35c79247f..8a67dc0c0 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -51,7 +51,7 @@ def __init__( api_version=None, pw_func=None, ): - super(ClientManager, self).__init__( + super().__init__( cli_options=cli_options, api_version=api_version, pw_func=pw_func, @@ -94,7 +94,7 @@ def setup_auth(self): except TypeError as e: self._fallback_load_auth_plugin(e) - return super(ClientManager, self).setup_auth() + return super().setup_auth() def _fallback_load_auth_plugin(self, e): # NOTES(RuiChen): Hack to avoid auth plugins choking on data they don't @@ -170,7 +170,9 @@ def get_plugin_modules(group): module = importlib.import_module(module_name) except Exception as err: sys.stderr.write( - "WARNING: Failed to import plugin %s: %s.\n" % (ep.name, err) + "WARNING: Failed to import plugin {}: {}.\n".format( + ep.name, err + ) ) continue diff --git a/openstackclient/common/configuration.py b/openstackclient/common/configuration.py index 4aa690a56..418f7fdd0 100644 --- a/openstackclient/common/configuration.py +++ b/openstackclient/common/configuration.py @@ -27,7 +27,7 @@ class ShowConfiguration(command.ShowOne): auth_required = False def get_parser(self, prog_name): - parser = super(ShowConfiguration, self).get_parser(prog_name) + parser = super().get_parser(prog_name) mask_group = parser.add_mutually_exclusive_group() mask_group.add_argument( "--mask", diff --git a/openstackclient/common/extension.py b/openstackclient/common/extension.py index f00bacaa9..ad7b1a934 100644 --- a/openstackclient/common/extension.py +++ b/openstackclient/common/extension.py @@ -148,7 +148,7 @@ class ShowExtension(command.ShowOne): _description = _("Show API extension") def get_parser(self, prog_name): - parser = super(ShowExtension, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'extension', metavar='', diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py index 7353b3505..7050d2fd0 100644 --- a/openstackclient/common/limits.py +++ b/openstackclient/common/limits.py @@ -28,7 +28,7 @@ class ShowLimits(command.Lister): _description = _("Show compute and block storage limits") def get_parser(self, prog_name): - parser = super(ShowLimits, self).get_parser(prog_name) + parser = super().get_parser(prog_name) type_group = parser.add_mutually_exclusive_group(required=True) type_group.add_argument( "--absolute", diff --git a/openstackclient/common/module.py b/openstackclient/common/module.py index 585fdf823..e9102561e 100644 --- a/openstackclient/common/module.py +++ b/openstackclient/common/module.py @@ -29,7 +29,7 @@ class ListCommand(command.Lister): auth_required = False def get_parser(self, prog_name): - parser = super(ListCommand, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--group', metavar='', @@ -72,7 +72,7 @@ class ListModule(command.ShowOne): auth_required = False def get_parser(self, prog_name): - parser = super(ListModule, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--all', action='store_true', diff --git a/openstackclient/common/progressbar.py b/openstackclient/common/progressbar.py index 98747ea12..2852bb250 100644 --- a/openstackclient/common/progressbar.py +++ b/openstackclient/common/progressbar.py @@ -16,7 +16,7 @@ import sys -class _ProgressBarBase(object): +class _ProgressBarBase: """A progress bar provider for a wrapped object. Base abstract class used by specific class wrapper to show @@ -39,7 +39,7 @@ def _display_progress_bar(self, size_read): self._percent += size_read / self._totalsize # Output something like this: [==========> ] 49% sys.stdout.write( - '\r[{0:<30}] {1:.0%}'.format( + '\r[{:<30}] {:.0%}'.format( '=' * int(round(self._percent * 29)) + '>', self._percent ) ) diff --git a/openstackclient/common/project_cleanup.py b/openstackclient/common/project_cleanup.py index fdd5b3b7f..2ac156cba 100644 --- a/openstackclient/common/project_cleanup.py +++ b/openstackclient/common/project_cleanup.py @@ -46,7 +46,7 @@ class ProjectCleanup(command.Command): _description = _("Clean resources associated with a project") def get_parser(self, prog_name): - parser = super(ProjectCleanup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) action_group = parser.add_mutually_exclusive_group() action_group.add_argument( '--dry-run', diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 409fea6c3..78688dfa8 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -383,7 +383,7 @@ def take_action(self, parsed_args): and ex.http_status <= 499 ): # Project not found, move on to next one - LOG.warning("Project %s not found: %s" % (p, ex)) + LOG.warning(f"Project {p} not found: {ex}") continue else: raise @@ -446,7 +446,7 @@ def take_action(self, parsed_args): except Exception as ex: if type(ex).__name__ == 'NotFound': # Project not found, move on to next one - LOG.warning("Project %s not found: %s" % (p, ex)) + LOG.warning(f"Project {p} not found: {ex}") continue else: raise @@ -500,7 +500,7 @@ def take_action(self, parsed_args): except Exception as ex: if type(ex).__name__ == 'NotFound': # Project not found, move on to next one - LOG.warning("Project %s not found: %s" % (p, ex)) + LOG.warning(f"Project {p} not found: {ex}") continue else: raise @@ -590,7 +590,7 @@ def _build_options_list(self): return rets def get_parser(self, prog_name): - parser = super(SetQuota, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'project', metavar='', diff --git a/openstackclient/common/versions.py b/openstackclient/common/versions.py index b7ebf02eb..662233875 100644 --- a/openstackclient/common/versions.py +++ b/openstackclient/common/versions.py @@ -23,7 +23,7 @@ class ShowVersions(command.Lister): _description = _("Show available versions of services") def get_parser(self, prog_name): - parser = super(ShowVersions, self).get_parser(prog_name) + parser = super().get_parser(prog_name) interface_group = parser.add_mutually_exclusive_group() interface_group.add_argument( "--all-interfaces", diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py index 9b07df6e1..809c20f98 100644 --- a/openstackclient/compute/v2/agent.py +++ b/openstackclient/compute/v2/agent.py @@ -36,7 +36,7 @@ class CreateAgent(command.ShowOne): """ def get_parser(self, prog_name): - parser = super(CreateAgent, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument("os", metavar="", help=_("Type of OS")) parser.add_argument( "architecture", @@ -77,7 +77,7 @@ class DeleteAgent(command.Command): """ def get_parser(self, prog_name): - parser = super(DeleteAgent, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "id", metavar="", nargs='+', help=_("ID of agent(s) to delete") ) @@ -114,7 +114,7 @@ class ListAgent(command.Lister): """ def get_parser(self, prog_name): - parser = super(ListAgent, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "--hypervisor", metavar="", @@ -155,7 +155,7 @@ class SetAgent(command.Command): """ def get_parser(self, prog_name): - parser = super(SetAgent, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument("id", metavar="", help=_("ID of the agent")) parser.add_argument( "--agent-version", diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index 49b58d179..967eec02c 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -55,7 +55,7 @@ class AddAggregateHost(command.ShowOne): _description = _("Add host to aggregate") def get_parser(self, prog_name): - parser = super(AddAggregateHost, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'aggregate', metavar='', @@ -88,7 +88,7 @@ class CreateAggregate(command.ShowOne): _description = _("Create a new aggregate") def get_parser(self, prog_name): - parser = super(CreateAggregate, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "name", metavar="", help=_("New aggregate name") ) @@ -136,7 +136,7 @@ class DeleteAggregate(command.Command): _description = _("Delete existing aggregate(s)") def get_parser(self, prog_name): - parser = super(DeleteAggregate, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'aggregate', metavar='', @@ -178,7 +178,7 @@ class ListAggregate(command.Lister): _description = _("List all aggregates") def get_parser(self, prog_name): - parser = super(ListAggregate, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -232,7 +232,7 @@ class RemoveAggregateHost(command.ShowOne): _description = _("Remove host from aggregate") def get_parser(self, prog_name): - parser = super(RemoveAggregateHost, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'aggregate', metavar='', @@ -265,7 +265,7 @@ class SetAggregate(command.Command): _description = _("Set aggregate properties") def get_parser(self, prog_name): - parser = super(SetAggregate, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'aggregate', metavar='', @@ -337,7 +337,7 @@ class ShowAggregate(command.ShowOne): _description = _("Display aggregate details") def get_parser(self, prog_name): - parser = super(ShowAggregate, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'aggregate', metavar='', @@ -366,7 +366,7 @@ class UnsetAggregate(command.Command): _description = _("Unset aggregate properties") def get_parser(self, prog_name): - parser = super(UnsetAggregate, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "aggregate", metavar="", @@ -403,7 +403,7 @@ class CacheImageForAggregate(command.Command): # not be anything to return. def get_parser(self, prog_name): - parser = super(CacheImageForAggregate, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'aggregate', metavar='', diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py index bd6d5584e..3059cb100 100644 --- a/openstackclient/compute/v2/console.py +++ b/openstackclient/compute/v2/console.py @@ -36,7 +36,7 @@ class ShowConsoleLog(command.Command): _description = _("Show server's console output") def get_parser(self, prog_name): - parser = super(ShowConsoleLog, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -78,7 +78,7 @@ class ShowConsoleURL(command.ShowOne): _description = _("Show server's remote console URL") def get_parser(self, prog_name): - parser = super(ShowConsoleURL, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index aa8c010f1..e4df62885 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -58,7 +58,7 @@ class CreateFlavor(command.ShowOne): _description = _("Create new flavor") def get_parser(self, prog_name): - parser = super(CreateFlavor, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "name", metavar="", help=_("New flavor name") ) @@ -214,7 +214,7 @@ class DeleteFlavor(command.Command): _description = _("Delete flavor(s)") def get_parser(self, prog_name): - parser = super(DeleteFlavor, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "flavor", metavar="", @@ -253,7 +253,7 @@ class ListFlavor(command.Lister): _description = _("List flavors") def get_parser(self, prog_name): - parser = super(ListFlavor, self).get_parser(prog_name) + parser = super().get_parser(prog_name) public_group = parser.add_mutually_exclusive_group() public_group.add_argument( "--public", @@ -375,7 +375,7 @@ class SetFlavor(command.Command): _description = _("Set flavor properties") def get_parser(self, prog_name): - parser = super(SetFlavor, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "flavor", metavar="", @@ -491,7 +491,7 @@ class ShowFlavor(command.ShowOne): _description = _("Display flavor details") def get_parser(self, prog_name): - parser = super(ShowFlavor, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "flavor", metavar="", @@ -540,7 +540,7 @@ class UnsetFlavor(command.Command): _description = _("Unset flavor properties") def get_parser(self, prog_name): - parser = super(UnsetFlavor, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "flavor", metavar="", diff --git a/openstackclient/compute/v2/host.py b/openstackclient/compute/v2/host.py index a5946d1d4..20925682d 100644 --- a/openstackclient/compute/v2/host.py +++ b/openstackclient/compute/v2/host.py @@ -65,7 +65,7 @@ class SetHost(command.Command): _description = _("Set host properties") def get_parser(self, prog_name): - parser = super(SetHost, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "host", metavar="", help=_("Host to modify (name only)") ) diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index 5d7815b26..8e4389953 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -16,7 +16,6 @@ """Keypair action implementations""" import collections -import io import logging import os @@ -77,7 +76,7 @@ class CreateKeypair(command.ShowOne): _description = _("Create new public or private key for server ssh access") def get_parser(self, prog_name): - parser = super(CreateKeypair, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', help=_("New public or private key name") ) @@ -130,9 +129,9 @@ def take_action(self, parsed_args): if parsed_args.public_key: generated_keypair = None try: - with io.open(os.path.expanduser(parsed_args.public_key)) as p: + with open(os.path.expanduser(parsed_args.public_key)) as p: public_key = p.read() - except IOError as e: + except OSError as e: msg = _("Key file %(public_key)s not found: %(exception)s") raise exceptions.CommandError( msg @@ -150,11 +149,11 @@ def take_action(self, parsed_args): # If user have us a file, save private key into specified file if parsed_args.private_key: try: - with io.open( + with open( os.path.expanduser(parsed_args.private_key), 'w+' ) as p: p.write(generated_keypair.private_key) - except IOError as e: + except OSError as e: msg = _( "Key file %(private_key)s can not be saved: " "%(exception)s" @@ -212,7 +211,7 @@ class DeleteKeypair(command.Command): _description = _("Delete public or private key(s)") def get_parser(self, prog_name): - parser = super(DeleteKeypair, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -388,7 +387,7 @@ class ShowKeypair(command.ShowOne): _description = _("Display key details") def get_parser(self, prog_name): - parser = super(ShowKeypair, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 1d97c349c..32aaf77f6 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -17,7 +17,6 @@ import argparse import getpass -import io import json import logging import os @@ -233,7 +232,7 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): image_id = image_info.get('id', '') try: image = image_client.get_image(image_id) - info['image'] = "%s (%s)" % (image.name, image_id) + info['image'] = f"{image.name} ({image_id})" except Exception: info['image'] = image_id else: @@ -251,7 +250,7 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): flavor_id = flavor_info.get('id', '') try: flavor = utils.find_resource(compute_client.flavors, flavor_id) - info['flavor'] = "%s (%s)" % (flavor.name, flavor_id) + info['flavor'] = f"{flavor.name} ({flavor_id})" except Exception: info['flavor'] = flavor_id else: # microversion >= 2.47 @@ -358,7 +357,7 @@ class AddFixedIP(command.ShowOne): _description = _("Add fixed IP address to server") def get_parser(self, prog_name): - parser = super(AddFixedIP, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "server", metavar="", @@ -551,7 +550,7 @@ class AddPort(command.Command): _description = _("Add port to server") def get_parser(self, prog_name): - parser = super(AddPort, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "server", metavar="", @@ -605,7 +604,7 @@ class AddNetwork(command.Command): _description = _("Add network to server") def get_parser(self, prog_name): - parser = super(AddNetwork, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "server", metavar="", @@ -660,7 +659,7 @@ class AddServerSecurityGroup(command.Command): _description = _("Add security group to server") def get_parser(self, prog_name): - parser = super(AddServerSecurityGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -696,7 +695,7 @@ class AddServerVolume(command.ShowOne): ) def get_parser(self, prog_name): - parser = super(AddServerVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -1050,7 +1049,7 @@ class CreateServer(command.ShowOne): _description = _("Create a new server") def get_parser(self, prog_name): - parser = super(CreateServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server_name', metavar='', @@ -1519,7 +1518,7 @@ def _match_image(image_api, wanted_properties): img_dict_items.extend(list(img.properties.items())) for key, value in img_dict_items: try: - set([key, value]) + {key, value} except TypeError: if key != 'properties': LOG.debug( @@ -1595,8 +1594,8 @@ def _match_image(image_api, wanted_properties): for f in parsed_args.file: dst, src = f.split('=', 1) try: - files[dst] = io.open(src, 'rb') - except IOError as e: + files[dst] = open(src, 'rb') + except OSError as e: msg = _("Can't open '%(source)s': %(exception)s") raise exceptions.CommandError( msg % {'source': src, 'exception': e} @@ -1617,8 +1616,8 @@ def _match_image(image_api, wanted_properties): userdata = None if parsed_args.user_data: try: - userdata = io.open(parsed_args.user_data) - except IOError as e: + userdata = open(parsed_args.user_data) + except OSError as e: msg = _("Can't open '%(data)s': %(exception)s") raise exceptions.CommandError( msg % {'data': parsed_args.user_data, 'exception': e} @@ -2065,7 +2064,7 @@ class CreateServerDump(command.Command): """ def get_parser(self, prog_name): - parser = super(CreateServerDump, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -2085,7 +2084,7 @@ class DeleteServer(command.Command): _description = _("Delete server(s)") def get_parser(self, prog_name): - parser = super(DeleteServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -2190,7 +2189,7 @@ class ListServer(command.Lister): _description = _("List servers") def get_parser(self, prog_name): - parser = super(ListServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--reservation-id', metavar='', @@ -2876,11 +2875,11 @@ def take_action(self, parsed_args): # present on microversion 2.47 or later and 'flavor' won't be # present if there are infra failures if parsed_args.name_lookup_one_by_one or flavor_id: - for f_id in set( + for f_id in { s.flavor['id'] for s in data if s.flavor and s.flavor.get('id') - ): + }: try: flavors[f_id] = compute_client.find_flavor( f_id, ignore_missing=False @@ -2988,7 +2987,7 @@ class LockServer(command.Command): ) def get_parser(self, prog_name): - parser = super(LockServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -3052,7 +3051,7 @@ class MigrateServer(command.Command): ) def get_parser(self, prog_name): - parser = super(MigrateServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -3226,7 +3225,7 @@ class PauseServer(command.Command): _description = _("Pause server(s)") def get_parser(self, prog_name): - parser = super(PauseServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -3309,7 +3308,7 @@ class RebuildServer(command.ShowOne): _description = _("Rebuild server") def get_parser(self, prog_name): - parser = super(RebuildServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -3564,8 +3563,8 @@ def _show_progress(progress): raise exceptions.CommandError(msg) try: - userdata = io.open(parsed_args.user_data) - except IOError as e: + userdata = open(parsed_args.user_data) + except OSError as e: msg = _("Can't open '%(data)s': %(exception)s") raise exceptions.CommandError( msg % {'data': parsed_args.user_data, 'exception': e} @@ -3700,7 +3699,7 @@ class EvacuateServer(command.ShowOne): ) def get_parser(self, prog_name): - parser = super(EvacuateServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -3807,7 +3806,7 @@ class RemoveFixedIP(command.Command): _description = _("Remove fixed IP address from server") def get_parser(self, prog_name): - parser = super(RemoveFixedIP, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "server", metavar="", @@ -3869,7 +3868,7 @@ class RemovePort(command.Command): _description = _("Remove port from server") def get_parser(self, prog_name): - parser = super(RemovePort, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "server", metavar="", @@ -3908,7 +3907,7 @@ class RemoveNetwork(command.Command): _description = _("Remove all ports of a network from server") def get_parser(self, prog_name): - parser = super(RemoveNetwork, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "server", metavar="", @@ -3948,7 +3947,7 @@ class RemoveServerSecurityGroup(command.Command): _description = _("Remove security group from server") def get_parser(self, prog_name): - parser = super(RemoveServerSecurityGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -3984,7 +3983,7 @@ class RemoveServerVolume(command.Command): ) def get_parser(self, prog_name): - parser = super(RemoveServerVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -4026,7 +4025,7 @@ class RescueServer(command.Command): ) def get_parser(self, prog_name): - parser = super(RescueServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -4078,7 +4077,7 @@ class ResizeServer(command.Command): ) def get_parser(self, prog_name): - parser = super(ResizeServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) phase_group = parser.add_mutually_exclusive_group() parser.add_argument( 'server', @@ -4176,7 +4175,7 @@ class ResizeConfirm(command.Command): ) def get_parser(self, prog_name): - parser = super(ResizeConfirm, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -4225,7 +4224,7 @@ class ResizeRevert(command.Command): ) def get_parser(self, prog_name): - parser = super(ResizeRevert, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -4269,7 +4268,7 @@ class RestoreServer(command.Command): _description = _("Restore server(s)") def get_parser(self, prog_name): - parser = super(RestoreServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -4292,7 +4291,7 @@ class ResumeServer(command.Command): _description = _("Resume server(s)") def get_parser(self, prog_name): - parser = super(ResumeServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -4315,7 +4314,7 @@ class SetServer(command.Command): _description = _("Set server properties") def get_parser(self, prog_name): - parser = super(SetServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -4589,7 +4588,7 @@ class ShowServer(command.ShowOne): ) def get_parser(self, prog_name): - parser = super(ShowServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -4657,7 +4656,7 @@ class SshServer(command.Command): _description = _("SSH to server") def get_parser(self, prog_name): - parser = super(SshServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -4809,7 +4808,7 @@ def take_action(self, parsed_args): ) cmd = ' '.join(['ssh', ip_address] + args) - LOG.debug("ssh command: {cmd}".format(cmd=cmd)) + 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 @@ -4899,7 +4898,7 @@ class SuspendServer(command.Command): _description = _("Suspend server(s)") def get_parser(self, prog_name): - parser = super(SuspendServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -4945,7 +4944,7 @@ class UnpauseServer(command.Command): _description = _("Unpause server(s)") def get_parser(self, prog_name): - parser = super(UnpauseServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -4968,7 +4967,7 @@ class UnrescueServer(command.Command): _description = _("Restore server from rescue mode") def get_parser(self, prog_name): - parser = super(UnrescueServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -4988,7 +4987,7 @@ class UnsetServer(command.Command): _description = _("Unset server properties and tags") def get_parser(self, prog_name): - parser = super(UnsetServer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py index ae2154756..59bd5540e 100644 --- a/openstackclient/compute/v2/server_backup.py +++ b/openstackclient/compute/v2/server_backup.py @@ -33,7 +33,7 @@ class CreateServerBackup(command.ShowOne): } def get_parser(self, prog_name): - parser = super(CreateServerBackup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py index 046057600..c4fe666e1 100644 --- a/openstackclient/compute/v2/server_group.py +++ b/openstackclient/compute/v2/server_group.py @@ -56,7 +56,7 @@ class CreateServerGroup(command.ShowOne): _description = _("Create a new server group.") def get_parser(self, prog_name): - parser = super(CreateServerGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -143,7 +143,7 @@ class DeleteServerGroup(command.Command): _description = _("Delete existing server group(s).") def get_parser(self, prog_name): - parser = super(DeleteServerGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server_group', metavar='', @@ -178,7 +178,7 @@ class ListServerGroup(command.Lister): _description = _("List all server groups.") def get_parser(self, prog_name): - parser = super(ListServerGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--all-projects', action='store_true', @@ -255,7 +255,7 @@ class ShowServerGroup(command.ShowOne): _description = _("Display server group details.") def get_parser(self, prog_name): - parser = super(ShowServerGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server_group', metavar='', diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py index ec4b7b23e..12b45b285 100644 --- a/openstackclient/compute/v2/server_image.py +++ b/openstackclient/compute/v2/server_image.py @@ -38,7 +38,7 @@ class CreateServerImage(command.ShowOne): } def get_parser(self, prog_name): - parser = super(CreateServerImage, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', diff --git a/openstackclient/compute/v2/server_migration.py b/openstackclient/compute/v2/server_migration.py index 18aaf407b..18299156a 100644 --- a/openstackclient/compute/v2/server_migration.py +++ b/openstackclient/compute/v2/server_migration.py @@ -28,7 +28,7 @@ class ListMigration(command.Lister): _description = _("""List server migrations""") def get_parser(self, prog_name): - parser = super(ListMigration, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--server', metavar='', @@ -381,7 +381,7 @@ class AbortMigration(command.Command): """ def get_parser(self, prog_name): - parser = super(AbortMigration, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', @@ -448,7 +448,7 @@ class ForceCompleteMigration(command.Command): """ def get_parser(self, prog_name): - parser = super(ForceCompleteMigration, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'server', metavar='', diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index 1cf59ab00..d6e522d03 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -32,7 +32,7 @@ class DeleteService(command.Command): _description = _("Delete compute service(s)") def get_parser(self, prog_name): - parser = super(DeleteService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "service", metavar="", @@ -85,7 +85,7 @@ class ListService(command.Lister): ) def get_parser(self, prog_name): - parser = super(ListService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "--host", metavar="", @@ -147,7 +147,7 @@ class SetService(command.Command): _description = _("Set compute service properties") def get_parser(self, prog_name): - parser = super(SetService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument("host", metavar="", help=_("Name of host")) parser.add_argument( "service", diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index 0d62bc6a0..84118b9d9 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -109,7 +109,7 @@ class ListUsage(command.Lister): _description = _("List resource usage per project") def get_parser(self, prog_name): - parser = super(ListUsage, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "--start", metavar="", @@ -210,7 +210,7 @@ class ShowUsage(command.ShowOne): _description = _("Show resource usage for a single project") def get_parser(self, prog_name): - parser = super(ShowUsage, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "--project", metavar="", diff --git a/openstackclient/identity/v2_0/catalog.py b/openstackclient/identity/v2_0/catalog.py index bea97cce5..ccac5d04b 100644 --- a/openstackclient/identity/v2_0/catalog.py +++ b/openstackclient/identity/v2_0/catalog.py @@ -39,7 +39,7 @@ def human_readable(self): for endpoint_type in ['publicURL', 'internalURL', 'adminURL']: url = ep.get(endpoint_type) if url: - ret += " %s: %s\n" % (endpoint_type, url) + ret += f" {endpoint_type}: {url}\n" return ret @@ -75,7 +75,7 @@ class ShowCatalog(command.ShowOne): _description = _("Display service catalog details") def get_parser(self, prog_name): - parser = super(ShowCatalog, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'service', metavar='', diff --git a/openstackclient/identity/v2_0/ec2creds.py b/openstackclient/identity/v2_0/ec2creds.py index 94d683303..6970f46dc 100644 --- a/openstackclient/identity/v2_0/ec2creds.py +++ b/openstackclient/identity/v2_0/ec2creds.py @@ -32,7 +32,7 @@ class CreateEC2Creds(command.ShowOne): _description = _("Create EC2 credentials") def get_parser(self, prog_name): - parser = super(CreateEC2Creds, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--project', metavar='', @@ -86,7 +86,7 @@ class DeleteEC2Creds(command.Command): _description = _("Delete EC2 credentials") def get_parser(self, prog_name): - parser = super(DeleteEC2Creds, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'access_keys', metavar='', @@ -138,7 +138,7 @@ class ListEC2Creds(command.Lister): _description = _("List EC2 credentials") def get_parser(self, prog_name): - parser = super(ListEC2Creds, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--user', metavar='', @@ -179,7 +179,7 @@ class ShowEC2Creds(command.ShowOne): _description = _("Display EC2 credentials details") def get_parser(self, prog_name): - parser = super(ShowEC2Creds, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'access_key', metavar='', diff --git a/openstackclient/identity/v2_0/endpoint.py b/openstackclient/identity/v2_0/endpoint.py index 2b9970a6a..ca11164b6 100644 --- a/openstackclient/identity/v2_0/endpoint.py +++ b/openstackclient/identity/v2_0/endpoint.py @@ -32,7 +32,7 @@ class CreateEndpoint(command.ShowOne): _description = _("Create new endpoint") def get_parser(self, prog_name): - parser = super(CreateEndpoint, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'service', metavar='', @@ -83,7 +83,7 @@ class DeleteEndpoint(command.Command): _description = _("Delete endpoint(s)") def get_parser(self, prog_name): - parser = super(DeleteEndpoint, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'endpoints', metavar='', @@ -121,7 +121,7 @@ class ListEndpoint(command.Lister): _description = _("List endpoints") def get_parser(self, prog_name): - parser = super(ListEndpoint, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -167,7 +167,7 @@ class ShowEndpoint(command.ShowOne): _description = _("Display endpoint details") def get_parser(self, prog_name): - parser = super(ShowEndpoint, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'endpoint_or_service', metavar='', diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py index e2f378db7..160755523 100644 --- a/openstackclient/identity/v2_0/project.py +++ b/openstackclient/identity/v2_0/project.py @@ -34,7 +34,7 @@ class CreateProject(command.ShowOne): _description = _("Create new project") def get_parser(self, prog_name): - parser = super(CreateProject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -108,7 +108,7 @@ class DeleteProject(command.Command): _description = _("Delete project(s)") def get_parser(self, prog_name): - parser = super(DeleteProject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'projects', metavar='', @@ -150,7 +150,7 @@ class ListProject(command.Lister): _description = _("List projects") def get_parser(self, prog_name): - parser = super(ListProject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -193,7 +193,7 @@ class SetProject(command.Command): _description = _("Set project properties") def get_parser(self, prog_name): - parser = super(SetProject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'project', metavar='', @@ -264,7 +264,7 @@ class ShowProject(command.ShowOne): _description = _("Display project details") def get_parser(self, prog_name): - parser = super(ShowProject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'project', metavar='', @@ -322,7 +322,7 @@ class UnsetProject(command.Command): _description = _("Unset project properties") def get_parser(self, prog_name): - parser = super(UnsetProject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'project', metavar='', diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py index 931957bd5..971c61a7f 100644 --- a/openstackclient/identity/v2_0/role.py +++ b/openstackclient/identity/v2_0/role.py @@ -32,7 +32,7 @@ class AddRole(command.ShowOne): _description = _("Add role to project:user") def get_parser(self, prog_name): - parser = super(AddRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'role', metavar='', @@ -75,7 +75,7 @@ class CreateRole(command.ShowOne): _description = _("Create new role") def get_parser(self, prog_name): - parser = super(CreateRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'role_name', metavar='', @@ -111,7 +111,7 @@ class DeleteRole(command.Command): _description = _("Delete role(s)") def get_parser(self, prog_name): - parser = super(DeleteRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'roles', metavar='', @@ -176,7 +176,7 @@ class RemoveRole(command.Command): _description = _("Remove role from project : user") def get_parser(self, prog_name): - parser = super(RemoveRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'role', metavar='', @@ -211,7 +211,7 @@ class ShowRole(command.ShowOne): _description = _("Display role details") def get_parser(self, prog_name): - parser = super(ShowRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'role', metavar='', diff --git a/openstackclient/identity/v2_0/role_assignment.py b/openstackclient/identity/v2_0/role_assignment.py index c3d492e27..6610bab94 100644 --- a/openstackclient/identity/v2_0/role_assignment.py +++ b/openstackclient/identity/v2_0/role_assignment.py @@ -24,7 +24,7 @@ class ListRoleAssignment(command.Lister): _description = _("List role assignments") def get_parser(self, prog_name): - parser = super(ListRoleAssignment, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--user', metavar='', diff --git a/openstackclient/identity/v2_0/service.py b/openstackclient/identity/v2_0/service.py index 15203e694..362dc4515 100644 --- a/openstackclient/identity/v2_0/service.py +++ b/openstackclient/identity/v2_0/service.py @@ -32,7 +32,7 @@ class CreateService(command.ShowOne): _description = _("Create new service") def get_parser(self, prog_name): - parser = super(CreateService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'type', metavar='', @@ -71,7 +71,7 @@ class DeleteService(command.Command): _description = _("Delete service(s)") def get_parser(self, prog_name): - parser = super(DeleteService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'services', metavar='', @@ -110,7 +110,7 @@ class ListService(command.Lister): _description = _("List services") def get_parser(self, prog_name): - parser = super(ListService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -135,7 +135,7 @@ class ShowService(command.ShowOne): _description = _("Display service details") def get_parser(self, prog_name): - parser = super(ShowService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'service', metavar='', diff --git a/openstackclient/identity/v2_0/token.py b/openstackclient/identity/v2_0/token.py index 8759df8d9..fe9436d8b 100644 --- a/openstackclient/identity/v2_0/token.py +++ b/openstackclient/identity/v2_0/token.py @@ -28,7 +28,7 @@ class IssueToken(command.ShowOne): required_scope = False def get_parser(self, prog_name): - parser = super(IssueToken, self).get_parser(prog_name) + parser = super().get_parser(prog_name) return parser def take_action(self, parsed_args): @@ -56,7 +56,7 @@ class RevokeToken(command.Command): _description = _("Revoke existing token") def get_parser(self, prog_name): - parser = super(RevokeToken, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'token', metavar='', diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py index 10d2f18c3..745a48d11 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -42,7 +42,7 @@ class takes project_cache as the second argument. """ def __init__(self, value, project_cache=None): - super(ProjectColumn, self).__init__(value) + super().__init__(value) self.project_cache = project_cache or {} def human_readable(self): @@ -59,7 +59,7 @@ class CreateUser(command.ShowOne): _description = _("Create new user") def get_parser(self, prog_name): - parser = super(CreateUser, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -162,7 +162,7 @@ class DeleteUser(command.Command): _description = _("Delete user(s)") def get_parser(self, prog_name): - parser = super(DeleteUser, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'users', metavar='', @@ -205,7 +205,7 @@ class ListUser(command.Lister): _description = _("List users") def get_parser(self, prog_name): - parser = super(ListUser, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--project', metavar='', @@ -294,7 +294,7 @@ class SetUser(command.Command): _description = _("Set user properties") def get_parser(self, prog_name): - parser = super(SetUser, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'user', metavar='', @@ -392,7 +392,7 @@ class ShowUser(command.ShowOne): _description = _("Display user details") def get_parser(self, prog_name): - parser = super(ShowUser, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'user', metavar='', diff --git a/openstackclient/identity/v3/access_rule.py b/openstackclient/identity/v3/access_rule.py index b9a66e9a5..655ad14b1 100644 --- a/openstackclient/identity/v3/access_rule.py +++ b/openstackclient/identity/v3/access_rule.py @@ -32,7 +32,7 @@ class DeleteAccessRule(command.Command): _description = _("Delete access rule(s)") def get_parser(self, prog_name): - parser = super(DeleteAccessRule, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'access_rule', metavar='', @@ -73,7 +73,7 @@ class ListAccessRule(command.Lister): _description = _("List access rules") def get_parser(self, prog_name): - parser = super(ListAccessRule, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--user', metavar='', @@ -110,7 +110,7 @@ class ShowAccessRule(command.ShowOne): _description = _("Display access rule details") def get_parser(self, prog_name): - parser = super(ShowAccessRule, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'access_rule', metavar='', diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py index c75de3a46..cffab9927 100644 --- a/openstackclient/identity/v3/application_credential.py +++ b/openstackclient/identity/v3/application_credential.py @@ -34,7 +34,7 @@ class CreateApplicationCredential(command.ShowOne): _description = _("Create new application credential") def get_parser(self, prog_name): - parser = super(CreateApplicationCredential, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -137,7 +137,7 @@ def take_action(self, parsed_args): try: with open(parsed_args.access_rules) as f: access_rules = json.load(f) - except IOError: + except OSError: msg = _( "Access rules is not valid JSON string or file does" " not exist." @@ -171,7 +171,7 @@ class DeleteApplicationCredential(command.Command): _description = _("Delete application credentials(s)") def get_parser(self, prog_name): - parser = super(DeleteApplicationCredential, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'application_credential', metavar='', @@ -213,7 +213,7 @@ class ListApplicationCredential(command.Lister): _description = _("List application credentials") def get_parser(self, prog_name): - parser = super(ListApplicationCredential, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--user', metavar='', @@ -250,7 +250,7 @@ class ShowApplicationCredential(command.ShowOne): _description = _("Display application credential details") def get_parser(self, prog_name): - parser = super(ShowApplicationCredential, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'application_credential', metavar='', diff --git a/openstackclient/identity/v3/catalog.py b/openstackclient/identity/v3/catalog.py index a5c2ee47f..a161461ce 100644 --- a/openstackclient/identity/v3/catalog.py +++ b/openstackclient/identity/v3/catalog.py @@ -34,7 +34,7 @@ def human_readable(self): for ep in self._value: region = ep.get('region_id') or ep.get('region') or '' ret += region + '\n' - ret += " %s: %s\n" % (ep['interface'], ep['url']) + ret += " {}: {}\n".format(ep['interface'], ep['url']) return ret @@ -70,7 +70,7 @@ class ShowCatalog(command.ShowOne): _description = _("Display service catalog details") def get_parser(self, prog_name): - parser = super(ShowCatalog, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'service', metavar='', diff --git a/openstackclient/identity/v3/consumer.py b/openstackclient/identity/v3/consumer.py index c40c98868..924b5726d 100644 --- a/openstackclient/identity/v3/consumer.py +++ b/openstackclient/identity/v3/consumer.py @@ -31,7 +31,7 @@ class CreateConsumer(command.ShowOne): _description = _("Create new consumer") def get_parser(self, prog_name): - parser = super(CreateConsumer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--description', metavar='', @@ -52,7 +52,7 @@ class DeleteConsumer(command.Command): _description = _("Delete consumer(s)") def get_parser(self, prog_name): - parser = super(DeleteConsumer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'consumer', metavar='', @@ -111,7 +111,7 @@ class SetConsumer(command.Command): _description = _("Set consumer properties") def get_parser(self, prog_name): - parser = super(SetConsumer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'consumer', metavar='', @@ -142,7 +142,7 @@ class ShowConsumer(command.ShowOne): _description = _("Display consumer details") def get_parser(self, prog_name): - parser = super(ShowConsumer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'consumer', metavar='', diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py index 80b65befa..1b0d52517 100644 --- a/openstackclient/identity/v3/credential.py +++ b/openstackclient/identity/v3/credential.py @@ -32,7 +32,7 @@ class CreateCredential(command.ShowOne): _description = _("Create new credential") def get_parser(self, prog_name): - parser = super(CreateCredential, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'user', metavar='', @@ -85,7 +85,7 @@ class DeleteCredential(command.Command): _description = _("Delete credential(s)") def get_parser(self, prog_name): - parser = super(DeleteCredential, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'credential', metavar='', @@ -122,7 +122,7 @@ class ListCredential(command.Lister): _description = _("List credentials") def get_parser(self, prog_name): - parser = super(ListCredential, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--user', metavar='', @@ -171,7 +171,7 @@ class SetCredential(command.Command): _description = _("Set credential properties") def get_parser(self, prog_name): - parser = super(SetCredential, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'credential', metavar='', @@ -232,7 +232,7 @@ class ShowCredential(command.ShowOne): _description = _("Display credential details") def get_parser(self, prog_name): - parser = super(ShowCredential, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'credential', metavar='', diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index 1fcf245c8..aaa270830 100644 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -33,7 +33,7 @@ class CreateDomain(command.ShowOne): _description = _("Create new domain") def get_parser(self, prog_name): - parser = super(CreateDomain, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -96,7 +96,7 @@ class DeleteDomain(command.Command): _description = _("Delete domain(s)") def get_parser(self, prog_name): - parser = super(DeleteDomain, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'domain', metavar='', @@ -135,7 +135,7 @@ class ListDomain(command.Lister): _description = _("List domains") def get_parser(self, prog_name): - parser = super(ListDomain, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--name', metavar='', @@ -175,7 +175,7 @@ class SetDomain(command.Command): _description = _("Set domain properties") def get_parser(self, prog_name): - parser = super(SetDomain, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'domain', metavar='', @@ -232,7 +232,7 @@ class ShowDomain(command.ShowOne): _description = _("Display domain details") def get_parser(self, prog_name): - parser = super(ShowDomain, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'domain', metavar='', diff --git a/openstackclient/identity/v3/ec2creds.py b/openstackclient/identity/v3/ec2creds.py index 49bedc746..a9fbe1ae4 100644 --- a/openstackclient/identity/v3/ec2creds.py +++ b/openstackclient/identity/v3/ec2creds.py @@ -60,7 +60,7 @@ class CreateEC2Creds(command.ShowOne): _description = _("Create EC2 credentials") def get_parser(self, prog_name): - parser = super(CreateEC2Creds, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--project', metavar='', @@ -122,7 +122,7 @@ class DeleteEC2Creds(command.Command): _description = _("Delete EC2 credentials") def get_parser(self, prog_name): - parser = super(DeleteEC2Creds, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'access_key', metavar='', @@ -166,7 +166,7 @@ class ListEC2Creds(command.Lister): _description = _("List EC2 credentials") def get_parser(self, prog_name): - parser = super(ListEC2Creds, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--user', metavar='', @@ -200,7 +200,7 @@ class ShowEC2Creds(command.ShowOne): _description = _("Display EC2 credentials details") def get_parser(self, prog_name): - parser = super(ShowEC2Creds, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'access_key', metavar='', diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index 7d954ce12..6e77bdf87 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -39,7 +39,7 @@ class AddProjectToEndpoint(command.Command): _description = _("Associate a project to an endpoint") def get_parser(self, prog_name): - parser = super(AddProjectToEndpoint, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'endpoint', metavar='', @@ -75,7 +75,7 @@ class CreateEndpoint(command.ShowOne): _description = _("Create new endpoint") def get_parser(self, prog_name): - parser = super(CreateEndpoint, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'service', metavar='', @@ -137,7 +137,7 @@ class DeleteEndpoint(command.Command): _description = _("Delete endpoint(s)") def get_parser(self, prog_name): - parser = super(DeleteEndpoint, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'endpoint', metavar='', @@ -177,7 +177,7 @@ class ListEndpoint(command.Lister): _description = _("List endpoints") def get_parser(self, prog_name): - parser = super(ListEndpoint, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--service', metavar='', @@ -285,7 +285,7 @@ class RemoveProjectFromEndpoint(command.Command): _description = _("Dissociate a project from an endpoint") def get_parser(self, prog_name): - parser = super(RemoveProjectFromEndpoint, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'endpoint', metavar='', @@ -321,7 +321,7 @@ class SetEndpoint(command.Command): _description = _("Set endpoint properties") def get_parser(self, prog_name): - parser = super(SetEndpoint, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'endpoint', metavar='', @@ -393,7 +393,7 @@ class ShowEndpoint(command.ShowOne): _description = _("Display endpoint details") def get_parser(self, prog_name): - parser = super(ShowEndpoint, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'endpoint', metavar='', diff --git a/openstackclient/identity/v3/endpoint_group.py b/openstackclient/identity/v3/endpoint_group.py index 1d3e5ddde..b5c2631f0 100644 --- a/openstackclient/identity/v3/endpoint_group.py +++ b/openstackclient/identity/v3/endpoint_group.py @@ -27,7 +27,7 @@ LOG = logging.getLogger(__name__) -class _FiltersReader(object): +class _FiltersReader: _description = _("Helper class capable of reading filters from files") def _read_filters(self, path): @@ -63,7 +63,7 @@ class AddProjectToEndpointGroup(command.Command): _description = _("Add a project to an endpoint group") def get_parser(self, prog_name): - parser = super(AddProjectToEndpointGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'endpointgroup', metavar='', @@ -97,7 +97,7 @@ class CreateEndpointGroup(command.ShowOne, _FiltersReader): _description = _("Create new endpoint group") def get_parser(self, prog_name): - parser = super(CreateEndpointGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -137,7 +137,7 @@ class DeleteEndpointGroup(command.Command): _description = _("Delete endpoint group(s)") def get_parser(self, prog_name): - parser = super(DeleteEndpointGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'endpointgroup', metavar='', @@ -177,7 +177,7 @@ class ListEndpointGroup(command.Lister): _description = _("List endpoint groups") def get_parser(self, prog_name): - parser = super(ListEndpointGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) list_group = parser.add_mutually_exclusive_group() list_group.add_argument( '--endpointgroup', @@ -242,9 +242,7 @@ class RemoveProjectFromEndpointGroup(command.Command): _description = _("Remove project from endpoint group") def get_parser(self, prog_name): - parser = super(RemoveProjectFromEndpointGroup, self).get_parser( - prog_name - ) + parser = super().get_parser(prog_name) parser.add_argument( 'endpointgroup', metavar='', @@ -278,7 +276,7 @@ class SetEndpointGroup(command.Command, _FiltersReader): _description = _("Set endpoint group properties") def get_parser(self, prog_name): - parser = super(SetEndpointGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'endpointgroup', metavar='', @@ -324,7 +322,7 @@ class ShowEndpointGroup(command.ShowOne): _description = _("Display endpoint group details") def get_parser(self, prog_name): - parser = super(ShowEndpointGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'endpointgroup', metavar='', diff --git a/openstackclient/identity/v3/federation_protocol.py b/openstackclient/identity/v3/federation_protocol.py index ae62390df..4e980860d 100644 --- a/openstackclient/identity/v3/federation_protocol.py +++ b/openstackclient/identity/v3/federation_protocol.py @@ -30,7 +30,7 @@ class CreateProtocol(command.ShowOne): _description = _("Create new federation protocol") def get_parser(self, prog_name): - parser = super(CreateProtocol, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'federation_protocol', metavar='', @@ -79,7 +79,7 @@ class DeleteProtocol(command.Command): _description = _("Delete federation protocol(s)") def get_parser(self, prog_name): - parser = super(DeleteProtocol, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'federation_protocol', metavar='', @@ -129,7 +129,7 @@ class ListProtocols(command.Lister): _description = _("List federation protocols") def get_parser(self, prog_name): - parser = super(ListProtocols, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--identity-provider', metavar='', @@ -158,7 +158,7 @@ class SetProtocol(command.Command): _description = _("Set federation protocol properties") def get_parser(self, prog_name): - parser = super(SetProtocol, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'federation_protocol', metavar='', @@ -202,7 +202,7 @@ class ShowProtocol(command.ShowOne): _description = _("Display federation protocol details") def get_parser(self, prog_name): - parser = super(ShowProtocol, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'federation_protocol', metavar='', diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index 65eb261a1..d9c3b93b8 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -33,7 +33,7 @@ class AddUserToGroup(command.Command): _description = _("Add user to group") def get_parser(self, prog_name): - parser = super(AddUserToGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'group', metavar='', @@ -93,7 +93,7 @@ class CheckUserInGroup(command.Command): _description = _("Check user membership in group") def get_parser(self, prog_name): - parser = super(CheckUserInGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'group', metavar='', @@ -141,7 +141,7 @@ class CreateGroup(command.ShowOne): _description = _("Create new group") def get_parser(self, prog_name): - parser = super(CreateGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -194,7 +194,7 @@ class DeleteGroup(command.Command): _description = _("Delete group(s)") def get_parser(self, prog_name): - parser = super(DeleteGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'groups', metavar='', @@ -241,7 +241,7 @@ class ListGroup(command.Lister): _description = _("List groups") def get_parser(self, prog_name): - parser = super(ListGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--domain', metavar='', @@ -304,7 +304,7 @@ class RemoveUserFromGroup(command.Command): _description = _("Remove user from group") def get_parser(self, prog_name): - parser = super(RemoveUserFromGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'group', metavar='', @@ -364,7 +364,7 @@ class SetGroup(command.Command): _description = _("Set group properties") def get_parser(self, prog_name): - parser = super(SetGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'group', metavar='', @@ -405,7 +405,7 @@ class ShowGroup(command.ShowOne): _description = _("Display group details") def get_parser(self, prog_name): - parser = super(ShowGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'group', metavar='', diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py index 230f81985..5e0c8f7c9 100644 --- a/openstackclient/identity/v3/identity_provider.py +++ b/openstackclient/identity/v3/identity_provider.py @@ -31,7 +31,7 @@ class CreateIdentityProvider(command.ShowOne): _description = _("Create new identity provider") def get_parser(self, prog_name): - parser = super(CreateIdentityProvider, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'identity_provider_id', metavar='', @@ -146,7 +146,7 @@ class DeleteIdentityProvider(command.Command): _description = _("Delete identity provider(s)") def get_parser(self, prog_name): - parser = super(DeleteIdentityProvider, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'identity_provider', metavar='', @@ -184,7 +184,7 @@ class ListIdentityProvider(command.Lister): _description = _("List identity providers") def get_parser(self, prog_name): - parser = super(ListIdentityProvider, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--id', metavar='', @@ -226,7 +226,7 @@ class SetIdentityProvider(command.Command): _description = _("Set identity provider properties") def get_parser(self, prog_name): - parser = super(SetIdentityProvider, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'identity_provider', metavar='', @@ -326,7 +326,7 @@ class ShowIdentityProvider(command.ShowOne): _description = _("Display identity provider details") def get_parser(self, prog_name): - parser = super(ShowIdentityProvider, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'identity_provider', metavar='', diff --git a/openstackclient/identity/v3/implied_role.py b/openstackclient/identity/v3/implied_role.py index 9d26ccd3f..3958896b0 100644 --- a/openstackclient/identity/v3/implied_role.py +++ b/openstackclient/identity/v3/implied_role.py @@ -51,7 +51,7 @@ class CreateImpliedRole(command.ShowOne): _description = _("Creates an association between prior and implied roles") def get_parser(self, prog_name): - parser = super(CreateImpliedRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'role', metavar='', @@ -81,7 +81,7 @@ class DeleteImpliedRole(command.Command): _description = _("Deletes an association between prior and implied roles") def get_parser(self, prog_name): - parser = super(DeleteImpliedRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'role', metavar='', @@ -113,7 +113,7 @@ class ListImpliedRole(command.Lister): ] def get_parser(self, prog_name): - parser = super(ListImpliedRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) return parser def take_action(self, parsed_args): diff --git a/openstackclient/identity/v3/limit.py b/openstackclient/identity/v3/limit.py index 1610b68d6..59cc1398c 100644 --- a/openstackclient/identity/v3/limit.py +++ b/openstackclient/identity/v3/limit.py @@ -29,7 +29,7 @@ class CreateLimit(command.ShowOne): _description = _("Create a limit") def get_parser(self, prog_name): - parser = super(CreateLimit, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--description', metavar='', @@ -108,7 +108,7 @@ class ListLimit(command.Lister): _description = _("List limits") def get_parser(self, prog_name): - parser = super(ListLimit, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--service', metavar='', @@ -190,7 +190,7 @@ class ShowLimit(command.ShowOne): _description = _("Display limit details") def get_parser(self, prog_name): - parser = super(ShowLimit, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'limit_id', metavar='', @@ -209,7 +209,7 @@ class SetLimit(command.ShowOne): _description = _("Update information about a limit") def get_parser(self, prog_name): - parser = super(SetLimit, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'limit_id', metavar='', @@ -247,7 +247,7 @@ class DeleteLimit(command.Command): _description = _("Delete a limit") def get_parser(self, prog_name): - parser = super(DeleteLimit, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'limit_id', metavar='', diff --git a/openstackclient/identity/v3/mapping.py b/openstackclient/identity/v3/mapping.py index ea5e7d70a..ff8f2aa31 100644 --- a/openstackclient/identity/v3/mapping.py +++ b/openstackclient/identity/v3/mapping.py @@ -28,7 +28,7 @@ LOG = logging.getLogger(__name__) -class _RulesReader(object): +class _RulesReader: _description = _("Helper class capable of reading rules from files") def _read_rules(self, path): @@ -101,7 +101,7 @@ class CreateMapping(command.ShowOne, _RulesReader): _description = _("Create new mapping") def get_parser(self, prog_name): - parser = super(CreateMapping, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'mapping', metavar='', @@ -134,7 +134,7 @@ class DeleteMapping(command.Command): _description = _("Delete mapping(s)") def get_parser(self, prog_name): - parser = super(DeleteMapping, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'mapping', metavar='', @@ -185,7 +185,7 @@ class SetMapping(command.Command, _RulesReader): _description = _("Set mapping properties") def get_parser(self, prog_name): - parser = super(SetMapping, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'mapping', metavar='', @@ -218,7 +218,7 @@ class ShowMapping(command.ShowOne): _description = _("Display mapping details") def get_parser(self, prog_name): - parser = super(ShowMapping, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'mapping', metavar='', diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py index c6642768b..51428e8c9 100644 --- a/openstackclient/identity/v3/policy.py +++ b/openstackclient/identity/v3/policy.py @@ -31,7 +31,7 @@ class CreatePolicy(command.ShowOne): _description = _("Create new policy") def get_parser(self, prog_name): - parser = super(CreatePolicy, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--type', metavar='', @@ -65,7 +65,7 @@ class DeletePolicy(command.Command): _description = _("Delete policy(s)") def get_parser(self, prog_name): - parser = super(DeletePolicy, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'policy', metavar='', @@ -105,7 +105,7 @@ class ListPolicy(command.Lister): _description = _("List policies") def get_parser(self, prog_name): - parser = super(ListPolicy, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -139,7 +139,7 @@ class SetPolicy(command.Command): _description = _("Set policy properties") def get_parser(self, prog_name): - parser = super(SetPolicy, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'policy', metavar='', @@ -177,7 +177,7 @@ class ShowPolicy(command.ShowOne): _description = _("Display policy details") def get_parser(self, prog_name): - parser = super(ShowPolicy, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'policy', metavar='', diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 6ff264aea..d66fd6121 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -34,7 +34,7 @@ class CreateProject(command.ShowOne): _description = _("Create new project") def get_parser(self, prog_name): - parser = super(CreateProject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -146,7 +146,7 @@ class DeleteProject(command.Command): _description = _("Delete project(s)") def get_parser(self, prog_name): - parser = super(DeleteProject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'projects', metavar='', @@ -200,7 +200,7 @@ class ListProject(command.Lister): _description = _("List projects") def get_parser(self, prog_name): - parser = super(ListProject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--domain', metavar='', @@ -314,7 +314,7 @@ class SetProject(command.Command): _description = _("Set project properties") def get_parser(self, prog_name): - parser = super(SetProject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'project', metavar='', @@ -389,7 +389,7 @@ class ShowProject(command.ShowOne): _description = _("Display project details") def get_parser(self, prog_name): - parser = super(ShowProject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'project', metavar='', diff --git a/openstackclient/identity/v3/region.py b/openstackclient/identity/v3/region.py index 21a80b94f..ba8c02cf2 100644 --- a/openstackclient/identity/v3/region.py +++ b/openstackclient/identity/v3/region.py @@ -29,7 +29,7 @@ class CreateRegion(command.ShowOne): _description = _("Create new region") def get_parser(self, prog_name): - parser = super(CreateRegion, self).get_parser(prog_name) + parser = super().get_parser(prog_name) # NOTE(stevemar): The API supports an optional region ID, but that # seems like poor UX, we will only support user-defined IDs. parser.add_argument( @@ -68,7 +68,7 @@ class DeleteRegion(command.Command): _description = _("Delete region(s)") def get_parser(self, prog_name): - parser = super(DeleteRegion, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'region', metavar='', @@ -106,7 +106,7 @@ class ListRegion(command.Lister): _description = _("List regions") def get_parser(self, prog_name): - parser = super(ListRegion, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--parent-region', metavar='', @@ -142,7 +142,7 @@ class SetRegion(command.Command): _description = _("Set region properties") def get_parser(self, prog_name): - parser = super(SetRegion, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'region', metavar='', @@ -176,7 +176,7 @@ class ShowRegion(command.ShowOne): _description = _("Display region details") def get_parser(self, prog_name): - parser = super(ShowRegion, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'region', metavar='', diff --git a/openstackclient/identity/v3/registered_limit.py b/openstackclient/identity/v3/registered_limit.py index 251eb2dbe..f71ef1f5a 100644 --- a/openstackclient/identity/v3/registered_limit.py +++ b/openstackclient/identity/v3/registered_limit.py @@ -29,7 +29,7 @@ class CreateRegisteredLimit(command.ShowOne): _description = _("Create a registered limit") def get_parser(self, prog_name): - parser = super(CreateRegisteredLimit, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--description', metavar='', @@ -98,7 +98,7 @@ class DeleteRegisteredLimit(command.Command): _description = _("Delete a registered limit") def get_parser(self, prog_name): - parser = super(DeleteRegisteredLimit, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'registered_limit_id', metavar='', @@ -140,7 +140,7 @@ class ListRegisteredLimit(command.Lister): _description = _("List registered limits") def get_parser(self, prog_name): - parser = super(ListRegisteredLimit, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--service', metavar='', @@ -207,7 +207,7 @@ class SetRegisteredLimit(command.ShowOne): _description = _("Update information about a registered limit") def get_parser(self, prog_name): - parser = super(SetRegisteredLimit, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'registered_limit_id', metavar='', @@ -298,7 +298,7 @@ class ShowRegisteredLimit(command.ShowOne): _description = _("Display registered limit details") def get_parser(self, prog_name): - parser = super(ShowRegisteredLimit, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'registered_limit_id', metavar='', diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index 4c4358851..c43f38994 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -134,7 +134,7 @@ class AddRole(command.Command): ) def get_parser(self, prog_name): - parser = super(AddRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'role', metavar='', @@ -179,7 +179,7 @@ class CreateRole(command.ShowOne): _description = _("Create new role") def get_parser(self, prog_name): - parser = super(CreateRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -241,7 +241,7 @@ class DeleteRole(command.Command): _description = _("Delete role(s)") def get_parser(self, prog_name): - parser = super(DeleteRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'roles', metavar='', @@ -293,7 +293,7 @@ class ListRole(command.Lister): _description = _("List roles") def get_parser(self, prog_name): - parser = super(ListRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--domain', metavar='', @@ -336,7 +336,7 @@ class RemoveRole(command.Command): ) def get_parser(self, prog_name): - parser = super(RemoveRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'role', metavar='', @@ -383,7 +383,7 @@ class SetRole(command.Command): _description = _("Set role properties") def get_parser(self, prog_name): - parser = super(SetRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'role', metavar='', @@ -433,7 +433,7 @@ class ShowRole(command.ShowOne): _description = _("Display role details") def get_parser(self, prog_name): - parser = super(ShowRole, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'role', metavar='', diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py index 0be6aafb6..81de1535f 100644 --- a/openstackclient/identity/v3/role_assignment.py +++ b/openstackclient/identity/v3/role_assignment.py @@ -24,7 +24,7 @@ class ListRoleAssignment(command.Lister): _description = _("List role assignments") def get_parser(self, prog_name): - parser = super(ListRoleAssignment, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--effective', action="store_true", diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index fde1799c1..fd2c1df84 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -32,7 +32,7 @@ class CreateService(command.ShowOne): _description = _("Create new service") def get_parser(self, prog_name): - parser = super(CreateService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'type', metavar='', @@ -83,7 +83,7 @@ class DeleteService(command.Command): _description = _("Delete service(s)") def get_parser(self, prog_name): - parser = super(DeleteService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'service', metavar='', @@ -121,7 +121,7 @@ class ListService(command.Lister): _description = _("List services") def get_parser(self, prog_name): - parser = super(ListService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -146,7 +146,7 @@ class SetService(command.Command): _description = _("Set service properties") def get_parser(self, prog_name): - parser = super(SetService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'service', metavar='', @@ -203,7 +203,7 @@ class ShowService(command.ShowOne): _description = _("Display service details") def get_parser(self, prog_name): - parser = super(ShowService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'service', metavar='', diff --git a/openstackclient/identity/v3/service_provider.py b/openstackclient/identity/v3/service_provider.py index e5af74926..79869ee60 100644 --- a/openstackclient/identity/v3/service_provider.py +++ b/openstackclient/identity/v3/service_provider.py @@ -29,7 +29,7 @@ class CreateServiceProvider(command.ShowOne): _description = _("Create new service provider") def get_parser(self, prog_name): - parser = super(CreateServiceProvider, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'service_provider_id', metavar='', @@ -94,7 +94,7 @@ class DeleteServiceProvider(command.Command): _description = _("Delete service provider(s)") def get_parser(self, prog_name): - parser = super(DeleteServiceProvider, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'service_provider', metavar='', @@ -153,7 +153,7 @@ class SetServiceProvider(command.Command): _description = _("Set service provider properties") def get_parser(self, prog_name): - parser = super(SetServiceProvider, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'service_provider', metavar='', @@ -213,7 +213,7 @@ class ShowServiceProvider(command.ShowOne): _description = _("Display service provider details") def get_parser(self, prog_name): - parser = super(ShowServiceProvider, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'service_provider', metavar='', diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py index c11de358a..8f2dc0940 100644 --- a/openstackclient/identity/v3/token.py +++ b/openstackclient/identity/v3/token.py @@ -27,7 +27,7 @@ class AuthorizeRequestToken(command.ShowOne): _description = _("Authorize a request token") def get_parser(self, prog_name): - parser = super(AuthorizeRequestToken, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--request-key', metavar='', @@ -70,7 +70,7 @@ class CreateAccessToken(command.ShowOne): _description = _("Create an access token") def get_parser(self, prog_name): - parser = super(CreateAccessToken, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--consumer-key', metavar='', @@ -119,7 +119,7 @@ class CreateRequestToken(command.ShowOne): _description = _("Create a request token") def get_parser(self, prog_name): - parser = super(CreateRequestToken, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--consumer-key', metavar='', @@ -178,7 +178,7 @@ class IssueToken(command.ShowOne): required_scope = False def get_parser(self, prog_name): - parser = super(IssueToken, self).get_parser(prog_name) + parser = super().get_parser(prog_name) return parser def take_action(self, parsed_args): @@ -214,7 +214,7 @@ class RevokeToken(command.Command): _description = _("Revoke existing token") def get_parser(self, prog_name): - parser = super(RevokeToken, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'token', metavar='', diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py index f2899d594..33fee4a90 100644 --- a/openstackclient/identity/v3/trust.py +++ b/openstackclient/identity/v3/trust.py @@ -32,7 +32,7 @@ class CreateTrust(command.ShowOne): _description = _("Create new trust") def get_parser(self, prog_name): - parser = super(CreateTrust, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'trustor', metavar='', @@ -150,7 +150,7 @@ class DeleteTrust(command.Command): _description = _("Delete trust(s)") def get_parser(self, prog_name): - parser = super(DeleteTrust, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'trust', metavar='', @@ -300,7 +300,7 @@ class ShowTrust(command.ShowOne): _description = _("Display trust details") def get_parser(self, prog_name): - parser = super(ShowTrust, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'trust', metavar='', diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 365240674..38b9a0ab0 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -188,7 +188,7 @@ class CreateUser(command.ShowOne): _description = _("Create new user") def get_parser(self, prog_name): - parser = super(CreateUser, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -308,7 +308,7 @@ class DeleteUser(command.Command): _description = _("Delete user(s)") def get_parser(self, prog_name): - parser = super(DeleteUser, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'users', metavar='', @@ -368,7 +368,7 @@ class ListUser(command.Lister): _description = _("List users") def get_parser(self, prog_name): - parser = super(ListUser, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--domain', metavar='', @@ -488,7 +488,7 @@ class SetUser(command.Command): _description = _("Set user properties") def get_parser(self, prog_name): - parser = super(SetUser, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'user', metavar='', @@ -621,7 +621,7 @@ class SetPasswordUser(command.Command): required_scope = False def get_parser(self, prog_name): - parser = super(SetPasswordUser, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--password', metavar='', @@ -686,7 +686,7 @@ class ShowUser(command.ShowOne): _description = _("Display user details") def get_parser(self, prog_name): - parser = super(ShowUser, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'user', metavar='', diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 10f997cd1..ecbb2050b 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -16,7 +16,6 @@ """Image V1 Action Implementations""" import argparse -import io import logging import os import sys @@ -110,7 +109,7 @@ class CreateImage(command.ShowOne): _description = _("Create/upload an image") def get_parser(self, prog_name): - parser = super(CreateImage, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "name", metavar="", @@ -320,7 +319,7 @@ def take_action(self, parsed_args): elif parsed_args.file: # Send an open file handle to glanceclient so it will # do a chunked transfer - kwargs["data"] = io.open(parsed_args.file, "rb") + kwargs["data"] = open(parsed_args.file, "rb") else: # Read file from stdin if not sys.stdin.isatty(): @@ -363,7 +362,7 @@ class DeleteImage(command.Command): _description = _("Delete image(s)") def get_parser(self, prog_name): - parser = super(DeleteImage, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "images", metavar="", @@ -403,7 +402,7 @@ class ListImage(command.Lister): _description = _("List available images") def get_parser(self, prog_name): - parser = super(ListImage, self).get_parser(prog_name) + parser = super().get_parser(prog_name) public_group = parser.add_mutually_exclusive_group() public_group.add_argument( "--public", @@ -534,7 +533,7 @@ class SaveImage(command.Command): _description = _("Save an image locally") def get_parser(self, prog_name): - parser = super(SaveImage, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "--file", metavar="", @@ -564,7 +563,7 @@ class SetImage(command.Command): _description = _("Set image properties") def get_parser(self, prog_name): - parser = super(SetImage, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "image", metavar="", @@ -771,7 +770,7 @@ def take_action(self, parsed_args): elif parsed_args.file: # Send an open file handle to glanceclient so it will # do a chunked transfer - kwargs["data"] = io.open(parsed_args.file, "rb") + kwargs["data"] = open(parsed_args.file, "rb") else: # Read file from stdin if sys.stdin.isatty() is not True: @@ -809,7 +808,7 @@ class ShowImage(command.ShowOne): _description = _("Display image details") def get_parser(self, prog_name): - parser = super(ShowImage, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "--human-readable", default=False, diff --git a/openstackclient/image/v2/task.py b/openstackclient/image/v2/task.py index 924eaaf13..1b7170f63 100644 --- a/openstackclient/image/v2/task.py +++ b/openstackclient/image/v2/task.py @@ -64,7 +64,7 @@ class ShowTask(command.ShowOne): _description = _('Display task details') def get_parser(self, prog_name): - parser = super(ShowTask, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'task', diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index 63c845cde..0dc291c92 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -132,7 +132,7 @@ def split_help(network_help, compute_help): def get_parser(self, prog_name): LOG.debug('get_parser(%s)', prog_name) - parser = super(NetDetectionMixin, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser = self.update_parser_common(parser) LOG.debug('common parser: %s', parser) if self.is_neutron or self.is_docs_build: @@ -318,7 +318,7 @@ def _parse_extra_properties(self, extra_properties): return result def get_parser(self, prog_name): - parser = super(NeutronCommandWithExtraArgs, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--extra-property', metavar='type=,name=,' diff --git a/openstackclient/network/utils.py b/openstackclient/network/utils.py index 85a4f5ac1..9cb3e1a36 100644 --- a/openstackclient/network/utils.py +++ b/openstackclient/network/utils.py @@ -27,7 +27,7 @@ def transform_compute_security_group_rule(sg_rule): elif from_port is None and to_port is None: port_range = {'port_range': ""} else: - port_range = {'port_range': "%s:%s" % (from_port, to_port)} + port_range = {'port_range': f"{from_port}:{to_port}"} info.update(port_range) if 'cidr' in info['ip_range']: info['ip_range'] = info['ip_range']['cidr'] @@ -76,7 +76,7 @@ def str2dict(strdict): msg = _("missing value for key '%s'") raise exceptions.CommandError(msg % kv) else: - kvlist[i - 1] = "%s;%s" % (kvlist[i - 1], kv) + kvlist[i - 1] = f"{kvlist[i - 1]};{kv}" for kv in kvlist: key, sep, value = kv.partition(':') result[key] = value diff --git a/openstackclient/network/v2/address_group.py b/openstackclient/network/v2/address_group.py index 7c7371840..fee3d143c 100644 --- a/openstackclient/network/v2/address_group.py +++ b/openstackclient/network/v2/address_group.py @@ -61,7 +61,7 @@ class CreateAddressGroup(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create a new Address Group") def get_parser(self, prog_name): - parser = super(CreateAddressGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar="", help=_("New address group name") ) @@ -108,7 +108,7 @@ class DeleteAddressGroup(command.Command): _description = _("Delete address group(s)") def get_parser(self, prog_name): - parser = super(DeleteAddressGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'address_group', metavar="", @@ -148,7 +148,7 @@ class ListAddressGroup(command.Lister): _description = _("List address groups") def get_parser(self, prog_name): - parser = super(ListAddressGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--name', @@ -213,7 +213,7 @@ class SetAddressGroup(common.NeutronCommandWithExtraArgs): _description = _("Set address group properties") def get_parser(self, prog_name): - parser = super(SetAddressGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'address_group', metavar="", @@ -265,7 +265,7 @@ class ShowAddressGroup(command.ShowOne): _description = _("Display address group details") def get_parser(self, prog_name): - parser = super(ShowAddressGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'address_group', metavar="", @@ -289,7 +289,7 @@ class UnsetAddressGroup(command.Command): _description = _("Unset address group properties") def get_parser(self, prog_name): - parser = super(UnsetAddressGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'address_group', metavar="", diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py index f6f9c6d35..f28215462 100644 --- a/openstackclient/network/v2/address_scope.py +++ b/openstackclient/network/v2/address_scope.py @@ -62,7 +62,7 @@ class CreateAddressScope(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create a new Address Scope") def get_parser(self, prog_name): - parser = super(CreateAddressScope, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar="", help=_("New address scope name") ) @@ -113,7 +113,7 @@ class DeleteAddressScope(command.Command): _description = _("Delete address scope(s)") def get_parser(self, prog_name): - parser = super(DeleteAddressScope, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'address_scope', metavar="", @@ -155,7 +155,7 @@ class ListAddressScope(command.Lister): _description = _("List address scopes") def get_parser(self, prog_name): - parser = super(ListAddressScope, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--name', @@ -249,7 +249,7 @@ class SetAddressScope(common.NeutronCommandWithExtraArgs): _description = _("Set address scope properties") def get_parser(self, prog_name): - parser = super(SetAddressScope, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'address_scope', metavar="", @@ -294,7 +294,7 @@ class ShowAddressScope(command.ShowOne): _description = _("Display address scope details") def get_parser(self, prog_name): - parser = super(ShowAddressScope, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'address_scope', metavar="", diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index f878a3897..fef7b7d27 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -440,7 +440,7 @@ class SetFloatingIP(common.NeutronCommandWithExtraArgs): _description = _("Set floating IP Properties") def get_parser(self, prog_name): - parser = super(SetFloatingIP, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'floating_ip', metavar='', @@ -548,7 +548,7 @@ class UnsetFloatingIP(common.NeutronCommandWithExtraArgs): _description = _("Unset floating IP Properties") def get_parser(self, prog_name): - parser = super(UnsetFloatingIP, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'floating_ip', metavar='', diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py index 324708ae3..e4a9aa511 100644 --- a/openstackclient/network/v2/floating_ip_port_forwarding.py +++ b/openstackclient/network/v2/floating_ip_port_forwarding.py @@ -98,9 +98,7 @@ class CreateFloatingIPPortForwarding( _description = _("Create floating IP port forwarding") def get_parser(self, prog_name): - parser = super(CreateFloatingIPPortForwarding, self).get_parser( - prog_name - ) + parser = super().get_parser(prog_name) parser.add_argument( '--internal-ip-address', required=True, @@ -201,9 +199,7 @@ class DeleteFloatingIPPortForwarding(command.Command): _description = _("Delete floating IP port forwarding") def get_parser(self, prog_name): - parser = super(DeleteFloatingIPPortForwarding, self).get_parser( - prog_name - ) + parser = super().get_parser(prog_name) parser.add_argument( 'floating_ip', metavar='', @@ -256,9 +252,7 @@ class ListFloatingIPPortForwarding(command.Lister): _description = _("List floating IP port forwarding") def get_parser(self, prog_name): - parser = super(ListFloatingIPPortForwarding, self).get_parser( - prog_name - ) + parser = super().get_parser(prog_name) parser.add_argument( 'floating_ip', metavar='', @@ -358,7 +352,7 @@ class SetFloatingIPPortForwarding(common.NeutronCommandWithExtraArgs): _description = _("Set floating IP Port Forwarding Properties") def get_parser(self, prog_name): - parser = super(SetFloatingIPPortForwarding, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'floating_ip', metavar='', @@ -458,9 +452,7 @@ class ShowFloatingIPPortForwarding(command.ShowOne): _description = _("Display floating IP Port Forwarding details") def get_parser(self, prog_name): - parser = super(ShowFloatingIPPortForwarding, self).get_parser( - prog_name - ) + parser = super().get_parser(prog_name) parser.add_argument( 'floating_ip', metavar='', diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py index 49b0c07cd..30b6a0e49 100644 --- a/openstackclient/network/v2/ip_availability.py +++ b/openstackclient/network/v2/ip_availability.py @@ -39,7 +39,7 @@ class ListIPAvailability(command.Lister): _description = _("List IP availability for network") def get_parser(self, prog_name): - parser = super(ListIPAvailability, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--ip-version', type=int, @@ -105,7 +105,7 @@ class ShowIPAvailability(command.ShowOne): _description = _("Show network IP availability details") def get_parser(self, prog_name): - parser = super(ShowIPAvailability, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'network', metavar="", diff --git a/openstackclient/network/v2/l3_conntrack_helper.py b/openstackclient/network/v2/l3_conntrack_helper.py index f55a46133..598ced997 100644 --- a/openstackclient/network/v2/l3_conntrack_helper.py +++ b/openstackclient/network/v2/l3_conntrack_helper.py @@ -49,7 +49,7 @@ class CreateConntrackHelper(command.ShowOne): _description = _("Create a new L3 conntrack helper") def get_parser(self, prog_name): - parser = super(CreateConntrackHelper, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar='', @@ -95,7 +95,7 @@ class DeleteConntrackHelper(command.Command): _description = _("Delete L3 conntrack helper") def get_parser(self, prog_name): - parser = super(DeleteConntrackHelper, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar='', @@ -143,7 +143,7 @@ class ListConntrackHelper(command.Lister): _description = _("List L3 conntrack helpers") def get_parser(self, prog_name): - parser = super(ListConntrackHelper, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar='', @@ -206,7 +206,7 @@ class SetConntrackHelper(command.Command): _description = _("Set L3 conntrack helper properties") def get_parser(self, prog_name): - parser = super(SetConntrackHelper, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar='', @@ -253,7 +253,7 @@ class ShowConntrackHelper(command.ShowOne): _description = _("Display L3 conntrack helper details") def get_parser(self, prog_name): - parser = super(ShowConntrackHelper, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar='', diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 10409cade..86883b35f 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -694,7 +694,7 @@ class SetNetwork(common.NeutronCommandWithExtraArgs): _description = _("Set network properties") def get_parser(self, prog_name): - parser = super(SetNetwork, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'network', metavar="", @@ -838,7 +838,7 @@ class UnsetNetwork(common.NeutronUnsetCommandWithExtraArgs): _description = _("Unset network properties") def get_parser(self, prog_name): - parser = super(UnsetNetwork, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'network', metavar="", diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py index 085e6cfff..d31bd3f56 100644 --- a/openstackclient/network/v2/network_agent.py +++ b/openstackclient/network/v2/network_agent.py @@ -60,7 +60,7 @@ class AddNetworkToAgent(command.Command): _description = _("Add network to an agent") def get_parser(self, prog_name): - parser = super(AddNetworkToAgent, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--dhcp', action='store_true', @@ -99,7 +99,7 @@ class AddRouterToAgent(command.Command): _description = _("Add router to an agent") def get_parser(self, prog_name): - parser = super(AddRouterToAgent, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--l3', action='store_true', help=_('Add router to an L3 agent') ) @@ -128,7 +128,7 @@ class DeleteNetworkAgent(command.Command): _description = _("Delete network agent(s)") def get_parser(self, prog_name): - parser = super(DeleteNetworkAgent, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'network_agent', metavar="", @@ -168,7 +168,7 @@ class ListNetworkAgent(command.Lister): _description = _("List network agents") def get_parser(self, prog_name): - parser = super(ListNetworkAgent, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--agent-type', metavar='', @@ -293,7 +293,7 @@ class RemoveNetworkFromAgent(command.Command): _description = _("Remove network from an agent.") def get_parser(self, prog_name): - parser = super(RemoveNetworkFromAgent, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--dhcp', action='store_true', @@ -331,7 +331,7 @@ class RemoveRouterFromAgent(command.Command): _description = _("Remove router from an agent") def get_parser(self, prog_name): - parser = super(RemoveRouterFromAgent, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--l3', action='store_true', @@ -364,7 +364,7 @@ class SetNetworkAgent(command.Command): _description = _("Set network agent properties") def get_parser(self, prog_name): - parser = super(SetNetworkAgent, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'network_agent', metavar="", @@ -403,7 +403,7 @@ class ShowNetworkAgent(command.ShowOne): _description = _("Display network agent details") def get_parser(self, prog_name): - parser = super(ShowNetworkAgent, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'network_agent', metavar="", diff --git a/openstackclient/network/v2/network_auto_allocated_topology.py b/openstackclient/network/v2/network_auto_allocated_topology.py index b2d0fe1d8..9f382eacf 100644 --- a/openstackclient/network/v2/network_auto_allocated_topology.py +++ b/openstackclient/network/v2/network_auto_allocated_topology.py @@ -65,7 +65,7 @@ class CreateAutoAllocatedTopology(command.ShowOne): _description = _("Create the auto allocated topology for project") def get_parser(self, prog_name): - parser = super(CreateAutoAllocatedTopology, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--project', metavar='', @@ -126,7 +126,7 @@ class DeleteAutoAllocatedTopology(command.Command): _description = _("Delete auto allocated topology for project") def get_parser(self, prog_name): - parser = super(DeleteAutoAllocatedTopology, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--project', metavar='', diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py index 2f308bed4..8bc526201 100644 --- a/openstackclient/network/v2/network_flavor.py +++ b/openstackclient/network/v2/network_flavor.py @@ -63,7 +63,7 @@ class AddNetworkFlavorToProfile(command.Command): _description = _("Add a service profile to a network flavor") def get_parser(self, prog_name): - parser = super(AddNetworkFlavorToProfile, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'flavor', metavar="", help=_("Network flavor (name or ID)") ) @@ -93,7 +93,7 @@ class CreateNetworkFlavor(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create new network flavor") def get_parser(self, prog_name): - parser = super(CreateNetworkFlavor, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar="", help=_("Name for the flavor") ) @@ -146,7 +146,7 @@ class DeleteNetworkFlavor(command.Command): _description = _("Delete network flavors") def get_parser(self, prog_name): - parser = super(DeleteNetworkFlavor, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'flavor', @@ -214,9 +214,7 @@ class RemoveNetworkFlavorFromProfile(command.Command): _description = _("Remove service profile from network flavor") def get_parser(self, prog_name): - parser = super(RemoveNetworkFlavorFromProfile, self).get_parser( - prog_name - ) + parser = super().get_parser(prog_name) parser.add_argument( 'flavor', metavar="", help=_("Network flavor (name or ID)") ) @@ -246,7 +244,7 @@ class SetNetworkFlavor(common.NeutronCommandWithExtraArgs): _description = _("Set network flavor properties") def get_parser(self, prog_name): - parser = super(SetNetworkFlavor, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'flavor', metavar="", @@ -290,7 +288,7 @@ class ShowNetworkFlavor(command.ShowOne): _description = _("Display network flavor details") def get_parser(self, prog_name): - parser = super(ShowNetworkFlavor, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'flavor', metavar='', diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py index c063d5dd3..1fb5a8011 100644 --- a/openstackclient/network/v2/network_flavor_profile.py +++ b/openstackclient/network/v2/network_flavor_profile.py @@ -67,7 +67,7 @@ class CreateNetworkFlavorProfile( _description = _("Create new network flavor profile") def get_parser(self, prog_name): - parser = super(CreateNetworkFlavorProfile, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--project', metavar="", @@ -129,7 +129,7 @@ class DeleteNetworkFlavorProfile(command.Command): _description = _("Delete network flavor profile") def get_parser(self, prog_name): - parser = super(DeleteNetworkFlavorProfile, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'flavor_profile', @@ -206,7 +206,7 @@ class SetNetworkFlavorProfile(common.NeutronCommandWithExtraArgs): _description = _("Set network flavor profile properties") def get_parser(self, prog_name): - parser = super(SetNetworkFlavorProfile, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'flavor_profile', metavar="", @@ -263,7 +263,7 @@ class ShowNetworkFlavorProfile(command.ShowOne): _description = _("Display network flavor profile details") def get_parser(self, prog_name): - parser = super(ShowNetworkFlavorProfile, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'flavor_profile', metavar='', diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py index d39bc31f4..5a6a8f2f2 100644 --- a/openstackclient/network/v2/network_meter.py +++ b/openstackclient/network/v2/network_meter.py @@ -65,7 +65,7 @@ class CreateMeter(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create network meter") def get_parser(self, prog_name): - parser = super(CreateMeter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--description', @@ -118,7 +118,7 @@ class DeleteMeter(command.Command): _description = _("Delete network meter") def get_parser(self, prog_name): - parser = super(DeleteMeter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'meter', @@ -187,7 +187,7 @@ class ShowMeter(command.ShowOne): _description = _("Show network meter") def get_parser(self, prog_name): - parser = super(ShowMeter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'meter', metavar='', help=_('Meter to display (name or ID)') ) diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py index 4e513e342..ea1bea269 100644 --- a/openstackclient/network/v2/network_meter_rule.py +++ b/openstackclient/network/v2/network_meter_rule.py @@ -69,7 +69,7 @@ class CreateMeterRule(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create a new meter rule") def get_parser(self, prog_name): - parser = super(CreateMeterRule, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--project', @@ -146,7 +146,7 @@ class DeleteMeterRule(command.Command): _description = _("Delete meter rule(s)") def get_parser(self, prog_name): - parser = super(DeleteMeterRule, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'meter_rule_id', @@ -222,7 +222,7 @@ class ShowMeterRule(command.ShowOne): _description = _("Display meter rules details") def get_parser(self, prog_name): - parser = super(ShowMeterRule, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'meter_rule_id', metavar='', diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py index ca0f87119..5023ea988 100644 --- a/openstackclient/network/v2/network_qos_policy.py +++ b/openstackclient/network/v2/network_qos_policy.py @@ -85,7 +85,7 @@ class CreateNetworkQosPolicy( _description = _("Create a QoS policy") def get_parser(self, prog_name): - parser = super(CreateNetworkQosPolicy, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', help=_("Name of QoS policy to create") ) @@ -144,7 +144,7 @@ class DeleteNetworkQosPolicy(command.Command): _description = _("Delete Qos Policy(s)") def get_parser(self, prog_name): - parser = super(DeleteNetworkQosPolicy, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'policy', metavar="", @@ -185,7 +185,7 @@ class ListNetworkQosPolicy(command.Lister): _description = _("List QoS policies") def get_parser(self, prog_name): - parser = super(ListNetworkQosPolicy, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--project', metavar='', @@ -244,7 +244,7 @@ class SetNetworkQosPolicy(common.NeutronCommandWithExtraArgs): _description = _("Set QoS policy properties") def get_parser(self, prog_name): - parser = super(SetNetworkQosPolicy, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'policy', metavar="", @@ -296,7 +296,7 @@ class ShowNetworkQosPolicy(command.ShowOne): _description = _("Display QoS policy details") def get_parser(self, prog_name): - parser = super(ShowNetworkQosPolicy, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'policy', metavar="", diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py index a6529c8b8..70f4d2843 100644 --- a/openstackclient/network/v2/network_qos_rule.py +++ b/openstackclient/network/v2/network_qos_rule.py @@ -159,10 +159,10 @@ def _get_item_properties(item, fields): def _rule_action_call(client, action, rule_type): rule_type = rule_type.replace('-', '_') - func_name = '%(action)s_qos_%(rule_type)s_rule' % { - 'action': action, - 'rule_type': rule_type, - } + func_name = '{action}_qos_{rule_type}_rule'.format( + action=action, + rule_type=rule_type, + ) return getattr(client, func_name) @@ -243,7 +243,7 @@ class CreateNetworkQosRule( _description = _("Create new Network QoS rule") def get_parser(self, prog_name): - parser = super(CreateNetworkQosRule, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_policy', metavar='', @@ -289,7 +289,7 @@ class DeleteNetworkQosRule(command.Command): _description = _("Delete Network QoS rule") def get_parser(self, prog_name): - parser = super(DeleteNetworkQosRule, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_policy', metavar='', @@ -326,7 +326,7 @@ class ListNetworkQosRule(command.Lister): _description = _("List Network QoS rules") def get_parser(self, prog_name): - parser = super(ListNetworkQosRule, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_policy', metavar='', @@ -372,7 +372,7 @@ class SetNetworkQosRule(common.NeutronCommandWithExtraArgs): _description = _("Set Network QoS rule properties") def get_parser(self, prog_name): - parser = super(SetNetworkQosRule, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_policy', metavar='', @@ -418,7 +418,7 @@ class ShowNetworkQosRule(command.ShowOne): _description = _("Display Network QoS rule details") def get_parser(self, prog_name): - parser = super(ShowNetworkQosRule, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_policy', metavar='', diff --git a/openstackclient/network/v2/network_qos_rule_type.py b/openstackclient/network/v2/network_qos_rule_type.py index 24702070b..fd4da84af 100644 --- a/openstackclient/network/v2/network_qos_rule_type.py +++ b/openstackclient/network/v2/network_qos_rule_type.py @@ -83,7 +83,7 @@ class ShowNetworkQosRuleType(command.ShowOne): _description = _("Show details about supported QoS rule type") def get_parser(self, prog_name): - parser = super(ShowNetworkQosRuleType, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'rule_type', metavar="", diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py index cb28ea3b2..e3e5aff19 100644 --- a/openstackclient/network/v2/network_rbac.py +++ b/openstackclient/network/v2/network_rbac.py @@ -96,7 +96,7 @@ class CreateNetworkRBAC(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create network RBAC policy") def get_parser(self, prog_name): - parser = super(CreateNetworkRBAC, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'rbac_object', metavar="", @@ -182,7 +182,7 @@ class DeleteNetworkRBAC(command.Command): _description = _("Delete network RBAC policy(s)") def get_parser(self, prog_name): - parser = super(DeleteNetworkRBAC, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'rbac_policy', metavar="", @@ -221,7 +221,7 @@ class ListNetworkRBAC(command.Lister): _description = _("List network RBAC policies") def get_parser(self, prog_name): - parser = super(ListNetworkRBAC, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--type', metavar='', @@ -315,7 +315,7 @@ class SetNetworkRBAC(common.NeutronCommandWithExtraArgs): _description = _("Set network RBAC policy properties") def get_parser(self, prog_name): - parser = super(SetNetworkRBAC, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'rbac_policy', metavar="", @@ -364,7 +364,7 @@ class ShowNetworkRBAC(command.ShowOne): _description = _("Display network RBAC policy details") def get_parser(self, prog_name): - parser = super(ShowNetworkRBAC, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'rbac_policy', metavar="", diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py index a17f37c03..740952315 100644 --- a/openstackclient/network/v2/network_segment.py +++ b/openstackclient/network/v2/network_segment.py @@ -39,7 +39,7 @@ class CreateNetworkSegment( _description = _("Create new network segment") def get_parser(self, prog_name): - parser = super(CreateNetworkSegment, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', help=_('New network segment name') ) @@ -109,7 +109,7 @@ class DeleteNetworkSegment(command.Command): _description = _("Delete network segment(s)") def get_parser(self, prog_name): - parser = super(DeleteNetworkSegment, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'network_segment', metavar='', @@ -150,7 +150,7 @@ class ListNetworkSegment(command.Lister): _description = _("List network segments") def get_parser(self, prog_name): - parser = super(ListNetworkSegment, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -213,7 +213,7 @@ class SetNetworkSegment(common.NeutronCommandWithExtraArgs): _description = _("Set network segment properties") def get_parser(self, prog_name): - parser = super(SetNetworkSegment, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'network_segment', metavar='', @@ -251,7 +251,7 @@ class ShowNetworkSegment(command.ShowOne): _description = _("Display network segment details") def get_parser(self, prog_name): - parser = super(ShowNetworkSegment, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'network_segment', metavar='', diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py index 59957617b..aa0509969 100644 --- a/openstackclient/network/v2/network_segment_range.py +++ b/openstackclient/network/v2/network_segment_range.py @@ -44,9 +44,7 @@ def _get_ranges(item): for a, b in itertools.groupby(enumerate(item), lambda xy: xy[1] - xy[0]): b = list(b) yield ( - "%s-%s" % (b[0][1], b[-1][1]) - if b[0][1] != b[-1][1] - else str(b[0][1]) + f"{b[0][1]}-{b[-1][1]}" if b[0][1] != b[-1][1] else str(b[0][1]) ) @@ -100,7 +98,7 @@ class CreateNetworkSegmentRange( _description = _("Create new network segment range") def get_parser(self, prog_name): - parser = super(CreateNetworkSegmentRange, self).get_parser(prog_name) + parser = super().get_parser(prog_name) shared_group = parser.add_mutually_exclusive_group() shared_group.add_argument( "--private", @@ -256,7 +254,7 @@ class DeleteNetworkSegmentRange(command.Command): _description = _("Delete network segment range(s)") def get_parser(self, prog_name): - parser = super(DeleteNetworkSegmentRange, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'network_segment_range', metavar='', @@ -309,7 +307,7 @@ class ListNetworkSegmentRange(command.Lister): _description = _("List network segment ranges") def get_parser(self, prog_name): - parser = super(ListNetworkSegmentRange, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -425,7 +423,7 @@ class SetNetworkSegmentRange(common.NeutronCommandWithExtraArgs): _description = _("Set network segment range properties") def get_parser(self, prog_name): - parser = super(SetNetworkSegmentRange, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'network_segment_range', metavar='', @@ -490,7 +488,7 @@ class ShowNetworkSegmentRange(command.ShowOne): _description = _("Display network segment range details") def get_parser(self, prog_name): - parser = super(ShowNetworkSegmentRange, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'network_segment_range', metavar='', diff --git a/openstackclient/network/v2/network_trunk.py b/openstackclient/network/v2/network_trunk.py index 6050ba657..2b00d03ec 100644 --- a/openstackclient/network/v2/network_trunk.py +++ b/openstackclient/network/v2/network_trunk.py @@ -43,7 +43,7 @@ class CreateNetworkTrunk(command.ShowOne): """Create a network trunk for a given project""" def get_parser(self, prog_name): - parser = super(CreateNetworkTrunk, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', help=_("Name of the trunk to create") ) @@ -100,7 +100,7 @@ class DeleteNetworkTrunk(command.Command): """Delete a given network trunk""" def get_parser(self, prog_name): - parser = super(DeleteNetworkTrunk, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'trunk', metavar="", @@ -138,7 +138,7 @@ class ListNetworkTrunk(command.Lister): """List all network trunks""" def get_parser(self, prog_name): - parser = super(ListNetworkTrunk, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -177,7 +177,7 @@ class SetNetworkTrunk(command.Command): """Set network trunk properties""" def get_parser(self, prog_name): - parser = super(SetNetworkTrunk, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'trunk', metavar="", help=_("Trunk to modify (name or ID)") ) @@ -242,7 +242,7 @@ class ShowNetworkTrunk(command.ShowOne): """Show information of a given network trunk""" def get_parser(self, prog_name): - parser = super(ShowNetworkTrunk, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'trunk', metavar="", help=_("Trunk to display (name or ID)") ) @@ -263,7 +263,7 @@ class ListNetworkSubport(command.Lister): """List all subports for a given network trunk""" def get_parser(self, prog_name): - parser = super(ListNetworkSubport, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--trunk', required=True, @@ -294,7 +294,7 @@ class UnsetNetworkTrunk(command.Command): """Unset subports from a given network trunk""" def get_parser(self, prog_name): - parser = super(UnsetNetworkTrunk, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'trunk', metavar="", diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 0e5fe7605..95e9f5a8a 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -444,7 +444,7 @@ class CreatePort(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create a new port") def get_parser(self, prog_name): - parser = super(CreatePort, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--network', @@ -681,7 +681,7 @@ class DeletePort(command.Command): _description = _("Delete port(s)") def get_parser(self, prog_name): - parser = super(DeletePort, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'port', metavar="", @@ -723,7 +723,7 @@ class ListPort(command.Lister): _description = _("List ports") def get_parser(self, prog_name): - parser = super(ListPort, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--device-owner', metavar='', @@ -896,7 +896,7 @@ class SetPort(common.NeutronCommandWithExtraArgs): _description = _("Set port properties") def get_parser(self, prog_name): - parser = super(SetPort, self).get_parser(prog_name) + parser = super().get_parser(prog_name) _add_updatable_args(parser) admin_group = parser.add_mutually_exclusive_group() admin_group.add_argument( @@ -1135,7 +1135,7 @@ class ShowPort(command.ShowOne): _description = _("Display port details") def get_parser(self, prog_name): - parser = super(ShowPort, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'port', metavar="", help=_("Port to display (name or ID)") ) @@ -1155,7 +1155,7 @@ class UnsetPort(common.NeutronUnsetCommandWithExtraArgs): _description = _("Unset port properties") def get_parser(self, prog_name): - parser = super(UnsetPort, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--fixed-ip', metavar='subnet=,ip-address=', diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index e72d3e2f5..7bf730727 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -301,7 +301,7 @@ class AddPortToRouter(command.Command): _description = _("Add a port to a router") def get_parser(self, prog_name): - parser = super(AddPortToRouter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar='', @@ -325,7 +325,7 @@ class AddSubnetToRouter(command.Command): _description = _("Add a subnet to a router") def get_parser(self, prog_name): - parser = super(AddSubnetToRouter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar='', @@ -351,7 +351,7 @@ class AddExtraRoutesToRouter(command.ShowOne): _description = _("Add extra static routes to a router's routing table.") def get_parser(self, prog_name): - parser = super(AddExtraRoutesToRouter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar='', @@ -402,7 +402,7 @@ class RemoveExtraRoutesFromRouter(command.ShowOne): ) def get_parser(self, prog_name): - parser = super(RemoveExtraRoutesFromRouter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar='', @@ -453,7 +453,7 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create a new router") def get_parser(self, prog_name): - parser = super(CreateRouter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', help=_("New router name") ) @@ -627,7 +627,7 @@ class DeleteRouter(command.Command): _description = _("Delete router(s)") def get_parser(self, prog_name): - parser = super(DeleteRouter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar="", @@ -669,7 +669,7 @@ class ListRouter(command.Lister): _description = _("List routers") def get_parser(self, prog_name): - parser = super(ListRouter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--name', metavar='', @@ -813,7 +813,7 @@ class RemovePortFromRouter(command.Command): _description = _("Remove a port from a router") def get_parser(self, prog_name): - parser = super(RemovePortFromRouter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar='', @@ -839,7 +839,7 @@ class RemoveSubnetFromRouter(command.Command): _description = _("Remove a subnet from a router") def get_parser(self, prog_name): - parser = super(RemoveSubnetFromRouter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar='', @@ -869,7 +869,7 @@ class SetRouter(common.NeutronCommandWithExtraArgs): _description = _("Set router properties") def get_parser(self, prog_name): - parser = super(SetRouter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar="", @@ -1103,7 +1103,7 @@ class ShowRouter(command.ShowOne): _description = _("Display router details") def get_parser(self, prog_name): - parser = super(ShowRouter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'router', metavar="", @@ -1139,7 +1139,7 @@ class UnsetRouter(common.NeutronUnsetCommandWithExtraArgs): _description = _("Unset router properties") def get_parser(self, prog_name): - parser = super(UnsetRouter, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--route', metavar='destination=,gateway=', diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index 6de9cad30..90b09cae8 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -434,7 +434,7 @@ class UnsetSecurityGroup(command.Command): _description = _("Unset security group properties") def get_parser(self, prog_name): - parser = super(UnsetSecurityGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'group', metavar="", diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index a9af74bba..3d172adb1 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -46,7 +46,7 @@ def _update_arguments(obj_list, parsed_args_list, option): class AllocationPoolsColumn(cliff_columns.FormattableColumn): def human_readable(self): pool_formatted = [ - '%s-%s' % (pool.get('start', ''), pool.get('end', '')) + '{}-{}'.format(pool.get('start', ''), pool.get('end', '')) for pool in self._value ] return ','.join(pool_formatted) @@ -292,7 +292,7 @@ class CreateSubnet(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create a subnet") def get_parser(self, prog_name): - parser = super(CreateSubnet, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', help=_("New subnet name") ) @@ -436,7 +436,7 @@ class DeleteSubnet(command.Command): _description = _("Delete subnet(s)") def get_parser(self, prog_name): - parser = super(DeleteSubnet, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'subnet', metavar="", @@ -478,7 +478,7 @@ class ListSubnet(command.Lister): _description = _("List subnets") def get_parser(self, prog_name): - parser = super(ListSubnet, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -651,7 +651,7 @@ class SetSubnet(common.NeutronCommandWithExtraArgs): _description = _("Set subnet properties") def get_parser(self, prog_name): - parser = super(SetSubnet, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'subnet', metavar="", @@ -744,7 +744,7 @@ class ShowSubnet(command.ShowOne): _description = _("Display subnet details") def get_parser(self, prog_name): - parser = super(ShowSubnet, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'subnet', metavar="", @@ -765,7 +765,7 @@ class UnsetSubnet(common.NeutronUnsetCommandWithExtraArgs): _description = _("Unset subnet properties") def get_parser(self, prog_name): - parser = super(UnsetSubnet, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--allocation-pool', metavar='start=,end=', diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index 833fe3257..c6d92a6b4 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -155,7 +155,7 @@ class CreateSubnetPool(command.ShowOne, common.NeutronCommandWithExtraArgs): _description = _("Create subnet pool") def get_parser(self, prog_name): - parser = super(CreateSubnetPool, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', help=_("Name of the new subnet pool") ) @@ -226,7 +226,7 @@ class DeleteSubnetPool(command.Command): _description = _("Delete subnet pool(s)") def get_parser(self, prog_name): - parser = super(DeleteSubnetPool, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'subnet_pool', metavar='', @@ -267,7 +267,7 @@ class ListSubnetPool(command.Lister): _description = _("List subnet pools") def get_parser(self, prog_name): - parser = super(ListSubnetPool, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -393,7 +393,7 @@ class SetSubnetPool(common.NeutronCommandWithExtraArgs): _description = _("Set subnet pool properties") def get_parser(self, prog_name): - parser = super(SetSubnetPool, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'subnet_pool', metavar='', @@ -464,7 +464,7 @@ class ShowSubnetPool(command.ShowOne): _description = _("Display subnet pool details") def get_parser(self, prog_name): - parser = super(ShowSubnetPool, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'subnet_pool', metavar='', @@ -486,7 +486,7 @@ class UnsetSubnetPool(command.Command): _description = _("Unset subnet pool properties") def get_parser(self, prog_name): - parser = super(UnsetSubnetPool, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'subnet_pool', metavar="", diff --git a/openstackclient/object/v1/account.py b/openstackclient/object/v1/account.py index ef3a47f79..686fdcc62 100644 --- a/openstackclient/object/v1/account.py +++ b/openstackclient/object/v1/account.py @@ -24,7 +24,7 @@ class SetAccount(command.Command): _description = _("Set account properties") def get_parser(self, prog_name): - parser = super(SetAccount, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "--property", metavar="", @@ -59,7 +59,7 @@ class UnsetAccount(command.Command): _description = _("Unset account properties") def get_parser(self, prog_name): - parser = super(UnsetAccount, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--property', metavar='', diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py index 25108e2c6..c4d0e42b9 100644 --- a/openstackclient/object/v1/container.py +++ b/openstackclient/object/v1/container.py @@ -32,7 +32,7 @@ class CreateContainer(command.Lister): _description = _("Create new container") def get_parser(self, prog_name): - parser = super(CreateContainer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--public', action='store_true', @@ -85,7 +85,7 @@ class DeleteContainer(command.Command): _description = _("Delete container") def get_parser(self, prog_name): - parser = super(DeleteContainer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--recursive', '-r', @@ -121,7 +121,7 @@ class ListContainer(command.Lister): _description = _("List containers") def get_parser(self, prog_name): - parser = super(ListContainer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "--prefix", metavar="", @@ -184,7 +184,7 @@ class SaveContainer(command.Command): _description = _("Save container contents locally") def get_parser(self, prog_name): - parser = super(SaveContainer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'container', metavar='', @@ -202,7 +202,7 @@ class SetContainer(command.Command): _description = _("Set container properties") def get_parser(self, prog_name): - parser = super(SetContainer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'container', metavar='', @@ -231,7 +231,7 @@ class ShowContainer(command.ShowOne): _description = _("Display container details") def get_parser(self, prog_name): - parser = super(ShowContainer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'container', metavar='', @@ -253,7 +253,7 @@ class UnsetContainer(command.Command): _description = _("Unset container properties") def get_parser(self, prog_name): - parser = super(UnsetContainer, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'container', metavar='', diff --git a/openstackclient/object/v1/object.py b/openstackclient/object/v1/object.py index 24cebb3b8..4edb31493 100644 --- a/openstackclient/object/v1/object.py +++ b/openstackclient/object/v1/object.py @@ -34,7 +34,7 @@ class CreateObject(command.Lister): _description = _("Upload object to container") def get_parser(self, prog_name): - parser = super(CreateObject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'container', metavar='', @@ -99,7 +99,7 @@ class DeleteObject(command.Command): _description = _("Delete object from container") def get_parser(self, prog_name): - parser = super(DeleteObject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'container', metavar='', @@ -125,7 +125,7 @@ class ListObject(command.Lister): _description = _("List objects") def get_parser(self, prog_name): - parser = super(ListObject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "container", metavar="", @@ -208,7 +208,7 @@ class SaveObject(command.Command): _description = _("Save object locally") def get_parser(self, prog_name): - parser = super(SaveObject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "--file", metavar="", @@ -241,7 +241,7 @@ class SetObject(command.Command): _description = _("Set object properties") def get_parser(self, prog_name): - parser = super(SetObject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'container', metavar='', @@ -276,7 +276,7 @@ class ShowObject(command.ShowOne): _description = _("Display object details") def get_parser(self, prog_name): - parser = super(ShowObject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'container', metavar='', @@ -304,7 +304,7 @@ class UnsetObject(command.Command): _description = _("Unset object properties") def get_parser(self, prog_name): - parser = super(UnsetObject, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'container', metavar='', diff --git a/openstackclient/shell.py b/openstackclient/shell.py index 68ebf8a47..5c64cb7cb 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -32,7 +32,7 @@ class OpenStackShell(shell.OpenStackShell): def __init__(self): - super(OpenStackShell, self).__init__( + super().__init__( description=__doc__.strip(), version=openstackclient.__version__, command_manager=commandmanager.CommandManager('openstack.cli'), @@ -49,15 +49,13 @@ def __init__(self): warnings.filterwarnings('ignore', module='openstack') def build_option_parser(self, description, version): - parser = super(OpenStackShell, self).build_option_parser( - description, version - ) + parser = super().build_option_parser(description, version) parser = clientmanager.build_plugin_option_parser(parser) parser = auth.build_auth_plugins_option_parser(parser) return parser def _final_defaults(self): - super(OpenStackShell, self)._final_defaults() + super()._final_defaults() # Set the default plugin to admin_token if endpoint and token are given if self.options.endpoint and self.options.token: @@ -132,7 +130,7 @@ def _load_commands(self): self.command_manager.add_command_group('openstack.extension') def initialize_app(self, argv): - super(OpenStackShell, self).initialize_app(argv) + super().initialize_app(argv) # Re-create the client_manager with our subclass self.client_manager = clientmanager.ClientManager( diff --git a/openstackclient/tests/functional/base.py b/openstackclient/tests/functional/base.py index 42fee80dc..2539c56fc 100644 --- a/openstackclient/tests/functional/base.py +++ b/openstackclient/tests/functional/base.py @@ -137,7 +137,7 @@ def get_openstack_configuration_value(cls, configuration): @classmethod def get_opts(cls, fields, output_format='value'): - return ' -f {0} {1}'.format( + return ' -f {} {}'.format( output_format, ' '.join(['-c ' + it for it in fields]) ) diff --git a/openstackclient/tests/functional/common/test_extension.py b/openstackclient/tests/functional/common/test_extension.py index 8784c55b1..c65f52db5 100644 --- a/openstackclient/tests/functional/common/test_extension.py +++ b/openstackclient/tests/functional/common/test_extension.py @@ -23,7 +23,7 @@ class ExtensionTests(base.TestCase): @classmethod def setUpClass(cls): - super(ExtensionTests, cls).setUpClass() + super().setUpClass() cls.haz_network = cls.is_service_enabled('network') def test_extension_list_compute(self): diff --git a/openstackclient/tests/functional/common/test_help.py b/openstackclient/tests/functional/common/test_help.py index 0df3b5bec..f1aea8ee9 100644 --- a/openstackclient/tests/functional/common/test_help.py +++ b/openstackclient/tests/functional/common/test_help.py @@ -60,12 +60,12 @@ 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: %s not found in help output:\n%s' % ( + msg = 'Command: {} not found in help output:\n{}'.format( command, raw_output, ) self.assertIn(command, raw_output, msg) - msg = 'Description: %s not found in help output:\n%s' % ( + msg = 'Description: {} not found in help output:\n{}'.format( description, raw_output, ) diff --git a/openstackclient/tests/functional/common/test_quota.py b/openstackclient/tests/functional/common/test_quota.py index be147c77f..0ee27c84e 100644 --- a/openstackclient/tests/functional/common/test_quota.py +++ b/openstackclient/tests/functional/common/test_quota.py @@ -187,7 +187,7 @@ def test_quota_set_class(self): self.assertTrue(cmd_output["snapshots"] >= 0) def _restore_quota_limit(self, resource, limit, project): - self.openstack('quota set --%s %s %s' % (resource, limit, project)) + self.openstack(f'quota set --{resource} {limit} {project}') def test_quota_network_set_with_no_force(self): if not self.haz_network: diff --git a/openstackclient/tests/functional/compute/v2/common.py b/openstackclient/tests/functional/compute/v2/common.py index 885dc1d91..ef59d458a 100644 --- a/openstackclient/tests/functional/compute/v2/common.py +++ b/openstackclient/tests/functional/compute/v2/common.py @@ -28,7 +28,7 @@ class ComputeTestCase(base.TestCase): def setUp(self): """Select common resources""" - super(ComputeTestCase, self).setUp() + super().setUp() self.flavor_name = self.get_flavor() self.image_name = self.get_image() self.network_arg = self.get_network() @@ -129,7 +129,7 @@ def wait_for_status( ) status = cmd_output['status'] if status == expected_status: - print('Server {} now has status {}'.format(name, status)) + print(f'Server {name} now has status {status}') break print( 'Server {}: Waiting for {}, current status: {}'.format( diff --git a/openstackclient/tests/functional/compute/v2/test_flavor.py b/openstackclient/tests/functional/compute/v2/test_flavor.py index 227e21d5e..4a0ff4883 100644 --- a/openstackclient/tests/functional/compute/v2/test_flavor.py +++ b/openstackclient/tests/functional/compute/v2/test_flavor.py @@ -22,7 +22,7 @@ class FlavorTests(base.TestCase): @classmethod def setUpClass(cls): - super(FlavorTests, cls).setUpClass() + super().setUpClass() # Make a project cmd_output = cls.openstack( "project create --enable " + cls.PROJECT_NAME, @@ -36,7 +36,7 @@ def tearDownClass(cls): raw_output = cls.openstack("project delete " + cls.PROJECT_NAME) cls.assertOutput('', raw_output) finally: - super(FlavorTests, cls).tearDownClass() + super().tearDownClass() def test_flavor_delete(self): """Test create w/project, delete multiple""" diff --git a/openstackclient/tests/functional/compute/v2/test_keypair.py b/openstackclient/tests/functional/compute/v2/test_keypair.py index 148e9451a..ebc78abda 100644 --- a/openstackclient/tests/functional/compute/v2/test_keypair.py +++ b/openstackclient/tests/functional/compute/v2/test_keypair.py @@ -57,7 +57,7 @@ class KeypairTests(KeypairBase): def setUp(self): """Create keypair with randomized name for tests.""" - super(KeypairTests, self).setUp() + super().setUp() self.KPName = data_utils.rand_name('TestKeyPair') self.keypair = self.keypair_create(self.KPName) diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py index d04fe31d6..e465acc4a 100644 --- a/openstackclient/tests/functional/compute/v2/test_server.py +++ b/openstackclient/tests/functional/compute/v2/test_server.py @@ -211,7 +211,7 @@ def test_server_set(self): flavor['name'], ) self.assertEqual( - '%s (%s)' % (flavor['name'], flavor['id']), + '{} ({})'.format(flavor['name'], flavor['id']), cmd_output["flavor"], ) image = self.openstack( @@ -223,7 +223,7 @@ def test_server_set(self): image['name'], ) self.assertEqual( - '%s (%s)' % (image['name'], image['id']), + '{} ({})'.format(image['name'], image['id']), cmd_output["image"], ) diff --git a/openstackclient/tests/functional/compute/v2/test_server_event.py b/openstackclient/tests/functional/compute/v2/test_server_event.py index c7ead8144..790aec69d 100644 --- a/openstackclient/tests/functional/compute/v2/test_server_event.py +++ b/openstackclient/tests/functional/compute/v2/test_server_event.py @@ -20,7 +20,7 @@ class ServerEventTests(common.ComputeTestCase): """Functional tests for server event""" def setUp(self): - super(ServerEventTests, self).setUp() + super().setUp() # NOTE(dtroyer): As long as these tests are read-only we can get away # with using the same server instance for all of them. diff --git a/openstackclient/tests/functional/identity/v2/common.py b/openstackclient/tests/functional/identity/v2/common.py index 9d3601eb3..a78f3b031 100644 --- a/openstackclient/tests/functional/identity/v2/common.py +++ b/openstackclient/tests/functional/identity/v2/common.py @@ -59,7 +59,7 @@ class IdentityTests(base.TestCase): @classmethod def setUpClass(cls): - super(IdentityTests, cls).setUpClass() + super().setUpClass() # create dummy project cls.project_name = data_utils.rand_name('TestProject') cls.project_description = data_utils.rand_name('description') @@ -90,10 +90,10 @@ def tearDownClass(cls): 'project delete %s' % cls.project_name ) finally: - super(IdentityTests, cls).tearDownClass() + super().tearDownClass() def setUp(self): - super(IdentityTests, self).setUp() + super().setUp() # prepare v2 env ver_fixture = fixtures.EnvironmentVariable( 'OS_IDENTITY_API_VERSION', '2.0' diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py index b3d55e654..8607c0573 100644 --- a/openstackclient/tests/functional/identity/v3/common.py +++ b/openstackclient/tests/functional/identity/v3/common.py @@ -140,7 +140,7 @@ class IdentityTests(base.TestCase): @classmethod def setUpClass(cls): - super(IdentityTests, cls).setUpClass() + super().setUpClass() # create dummy domain cls.domain_name = data_utils.rand_name('TestDomain') cls.domain_description = data_utils.rand_name('description') @@ -188,10 +188,10 @@ def tearDownClass(cls): 'domain delete %s' % cls.domain_name ) finally: - super(IdentityTests, cls).tearDownClass() + super().tearDownClass() def setUp(self): - super(IdentityTests, self).setUp() + super().setUp() # prepare v3 env ver_fixture = fixtures.EnvironmentVariable( 'OS_IDENTITY_API_VERSION', '3' diff --git a/openstackclient/tests/functional/identity/v3/test_application_credential.py b/openstackclient/tests/functional/identity/v3/test_application_credential.py index dbec1cb24..22f2b90bb 100644 --- a/openstackclient/tests/functional/identity/v3/test_application_credential.py +++ b/openstackclient/tests/functional/identity/v3/test_application_credential.py @@ -39,12 +39,10 @@ class ApplicationCredentialTests(common.IdentityTests): def test_application_credential_create(self): name = data_utils.rand_name('name') - raw_output = self.openstack( - 'application credential create %(name)s' % {'name': name} - ) + raw_output = self.openstack(f'application credential create {name}') self.addCleanup( self.openstack, - 'application credential delete %(name)s' % {'name': name}, + f'application credential delete {name}', ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.APPLICATION_CREDENTIAL_FIELDS) @@ -145,16 +143,14 @@ def test_application_credential_create_with_options(self): ) self.addCleanup( self.openstack, - 'application credential delete %(name)s' % {'name': name}, + f'application credential delete {name}', ) items = self.parse_show(raw_output) self.assert_show_fields(items, self.APPLICATION_CREDENTIAL_FIELDS) def test_application_credential_delete(self): name = data_utils.rand_name('name') - self.openstack( - 'application credential create %(name)s' % {'name': name} - ) + self.openstack(f'application credential create {name}') raw_output = self.openstack( 'application credential delete ' '%(name)s' % {'name': name} ) @@ -169,12 +165,10 @@ def test_application_credential_list(self): def test_application_credential_show(self): name = data_utils.rand_name('name') - raw_output = self.openstack( - 'application credential create %(name)s' % {'name': name} - ) + raw_output = self.openstack(f'application credential create {name}') self.addCleanup( self.openstack, - 'application credential delete %(name)s' % {'name': name}, + f'application credential delete {name}', ) raw_output = self.openstack( 'application credential show ' '%(name)s' % {'name': name} diff --git a/openstackclient/tests/functional/identity/v3/test_domain.py b/openstackclient/tests/functional/identity/v3/test_domain.py index bf5020ca6..08e0e0790 100644 --- a/openstackclient/tests/functional/identity/v3/test_domain.py +++ b/openstackclient/tests/functional/identity/v3/test_domain.py @@ -50,9 +50,7 @@ def test_domain_multi_delete(self): self.assertEqual(0, len(raw_output)) raw_output = self.openstack('domain set --disable %s' % domain_2) self.assertEqual(0, len(raw_output)) - raw_output = self.openstack( - 'domain delete %s %s' % (domain_1, domain_2) - ) + raw_output = self.openstack(f'domain delete {domain_1} {domain_2}') self.assertEqual(0, len(raw_output)) def test_domain_delete_failure(self): diff --git a/openstackclient/tests/functional/identity/v3/test_endpoint.py b/openstackclient/tests/functional/identity/v3/test_endpoint.py index 53f2bd22b..1fff6abeb 100644 --- a/openstackclient/tests/functional/identity/v3/test_endpoint.py +++ b/openstackclient/tests/functional/identity/v3/test_endpoint.py @@ -30,7 +30,7 @@ def test_endpoint_multi_delete(self): endpoint_1 = self._create_dummy_endpoint(add_clean_up=False) endpoint_2 = self._create_dummy_endpoint(add_clean_up=False) raw_output = self.openstack( - 'endpoint delete %s %s' % (endpoint_1, endpoint_2) + f'endpoint delete {endpoint_1} {endpoint_2}' ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_idp.py b/openstackclient/tests/functional/identity/v3/test_idp.py index b359da7cd..05f3ee84e 100644 --- a/openstackclient/tests/functional/identity/v3/test_idp.py +++ b/openstackclient/tests/functional/identity/v3/test_idp.py @@ -32,7 +32,7 @@ def test_idp_multi_delete(self): idp_1 = self._create_dummy_idp(add_clean_up=False) idp_2 = self._create_dummy_idp(add_clean_up=False) raw_output = self.openstack( - 'identity provider delete %s %s' % (idp_1, idp_2) + f'identity provider delete {idp_1} {idp_2}' ) self.assertEqual(0, len(raw_output)) diff --git a/openstackclient/tests/functional/identity/v3/test_region.py b/openstackclient/tests/functional/identity/v3/test_region.py index bc014737d..48ca83781 100644 --- a/openstackclient/tests/functional/identity/v3/test_region.py +++ b/openstackclient/tests/functional/identity/v3/test_region.py @@ -29,9 +29,7 @@ def test_region_delete(self): def test_region_multi_delete(self): region_1 = self._create_dummy_region(add_clean_up=False) region_2 = self._create_dummy_region(add_clean_up=False) - raw_output = self.openstack( - 'region delete %s %s' % (region_1, region_2) - ) + raw_output = self.openstack(f'region delete {region_1} {region_2}') self.assertEqual(0, len(raw_output)) def test_region_list(self): diff --git a/openstackclient/tests/functional/identity/v3/test_role.py b/openstackclient/tests/functional/identity/v3/test_role.py index 2d5a15e47..9a1aa648f 100644 --- a/openstackclient/tests/functional/identity/v3/test_role.py +++ b/openstackclient/tests/functional/identity/v3/test_role.py @@ -55,7 +55,7 @@ def test_role_set(self): role_name = self._create_dummy_role() new_role_name = data_utils.rand_name('NewTestRole') raw_output = self.openstack( - 'role set --name %s %s' % (new_role_name, role_name) + 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) @@ -66,7 +66,7 @@ def test_role_set_description(self): role_name = self._create_dummy_role() description = data_utils.rand_name("NewDescription") raw_output = self.openstack( - 'role set --description %s %s' % (description, role_name) + f'role set --description {description} {role_name}' ) self.assertEqual(0, len(raw_output)) raw_output = self.openstack('role show %s' % role_name) diff --git a/openstackclient/tests/functional/identity/v3/test_service.py b/openstackclient/tests/functional/identity/v3/test_service.py index 5d59b65e9..98fa349d1 100644 --- a/openstackclient/tests/functional/identity/v3/test_service.py +++ b/openstackclient/tests/functional/identity/v3/test_service.py @@ -27,9 +27,7 @@ def test_service_delete(self): def test_service_multi_delete(self): service_1 = self._create_dummy_service(add_clean_up=False) service_2 = self._create_dummy_service(add_clean_up=False) - raw_output = self.openstack( - 'service delete %s %s' % (service_1, service_2) - ) + raw_output = self.openstack(f'service delete {service_1} {service_2}') self.assertEqual(0, len(raw_output)) def test_service_list(self): diff --git a/openstackclient/tests/functional/identity/v3/test_service_provider.py b/openstackclient/tests/functional/identity/v3/test_service_provider.py index d6750595d..b7ab14bc4 100644 --- a/openstackclient/tests/functional/identity/v3/test_service_provider.py +++ b/openstackclient/tests/functional/identity/v3/test_service_provider.py @@ -31,9 +31,7 @@ def test_sp_delete(self): def test_sp_multi_delete(self): sp1 = self._create_dummy_sp(add_clean_up=False) sp2 = self._create_dummy_sp(add_clean_up=False) - raw_output = self.openstack( - 'service provider delete %s %s' % (sp1, sp2) - ) + raw_output = self.openstack(f'service provider delete {sp1} {sp2}') self.assertEqual(0, len(raw_output)) def test_sp_show(self): diff --git a/openstackclient/tests/functional/image/base.py b/openstackclient/tests/functional/image/base.py index 4b2ab64b7..d948f8155 100644 --- a/openstackclient/tests/functional/image/base.py +++ b/openstackclient/tests/functional/image/base.py @@ -18,7 +18,7 @@ class BaseImageTests(base.TestCase): @classmethod def setUpClass(cls): - super(BaseImageTests, cls).setUpClass() + super().setUpClass() # TODO(dtroyer): maybe do image API discovery here to determine # what is available, it isn't in the service catalog cls.haz_v1_api = False diff --git a/openstackclient/tests/functional/image/v2/test_image.py b/openstackclient/tests/functional/image/v2/test_image.py index 9d7f570ab..93251ea53 100644 --- a/openstackclient/tests/functional/image/v2/test_image.py +++ b/openstackclient/tests/functional/image/v2/test_image.py @@ -21,7 +21,7 @@ class ImageTests(base.BaseImageTests): """Functional tests for Image commands""" def setUp(self): - super(ImageTests, self).setUp() + super().setUp() ver_fixture = fixtures.EnvironmentVariable('OS_IMAGE_API_VERSION', '2') self.useFixture(ver_fixture) diff --git a/openstackclient/tests/functional/image/v2/test_info.py b/openstackclient/tests/functional/image/v2/test_info.py index a04e0cb56..c601ea082 100644 --- a/openstackclient/tests/functional/image/v2/test_info.py +++ b/openstackclient/tests/functional/image/v2/test_info.py @@ -20,7 +20,7 @@ class InfoTests(base.BaseImageTests): """Functional tests for Info commands""" def setUp(self): - super(InfoTests, self).setUp() + super().setUp() def tearDown(self): super().tearDown() diff --git a/openstackclient/tests/functional/network/v2/common.py b/openstackclient/tests/functional/network/v2/common.py index eecfff468..f7c7212a1 100644 --- a/openstackclient/tests/functional/network/v2/common.py +++ b/openstackclient/tests/functional/network/v2/common.py @@ -91,27 +91,25 @@ def _list_tag_check(self, project_id, expected): def _create_resource_for_tag_test(self, name, args): return self.openstack( - '{} create {} {}'.format(self.base_command, args, name), + f'{self.base_command} create {args} {name}', parse_output=True, ) def _create_resource_and_tag_check(self, args, expected): name = uuid.uuid4().hex cmd_output = self._create_resource_for_tag_test(name, args) - self.addCleanup( - self.openstack, '{} delete {}'.format(self.base_command, name) - ) + self.addCleanup(self.openstack, f'{self.base_command} delete {name}') self.assertIsNotNone(cmd_output["id"]) self.assertEqual(set(expected), set(cmd_output['tags'])) return name def _set_resource_and_tag_check(self, command, name, args, expected): cmd_output = self.openstack( - '{} {} {} {}'.format(self.base_command, command, args, name) + f'{self.base_command} {command} {args} {name}' ) self.assertFalse(cmd_output) cmd_output = self.openstack( - '{} show {}'.format(self.base_command, name), + f'{self.base_command} show {name}', parse_output=True, ) self.assertEqual(set(expected), set(cmd_output['tags'])) diff --git a/openstackclient/tests/functional/network/v2/test_network.py b/openstackclient/tests/functional/network/v2/test_network.py index fde34c278..8dbe883b6 100644 --- a/openstackclient/tests/functional/network/v2/test_network.py +++ b/openstackclient/tests/functional/network/v2/test_network.py @@ -192,7 +192,7 @@ def test_network_delete_network(self): cmd_output["description"], ) - del_output = self.openstack('network delete %s %s' % (name1, name2)) + del_output = self.openstack(f'network delete {name1} {name2}') self.assertOutput('', del_output) def test_network_list(self): @@ -237,7 +237,7 @@ def test_network_list(self): else: network_options = '--subnet 4.5.6.7/28 ' cmd_output = self.openstack( - 'network create --share %s%s' % (network_options, name2), + f'network create --share {network_options}{name2}', parse_output=True, ) self.addCleanup(self.openstack, 'network delete ' + name2) @@ -353,7 +353,9 @@ def test_network_dhcp_agent(self): # Add Agent to Network self.openstack( - 'network agent add network --dhcp %s %s' % (agent_id, network_id) + 'network agent add network --dhcp {} {}'.format( + agent_id, network_id + ) ) # Test network list --agent diff --git a/openstackclient/tests/functional/network/v2/test_network_agent.py b/openstackclient/tests/functional/network/v2/test_network_agent.py index c9a2ee121..bbe168aee 100644 --- a/openstackclient/tests/functional/network/v2/test_network_agent.py +++ b/openstackclient/tests/functional/network/v2/test_network_agent.py @@ -123,7 +123,9 @@ def test_network_dhcp_agent_list(self): # Add Agent to Network self.openstack( - 'network agent add network --dhcp %s %s' % (agent_id, network_id) + 'network agent add network --dhcp {} {}'.format( + agent_id, network_id + ) ) # Test network agent list --network @@ -169,9 +171,7 @@ def test_network_agent_list_routers(self): agent_id = cmd_output[0]['ID'] # Add router to agent - self.openstack( - 'network agent add router --l3 %s %s' % (agent_id, router_id) - ) + self.openstack(f'network agent add router --l3 {agent_id} {router_id}') # Test router list --agent cmd_output = self.openstack( @@ -184,7 +184,9 @@ def test_network_agent_list_routers(self): # Remove router from agent self.openstack( - 'network agent remove router --l3 %s %s' % (agent_id, router_id) + 'network agent remove router --l3 {} {}'.format( + agent_id, router_id + ) ) cmd_output = self.openstack( 'network agent list --router %s' % router_id, 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 0e16a5035..8db484e2e 100644 --- a/openstackclient/tests/functional/network/v2/test_network_qos_rule.py +++ b/openstackclient/tests/functional/network/v2/test_network_qos_rule.py @@ -69,7 +69,7 @@ def test_qos_rule_create_delete(self): parse_output=True, ) raw_output = self.openstack( - 'network qos rule delete %s %s' % (policy_name, rule['id']) + 'network qos rule delete {} {}'.format(policy_name, rule['id']) ) self.assertEqual('', raw_output) @@ -144,7 +144,7 @@ def test_qos_rule_create_delete(self): parse_output=True, ) raw_output = self.openstack( - 'network qos rule delete %s %s' % (policy_name, rule['id']) + 'network qos rule delete {} {}'.format(policy_name, rule['id']) ) self.assertEqual('', raw_output) @@ -217,7 +217,7 @@ def test_qos_rule_create_delete(self): parse_output=True, ) raw_output = self.openstack( - 'network qos rule delete %s %s' % (policy_name, rule['id']) + 'network qos rule delete {} {}'.format(policy_name, rule['id']) ) self.assertEqual('', raw_output) @@ -292,7 +292,7 @@ def test_qos_rule_create_delete(self): parse_output=True, ) raw_output = self.openstack( - 'network qos rule delete %s %s' % (policy_name, rule['id']) + 'network qos rule delete {} {}'.format(policy_name, rule['id']) ) self.assertEqual('', raw_output) diff --git a/openstackclient/tests/functional/network/v2/test_port.py b/openstackclient/tests/functional/network/v2/test_port.py index 700c8dfd4..0ba377341 100644 --- a/openstackclient/tests/functional/network/v2/test_port.py +++ b/openstackclient/tests/functional/network/v2/test_port.py @@ -47,7 +47,7 @@ def tearDownClass(cls): def test_port_delete(self): """Test create, delete multiple""" json_output = self.openstack( - 'port create --network %s %s' % (self.NETWORK_NAME, self.NAME), + f'port create --network {self.NETWORK_NAME} {self.NAME}', parse_output=True, ) id1 = json_output.get('id') @@ -56,7 +56,9 @@ def test_port_delete(self): self.assertEqual(self.NAME, json_output.get('name')) json_output = self.openstack( - 'port create --network %s %sx' % (self.NETWORK_NAME, self.NAME), + 'port create --network {} {}x'.format( + self.NETWORK_NAME, self.NAME + ), parse_output=True, ) id2 = json_output.get('id') @@ -65,13 +67,13 @@ def test_port_delete(self): self.assertEqual(self.NAME + 'x', json_output.get('name')) # Clean up after ourselves - raw_output = self.openstack('port delete %s %s' % (id1, id2)) + raw_output = self.openstack(f'port delete {id1} {id2}') self.assertOutput('', raw_output) def test_port_list(self): """Test create defaults, list, delete""" json_output = self.openstack( - 'port create --network %s %s' % (self.NETWORK_NAME, self.NAME), + f'port create --network {self.NETWORK_NAME} {self.NAME}', parse_output=True, ) id1 = json_output.get('id') @@ -82,7 +84,9 @@ def test_port_list(self): self.assertEqual(self.NAME, json_output.get('name')) json_output = self.openstack( - 'port create --network %s %sx' % (self.NETWORK_NAME, self.NAME), + 'port create --network {} {}x'.format( + self.NETWORK_NAME, self.NAME + ), parse_output=True, ) id2 = json_output.get('id') @@ -169,7 +173,7 @@ def test_port_set(self): self.assertIsNotNone(json_output.get('mac_address')) raw_output = self.openstack( - 'port unset --security-group %s %s' % (sg_id, id1) + f'port unset --security-group {sg_id} {id1}' ) self.assertOutput('', raw_output) @@ -247,7 +251,7 @@ def test_port_set_sg(self): ) raw_output = self.openstack( - 'port unset --security-group %s %s' % (sg_id1, id1) + f'port unset --security-group {sg_id1} {id1}' ) self.assertOutput('', raw_output) diff --git a/openstackclient/tests/functional/network/v2/test_router.py b/openstackclient/tests/functional/network/v2/test_router.py index 4f7c4c4a5..bd4261ac7 100644 --- a/openstackclient/tests/functional/network/v2/test_router.py +++ b/openstackclient/tests/functional/network/v2/test_router.py @@ -287,10 +287,10 @@ def test_router_add_remove_route(self): self.openstack('router create %s' % router_name) self.addCleanup(self.openstack, 'router delete %s' % router_name) - self.openstack('router add subnet %s %s' % (router_name, subnet_name)) + self.openstack(f'router add subnet {router_name} {subnet_name}') self.addCleanup( self.openstack, - 'router remove subnet %s %s' % (router_name, subnet_name), + f'router remove subnet {router_name} {subnet_name}', ) out1 = ( diff --git a/openstackclient/tests/functional/object/v1/common.py b/openstackclient/tests/functional/object/v1/common.py index b01334302..036731da5 100644 --- a/openstackclient/tests/functional/object/v1/common.py +++ b/openstackclient/tests/functional/object/v1/common.py @@ -18,5 +18,5 @@ class ObjectStoreTests(base.TestCase): @classmethod def setUpClass(cls): - super(ObjectStoreTests, cls).setUpClass() + super().setUpClass() cls.haz_object_store = cls.is_service_enabled('object-store') diff --git a/openstackclient/tests/functional/object/v1/test_container.py b/openstackclient/tests/functional/object/v1/test_container.py index 22adbf6f6..6480fb907 100644 --- a/openstackclient/tests/functional/object/v1/test_container.py +++ b/openstackclient/tests/functional/object/v1/test_container.py @@ -23,7 +23,7 @@ class ContainerTests(common.ObjectStoreTests): @classmethod def setUpClass(cls): - super(ContainerTests, cls).setUpClass() + super().setUpClass() if cls.haz_object_store: opts = cls.get_opts(['container']) raw_output = cls.openstack('container create ' + cls.NAME + opts) @@ -36,10 +36,10 @@ def tearDownClass(cls): raw_output = cls.openstack('container delete ' + cls.NAME) cls.assertOutput('', raw_output) finally: - super(ContainerTests, cls).tearDownClass() + super().tearDownClass() def setUp(self): - super(ContainerTests, self).setUp() + super().setUp() # Skip tests if no object-store is present if not self.haz_object_store: self.skipTest("No object-store service present") diff --git a/openstackclient/tests/functional/object/v1/test_object.py b/openstackclient/tests/functional/object/v1/test_object.py index f8940f77a..73044de9d 100644 --- a/openstackclient/tests/functional/object/v1/test_object.py +++ b/openstackclient/tests/functional/object/v1/test_object.py @@ -27,7 +27,7 @@ class ObjectTests(common.ObjectStoreTests): CONTAINER_NAME = uuid.uuid4().hex def setUp(self): - super(ObjectTests, self).setUp() + super().setUp() # Skip tests if no object-store is present if not self.haz_object_store: self.skipTest("No object-store service present") @@ -54,7 +54,7 @@ def _test_object(self, object_file): # TODO(stevemar): Assert returned fields raw_output = self.openstack( - 'object create %s %s' % (self.CONTAINER_NAME, object_file) + f'object create {self.CONTAINER_NAME} {object_file}' ) items = self.parse_listing(raw_output) self.assert_show_fields(items, OBJECT_FIELDS) @@ -63,9 +63,7 @@ def _test_object(self, object_file): items = self.parse_listing(raw_output) self.assert_table_structure(items, BASIC_LIST_HEADERS) - self.openstack( - 'object save %s %s' % (self.CONTAINER_NAME, object_file) - ) + self.openstack(f'object save {self.CONTAINER_NAME} {object_file}') # TODO(stevemar): Assert returned fields tmp_file = 'tmp.txt' @@ -77,23 +75,21 @@ def _test_object(self, object_file): # TODO(stevemar): Assert returned fields raw_output = self.openstack( - 'object save %s %s --file -' % (self.CONTAINER_NAME, object_file) + 'object save {} {} --file -'.format( + self.CONTAINER_NAME, object_file + ) ) self.assertEqual(raw_output, 'test content') - self.openstack( - 'object show %s %s' % (self.CONTAINER_NAME, object_file) - ) + self.openstack(f'object show {self.CONTAINER_NAME} {object_file}') # TODO(stevemar): Assert returned fields raw_output = self.openstack( - 'object delete %s %s' % (self.CONTAINER_NAME, object_file) + f'object delete {self.CONTAINER_NAME} {object_file}' ) self.assertEqual(0, len(raw_output)) - self.openstack( - 'object create %s %s' % (self.CONTAINER_NAME, object_file) - ) + self.openstack(f'object create {self.CONTAINER_NAME} {object_file}') raw_output = self.openstack( 'container delete -r %s' % self.CONTAINER_NAME ) diff --git a/openstackclient/tests/functional/volume/base.py b/openstackclient/tests/functional/volume/base.py index f15a31451..ab05642eb 100644 --- a/openstackclient/tests/functional/volume/base.py +++ b/openstackclient/tests/functional/volume/base.py @@ -70,7 +70,7 @@ def wait_for_delete( result = cls.openstack(check_type + ' list', parse_output=True) names = [x[name_field] for x in result] if check_name not in names: - print('{} {} is now deleted'.format(check_type, check_name)) + print(f'{check_type} {check_name} is now deleted') return print( 'Checking {} {} Waiting for deleted'.format( diff --git a/openstackclient/tests/functional/volume/v1/test_service.py b/openstackclient/tests/functional/volume/v1/test_service.py index 0f510a8a7..8d058c26f 100644 --- a/openstackclient/tests/functional/volume/v1/test_service.py +++ b/openstackclient/tests/functional/volume/v1/test_service.py @@ -20,7 +20,7 @@ def test_volume_service_list(self): cmd_output = self.openstack('volume service list', parse_output=True) # Get the nonredundant services and hosts - services = list(set([x['Binary'] for x in cmd_output])) + services = list({x['Binary'] for x in cmd_output}) # Test volume service list --service cmd_output = self.openstack( diff --git a/openstackclient/tests/functional/volume/v1/test_snapshot.py b/openstackclient/tests/functional/volume/v1/test_snapshot.py index 52877d5a4..1acd014a6 100644 --- a/openstackclient/tests/functional/volume/v1/test_snapshot.py +++ b/openstackclient/tests/functional/volume/v1/test_snapshot.py @@ -22,7 +22,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests): @classmethod def setUpClass(cls): - super(VolumeSnapshotTests, cls).setUpClass() + super().setUpClass() # create a volume for all tests to create snapshot cmd_output = cls.openstack( 'volume create ' + '--size 1 ' + cls.VOLLY, @@ -38,7 +38,7 @@ def tearDownClass(cls): raw_output = cls.openstack('volume delete --force ' + cls.VOLLY) cls.assertOutput('', raw_output) finally: - super(VolumeSnapshotTests, cls).tearDownClass() + super().tearDownClass() def test_volume_snapshot_delete(self): """Test create, delete multiple""" diff --git a/openstackclient/tests/functional/volume/v1/test_transfer_request.py b/openstackclient/tests/functional/volume/v1/test_transfer_request.py index 37aa4ca22..68fe6666f 100644 --- a/openstackclient/tests/functional/volume/v1/test_transfer_request.py +++ b/openstackclient/tests/functional/volume/v1/test_transfer_request.py @@ -23,7 +23,7 @@ class TransferRequestTests(common.BaseVolumeTests): @classmethod def setUpClass(cls): - super(TransferRequestTests, cls).setUpClass() + super().setUpClass() cmd_output = cls.openstack( 'volume create --size 1 ' + cls.VOLUME_NAME, parse_output=True, @@ -40,7 +40,7 @@ def tearDownClass(cls): ) cls.assertOutput('', raw_output_volume) finally: - super(TransferRequestTests, cls).tearDownClass() + super().tearDownClass() def test_volume_transfer_request_accept(self): volume_name = uuid.uuid4().hex diff --git a/openstackclient/tests/functional/volume/v1/test_volume_type.py b/openstackclient/tests/functional/volume/v1/test_volume_type.py index 6448af6e4..97c8b66c1 100644 --- a/openstackclient/tests/functional/volume/v1/test_volume_type.py +++ b/openstackclient/tests/functional/volume/v1/test_volume_type.py @@ -110,7 +110,7 @@ def test_multi_delete(self): time.sleep(5) self.openstack('volume type create %s' % vol_type2) time.sleep(5) - cmd = 'volume type delete %s %s' % (vol_type1, vol_type2) + cmd = f'volume type delete {vol_type1} {vol_type2}' raw_output = self.openstack(cmd) self.assertOutput('', raw_output) diff --git a/openstackclient/tests/functional/volume/v2/test_service.py b/openstackclient/tests/functional/volume/v2/test_service.py index 1101affcd..cca32f962 100644 --- a/openstackclient/tests/functional/volume/v2/test_service.py +++ b/openstackclient/tests/functional/volume/v2/test_service.py @@ -20,8 +20,8 @@ def test_volume_service_list(self): cmd_output = self.openstack('volume service list', parse_output=True) # Get the nonredundant services and hosts - services = list(set([x['Binary'] for x in cmd_output])) - hosts = list(set([x['Host'] for x in cmd_output])) + services = list({x['Binary'] for x in cmd_output}) + hosts = list({x['Host'] for x in cmd_output}) # Test volume service list --service cmd_output = self.openstack( diff --git a/openstackclient/tests/functional/volume/v2/test_volume_backup.py b/openstackclient/tests/functional/volume/v2/test_volume_backup.py index e6f7dec9f..6bd37f4dc 100644 --- a/openstackclient/tests/functional/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/functional/volume/v2/test_volume_backup.py @@ -19,7 +19,7 @@ class VolumeBackupTests(common.BaseVolumeTests): """Functional tests for volume backups.""" def setUp(self): - super(VolumeBackupTests, self).setUp() + super().setUp() self.backup_enabled = False serv_list = self.openstack('volume service list', parse_output=True) for service in serv_list: @@ -48,7 +48,7 @@ def test_volume_backup_restore(self): # restore the backup backup_restored = self.openstack( - 'volume backup restore %s %s' % (backup['id'], vol_id), + 'volume backup restore {} {}'.format(backup['id'], vol_id), parse_output=True, ) self.assertEqual(backup_restored['backup_id'], backup['id']) diff --git a/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py b/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py index 7aefdf297..e5daded1b 100644 --- a/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py +++ b/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py @@ -22,7 +22,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests): @classmethod def setUpClass(cls): - super(VolumeSnapshotTests, cls).setUpClass() + super().setUpClass() # create a volume for all tests to create snapshot cmd_output = cls.openstack( 'volume create ' + '--size 1 ' + cls.VOLLY, @@ -38,7 +38,7 @@ def tearDownClass(cls): raw_output = cls.openstack('volume delete --force ' + cls.VOLLY) cls.assertOutput('', raw_output) finally: - super(VolumeSnapshotTests, cls).tearDownClass() + super().tearDownClass() def test_volume_snapshot_delete(self): """Test create, delete multiple""" diff --git a/openstackclient/tests/functional/volume/v2/test_volume_type.py b/openstackclient/tests/functional/volume/v2/test_volume_type.py index b9bbcfc34..bdbced00f 100644 --- a/openstackclient/tests/functional/volume/v2/test_volume_type.py +++ b/openstackclient/tests/functional/volume/v2/test_volume_type.py @@ -129,7 +129,7 @@ def test_multi_delete(self): time.sleep(5) self.openstack('volume type create %s' % vol_type2) time.sleep(5) - cmd = 'volume type delete %s %s' % (vol_type1, vol_type2) + cmd = f'volume type delete {vol_type1} {vol_type2}' raw_output = self.openstack(cmd) self.assertOutput('', raw_output) diff --git a/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py b/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py index b9e86a93b..2c9b72ba1 100644 --- a/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py +++ b/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py @@ -22,7 +22,7 @@ class VolumeSnapshotTests(common.BaseVolumeTests): @classmethod def setUpClass(cls): - super(VolumeSnapshotTests, cls).setUpClass() + super().setUpClass() # create a volume for all tests to create snapshot cmd_output = cls.openstack( 'volume create ' + '--size 1 ' + cls.VOLLY, @@ -38,7 +38,7 @@ def tearDownClass(cls): raw_output = cls.openstack('volume delete --force ' + cls.VOLLY) cls.assertOutput('', raw_output) finally: - super(VolumeSnapshotTests, cls).tearDownClass() + super().tearDownClass() def test_volume_snapshot_delete(self): """Test create, delete multiple""" diff --git a/openstackclient/tests/functional/volume/v3/test_volume_type.py b/openstackclient/tests/functional/volume/v3/test_volume_type.py index 4edb3416f..78f9e4063 100644 --- a/openstackclient/tests/functional/volume/v3/test_volume_type.py +++ b/openstackclient/tests/functional/volume/v3/test_volume_type.py @@ -129,7 +129,7 @@ def test_multi_delete(self): time.sleep(5) self.openstack('volume type create %s' % vol_type2) time.sleep(5) - cmd = 'volume type delete %s %s' % (vol_type1, vol_type2) + cmd = f'volume type delete {vol_type1} {vol_type2}' raw_output = self.openstack(cmd) self.assertOutput('', raw_output) diff --git a/openstackclient/tests/unit/api/fakes.py b/openstackclient/tests/unit/api/fakes.py index 5db722423..192bd69d5 100644 --- a/openstackclient/tests/unit/api/fakes.py +++ b/openstackclient/tests/unit/api/fakes.py @@ -50,6 +50,6 @@ class TestSession(utils.TestCase): BASE_URL = 'https://api.example.com:1234/vX' def setUp(self): - super(TestSession, self).setUp() + super().setUp() self.sess = session.Session() self.requests_mock = self.useFixture(fixture.Fixture()) diff --git a/openstackclient/tests/unit/api/test_api.py b/openstackclient/tests/unit/api/test_api.py index 597fa1074..f930260b1 100644 --- a/openstackclient/tests/unit/api/test_api.py +++ b/openstackclient/tests/unit/api/test_api.py @@ -21,7 +21,7 @@ class TestKeystoneSession(api_fakes.TestSession): def setUp(self): - super(TestKeystoneSession, self).setUp() + super().setUp() self.api = api.KeystoneSession( session=self.sess, endpoint=self.BASE_URL, @@ -40,7 +40,7 @@ def test_session_request(self): class TestBaseAPI(api_fakes.TestSession): def setUp(self): - super(TestBaseAPI, self).setUp() + super().setUp() self.api = api.BaseAPI( session=self.sess, endpoint=self.BASE_URL, diff --git a/openstackclient/tests/unit/api/test_compute_v2.py b/openstackclient/tests/unit/api/test_compute_v2.py index 1ed164ac0..3856a7700 100644 --- a/openstackclient/tests/unit/api/test_compute_v2.py +++ b/openstackclient/tests/unit/api/test_compute_v2.py @@ -27,7 +27,7 @@ class TestComputeAPIv2(utils.TestCase): def setUp(self): - super(TestComputeAPIv2, self).setUp() + super().setUp() sess = session.Session() self.api = compute.APIv2(session=sess, endpoint=FAKE_URL) self.requests_mock = self.useFixture(fixture.Fixture()) diff --git a/openstackclient/tests/unit/api/test_image_v1.py b/openstackclient/tests/unit/api/test_image_v1.py index 74899ad42..4d0ac53ea 100644 --- a/openstackclient/tests/unit/api/test_image_v1.py +++ b/openstackclient/tests/unit/api/test_image_v1.py @@ -26,7 +26,7 @@ class TestImageAPIv1(utils.TestCase): def setUp(self): - super(TestImageAPIv1, self).setUp() + super().setUp() sess = session.Session() self.api = image_v1.APIv1(session=sess, endpoint=FAKE_URL) diff --git a/openstackclient/tests/unit/api/test_image_v2.py b/openstackclient/tests/unit/api/test_image_v2.py index 0de71acb0..a4cb606d8 100644 --- a/openstackclient/tests/unit/api/test_image_v2.py +++ b/openstackclient/tests/unit/api/test_image_v2.py @@ -26,7 +26,7 @@ class TestImageAPIv2(utils.TestCase): def setUp(self): - super(TestImageAPIv2, self).setUp() + super().setUp() sess = session.Session() self.api = image_v2.APIv2(session=sess, endpoint=FAKE_URL) diff --git a/openstackclient/tests/unit/api/test_object_store_v1.py b/openstackclient/tests/unit/api/test_object_store_v1.py index e516d3de3..39bc26a61 100644 --- a/openstackclient/tests/unit/api/test_object_store_v1.py +++ b/openstackclient/tests/unit/api/test_object_store_v1.py @@ -52,7 +52,7 @@ class TestObjectAPIv1(utils.TestCase): def setUp(self): - super(TestObjectAPIv1, self).setUp() + super().setUp() sess = session.Session() self.api = object_store.APIv1(session=sess, endpoint=FAKE_URL) self.requests_mock = self.useFixture(fixture.Fixture()) @@ -60,7 +60,7 @@ def setUp(self): class TestContainer(TestObjectAPIv1): def setUp(self): - super(TestContainer, self).setUp() + super().setUp() def test_container_create(self): headers = { @@ -181,9 +181,9 @@ def test_container_show(self): class TestObject(TestObjectAPIv1): def setUp(self): - super(TestObject, self).setUp() + super().setUp() - @mock.patch('openstackclient.api.object_store_v1.io.open') + @mock.patch('openstackclient.api.object_store_v1.open') def base_object_create(self, file_contents, mock_open): mock_open.read.return_value = file_contents diff --git a/openstackclient/tests/unit/common/test_logs.py b/openstackclient/tests/unit/common/test_logs.py index 4cabd60b2..234ba427e 100644 --- a/openstackclient/tests/unit/common/test_logs.py +++ b/openstackclient/tests/unit/common/test_logs.py @@ -80,7 +80,7 @@ def test_nothing(self): ) def test_options(self): - class Opts(object): + class Opts: cloud = 'cloudy' os_project_name = 'projecty' username = 'usernamey' @@ -111,7 +111,7 @@ def test_config(self): class TestLogConfigurator(utils.TestCase): def setUp(self): - super(TestLogConfigurator, self).setUp() + super().setUp() self.options = mock.Mock() self.options.verbose_level = 1 self.options.log_file = None diff --git a/openstackclient/tests/unit/common/test_module.py b/openstackclient/tests/unit/common/test_module.py index 18ac7c20b..68255a04e 100644 --- a/openstackclient/tests/unit/common/test_module.py +++ b/openstackclient/tests/unit/common/test_module.py @@ -53,7 +53,7 @@ class TestCommandList(utils.TestCommand): def setUp(self): - super(TestCommandList, self).setUp() + super().setUp() self.app.command_manager = mock.Mock() self.app.command_manager.get_command_groups.return_value = [ @@ -126,7 +126,7 @@ def test_command_list_with_group(self): ) class TestModuleList(utils.TestCommand): def setUp(self): - super(TestModuleList, self).setUp() + super().setUp() # Get the command object to test self.cmd = osc_module.ListModule(self.app, None) diff --git a/openstackclient/tests/unit/common/test_project_cleanup.py b/openstackclient/tests/unit/common/test_project_cleanup.py index df6ac1732..019d6cf15 100644 --- a/openstackclient/tests/unit/common/test_project_cleanup.py +++ b/openstackclient/tests/unit/common/test_project_cleanup.py @@ -22,7 +22,7 @@ class TestProjectCleanup(test_utils.TestCommand): project = identity_fakes.FakeProject.create_one_project() def setUp(self): - super(TestProjectCleanup, self).setUp() + super().setUp() self.cmd = project_cleanup.ProjectCleanup(self.app, None) self.project_cleanup_mock = mock.Mock() diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index d99508abe..7edddbb19 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -40,7 +40,7 @@ def get_keys(self): class TestQuota(compute_fakes.TestComputev2): def setUp(self): - super(TestQuota, self).setUp() + super().setUp() # Set up common projects self.projects = identity_fakes_v3.FakeProject.create_projects(count=2) @@ -109,7 +109,7 @@ class TestQuotaList(TestQuota): ) def setUp(self): - super(TestQuotaList, self).setUp() + super().setUp() # Work with multiple projects in this class self.projects_mock.get.side_effect = self.projects @@ -629,7 +629,7 @@ def test_quota_list_volume_by_project(self): class TestQuotaSet(TestQuota): def setUp(self): - super(TestQuotaSet, self).setUp() + super().setUp() self.compute_quotas_mock.update.return_value = FakeQuotaResource( None, diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index f8a816b6a..6c201db46 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -81,7 +81,7 @@ QUOTA_data = tuple(QUOTA[x] for x in sorted(QUOTA)) -class FakeComputev2Client(object): +class FakeComputev2Client: def __init__(self, **kwargs): self.agents = mock.Mock() self.agents.resource_class = fakes.FakeResource(None, {}) @@ -1210,7 +1210,7 @@ def create_one_comp_detailed_quota(attrs=None): return quota -class FakeLimits(object): +class FakeLimits: """Fake limits""" def __init__(self, absolute_attrs=None, rate_attrs=None): @@ -1310,7 +1310,7 @@ def rate_limits(self): return reference_data -class FakeAbsoluteLimit(object): +class FakeAbsoluteLimit: """Data model that represents an absolute limit""" def __init__(self, name, value): @@ -1318,7 +1318,7 @@ def __init__(self, name, value): self.value = value -class FakeRateLimit(object): +class FakeRateLimit: """Data model that represents a flattened view of a single rate limit""" def __init__(self, verb, uri, value, remain, unit, next_available): diff --git a/openstackclient/tests/unit/compute/v2/test_agent.py b/openstackclient/tests/unit/compute/v2/test_agent.py index dc3ba6730..3969e9d8f 100644 --- a/openstackclient/tests/unit/compute/v2/test_agent.py +++ b/openstackclient/tests/unit/compute/v2/test_agent.py @@ -49,7 +49,7 @@ class TestAgent(compute_fakes.TestComputev2): ) def setUp(self): - super(TestAgent, self).setUp() + super().setUp() self.agents_mock = self.compute_client.agents self.agents_mock.reset_mock() @@ -57,7 +57,7 @@ def setUp(self): class TestAgentCreate(TestAgent): def setUp(self): - super(TestAgentCreate, self).setUp() + super().setUp() self.agents_mock.create.return_value = self.fake_agent self.cmd = agent.CreateAgent(self.app, None) @@ -99,7 +99,7 @@ class TestAgentDelete(TestAgent): fake_agents = compute_fakes.create_agents(count=2) def setUp(self): - super(TestAgentDelete, self).setUp() + super().setUp() self.agents_mock.get.return_value = self.fake_agents self.cmd = agent.DeleteAgent(self.app, None) @@ -198,7 +198,7 @@ class TestAgentList(TestAgent): ) def setUp(self): - super(TestAgentList, self).setUp() + super().setUp() self.agents_mock.list.return_value = self.agents self.cmd = agent.ListAgent(self.app, None) @@ -231,7 +231,7 @@ def test_agent_list_with_hypervisor(self): class TestAgentSet(TestAgent): def setUp(self): - super(TestAgentSet, self).setUp() + super().setUp() self.agents_mock.update.return_value = self.fake_agent self.agents_mock.list.return_value = [self.fake_agent] diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index 13a643720..a6709f708 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -58,7 +58,7 @@ class TestAggregate(compute_fakes.TestComputev2): class TestAggregateAddHost(TestAggregate): def setUp(self): - super(TestAggregateAddHost, self).setUp() + super().setUp() self.compute_sdk_client.find_aggregate.return_value = self.fake_ag self.compute_sdk_client.add_host_to_aggregate.return_value = ( @@ -89,7 +89,7 @@ def test_aggregate_add_host(self): class TestAggregateCreate(TestAggregate): def setUp(self): - super(TestAggregateCreate, self).setUp() + super().setUp() self.compute_sdk_client.create_aggregate.return_value = self.fake_ag self.compute_sdk_client.set_aggregate_metadata.return_value = ( @@ -159,7 +159,7 @@ class TestAggregateDelete(TestAggregate): fake_ags = compute_fakes.create_aggregates(count=2) def setUp(self): - super(TestAggregateDelete, self).setUp() + super().setUp() self.compute_sdk_client.find_aggregate = compute_fakes.get_aggregates( self.fake_ags @@ -267,7 +267,7 @@ class TestAggregateList(TestAggregate): ) def setUp(self): - super(TestAggregateList, self).setUp() + super().setUp() self.compute_sdk_client.aggregates.return_value = [self.fake_ag] self.cmd = aggregate.ListAggregate(self.app, None) @@ -295,7 +295,7 @@ def test_aggregate_list_with_long(self): class TestAggregateRemoveHost(TestAggregate): def setUp(self): - super(TestAggregateRemoveHost, self).setUp() + super().setUp() self.compute_sdk_client.find_aggregate.return_value = self.fake_ag self.compute_sdk_client.remove_host_from_aggregate.return_value = ( @@ -326,7 +326,7 @@ def test_aggregate_remove_host(self): class TestAggregateSet(TestAggregate): def setUp(self): - super(TestAggregateSet, self).setUp() + super().setUp() self.compute_sdk_client.find_aggregate.return_value = self.fake_ag self.cmd = aggregate.SetAggregate(self.app, None) @@ -513,7 +513,7 @@ class TestAggregateShow(TestAggregate): ) def setUp(self): - super(TestAggregateShow, self).setUp() + super().setUp() self.compute_sdk_client.find_aggregate.return_value = self.fake_ag self.cmd = aggregate.ShowAggregate(self.app, None) @@ -537,7 +537,7 @@ def test_aggregate_show(self): class TestAggregateUnset(TestAggregate): def setUp(self): - super(TestAggregateUnset, self).setUp() + super().setUp() self.compute_sdk_client.find_aggregate.return_value = self.fake_ag self.cmd = aggregate.UnsetAggregate(self.app, None) @@ -598,7 +598,7 @@ class TestAggregateCacheImage(TestAggregate): images = image_fakes.create_images(count=2) def setUp(self): - super(TestAggregateCacheImage, self).setUp() + super().setUp() self.compute_sdk_client.find_aggregate.return_value = self.fake_ag self.find_image_mock = mock.Mock(side_effect=self.images) diff --git a/openstackclient/tests/unit/compute/v2/test_console.py b/openstackclient/tests/unit/compute/v2/test_console.py index c04f55cb0..ed64e0538 100644 --- a/openstackclient/tests/unit/compute/v2/test_console.py +++ b/openstackclient/tests/unit/compute/v2/test_console.py @@ -24,7 +24,7 @@ class TestConsoleLog(compute_fakes.TestComputev2): _server = compute_fakes.create_one_server() def setUp(self): - super(TestConsoleLog, self).setUp() + super().setUp() self.compute_sdk_client.find_server.return_value = self._server @@ -80,7 +80,7 @@ class TestConsoleUrlShow(compute_fakes.TestComputev2): _server = compute_fakes.create_one_server() def setUp(self): - super(TestConsoleUrlShow, self).setUp() + super().setUp() self.compute_sdk_client.find_server.return_value = self._server fake_console_data = { 'url': 'http://localhost', diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py index 5312d7051..a6e302c1f 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -28,7 +28,7 @@ class TestFlavor(compute_fakes.TestComputev2): def setUp(self): - super(TestFlavor, self).setUp() + super().setUp() self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() @@ -83,7 +83,7 @@ class TestFlavorCreate(TestFlavor): ) def setUp(self): - super(TestFlavorCreate, self).setUp() + super().setUp() # Return a project self.projects_mock.get.return_value = self.project @@ -403,7 +403,7 @@ class TestFlavorDelete(TestFlavor): flavors = compute_fakes.create_flavors(count=2) def setUp(self): - super(TestFlavorDelete, self).setUp() + super().setUp() self.compute_sdk_client.delete_flavor.return_value = None @@ -513,7 +513,7 @@ class TestFlavorList(TestFlavor): ) def setUp(self): - super(TestFlavorList, self).setUp() + super().setUp() self.api_mock = mock.Mock() self.api_mock.side_effect = [ @@ -756,7 +756,7 @@ class TestFlavorSet(TestFlavor): project = identity_fakes.FakeProject.create_one_project() def setUp(self): - super(TestFlavorSet, self).setUp() + super().setUp() self.compute_sdk_client.find_flavor.return_value = self.flavor # Return a project @@ -1006,7 +1006,7 @@ class TestFlavorShow(TestFlavor): ) def setUp(self): - super(TestFlavorShow, self).setUp() + super().setUp() # Return value of _find_resource() self.compute_sdk_client.find_flavor.return_value = self.flavor @@ -1093,7 +1093,7 @@ class TestFlavorUnset(TestFlavor): project = identity_fakes.FakeProject.create_one_project() def setUp(self): - super(TestFlavorUnset, self).setUp() + super().setUp() self.compute_sdk_client.find_flavor.return_value = self.flavor # Return a project diff --git a/openstackclient/tests/unit/compute/v2/test_host.py b/openstackclient/tests/unit/compute/v2/test_host.py index 8a899ea2b..0ad83289c 100644 --- a/openstackclient/tests/unit/compute/v2/test_host.py +++ b/openstackclient/tests/unit/compute/v2/test_host.py @@ -26,7 +26,7 @@ class TestHostList(compute_fakes.TestComputev2): _host = compute_fakes.create_one_host() def setUp(self): - super(TestHostList, self).setUp() + super().setUp() self.compute_sdk_client.get.return_value = fakes.FakeResponse( data={'hosts': [self._host]} @@ -83,7 +83,7 @@ def test_host_list_with_option(self, h_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.host_set') class TestHostSet(compute_fakes.TestComputev2): def setUp(self): - super(TestHostSet, self).setUp() + super().setUp() self.host = compute_fakes.create_one_host() @@ -135,7 +135,7 @@ class TestHostShow(compute_fakes.TestComputev2): _host = compute_fakes.create_one_host() def setUp(self): - super(TestHostShow, self).setUp() + super().setUp() output_data = { "resource": { diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py index bb755310a..5a4e3c52f 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor_stats.py @@ -21,7 +21,7 @@ class TestHypervisorStats(compute_fakes.TestComputev2): def setUp(self): - super(TestHypervisorStats, self).setUp() + super().setUp() self.compute_sdk_client.get = mock.Mock() @@ -65,7 +65,7 @@ class TestHypervisorStatsShow(TestHypervisorStats): _stats = create_one_hypervisor_stats() def setUp(self): - super(TestHypervisorStatsShow, self).setUp() + super().setUp() self.compute_sdk_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 d944c216f..b7d46aebe 100644 --- a/openstackclient/tests/unit/compute/v2/test_keypair.py +++ b/openstackclient/tests/unit/compute/v2/test_keypair.py @@ -29,7 +29,7 @@ class TestKeypair(compute_fakes.TestComputev2): def setUp(self): - super(TestKeypair, self).setUp() + super().setUp() # Initialize the user mock self.users_mock = self.identity_client.users @@ -118,7 +118,9 @@ def test_keypair_create_public_key(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch('io.open') as mock_open: + with mock.patch( + 'openstackclient.compute.v2.keypair.open' + ) as mock_open: mock_open.return_value = mock.MagicMock() m_file = mock_open.return_value.__enter__.return_value m_file.read.return_value = 'dummy' @@ -152,7 +154,9 @@ def test_keypair_create_private_key(self, mock_generate): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch('io.open') as mock_open: + with mock.patch( + 'openstackclient.compute.v2.keypair.open' + ) as mock_open: mock_open.return_value = mock.MagicMock() m_file = mock_open.return_value.__enter__.return_value @@ -199,7 +203,9 @@ def test_keypair_create_with_key_type(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch('io.open') as mock_open: + with mock.patch( + 'openstackclient.compute.v2.keypair.open' + ) as mock_open: mock_open.return_value = mock.MagicMock() m_file = mock_open.return_value.__enter__.return_value m_file.read.return_value = 'dummy' @@ -231,7 +237,9 @@ def test_keypair_create_with_key_type_pre_v22(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch('io.open') as mock_open: + with mock.patch( + 'openstackclient.compute.v2.keypair.open' + ) as mock_open: mock_open.return_value = mock.MagicMock() m_file = mock_open.return_value.__enter__.return_value m_file.read.return_value = 'dummy' diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 9be771cde..89b0bddea 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -63,7 +63,7 @@ def test_human_readable(self): class TestServer(compute_fakes.TestComputev2): def setUp(self): - super(TestServer, self).setUp() + super().setUp() # Get a shortcut to the compute client ServerManager Mock self.servers_mock = self.compute_client.servers @@ -452,7 +452,7 @@ def test_server_add_fixed_ip_with_fixed_ip_with_tag(self, sm_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_add') class TestServerAddFloatingIPCompute(compute_fakes.TestComputev2): def setUp(self): - super(TestServerAddFloatingIPCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -721,7 +721,7 @@ def test_server_add_floating_ip_with_fixed_ip_no_port_found(self): class TestServerAddPort(TestServer): def setUp(self): - super(TestServerAddPort, self).setUp() + super().setUp() # Get the command object to test self.cmd = server.AddPort(self.app, None) @@ -814,7 +814,7 @@ def test_server_add_port_with_tag_pre_v249(self, sm_mock): class TestServerVolume(TestServer): def setUp(self): - super(TestServerVolume, self).setUp() + super().setUp() self.methods = { 'create_volume_attachment': None, @@ -838,7 +838,7 @@ def setUp(self): class TestServerAddVolume(TestServerVolume): def setUp(self): - super(TestServerAddVolume, self).setUp() + super().setUp() # Get the command object to test self.cmd = server.AddServerVolume(self.app, None) @@ -1142,7 +1142,7 @@ def test_server_add_volume_with_disable_and_enable_delete_on_termination( class TestServerRemoveVolume(TestServerVolume): def setUp(self): - super(TestServerRemoveVolume, self).setUp() + super().setUp() # Get the command object to test self.cmd = server.RemoveServerVolume(self.app, None) @@ -1172,7 +1172,7 @@ def test_server_remove_volume(self): class TestServerAddNetwork(TestServer): def setUp(self): - super(TestServerAddNetwork, self).setUp() + super().setUp() # Get the command object to test self.cmd = server.AddNetwork(self.app, None) @@ -1268,7 +1268,7 @@ def test_server_add_network_with_tag_pre_v249(self, sm_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.security_group_find') class TestServerAddSecurityGroup(TestServer): def setUp(self): - super(TestServerAddSecurityGroup, self).setUp() + super().setUp() self.security_group = compute_fakes.create_one_security_group() @@ -1331,7 +1331,7 @@ def datalist(self): return datalist def setUp(self): - super(TestServerCreate, self).setUp() + super().setUp() attrs = { 'networks': {}, @@ -2360,7 +2360,7 @@ def test_server_create_with_wait_fails(self, mock_wait_for_status): self.new_server.name, self.image, self.flavor, **kwargs ) - @mock.patch('openstackclient.compute.v2.server.io.open') + @mock.patch('openstackclient.compute.v2.server.open') def test_server_create_userdata(self, mock_open): mock_file = mock.Mock(name='File') mock_open.return_value = mock_file @@ -4454,7 +4454,7 @@ def test_server_create_with_trusted_image_cert_boot_from_volume(self): class TestServerDelete(TestServer): def setUp(self): - super(TestServerDelete, self).setUp() + super().setUp() self.servers_mock.delete.return_value = None self.servers_mock.force_delete.return_value = None @@ -4650,7 +4650,7 @@ class _TestServerList(TestServer): ) def setUp(self): - super(_TestServerList, self).setUp() + super().setUp() # Default params of the core function of the command in the case of no # commandline option specified. @@ -4677,7 +4677,7 @@ def setUp(self): 'status': 'ACTIVE', 'OS-EXT-STS:task_state': 'None', 'OS-EXT-STS:power_state': 0x01, # Running - 'networks': {u'public': [u'10.20.30.40', u'2001:db8::5']}, + 'networks': {'public': ['10.20.30.40', '2001:db8::5']}, 'OS-EXT-AZ:availability_zone': 'availability-zone-xxx', 'OS-EXT-SRV-ATTR:host': 'host-name-xxx', 'Metadata': format_columns.DictColumn({}), @@ -4702,7 +4702,7 @@ def setUp(self): class TestServerList(_TestServerList): def setUp(self): - super(TestServerList, self).setUp() + super().setUp() Image = collections.namedtuple('Image', 'id name') self.image_client.images.return_value = [ @@ -5367,7 +5367,7 @@ class TestServerListV273(_TestServerList): ) def setUp(self): - super(TestServerListV273, self).setUp() + super().setUp() # The fake servers' attributes. Use the original attributes names in # nova, not the ones printed by "server list" command. @@ -5658,7 +5658,7 @@ def test_server_lock_with_reason_pre_v273(self, sm_mock): class TestServerMigrate(TestServer): def setUp(self): - super(TestServerMigrate, self).setUp() + super().setUp() methods = { 'migrate': None, @@ -6138,7 +6138,7 @@ def test_server_pause_multi_servers(self): class TestServerRebuild(TestServer): def setUp(self): - super(TestServerRebuild, self).setUp() + super().setUp() # Return value for utils.find_resource for image self.image = image_fakes.create_one_image() @@ -6566,7 +6566,7 @@ def test_rebuild_with_keypair_name_and_unset(self): verifylist, ) - @mock.patch('openstackclient.compute.v2.server.io.open') + @mock.patch('openstackclient.compute.v2.server.open') def test_rebuild_with_user_data(self, mock_open): self.compute_client.api_version = api_versions.APIVersion('2.57') @@ -6878,7 +6878,7 @@ def test_rebuild_with_reimage_boot_volume_pre_v293(self): class TestEvacuateServer(TestServer): def setUp(self): - super(TestEvacuateServer, self).setUp() + super().setUp() # Return value for utils.find_resource for image self.image = image_fakes.create_one_image() @@ -7035,7 +7035,7 @@ def test_evacuate_with_wait_ok(self, mock_wait_for_status): class TestServerRemoveFixedIP(TestServer): def setUp(self): - super(TestServerRemoveFixedIP, self).setUp() + super().setUp() # Get the command object to test self.cmd = server.RemoveFixedIP(self.app, None) @@ -7066,7 +7066,7 @@ def test_server_remove_fixed_ip(self): class TestServerRescue(TestServer): def setUp(self): - super(TestServerRescue, self).setUp() + super().setUp() # Return value for utils.find_resource for image self.image = image_fakes.create_one_image() @@ -7155,7 +7155,7 @@ def test_rescue_with_current_image_and_password(self): @mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_remove') class TestServerRemoveFloatingIPCompute(compute_fakes.TestComputev2): def setUp(self): - super(TestServerRemoveFloatingIPCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -7223,7 +7223,7 @@ def test_server_remove_floating_ip_default(self): class TestServerRemovePort(TestServer): def setUp(self): - super(TestServerRemovePort, self).setUp() + super().setUp() # Get the command object to test self.cmd = server.RemovePort(self.app, None) @@ -7271,7 +7271,7 @@ def test_server_remove_port_no_neutron(self): class TestServerRemoveNetwork(TestServer): def setUp(self): - super(TestServerRemoveNetwork, self).setUp() + super().setUp() # Get the command object to test self.cmd = server.RemoveNetwork(self.app, None) @@ -7330,7 +7330,7 @@ def test_server_remove_network_no_neutron(self): @mock.patch('openstackclient.api.compute_v2.APIv2.security_group_find') class TestServerRemoveSecurityGroup(TestServer): def setUp(self): - super(TestServerRemoveSecurityGroup, self).setUp() + super().setUp() self.security_group = compute_fakes.create_one_security_group() @@ -7369,7 +7369,7 @@ def test_server_remove_security_group(self, sg_find_mock): class TestServerResize(TestServer): def setUp(self): - super(TestServerResize, self).setUp() + super().setUp() self.server = compute_fakes.create_one_server() @@ -7568,7 +7568,7 @@ def test_server_resize_with_wait_fails(self, mock_wait_for_status): class TestServerResizeConfirm(TestServer): def setUp(self): - super(TestServerResizeConfirm, self).setUp() + super().setUp() methods = { 'confirm_resize': None, @@ -7672,7 +7672,7 @@ def test_migration_confirm(self): class TestServerResizeRevert(TestServer): def setUp(self): - super(TestServerResizeRevert, self).setUp() + super().setUp() methods = { 'revert_resize': None, @@ -7804,7 +7804,7 @@ def test_server_resume_multi_servers(self): class TestServerSet(TestServer): def setUp(self): - super(TestServerSet, self).setUp() + super().setUp() self.attrs = { 'api_version': None, @@ -8205,7 +8205,7 @@ def test_shelve_offload(self, mock_wait_for_status): class TestServerShow(TestServer): def setUp(self): - super(TestServerShow, self).setUp() + super().setUp() self.image = image_fakes.create_one_image() self.flavor = compute_fakes.create_one_flavor() @@ -8610,7 +8610,7 @@ def test_server_unpause_multi_servers(self): class TestServerUnset(TestServer): def setUp(self): - super(TestServerUnset, self).setUp() + super().setUp() self.fake_server = self.setup_servers_mock(1)[0] @@ -9062,11 +9062,11 @@ def test_prep_server_detail(self, find_resource): _image = image_fakes.create_one_image() _flavor = compute_fakes.create_one_flavor() server_info = { - 'image': {u'id': _image.id}, - 'flavor': {u'id': _flavor.id}, - 'tenant_id': u'tenant-id-xxx', - 'addresses': {u'public': [u'10.20.30.40', u'2001:db8::f']}, - 'links': u'http://xxx.yyy.com', + 'image': {'id': _image.id}, + 'flavor': {'id': _flavor.id}, + 'tenant_id': 'tenant-id-xxx', + 'addresses': {'public': ['10.20.30.40', '2001:db8::f']}, + 'links': 'http://xxx.yyy.com', 'properties': '', 'volumes_attached': [{"id": "6344fe9d-ef20-45b2-91a6"}], } @@ -9078,8 +9078,8 @@ def test_prep_server_detail(self, find_resource): info = { 'id': _server.id, 'name': _server.name, - 'image': '%s (%s)' % (_image.name, _image.id), - 'flavor': '%s (%s)' % (_flavor.name, _flavor.id), + 'image': f'{_image.name} ({_image.id})', + 'flavor': f'{_flavor.name} ({_flavor.id})', 'OS-EXT-STS:power_state': server.PowerStateColumn( getattr(_server, 'OS-EXT-STS:power_state') ), diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py index a72f96f16..9d5269ddb 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_backup.py +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -64,7 +64,7 @@ def image_data(self, image): return datalist def setUp(self): - super(TestServerBackupCreate, self).setUp() + super().setUp() # Get the command object to test self.cmd = server_backup.CreateServerBackup(self.app, None) diff --git a/openstackclient/tests/unit/compute/v2/test_server_event.py b/openstackclient/tests/unit/compute/v2/test_server_event.py index ce9345bb0..5f76e2adf 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_event.py +++ b/openstackclient/tests/unit/compute/v2/test_server_event.py @@ -27,7 +27,7 @@ class TestServerEvent(compute_fakes.TestComputev2): fake_server = compute_fakes.create_one_server() def setUp(self): - super(TestServerEvent, self).setUp() + super().setUp() patcher = mock.patch.object( sdk_utils, 'supports_microversion', return_value=True diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py index d658b1507..efb8c28b9 100644 --- a/openstackclient/tests/unit/compute/v2/test_service.py +++ b/openstackclient/tests/unit/compute/v2/test_service.py @@ -26,7 +26,7 @@ class TestServiceDelete(compute_fakes.TestComputev2): services = compute_fakes.create_services(count=2) def setUp(self): - super(TestServiceDelete, self).setUp() + super().setUp() self.compute_sdk_client.delete_service.return_value = None @@ -123,7 +123,7 @@ class TestServiceList(compute_fakes.TestComputev2): data_long = [data[0] + (service.disabled_reason,)] def setUp(self): - super(TestServiceList, self).setUp() + super().setUp() self.compute_sdk_client.services.return_value = [self.service] @@ -225,7 +225,7 @@ def test_service_list_with_long_option_2_11(self, sm_mock): class TestServiceSet(compute_fakes.TestComputev2): def setUp(self): - super(TestServiceSet, self).setUp() + super().setUp() self.service = compute_fakes.create_one_service() diff --git a/openstackclient/tests/unit/compute/v2/test_usage.py b/openstackclient/tests/unit/compute/v2/test_usage.py index e47c044ae..2efd25b54 100644 --- a/openstackclient/tests/unit/compute/v2/test_usage.py +++ b/openstackclient/tests/unit/compute/v2/test_usage.py @@ -21,7 +21,7 @@ class TestUsage(compute_fakes.TestComputev2): def setUp(self): - super(TestUsage, self).setUp() + super().setUp() self.projects_mock = self.identity_client.projects self.projects_mock.reset_mock() @@ -53,7 +53,7 @@ class TestUsageList(TestUsage): ] def setUp(self): - super(TestUsageList, self).setUp() + super().setUp() self.compute_sdk_client.usages.return_value = self.usages @@ -144,7 +144,7 @@ class TestUsageShow(TestUsage): ) def setUp(self): - super(TestUsageShow, self).setUp() + super().setUp() self.compute_sdk_client.get_usage.return_value = self.usage diff --git a/openstackclient/tests/unit/fakes.py b/openstackclient/tests/unit/fakes.py index e450e6296..cc42b1750 100644 --- a/openstackclient/tests/unit/fakes.py +++ b/openstackclient/tests/unit/fakes.py @@ -47,7 +47,7 @@ TEST_VERSIONS = fixture.DiscoveryList(href=AUTH_URL) -class FakeStdout(object): +class FakeStdout: def __init__(self): self.content = [] @@ -61,7 +61,7 @@ def make_string(self): return result -class FakeLog(object): +class FakeLog: def __init__(self): self.messages = {} @@ -81,7 +81,7 @@ def critical(self, msg): self.messages['critical'] = msg -class FakeApp(object): +class FakeApp: def __init__(self, _stdout, _log): self.stdout = _stdout self.client_manager = None @@ -92,18 +92,18 @@ def __init__(self, _stdout, _log): self.log = _log -class FakeOptions(object): +class FakeOptions: def __init__(self, **kwargs): self.os_beta_command = False -class FakeClient(object): +class FakeClient: def __init__(self, **kwargs): self.endpoint = kwargs['endpoint'] self.token = kwargs['token'] -class FakeClientManager(object): +class FakeClientManager: _api_version = { 'image': '2', } @@ -158,7 +158,7 @@ def is_volume_endpoint_enabled(self, client): return self.volume_endpoint_enabled -class FakeModule(object): +class FakeModule: def __init__(self, name, version): self.name = name self.__version__ = version @@ -167,7 +167,7 @@ def __init__(self, name, version): self.version.__version__ = version -class FakeResource(object): +class FakeResource: def __init__(self, manager=None, info=None, loaded=False, methods=None): """Set attributes and methods for a resource. @@ -210,8 +210,8 @@ def __repr__(self): reprkeys = sorted( k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager' ) - info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) - return "<%s %s>" % (self.__class__.__name__, info) + info = ", ".join(f"{k}={getattr(self, k)}" for k in reprkeys) + return f"<{self.__class__.__name__} {info}>" def keys(self): return self._info.keys() @@ -237,7 +237,7 @@ class FakeResponse(requests.Response): def __init__( self, headers=None, status_code=200, data=None, encoding=None ): - super(FakeResponse, self).__init__() + super().__init__() headers = headers or {} diff --git a/openstackclient/tests/unit/identity/v2_0/fakes.py b/openstackclient/tests/unit/identity/v2_0/fakes.py index b9364e7f0..e07b02626 100644 --- a/openstackclient/tests/unit/identity/v2_0/fakes.py +++ b/openstackclient/tests/unit/identity/v2_0/fakes.py @@ -155,7 +155,7 @@ def fake_auth_ref(fake_token, fake_service=None): return auth_ref -class FakeIdentityv2Client(object): +class FakeIdentityv2Client: def __init__(self, **kwargs): self.roles = mock.Mock() self.roles.resource_class = fakes.FakeResource(None, {}) @@ -210,7 +210,7 @@ class TestIdentityv2( ): ... -class FakeExtension(object): +class FakeExtension: """Fake one or more extension.""" @staticmethod @@ -249,7 +249,7 @@ def create_one_extension(attrs=None): return extension -class FakeCatalog(object): +class FakeCatalog: """Fake one or more catalog.""" @staticmethod @@ -299,7 +299,7 @@ def create_catalog(attrs=None): return catalog -class FakeProject(object): +class FakeProject: """Fake one or more project.""" @staticmethod @@ -346,7 +346,7 @@ def create_projects(attrs=None, count=2): return projects -class FakeEndpoint(object): +class FakeEndpoint: """Fake one or more endpoint.""" @staticmethod @@ -397,7 +397,7 @@ def create_endpoints(attrs=None, count=2): return endpoints -class FakeService(object): +class FakeService: """Fake one or more service.""" @staticmethod @@ -444,7 +444,7 @@ def create_services(attrs=None, count=2): return services -class FakeRole(object): +class FakeRole: """Fake one or more role.""" @staticmethod @@ -487,7 +487,7 @@ def create_roles(attrs=None, count=2): return roles -class FakeUser(object): +class FakeUser: """Fake one or more user.""" @staticmethod diff --git a/openstackclient/tests/unit/identity/v2_0/test_catalog.py b/openstackclient/tests/unit/identity/v2_0/test_catalog.py index c420d33c3..63b3d475d 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_catalog.py +++ b/openstackclient/tests/unit/identity/v2_0/test_catalog.py @@ -22,7 +22,7 @@ class TestCatalog(utils.TestCommand): service_catalog = identity_fakes.FakeCatalog.create_catalog() def setUp(self): - super(TestCatalog, self).setUp() + super().setUp() self.sc_mock = mock.Mock() self.sc_mock.service_catalog.catalog.return_value = [ @@ -43,7 +43,7 @@ class TestCatalogList(TestCatalog): ) def setUp(self): - super(TestCatalogList, self).setUp() + super().setUp() # Get the command object to test self.cmd = catalog.ListCatalog(self.app, None) @@ -126,7 +126,7 @@ def test_catalog_list_with_endpoint_url(self): class TestCatalogShow(TestCatalog): def setUp(self): - super(TestCatalogShow, self).setUp() + super().setUp() # Get the command object to test self.cmd = catalog.ShowCatalog(self.app, None) diff --git a/openstackclient/tests/unit/identity/v2_0/test_endpoint.py b/openstackclient/tests/unit/identity/v2_0/test_endpoint.py index 84ed6c9a4..0bc82bc7e 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_endpoint.py +++ b/openstackclient/tests/unit/identity/v2_0/test_endpoint.py @@ -24,7 +24,7 @@ class TestEndpoint(identity_fakes.TestIdentityv2): fake_endpoint = identity_fakes.FakeEndpoint.create_one_endpoint(attr) def setUp(self): - super(TestEndpoint, self).setUp() + super().setUp() # Get a shortcut to the EndpointManager Mock self.endpoints_mock = self.identity_client.endpoints @@ -37,7 +37,7 @@ def setUp(self): class TestEndpointCreate(TestEndpoint): def setUp(self): - super(TestEndpointCreate, self).setUp() + super().setUp() self.endpoints_mock.create.return_value = self.fake_endpoint @@ -109,7 +109,7 @@ def test_endpoint_create(self): class TestEndpointDelete(TestEndpoint): def setUp(self): - super(TestEndpointDelete, self).setUp() + super().setUp() self.endpoints_mock.get.return_value = self.fake_endpoint self.endpoints_mock.delete.return_value = None @@ -136,7 +136,7 @@ def test_endpoint_delete_no_options(self): class TestEndpointList(TestEndpoint): def setUp(self): - super(TestEndpointList, self).setUp() + super().setUp() self.endpoints_mock.list.return_value = [self.fake_endpoint] @@ -211,7 +211,7 @@ def test_endpoint_list_long(self): class TestEndpointShow(TestEndpoint): def setUp(self): - super(TestEndpointShow, self).setUp() + super().setUp() self.endpoints_mock.list.return_value = [self.fake_endpoint] diff --git a/openstackclient/tests/unit/identity/v2_0/test_project.py b/openstackclient/tests/unit/identity/v2_0/test_project.py index cfd826337..9b203b22b 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_project.py +++ b/openstackclient/tests/unit/identity/v2_0/test_project.py @@ -56,7 +56,7 @@ class TestProject(identity_fakes.TestIdentityv2): ) def setUp(self): - super(TestProject, self).setUp() + super().setUp() # Get a shortcut to the TenantManager Mock self.projects_mock = self.identity_client.tenants @@ -65,7 +65,7 @@ def setUp(self): class TestProjectCreate(TestProject): def setUp(self): - super(TestProjectCreate, self).setUp() + super().setUp() self.projects_mock.create.return_value = self.fake_project @@ -289,7 +289,7 @@ def test_project_create_or_show_not_exists(self): class TestProjectDelete(TestProject): def setUp(self): - super(TestProjectDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.projects_mock.get.return_value = self.fake_project @@ -341,7 +341,7 @@ def test_delete_multi_projects_with_exception(self, find_mock): class TestProjectList(TestProject): def setUp(self): - super(TestProjectList, self).setUp() + super().setUp() self.projects_mock.list.return_value = [self.fake_project] @@ -431,7 +431,7 @@ def test_project_list_sort(self): class TestProjectSet(TestProject): def setUp(self): - super(TestProjectSet, self).setUp() + super().setUp() self.projects_mock.get.return_value = self.fake_project self.projects_mock.update.return_value = self.fake_project @@ -613,7 +613,7 @@ class TestProjectShow(TestProject): fake_proj_show = identity_fakes.FakeProject.create_one_project() def setUp(self): - super(TestProjectShow, self).setUp() + super().setUp() self.projects_mock.get.return_value = self.fake_proj_show @@ -654,7 +654,7 @@ class TestProjectUnset(TestProject): fake_proj = identity_fakes.FakeProject.create_one_project(attr) def setUp(self): - super(TestProjectUnset, self).setUp() + super().setUp() self.projects_mock.get.return_value = self.fake_proj diff --git a/openstackclient/tests/unit/identity/v2_0/test_role.py b/openstackclient/tests/unit/identity/v2_0/test_role.py index 6556c6d7d..830653b0f 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_role.py +++ b/openstackclient/tests/unit/identity/v2_0/test_role.py @@ -40,7 +40,7 @@ class TestRole(identity_fakes.TestIdentityv2): fake_user = identity_fakes.FakeUser.create_one_user(attr) def setUp(self): - super(TestRole, self).setUp() + super().setUp() # Get a shortcut to the TenantManager Mock self.projects_mock = self.identity_client.tenants @@ -64,7 +64,7 @@ def setUp(self): class TestRoleAdd(TestRole): def setUp(self): - super(TestRoleAdd, self).setUp() + super().setUp() self.projects_mock.get.return_value = self.fake_project @@ -121,7 +121,7 @@ class TestRoleCreate(TestRole): ) def setUp(self): - super(TestRoleCreate, self).setUp() + super().setUp() self.roles_mock.create.return_value = self.fake_role_c @@ -212,7 +212,7 @@ def test_role_create_or_show_not_exists(self): class TestRoleDelete(TestRole): def setUp(self): - super(TestRoleDelete, self).setUp() + super().setUp() self.roles_mock.get.return_value = self.fake_role self.roles_mock.delete.return_value = None @@ -263,7 +263,7 @@ def test_delete_multi_roles_with_exception(self, find_mock): class TestRoleList(TestRole): def setUp(self): - super(TestRoleList, self).setUp() + super().setUp() self.roles_mock.list.return_value = [self.fake_role] @@ -295,7 +295,7 @@ def test_role_list(self): class TestRoleRemove(TestRole): def setUp(self): - super(TestRoleRemove, self).setUp() + super().setUp() self.projects_mock.get.return_value = self.fake_project @@ -335,7 +335,7 @@ def test_role_remove(self): class TestRoleShow(TestRole): def setUp(self): - super(TestRoleShow, self).setUp() + super().setUp() self.roles_mock.get.return_value = self.fake_role 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 9cc7d5e75..a06c270f0 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py +++ b/openstackclient/tests/unit/identity/v2_0/test_role_assignment.py @@ -23,7 +23,7 @@ class TestRoleAssignment(identity_fakes.TestIdentityv2): def setUp(self): - super(TestRoleAssignment, self).setUp() + super().setUp() class TestRoleAssignmentList(TestRoleAssignment): diff --git a/openstackclient/tests/unit/identity/v2_0/test_service.py b/openstackclient/tests/unit/identity/v2_0/test_service.py index b5ec353f9..22e7b837a 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_service.py +++ b/openstackclient/tests/unit/identity/v2_0/test_service.py @@ -24,7 +24,7 @@ class TestService(identity_fakes.TestIdentityv2): fake_service = identity_fakes.FakeService.create_one_service() def setUp(self): - super(TestService, self).setUp() + super().setUp() # Get a shortcut to the ServiceManager Mock self.services_mock = self.identity_client.services @@ -47,7 +47,7 @@ class TestServiceCreate(TestService): ) def setUp(self): - super(TestServiceCreate, self).setUp() + super().setUp() self.services_mock.create.return_value = self.fake_service_c @@ -141,7 +141,7 @@ def test_service_create_description(self): class TestServiceDelete(TestService): def setUp(self): - super(TestServiceDelete, self).setUp() + super().setUp() self.services_mock.get.side_effect = identity_exc.NotFound(None) self.services_mock.find.return_value = self.fake_service @@ -169,7 +169,7 @@ def test_service_delete_no_options(self): class TestServiceList(TestService): def setUp(self): - super(TestServiceList, self).setUp() + super().setUp() self.services_mock.list.return_value = [self.fake_service] @@ -232,7 +232,7 @@ class TestServiceShow(TestService): fake_service_s = identity_fakes.FakeService.create_one_service() def setUp(self): - super(TestServiceShow, self).setUp() + super().setUp() self.services_mock.get.side_effect = identity_exc.NotFound(None) self.services_mock.find.return_value = self.fake_service_s diff --git a/openstackclient/tests/unit/identity/v2_0/test_token.py b/openstackclient/tests/unit/identity/v2_0/test_token.py index 56e59f4ad..1e90104d9 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_token.py +++ b/openstackclient/tests/unit/identity/v2_0/test_token.py @@ -24,7 +24,7 @@ class TestToken(identity_fakes.TestIdentityv2): fake_project = identity_fakes.FakeProject.create_one_project() def setUp(self): - super(TestToken, self).setUp() + super().setUp() # Get a shortcut to the Auth Ref Mock self.ar_mock = mock.PropertyMock() @@ -33,7 +33,7 @@ def setUp(self): class TestTokenIssue(TestToken): def setUp(self): - super(TestTokenIssue, self).setUp() + super().setUp() self.cmd = token.IssueToken(self.app, None) @@ -95,7 +95,7 @@ class TestTokenRevoke(TestToken): TOKEN = 'fob' def setUp(self): - super(TestTokenRevoke, self).setUp() + super().setUp() self.tokens_mock = self.identity_client.tokens self.tokens_mock.reset_mock() self.tokens_mock.delete.return_value = True diff --git a/openstackclient/tests/unit/identity/v2_0/test_user.py b/openstackclient/tests/unit/identity/v2_0/test_user.py index 3355e4cb5..725a82180 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_user.py +++ b/openstackclient/tests/unit/identity/v2_0/test_user.py @@ -31,7 +31,7 @@ class TestUser(identity_fakes.TestIdentityv2): fake_user = identity_fakes.FakeUser.create_one_user(attr) def setUp(self): - super(TestUser, self).setUp() + super().setUp() # Get a shortcut to the TenantManager Mock self.projects_mock = self.identity_client.tenants @@ -65,7 +65,7 @@ class TestUserCreate(TestUser): ) def setUp(self): - super(TestUserCreate, self).setUp() + super().setUp() self.projects_mock.get.return_value = self.fake_project_c @@ -363,7 +363,7 @@ def test_user_create_or_show_not_exists(self): class TestUserDelete(TestUser): def setUp(self): - super(TestUserDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.users_mock.get.return_value = self.fake_user @@ -432,7 +432,7 @@ class TestUserList(TestUser): ) def setUp(self): - super(TestUserList, self).setUp() + super().setUp() self.projects_mock.get.return_value = self.fake_project_l self.projects_mock.list.return_value = [self.fake_project_l] @@ -513,7 +513,7 @@ def test_user_list_long(self): class TestUserSet(TestUser): def setUp(self): - super(TestUserSet, self).setUp() + super().setUp() self.projects_mock.get.return_value = self.fake_project self.users_mock.get.return_value = self.fake_user @@ -755,7 +755,7 @@ def test_user_set_disable(self): class TestUserShow(TestUser): def setUp(self): - super(TestUserShow, self).setUp() + super().setUp() self.users_mock.get.return_value = self.fake_user diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py index b56762497..ad5ceb284 100644 --- a/openstackclient/tests/unit/identity/v3/fakes.py +++ b/openstackclient/tests/unit/identity/v3/fakes.py @@ -565,7 +565,7 @@ def fake_auth_ref(fake_token, fake_service=None): return auth_ref -class FakeAuth(object): +class FakeAuth: def __init__(self, auth_method_class=None): self._auth_method_class = auth_method_class @@ -573,12 +573,12 @@ def get_token(self, *args, **kwargs): return token_id -class FakeSession(object): +class FakeSession: def __init__(self, **kwargs): self.auth = FakeAuth() -class FakeIdentityv3Client(object): +class FakeIdentityv3Client: def __init__(self, **kwargs): self.domains = mock.Mock() self.domains.resource_class = fakes.FakeResource(None, {}) @@ -633,7 +633,7 @@ def __init__(self, **kwargs): self.limits.resource_class = fakes.FakeResource(None, {}) -class FakeFederationManager(object): +class FakeFederationManager: def __init__(self, **kwargs): self.identity_providers = mock.Mock() self.identity_providers.resource_class = fakes.FakeResource(None, {}) @@ -651,13 +651,13 @@ def __init__(self, **kwargs): class FakeFederatedClient(FakeIdentityv3Client): def __init__(self, **kwargs): - super(FakeFederatedClient, self).__init__(**kwargs) + super().__init__(**kwargs) self.federation = FakeFederationManager() class FakeOAuth1Client(FakeIdentityv3Client): def __init__(self, **kwargs): - super(FakeOAuth1Client, self).__init__(**kwargs) + super().__init__(**kwargs) self.access_tokens = mock.Mock() self.access_tokens.resource_class = fakes.FakeResource(None, {}) @@ -696,7 +696,7 @@ class TestIdentityv3( # We don't use FakeClientMixin since we want a different fake legacy client class TestFederatedIdentity(utils.TestCommand): def setUp(self): - super(TestFederatedIdentity, self).setUp() + super().setUp() self.app.client_manager.identity = FakeFederatedClient( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN @@ -716,7 +716,7 @@ def setUp(self): # We don't use FakeClientMixin since we want a different fake legacy client class TestOAuth1(utils.TestCommand): def setUp(self): - super(TestOAuth1, self).setUp() + super().setUp() self.app.client_manager.identity = FakeOAuth1Client( endpoint=fakes.AUTH_URL, token=fakes.AUTH_TOKEN @@ -733,7 +733,7 @@ def setUp(self): ) -class FakeProject(object): +class FakeProject: """Fake one or more project.""" @staticmethod @@ -785,7 +785,7 @@ def create_projects(attrs=None, count=2): return projects -class FakeDomain(object): +class FakeDomain: """Fake one or more domain.""" @staticmethod @@ -817,7 +817,7 @@ def create_one_domain(attrs=None): return domain -class FakeCredential(object): +class FakeCredential: """Fake one or more credential.""" @staticmethod @@ -887,7 +887,7 @@ def get_credentials(credentials=None, count=2): return mock.Mock(side_effect=credentials) -class FakeUser(object): +class FakeUser: """Fake one or more user.""" @staticmethod @@ -956,7 +956,7 @@ def get_users(users=None, count=2): return mock.Mock(side_effect=users) -class FakeGroup(object): +class FakeGroup: """Fake one or more group.""" @staticmethod @@ -1023,7 +1023,7 @@ def get_groups(groups=None, count=2): return mock.Mock(side_effect=groups) -class FakeEndpoint(object): +class FakeEndpoint: """Fake one or more endpoint.""" @staticmethod @@ -1080,7 +1080,7 @@ def create_one_endpoint_filter(attrs=None): return endpoint_filter -class FakeEndpointGroup(object): +class FakeEndpointGroup: """Fake one or more endpoint group.""" @staticmethod @@ -1140,7 +1140,7 @@ def create_one_endpointgroup_filter(attrs=None): return endpointgroup_filter -class FakeService(object): +class FakeService: """Fake one or more service.""" @staticmethod @@ -1172,7 +1172,7 @@ def create_one_service(attrs=None): return service -class FakeRoleAssignment(object): +class FakeRoleAssignment: """Fake one or more role assignment.""" @staticmethod @@ -1202,7 +1202,7 @@ def create_one_role_assignment(attrs=None): return role_assignment -class FakeImpliedRoleResponse(object): +class FakeImpliedRoleResponse: """Fake one or more role assignment.""" def __init__(self, prior_role, implied_roles): diff --git a/openstackclient/tests/unit/identity/v3/test_access_rule.py b/openstackclient/tests/unit/identity/v3/test_access_rule.py index b6ef20be7..5367c1987 100644 --- a/openstackclient/tests/unit/identity/v3/test_access_rule.py +++ b/openstackclient/tests/unit/identity/v3/test_access_rule.py @@ -25,7 +25,7 @@ class TestAccessRule(identity_fakes.TestIdentityv3): def setUp(self): - super(TestAccessRule, self).setUp() + super().setUp() identity_manager = self.identity_client self.access_rules_mock = identity_manager.access_rules @@ -36,7 +36,7 @@ def setUp(self): class TestAccessRuleDelete(TestAccessRule): def setUp(self): - super(TestAccessRuleDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.access_rules_mock.get.return_value = fakes.FakeResource( @@ -98,7 +98,7 @@ def test_delete_multi_access_rules_with_exception(self): class TestAccessRuleList(TestAccessRule): def setUp(self): - super(TestAccessRuleList, self).setUp() + super().setUp() self.access_rules_mock.list.return_value = [ fakes.FakeResource( @@ -135,7 +135,7 @@ def test_access_rule_list(self): class TestAccessRuleShow(TestAccessRule): def setUp(self): - super(TestAccessRuleShow, self).setUp() + super().setUp() self.access_rules_mock.get.return_value = fakes.FakeResource( None, diff --git a/openstackclient/tests/unit/identity/v3/test_application_credential.py b/openstackclient/tests/unit/identity/v3/test_application_credential.py index 129b62cad..d9c3531dd 100644 --- a/openstackclient/tests/unit/identity/v3/test_application_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py @@ -27,7 +27,7 @@ class TestApplicationCredential(identity_fakes.TestIdentityv3): def setUp(self): - super(TestApplicationCredential, self).setUp() + super().setUp() identity_manager = self.identity_client self.app_creds_mock = identity_manager.application_credentials @@ -38,7 +38,7 @@ def setUp(self): class TestApplicationCredentialCreate(TestApplicationCredential): def setUp(self): - super(TestApplicationCredentialCreate, self).setUp() + super().setUp() self.roles_mock.get.return_value = fakes.FakeResource( None, @@ -296,7 +296,7 @@ def test_application_credential_create_with_access_rules_file( class TestApplicationCredentialDelete(TestApplicationCredential): def setUp(self): - super(TestApplicationCredentialDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.app_creds_mock.get.return_value = fakes.FakeResource( @@ -361,7 +361,7 @@ def test_delete_multi_app_creds_with_exception(self, find_mock): class TestApplicationCredentialList(TestApplicationCredential): def setUp(self): - super(TestApplicationCredentialList, self).setUp() + super().setUp() self.app_creds_mock.list.return_value = [ fakes.FakeResource( @@ -404,7 +404,7 @@ def test_application_credential_list(self): class TestApplicationCredentialShow(TestApplicationCredential): def setUp(self): - super(TestApplicationCredentialShow, self).setUp() + super().setUp() self.app_creds_mock.get.return_value = fakes.FakeResource( None, diff --git a/openstackclient/tests/unit/identity/v3/test_catalog.py b/openstackclient/tests/unit/identity/v3/test_catalog.py index a157859e7..ed2db3b4d 100644 --- a/openstackclient/tests/unit/identity/v3/test_catalog.py +++ b/openstackclient/tests/unit/identity/v3/test_catalog.py @@ -47,7 +47,7 @@ class TestCatalog(utils.TestCommand): } def setUp(self): - super(TestCatalog, self).setUp() + super().setUp() self.sc_mock = mock.Mock() self.sc_mock.service_catalog.catalog.return_value = [ @@ -62,7 +62,7 @@ def setUp(self): class TestCatalogList(TestCatalog): def setUp(self): - super(TestCatalogList, self).setUp() + super().setUp() # Get the command object to test self.cmd = catalog.ListCatalog(self.app, None) @@ -100,7 +100,7 @@ def test_catalog_list(self): class TestCatalogShow(TestCatalog): def setUp(self): - super(TestCatalogShow, self).setUp() + super().setUp() # Get the command object to test self.cmd = catalog.ShowCatalog(self.app, None) diff --git a/openstackclient/tests/unit/identity/v3/test_consumer.py b/openstackclient/tests/unit/identity/v3/test_consumer.py index 8067965ae..94b81dd94 100644 --- a/openstackclient/tests/unit/identity/v3/test_consumer.py +++ b/openstackclient/tests/unit/identity/v3/test_consumer.py @@ -19,14 +19,14 @@ class TestOAuth1(identity_fakes.TestOAuth1): def setUp(self): - super(TestOAuth1, self).setUp() + super().setUp() self.consumers_mock = self.identity_client.oauth1.consumers self.consumers_mock.reset_mock() class TestConsumerCreate(TestOAuth1): def setUp(self): - super(TestConsumerCreate, self).setUp() + super().setUp() self.consumers_mock.create.return_value = fakes.FakeResource( None, @@ -63,7 +63,7 @@ def test_create_consumer(self): class TestConsumerDelete(TestOAuth1): def setUp(self): - super(TestConsumerDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.consumers_mock.get.return_value = fakes.FakeResource( @@ -94,7 +94,7 @@ def test_delete_consumer(self): class TestConsumerList(TestOAuth1): def setUp(self): - super(TestConsumerList, self).setUp() + super().setUp() self.consumers_mock.get.return_value = fakes.FakeResource( None, @@ -136,7 +136,7 @@ def test_consumer_list(self): class TestConsumerSet(TestOAuth1): def setUp(self): - super(TestConsumerSet, self).setUp() + super().setUp() self.consumers_mock.get.return_value = fakes.FakeResource( None, @@ -179,7 +179,7 @@ def test_consumer_update(self): class TestConsumerShow(TestOAuth1): def setUp(self): - super(TestConsumerShow, self).setUp() + super().setUp() consumer_no_secret = copy.deepcopy(identity_fakes.OAUTH_CONSUMER) del consumer_no_secret['secret'] diff --git a/openstackclient/tests/unit/identity/v3/test_credential.py b/openstackclient/tests/unit/identity/v3/test_credential.py index 47af5053b..794f40b05 100644 --- a/openstackclient/tests/unit/identity/v3/test_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_credential.py @@ -22,7 +22,7 @@ class TestCredential(identity_fakes.TestIdentityv3): def setUp(self): - super(TestCredential, self).setUp() + super().setUp() # Get a shortcut to the CredentialManager Mock self.credentials_mock = self.identity_client.credentials @@ -49,7 +49,7 @@ class TestCredentialCreate(TestCredential): ) def setUp(self): - super(TestCredentialCreate, self).setUp() + super().setUp() self.credential = identity_fakes.FakeCredential.create_one_credential( attrs={'user_id': self.user.id, 'project_id': self.project.id} @@ -126,7 +126,7 @@ class TestCredentialDelete(TestCredential): credentials = identity_fakes.FakeCredential.create_credentials(count=2) def setUp(self): - super(TestCredentialDelete, self).setUp() + super().setUp() self.credentials_mock.delete.return_value = None @@ -206,7 +206,7 @@ class TestCredentialList(TestCredential): ) def setUp(self): - super(TestCredentialList, self).setUp() + super().setUp() self.user = identity_fakes.FakeUser.create_one_user() self.users_mock.get.return_value = self.user @@ -257,7 +257,7 @@ class TestCredentialSet(TestCredential): credential = identity_fakes.FakeCredential.create_one_credential() def setUp(self): - super(TestCredentialSet, self).setUp() + super().setUp() self.cmd = credential.SetCredential(self.app, None) def test_credential_set_no_options(self): @@ -353,7 +353,7 @@ class TestCredentialShow(TestCredential): ) def setUp(self): - super(TestCredentialShow, self).setUp() + super().setUp() self.credential = identity_fakes.FakeCredential.create_one_credential() self.credentials_mock.get.return_value = self.credential diff --git a/openstackclient/tests/unit/identity/v3/test_domain.py b/openstackclient/tests/unit/identity/v3/test_domain.py index 76e784827..fe3f655e4 100644 --- a/openstackclient/tests/unit/identity/v3/test_domain.py +++ b/openstackclient/tests/unit/identity/v3/test_domain.py @@ -16,7 +16,7 @@ class TestDomain(identity_fakes.TestIdentityv3): def setUp(self): - super(TestDomain, self).setUp() + super().setUp() # Get a shortcut to the DomainManager Mock self.domains_mock = self.identity_client.domains @@ -27,7 +27,7 @@ class TestDomainCreate(TestDomain): columns = ('description', 'enabled', 'id', 'name', 'tags') def setUp(self): - super(TestDomainCreate, self).setUp() + super().setUp() self.domain = identity_fakes.FakeDomain.create_one_domain() self.domains_mock.create.return_value = self.domain @@ -214,7 +214,7 @@ class TestDomainDelete(TestDomain): domain = identity_fakes.FakeDomain.create_one_domain() def setUp(self): - super(TestDomainDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.domains_mock.get.return_value = self.domain @@ -244,7 +244,7 @@ class TestDomainList(TestDomain): domain = identity_fakes.FakeDomain.create_one_domain() def setUp(self): - super(TestDomainList, self).setUp() + super().setUp() self.domains_mock.list.return_value = [self.domain] @@ -329,7 +329,7 @@ class TestDomainSet(TestDomain): domain = identity_fakes.FakeDomain.create_one_domain() def setUp(self): - super(TestDomainSet, self).setUp() + super().setUp() self.domains_mock.get.return_value = self.domain @@ -478,7 +478,7 @@ def test_domain_set_no_immutable_option(self): class TestDomainShow(TestDomain): def setUp(self): - super(TestDomainShow, self).setUp() + super().setUp() self.domain = identity_fakes.FakeDomain.create_one_domain() self.domains_mock.get.return_value = self.domain diff --git a/openstackclient/tests/unit/identity/v3/test_endpoint.py b/openstackclient/tests/unit/identity/v3/test_endpoint.py index 02c125eb9..1dafe48e4 100644 --- a/openstackclient/tests/unit/identity/v3/test_endpoint.py +++ b/openstackclient/tests/unit/identity/v3/test_endpoint.py @@ -16,7 +16,7 @@ class TestEndpoint(identity_fakes.TestIdentityv3): def setUp(self): - super(TestEndpoint, self).setUp() + super().setUp() # Get a shortcut to the EndpointManager Mock self.endpoints_mock = self.identity_client.endpoints @@ -52,7 +52,7 @@ class TestEndpointCreate(TestEndpoint): ) def setUp(self): - super(TestEndpointCreate, self).setUp() + super().setUp() self.endpoint = identity_fakes.FakeEndpoint.create_one_endpoint( attrs={'service_id': self.service.id} @@ -247,7 +247,7 @@ class TestEndpointDelete(TestEndpoint): endpoint = identity_fakes.FakeEndpoint.create_one_endpoint() def setUp(self): - super(TestEndpointDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource(endpoint) self.endpoints_mock.get.return_value = self.endpoint @@ -290,7 +290,7 @@ class TestEndpointList(TestEndpoint): ) def setUp(self): - super(TestEndpointList, self).setUp() + super().setUp() self.endpoints_mock.list.return_value = [self.endpoint] @@ -477,7 +477,7 @@ class TestEndpointSet(TestEndpoint): ) def setUp(self): - super(TestEndpointSet, self).setUp() + super().setUp() # This is the return value for utils.find_resource(endpoint) self.endpoints_mock.get.return_value = self.endpoint @@ -659,7 +659,7 @@ class TestEndpointShow(TestEndpoint): ) def setUp(self): - super(TestEndpointShow, self).setUp() + super().setUp() self.endpoints_mock.get.return_value = self.endpoint @@ -785,7 +785,7 @@ class TestAddProjectToEndpoint(TestEndpoint): ) def setUp(self): - super(TestAddProjectToEndpoint, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.endpoints_mock.get.return_value = self.endpoint @@ -844,7 +844,7 @@ class TestRemoveProjectEndpoint(TestEndpoint): ) def setUp(self): - super(TestRemoveProjectEndpoint, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.endpoints_mock.get.return_value = self.endpoint diff --git a/openstackclient/tests/unit/identity/v3/test_endpoint_group.py b/openstackclient/tests/unit/identity/v3/test_endpoint_group.py index 00208b48a..e739a7d92 100644 --- a/openstackclient/tests/unit/identity/v3/test_endpoint_group.py +++ b/openstackclient/tests/unit/identity/v3/test_endpoint_group.py @@ -19,7 +19,7 @@ class TestEndpointGroup(identity_fakes.TestIdentityv3): def setUp(self): - super(TestEndpointGroup, self).setUp() + super().setUp() # Get a shortcut to the EndpointManager Mock self.endpoint_groups_mock = self.identity_client.endpoint_groups @@ -49,7 +49,7 @@ class TestEndpointGroupCreate(TestEndpointGroup): ) def setUp(self): - super(TestEndpointGroupCreate, self).setUp() + super().setUp() self.endpoint_group = ( identity_fakes.FakeEndpointGroup.create_one_endpointgroup( @@ -110,7 +110,7 @@ class TestEndpointGroupDelete(TestEndpointGroup): ) def setUp(self): - super(TestEndpointGroupDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource(endpoint) self.endpoint_groups_mock.get.return_value = self.endpoint_group @@ -150,7 +150,7 @@ class TestEndpointGroupList(TestEndpointGroup): ) def setUp(self): - super(TestEndpointGroupList, self).setUp() + super().setUp() self.endpoint_groups_mock.list.return_value = [self.endpoint_group] self.endpoint_groups_mock.get.return_value = self.endpoint_group @@ -256,7 +256,7 @@ class TestEndpointGroupSet(TestEndpointGroup): ) def setUp(self): - super(TestEndpointGroupSet, self).setUp() + super().setUp() # This is the return value for utils.find_resource(endpoint) self.endpoint_groups_mock.get.return_value = self.endpoint_group @@ -371,7 +371,7 @@ class TestAddProjectToEndpointGroup(TestEndpointGroup): ) def setUp(self): - super(TestAddProjectToEndpointGroup, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.endpoint_groups_mock.get.return_value = self.endpoint_group @@ -432,7 +432,7 @@ class TestRemoveProjectEndpointGroup(TestEndpointGroup): ) def setUp(self): - super(TestRemoveProjectEndpointGroup, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.endpoint_groups_mock.get.return_value = self.endpoint_group diff --git a/openstackclient/tests/unit/identity/v3/test_group.py b/openstackclient/tests/unit/identity/v3/test_group.py index 646a43203..04e5a9cf9 100644 --- a/openstackclient/tests/unit/identity/v3/test_group.py +++ b/openstackclient/tests/unit/identity/v3/test_group.py @@ -24,7 +24,7 @@ class TestGroup(identity_fakes.TestIdentityv3): def setUp(self): - super(TestGroup, self).setUp() + super().setUp() # Get a shortcut to the DomainManager Mock self.domains_mock = self.identity_client.domains @@ -44,7 +44,7 @@ class TestGroupAddUser(TestGroup): users = identity_fakes.FakeUser.create_users(count=2) def setUp(self): - super(TestGroupAddUser, self).setUp() + super().setUp() self.groups_mock.get.return_value = self._group self.users_mock.get = identity_fakes.FakeUser.get_users(self.users) @@ -123,7 +123,7 @@ class TestGroupCheckUser(TestGroup): user = identity_fakes.FakeUser.create_one_user() def setUp(self): - super(TestGroupCheckUser, self).setUp() + super().setUp() self.groups_mock.get.return_value = self.group self.users_mock.get.return_value = self.user @@ -179,7 +179,7 @@ class TestGroupCreate(TestGroup): ) def setUp(self): - super(TestGroupCreate, self).setUp() + super().setUp() self.group = identity_fakes.FakeGroup.create_one_group( attrs={'domain_id': self.domain.id} ) @@ -263,7 +263,7 @@ class TestGroupDelete(TestGroup): ) def setUp(self): - super(TestGroupDelete, self).setUp() + super().setUp() self.groups_mock.get = identity_fakes.FakeGroup.get_groups(self.groups) self.groups_mock.delete.return_value = None @@ -368,7 +368,7 @@ class TestGroupList(TestGroup): ) def setUp(self): - super(TestGroupList, self).setUp() + super().setUp() self.groups_mock.get.return_value = self.group self.groups_mock.list.return_value = [self.group] @@ -496,7 +496,7 @@ class TestGroupRemoveUser(TestGroup): users = identity_fakes.FakeUser.create_users(count=2) def setUp(self): - super(TestGroupRemoveUser, self).setUp() + super().setUp() self.groups_mock.get.return_value = self._group self.users_mock.get = identity_fakes.FakeUser.get_users(self.users) @@ -577,7 +577,7 @@ class TestGroupSet(TestGroup): ) def setUp(self): - super(TestGroupSet, self).setUp() + super().setUp() self.groups_mock.get.return_value = self.group self.domains_mock.get.return_value = self.domain @@ -657,7 +657,7 @@ class TestGroupShow(TestGroup): ) def setUp(self): - super(TestGroupShow, self).setUp() + super().setUp() self.group = identity_fakes.FakeGroup.create_one_group( attrs={'domain_id': self.domain.id} ) diff --git a/openstackclient/tests/unit/identity/v3/test_identity_provider.py b/openstackclient/tests/unit/identity/v3/test_identity_provider.py index 4426e096e..20e7f497a 100644 --- a/openstackclient/tests/unit/identity/v3/test_identity_provider.py +++ b/openstackclient/tests/unit/identity/v3/test_identity_provider.py @@ -25,7 +25,7 @@ class TestIdentityProvider(identity_fakes.TestFederatedIdentity): def setUp(self): - super(TestIdentityProvider, self).setUp() + super().setUp() # Identity Provider mocks federation_lib = self.identity_client.federation @@ -59,7 +59,7 @@ class TestIdentityProviderCreate(TestIdentityProvider): ) def setUp(self): - super(TestIdentityProviderCreate, self).setUp() + super().setUp() copied_idp = copy.deepcopy(identity_fakes.IDENTITY_PROVIDER) resource = fakes.FakeResource(None, copied_idp, loaded=True) @@ -401,7 +401,7 @@ def test_create_identity_provider_authttl_not_int(self): class TestIdentityProviderDelete(TestIdentityProvider): def setUp(self): - super(TestIdentityProviderDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.identity_providers_mock.get.return_value = fakes.FakeResource( @@ -432,7 +432,7 @@ def test_delete_identity_provider(self): class TestIdentityProviderList(TestIdentityProvider): def setUp(self): - super(TestIdentityProviderList, self).setUp() + super().setUp() self.identity_providers_mock.get.return_value = fakes.FakeResource( None, @@ -540,7 +540,7 @@ class TestIdentityProviderSet(TestIdentityProvider): ) def setUp(self): - super(TestIdentityProviderSet, self).setUp() + super().setUp() self.cmd = identity_provider.SetIdentityProvider(self.app, None) def test_identity_provider_set_description(self): @@ -838,7 +838,7 @@ def test_identity_provider_set_authttl_not_int(self): class TestIdentityProviderShow(TestIdentityProvider): def setUp(self): - super(TestIdentityProviderShow, self).setUp() + super().setUp() ret = fakes.FakeResource( None, diff --git a/openstackclient/tests/unit/identity/v3/test_implied_role.py b/openstackclient/tests/unit/identity/v3/test_implied_role.py index 7b53de5eb..f6bc6063f 100644 --- a/openstackclient/tests/unit/identity/v3/test_implied_role.py +++ b/openstackclient/tests/unit/identity/v3/test_implied_role.py @@ -22,7 +22,7 @@ class TestRole(identity_fakes.TestIdentityv3): def setUp(self): - super(TestRole, self).setUp() + super().setUp() identity_client = self.identity_client @@ -56,7 +56,7 @@ def _is_inheritance_testcase(self): class TestImpliedRoleCreate(TestRole): def setUp(self): - super(TestImpliedRoleCreate, self).setUp() + super().setUp() self.roles_mock.list.return_value = [ fakes.FakeResource( @@ -116,7 +116,7 @@ def test_implied_role_create(self): class TestImpliedRoleDelete(TestRole): def setUp(self): - super(TestImpliedRoleDelete, self).setUp() + super().setUp() self.roles_mock.list.return_value = [ fakes.FakeResource( @@ -163,7 +163,7 @@ def test_implied_role_delete(self): class TestImpliedRoleList(TestRole): def setUp(self): - super(TestImpliedRoleList, self).setUp() + super().setUp() self.inference_rules_mock.list_inference_roles.return_value = ( identity_fakes.FakeImpliedRoleResponse.create_list() diff --git a/openstackclient/tests/unit/identity/v3/test_limit.py b/openstackclient/tests/unit/identity/v3/test_limit.py index 1fb660b1a..ef0fa4b1e 100644 --- a/openstackclient/tests/unit/identity/v3/test_limit.py +++ b/openstackclient/tests/unit/identity/v3/test_limit.py @@ -22,7 +22,7 @@ class TestLimit(identity_fakes.TestIdentityv3): def setUp(self): - super(TestLimit, self).setUp() + super().setUp() identity_manager = self.identity_client @@ -40,7 +40,7 @@ def setUp(self): class TestLimitCreate(TestLimit): def setUp(self): - super(TestLimitCreate, self).setUp() + super().setUp() self.service = fakes.FakeResource( None, copy.deepcopy(identity_fakes.SERVICE), loaded=True @@ -181,7 +181,7 @@ def test_limit_create_with_options(self): class TestLimitDelete(TestLimit): def setUp(self): - super(TestLimitDelete, self).setUp() + super().setUp() self.cmd = limit.DeleteLimit(self.app, None) def test_limit_delete(self): @@ -213,7 +213,7 @@ def test_limit_delete_with_exception(self): class TestLimitShow(TestLimit): def setUp(self): - super(TestLimitShow, self).setUp() + super().setUp() self.limit_mock.get.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.LIMIT), loaded=True @@ -254,7 +254,7 @@ def test_limit_show(self): class TestLimitSet(TestLimit): def setUp(self): - super(TestLimitSet, self).setUp() + super().setUp() self.cmd = limit.SetLimit(self.app, None) def test_limit_set_description(self): @@ -355,7 +355,7 @@ def test_limit_set_resource_limit(self): class TestLimitList(TestLimit): def setUp(self): - super(TestLimitList, self).setUp() + super().setUp() self.limit_mock.list.return_value = [ fakes.FakeResource( diff --git a/openstackclient/tests/unit/identity/v3/test_mappings.py b/openstackclient/tests/unit/identity/v3/test_mappings.py index 50386609e..60a89dd61 100644 --- a/openstackclient/tests/unit/identity/v3/test_mappings.py +++ b/openstackclient/tests/unit/identity/v3/test_mappings.py @@ -24,7 +24,7 @@ class TestMapping(identity_fakes.TestFederatedIdentity): def setUp(self): - super(TestMapping, self).setUp() + super().setUp() federation_lib = self.identity_client.federation self.mapping_mock = federation_lib.mappings @@ -33,7 +33,7 @@ def setUp(self): class TestMappingCreate(TestMapping): def setUp(self): - super(TestMappingCreate, self).setUp() + super().setUp() self.mapping_mock.create.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.MAPPING_RESPONSE), loaded=True ) @@ -75,7 +75,7 @@ def test_create_mapping(self): class TestMappingDelete(TestMapping): def setUp(self): - super(TestMappingDelete, self).setUp() + super().setUp() self.mapping_mock.get.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.MAPPING_RESPONSE), loaded=True ) @@ -96,7 +96,7 @@ def test_delete_mapping(self): class TestMappingList(TestMapping): def setUp(self): - super(TestMappingList, self).setUp() + super().setUp() self.mapping_mock.get.return_value = fakes.FakeResource( None, {'id': identity_fakes.mapping_id}, loaded=True ) @@ -141,7 +141,7 @@ def test_mapping_list(self): class TestMappingSet(TestMapping): def setUp(self): - super(TestMappingSet, self).setUp() + super().setUp() self.mapping_mock.get.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.MAPPING_RESPONSE), loaded=True @@ -201,7 +201,7 @@ def test_set_rules_wrong_file_path(self): class TestMappingShow(TestMapping): def setUp(self): - super(TestMappingShow, self).setUp() + super().setUp() self.mapping_mock.get.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.MAPPING_RESPONSE), loaded=True diff --git a/openstackclient/tests/unit/identity/v3/test_oauth.py b/openstackclient/tests/unit/identity/v3/test_oauth.py index 020a00232..576f8b20e 100644 --- a/openstackclient/tests/unit/identity/v3/test_oauth.py +++ b/openstackclient/tests/unit/identity/v3/test_oauth.py @@ -19,7 +19,7 @@ class TestOAuth1(identity_fakes.TestOAuth1): def setUp(self): - super(TestOAuth1, self).setUp() + super().setUp() identity_client = self.identity_client self.access_tokens_mock = identity_client.oauth1.access_tokens self.access_tokens_mock.reset_mock() @@ -33,7 +33,7 @@ def setUp(self): class TestAccessTokenCreate(TestOAuth1): def setUp(self): - super(TestAccessTokenCreate, self).setUp() + super().setUp() self.access_tokens_mock.create.return_value = fakes.FakeResource( None, @@ -87,7 +87,7 @@ def test_create_access_tokens(self): class TestRequestTokenAuthorize(TestOAuth1): def setUp(self): - super(TestRequestTokenAuthorize, self).setUp() + super().setUp() self.roles_mock.get.return_value = fakes.FakeResource( None, @@ -127,7 +127,7 @@ def test_authorize_request_tokens(self): class TestRequestTokenCreate(TestOAuth1): def setUp(self): - super(TestRequestTokenCreate, self).setUp() + super().setUp() self.request_tokens_mock.create.return_value = fakes.FakeResource( None, diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py index ffef854e9..9b7fc8cea 100644 --- a/openstackclient/tests/unit/identity/v3/test_project.py +++ b/openstackclient/tests/unit/identity/v3/test_project.py @@ -26,7 +26,7 @@ class TestProject(identity_fakes.TestIdentityv3): def setUp(self): - super(TestProject, self).setUp() + super().setUp() # Get a shortcut to the DomainManager Mock self.domains_mock = self.identity_client.domains @@ -52,7 +52,7 @@ class TestProjectCreate(TestProject): ) def setUp(self): - super(TestProjectCreate, self).setUp() + super().setUp() self.project = identity_fakes.FakeProject.create_one_project( attrs={'domain_id': self.domain.id} @@ -676,7 +676,7 @@ class TestProjectDelete(TestProject): project = identity_fakes.FakeProject.create_one_project() def setUp(self): - super(TestProjectDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.projects_mock.get.return_value = self.project @@ -759,7 +759,7 @@ class TestProjectList(TestProject): ) def setUp(self): - super(TestProjectList, self).setUp() + super().setUp() self.projects_mock.list.return_value = [self.project] @@ -951,7 +951,7 @@ class TestProjectSet(TestProject): ) def setUp(self): - super(TestProjectSet, self).setUp() + super().setUp() self.domains_mock.get.return_value = self.domain @@ -1130,7 +1130,7 @@ def test_project_set_tags(self): # Set expected values. new tag is added to original tags for update. kwargs = { 'name': 'qwerty', - 'tags': sorted(set(['tag1', 'tag2', 'tag3', 'foo'])), + 'tags': sorted({'tag1', 'tag2', 'tag3', 'foo'}), } # ProjectManager.update(project, name=, domain=, description=, # enabled=, **kwargs) @@ -1155,7 +1155,7 @@ def test_project_remove_tags(self): result = self.cmd.take_action(parsed_args) - kwargs = {'tags': list(set(['tag3']))} + kwargs = {'tags': list({'tag3'})} self.projects_mock.update.assert_called_with(self.project.id, **kwargs) self.assertIsNone(result) @@ -1214,7 +1214,7 @@ class TestProjectShow(TestProject): domain = identity_fakes.FakeDomain.create_one_domain() def setUp(self): - super(TestProjectShow, self).setUp() + super().setUp() self.project = identity_fakes.FakeProject.create_one_project( attrs={'domain_id': self.domain.id} diff --git a/openstackclient/tests/unit/identity/v3/test_protocol.py b/openstackclient/tests/unit/identity/v3/test_protocol.py index d40f916e5..c85699685 100644 --- a/openstackclient/tests/unit/identity/v3/test_protocol.py +++ b/openstackclient/tests/unit/identity/v3/test_protocol.py @@ -21,7 +21,7 @@ class TestProtocol(identity_fakes.TestFederatedIdentity): def setUp(self): - super(TestProtocol, self).setUp() + super().setUp() federation_lib = self.identity_client.federation self.protocols_mock = federation_lib.protocols @@ -30,7 +30,7 @@ def setUp(self): class TestProtocolCreate(TestProtocol): def setUp(self): - super(TestProtocolCreate, self).setUp() + super().setUp() proto = copy.deepcopy(identity_fakes.PROTOCOL_OUTPUT) resource = fakes.FakeResource(None, proto, loaded=True) @@ -72,7 +72,7 @@ def test_create_protocol(self): class TestProtocolDelete(TestProtocol): def setUp(self): - super(TestProtocolDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.protocols_mock.get.return_value = fakes.FakeResource( @@ -106,7 +106,7 @@ def test_delete_identity_provider(self): class TestProtocolList(TestProtocol): def setUp(self): - super(TestProtocolList, self).setUp() + super().setUp() self.protocols_mock.get.return_value = fakes.FakeResource( None, identity_fakes.PROTOCOL_ID_MAPPING, loaded=True @@ -132,7 +132,7 @@ def test_list_protocols(self): class TestProtocolSet(TestProtocol): def setUp(self): - super(TestProtocolSet, self).setUp() + super().setUp() self.protocols_mock.get.return_value = fakes.FakeResource( None, identity_fakes.PROTOCOL_OUTPUT, loaded=True ) @@ -178,7 +178,7 @@ def test_set_new_mapping(self): class TestProtocolShow(TestProtocol): def setUp(self): - super(TestProtocolShow, self).setUp() + super().setUp() self.protocols_mock.get.return_value = fakes.FakeResource( None, identity_fakes.PROTOCOL_OUTPUT, loaded=False ) diff --git a/openstackclient/tests/unit/identity/v3/test_region.py b/openstackclient/tests/unit/identity/v3/test_region.py index be28f9448..9b66c3ce9 100644 --- a/openstackclient/tests/unit/identity/v3/test_region.py +++ b/openstackclient/tests/unit/identity/v3/test_region.py @@ -20,7 +20,7 @@ class TestRegion(identity_fakes.TestIdentityv3): def setUp(self): - super(TestRegion, self).setUp() + super().setUp() # Get a shortcut to the RegionManager Mock self.regions_mock = self.identity_client.regions @@ -40,7 +40,7 @@ class TestRegionCreate(TestRegion): ) def setUp(self): - super(TestRegionCreate, self).setUp() + super().setUp() self.regions_mock.create.return_value = fakes.FakeResource( None, @@ -134,7 +134,7 @@ def test_region_create_parent_region_id(self): class TestRegionDelete(TestRegion): def setUp(self): - super(TestRegionDelete, self).setUp() + super().setUp() self.regions_mock.delete.return_value = None @@ -173,7 +173,7 @@ class TestRegionList(TestRegion): ) def setUp(self): - super(TestRegionList, self).setUp() + super().setUp() self.regions_mock.list.return_value = [ fakes.FakeResource( @@ -224,7 +224,7 @@ def test_region_list_parent_region_id(self): class TestRegionSet(TestRegion): def setUp(self): - super(TestRegionSet, self).setUp() + super().setUp() self.regions_mock.update.return_value = fakes.FakeResource( None, @@ -301,7 +301,7 @@ def test_region_set_parent_region_id(self): class TestRegionShow(TestRegion): def setUp(self): - super(TestRegionShow, self).setUp() + super().setUp() self.regions_mock.get.return_value = fakes.FakeResource( None, diff --git a/openstackclient/tests/unit/identity/v3/test_registered_limit.py b/openstackclient/tests/unit/identity/v3/test_registered_limit.py index 2f51b9772..792096cde 100644 --- a/openstackclient/tests/unit/identity/v3/test_registered_limit.py +++ b/openstackclient/tests/unit/identity/v3/test_registered_limit.py @@ -22,7 +22,7 @@ class TestRegisteredLimit(identity_fakes.TestIdentityv3): def setUp(self): - super(TestRegisteredLimit, self).setUp() + super().setUp() self.registered_limit_mock = self.identity_client.registered_limits @@ -35,7 +35,7 @@ def setUp(self): class TestRegisteredLimitCreate(TestRegisteredLimit): def setUp(self): - super(TestRegisteredLimitCreate, self).setUp() + super().setUp() self.service = fakes.FakeResource( None, copy.deepcopy(identity_fakes.SERVICE), loaded=True @@ -159,7 +159,7 @@ def test_registered_limit_create_with_options(self): class TestRegisteredLimitDelete(TestRegisteredLimit): def setUp(self): - super(TestRegisteredLimitDelete, self).setUp() + super().setUp() self.cmd = registered_limit.DeleteRegisteredLimit(self.app, None) @@ -198,7 +198,7 @@ def test_registered_limit_delete_with_exception(self): class TestRegisteredLimitShow(TestRegisteredLimit): def setUp(self): - super(TestRegisteredLimitShow, self).setUp() + super().setUp() self.registered_limit_mock.get.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.REGISTERED_LIMIT), loaded=True @@ -241,7 +241,7 @@ def test_registered_limit_show(self): class TestRegisteredLimitSet(TestRegisteredLimit): def setUp(self): - super(TestRegisteredLimitSet, self).setUp() + super().setUp() self.cmd = registered_limit.SetRegisteredLimit(self.app, None) def test_registered_limit_set_description(self): @@ -489,7 +489,7 @@ def test_registered_limit_set_region(self): class TestRegisteredLimitList(TestRegisteredLimit): def setUp(self): - super(TestRegisteredLimitList, self).setUp() + super().setUp() self.registered_limit_mock.get.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.REGISTERED_LIMIT), loaded=True diff --git a/openstackclient/tests/unit/identity/v3/test_role.py b/openstackclient/tests/unit/identity/v3/test_role.py index 6d9a2ff09..09b7536e3 100644 --- a/openstackclient/tests/unit/identity/v3/test_role.py +++ b/openstackclient/tests/unit/identity/v3/test_role.py @@ -27,7 +27,7 @@ class TestRole(identity_fakes.TestIdentityv3): def setUp(self): - super(TestRole, self).setUp() + super().setUp() # Get a shortcut to the UserManager Mock self.users_mock = self.identity_client.users @@ -60,7 +60,7 @@ def _is_inheritance_testcase(self): class TestRoleAdd(TestRole): def setUp(self): - super(TestRoleAdd, self).setUp() + super().setUp() self.users_mock.get.return_value = fakes.FakeResource( None, @@ -371,7 +371,7 @@ class TestRoleAddInherited(TestRoleAdd, TestRoleInherited): class TestRoleCreate(TestRole): def setUp(self): - super(TestRoleCreate, self).setUp() + super().setUp() self.domains_mock.get.return_value = fakes.FakeResource( None, @@ -592,7 +592,7 @@ def test_role_create_with_no_immutable_option(self): class TestRoleDelete(TestRole): def setUp(self): - super(TestRoleDelete, self).setUp() + super().setUp() self.roles_mock.get.return_value = fakes.FakeResource( None, @@ -691,7 +691,7 @@ class TestRoleList(TestRole): ) def setUp(self): - super(TestRoleList, self).setUp() + super().setUp() self.roles_mock.list.return_value = [ fakes.FakeResource( @@ -766,7 +766,7 @@ def test_role_list_domain_role(self): class TestRoleRemove(TestRole): def setUp(self): - super(TestRoleRemove, self).setUp() + super().setUp() self.users_mock.get.return_value = fakes.FakeResource( None, @@ -1316,7 +1316,7 @@ def test_role_remove_with_error(self): class TestRoleSet(TestRole): def setUp(self): - super(TestRoleSet, self).setUp() + super().setUp() self.roles_mock.get.return_value = fakes.FakeResource( None, @@ -1491,7 +1491,7 @@ def test_role_set_with_no_immutable(self): class TestRoleShow(TestRole): def setUp(self): - super(TestRoleShow, self).setUp() + super().setUp() self.roles_mock.get.return_value = fakes.FakeResource( None, diff --git a/openstackclient/tests/unit/identity/v3/test_role_assignment.py b/openstackclient/tests/unit/identity/v3/test_role_assignment.py index afc7ac5b7..fa793bc82 100644 --- a/openstackclient/tests/unit/identity/v3/test_role_assignment.py +++ b/openstackclient/tests/unit/identity/v3/test_role_assignment.py @@ -21,7 +21,7 @@ class TestRoleAssignment(identity_fakes.TestIdentityv3): def setUp(self): - super(TestRoleAssignment, self).setUp() + super().setUp() class TestRoleAssignmentList(TestRoleAssignment): diff --git a/openstackclient/tests/unit/identity/v3/test_service.py b/openstackclient/tests/unit/identity/v3/test_service.py index 36669d7d5..8993c8f84 100644 --- a/openstackclient/tests/unit/identity/v3/test_service.py +++ b/openstackclient/tests/unit/identity/v3/test_service.py @@ -22,7 +22,7 @@ class TestService(identity_fakes.TestIdentityv3): def setUp(self): - super(TestService, self).setUp() + super().setUp() # Get a shortcut to the ServiceManager Mock self.services_mock = self.identity_client.services @@ -39,7 +39,7 @@ class TestServiceCreate(TestService): ) def setUp(self): - super(TestServiceCreate, self).setUp() + super().setUp() self.service = identity_fakes.FakeService.create_one_service() self.datalist = ( @@ -181,7 +181,7 @@ class TestServiceDelete(TestService): service = identity_fakes.FakeService.create_one_service() def setUp(self): - super(TestServiceDelete, self).setUp() + super().setUp() self.services_mock.get.side_effect = identity_exc.NotFound(None) self.services_mock.find.return_value = self.service @@ -211,7 +211,7 @@ class TestServiceList(TestService): service = identity_fakes.FakeService.create_one_service() def setUp(self): - super(TestServiceList, self).setUp() + super().setUp() self.services_mock.list.return_value = [self.service] @@ -275,7 +275,7 @@ class TestServiceSet(TestService): service = identity_fakes.FakeService.create_one_service() def setUp(self): - super(TestServiceSet, self).setUp() + super().setUp() self.services_mock.get.side_effect = identity_exc.NotFound(None) self.services_mock.find.return_value = self.service @@ -435,7 +435,7 @@ class TestServiceShow(TestService): service = identity_fakes.FakeService.create_one_service() def setUp(self): - super(TestServiceShow, self).setUp() + super().setUp() self.services_mock.get.side_effect = identity_exc.NotFound(None) self.services_mock.find.return_value = self.service diff --git a/openstackclient/tests/unit/identity/v3/test_service_provider.py b/openstackclient/tests/unit/identity/v3/test_service_provider.py index 8954c2a26..23402d948 100644 --- a/openstackclient/tests/unit/identity/v3/test_service_provider.py +++ b/openstackclient/tests/unit/identity/v3/test_service_provider.py @@ -21,7 +21,7 @@ class TestServiceProvider(service_fakes.TestFederatedIdentity): def setUp(self): - super(TestServiceProvider, self).setUp() + super().setUp() federation_lib = self.identity_client.federation self.service_providers_mock = federation_lib.service_providers @@ -45,7 +45,7 @@ class TestServiceProviderCreate(TestServiceProvider): ) def setUp(self): - super(TestServiceProviderCreate, self).setUp() + super().setUp() copied_sp = copy.deepcopy(service_fakes.SERVICE_PROVIDER) resource = fakes.FakeResource(None, copied_sp, loaded=True) @@ -166,7 +166,7 @@ def test_create_service_provider_disabled(self): class TestServiceProviderDelete(TestServiceProvider): def setUp(self): - super(TestServiceProviderDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.service_providers_mock.get.return_value = fakes.FakeResource( @@ -197,7 +197,7 @@ def test_delete_service_provider(self): class TestServiceProviderList(TestServiceProvider): def setUp(self): - super(TestServiceProviderList, self).setUp() + super().setUp() self.service_providers_mock.get.return_value = fakes.FakeResource( None, @@ -257,7 +257,7 @@ class TestServiceProviderSet(TestServiceProvider): ) def setUp(self): - super(TestServiceProviderSet, self).setUp() + super().setUp() self.cmd = service_provider.SetServiceProvider(self.app, None) def test_service_provider_disable(self): @@ -365,7 +365,7 @@ def prepare(self): class TestServiceProviderShow(TestServiceProvider): def setUp(self): - super(TestServiceProviderShow, self).setUp() + super().setUp() ret = fakes.FakeResource( None, diff --git a/openstackclient/tests/unit/identity/v3/test_token.py b/openstackclient/tests/unit/identity/v3/test_token.py index ba1a026c8..96375ea35 100644 --- a/openstackclient/tests/unit/identity/v3/test_token.py +++ b/openstackclient/tests/unit/identity/v3/test_token.py @@ -21,7 +21,7 @@ class TestToken(identity_fakes.TestIdentityv3): def setUp(self): - super(TestToken, self).setUp() + super().setUp() # Get a shortcut to the Auth Ref Mock self.ar_mock = mock.PropertyMock() @@ -30,7 +30,7 @@ def setUp(self): class TestTokenIssue(TestToken): def setUp(self): - super(TestTokenIssue, self).setUp() + super().setUp() self.cmd = token.IssueToken(self.app, None) @@ -118,7 +118,7 @@ class TestTokenRevoke(TestToken): TOKEN = 'fob' def setUp(self): - super(TestTokenRevoke, self).setUp() + super().setUp() self.tokens_mock = self.identity_client.tokens self.tokens_mock.reset_mock() self.tokens_mock.revoke_token.return_value = True diff --git a/openstackclient/tests/unit/identity/v3/test_trust.py b/openstackclient/tests/unit/identity/v3/test_trust.py index 6ca90721e..fb7ac9b0c 100644 --- a/openstackclient/tests/unit/identity/v3/test_trust.py +++ b/openstackclient/tests/unit/identity/v3/test_trust.py @@ -24,7 +24,7 @@ class TestTrust(identity_fakes.TestIdentityv3): def setUp(self): - super(TestTrust, self).setUp() + super().setUp() self.trusts_mock = self.identity_client.trusts self.trusts_mock.reset_mock() @@ -38,7 +38,7 @@ def setUp(self): class TestTrustCreate(TestTrust): def setUp(self): - super(TestTrustCreate, self).setUp() + super().setUp() self.projects_mock.get.return_value = fakes.FakeResource( None, @@ -127,7 +127,7 @@ def test_trust_create_basic(self): class TestTrustDelete(TestTrust): def setUp(self): - super(TestTrustDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.trusts_mock.get.return_value = fakes.FakeResource( @@ -186,7 +186,7 @@ def test_delete_multi_trusts_with_exception(self, find_mock): class TestTrustList(TestTrust): def setUp(self): - super(TestTrustList, self).setUp() + super().setUp() self.trusts_mock.list.return_value = [ fakes.FakeResource( @@ -363,7 +363,7 @@ def test_trust_list_trustor(self): class TestTrustShow(TestTrust): def setUp(self): - super(TestTrustShow, self).setUp() + super().setUp() self.trusts_mock.get.return_value = fakes.FakeResource( None, diff --git a/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py b/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py index 882dd0862..40b199101 100644 --- a/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py +++ b/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py @@ -19,7 +19,7 @@ class TestUnscopedSAML(identity_fakes.TestFederatedIdentity): def setUp(self): - super(TestUnscopedSAML, self).setUp() + super().setUp() federation_lib = self.identity_client.federation self.projects_mock = federation_lib.projects @@ -30,7 +30,7 @@ def setUp(self): class TestDomainList(TestUnscopedSAML): def setUp(self): - super(TestDomainList, self).setUp() + super().setUp() self.domains_mock.list.return_value = [ fakes.FakeResource( @@ -70,7 +70,7 @@ def test_accessible_domains_list(self): class TestProjectList(TestUnscopedSAML): def setUp(self): - super(TestProjectList, self).setUp() + super().setUp() self.projects_mock.list.return_value = [ fakes.FakeResource( diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index 63d847191..19c1a6dcc 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -918,7 +918,7 @@ class TestUserList(identity_fakes.TestIdentityv3): ) def setUp(self): - super(TestUserList, self).setUp() + super().setUp() self.identity_sdk_client.find_user.return_value = self.user self.identity_sdk_client.users.return_value = [self.user] @@ -1088,7 +1088,7 @@ class TestUserSet(identity_fakes.TestIdentityv3): ) def setUp(self): - super(TestUserSet, self).setUp() + super().setUp() self.identity_sdk_client.find_project.return_value = self.project self.identity_sdk_client.find_user.return_value = self.user @@ -1731,7 +1731,7 @@ def test_user_set_with_multiple_options(self): class TestUserSetPassword(identity_fakes.TestIdentityv3): def setUp(self): - super(TestUserSetPassword, self).setUp() + super().setUp() self.cmd = user.SetPasswordUser(self.app, None) @staticmethod @@ -1803,7 +1803,7 @@ class TestUserShow(identity_fakes.TestIdentityv3): user = sdk_fakes.generate_fake_resource(_user.User) def setUp(self): - super(TestUserShow, self).setUp() + super().setUp() self.identity_sdk_client.find_user.return_value = self.user diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py index bf62d10c2..971e969c7 100644 --- a/openstackclient/tests/unit/image/v1/test_image.py +++ b/openstackclient/tests/unit/image/v1/test_image.py @@ -52,7 +52,7 @@ class TestImageCreate(image_fakes.TestImagev1): ) def setUp(self): - super(TestImageCreate, self).setUp() + 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) @@ -145,7 +145,7 @@ def test_image_reserve_options(self, raw_input): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) - @mock.patch('openstackclient.image.v1.image.io.open', name='Open') + @mock.patch('openstackclient.image.v1.image.open', name='Open') def test_image_create_file(self, mock_open): mock_file = mock.Mock(name='File') mock_open.return_value = mock_file @@ -209,7 +209,7 @@ class TestImageDelete(image_fakes.TestImagev1): _image = image_fakes.create_one_image() def setUp(self): - super(TestImageDelete, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.image_client.find_image = mock.Mock(return_value=self._image) @@ -259,7 +259,7 @@ class TestImageList(image_fakes.TestImagev1): image_info = copy.deepcopy(info) def setUp(self): - super(TestImageList, self).setUp() + super().setUp() self.image_client.images = mock.Mock() self.image_client.images.side_effect = [ @@ -438,7 +438,7 @@ class TestImageSet(image_fakes.TestImagev1): _image = image_fakes.create_one_image() def setUp(self): - super(TestImageSet, self).setUp() + super().setUp() # This is the return value for utils.find_resource() self.image_client.find_image = mock.Mock(return_value=self._image) @@ -710,7 +710,7 @@ class TestImageShow(image_fakes.TestImagev1): ) def setUp(self): - super(TestImageShow, self).setUp() + super().setUp() self.image_client.find_image = mock.Mock(return_value=self._image) diff --git a/openstackclient/tests/unit/integ/base.py b/openstackclient/tests/unit/integ/base.py index 5bef20b4c..cecf122b2 100644 --- a/openstackclient/tests/unit/integ/base.py +++ b/openstackclient/tests/unit/integ/base.py @@ -119,6 +119,6 @@ def make_v3_token(req_mock): class TestInteg(utils.TestCase): def setUp(self): - super(TestInteg, self).setUp() + super().setUp() self.requests_mock = self.useFixture(fixture.Fixture()) diff --git a/openstackclient/tests/unit/integ/cli/test_project.py b/openstackclient/tests/unit/integ/cli/test_project.py index abea0bf43..b536a764d 100644 --- a/openstackclient/tests/unit/integ/cli/test_project.py +++ b/openstackclient/tests/unit/integ/cli/test_project.py @@ -21,7 +21,7 @@ class TestIntegV2ProjectID(test_base.TestInteg): def setUp(self): - super(TestIntegV2ProjectID, self).setUp() + super().setUp() env = { "OS_AUTH_URL": test_base.V2_AUTH_URL, "OS_PROJECT_ID": test_shell.DEFAULT_PROJECT_ID, @@ -78,7 +78,7 @@ def test_project_id_arg(self): class TestIntegV2ProjectName(test_base.TestInteg): def setUp(self): - super(TestIntegV2ProjectName, self).setUp() + super().setUp() env = { "OS_AUTH_URL": test_base.V2_AUTH_URL, "OS_PROJECT_NAME": test_shell.DEFAULT_PROJECT_NAME, @@ -135,7 +135,7 @@ def test_project_name_arg(self): class TestIntegV3ProjectID(test_base.TestInteg): def setUp(self): - super(TestIntegV3ProjectID, self).setUp() + super().setUp() env = { "OS_AUTH_URL": test_base.V3_AUTH_URL, "OS_PROJECT_ID": test_shell.DEFAULT_PROJECT_NAME, @@ -190,7 +190,7 @@ def test_project_id_arg(self): class TestIntegV3ProjectName(test_base.TestInteg): def setUp(self): - super(TestIntegV3ProjectName, self).setUp() + super().setUp() env = { "OS_AUTH_URL": test_base.V3_AUTH_URL, "OS_PROJECT_NAME": test_shell.DEFAULT_PROJECT_NAME, diff --git a/openstackclient/tests/unit/integ/cli/test_shell.py b/openstackclient/tests/unit/integ/cli/test_shell.py index 6951c2afc..0a3a8ad43 100644 --- a/openstackclient/tests/unit/integ/cli/test_shell.py +++ b/openstackclient/tests/unit/integ/cli/test_shell.py @@ -23,7 +23,7 @@ class TestIntegShellCliNoAuth(test_base.TestInteg): def setUp(self): - super(TestIntegShellCliNoAuth, self).setUp() + super().setUp() env = {} self.useFixture(osc_lib_utils.EnvFixture(copy.deepcopy(env))) @@ -67,7 +67,7 @@ def test_shell_args_cacert_insecure(self): class TestIntegShellCliV2(test_base.TestInteg): def setUp(self): - super(TestIntegShellCliV2, self).setUp() + super().setUp() env = { "OS_AUTH_URL": test_base.V2_AUTH_URL, "OS_PROJECT_NAME": test_shell.DEFAULT_PROJECT_NAME, @@ -154,7 +154,7 @@ def test_shell_args_cacert_insecure(self): class TestIntegShellCliV2Ignore(test_base.TestInteg): def setUp(self): - super(TestIntegShellCliV2Ignore, self).setUp() + super().setUp() env = { "OS_AUTH_URL": test_base.V2_AUTH_URL, "OS_PROJECT_NAME": test_shell.DEFAULT_PROJECT_NAME, @@ -200,7 +200,7 @@ def test_shell_args_ignore_v3(self): class TestIntegShellCliV3(test_base.TestInteg): def setUp(self): - super(TestIntegShellCliV3, self).setUp() + super().setUp() env = { "OS_AUTH_URL": test_base.V3_AUTH_URL, "OS_PROJECT_DOMAIN_ID": test_shell.DEFAULT_PROJECT_DOMAIN_ID, @@ -290,7 +290,7 @@ def test_shell_args_cacert_insecure(self): class TestIntegShellCliV3Prompt(test_base.TestInteg): def setUp(self): - super(TestIntegShellCliV3Prompt, self).setUp() + super().setUp() env = { "OS_AUTH_URL": test_base.V3_AUTH_URL, "OS_PROJECT_DOMAIN_ID": test_shell.DEFAULT_PROJECT_DOMAIN_ID, @@ -339,7 +339,7 @@ class TestIntegShellCliPrecedence(test_base.TestInteg): """ def setUp(self): - super(TestIntegShellCliPrecedence, self).setUp() + super().setUp() env = { "OS_AUTH_URL": test_base.V3_AUTH_URL, "OS_PROJECT_DOMAIN_ID": test_shell.DEFAULT_PROJECT_DOMAIN_ID, @@ -412,7 +412,7 @@ class TestIntegShellCliPrecedenceOCC(test_base.TestInteg): """ def setUp(self): - super(TestIntegShellCliPrecedenceOCC, self).setUp() + super().setUp() env = { "OS_CLOUD": "megacloud", "OS_AUTH_URL": test_base.V3_AUTH_URL, diff --git a/openstackclient/tests/unit/network/test_common.py b/openstackclient/tests/unit/network/test_common.py index 6bdbadef7..c1ad9b28d 100644 --- a/openstackclient/tests/unit/network/test_common.py +++ b/openstackclient/tests/unit/network/test_common.py @@ -102,9 +102,7 @@ class FakeCreateNeutronCommandWithExtraArgs( common.NeutronCommandWithExtraArgs ): def get_parser(self, prog_name): - parser = super(FakeCreateNeutronCommandWithExtraArgs, self).get_parser( - prog_name - ) + parser = super().get_parser(prog_name) parser.add_argument( '--known-attribute', ) @@ -164,19 +162,19 @@ def test_take_action_compute(self): class TestNetworkAndComputeCommand(TestNetworkAndCompute): def setUp(self): - super(TestNetworkAndComputeCommand, self).setUp() + super().setUp() self.cmd = FakeNetworkAndComputeCommand(self.app, None) class TestNetworkAndComputeLister(TestNetworkAndCompute): def setUp(self): - super(TestNetworkAndComputeLister, self).setUp() + super().setUp() self.cmd = FakeNetworkAndComputeLister(self.app, None) class TestNetworkAndComputeShowOne(TestNetworkAndCompute): def setUp(self): - super(TestNetworkAndComputeShowOne, self).setUp() + super().setUp() self.cmd = FakeNetworkAndComputeShowOne(self.app, None) def test_take_action_with_http_exception(self): @@ -202,7 +200,7 @@ def test_take_action_with_http_exception(self): class TestNeutronCommandWithExtraArgs(utils.TestCommand): def setUp(self): - super(TestNeutronCommandWithExtraArgs, self).setUp() + super().setUp() # Create client mocks. Note that we intentionally do not use specced # mocks since we want to test fake methods. diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index 538878e77..4874bf856 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -133,7 +133,7 @@ def create_one_extension(attrs=None): return extension -class FakeNetworkQosPolicy(object): +class FakeNetworkQosPolicy: """Fake one or more QoS policies.""" @staticmethod @@ -213,7 +213,7 @@ def get_qos_policies(qos_policies=None, count=2): return mock.Mock(side_effect=qos_policies) -class FakeNetworkSecGroup(object): +class FakeNetworkSecGroup: """Fake one security group.""" @staticmethod @@ -244,7 +244,7 @@ def create_one_security_group(attrs=None): return security_group -class FakeNetworkQosRule(object): +class FakeNetworkQosRule: """Fake one or more Network QoS rules.""" @staticmethod @@ -325,7 +325,7 @@ def get_qos_rules(qos_rules=None, count=2): return mock.Mock(side_effect=qos_rules) -class FakeNetworkQosRuleType(object): +class FakeNetworkQosRuleType: """Fake one or more Network QoS rule types.""" @staticmethod @@ -372,7 +372,7 @@ def create_qos_rule_types(attrs=None, count=2): return qos_rule_types -class FakeRouter(object): +class FakeRouter: """Fake one or more routers.""" @staticmethod @@ -456,7 +456,7 @@ def get_routers(routers=None, count=2): return mock.Mock(side_effect=routers) -class FakeSecurityGroup(object): +class FakeSecurityGroup: """Fake one or more security groups.""" @staticmethod @@ -530,7 +530,7 @@ def get_security_groups(security_groups=None, count=2): return mock.Mock(side_effect=security_groups) -class FakeSecurityGroupRule(object): +class FakeSecurityGroupRule: """Fake one or more security group rules.""" @staticmethod @@ -612,7 +612,7 @@ def get_security_group_rules(security_group_rules=None, count=2): return mock.Mock(side_effect=security_group_rules) -class FakeSubnet(object): +class FakeSubnet: """Fake one or more subnets.""" @staticmethod @@ -700,7 +700,7 @@ def get_subnets(subnets=None, count=2): return mock.Mock(side_effect=subnets) -class FakeFloatingIP(object): +class FakeFloatingIP: """Fake one or more floating ip.""" @staticmethod @@ -777,7 +777,7 @@ def get_floating_ips(floating_ips=None, count=2): return mock.Mock(side_effect=floating_ips) -class FakeNetworkMeter(object): +class FakeNetworkMeter: """Fake network meter""" @staticmethod @@ -819,7 +819,7 @@ def get_meter(meter=None, count=2): return mock.Mock(side_effect=meter) -class FakeNetworkMeterRule(object): +class FakeNetworkMeterRule: """Fake metering rule""" @staticmethod @@ -864,7 +864,7 @@ def get_meter_rule(meter_rule=None, count=2): return mock.Mock(side_effect=meter_rule) -class FakeSubnetPool(object): +class FakeSubnetPool: """Fake one or more subnet pools.""" @staticmethod @@ -951,7 +951,7 @@ def get_subnet_pools(subnet_pools=None, count=2): return mock.Mock(side_effect=subnet_pools) -class FakeNetworkServiceProvider(object): +class FakeNetworkServiceProvider: """Fake Network Service Providers""" @staticmethod @@ -989,7 +989,7 @@ def create_network_service_providers(attrs=None, count=2): return service_providers -class FakeQuota(object): +class FakeQuota: """Fake quota""" @staticmethod @@ -1065,7 +1065,7 @@ def create_one_net_detailed_quota(attrs=None): return quota -class FakeFloatingIPPortForwarding(object): +class FakeFloatingIPPortForwarding: """Fake one or more Port forwarding""" @staticmethod @@ -1169,7 +1169,7 @@ def get_port_forwardings(port_forwardings=None, count=2, use_range=False): return mock.Mock(side_effect=port_forwardings) -class FakeL3ConntrackHelper(object): +class FakeL3ConntrackHelper: """Fake one or more L3 conntrack helper""" @staticmethod diff --git a/openstackclient/tests/unit/network/v2/test_address_group.py b/openstackclient/tests/unit/network/v2/test_address_group.py index f2e8fb0d5..411d19923 100644 --- a/openstackclient/tests/unit/network/v2/test_address_group.py +++ b/openstackclient/tests/unit/network/v2/test_address_group.py @@ -24,7 +24,7 @@ class TestAddressGroup(network_fakes.TestNetworkV2): def setUp(self): - super(TestAddressGroup, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity_client.projects @@ -57,7 +57,7 @@ class TestCreateAddressGroup(TestAddressGroup): ) def setUp(self): - super(TestCreateAddressGroup, self).setUp() + super().setUp() self.network_client.create_address_group = mock.Mock( return_value=self.new_address_group ) @@ -144,7 +144,7 @@ class TestDeleteAddressGroup(TestAddressGroup): _address_groups = network_fakes.create_address_groups(count=2) def setUp(self): - super(TestDeleteAddressGroup, self).setUp() + super().setUp() self.network_client.delete_address_group = mock.Mock(return_value=None) self.network_client.find_address_group = ( network_fakes.get_address_groups( @@ -250,7 +250,7 @@ class TestListAddressGroup(TestAddressGroup): ) def setUp(self): - super(TestListAddressGroup, self).setUp() + super().setUp() self.network_client.address_groups = mock.Mock( return_value=self.address_groups ) @@ -332,7 +332,7 @@ class TestSetAddressGroup(TestAddressGroup): _address_group = network_fakes.create_one_address_group() def setUp(self): - super(TestSetAddressGroup, self).setUp() + 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 @@ -441,7 +441,7 @@ class TestShowAddressGroup(TestAddressGroup): ) def setUp(self): - super(TestShowAddressGroup, self).setUp() + super().setUp() self.network_client.find_address_group = mock.Mock( return_value=self._address_group ) @@ -485,7 +485,7 @@ class TestUnsetAddressGroup(TestAddressGroup): _address_group = network_fakes.create_one_address_group() def setUp(self): - super(TestUnsetAddressGroup, self).setUp() + super().setUp() self.network_client.find_address_group = mock.Mock( return_value=self._address_group ) diff --git a/openstackclient/tests/unit/network/v2/test_address_scope.py b/openstackclient/tests/unit/network/v2/test_address_scope.py index 308ee2343..c3eb83d06 100644 --- a/openstackclient/tests/unit/network/v2/test_address_scope.py +++ b/openstackclient/tests/unit/network/v2/test_address_scope.py @@ -24,7 +24,7 @@ class TestAddressScope(network_fakes.TestNetworkV2): def setUp(self): - super(TestAddressScope, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity_client.projects @@ -51,7 +51,7 @@ class TestCreateAddressScope(TestAddressScope): ) def setUp(self): - super(TestCreateAddressScope, self).setUp() + super().setUp() self.network_client.create_address_scope = mock.Mock( return_value=self.new_address_scope ) @@ -159,7 +159,7 @@ class TestDeleteAddressScope(TestAddressScope): _address_scopes = network_fakes.create_address_scopes(count=2) def setUp(self): - super(TestDeleteAddressScope, self).setUp() + super().setUp() self.network_client.delete_address_scope = mock.Mock(return_value=None) self.network_client.find_address_scope = ( network_fakes.get_address_scopes( @@ -266,7 +266,7 @@ class TestListAddressScope(TestAddressScope): ) def setUp(self): - super(TestListAddressScope, self).setUp() + super().setUp() self.network_client.address_scopes = mock.Mock( return_value=self.address_scopes ) @@ -397,7 +397,7 @@ class TestSetAddressScope(TestAddressScope): _address_scope = network_fakes.create_one_address_scope() def setUp(self): - super(TestSetAddressScope, self).setUp() + 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 @@ -487,7 +487,7 @@ class TestShowAddressScope(TestAddressScope): ) def setUp(self): - super(TestShowAddressScope, self).setUp() + super().setUp() self.network_client.find_address_scope = mock.Mock( return_value=self._address_scope ) 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 1d8e22c13..f1e9e28cc 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 @@ -30,7 +30,7 @@ class TestDefaultSecurityGroupRule(network_fakes.TestNetworkV2): def setUp(self): - super(TestDefaultSecurityGroupRule, self).setUp() + super().setUp() self.app.client_manager.sdk_connection = mock.Mock() self.app.client_manager.sdk_connection.network = mock.Mock( @@ -101,7 +101,7 @@ def _setup_default_security_group_rule(self, attrs=None): ) def setUp(self): - super(TestCreateDefaultSecurityGroupRule, self).setUp() + super().setUp() # Get the command object to test self.cmd = default_security_group_rule.CreateDefaultSecurityGroupRule( @@ -842,7 +842,7 @@ class TestDeleteDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): ) def setUp(self): - super(TestDeleteDefaultSecurityGroupRule, self).setUp() + super().setUp() self.sdk_client.delete_default_security_group_rule.return_value = None @@ -977,7 +977,7 @@ class TestListDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): ) def setUp(self): - super(TestListDefaultSecurityGroupRule, self).setUp() + super().setUp() self.sdk_client.default_security_group_rules.return_value = ( self._default_sg_rules @@ -1099,7 +1099,7 @@ class TestShowDefaultSecurityGroupRule(TestDefaultSecurityGroupRule): ) def setUp(self): - super(TestShowDefaultSecurityGroupRule, self).setUp() + super().setUp() self.sdk_client.find_default_security_group_rule.return_value = ( self._default_sg_rule 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 ed6ec12dc..b7ca58751 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py @@ -46,7 +46,7 @@ class TestCreateFloatingIPCompute(compute_fakes.TestComputev2): ) def setUp(self): - super(TestCreateFloatingIPCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -90,7 +90,7 @@ class TestDeleteFloatingIPCompute(compute_fakes.TestComputev2): _floating_ips = compute_fakes.create_floating_ips(count=2) def setUp(self): - super(TestDeleteFloatingIPCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -186,7 +186,7 @@ class TestListFloatingIPCompute(compute_fakes.TestComputev2): ) def setUp(self): - super(TestListFloatingIPCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -228,7 +228,7 @@ class TestShowFloatingIPCompute(compute_fakes.TestComputev2): ) def setUp(self): - super(TestShowFloatingIPCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False 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 0cdd8f3ae..34ca21ecd 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py @@ -24,7 +24,7 @@ class TestFloatingIPNetwork(network_fakes.TestNetworkV2): def setUp(self): - super(TestFloatingIPNetwork, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity_client.projects @@ -81,7 +81,7 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork): ) def setUp(self): - super(TestCreateFloatingIPNetwork, self).setUp() + super().setUp() self.network_client.create_ip = mock.Mock( return_value=self.floating_ip @@ -301,7 +301,7 @@ class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork): floating_ips = network_fakes.FakeFloatingIP.create_floating_ips(count=2) def setUp(self): - super(TestDeleteFloatingIPNetwork, self).setUp() + super().setUp() self.network_client.delete_ip = mock.Mock(return_value=None) @@ -467,7 +467,7 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): ) def setUp(self): - super(TestListFloatingIPNetwork, self).setUp() + super().setUp() self.network_client.ips = mock.Mock(return_value=self.floating_ips) self.network_client.find_network = mock.Mock( @@ -742,7 +742,7 @@ class TestShowFloatingIPNetwork(TestFloatingIPNetwork): ) def setUp(self): - super(TestShowFloatingIPNetwork, self).setUp() + super().setUp() self.network_client.find_ip = mock.Mock(return_value=self.floating_ip) @@ -784,7 +784,7 @@ class TestSetFloatingIP(TestFloatingIPNetwork): ) def setUp(self): - super(TestSetFloatingIP, self).setUp() + 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) @@ -1035,7 +1035,7 @@ class TestUnsetFloatingIP(TestFloatingIPNetwork): ) def setUp(self): - super(TestUnsetFloatingIP, self).setUp() + 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) 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 cc07d5e69..29084c15b 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 @@ -32,7 +32,7 @@ class TestListFloatingIPPoolCompute(compute_fakes.TestComputev2): data.append((pool['name'],)) def setUp(self): - super(TestListFloatingIPPoolCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py b/openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py index 5fe53e34d..fee8f4390 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_pool_network.py @@ -19,12 +19,12 @@ class TestFloatingIPPoolNetwork(network_fakes.TestNetworkV2): def setUp(self): - super(TestFloatingIPPoolNetwork, self).setUp() + super().setUp() class TestListFloatingIPPoolNetwork(TestFloatingIPPoolNetwork): def setUp(self): - super(TestListFloatingIPPoolNetwork, self).setUp() + super().setUp() # Get the command object to test self.cmd = floating_ip_pool.ListFloatingIPPool(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 b4560f7b3..6c0e163ce 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 @@ -26,7 +26,7 @@ class TestFloatingIPPortForwarding(network_fakes.TestNetworkV2): def setUp(self): - super(TestFloatingIPPortForwarding, self).setUp() + super().setUp() self.floating_ip = ( network_fakes.FakeFloatingIP.create_one_floating_ip() @@ -38,7 +38,7 @@ def setUp(self): class TestCreateFloatingIPPortForwarding(TestFloatingIPPortForwarding): def setUp(self): - super(TestCreateFloatingIPPortForwarding, self).setUp() + super().setUp() self.new_port_forwarding = network_fakes.FakeFloatingIPPortForwarding.create_one_port_forwarding( # noqa: E501 attrs={ 'internal_port_id': self.port.id, @@ -342,7 +342,7 @@ def test_create_all_options(self): class TestDeleteFloatingIPPortForwarding(TestFloatingIPPortForwarding): def setUp(self): - super(TestDeleteFloatingIPPortForwarding, self).setUp() + super().setUp() self._port_forwarding = ( network_fakes.FakeFloatingIPPortForwarding.create_port_forwardings( count=2, @@ -465,7 +465,7 @@ class TestListFloatingIPPortForwarding(TestFloatingIPPortForwarding): ) def setUp(self): - super(TestListFloatingIPPortForwarding, self).setUp() + super().setUp() self.port_forwardings = ( network_fakes.FakeFloatingIPPortForwarding.create_port_forwardings( count=3, @@ -551,7 +551,7 @@ def test_port_forwarding_list_all_options(self): class TestSetFloatingIPPortForwarding(TestFloatingIPPortForwarding): # The Port Forwarding to set. def setUp(self): - super(TestSetFloatingIPPortForwarding, self).setUp() + super().setUp() self._port_forwarding = network_fakes.FakeFloatingIPPortForwarding.create_one_port_forwarding( # noqa: E501 attrs={ 'floatingip_id': self.floating_ip.id, @@ -672,7 +672,7 @@ class TestShowFloatingIPPortForwarding(TestFloatingIPPortForwarding): ) def setUp(self): - super(TestShowFloatingIPPortForwarding, self).setUp() + super().setUp() self._port_forwarding = network_fakes.FakeFloatingIPPortForwarding.create_one_port_forwarding( # noqa: E501 attrs={ 'floatingip_id': self.floating_ip.id, diff --git a/openstackclient/tests/unit/network/v2/test_ip_availability.py b/openstackclient/tests/unit/network/v2/test_ip_availability.py index 0175f3546..4224d1266 100644 --- a/openstackclient/tests/unit/network/v2/test_ip_availability.py +++ b/openstackclient/tests/unit/network/v2/test_ip_availability.py @@ -23,7 +23,7 @@ class TestIPAvailability(network_fakes.TestNetworkV2): def setUp(self): - super(TestIPAvailability, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity_client.projects @@ -52,7 +52,7 @@ class TestListIPAvailability(TestIPAvailability): ) def setUp(self): - super(TestListIPAvailability, self).setUp() + super().setUp() self.cmd = ip_availability.ListIPAvailability(self.app, None) self.network_client.network_ip_availabilities = mock.Mock( @@ -132,7 +132,7 @@ class TestShowIPAvailability(TestIPAvailability): ) def setUp(self): - super(TestShowIPAvailability, self).setUp() + super().setUp() self.network_client.find_network_ip_availability = mock.Mock( return_value=self._ip_availability 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 1f3d34e69..c7aadf0bf 100644 --- a/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +++ b/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py @@ -22,7 +22,7 @@ class TestConntrackHelper(network_fakes.TestNetworkV2): def setUp(self): - super(TestConntrackHelper, self).setUp() + super().setUp() self.router = network_fakes.FakeRouter.create_one_router() self.network_client.find_router = mock.Mock(return_value=self.router) @@ -30,7 +30,7 @@ def setUp(self): class TestCreateL3ConntrackHelper(TestConntrackHelper): def setUp(self): - super(TestCreateL3ConntrackHelper, self).setUp() + super().setUp() attrs = {'router_id': self.router.id} self.ct_helper = ( network_fakes.FakeL3ConntrackHelper.create_one_l3_conntrack_helper( @@ -112,7 +112,7 @@ def test_create_wrong_options(self): class TestDeleteL3ConntrackHelper(TestConntrackHelper): def setUp(self): - super(TestDeleteL3ConntrackHelper, self).setUp() + super().setUp() attrs = {'router_id': self.router.id} self.ct_helper = ( network_fakes.FakeL3ConntrackHelper.create_one_l3_conntrack_helper( @@ -156,7 +156,7 @@ def test_delete_error(self): class TestListL3ConntrackHelper(TestConntrackHelper): def setUp(self): - super(TestListL3ConntrackHelper, self).setUp() + super().setUp() attrs = {'router_id': self.router.id} ct_helpers = ( network_fakes.FakeL3ConntrackHelper.create_l3_conntrack_helpers( @@ -209,7 +209,7 @@ def test_conntrack_helpers_list(self): class TestSetL3ConntrackHelper(TestConntrackHelper): def setUp(self): - super(TestSetL3ConntrackHelper, self).setUp() + super().setUp() attrs = {'router_id': self.router.id} self.ct_helper = ( network_fakes.FakeL3ConntrackHelper.create_one_l3_conntrack_helper( @@ -265,7 +265,7 @@ def test_set_port(self): class TestShowL3ConntrackHelper(TestConntrackHelper): def setUp(self): - super(TestShowL3ConntrackHelper, self).setUp() + super().setUp() attrs = {'router_id': self.router.id} self.ct_helper = ( network_fakes.FakeL3ConntrackHelper.create_one_l3_conntrack_helper( diff --git a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py index ef7549a00..bcbc0ae7d 100644 --- a/openstackclient/tests/unit/network/v2/test_ndp_proxy.py +++ b/openstackclient/tests/unit/network/v2/test_ndp_proxy.py @@ -24,7 +24,7 @@ class TestNDPProxy(network_fakes.TestNetworkV2): def setUp(self): - super(TestNDPProxy, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity_client.projects # Get a shortcut to the DomainManager Mock @@ -40,7 +40,7 @@ def setUp(self): class TestCreateNDPProxy(TestNDPProxy): def setUp(self): - super(TestCreateNDPProxy, self).setUp() + super().setUp() attrs = {'router_id': self.router.id, 'port_id': self.port.id} self.ndp_proxy = network_fakes.create_one_ndp_proxy(attrs) self.columns = ( @@ -125,7 +125,7 @@ def test_create_all_options(self): class TestDeleteNDPProxy(TestNDPProxy): def setUp(self): - super(TestDeleteNDPProxy, self).setUp() + super().setUp() 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] @@ -183,7 +183,7 @@ def test_multi_ndp_proxies_delete(self): class TestListNDPProxy(TestNDPProxy): def setUp(self): - super(TestListNDPProxy, self).setUp() + super().setUp() attrs = {'router_id': self.router.id, 'port_id': self.port.id} ndp_proxies = network_fakes.create_ndp_proxies(attrs, count=3) self.columns = ( @@ -339,7 +339,7 @@ def test_ndp_proxy_list_project_domain(self): class TestSetNDPProxy(TestNDPProxy): def setUp(self): - super(TestSetNDPProxy, self).setUp() + 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) @@ -407,7 +407,7 @@ def test_set_description(self): class TestShowNDPProxy(TestNDPProxy): def setUp(self): - super(TestShowNDPProxy, self).setUp() + super().setUp() attrs = {'router_id': self.router.id, 'port_id': self.port.id} self.ndp_proxy = network_fakes.create_one_ndp_proxy(attrs) diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index fd67da7ba..33fe5be3a 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -29,7 +29,7 @@ # class TestNetwork(network_fakes.TestNetworkV2): def setUp(self): - super(TestNetwork, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity_client.projects @@ -112,7 +112,7 @@ class TestCreateNetworkIdentityV3(TestNetwork): ) def setUp(self): - super(TestCreateNetworkIdentityV3, self).setUp() + super().setUp() self.network_client.create_network = mock.Mock( return_value=self._network @@ -381,7 +381,7 @@ class TestCreateNetworkIdentityV2( ) def setUp(self): - super(TestCreateNetworkIdentityV2, self).setUp() + super().setUp() self.network_client.create_network = mock.Mock( return_value=self._network @@ -453,7 +453,7 @@ def test_create_with_domain_identityv2(self): class TestDeleteNetwork(TestNetwork): def setUp(self): - super(TestDeleteNetwork, self).setUp() + super().setUp() # The networks to delete self._networks = network_fakes.create_networks(count=3) @@ -591,7 +591,7 @@ class TestListNetwork(TestNetwork): ) def setUp(self): - super(TestListNetwork, self).setUp() + super().setUp() # Get the command object to test self.cmd = network.ListNetwork(self.app, None) @@ -949,7 +949,7 @@ class TestSetNetwork(TestNetwork): ) def setUp(self): - super(TestSetNetwork, self).setUp() + super().setUp() self.network_client.update_network = mock.Mock(return_value=None) self.network_client.set_tags = mock.Mock(return_value=None) @@ -1198,7 +1198,7 @@ class TestShowNetwork(TestNetwork): ) def setUp(self): - super(TestShowNetwork, self).setUp() + super().setUp() self.network_client.find_network = mock.Mock( return_value=self._network @@ -1246,7 +1246,7 @@ class TestUnsetNetwork(TestNetwork): ) def setUp(self): - super(TestUnsetNetwork, self).setUp() + super().setUp() self.network_client.update_network = mock.Mock(return_value=None) self.network_client.set_tags = mock.Mock(return_value=None) diff --git a/openstackclient/tests/unit/network/v2/test_network_agent.py b/openstackclient/tests/unit/network/v2/test_network_agent.py index 97748eeaf..15bacae58 100644 --- a/openstackclient/tests/unit/network/v2/test_network_agent.py +++ b/openstackclient/tests/unit/network/v2/test_network_agent.py @@ -24,7 +24,7 @@ class TestNetworkAgent(network_fakes.TestNetworkV2): def setUp(self): - super(TestNetworkAgent, self).setUp() + super().setUp() class TestAddNetworkToAgent(TestNetworkAgent): @@ -32,7 +32,7 @@ class TestAddNetworkToAgent(TestNetworkAgent): agent = network_fakes.create_one_network_agent() def setUp(self): - super(TestAddNetworkToAgent, self).setUp() + super().setUp() self.network_client.get_agent = mock.Mock(return_value=self.agent) self.network_client.find_network = mock.Mock(return_value=self.net) @@ -74,7 +74,7 @@ class TestAddRouterAgent(TestNetworkAgent): _agent = network_fakes.create_one_network_agent() def setUp(self): - super(TestAddRouterAgent, self).setUp() + super().setUp() self.network_client.get_agent = mock.Mock(return_value=self._agent) self.network_client.find_router = mock.Mock(return_value=self._router) @@ -119,7 +119,7 @@ class TestDeleteNetworkAgent(TestNetworkAgent): network_agents = network_fakes.create_network_agents(count=2) def setUp(self): - super(TestDeleteNetworkAgent, self).setUp() + super().setUp() self.network_client.delete_agent = mock.Mock(return_value=None) # Get the command object to test @@ -218,7 +218,7 @@ class TestListNetworkAgent(TestNetworkAgent): ) def setUp(self): - super(TestListNetworkAgent, self).setUp() + super().setUp() self.network_client.agents = mock.Mock( return_value=self.network_agents ) @@ -369,7 +369,7 @@ class TestRemoveNetworkFromAgent(TestNetworkAgent): agent = network_fakes.create_one_network_agent() def setUp(self): - super(TestRemoveNetworkFromAgent, self).setUp() + super().setUp() self.network_client.get_agent = mock.Mock(return_value=self.agent) self.network_client.find_network = mock.Mock(return_value=self.net) @@ -426,7 +426,7 @@ class TestRemoveRouterAgent(TestNetworkAgent): _agent = network_fakes.create_one_network_agent() def setUp(self): - super(TestRemoveRouterAgent, self).setUp() + super().setUp() self.network_client.get_agent = mock.Mock(return_value=self._agent) self.network_client.find_router = mock.Mock(return_value=self._router) @@ -471,7 +471,7 @@ class TestSetNetworkAgent(TestNetworkAgent): _network_agent = network_fakes.create_one_network_agent() def setUp(self): - super(TestSetNetworkAgent, self).setUp() + super().setUp() self.network_client.update_agent = mock.Mock(return_value=None) self.network_client.get_agent = mock.Mock( return_value=self._network_agent @@ -587,7 +587,7 @@ class TestShowNetworkAgent(TestNetworkAgent): ) def setUp(self): - super(TestShowNetworkAgent, self).setUp() + super().setUp() self.network_client.get_agent = mock.Mock( return_value=self._network_agent ) 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 76305869b..20c645bd4 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 @@ -22,7 +22,7 @@ class TestAutoAllocatedTopology(network_fakes.TestNetworkV2): def setUp(self): - super(TestAutoAllocatedTopology, self).setUp() + super().setUp() self.projects_mock = self.identity_client.projects @@ -45,7 +45,7 @@ class TestCreateAutoAllocatedTopology(TestAutoAllocatedTopology): ) def setUp(self): - super(TestCreateAutoAllocatedTopology, self).setUp() + super().setUp() self.cmd = network_auto_allocated_topology.CreateAutoAllocatedTopology( self.app, None @@ -150,7 +150,7 @@ class TestValidateAutoAllocatedTopology(TestAutoAllocatedTopology): ) def setUp(self): - super(TestValidateAutoAllocatedTopology, self).setUp() + super().setUp() self.cmd = network_auto_allocated_topology.CreateAutoAllocatedTopology( self.app, None @@ -223,7 +223,7 @@ class TestDeleteAutoAllocatedTopology(TestAutoAllocatedTopology): ) def setUp(self): - super(TestDeleteAutoAllocatedTopology, self).setUp() + super().setUp() self.cmd = network_auto_allocated_topology.DeleteAutoAllocatedTopology( self.app, None diff --git a/openstackclient/tests/unit/network/v2/test_network_compute.py b/openstackclient/tests/unit/network/v2/test_network_compute.py index ebe37243a..651bf72a5 100644 --- a/openstackclient/tests/unit/network/v2/test_network_compute.py +++ b/openstackclient/tests/unit/network/v2/test_network_compute.py @@ -101,7 +101,7 @@ class TestCreateNetworkCompute(compute_fakes.TestComputev2): ) def setUp(self): - super(TestCreateNetworkCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -168,7 +168,7 @@ def test_network_create_default_options(self, net_mock): @mock.patch('openstackclient.api.compute_v2.APIv2.network_delete') class TestDeleteNetworkCompute(compute_fakes.TestComputev2): def setUp(self): - super(TestDeleteNetworkCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -267,7 +267,7 @@ class TestListNetworkCompute(compute_fakes.TestComputev2): ) def setUp(self): - super(TestListNetworkCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -366,7 +366,7 @@ class TestShowNetworkCompute(compute_fakes.TestComputev2): ) def setUp(self): - super(TestShowNetworkCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False diff --git a/openstackclient/tests/unit/network/v2/test_network_flavor.py b/openstackclient/tests/unit/network/v2/test_network_flavor.py index a7973dd54..cfbe1f7b0 100644 --- a/openstackclient/tests/unit/network/v2/test_network_flavor.py +++ b/openstackclient/tests/unit/network/v2/test_network_flavor.py @@ -26,7 +26,7 @@ class TestNetworkFlavor(network_fakes.TestNetworkV2): def setUp(self): - super(TestNetworkFlavor, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity_client.projects @@ -39,7 +39,7 @@ class TestAddNetworkFlavorToProfile(TestNetworkFlavor): service_profile = network_fakes.create_one_service_profile() def setUp(self): - super(TestAddNetworkFlavorToProfile, self).setUp() + super().setUp() self.network_client.find_flavor = mock.Mock( return_value=self.network_flavor @@ -101,7 +101,7 @@ class TestCreateNetworkFlavor(TestNetworkFlavor): ) def setUp(self): - super(TestCreateNetworkFlavor, self).setUp() + super().setUp() self.network_client.create_flavor = mock.Mock( return_value=self.new_network_flavor ) @@ -217,7 +217,7 @@ class TestDeleteNetworkFlavor(TestNetworkFlavor): _network_flavors = network_fakes.create_flavor(count=2) def setUp(self): - super(TestDeleteNetworkFlavor, self).setUp() + super().setUp() self.network_client.delete_flavor = mock.Mock(return_value=None) self.network_client.find_flavor = network_fakes.get_flavor( network_flavors=self._network_flavors @@ -322,7 +322,7 @@ class TestListNetworkFlavor(TestNetworkFlavor): ) def setUp(self): - super(TestListNetworkFlavor, self).setUp() + super().setUp() self.network_client.flavors = mock.Mock( return_value=self._network_flavors ) @@ -347,7 +347,7 @@ class TestRemoveNetworkFlavorFromProfile(TestNetworkFlavor): service_profile = network_fakes.create_one_service_profile() def setUp(self): - super(TestRemoveNetworkFlavorFromProfile, self).setUp() + super().setUp() self.network_client.find_flavor = mock.Mock( return_value=self.network_flavor ) @@ -411,7 +411,7 @@ class TestShowNetworkFlavor(TestNetworkFlavor): ) def setUp(self): - super(TestShowNetworkFlavor, self).setUp() + super().setUp() self.network_client.find_flavor = mock.Mock( return_value=self.new_network_flavor ) @@ -455,7 +455,7 @@ class TestSetNetworkFlavor(TestNetworkFlavor): new_network_flavor = network_fakes.create_one_network_flavor() def setUp(self): - super(TestSetNetworkFlavor, self).setUp() + 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 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 a54045aa6..44cc4f493 100644 --- a/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py +++ b/openstackclient/tests/unit/network/v2/test_network_flavor_profile.py @@ -21,7 +21,7 @@ class TestFlavorProfile(network_fakes.TestNetworkV2): def setUp(self): - super(TestFlavorProfile, self).setUp() + super().setUp() # Get the ProjectManager Mock self.projects_mock = self.identity_client.projects @@ -53,7 +53,7 @@ class TestCreateFlavorProfile(TestFlavorProfile): ) def setUp(self): - super(TestCreateFlavorProfile, self).setUp() + super().setUp() self.network_client.create_service_profile = mock.Mock( return_value=self.new_flavor_profile ) @@ -227,7 +227,7 @@ class TestDeleteFlavorProfile(TestFlavorProfile): _network_flavor_profiles = network_fakes.create_service_profile(count=2) def setUp(self): - super(TestDeleteFlavorProfile, self).setUp() + super().setUp() self.network_client.delete_service_profile = mock.Mock( return_value=None ) @@ -346,7 +346,7 @@ class TestListFlavorProfile(TestFlavorProfile): ) def setUp(self): - super(TestListFlavorProfile, self).setUp() + super().setUp() self.network_client.service_profiles = mock.Mock( return_value=self._network_flavor_profiles ) @@ -389,7 +389,7 @@ class TestShowFlavorProfile(TestFlavorProfile): ) def setUp(self): - super(TestShowFlavorProfile, self).setUp() + super().setUp() self.network_client.find_service_profile = mock.Mock( return_value=self.network_flavor_profile ) @@ -422,7 +422,7 @@ class TestSetFlavorProfile(TestFlavorProfile): network_flavor_profile = network_fakes.create_one_service_profile() def setUp(self): - super(TestSetFlavorProfile, self).setUp() + super().setUp() self.network_client.update_service_profile = mock.Mock( return_value=None ) diff --git a/openstackclient/tests/unit/network/v2/test_network_meter.py b/openstackclient/tests/unit/network/v2/test_network_meter.py index af0513678..a92035d6d 100644 --- a/openstackclient/tests/unit/network/v2/test_network_meter.py +++ b/openstackclient/tests/unit/network/v2/test_network_meter.py @@ -26,7 +26,7 @@ class TestMeter(network_fakes.TestNetworkV2): def setUp(self): - super(TestMeter, self).setUp() + super().setUp() self.projects_mock = self.identity_client.projects self.domains_mock = self.identity_client.domains @@ -54,7 +54,7 @@ class TestCreateMeter(TestMeter): ) def setUp(self): - super(TestCreateMeter, self).setUp() + super().setUp() self.network_client.create_metering_label = mock.Mock( return_value=self.new_meter ) @@ -128,7 +128,7 @@ def test_create_all_options(self): class TestDeleteMeter(TestMeter): def setUp(self): - super(TestDeleteMeter, self).setUp() + super().setUp() self.meter_list = network_fakes.FakeNetworkMeter.create_meter(count=2) @@ -238,7 +238,7 @@ class TestListMeter(TestMeter): ) def setUp(self): - super(TestListMeter, self).setUp() + super().setUp() self.network_client.metering_labels = mock.Mock( return_value=self.meter_list @@ -278,7 +278,7 @@ class TestShowMeter(TestMeter): ) def setUp(self): - super(TestShowMeter, self).setUp() + super().setUp() self.cmd = network_meter.ShowMeter(self.app, None) 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 8ee020809..e90ed05f3 100644 --- a/openstackclient/tests/unit/network/v2/test_network_meter_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_meter_rule.py @@ -26,7 +26,7 @@ class TestMeterRule(network_fakes.TestNetworkV2): def setUp(self): - super(TestMeterRule, self).setUp() + super().setUp() self.projects_mock = self.identity_client.projects self.domains_mock = self.identity_client.domains @@ -60,7 +60,7 @@ class TestCreateMeterRule(TestMeterRule): ) def setUp(self): - super(TestCreateMeterRule, self).setUp() + super().setUp() fake_meter = network_fakes.FakeNetworkMeter.create_one_meter( {'id': self.new_rule.metering_label_id} ) @@ -142,7 +142,7 @@ def test_create_all_options(self): class TestDeleteMeterRule(TestMeterRule): def setUp(self): - super(TestDeleteMeterRule, self).setUp() + super().setUp() self.rule_list = network_fakes.FakeNetworkMeterRule.create_meter_rule( count=2 ) @@ -260,7 +260,7 @@ class TestListMeterRule(TestMeterRule): ) def setUp(self): - super(TestListMeterRule, self).setUp() + super().setUp() self.network_client.metering_label_rules = mock.Mock( return_value=self.rule_list @@ -307,7 +307,7 @@ class TestShowMeterRule(TestMeterRule): ) def setUp(self): - super(TestShowMeterRule, self).setUp() + super().setUp() self.cmd = network_meter_rule.ShowMeterRule(self.app, None) 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 8129ff202..0a7cbb980 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_policy.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py @@ -26,7 +26,7 @@ class TestQosPolicy(network_fakes.TestNetworkV2): def setUp(self): - super(TestQosPolicy, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.app.client_manager.identity.projects @@ -61,7 +61,7 @@ class TestCreateNetworkQosPolicy(TestQosPolicy): ) def setUp(self): - super(TestCreateNetworkQosPolicy, self).setUp() + super().setUp() self.network_client.create_qos_policy = mock.Mock( return_value=self.new_qos_policy ) @@ -163,7 +163,7 @@ class TestDeleteNetworkQosPolicy(TestQosPolicy): ) def setUp(self): - super(TestDeleteNetworkQosPolicy, self).setUp() + 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( @@ -268,7 +268,7 @@ class TestListNetworkQosPolicy(TestQosPolicy): ) def setUp(self): - super(TestListNetworkQosPolicy, self).setUp() + super().setUp() self.network_client.qos_policies = mock.Mock( return_value=self.qos_policies ) @@ -348,7 +348,7 @@ class TestSetNetworkQosPolicy(TestQosPolicy): _qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() def setUp(self): - super(TestSetNetworkQosPolicy, self).setUp() + 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 @@ -449,7 +449,7 @@ class TestShowNetworkQosPolicy(TestQosPolicy): ) def setUp(self): - super(TestShowNetworkQosPolicy, self).setUp() + super().setUp() self.network_client.find_qos_policy = mock.Mock( return_value=self._qos_policy ) 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 8ecf4fa8b..1f6fe093e 100644 --- a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py +++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py @@ -53,7 +53,7 @@ class TestNetworkQosRule(network_fakes.TestNetworkV2): def setUp(self): - super(TestNetworkQosRule, self).setUp() + super().setUp() self.qos_policy = ( network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() ) @@ -67,7 +67,7 @@ def test_check_type_parameters(self): pass def setUp(self): - super(TestCreateNetworkQosRuleMinimumBandwidth, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_BANDWIDTH, @@ -174,7 +174,7 @@ def test_check_type_parameters(self): pass def setUp(self): - super(TestCreateNetworkQosRuleMinimumPacketRate, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_PACKET_RATE, @@ -281,7 +281,7 @@ def test_check_type_parameters(self): pass def setUp(self): - super(TestCreateNetworkQosRuleDSCPMarking, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_DSCP_MARKING, @@ -379,7 +379,7 @@ def test_check_type_parameters(self): pass def setUp(self): - super(TestCreateNetworkQosRuleBandwidtLimit, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, @@ -540,7 +540,7 @@ def test_create_wrong_options(self): class TestDeleteNetworkQosRuleMinimumBandwidth(TestNetworkQosRule): def setUp(self): - super(TestDeleteNetworkQosRuleMinimumBandwidth, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_BANDWIDTH, @@ -598,16 +598,16 @@ def test_qos_policy_delete_error(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) except exceptions.CommandError as e: - msg = 'Failed to delete Network QoS rule ID "%(rule)s": %(e)s' % { - 'rule': self.new_rule.id, - 'e': 'Error message', - } + msg = 'Failed to delete Network QoS rule ID "{rule}": {e}'.format( + rule=self.new_rule.id, + e='Error message', + ) self.assertEqual(msg, str(e)) class TestDeleteNetworkQosRuleMinimumPacketRate(TestNetworkQosRule): def setUp(self): - super(TestDeleteNetworkQosRuleMinimumPacketRate, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_PACKET_RATE, @@ -665,16 +665,16 @@ def test_qos_policy_delete_error(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) except exceptions.CommandError as e: - msg = 'Failed to delete Network QoS rule ID "%(rule)s": %(e)s' % { - 'rule': self.new_rule.id, - 'e': 'Error message', - } + msg = 'Failed to delete Network QoS rule ID "{rule}": {e}'.format( + rule=self.new_rule.id, + e='Error message', + ) self.assertEqual(msg, str(e)) class TestDeleteNetworkQosRuleDSCPMarking(TestNetworkQosRule): def setUp(self): - super(TestDeleteNetworkQosRuleDSCPMarking, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_DSCP_MARKING, @@ -732,16 +732,16 @@ def test_qos_policy_delete_error(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) except exceptions.CommandError as e: - msg = 'Failed to delete Network QoS rule ID "%(rule)s": %(e)s' % { - 'rule': self.new_rule.id, - 'e': 'Error message', - } + msg = 'Failed to delete Network QoS rule ID "{rule}": {e}'.format( + rule=self.new_rule.id, + e='Error message', + ) self.assertEqual(msg, str(e)) class TestDeleteNetworkQosRuleBandwidthLimit(TestNetworkQosRule): def setUp(self): - super(TestDeleteNetworkQosRuleBandwidthLimit, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, @@ -799,16 +799,16 @@ def test_qos_policy_delete_error(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) except exceptions.CommandError as e: - msg = 'Failed to delete Network QoS rule ID "%(rule)s": %(e)s' % { - 'rule': self.new_rule.id, - 'e': 'Error message', - } + msg = 'Failed to delete Network QoS rule ID "{rule}": {e}'.format( + rule=self.new_rule.id, + e='Error message', + ) self.assertEqual(msg, str(e)) class TestSetNetworkQosRuleMinimumBandwidth(TestNetworkQosRule): def setUp(self): - super(TestSetNetworkQosRuleMinimumBandwidth, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_BANDWIDTH, @@ -912,7 +912,7 @@ def test_set_wrong_options(self): class TestSetNetworkQosRuleMinimumPacketRate(TestNetworkQosRule): def setUp(self): - super(TestSetNetworkQosRuleMinimumPacketRate, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_PACKET_RATE, @@ -1016,7 +1016,7 @@ def test_set_wrong_options(self): class TestSetNetworkQosRuleDSCPMarking(TestNetworkQosRule): def setUp(self): - super(TestSetNetworkQosRuleDSCPMarking, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_DSCP_MARKING, @@ -1120,7 +1120,7 @@ def test_set_wrong_options(self): class TestSetNetworkQosRuleBandwidthLimit(TestNetworkQosRule): def setUp(self): - super(TestSetNetworkQosRuleBandwidthLimit, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, @@ -1297,7 +1297,7 @@ def test_set_wrong_options(self): class TestListNetworkQosRule(TestNetworkQosRule): def setUp(self): - super(TestListNetworkQosRule, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_BANDWIDTH, @@ -1387,7 +1387,7 @@ def test_qos_rule_list(self): class TestShowNetworkQosRuleMinimumBandwidth(TestNetworkQosRule): def setUp(self): - super(TestShowNetworkQosRuleMinimumBandwidth, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_BANDWIDTH, @@ -1455,7 +1455,7 @@ def test_show_all_options(self): class TestShowNetworkQosRuleMinimumPacketRate(TestNetworkQosRule): def setUp(self): - super(TestShowNetworkQosRuleMinimumPacketRate, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_MINIMUM_PACKET_RATE, @@ -1523,7 +1523,7 @@ def test_show_all_options(self): class TestShowNetworkQosDSCPMarking(TestNetworkQosRule): def setUp(self): - super(TestShowNetworkQosDSCPMarking, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_DSCP_MARKING, @@ -1589,7 +1589,7 @@ def test_show_all_options(self): class TestShowNetworkQosBandwidthLimit(TestNetworkQosRule): def setUp(self): - super(TestShowNetworkQosBandwidthLimit, self).setUp() + super().setUp() attrs = { 'qos_policy_id': self.qos_policy.id, 'type': RULE_TYPE_BANDWIDTH_LIMIT, 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 c42a00af7..4838df2e8 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 @@ -22,7 +22,7 @@ class TestNetworkQosRuleType(network_fakes.TestNetworkV2): def setUp(self): - super(TestNetworkQosRuleType, self).setUp() + super().setUp() class TestShowNetworkQosRuleType(TestNetworkQosRuleType): @@ -35,7 +35,7 @@ class TestShowNetworkQosRuleType(TestNetworkQosRuleType): data = [qos_rule_type.drivers, qos_rule_type.type] def setUp(self): - super(TestShowNetworkQosRuleType, self).setUp() + super().setUp() self.network_client.get_qos_rule_type = mock.Mock( return_value=self.qos_rule_type ) @@ -85,7 +85,7 @@ class TestListNetworkQosRuleType(TestNetworkQosRuleType): data.append((qos_rule_type.type,)) def setUp(self): - super(TestListNetworkQosRuleType, self).setUp() + super().setUp() self.network_client.qos_rule_types = mock.Mock( return_value=self.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 e40c1df41..c13b72bb4 100644 --- a/openstackclient/tests/unit/network/v2/test_network_rbac.py +++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py @@ -25,7 +25,7 @@ class TestNetworkRBAC(network_fakes.TestNetworkV2): def setUp(self): - super(TestNetworkRBAC, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity_client.projects @@ -67,7 +67,7 @@ class TestCreateNetworkRBAC(TestNetworkRBAC): ] def setUp(self): - super(TestCreateNetworkRBAC, self).setUp() + super().setUp() # Get the command object to test self.cmd = network_rbac.CreateNetworkRBAC(self.app, None) @@ -342,7 +342,7 @@ class TestDeleteNetworkRBAC(TestNetworkRBAC): rbac_policies = network_fakes.create_network_rbacs(count=2) def setUp(self): - super(TestDeleteNetworkRBAC, self).setUp() + super().setUp() self.network_client.delete_rbac_policy = mock.Mock(return_value=None) self.network_client.find_rbac_policy = network_fakes.get_network_rbacs( rbac_policies=self.rbac_policies @@ -457,7 +457,7 @@ class TestListNetworkRABC(TestNetworkRBAC): ) def setUp(self): - super(TestListNetworkRABC, self).setUp() + super().setUp() # Get the command object to test self.cmd = network_rbac.ListNetworkRBAC(self.app, None) @@ -559,7 +559,7 @@ class TestSetNetworkRBAC(TestNetworkRBAC): ) def setUp(self): - super(TestSetNetworkRBAC, self).setUp() + super().setUp() # Get the command object to test self.cmd = network_rbac.SetNetworkRBAC(self.app, None) @@ -634,7 +634,7 @@ class TestShowNetworkRBAC(TestNetworkRBAC): ] def setUp(self): - super(TestShowNetworkRBAC, self).setUp() + super().setUp() # Get the command object to test self.cmd = network_rbac.ShowNetworkRBAC(self.app, None) diff --git a/openstackclient/tests/unit/network/v2/test_network_segment.py b/openstackclient/tests/unit/network/v2/test_network_segment.py index 38342a2c3..936a20aef 100644 --- a/openstackclient/tests/unit/network/v2/test_network_segment.py +++ b/openstackclient/tests/unit/network/v2/test_network_segment.py @@ -23,7 +23,7 @@ class TestNetworkSegment(network_fakes.TestNetworkV2): def setUp(self): - super(TestNetworkSegment, self).setUp() + super().setUp() class TestCreateNetworkSegment(TestNetworkSegment): @@ -56,7 +56,7 @@ class TestCreateNetworkSegment(TestNetworkSegment): ) def setUp(self): - super(TestCreateNetworkSegment, self).setUp() + super().setUp() self.network_client.create_segment = mock.Mock( return_value=self._network_segment @@ -170,7 +170,7 @@ class TestDeleteNetworkSegment(TestNetworkSegment): _network_segments = network_fakes.create_network_segments() def setUp(self): - super(TestDeleteNetworkSegment, self).setUp() + super().setUp() self.network_client.delete_segment = mock.Mock(return_value=None) self.network_client.find_segment = mock.Mock( @@ -286,7 +286,7 @@ class TestListNetworkSegment(TestNetworkSegment): ) def setUp(self): - super(TestListNetworkSegment, self).setUp() + super().setUp() # Get the command object to test self.cmd = network_segment.ListNetworkSegment(self.app, None) @@ -350,7 +350,7 @@ class TestSetNetworkSegment(TestNetworkSegment): _network_segment = network_fakes.create_one_network_segment() def setUp(self): - super(TestSetNetworkSegment, self).setUp() + super().setUp() self.network_client.find_segment = mock.Mock( return_value=self._network_segment @@ -430,7 +430,7 @@ class TestShowNetworkSegment(TestNetworkSegment): ) def setUp(self): - super(TestShowNetworkSegment, self).setUp() + super().setUp() self.network_client.find_segment = mock.Mock( return_value=self._network_segment 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 e33d4d592..d5e406f39 100644 --- a/openstackclient/tests/unit/network/v2/test_network_segment_range.py +++ b/openstackclient/tests/unit/network/v2/test_network_segment_range.py @@ -40,7 +40,7 @@ def test__get_ranges(self): class TestNetworkSegmentRange(network_fakes.TestNetworkV2): def setUp(self): - super(TestNetworkSegmentRange, self).setUp() + super().setUp() class TestCreateNetworkSegmentRange(TestNetworkSegmentRange): @@ -76,7 +76,7 @@ class TestCreateNetworkSegmentRange(TestNetworkSegmentRange): ) def setUp(self): - super(TestCreateNetworkSegmentRange, self).setUp() + super().setUp() self.network_client.create_network_segment_range = mock.Mock( return_value=self._network_segment_range @@ -349,7 +349,7 @@ class TestDeleteNetworkSegmentRange(TestNetworkSegmentRange): _network_segment_ranges = network_fakes.create_network_segment_ranges() def setUp(self): - super(TestDeleteNetworkSegmentRange, self).setUp() + super().setUp() self.network_client.delete_network_segment_range = mock.Mock( return_value=None @@ -491,7 +491,7 @@ class TestListNetworkSegmentRange(TestNetworkSegmentRange): ) def setUp(self): - super(TestListNetworkSegmentRange, self).setUp() + super().setUp() self.network_client.network_segment_ranges = mock.Mock( return_value=self._network_segment_ranges @@ -565,7 +565,7 @@ class TestSetNetworkSegmentRange(TestNetworkSegmentRange): ) def setUp(self): - super(TestSetNetworkSegmentRange, self).setUp() + super().setUp() self.network_client.find_network_segment_range = mock.Mock( return_value=self._network_segment_range @@ -660,7 +660,7 @@ class TestShowNetworkSegmentRange(TestNetworkSegmentRange): ) def setUp(self): - super(TestShowNetworkSegmentRange, self).setUp() + super().setUp() self.network_client.find_network_segment_range = mock.Mock( return_value=self._network_segment_range 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 c08064fe7..9f61bce59 100644 --- a/openstackclient/tests/unit/network/v2/test_network_service_provider.py +++ b/openstackclient/tests/unit/network/v2/test_network_service_provider.py @@ -23,7 +23,7 @@ class TestNetworkServiceProvider(fakes.TestNetworkV2): def setUp(self): - super(TestNetworkServiceProvider, self).setUp() + super().setUp() class TestListNetworkServiceProvider(TestNetworkServiceProvider): @@ -51,7 +51,7 @@ class TestListNetworkServiceProvider(TestNetworkServiceProvider): ) def setUp(self): - super(TestListNetworkServiceProvider, self).setUp() + super().setUp() self.network_client.service_providers = mock.Mock( return_value=self.provider_list ) diff --git a/openstackclient/tests/unit/network/v2/test_network_trunk.py b/openstackclient/tests/unit/network/v2/test_network_trunk.py index 47952d988..153d7f995 100644 --- a/openstackclient/tests/unit/network/v2/test_network_trunk.py +++ b/openstackclient/tests/unit/network/v2/test_network_trunk.py @@ -233,7 +233,7 @@ def test_create_network_trunk_subports_without_optional_keys(self): '--parent-port', self.new_trunk.port_id, '--subport', - 'port=%(port)s' % {'port': subport['port_id']}, + 'port={port}'.format(port=subport['port_id']), self.new_trunk.name, ] verifylist = [ @@ -712,7 +712,7 @@ def test_set_network_trunk_subports_without_optional_keys(self): subport.pop('segmentation_id') arglist = [ '--subport', - 'port=%(port)s' % {'port': subport['port_id']}, + 'port={port}'.format(port=subport['port_id']), self._trunk['name'], ] verifylist = [ diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 5a331635f..d47f86615 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -30,7 +30,7 @@ class TestPort(network_fakes.TestNetworkV2): def setUp(self): - super(TestPort, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity_client.projects @@ -127,7 +127,7 @@ class TestCreatePort(TestPort): columns, data = TestPort._get_common_cols_data(_port) def setUp(self): - super(TestCreatePort, self).setUp() + super().setUp() self.network_client.create_port = mock.Mock(return_value=self._port) self.network_client.set_tags = mock.Mock(return_value=None) @@ -1118,7 +1118,7 @@ class TestDeletePort(TestPort): _ports = network_fakes.create_ports(count=2) def setUp(self): - super(TestDeletePort, self).setUp() + super().setUp() self.network_client.delete_port = mock.Mock(return_value=None) self.network_client.find_port = network_fakes.get_ports( @@ -1240,7 +1240,7 @@ class TestListPort(TestPort): ) def setUp(self): - super(TestListPort, self).setUp() + super().setUp() self.network_client.ports = mock.Mock(return_value=self._ports) fake_router = network_fakes.FakeRouter.create_one_router( @@ -1490,7 +1490,7 @@ def test_port_list_fixed_ip_opts(self): ip_address = self._ports[0].fixed_ips[0]['ip_address'] arglist = [ '--fixed-ip', - "subnet=%s,ip-address=%s" % (subnet_id, ip_address), + f"subnet={subnet_id},ip-address={ip_address}", ] verifylist = [ ('fixed_ip', [{'subnet': subnet_id, 'ip-address': ip_address}]) @@ -1720,7 +1720,7 @@ class TestSetPort(TestPort): _port = network_fakes.create_one_port({'tags': ['green', 'red']}) def setUp(self): - super(TestSetPort, self).setUp() + super().setUp() self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet() self.network_client.find_subnet = mock.Mock( return_value=self.fake_subnet @@ -2507,7 +2507,7 @@ class TestShowPort(TestPort): columns, data = TestPort._get_common_cols_data(_port) def setUp(self): - super(TestShowPort, self).setUp() + super().setUp() self.network_client.find_port = mock.Mock(return_value=self._port) @@ -2547,7 +2547,7 @@ def test_show_all_options(self): class TestUnsetPort(TestPort): def setUp(self): - super(TestUnsetPort, self).setUp() + super().setUp() self._testport = network_fakes.create_one_port( { 'fixed_ips': [ diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 6e645996f..8f8704b90 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -25,7 +25,7 @@ class TestRouter(network_fakes.TestNetworkV2): def setUp(self): - super(TestRouter, self).setUp() + super().setUp() self.projects_mock = self.identity_client.projects @@ -39,7 +39,7 @@ class TestAddPortToRouter(TestRouter): ) def setUp(self): - super(TestAddPortToRouter, self).setUp() + super().setUp() self.network_client.find_router = mock.Mock(return_value=self._router) self.network_client.find_port = mock.Mock(return_value=self._port) @@ -89,7 +89,7 @@ class TestAddSubnetToRouter(TestRouter): ) def setUp(self): - super(TestAddSubnetToRouter, self).setUp() + super().setUp() self.network_client.find_router = mock.Mock(return_value=self._router) self.network_client.find_subnet = mock.Mock(return_value=self._subnet) @@ -164,7 +164,7 @@ class TestCreateRouter(TestRouter): ) def setUp(self): - super(TestCreateRouter, self).setUp() + super().setUp() self.network_client.create_router = mock.Mock( return_value=self.new_router @@ -484,7 +484,7 @@ class TestDeleteRouter(TestRouter): _routers = network_fakes.FakeRouter.create_routers(count=2) def setUp(self): - super(TestDeleteRouter, self).setUp() + super().setUp() self.network_client.delete_router = mock.Mock(return_value=None) @@ -642,7 +642,7 @@ class TestListRouter(TestRouter): ) def setUp(self): - super(TestListRouter, self).setUp() + super().setUp() # Get the command object to test self.cmd = router.ListRouter(self.app, None) @@ -905,7 +905,7 @@ class TestRemovePortFromRouter(TestRouter): ) def setUp(self): - super(TestRemovePortFromRouter, self).setUp() + super().setUp() self.network_client.find_router = mock.Mock(return_value=self._router) self.network_client.find_port = mock.Mock(return_value=self._port) @@ -952,7 +952,7 @@ class TestRemoveSubnetFromRouter(TestRouter): ) def setUp(self): - super(TestRemoveSubnetFromRouter, self).setUp() + super().setUp() self.network_client.find_router = mock.Mock(return_value=self._router) self.network_client.find_subnet = mock.Mock(return_value=self._subnet) @@ -993,7 +993,7 @@ class TestAddExtraRoutesToRouter(TestRouter): _router = network_fakes.FakeRouter.create_one_router() def setUp(self): - super(TestAddExtraRoutesToRouter, self).setUp() + super().setUp() self.network_client.add_extra_routes_to_router = mock.Mock( return_value=self._router ) @@ -1082,7 +1082,7 @@ class TestRemoveExtraRoutesFromRouter(TestRouter): _router = network_fakes.FakeRouter.create_one_router() def setUp(self): - super(TestRemoveExtraRoutesFromRouter, self).setUp() + super().setUp() self.network_client.remove_extra_routes_from_router = mock.Mock( return_value=self._router ) @@ -1180,7 +1180,7 @@ class TestSetRouter(TestRouter): _extensions = {'fake': network_fakes.create_one_extension()} def setUp(self): - super(TestSetRouter, self).setUp() + 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) @@ -1710,7 +1710,7 @@ class TestShowRouter(TestRouter): ) def setUp(self): - super(TestShowRouter, self).setUp() + super().setUp() self.network_client.find_router = mock.Mock(return_value=self._router) self.network_client.ports = mock.Mock(return_value=[self._port]) @@ -1793,7 +1793,7 @@ def test_show_no_extra_route_extension(self): class TestUnsetRouter(TestRouter): def setUp(self): - super(TestUnsetRouter, self).setUp() + super().setUp() self.fake_network = network_fakes.create_one_network() self.fake_qos_policy = ( network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() @@ -2138,9 +2138,9 @@ def test_create_multiple_gateways(self): "--external-gateway", self._second_network.id, '--fixed-ip', - 'subnet={},ip-address=10.0.1.1'.format(self._subnet.id), + f'subnet={self._subnet.id},ip-address=10.0.1.1', '--fixed-ip', - 'subnet={},ip-address=10.0.1.2'.format(self._subnet.id), + f'subnet={self._subnet.id},ip-address=10.0.1.2', ] verifylist = [ ('name', self._router.name), @@ -2242,9 +2242,9 @@ def test_update_multiple_gateways(self): "--external-gateway", self._second_network.id, '--fixed-ip', - 'subnet={},ip-address=10.0.1.1'.format(self._subnet.id), + f'subnet={self._subnet.id},ip-address=10.0.1.1', '--fixed-ip', - 'subnet={},ip-address=10.0.1.2'.format(self._subnet.id), + f'subnet={self._subnet.id},ip-address=10.0.1.2', "--no-qos-policy", ] verifylist = [ @@ -2327,7 +2327,7 @@ def test_add_gateway_network_fixed_ip(self): self._router.name, self._network.id, '--fixed-ip', - 'subnet={},ip-address=10.0.1.1'.format(self._subnet.id), + f'subnet={self._subnet.id},ip-address=10.0.1.1', ] verifylist = [ ('router', self._router.name), @@ -2360,9 +2360,9 @@ def test_add_gateway_network_multiple_fixed_ips(self): self._router.name, self._network.id, '--fixed-ip', - 'subnet={},ip-address=10.0.1.1'.format(self._subnet.id), + f'subnet={self._subnet.id},ip-address=10.0.1.1', '--fixed-ip', - 'subnet={},ip-address=10.0.1.2'.format(self._subnet.id), + f'subnet={self._subnet.id},ip-address=10.0.1.2', ] verifylist = [ ('router', self._router.name), @@ -2453,7 +2453,7 @@ def test_remove_gateway_network_fixed_ip(self): self._router.name, self._network.id, '--fixed-ip', - 'subnet={},ip-address=10.0.1.1'.format(self._subnet.id), + f'subnet={self._subnet.id},ip-address=10.0.1.1', ] verifylist = [ ('router', self._router.name), @@ -2486,9 +2486,9 @@ def test_remove_gateway_network_multiple_fixed_ips(self): self._router.name, self._network.id, '--fixed-ip', - 'subnet={},ip-address=10.0.1.1'.format(self._subnet.id), + f'subnet={self._subnet.id},ip-address=10.0.1.1', '--fixed-ip', - 'subnet={},ip-address=10.0.1.2'.format(self._subnet.id), + f'subnet={self._subnet.id},ip-address=10.0.1.2', ] verifylist = [ ('router', self._router.name), 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 f9ea640ff..806878810 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_compute.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_compute.py @@ -47,7 +47,7 @@ class TestCreateSecurityGroupCompute(compute_fakes.TestComputev2): ) def setUp(self): - super(TestCreateSecurityGroupCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -107,7 +107,7 @@ class TestDeleteSecurityGroupCompute(compute_fakes.TestComputev2): _security_groups = compute_fakes.create_security_groups() def setUp(self): - super(TestDeleteSecurityGroupCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -221,7 +221,7 @@ class TestListSecurityGroupCompute(compute_fakes.TestComputev2): ) def setUp(self): - super(TestListSecurityGroupCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -267,7 +267,7 @@ class TestSetSecurityGroupCompute(compute_fakes.TestComputev2): _security_group = compute_fakes.create_one_security_group() def setUp(self): - super(TestSetSecurityGroupCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -355,7 +355,7 @@ class TestShowSecurityGroupCompute(compute_fakes.TestComputev2): ) def setUp(self): - super(TestShowSecurityGroupCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False 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 927a05df7..9619b2963 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py @@ -24,7 +24,7 @@ class TestSecurityGroupNetwork(network_fakes.TestNetworkV2): def setUp(self): - super(TestSecurityGroupNetwork, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity_client.projects @@ -61,7 +61,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork): ) def setUp(self): - super(TestCreateSecurityGroupNetwork, self).setUp() + super().setUp() self.network_client.create_security_group = mock.Mock( return_value=self._security_group @@ -177,7 +177,7 @@ class TestDeleteSecurityGroupNetwork(TestSecurityGroupNetwork): _security_groups = network_fakes.FakeSecurityGroup.create_security_groups() def setUp(self): - super(TestDeleteSecurityGroupNetwork, self).setUp() + super().setUp() self.network_client.delete_security_group = mock.Mock( return_value=None @@ -289,7 +289,7 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork): ) def setUp(self): - super(TestListSecurityGroupNetwork, self).setUp() + super().setUp() self.network_client.security_groups = mock.Mock( return_value=self._security_groups @@ -419,7 +419,7 @@ class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork): ) def setUp(self): - super(TestSetSecurityGroupNetwork, self).setUp() + super().setUp() self.network_client.update_security_group = mock.Mock( return_value=None @@ -549,7 +549,7 @@ class TestShowSecurityGroupNetwork(TestSecurityGroupNetwork): ) def setUp(self): - super(TestShowSecurityGroupNetwork, self).setUp() + super().setUp() self.network_client.find_security_group = mock.Mock( return_value=self._security_group @@ -590,7 +590,7 @@ class TestUnsetSecurityGroupNetwork(TestSecurityGroupNetwork): ) def setUp(self): - super(TestUnsetSecurityGroupNetwork, self).setUp() + super().setUp() self.network_client.update_security_group = mock.Mock( return_value=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 22291a4b2..cc780d54a 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 @@ -47,7 +47,7 @@ def _setup_security_group_rule(self, attrs=None): return expected_columns, expected_data def setUp(self): - super(TestCreateSecurityGroupRuleCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -302,7 +302,7 @@ class TestDeleteSecurityGroupRuleCompute(compute_fakes.TestComputev2): _security_group_rules = compute_fakes.create_security_group_rules(count=2) def setUp(self): - super(TestDeleteSecurityGroupRuleCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -428,7 +428,7 @@ class TestListSecurityGroupRuleCompute(compute_fakes.TestComputev2): expected_data_no_group.append(expected_rule_no_group) def setUp(self): - super(TestListSecurityGroupRuleCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False @@ -510,7 +510,7 @@ class TestShowSecurityGroupRuleCompute(compute_fakes.TestComputev2): ) def setUp(self): - super(TestShowSecurityGroupRuleCompute, self).setUp() + super().setUp() self.app.client_manager.network_endpoint_enabled = False 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 fb9e0799c..c9018db5c 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 @@ -25,7 +25,7 @@ class TestSecurityGroupRuleNetwork(network_fakes.TestNetworkV2): def setUp(self): - super(TestSecurityGroupRuleNetwork, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity_client.projects @@ -89,7 +89,7 @@ def _setup_security_group_rule(self, attrs=None): ) def setUp(self): - super(TestCreateSecurityGroupRuleNetwork, self).setUp() + super().setUp() self.network_client.find_security_group = mock.Mock( return_value=self._security_group @@ -970,7 +970,7 @@ class TestDeleteSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): ) def setUp(self): - super(TestDeleteSecurityGroupRuleNetwork, self).setUp() + super().setUp() self.network_client.delete_security_group_rule = mock.Mock( return_value=None @@ -1142,7 +1142,7 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): ) def setUp(self): - super(TestListSecurityGroupRuleNetwork, self).setUp() + super().setUp() self.network_client.find_security_group = mock.Mock( return_value=self._security_group @@ -1299,7 +1299,7 @@ class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): ) def setUp(self): - super(TestShowSecurityGroupRuleNetwork, self).setUp() + super().setUp() self.network_client.find_security_group_rule = mock.Mock( return_value=self._security_group_rule diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py index abe90a003..8b8207411 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet.py +++ b/openstackclient/tests/unit/network/v2/test_subnet.py @@ -25,7 +25,7 @@ class TestSubnet(network_fakes.TestNetworkV2): def setUp(self): - super(TestSubnet, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity_client.projects @@ -257,7 +257,7 @@ def _init_subnet_variables(self): def setUp(self): self._init_subnet_variables() - super(TestCreateSubnet, self).setUp() + super().setUp() # Get the command object to test self.cmd = subnet_v2.CreateSubnet(self.app, None) @@ -719,7 +719,7 @@ class TestDeleteSubnet(TestSubnet): _subnets = network_fakes.FakeSubnet.create_subnets(count=2) def setUp(self): - super(TestDeleteSubnet, self).setUp() + super().setUp() self.network_client.delete_subnet = mock.Mock(return_value=None) @@ -850,7 +850,7 @@ class TestListSubnet(TestSubnet): ) def setUp(self): - super(TestListSubnet, self).setUp() + super().setUp() # Get the command object to test self.cmd = subnet_v2.ListSubnet(self.app, None) @@ -1181,7 +1181,7 @@ class TestSetSubnet(TestSubnet): ) def setUp(self): - super(TestSetSubnet, self).setUp() + 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) @@ -1509,7 +1509,7 @@ class TestShowSubnet(TestSubnet): ) def setUp(self): - super(TestShowSubnet, self).setUp() + super().setUp() # Get the command object to test self.cmd = subnet_v2.ShowSubnet(self.app, None) @@ -1551,7 +1551,7 @@ def test_show_all_options(self): class TestUnsetSubnet(TestSubnet): def setUp(self): - super(TestUnsetSubnet, self).setUp() + super().setUp() # Add three dns_nameserver entries so we can verify ordering self._testsubnet = network_fakes.FakeSubnet.create_one_subnet( { diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py index 1221a9a75..214bfdce5 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py @@ -24,7 +24,7 @@ class TestSubnetPool(network_fakes.TestNetworkV2): def setUp(self): - super(TestSubnetPool, self).setUp() + super().setUp() # Get a shortcut to the ProjectManager Mock self.projects_mock = self.identity_client.projects @@ -74,7 +74,7 @@ class TestCreateSubnetPool(TestSubnetPool): ) def setUp(self): - super(TestCreateSubnetPool, self).setUp() + super().setUp() self.network_client.create_subnet_pool = mock.Mock( return_value=self._subnet_pool @@ -385,7 +385,7 @@ class TestDeleteSubnetPool(TestSubnetPool): _subnet_pools = network_fakes.FakeSubnetPool.create_subnet_pools(count=2) def setUp(self): - super(TestDeleteSubnetPool, self).setUp() + super().setUp() self.network_client.delete_subnet_pool = mock.Mock(return_value=None) @@ -509,7 +509,7 @@ class TestListSubnetPool(TestSubnetPool): ) def setUp(self): - super(TestListSubnetPool, self).setUp() + super().setUp() # Get the command object to test self.cmd = subnet_pool.ListSubnetPool(self.app, None) @@ -732,7 +732,7 @@ class TestSetSubnetPool(TestSubnetPool): _address_scope = network_fakes.create_one_address_scope() def setUp(self): - super(TestSetSubnetPool, self).setUp() + super().setUp() self.network_client.update_subnet_pool = mock.Mock(return_value=None) self.network_client.set_tags = mock.Mock(return_value=None) @@ -1077,7 +1077,7 @@ class TestShowSubnetPool(TestSubnetPool): ) def setUp(self): - super(TestShowSubnetPool, self).setUp() + super().setUp() self.network_client.find_subnet_pool = mock.Mock( return_value=self._subnet_pool @@ -1118,7 +1118,7 @@ def test_show_all_options(self): class TestUnsetSubnetPool(TestSubnetPool): def setUp(self): - super(TestUnsetSubnetPool, self).setUp() + super().setUp() self._subnetpool = network_fakes.FakeSubnetPool.create_one_subnet_pool( {'tags': ['green', 'red']} ) diff --git a/openstackclient/tests/unit/object/v1/fakes.py b/openstackclient/tests/unit/object/v1/fakes.py index 29873a35a..eedccdc0e 100644 --- a/openstackclient/tests/unit/object/v1/fakes.py +++ b/openstackclient/tests/unit/object/v1/fakes.py @@ -82,7 +82,7 @@ class TestObjectv1(utils.TestCommand): def setUp(self): - super(TestObjectv1, self).setUp() + super().setUp() self.app.client_manager.session = session.Session() self.app.client_manager.object_store = object_store.APIv1( diff --git a/openstackclient/tests/unit/object/v1/test_container.py b/openstackclient/tests/unit/object/v1/test_container.py index ee7a41c73..9143df9c9 100644 --- a/openstackclient/tests/unit/object/v1/test_container.py +++ b/openstackclient/tests/unit/object/v1/test_container.py @@ -25,7 +25,7 @@ AUTH_URL = "http://0.0.0.0" -class FakeClient(object): +class FakeClient: def __init__(self, endpoint=None, **kwargs): self.endpoint = AUTH_URL self.token = AUTH_TOKEN @@ -35,7 +35,7 @@ class TestContainer(object_fakes.TestObjectv1): columns = ('Name',) def setUp(self): - super(TestContainer, self).setUp() + super().setUp() self.app.client_manager.object_store = object_store.APIv1( session=mock.Mock(), service_type="object-store", @@ -48,7 +48,7 @@ def setUp(self): @mock.patch('openstackclient.api.object_store_v1.APIv1.container_delete') class TestContainerDelete(TestContainer): def setUp(self): - super(TestContainerDelete, self).setUp() + super().setUp() # Get the command object to test self.cmd = container.DeleteContainer(self.app, None) @@ -132,7 +132,7 @@ def test_r_delete(self, c_mock, o_list_mock, o_delete_mock): @mock.patch('openstackclient.api.object_store_v1.APIv1.container_list') class TestContainerList(TestContainer): def setUp(self): - super(TestContainerList, self).setUp() + super().setUp() # Get the command object to test self.cmd = container.ListContainer(self.app, None) @@ -345,7 +345,7 @@ def test_object_list_containers_all(self, c_mock): @mock.patch('openstackclient.api.object_store_v1.APIv1.container_show') class TestContainerShow(TestContainer): def setUp(self): - super(TestContainerShow, self).setUp() + super().setUp() # Get the command object to test self.cmd = container.ShowContainer(self.app, None) diff --git a/openstackclient/tests/unit/object/v1/test_container_all.py b/openstackclient/tests/unit/object/v1/test_container_all.py index ede3150ca..0a795dd86 100644 --- a/openstackclient/tests/unit/object/v1/test_container_all.py +++ b/openstackclient/tests/unit/object/v1/test_container_all.py @@ -21,7 +21,7 @@ class TestContainerAll(object_fakes.TestObjectv1): def setUp(self): - super(TestContainerAll, self).setUp() + super().setUp() self.requests_mock = self.useFixture(fixture.Fixture()) @@ -34,7 +34,7 @@ class TestContainerCreate(TestContainerAll): ) def setUp(self): - super(TestContainerCreate, self).setUp() + super().setUp() # Get the command object to test self.cmd = container_cmds.CreateContainer(self.app, None) @@ -182,7 +182,7 @@ def test_object_create_container_more(self): class TestContainerDelete(TestContainerAll): def setUp(self): - super(TestContainerDelete, self).setUp() + super().setUp() # Get the command object to test self.cmd = container_cmds.DeleteContainer(self.app, None) @@ -242,7 +242,7 @@ class TestContainerList(TestContainerAll): columns = ('Name',) def setUp(self): - super(TestContainerList, self).setUp() + super().setUp() # Get the command object to test self.cmd = container_cmds.ListContainer(self.app, None) @@ -309,7 +309,7 @@ def test_object_list_containers_prefix(self): class TestContainerSave(TestContainerAll): def setUp(self): - super(TestContainerSave, self).setUp() + super().setUp() # Get the command object to test self.cmd = container_cmds.SaveContainer(self.app, None) @@ -357,7 +357,7 @@ def setUp(self): class TestContainerShow(TestContainerAll): def setUp(self): - super(TestContainerShow, self).setUp() + super().setUp() # Get the command object to test self.cmd = container_cmds.ShowContainer(self.app, None) diff --git a/openstackclient/tests/unit/object/v1/test_object.py b/openstackclient/tests/unit/object/v1/test_object.py index 020b16314..665a71d14 100644 --- a/openstackclient/tests/unit/object/v1/test_object.py +++ b/openstackclient/tests/unit/object/v1/test_object.py @@ -27,7 +27,7 @@ class TestObject(object_fakes.TestObjectv1): def setUp(self): - super(TestObject, self).setUp() + super().setUp() self.app.client_manager.object_store = object_store.APIv1( session=mock.Mock(), service_type="object-store", @@ -41,7 +41,7 @@ class TestObjectList(TestObject): datalist = ((object_fakes.object_name_2,),) def setUp(self): - super(TestObjectList, self).setUp() + super().setUp() # Get the command object to test self.cmd = obj.ListObject(self.app, None) @@ -323,7 +323,7 @@ def test_object_list_objects_all(self, o_mock): @mock.patch('openstackclient.api.object_store_v1.APIv1.object_show') class TestObjectShow(TestObject): def setUp(self): - super(TestObjectShow, self).setUp() + super().setUp() # Get the command object to test self.cmd = obj.ShowObject(self.app, None) diff --git a/openstackclient/tests/unit/object/v1/test_object_all.py b/openstackclient/tests/unit/object/v1/test_object_all.py index d425373b2..484ac1b91 100644 --- a/openstackclient/tests/unit/object/v1/test_object_all.py +++ b/openstackclient/tests/unit/object/v1/test_object_all.py @@ -24,14 +24,14 @@ class TestObjectAll(object_fakes.TestObjectv1): def setUp(self): - super(TestObjectAll, self).setUp() + super().setUp() self.requests_mock = self.useFixture(fixture.Fixture()) class TestObjectCreate(TestObjectAll): def setUp(self): - super(TestObjectCreate, self).setUp() + super().setUp() # Get the command object to test self.cmd = object_cmds.CreateObject(self.app, None) @@ -65,7 +65,7 @@ class TestObjectList(TestObjectAll): columns = ('Name',) def setUp(self): - super(TestObjectList, self).setUp() + super().setUp() # Get the command object to test self.cmd = object_cmds.ListObject(self.app, None) @@ -140,7 +140,7 @@ def test_object_list_objects_prefix(self): class TestObjectShow(TestObjectAll): def setUp(self): - super(TestObjectShow, self).setUp() + super().setUp() # Get the command object to test self.cmd = object_cmds.ShowObject(self.app, None) @@ -208,7 +208,7 @@ def test_object_show(self): class TestObjectSave(TestObjectAll): def setUp(self): - super(TestObjectSave, self).setUp() + super().setUp() # Get the command object to test self.cmd = object_cmds.SaveObject(self.app, None) diff --git a/openstackclient/tests/unit/test_shell.py b/openstackclient/tests/unit/test_shell.py index 5d4bc6a83..fd95fce61 100644 --- a/openstackclient/tests/unit/test_shell.py +++ b/openstackclient/tests/unit/test_shell.py @@ -142,7 +142,7 @@ class TestShell(osc_lib_test_utils.TestShell): shell_class_name = "openstackclient.shell.OpenStackShell" def setUp(self): - super(TestShell, self).setUp() + super().setUp() # TODO(dtroyer): remove this once the shell_class_patch patch is # released in osc-lib mod_str, _sep, class_str = self.shell_class_name.rpartition('.') @@ -228,7 +228,7 @@ def _assert_cli(self, cmd_options, default_args): class TestShellOptions(TestShell): def setUp(self): - super(TestShellOptions, self).setUp() + super().setUp() self.useFixture(osc_lib_test_utils.EnvFixture()) def _test_options_init_app(self, test_opts): @@ -290,7 +290,7 @@ def _test_env_get_one_cloud(self, test_opts): class TestShellTokenAuthEnv(TestShell): def setUp(self): - super(TestShellTokenAuthEnv, self).setUp() + super().setUp() env = { "OS_TOKEN": DEFAULT_TOKEN, "OS_AUTH_URL": DEFAULT_AUTH_URL, @@ -333,7 +333,7 @@ def test_empty_auth(self): class TestShellTokenEndpointAuthEnv(TestShell): def setUp(self): - super(TestShellTokenEndpointAuthEnv, self).setUp() + super().setUp() env = { "OS_TOKEN": DEFAULT_TOKEN, "OS_ENDPOINT": DEFAULT_SERVICE_URL, @@ -376,7 +376,7 @@ def test_empty_auth(self): class TestShellCli(TestShell): def setUp(self): - super(TestShellCli, self).setUp() + super().setUp() env = { "OS_COMPUTE_API_VERSION": DEFAULT_COMPUTE_API_VERSION, "OS_IDENTITY_API_VERSION": DEFAULT_IDENTITY_API_VERSION, diff --git a/openstackclient/tests/unit/utils.py b/openstackclient/tests/unit/utils.py index daba426ec..df7341f0f 100644 --- a/openstackclient/tests/unit/utils.py +++ b/openstackclient/tests/unit/utils.py @@ -67,7 +67,7 @@ class TestCommand(TestCase): """Test cliff command classes""" def setUp(self): - super(TestCommand, self).setUp() + super().setUp() # Build up a fake app self.fake_stdout = fakes.FakeStdout() self.fake_log = fakes.FakeLog() diff --git a/openstackclient/tests/unit/volume/test_find_resource.py b/openstackclient/tests/unit/volume/test_find_resource.py index e1514b015..df087dd5d 100644 --- a/openstackclient/tests/unit/volume/test_find_resource.py +++ b/openstackclient/tests/unit/volume/test_find_resource.py @@ -37,7 +37,7 @@ class TestFindResourceVolumes(test_utils.TestCase): def setUp(self): - super(TestFindResourceVolumes, self).setUp() + super().setUp() api = mock.Mock() api.client = mock.Mock() api.client.get = mock.Mock() @@ -62,7 +62,7 @@ def test_not_find(self): class TestFindResourceVolumeSnapshots(test_utils.TestCase): def setUp(self): - super(TestFindResourceVolumeSnapshots, self).setUp() + super().setUp() api = mock.Mock() api.client = mock.Mock() api.client.get = mock.Mock() diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py index 6e3a5cdd8..0f0d532dd 100644 --- a/openstackclient/tests/unit/volume/v1/test_volume.py +++ b/openstackclient/tests/unit/volume/v1/test_volume.py @@ -1425,7 +1425,8 @@ def test_attachments_column_without_server_cache(self): col = volume.AttachmentsColumn(_volume.attachments, {}) self.assertEqual( - 'Attached to %s on %s ' % (server_id, device), col.human_readable() + f'Attached to {server_id} on {device} ', + col.human_readable(), ) self.assertEqual(_volume.attachments, col.machine_readable()) @@ -1440,7 +1441,7 @@ def test_attachments_column_with_server_cache(self): col = volume.AttachmentsColumn(_volume.attachments, server_cache) self.assertEqual( - 'Attached to %s on %s ' % ('fake-server-name', device), + '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/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index 00e7618c7..975a6a717 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -1085,7 +1085,7 @@ def create_one_detailed_quota(attrs=None): return quota -class FakeLimits(object): +class FakeLimits: """Fake limits""" def __init__(self, absolute_attrs=None): @@ -1176,7 +1176,7 @@ def rate_limits(self): return reference_data -class FakeAbsoluteLimit(object): +class FakeAbsoluteLimit: """Data model that represents an absolute limit.""" def __init__(self, name, value): @@ -1184,7 +1184,7 @@ def __init__(self, name, value): self.value = value -class FakeRateLimit(object): +class FakeRateLimit: """Data model that represents a flattened view of a single rate limit.""" def __init__(self, verb, uri, value, remain, unit, next_available): diff --git a/openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py b/openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py index 6580ac1f7..5a5b9c005 100644 --- a/openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py +++ b/openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py @@ -20,7 +20,7 @@ class TestConsistencyGroupSnapshot(volume_fakes.TestVolume): def setUp(self): - super(TestConsistencyGroupSnapshot, self).setUp() + super().setUp() # Get a shortcut to the TransferManager Mock self.cgsnapshots_mock = self.volume_client.cgsnapshots @@ -53,7 +53,7 @@ class TestConsistencyGroupSnapshotCreate(TestConsistencyGroupSnapshot): ) def setUp(self): - super(TestConsistencyGroupSnapshotCreate, self).setUp() + super().setUp() self.cgsnapshots_mock.create.return_value = ( self._consistency_group_snapshot ) @@ -126,7 +126,7 @@ class TestConsistencyGroupSnapshotDelete(TestConsistencyGroupSnapshot): ) def setUp(self): - super(TestConsistencyGroupSnapshotDelete, self).setUp() + super().setUp() self.cgsnapshots_mock.get = ( volume_fakes.get_consistency_group_snapshots( @@ -217,7 +217,7 @@ class TestConsistencyGroupSnapshotList(TestConsistencyGroupSnapshot): ) def setUp(self): - super(TestConsistencyGroupSnapshotList, self).setUp() + super().setUp() self.cgsnapshots_mock.list.return_value = ( self.consistency_group_snapshots @@ -332,7 +332,7 @@ class TestConsistencyGroupSnapshotShow(TestConsistencyGroupSnapshot): ) def setUp(self): - super(TestConsistencyGroupSnapshotShow, self).setUp() + super().setUp() self.cgsnapshots_mock.get.return_value = ( self._consistency_group_snapshot diff --git a/openstackclient/tests/unit/volume/v2/test_qos_specs.py b/openstackclient/tests/unit/volume/v2/test_qos_specs.py index 950012416..50c7419f1 100644 --- a/openstackclient/tests/unit/volume/v2/test_qos_specs.py +++ b/openstackclient/tests/unit/volume/v2/test_qos_specs.py @@ -27,7 +27,7 @@ class TestQos(volume_fakes.TestVolume): def setUp(self): - super(TestQos, self).setUp() + super().setUp() self.qos_mock = self.volume_client.qos_specs self.qos_mock.reset_mock() @@ -41,7 +41,7 @@ class TestQosAssociate(TestQos): qos_spec = volume_fakes.create_one_qos() def setUp(self): - super(TestQosAssociate, self).setUp() + super().setUp() self.qos_mock.get.return_value = self.qos_spec self.types_mock.get.return_value = self.volume_type @@ -68,7 +68,7 @@ class TestQosCreate(TestQos): columns = ('consumer', 'id', 'name', 'properties') def setUp(self): - super(TestQosCreate, self).setUp() + super().setUp() self.new_qos_spec = volume_fakes.create_one_qos() self.qos_mock.create.return_value = self.new_qos_spec @@ -158,7 +158,7 @@ class TestQosDelete(TestQos): qos_specs = volume_fakes.create_qoses(count=2) def setUp(self): - super(TestQosDelete, self).setUp() + super().setUp() self.qos_mock.get = volume_fakes.get_qoses(self.qos_specs) # Get the command object to test @@ -238,7 +238,7 @@ class TestQosDisassociate(TestQos): qos_spec = volume_fakes.create_one_qos() def setUp(self): - super(TestQosDisassociate, self).setUp() + super().setUp() self.qos_mock.get.return_value = self.qos_spec self.types_mock.get.return_value = self.volume_type @@ -302,7 +302,7 @@ class TestQosList(TestQos): ) def setUp(self): - super(TestQosList, self).setUp() + super().setUp() self.qos_mock.list.return_value = self.qos_specs self.qos_mock.get_associations.return_value = [self.qos_association] @@ -354,7 +354,7 @@ class TestQosSet(TestQos): qos_spec = volume_fakes.create_one_qos() def setUp(self): - super(TestQosSet, self).setUp() + super().setUp() self.qos_mock.get.return_value = self.qos_spec # Get the command object to test @@ -403,7 +403,7 @@ class TestQosShow(TestQos): ) def setUp(self): - super(TestQosShow, self).setUp() + super().setUp() self.qos_mock.get.return_value = self.qos_spec self.qos_mock.get_associations.return_value = [self.qos_association] @@ -428,7 +428,7 @@ class TestQosUnset(TestQos): qos_spec = volume_fakes.create_one_qos() def setUp(self): - super(TestQosUnset, self).setUp() + super().setUp() self.qos_mock.get.return_value = self.qos_spec # Get the command object to test diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index 130bf7003..0176994b6 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -1825,7 +1825,8 @@ def test_attachments_column_without_server_cache(self): col = volume.AttachmentsColumn(_volume.attachments, {}) self.assertEqual( - 'Attached to %s on %s ' % (server_id, device), col.human_readable() + f'Attached to {server_id} on {device} ', + col.human_readable(), ) self.assertEqual(_volume.attachments, col.machine_readable()) @@ -1840,7 +1841,7 @@ def test_attachments_column_with_server_cache(self): col = volume.AttachmentsColumn(_volume.attachments, server_cache) self.assertEqual( - 'Attached to %s on %s ' % ('fake-server-name', device), + 'Attached to {} on {} '.format('fake-server-name', device), col.human_readable(), ) self.assertEqual(_volume.attachments, col.machine_readable()) diff --git a/openstackclient/volume/v1/qos_specs.py b/openstackclient/volume/v1/qos_specs.py index 830d5d827..74938392e 100644 --- a/openstackclient/volume/v1/qos_specs.py +++ b/openstackclient/volume/v1/qos_specs.py @@ -33,7 +33,7 @@ class AssociateQos(command.Command): _description = _("Associate a QoS specification to a volume type") def get_parser(self, prog_name): - parser = super(AssociateQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_spec', metavar='', @@ -62,7 +62,7 @@ class CreateQos(command.ShowOne): _description = _("Create new QoS specification") def get_parser(self, prog_name): - parser = super(CreateQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -116,7 +116,7 @@ class DeleteQos(command.Command): _description = _("Delete QoS specification") def get_parser(self, prog_name): - parser = super(DeleteQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_specs', metavar='', @@ -162,7 +162,7 @@ class DisassociateQos(command.Command): _description = _("Disassociate a QoS specification from a volume type") def get_parser(self, prog_name): - parser = super(DisassociateQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_spec', metavar='', @@ -249,7 +249,7 @@ class SetQos(command.Command): _description = _("Set QoS specification properties") def get_parser(self, prog_name): - parser = super(SetQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_spec', metavar='', @@ -311,7 +311,7 @@ class ShowQos(command.ShowOne): _description = _("Display QoS specification details") def get_parser(self, prog_name): - parser = super(ShowQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_spec', metavar='', @@ -348,7 +348,7 @@ class UnsetQos(command.Command): _description = _("Unset QoS specification properties") def get_parser(self, prog_name): - parser = super(UnsetQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_spec', metavar='', diff --git a/openstackclient/volume/v1/service.py b/openstackclient/volume/v1/service.py index fb5869d51..2a33fc0b9 100644 --- a/openstackclient/volume/v1/service.py +++ b/openstackclient/volume/v1/service.py @@ -25,7 +25,7 @@ class ListService(command.Lister): _description = _("List service command") def get_parser(self, prog_name): - parser = super(ListService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "--host", metavar="", @@ -86,7 +86,7 @@ class SetService(command.Command): _description = _("Set volume service properties") def get_parser(self, prog_name): - parser = super(SetService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument("host", metavar="", help=_("Name of host")) parser.add_argument( "service", diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index 0248642b0..3f1eea16b 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -45,7 +45,7 @@ class takes server_cache as the second argument. """ def __init__(self, value, server_cache=None): - super(AttachmentsColumn, self).__init__(value) + super().__init__(value) self._server_cache = server_cache or {} def human_readable(self): @@ -60,7 +60,7 @@ def human_readable(self): if server in self._server_cache.keys(): server = self._server_cache[server].name device = attachment['device'] - msg += 'Attached to %s on %s ' % (server, device) + msg += f'Attached to {server} on {device} ' return msg @@ -83,7 +83,7 @@ class CreateVolume(command.ShowOne): _description = _("Create new volume") def get_parser(self, prog_name): - parser = super(CreateVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -298,7 +298,7 @@ class DeleteVolume(command.Command): _description = _("Delete volume(s)") def get_parser(self, prog_name): - parser = super(DeleteVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volumes', metavar='', @@ -350,7 +350,7 @@ class ListVolume(command.Lister): _description = _("List volumes") def get_parser(self, prog_name): - parser = super(ListVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--name', metavar='', @@ -466,7 +466,7 @@ class MigrateVolume(command.Command): _description = _("Migrate volume to a new host") def get_parser(self, prog_name): - parser = super(MigrateVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume', metavar="", @@ -504,7 +504,7 @@ class SetVolume(command.Command): _description = _("Set volume properties") def get_parser(self, prog_name): - parser = super(SetVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume', metavar='', @@ -663,7 +663,7 @@ class ShowVolume(command.ShowOne): _description = _("Show volume details") def get_parser(self, prog_name): - parser = super(ShowVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume', metavar='', @@ -706,7 +706,7 @@ class UnsetVolume(command.Command): _description = _("Unset volume properties") def get_parser(self, prog_name): - parser = super(UnsetVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume', metavar='', diff --git a/openstackclient/volume/v1/volume_backup.py b/openstackclient/volume/v1/volume_backup.py index c90dec195..99d583809 100644 --- a/openstackclient/volume/v1/volume_backup.py +++ b/openstackclient/volume/v1/volume_backup.py @@ -42,7 +42,7 @@ class takes volume_cache as the second argument. """ def __init__(self, value, volume_cache=None): - super(VolumeIdColumn, self).__init__(value) + super().__init__(value) self._volume_cache = volume_cache or {} def human_readable(self): @@ -61,7 +61,7 @@ class CreateVolumeBackup(command.ShowOne): _description = _("Create new volume backup") def get_parser(self, prog_name): - parser = super(CreateVolumeBackup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume', metavar='', @@ -105,7 +105,7 @@ class DeleteVolumeBackup(command.Command): _description = _("Delete volume backup(s)") def get_parser(self, prog_name): - parser = super(DeleteVolumeBackup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'backups', metavar='', @@ -145,7 +145,7 @@ class ListVolumeBackup(command.Lister): _description = _("List volume backups") def get_parser(self, prog_name): - parser = super(ListVolumeBackup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -254,7 +254,7 @@ class RestoreVolumeBackup(command.Command): _description = _("Restore volume backup") def get_parser(self, prog_name): - parser = super(RestoreVolumeBackup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'backup', metavar='', @@ -287,7 +287,7 @@ class ShowVolumeBackup(command.ShowOne): _description = _("Display volume backup details") def get_parser(self, prog_name): - parser = super(ShowVolumeBackup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'backup', metavar='', diff --git a/openstackclient/volume/v1/volume_snapshot.py b/openstackclient/volume/v1/volume_snapshot.py index fd4bb774a..18d8c3d62 100644 --- a/openstackclient/volume/v1/volume_snapshot.py +++ b/openstackclient/volume/v1/volume_snapshot.py @@ -44,7 +44,7 @@ class takes volume_cache as the second argument. """ def __init__(self, value, volume_cache=None): - super(VolumeIdColumn, self).__init__(value) + super().__init__(value) self._volume_cache = volume_cache or {} def human_readable(self): @@ -63,7 +63,7 @@ class CreateVolumeSnapshot(command.ShowOne): _description = _("Create new volume snapshot") def get_parser(self, prog_name): - parser = super(CreateVolumeSnapshot, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'snapshot_name', metavar='', @@ -122,7 +122,7 @@ class DeleteVolumeSnapshot(command.Command): _description = _("Delete volume snapshot(s)") def get_parser(self, prog_name): - parser = super(DeleteVolumeSnapshot, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'snapshots', metavar='', @@ -163,7 +163,7 @@ class ListVolumeSnapshot(command.Lister): _description = _("List volume snapshots") def get_parser(self, prog_name): - parser = super(ListVolumeSnapshot, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--all-projects', action='store_true', @@ -283,7 +283,7 @@ class SetVolumeSnapshot(command.Command): _description = _("Set volume snapshot properties") def get_parser(self, prog_name): - parser = super(SetVolumeSnapshot, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'snapshot', metavar='', @@ -374,7 +374,7 @@ class ShowVolumeSnapshot(command.ShowOne): _description = _("Display volume snapshot details") def get_parser(self, prog_name): - parser = super(ShowVolumeSnapshot, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'snapshot', metavar='', @@ -403,7 +403,7 @@ class UnsetVolumeSnapshot(command.Command): _description = _("Unset volume snapshot properties") def get_parser(self, prog_name): - parser = super(UnsetVolumeSnapshot, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'snapshot', metavar='', diff --git a/openstackclient/volume/v1/volume_transfer_request.py b/openstackclient/volume/v1/volume_transfer_request.py index a1b8c0c4e..d82584dc7 100644 --- a/openstackclient/volume/v1/volume_transfer_request.py +++ b/openstackclient/volume/v1/volume_transfer_request.py @@ -30,7 +30,7 @@ class AcceptTransferRequest(command.ShowOne): _description = _("Accept volume transfer request.") def get_parser(self, prog_name): - parser = super(AcceptTransferRequest, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'transfer_request', metavar="", @@ -72,7 +72,7 @@ class CreateTransferRequest(command.ShowOne): _description = _("Create volume transfer request.") def get_parser(self, prog_name): - parser = super(CreateTransferRequest, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--name', metavar="", @@ -104,7 +104,7 @@ class DeleteTransferRequest(command.Command): _description = _("Delete volume transfer request(s).") def get_parser(self, prog_name): - parser = super(DeleteTransferRequest, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'transfer_request', metavar="", @@ -147,7 +147,7 @@ class ListTransferRequest(command.Lister): _description = _("Lists all volume transfer requests.") def get_parser(self, prog_name): - parser = super(ListTransferRequest, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--all-projects', dest='all_projects', @@ -181,7 +181,7 @@ class ShowTransferRequest(command.ShowOne): _description = _("Show volume transfer request details.") def get_parser(self, prog_name): - parser = super(ShowTransferRequest, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'transfer_request', metavar="", diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py index 38fd82971..fed0a601f 100644 --- a/openstackclient/volume/v1/volume_type.py +++ b/openstackclient/volume/v1/volume_type.py @@ -43,7 +43,7 @@ class takes encryption_data as the second argument. """ def __init__(self, value, encryption_data=None): - super(EncryptionInfoColumn, self).__init__(value) + super().__init__(value) self._encryption_data = encryption_data or {} def _get_encryption_info(self): @@ -88,7 +88,7 @@ class CreateVolumeType(command.ShowOne): _description = _("Create new volume type") def get_parser(self, prog_name): - parser = super(CreateVolumeType, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -191,7 +191,7 @@ class DeleteVolumeType(command.Command): _description = _("Delete volume type(s)") def get_parser(self, prog_name): - parser = super(DeleteVolumeType, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume_types', metavar='', @@ -233,7 +233,7 @@ class ListVolumeType(command.Lister): _description = _("List volume types") def get_parser(self, prog_name): - parser = super(ListVolumeType, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -306,7 +306,7 @@ class SetVolumeType(command.Command): _description = _("Set volume type properties") def get_parser(self, prog_name): - parser = super(SetVolumeType, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume_type', metavar='', @@ -411,7 +411,7 @@ class ShowVolumeType(command.ShowOne): _description = _("Display volume type details") def get_parser(self, prog_name): - parser = super(ShowVolumeType, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "volume_type", metavar="", @@ -462,7 +462,7 @@ class UnsetVolumeType(command.Command): _description = _("Unset volume type properties") def get_parser(self, prog_name): - parser = super(UnsetVolumeType, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume_type', metavar='', diff --git a/openstackclient/volume/v2/backup_record.py b/openstackclient/volume/v2/backup_record.py index be4b9c692..98f7c7171 100644 --- a/openstackclient/volume/v2/backup_record.py +++ b/openstackclient/volume/v2/backup_record.py @@ -34,7 +34,7 @@ class ExportBackupRecord(command.ShowOne): ) def get_parser(self, prog_name): - parser = super(ExportBackupRecord, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "backup", metavar="", @@ -65,7 +65,7 @@ class ImportBackupRecord(command.ShowOne): ) def get_parser(self, prog_name): - parser = super(ImportBackupRecord, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "backup_service", metavar="", diff --git a/openstackclient/volume/v2/consistency_group.py b/openstackclient/volume/v2/consistency_group.py index 3b4d88033..ee17acc53 100644 --- a/openstackclient/volume/v2/consistency_group.py +++ b/openstackclient/volume/v2/consistency_group.py @@ -52,7 +52,7 @@ class AddVolumeToConsistencyGroup(command.Command): _description = _("Add volume(s) to consistency group") def get_parser(self, prog_name): - parser = super(AddVolumeToConsistencyGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'consistency_group', metavar="", @@ -94,7 +94,7 @@ class CreateConsistencyGroup(command.ShowOne): _description = _("Create new consistency group.") def get_parser(self, prog_name): - parser = super(CreateConsistencyGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "name", metavar="", @@ -196,7 +196,7 @@ class DeleteConsistencyGroup(command.Command): _description = _("Delete consistency group(s).") def get_parser(self, prog_name): - parser = super(DeleteConsistencyGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'consistency_groups', metavar='', @@ -246,7 +246,7 @@ class ListConsistencyGroup(command.Lister): _description = _("List consistency groups.") def get_parser(self, prog_name): - parser = super(ListConsistencyGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--all-projects', action="store_true", @@ -297,9 +297,7 @@ class RemoveVolumeFromConsistencyGroup(command.Command): _description = _("Remove volume(s) from consistency group") def get_parser(self, prog_name): - parser = super(RemoveVolumeFromConsistencyGroup, self).get_parser( - prog_name - ) + parser = super().get_parser(prog_name) parser.add_argument( 'consistency_group', metavar="", @@ -341,7 +339,7 @@ class SetConsistencyGroup(command.Command): _description = _("Set consistency group properties") def get_parser(self, prog_name): - parser = super(SetConsistencyGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'consistency_group', metavar='', @@ -379,7 +377,7 @@ class ShowConsistencyGroup(command.ShowOne): _description = _("Display consistency group details.") def get_parser(self, prog_name): - parser = super(ShowConsistencyGroup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "consistency_group", metavar="", diff --git a/openstackclient/volume/v2/consistency_group_snapshot.py b/openstackclient/volume/v2/consistency_group_snapshot.py index fe3c569f2..220d4f7f0 100644 --- a/openstackclient/volume/v2/consistency_group_snapshot.py +++ b/openstackclient/volume/v2/consistency_group_snapshot.py @@ -30,9 +30,7 @@ class CreateConsistencyGroupSnapshot(command.ShowOne): _description = _("Create new consistency group snapshot.") def get_parser(self, prog_name): - parser = super(CreateConsistencyGroupSnapshot, self).get_parser( - prog_name - ) + parser = super().get_parser(prog_name) parser.add_argument( "snapshot_name", metavar="", @@ -77,9 +75,7 @@ class DeleteConsistencyGroupSnapshot(command.Command): _description = _("Delete consistency group snapshot(s).") def get_parser(self, prog_name): - parser = super(DeleteConsistencyGroupSnapshot, self).get_parser( - prog_name - ) + parser = super().get_parser(prog_name) parser.add_argument( "consistency_group_snapshot", metavar="", @@ -122,9 +118,7 @@ class ListConsistencyGroupSnapshot(command.Lister): _description = _("List consistency group snapshots.") def get_parser(self, prog_name): - parser = super(ListConsistencyGroupSnapshot, self).get_parser( - prog_name - ) + parser = super().get_parser(prog_name) parser.add_argument( '--all-projects', action="store_true", @@ -202,9 +196,7 @@ class ShowConsistencyGroupSnapshot(command.ShowOne): _description = _("Display consistency group snapshot details") def get_parser(self, prog_name): - parser = super(ShowConsistencyGroupSnapshot, self).get_parser( - prog_name - ) + parser = super().get_parser(prog_name) parser.add_argument( "consistency_group_snapshot", metavar="", diff --git a/openstackclient/volume/v2/qos_specs.py b/openstackclient/volume/v2/qos_specs.py index 0454ecae8..fa6cec494 100644 --- a/openstackclient/volume/v2/qos_specs.py +++ b/openstackclient/volume/v2/qos_specs.py @@ -33,7 +33,7 @@ class AssociateQos(command.Command): _description = _("Associate a QoS specification to a volume type") def get_parser(self, prog_name): - parser = super(AssociateQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_spec', metavar='', @@ -62,7 +62,7 @@ class CreateQos(command.ShowOne): _description = _("Create new QoS specification") def get_parser(self, prog_name): - parser = super(CreateQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'name', metavar='', @@ -117,7 +117,7 @@ class DeleteQos(command.Command): _description = _("Delete QoS specification") def get_parser(self, prog_name): - parser = super(DeleteQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_specs', metavar='', @@ -163,7 +163,7 @@ class DisassociateQos(command.Command): _description = _("Disassociate a QoS specification from a volume type") def get_parser(self, prog_name): - parser = super(DisassociateQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_spec', metavar='', @@ -251,7 +251,7 @@ class SetQos(command.Command): _description = _("Set QoS specification properties") def get_parser(self, prog_name): - parser = super(SetQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_spec', metavar='', @@ -313,7 +313,7 @@ class ShowQos(command.ShowOne): _description = _("Display QoS specification details") def get_parser(self, prog_name): - parser = super(ShowQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_spec', metavar='', @@ -350,7 +350,7 @@ class UnsetQos(command.Command): _description = _("Unset QoS specification properties") def get_parser(self, prog_name): - parser = super(UnsetQos, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'qos_spec', metavar='', diff --git a/openstackclient/volume/v2/service.py b/openstackclient/volume/v2/service.py index fb5869d51..2a33fc0b9 100644 --- a/openstackclient/volume/v2/service.py +++ b/openstackclient/volume/v2/service.py @@ -25,7 +25,7 @@ class ListService(command.Lister): _description = _("List service command") def get_parser(self, prog_name): - parser = super(ListService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "--host", metavar="", @@ -86,7 +86,7 @@ class SetService(command.Command): _description = _("Set volume service properties") def get_parser(self, prog_name): - parser = super(SetService, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument("host", metavar="", help=_("Name of host")) parser.add_argument( "service", diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 98f4a5cab..623a943b5 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -69,7 +69,7 @@ class takes server_cache as the second argument. """ def __init__(self, value, server_cache=None): - super(AttachmentsColumn, self).__init__(value) + super().__init__(value) self._server_cache = server_cache or {} def human_readable(self): @@ -84,7 +84,7 @@ def human_readable(self): if server in self._server_cache.keys(): server = self._server_cache[server].name device = attachment['device'] - msg += 'Attached to %s on %s ' % (server, device) + msg += f'Attached to {server} on {device} ' return msg @@ -360,7 +360,7 @@ class DeleteVolume(command.Command): _description = _("Delete volume(s)") def get_parser(self, prog_name): - parser = super(DeleteVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "volumes", metavar="", @@ -422,7 +422,7 @@ class ListVolume(command.Lister): _description = _("List volumes") def get_parser(self, prog_name): - parser = super(ListVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--project', metavar='', @@ -566,7 +566,7 @@ class MigrateVolume(command.Command): _description = _("Migrate volume to a new host") def get_parser(self, prog_name): - parser = super(MigrateVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume', metavar="", @@ -614,7 +614,7 @@ class SetVolume(command.Command): _description = _("Set volume properties") def get_parser(self, prog_name): - parser = super(SetVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume', metavar='', @@ -920,7 +920,7 @@ class ShowVolume(command.ShowOne): _description = _("Display volume details") def get_parser(self, prog_name): - parser = super(ShowVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume', metavar="", @@ -953,7 +953,7 @@ class UnsetVolume(command.Command): _description = _("Unset volume properties") def get_parser(self, prog_name): - parser = super(UnsetVolume, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume', metavar='', diff --git a/openstackclient/volume/v2/volume_backend.py b/openstackclient/volume/v2/volume_backend.py index a0125032c..2fbbed64a 100644 --- a/openstackclient/volume/v2/volume_backend.py +++ b/openstackclient/volume/v2/volume_backend.py @@ -25,7 +25,7 @@ class ShowCapability(command.Lister): _description = _("Show capability command") def get_parser(self, prog_name): - parser = super(ShowCapability, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "host", metavar="", @@ -72,7 +72,7 @@ class ListPool(command.Lister): _description = _("List pool command") def get_parser(self, prog_name): - parser = super(ListPool, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "--long", action="store_true", diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py index 4454d17b0..7eed05a1d 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -44,7 +44,7 @@ class takes volume_cache as the second argument. """ def __init__(self, value, volume_cache=None): - super(VolumeIdColumn, self).__init__(value) + super().__init__(value) self._volume_cache = volume_cache or {} def human_readable(self): @@ -184,7 +184,7 @@ class DeleteVolumeBackup(command.Command): _description = _("Delete volume backup(s)") def get_parser(self, prog_name): - parser = super(DeleteVolumeBackup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "backups", metavar="", @@ -236,7 +236,7 @@ class ListVolumeBackup(command.Lister): _description = _("List volume backups") def get_parser(self, prog_name): - parser = super(ListVolumeBackup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "--long", action="store_true", @@ -381,7 +381,7 @@ class RestoreVolumeBackup(command.ShowOne): _description = _("Restore volume backup") def get_parser(self, prog_name): - parser = super(RestoreVolumeBackup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "backup", metavar="", @@ -633,7 +633,7 @@ class ShowVolumeBackup(command.ShowOne): _description = _("Display volume backup details") def get_parser(self, prog_name): - parser = super(ShowVolumeBackup, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "backup", metavar="", diff --git a/openstackclient/volume/v2/volume_host.py b/openstackclient/volume/v2/volume_host.py index df93c0590..2b0df0aa2 100644 --- a/openstackclient/volume/v2/volume_host.py +++ b/openstackclient/volume/v2/volume_host.py @@ -23,7 +23,7 @@ class FailoverVolumeHost(command.Command): _description = _("Failover volume host to different backend") def get_parser(self, prog_name): - parser = super(FailoverVolumeHost, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "host", metavar="", help=_("Name of volume host") ) @@ -49,7 +49,7 @@ class SetVolumeHost(command.Command): _description = _("Set volume host properties") def get_parser(self, prog_name): - parser = super(SetVolumeHost, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "host", metavar="", help=_("Name of volume host") ) diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index 93560f53c..3398f502a 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -45,7 +45,7 @@ class takes volume_cache as the second argument. """ def __init__(self, value, volume_cache=None): - super(VolumeIdColumn, self).__init__(value) + super().__init__(value) self._volume_cache = volume_cache or {} def human_readable(self): @@ -64,7 +64,7 @@ class CreateVolumeSnapshot(command.ShowOne): _description = _("Create new volume snapshot") def get_parser(self, prog_name): - parser = super(CreateVolumeSnapshot, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "snapshot_name", metavar="", @@ -159,7 +159,7 @@ class DeleteVolumeSnapshot(command.Command): _description = _("Delete volume snapshot(s)") def get_parser(self, prog_name): - parser = super(DeleteVolumeSnapshot, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "snapshots", metavar="", @@ -210,7 +210,7 @@ class ListVolumeSnapshot(command.Lister): _description = _("List volume snapshots") def get_parser(self, prog_name): - parser = super(ListVolumeSnapshot, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--all-projects', action='store_true', @@ -346,7 +346,7 @@ class SetVolumeSnapshot(command.Command): _description = _("Set volume snapshot properties") def get_parser(self, prog_name): - parser = super(SetVolumeSnapshot, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'snapshot', metavar='', @@ -461,7 +461,7 @@ class ShowVolumeSnapshot(command.ShowOne): _description = _("Display volume snapshot details") def get_parser(self, prog_name): - parser = super(ShowVolumeSnapshot, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "snapshot", metavar="", @@ -488,7 +488,7 @@ class UnsetVolumeSnapshot(command.Command): _description = _("Unset volume snapshot properties") def get_parser(self, prog_name): - parser = super(UnsetVolumeSnapshot, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'snapshot', metavar='', diff --git a/openstackclient/volume/v2/volume_transfer_request.py b/openstackclient/volume/v2/volume_transfer_request.py index e25770e30..d531efc6b 100644 --- a/openstackclient/volume/v2/volume_transfer_request.py +++ b/openstackclient/volume/v2/volume_transfer_request.py @@ -31,7 +31,7 @@ class AcceptTransferRequest(command.ShowOne): _description = _("Accept volume transfer request.") def get_parser(self, prog_name): - parser = super(AcceptTransferRequest, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'transfer_request', metavar="", @@ -70,7 +70,7 @@ class CreateTransferRequest(command.ShowOne): _description = _("Create volume transfer request.") def get_parser(self, prog_name): - parser = super(CreateTransferRequest, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--name', metavar="", @@ -137,7 +137,7 @@ class DeleteTransferRequest(command.Command): _description = _("Delete volume transfer request(s).") def get_parser(self, prog_name): - parser = super(DeleteTransferRequest, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'transfer_request', metavar="", @@ -180,7 +180,7 @@ class ListTransferRequest(command.Lister): _description = _("Lists all volume transfer requests.") def get_parser(self, prog_name): - parser = super(ListTransferRequest, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--all-projects', dest='all_projects', @@ -214,7 +214,7 @@ class ShowTransferRequest(command.ShowOne): _description = _("Show volume transfer request details.") def get_parser(self, prog_name): - parser = super(ShowTransferRequest, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'transfer_request', metavar="", diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index 00821178c..c472a3c28 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -44,7 +44,7 @@ class takes encryption_data as the second argument. """ def __init__(self, value, encryption_data=None): - super(EncryptionInfoColumn, self).__init__(value) + super().__init__(value) self._encryption_data = encryption_data or {} def _get_encryption_info(self): @@ -110,7 +110,7 @@ class CreateVolumeType(command.ShowOne): _description = _("Create new volume type") def get_parser(self, prog_name): - parser = super(CreateVolumeType, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "name", metavar="", @@ -331,7 +331,7 @@ class DeleteVolumeType(command.Command): _description = _("Delete volume type(s)") def get_parser(self, prog_name): - parser = super(DeleteVolumeType, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "volume_types", metavar="", @@ -373,7 +373,7 @@ class ListVolumeType(command.Lister): _description = _("List volume types") def get_parser(self, prog_name): - parser = super(ListVolumeType, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( '--long', action='store_true', @@ -565,7 +565,7 @@ class SetVolumeType(command.Command): _description = _("Set volume type properties") def get_parser(self, prog_name): - parser = super(SetVolumeType, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume_type', metavar='', @@ -804,7 +804,7 @@ class ShowVolumeType(command.ShowOne): _description = _("Display volume type details") def get_parser(self, prog_name): - parser = super(ShowVolumeType, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( "volume_type", metavar="", @@ -875,7 +875,7 @@ class UnsetVolumeType(command.Command): _description = _("Unset volume type properties") def get_parser(self, prog_name): - parser = super(UnsetVolumeType, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'volume_type', metavar='', diff --git a/openstackclient/volume/v3/volume_message.py b/openstackclient/volume/v3/volume_message.py index 071a10d45..0fc724a58 100644 --- a/openstackclient/volume/v3/volume_message.py +++ b/openstackclient/volume/v3/volume_message.py @@ -133,7 +133,7 @@ class ShowMessage(command.ShowOne): _description = _('Show a volume failure message') def get_parser(self, prog_name): - parser = super(ShowMessage, self).get_parser(prog_name) + parser = super().get_parser(prog_name) parser.add_argument( 'message_id', metavar='', diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index 2ad39ab62..fa48392f2 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # 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 From 837a3dc0151233ded9e5513e21f6f06d36ec6ae3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 25 Apr 2024 10:20:59 +0100 Subject: [PATCH 104/403] tests: Fix trivial sorting issue We've seen an ordering issue pop up occasionally in the CI. Resolve it. Change-Id: I4dd10268b673c260ac0894fac92cd8bea9e626f4 Signed-off-by: Stephen Finucane --- openstackclient/identity/v3/tag.py | 4 ++-- openstackclient/tests/unit/identity/v3/test_project.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openstackclient/identity/v3/tag.py b/openstackclient/identity/v3/tag.py index 7a9321866..ce1dfe891 100644 --- a/openstackclient/identity/v3/tag.py +++ b/openstackclient/identity/v3/tag.py @@ -129,7 +129,7 @@ def update_tags_in_args(parsed_args, obj, args): args['tags'] = [] obj.tags = [] if parsed_args.remove_tag: - args['tags'] = list(set(obj.tags) - set(parsed_args.remove_tag)) + args['tags'] = sorted(set(obj.tags) - set(parsed_args.remove_tag)) return if parsed_args.tags: - args['tags'] = list(set(obj.tags).union(set(parsed_args.tags))) + args['tags'] = sorted(set(obj.tags).union(set(parsed_args.tags))) diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py index d9427e37c..ffef854e9 100644 --- a/openstackclient/tests/unit/identity/v3/test_project.py +++ b/openstackclient/tests/unit/identity/v3/test_project.py @@ -1130,7 +1130,7 @@ def test_project_set_tags(self): # Set expected values. new tag is added to original tags for update. kwargs = { 'name': 'qwerty', - 'tags': list(set(['tag1', 'tag2', 'tag3', 'foo'])), + 'tags': sorted(set(['tag1', 'tag2', 'tag3', 'foo'])), } # ProjectManager.update(project, name=, domain=, description=, # enabled=, **kwargs) From 25f2e224293037c7ab46408a83ca95dfc3094ff9 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Mon, 29 Apr 2024 10:48:50 +0000 Subject: [PATCH 105/403] reno: Update master for unmaintained/zed Update the zed release notes configuration to build from unmaintained/zed. Change-Id: Ia39713784cb393f5d2371922eb314720131561f1 --- releasenotes/source/zed.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/source/zed.rst b/releasenotes/source/zed.rst index 9608c05e4..6cc2b1554 100644 --- a/releasenotes/source/zed.rst +++ b/releasenotes/source/zed.rst @@ -3,4 +3,4 @@ Zed Series Release Notes ======================== .. release-notes:: - :branch: stable/zed + :branch: unmaintained/zed From 0fd107e6c76835087524d49c216a38985ffffa38 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 23 Apr 2024 12:23:13 +0100 Subject: [PATCH 106/403] pre-commit: Add pyupgrade hook Another day, another useful hook. We also ignore the preceding patch that actually did the work, renaming the incorrect named file in the process. Change-Id: I412827761fbdeb36702ebaf5c1b727c62e629299 Signed-off-by: Stephen Finucane --- .git-ignore-blame-revs => .git-blame-ignore-revs | 1 + .pre-commit-config.yaml | 5 +++++ 2 files changed, 6 insertions(+) rename .git-ignore-blame-revs => .git-blame-ignore-revs (89%) diff --git a/.git-ignore-blame-revs b/.git-blame-ignore-revs similarity index 89% rename from .git-ignore-blame-revs rename to .git-blame-ignore-revs index 625df1357..62caf9485 100644 --- a/.git-ignore-blame-revs +++ b/.git-blame-ignore-revs @@ -1,6 +1,7 @@ # You can configure git to automatically use this file with the following config: # git config --global blame.ignoreRevsFile .git-blame-ignore-revs +c5b772db76c071e493a81105c7d8c0def08b2264 # trivial: Prepare for pyupgrade pre-commit hook ed0314ac76ae58a6621077feb742efd5c14c3a62 # Blacken everything else ac64fdb93c32972575a4523ccb23d0279ef584f5 # Blacken openstackclient.api a3778109d0051a25901569e7bafe54915ab25f82 # Blacken openstack.common diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9c384867d..6154c51ea 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,6 +17,11 @@ repos: - id: check-yaml files: .*\.(yaml|yml)$ args: ['--unsafe'] + - repo: https://github.com/asottile/pyupgrade + rev: v3.15.2 + hooks: + - id: pyupgrade + args: ['--py38-plus'] - repo: https://github.com/psf/black rev: 24.4.0 hooks: From 2e7ba5e3dd8055e7eb2dcac9db1009db512a7fcc Mon Sep 17 00:00:00 2001 From: elajkat Date: Tue, 5 Dec 2023 14:01:36 +0100 Subject: [PATCH 107/403] Router flavor_id can be a name Change-Id: I72fc21a1adb4790a2a51e9b37744ee1ee3d01f32 Partial-Bug: #2020823 --- openstackclient/network/v2/router.py | 13 ++++++++-- .../tests/unit/network/v2/test_router.py | 26 ++++++++++++++++++- ...r-accepts-name-or-id-e9cecafcddf81cb2.yaml | 6 +++++ 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/Router-flavor-accepts-name-or-id-e9cecafcddf81cb2.yaml diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index e72d3e2f5..5e49c6df0 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -13,6 +13,7 @@ """Router action implementations""" +import argparse import collections import copy import json @@ -198,6 +199,8 @@ def _get_external_gateway_attrs(client_manager, parsed_args): def _get_attrs(client_manager, parsed_args): attrs = {} + n_client = client_manager.network + if parsed_args.name is not None: attrs['name'] = parsed_args.name if parsed_args.enable: @@ -229,7 +232,8 @@ 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: - attrs['flavor_id'] = parsed_args.flavor_id + flavor = n_client.find_flavor(parsed_args.flavor_id) + attrs['flavor_id'] = flavor.id for attr in ('enable_default_route_bfd', 'enable_default_route_ecmp'): value = getattr(parsed_args, attr, None) @@ -560,10 +564,15 @@ def get_parser(self, prog_name): action='store_false', help=_("Disable IPv6 NDP proxy on external gateway"), ) + parser.add_argument( + '--flavor', + metavar='', + help=_("Associate the router to a flavor (by name or ID"), + ) parser.add_argument( '--flavor-id', metavar='', - help=_("Associate the router to a flavor by ID"), + help=argparse.SUPPRESS, ) _parser_add_bfd_ecmp_arguments(parser) diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 6e645996f..d410c0f9d 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -384,13 +384,15 @@ 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(self): + def test_create_with_flavor_id_or_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.id, ] + arglist_with_name = [self.new_router.name, '--flavor-id', _flavor.name] verifylist = [ ('name', self.new_router.name), ('enable', True), @@ -410,6 +412,28 @@ def test_create_with_flavor_id(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) + self.network_client.create_router.reset_mock() + verifylist_w_name = [ + ('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 + ) + columns, data = self.cmd.take_action(parsed_args_w_name) + 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_enable_default_route_bfd(self): self.network_client.find_extension = mock.Mock( return_value=network_fakes.create_one_extension( diff --git a/releasenotes/notes/Router-flavor-accepts-name-or-id-e9cecafcddf81cb2.yaml b/releasenotes/notes/Router-flavor-accepts-name-or-id-e9cecafcddf81cb2.yaml new file mode 100644 index 000000000..be5559d06 --- /dev/null +++ b/releasenotes/notes/Router-flavor-accepts-name-or-id-e9cecafcddf81cb2.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + The ``router create --flavor-id`` parameter has been deprecated + in favour of the ``--flavor`` parameter, which accepts both + flavor names and flavor IDs. From 50c595b6e0906cad31cc0922c77f2d7a4a469787 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 24 Jan 2024 12:03:59 +0000 Subject: [PATCH 108/403] identity: Make better use of argparse Change-Id: I50d2d28422e609656408b9b59f330d6a78314344 Signed-off-by: Stephen Finucane --- openstackclient/identity/v3/service.py | 24 ++++++++------- .../tests/unit/identity/v3/test_service.py | 30 +++++++------------ 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index fd2c1df84..42ee1e474 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -52,11 +52,15 @@ def get_parser(self, prog_name): enable_group.add_argument( '--enable', action='store_true', + dest='is_enabled', + default=True, help=_('Enable service (default)'), ) enable_group.add_argument( '--disable', - action='store_true', + action='store_false', + dest='is_enabled', + default=True, help=_('Disable service'), ) return parser @@ -64,15 +68,11 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - enabled = True - if parsed_args.disable: - enabled = False - service = identity_client.services.create( name=parsed_args.name, type=parsed_args.type, description=parsed_args.description, - enabled=enabled, + enabled=parsed_args.is_enabled, ) service._info.pop('links') @@ -171,11 +171,15 @@ def get_parser(self, prog_name): enable_group.add_argument( '--enable', action='store_true', + dest='is_enabled', + default=None, help=_('Enable service'), ) enable_group.add_argument( '--disable', - action='store_true', + action='store_false', + dest='is_enabled', + default=None, help=_('Disable service'), ) return parser @@ -191,10 +195,8 @@ 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 + if parsed_args.is_enabled is not None: + kwargs['enabled'] = parsed_args.is_enabled identity_client.services.update(service.id, **kwargs) diff --git a/openstackclient/tests/unit/identity/v3/test_service.py b/openstackclient/tests/unit/identity/v3/test_service.py index 8993c8f84..57de1a1f7 100644 --- a/openstackclient/tests/unit/identity/v3/test_service.py +++ b/openstackclient/tests/unit/identity/v3/test_service.py @@ -63,8 +63,7 @@ def test_service_create_name(self): verifylist = [ ('name', self.service.name), ('description', None), - ('enable', False), - ('disable', False), + ('is_enabled', True), ('type', self.service.type), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -94,8 +93,7 @@ def test_service_create_description(self): verifylist = [ ('name', None), ('description', self.service.description), - ('enable', False), - ('disable', False), + ('is_enabled', True), ('type', self.service.type), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -124,8 +122,7 @@ def test_service_create_enable(self): verifylist = [ ('name', None), ('description', None), - ('enable', True), - ('disable', False), + ('is_enabled', True), ('type', self.service.type), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -154,8 +151,7 @@ def test_service_create_disable(self): verifylist = [ ('name', None), ('description', None), - ('enable', False), - ('disable', True), + ('is_enabled', False), ('type', self.service.type), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -292,8 +288,7 @@ def test_service_set_no_options(self): ('type', None), ('name', None), ('description', None), - ('enable', False), - ('disable', False), + ('is_enabled', None), ('service', self.service.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -312,8 +307,7 @@ def test_service_set_type(self): ('type', self.service.type), ('name', None), ('description', None), - ('enable', False), - ('disable', False), + ('is_enabled', None), ('service', self.service.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -338,8 +332,7 @@ def test_service_set_name(self): ('type', None), ('name', self.service.name), ('description', None), - ('enable', False), - ('disable', False), + ('is_enabled', None), ('service', self.service.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -364,8 +357,7 @@ def test_service_set_description(self): ('type', None), ('name', None), ('description', self.service.description), - ('enable', False), - ('disable', False), + ('is_enabled', None), ('service', self.service.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -389,8 +381,7 @@ def test_service_set_enable(self): ('type', None), ('name', None), ('description', None), - ('enable', True), - ('disable', False), + ('is_enabled', True), ('service', self.service.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -414,8 +405,7 @@ def test_service_set_disable(self): ('type', None), ('name', None), ('description', None), - ('enable', False), - ('disable', True), + ('is_enabled', False), ('service', self.service.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) From 4c312849364d1e5711fb97a1303b66078fed017d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 10 May 2024 11:16:56 +0100 Subject: [PATCH 109/403] Fix tests on Python 3.12 We were seeing the following test failures on Python 3.12: openstackclient.tests.unit.common.test_module.TestModuleList.test_module_list_all openstackclient.tests.unit.common.test_module.TestModuleList.test_module_list_no_options Both failures were caused by missing attributes of 'sys', e.g. AttributeError: module 'sys' has no attribute 'builtin_module_names' Fix this by exposing the real 'sys' module as part of our mock of 'sys.modules'. Change-Id: I17391a46f08896f49dccaf75ad685dab1375a03d Signed-off-by: Stephen Finucane --- openstackclient/tests/unit/common/test_module.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openstackclient/tests/unit/common/test_module.py b/openstackclient/tests/unit/common/test_module.py index 68255a04e..476538a19 100644 --- a/openstackclient/tests/unit/common/test_module.py +++ b/openstackclient/tests/unit/common/test_module.py @@ -15,6 +15,7 @@ """Test module module""" +import sys from unittest import mock from openstackclient.common import module as osc_module @@ -43,6 +44,7 @@ module_version_5 = '0.0.1' 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), From 0d9ace64250f8ba9d07102f602c47f118d38ef51 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Mon, 29 Apr 2024 17:29:29 +0530 Subject: [PATCH 110/403] Add CreateVolume class to v3 This patch acts as a base framework to add the parameters needed for manage volume support. This includes 2 changes: 1. Move get_parser and take_action code to common methods which can be utilized by both v2 and v3 2. Make _check_size_arg as a static method and move it inside CreateVolume class since it's not used by other classes. [2] was initially thought to be a follow up change but since we are implementing changes into the _check_size_arg method for v3, it makes sense to just include it in CreateVolume class to avoid adding a new additional method. Similar changes are done for v2 as well. Change-Id: I9315e457ebd6c5ba4cc67452f92c9dc8c139ee3c --- openstackclient/volume/v2/volume.py | 45 +++++++++++++++++------------ openstackclient/volume/v3/volume.py | 33 ++++++++++++++++++++- setup.cfg | 2 +- 3 files changed, 59 insertions(+), 21 deletions(-) diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 623a943b5..eaaa40a5a 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -88,27 +88,27 @@ def human_readable(self): 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. - """ +class CreateVolume(command.ShowOne): + _description = _("Create new volume") - if ( - args.snapshot or args.source or args.backup - ) is None and args.size is None: - msg = _( - "--size is a required option if snapshot, backup " - "or source volume are not specified." - ) - raise exceptions.CommandError(msg) + @staticmethod + 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. + """ -class CreateVolume(command.ShowOne): - _description = _("Create new volume") + if ( + args.snapshot or args.source or args.backup + ) is None and args.size is None: + msg = _( + "--size is a required option if snapshot, backup " + "or source volume are not specified." + ) + 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,10 +216,14 @@ def get_parser(self, prog_name): action="store_true", 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): - _check_size_arg(parsed_args) + def _take_action(self, parsed_args): + CreateVolume._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 @@ -355,6 +359,9 @@ def take_action(self, parsed_args): volume._info.pop("links", None) return zip(*sorted(volume._info.items())) + def take_action(self, parsed_args): + return self._take_action(parsed_args) + class DeleteVolume(command.Command): _description = _("Delete volume(s)") diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 91d58c307..7f5a88fcd 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -23,7 +23,7 @@ from osc_lib import utils from openstackclient.i18n import _ - +from openstackclient.volume.v2 import volume as volume_v2 LOG = logging.getLogger(__name__) @@ -113,3 +113,34 @@ def take_action(self, parsed_args): ) volume_client.revert_volume_to_snapshot(volume, snapshot) + + +class CreateVolume(volume_v2.CreateVolume): + _description = _("Create new volume") + + @staticmethod + def _check_size_arg(args): + """Check whether --size option is required or not. + + Require size parameter in case if any of the following is not specified: + * snapshot + * source volume + * backup + * remote source (volume to be managed) + """ + + if ( + args.snapshot or args.source or args.backup or args.remote_source + ) is None and args.size is None: + msg = _( + "--size is a required option if none of --snapshot, " + "--backup, --source, or --remote-source are provided." + ) + raise exceptions.CommandError(msg) + + def get_parser(self, prog_name): + parser, _ = self._get_parser(prog_name) + return parser + + def take_action(self, parsed_args): + return self._take_action(parsed_args) diff --git a/setup.cfg b/setup.cfg index bb3bcd50e..0bca7c1e6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -765,7 +765,7 @@ openstack.volume.v3 = 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_create = openstackclient.volume.v3.volume:CreateVolume volume_delete = openstackclient.volume.v2.volume:DeleteVolume volume_list = openstackclient.volume.v2.volume:ListVolume volume_migrate = openstackclient.volume.v2.volume:MigrateVolume From cc7773f53bac61f602ce8ebc908878fd0914724c Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Fri, 15 Sep 2023 01:38:32 +0530 Subject: [PATCH 111/403] Add support for managing volumes This patch adds support for the cinder manage command to bring a volume under OpenStack management. Change-Id: I12b63bfc4f0c9bc29cf9d4efd9a5cd038ec00af3 --- .../tests/unit/volume/v3/test_volume.py | 229 ++++++++++++++++++ openstackclient/volume/v3/volume.py | 95 +++++++- ...olume-manage-command-088890446d0e81c7.yaml | 6 + 3 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-volume-manage-command-088890446d0e81c7.yaml diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index a9db383ad..ad6595bea 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -198,3 +198,232 @@ def test_volume_revert_to_snapshot(self): self.snapshot.id, ignore_missing=False, ) + + +class TestVolumeCreate(BaseVolumeTest): + 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) + + def test_volume_create_remote_source(self): + self.volume_sdk_client.manage_volume.return_value = self.new_volume + + arglist = [ + '--remote-source', + 'key=val', + '--host', + 'fake_host', + self.new_volume.name, + ] + verifylist = [ + ('remote_source', {'key': 'val'}), + ('host', 'fake_host'), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.manage_volume.assert_called_with( + host='fake_host', + ref={'key': 'val'}, + name=parsed_args.name, + description=parsed_args.description, + volume_type=parsed_args.type, + availability_zone=parsed_args.availability_zone, + metadata=parsed_args.property, + bootable=parsed_args.bootable, + cluster=getattr(parsed_args, 'cluster', None), + ) + + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.datalist, data) + + def test_volume_create_remote_source_pre_316(self): + self._set_mock_microversion('3.15') + arglist = [ + '--remote-source', + 'key=val', + '--cluster', + 'fake_cluster', + self.new_volume.name, + ] + verifylist = [ + ('remote_source', {'key': 'val'}), + ('cluster', 'fake_cluster'), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + '--os-volume-api-version 3.16 or greater is required', str(exc) + ) + + def test_volume_create_remote_source_host_and_cluster(self): + self._set_mock_microversion('3.16') + arglist = [ + '--remote-source', + 'key=val', + '--host', + 'fake_host', + '--cluster', + 'fake_cluster', + self.new_volume.name, + ] + verifylist = [ + ('remote_source', {'key': 'val'}), + ('host', 'fake_host'), + ('cluster', 'fake_cluster'), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + 'Only one of --host or --cluster needs to be specified', str(exc) + ) + + def test_volume_create_remote_source_no_host_or_cluster(self): + arglist = [ + '--remote-source', + 'key=val', + self.new_volume.name, + ] + verifylist = [ + ('remote_source', {'key': 'val'}), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + 'One of --host or --cluster needs to be specified to ', str(exc) + ) + + def test_volume_create_remote_source_size(self): + arglist = [ + '--size', + str(self.new_volume.size), + '--remote-source', + 'key=val', + self.new_volume.name, + ] + verifylist = [ + ('size', self.new_volume.size), + ('remote_source', {'key': 'val'}), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + '--size, --consistency-group, --hint, --read-only and ' + '--read-write options are not supported', + str(exc), + ) + + def test_volume_create_host_no_remote_source(self): + arglist = [ + '--size', + str(self.new_volume.size), + '--host', + 'fake_host', + self.new_volume.name, + ] + verifylist = [ + ('size', self.new_volume.size), + ('host', 'fake_host'), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + '--host and --cluster options are only supported ', + str(exc), + ) diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 7f5a88fcd..b73b3b2ee 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -18,6 +18,7 @@ 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 @@ -139,8 +140,100 @@ def _check_size_arg(args): raise exceptions.CommandError(msg) def get_parser(self, prog_name): - parser, _ = self._get_parser(prog_name) + parser, source_group = self._get_parser(prog_name) + + source_group.add_argument( + "--remote-source", + metavar="", + action=parseractions.KeyValueAction, + help=_( + "The attribute(s) of the existing remote volume " + "(admin required) (repeat option to specify multiple " + "attributes) e.g.: '--remote-source source-name=test_name " + "--remote-source source-id=test_id'" + ), + ) + parser.add_argument( + "--host", + metavar="", + help=_( + "Cinder host on which the existing volume resides; " + "takes the form: host@backend-name#pool. This is only " + "used along with the --remote-source option." + ), + ) + parser.add_argument( + "--cluster", + metavar="", + help=_( + "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)", + ), + ) return parser def take_action(self, parsed_args): + CreateVolume._check_size_arg(parsed_args) + + volume_client_sdk = self.app.client_manager.sdk_connection.volume + + if ( + parsed_args.host or parsed_args.cluster + ) and not parsed_args.remote_source: + msg = _( + "The --host and --cluster options are only supported " + "with --remote-source parameter." + ) + raise exceptions.CommandError(msg) + + if parsed_args.remote_source: + if ( + parsed_args.size + or parsed_args.consistency_group + or parsed_args.hint + or parsed_args.read_only + or parsed_args.read_write + ): + msg = _( + "The --size, --consistency-group, --hint, --read-only " + "and --read-write options are not supported with the " + "--remote-source parameter." + ) + raise exceptions.CommandError(msg) + if parsed_args.cluster: + if not sdk_utils.supports_microversion( + volume_client_sdk, '3.16' + ): + msg = _( + "--os-volume-api-version 3.16 or greater is required " + "to support the cluster parameter." + ) + raise exceptions.CommandError(msg) + if parsed_args.cluster and parsed_args.host: + msg = _( + "Only one of --host or --cluster needs to be specified " + "to manage a volume." + ) + raise exceptions.CommandError(msg) + if not parsed_args.cluster and not parsed_args.host: + msg = _( + "One of --host or --cluster needs to be specified to " + "manage a volume." + ) + raise exceptions.CommandError(msg) + volume = volume_client_sdk.manage_volume( + host=parsed_args.host, + cluster=parsed_args.cluster, + ref=parsed_args.remote_source, + name=parsed_args.name, + description=parsed_args.description, + volume_type=parsed_args.type, + availability_zone=parsed_args.availability_zone, + metadata=parsed_args.property, + bootable=parsed_args.bootable, + ) + return zip(*sorted(volume.items())) + return self._take_action(parsed_args) diff --git a/releasenotes/notes/add-volume-manage-command-088890446d0e81c7.yaml b/releasenotes/notes/add-volume-manage-command-088890446d0e81c7.yaml new file mode 100644 index 000000000..e30def193 --- /dev/null +++ b/releasenotes/notes/add-volume-manage-command-088890446d0e81c7.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add support for managing volumes with + ``openstack volume create --remote-source + --host `` command. From 00af14b3f207db101b08ff9add5bab03a70da8b8 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Wed, 15 May 2024 06:30:22 +0530 Subject: [PATCH 112/403] Add DeleteVolume class to v3 This is done to support the volume unmanage command. Change-Id: Ib59b1f599be152a25c4b6a31988c28079f552ba9 --- openstackclient/volume/v3/volume.py | 39 +++++++++++++++++++++++++++++ setup.cfg | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index b73b3b2ee..75d8f9ad2 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -237,3 +237,42 @@ def take_action(self, parsed_args): return zip(*sorted(volume.items())) return self._take_action(parsed_args) + + +class DeleteVolume(volume_v2.DeleteVolume): + _description = _("Delete volume(s)") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + 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, cascade=parsed_args.purge + ) + 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) diff --git a/setup.cfg b/setup.cfg index 0bca7c1e6..093bfe7a1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -766,7 +766,7 @@ openstack.volume.v3 = consistency_group_snapshot_show = openstackclient.volume.v2.consistency_group_snapshot:ShowConsistencyGroupSnapshot volume_create = openstackclient.volume.v3.volume:CreateVolume - volume_delete = openstackclient.volume.v2.volume:DeleteVolume + volume_delete = openstackclient.volume.v3.volume:DeleteVolume volume_list = openstackclient.volume.v2.volume:ListVolume volume_migrate = openstackclient.volume.v2.volume:MigrateVolume volume_set = openstackclient.volume.v2.volume:SetVolume From bb459d49a8358ca2a8fb94ba21f2385d059e173d Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Fri, 17 May 2024 00:29:32 +0900 Subject: [PATCH 113/403] Remove clients of retired projects Murano[1], Sahara[2] Senlin[3], and Solum[4] are being retired because of no activity and no maintainer. [1] https://review.opendev.org/c/openstack/governance/+/919358 [2] https://review.opendev.org/c/openstack/governance/+/919374 [3] https://review.opendev.org/c/openstack/governance/+/919347 [4] https://review.opendev.org/c/openstack/governance/+/919211 Change-Id: I20a1f55840abad810517b4f583bb1040e63b6210 --- doc/requirements.txt | 3 --- doc/source/cli/commands.rst | 15 --------------- doc/source/cli/plugin-commands/index.rst | 7 ------- doc/source/cli/plugin-commands/sahara.rst | 4 ---- doc/source/cli/plugin-commands/senlin.rst | 4 ---- doc/source/contributor/plugins.rst | 4 ---- 6 files changed, 37 deletions(-) delete mode 100644 doc/source/cli/plugin-commands/sahara.rst delete mode 100644 doc/source/cli/plugin-commands/senlin.rst diff --git a/doc/requirements.txt b/doc/requirements.txt index e56ff4c80..79e3ded4f 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -19,11 +19,8 @@ python-ironic-inspector-client>=1.5.0 # Apache-2.0 python-magnumclient>=2.3.0 # Apache-2.0 python-manilaclient>=2.0.0 # Apache-2.0 python-mistralclient!=3.2.0,>=3.1.0 # Apache-2.0 -python-muranoclient>=0.8.2 # Apache-2.0 python-neutronclient>=6.7.0 # Apache-2.0 python-octaviaclient>=1.11.0 # Apache-2.0 -python-saharaclient>=1.4.0 # Apache-2.0 -python-senlinclient>=1.1.0 # Apache-2.0 python-troveclient>=3.1.0 # Apache-2.0 python-watcherclient>=2.5.0 # Apache-2.0 python-zaqarclient>=1.0.0 # Apache-2.0 diff --git a/doc/source/cli/commands.rst b/doc/source/cli/commands.rst index 4f3aad313..653e2a50b 100644 --- a/doc/source/cli/commands.rst +++ b/doc/source/cli/commands.rst @@ -191,17 +191,6 @@ conflicts when creating new plugins. For a complete list check out * ``appcontainer service``: (**Application Container (Zun)**) * ``baremetal``: (**Baremetal (Ironic)**) * ``claim``: (**Messaging (Zaqar)**) -* ``cluster``: (**Clustering (Senlin)**) -* ``cluster action``: (**Clustering (Senlin)**) -* ``cluster event``: (**Clustering (Senlin)**) -* ``cluster members``: (**Clustering (Senlin)**) -* ``cluster node``: (**Clustering (Senlin)**) -* ``cluster policy``: (**Clustering (Senlin)**) -* ``cluster policy binding``: (**Clustering (Senlin)**) -* ``cluster policy type``: (**Clustering (Senlin)**) -* ``cluster profile``: (**Clustering (Senlin)**) -* ``cluster profile type``: (**Clustering (Senlin)**) -* ``cluster receiver``: (**Clustering (Senlin)**) * ``coe ca``: (**Container Orchestration Engine (Magnum)**) * ``coe cluster``: (**Container Orchestration Engine (Magnum)**) * ``coe cluster template``: (**Container Orchestration Engine (Magnum)**) @@ -210,10 +199,6 @@ conflicts when creating new plugins. For a complete list check out * ``coe stats``: (**Container Orchestration Engine (Magnum)**) * ``cron trigger``: (**Workflow Engine (Mistral)**) * ``database flavor``: (**Database (Trove)**) -* ``dataprocessing data source``: (**Data Processing (Sahara)**) -* ``dataprocessing image``: (**Data Processing (Sahara)**) -* ``dataprocessing image tags``: (**Data Processing (Sahara)**) -* ``dataprocessing plugin``: (**Data Processing (Sahara)**) * ``loadbalancer``: (**Load Balancer (Octavia)**) * ``loadbalancer healthmonitor``: (**Load Balancer (Octavia)**) * ``loadbalancer l7policy``: (**Load Balancer (Octavia)**) diff --git a/doc/source/cli/plugin-commands/index.rst b/doc/source/cli/plugin-commands/index.rst index 340a8d4da..ac61cc0e0 100644 --- a/doc/source/cli/plugin-commands/index.rst +++ b/doc/source/cli/plugin-commands/index.rst @@ -21,8 +21,6 @@ Plugin Commands neutron octavia placement - sahara - senlin trove watcher zaqar @@ -34,8 +32,3 @@ Plugin Commands .. # cueclient is not in global-requirements .. # list-plugins:: openstack.mb.v1 .. # :detailed: - -.. murano -.. # the murano docs cause warnings and a broken docs build -.. # .. list-plugins:: openstack.application_catalog.v1 -.. # :detailed: diff --git a/doc/source/cli/plugin-commands/sahara.rst b/doc/source/cli/plugin-commands/sahara.rst deleted file mode 100644 index 7c51756a3..000000000 --- a/doc/source/cli/plugin-commands/sahara.rst +++ /dev/null @@ -1,4 +0,0 @@ -sahara ------- - -.. autoprogram-cliff:: openstack.data_processing.v1 diff --git a/doc/source/cli/plugin-commands/senlin.rst b/doc/source/cli/plugin-commands/senlin.rst deleted file mode 100644 index 90929058f..000000000 --- a/doc/source/cli/plugin-commands/senlin.rst +++ /dev/null @@ -1,4 +0,0 @@ -senlin ------- - -.. autoprogram-cliff:: openstack.clustering.v1 diff --git a/doc/source/contributor/plugins.rst b/doc/source/contributor/plugins.rst index f94ad1e29..beebb0310 100644 --- a/doc/source/contributor/plugins.rst +++ b/doc/source/contributor/plugins.rst @@ -34,11 +34,8 @@ The following is a list of projects that are an OpenStackClient plugin. - python-magnumclient - python-manilaclient - python-mistralclient -- python-muranoclient - python-neutronclient\*\* - python-octaviaclient -- python-saharaclient -- python-senlinclient - python-troveclient - python-watcherclient - python-zaqarclient @@ -49,7 +46,6 @@ The following is a list of projects that are an OpenStackClient plugin. The following is a list of projects that are not an OpenStackClient plugin. - python-monascaclient -- python-solumclient Implementation ============== From 7eccd403c711026b8cd596326baa8337114b6c9c Mon Sep 17 00:00:00 2001 From: Tobias Urdin Date: Sun, 19 May 2024 22:17:06 +0200 Subject: [PATCH 114/403] 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 cf6dd62dd14bb3e234b6e9f6ed716db1154e187f Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Sun, 19 May 2024 22:55:03 +0900 Subject: [PATCH 115/403] Drop direct dependency on simplejson The simplejson library is not included by the requirements but it was directly imported. Drop the direct inclusion and use the alias in the requests library to avoid ImportError caused by missing simplejson in the environment. Also fix the missing requests library in requirements. Change-Id: I8713f45c5f2717cc53ba043aaeb479e72f641f78 --- openstackclient/api/api.py | 7 +++---- requirements.txt | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openstackclient/api/api.py b/openstackclient/api/api.py index 15dcdf32f..c8def49e3 100644 --- a/openstackclient/api/api.py +++ b/openstackclient/api/api.py @@ -12,11 +12,10 @@ # """Base API Library""" - from keystoneauth1 import exceptions as ks_exceptions from keystoneauth1 import session as ks_session from osc_lib import exceptions -import simplejson as json +import requests from openstackclient.i18n import _ @@ -118,7 +117,7 @@ def create(self, url, session=None, method=None, **params): # Should this move into _requests()? try: return ret.json() - except json.JSONDecodeError: + except requests.JSONDecodeError: return ret def delete(self, url, session=None, **params): @@ -169,7 +168,7 @@ def list(self, path, session=None, body=None, detailed=False, **params): ) try: return ret.json() - except json.JSONDecodeError: + except requests.JSONDecodeError: return ret # Layered actions built on top of the basic action methods do not diff --git a/requirements.txt b/requirements.txt index a1b87a042..0cc6c7e88 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,5 @@ oslo.i18n>=3.15.3 # Apache-2.0 python-keystoneclient>=3.22.0 # Apache-2.0 python-novaclient>=18.1.0 # Apache-2.0 python-cinderclient>=3.3.0 # Apache-2.0 +requests>=2.14.2 # Apache-2.0 stevedore>=2.0.1 # Apache-2.0 From 548337098186352f49487b4702106cad18fb3a1f Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Fri, 24 May 2024 16:27:01 -0400 Subject: [PATCH 116/403] Remove admin only text from 'port create' help text Specifying a MAC address is only admin only for 'port set' [0], remove it from the 'port create' help text. [0] https://docs.openstack.org/api-ref/network/v2/index.html#ports Change-Id: Ic3296dd03676b460b3d08b1bbaae6f1d132e839d --- openstackclient/network/v2/port.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 95e9f5a8a..fb0ecaff2 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -284,7 +284,7 @@ def _prepare_filter_fixed_ips(client_manager, parsed_args): return ips -def _add_updatable_args(parser): +def _add_updatable_args(parser, create=False): parser.add_argument( '--description', metavar='', @@ -296,7 +296,11 @@ def _add_updatable_args(parser): parser.add_argument( '--mac-address', metavar='', - help=_("MAC address of this port (admin only)"), + help=( + _("MAC address of this port") + if create + else _("MAC address of this port (admin only)") + ), ) parser.add_argument( '--device-owner', @@ -452,7 +456,7 @@ def get_parser(self, prog_name): required=True, help=_("Network this port belongs to (name or ID)"), ) - _add_updatable_args(parser) + _add_updatable_args(parser, create=True) fixed_ip = parser.add_mutually_exclusive_group() fixed_ip.add_argument( '--fixed-ip', From fdc2763ac2052f121e919a8513119e95ddd362d8 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Wed, 15 May 2024 08:02:12 +0530 Subject: [PATCH 117/403] Add support for volume unmanage This patch adds support for unmanaging a volume with the ``openstack volume delete --remote`` command. Change-Id: Id71681e817f6e56b4ef553079f0bcfac8252d3cf --- .../tests/unit/volume/v3/test_volume.py | 101 ++++++++++++++++++ openstackclient/volume/v3/volume.py | 17 ++- ...ume-unmanage-support-9b7139e5e948de77.yaml | 5 + 3 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-volume-unmanage-support-9b7139e5e948de77.yaml diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index ad6595bea..3aaaae00c 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -427,3 +427,104 @@ def test_volume_create_host_no_remote_source(self): '--host and --cluster options are only supported ', str(exc), ) + + +class TestVolumeDelete(BaseVolumeTest): + 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) + + 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] + verifylist = [ + ("remote", True), + ("force", False), + ("purge", False), + ("volumes", [vol.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} + ) + + arglist = ['--remote'] + arglist += [v.id for v in volumes] + verifylist = [ + ('remote', True), + ('force', False), + ('purge', False), + ('volumes', arglist[1:]), + ] + 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}) + + arglist = [ + '--remote', + '--purge', + vol.id, + ] + verifylist = [ + ('remote', True), + ('force', False), + ('purge', True), + ('volumes', [vol.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 and --purge options are not supported with the " + "--remote parameter.", + str(exc), + ) + + def test_volume_delete_remote_with_force(self): + vol = sdk_fakes.generate_fake_resource(_volume.Volume, **{'size': 1}) + + arglist = [ + '--remote', + '--force', + vol.id, + ] + verifylist = [ + ('remote', True), + ('force', True), + ('purge', False), + ('volumes', [vol.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 and --purge options are not supported with the " + "--remote parameter.", + str(exc), + ) diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 75d8f9ad2..844831839 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -244,16 +244,31 @@ class DeleteVolume(volume_v2.DeleteVolume): def get_parser(self, prog_name): parser = super().get_parser(prog_name) + parser.add_argument( + '--remote', + action='store_true', + help=_("Specify this parameter to unmanage a volume."), + ) 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 and (parsed_args.force or parsed_args.purge): + msg = _( + "The --force and --purge options are not " + "supported with the --remote parameter." + ) + raise exceptions.CommandError(msg) + for i in parsed_args.volumes: try: volume_obj = utils.find_resource(volume_client.volumes, i) - if parsed_args.force: + if parsed_args.remote: + volume_client_sdk.unmanage_volume(volume_obj.id) + elif parsed_args.force: volume_client.volumes.force_delete(volume_obj.id) else: volume_client.volumes.delete( diff --git a/releasenotes/notes/add-volume-unmanage-support-9b7139e5e948de77.yaml b/releasenotes/notes/add-volume-unmanage-support-9b7139e5e948de77.yaml new file mode 100644 index 000000000..cba1e60cd --- /dev/null +++ b/releasenotes/notes/add-volume-unmanage-support-9b7139e5e948de77.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add support for unmanaging volumes with + ``openstack volume delete --remote `` command. From 887f1e9385a03c98db17ac2941c67a6fa0fd3612 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 26 Apr 2024 13:14:32 +0100 Subject: [PATCH 118/403] tox: Add testenv descriptions Change-Id: I0a7880d72248e94690a973d3a1fd33c176ed658e Signed-off-by: Stephen Finucane --- tox.ini | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index b97f72ace..e7c84c511 100644 --- a/tox.ini +++ b/tox.ini @@ -3,6 +3,8 @@ minversion = 4.3.0 envlist = py3,pep8 [testenv] +description = + Run unit tests. usedevelop = true setenv = OS_STDOUT_CAPTURE=1 @@ -16,6 +18,8 @@ commands = stestr run {posargs} [testenv:pep8] +description = + Run style checks. skip_install = true deps = pre-commit @@ -23,6 +27,8 @@ commands = pre-commit run --all-files --show-diff-on-failure [testenv:bandit] +description = + Run bandit security checks. skip_install = true deps = pre-commit @@ -39,6 +45,8 @@ commands = stestr run {posargs} [testenv:functional{,-tips,-py38,-py39,-py310,-py311,-py312}] +description = + Run functional tests. setenv = OS_TEST_PATH=./openstackclient/tests/functional passenv = @@ -52,6 +60,8 @@ commands = {[testenv]commands} [testenv:venv] +description = + Run specified command in a virtual environment with all dependencies installed. deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt @@ -60,22 +70,28 @@ commands = {posargs} [testenv:cover] +description = + Run unit tests and generate coverage report. setenv = - VIRTUAL_ENV={envdir} + {[testenv]setenv} PYTHON=coverage run --source openstackclient --parallel-mode commands = - stestr -q run {posargs} + stestr run {posargs} coverage combine coverage html -d cover coverage xml -o cover/coverage.xml [testenv:debug] +description = + Run specified tests through oslo_debug_helper, which allows use of pdb. passenv = OS_* commands = oslo_debug_helper -t openstackclient/tests {posargs} [testenv:docs] +description = + Build documentation in HTML format. deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt @@ -86,6 +102,8 @@ commands = whereto doc/build/html/.htaccess doc/test/redirect-tests.txt [testenv:releasenotes] +description = + Build release note documentation in HTML format. deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt From de9d0f9e1b511c6aca7dea57f62331bb2638ef18 Mon Sep 17 00:00:00 2001 From: ArtofBugs Date: Thu, 29 Feb 2024 13:01:19 -0800 Subject: [PATCH 119/403] Identity: Migrate 'role assignment' commands to SDK Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/913448 Change-Id: I579775d2dc4110951e934e00b51bf8d7546e207b --- .../identity/v3/role_assignment.py | 230 +++--- .../tests/functional/identity/v3/common.py | 9 + .../identity/v3/test_role_assignment.py | 210 +++++ .../unit/identity/v3/test_role_assignment.py | 737 ++++++++++-------- ...le-assignment-to-sdk-e6e52bef467b4e4c.yaml | 4 + 5 files changed, 723 insertions(+), 467 deletions(-) create mode 100644 openstackclient/tests/functional/identity/v3/test_role_assignment.py create mode 100644 releasenotes/notes/migrate-role-assignment-to-sdk-e6e52bef467b4e4c.yaml diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py index 81de1535f..975329793 100644 --- a/openstackclient/identity/v3/role_assignment.py +++ b/openstackclient/identity/v3/role_assignment.py @@ -13,13 +13,53 @@ """Identity v3 Assignment action implementations""" +from openstack import exceptions as sdk_exceptions from osc_lib.command import command -from osc_lib import utils from openstackclient.i18n import _ from openstackclient.identity import common +def _format_role_assignment_(assignment, include_names): + def _get_names(attr): + return ( + ( + attr['name'] + + ( + "@" + domain['name'] + if (domain := attr.get('domain')) + else '' + ) + ) + or '' + if attr + else '' + ) + + def _get_ids(attr): + return attr['id'] or '' if attr else '' + + func = _get_names if include_names else _get_ids + return ( + func(assignment.role), + func(assignment.user), + func(assignment.group), + func(assignment.scope.get('project')), + func(assignment.scope.get('domain')), + 'all' if assignment.scope.get("system") else '', + assignment.scope.get("OS-INHERIT:inherited_to") == 'projects', + ) + + +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") @@ -28,7 +68,7 @@ def get_parser(self, prog_name): parser.add_argument( '--effective', action="store_true", - default=False, + default=None, help=_('Returns only effective role assignments'), ) parser.add_argument( @@ -88,81 +128,74 @@ def get_parser(self, prog_name): ) return parser - def _as_tuple(self, assignment): - return ( - assignment.role, - assignment.user, - assignment.group, - assignment.project, - assignment.domain, - assignment.system, - assignment.inherited, - ) - 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 - role = None + role_id = None role_domain_id = None if parsed_args.role_domain: - role_domain_id = common.find_domain( - identity_client, parsed_args.role_domain - ).id + role_domain_id = _find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.role_domain, + ) if parsed_args.role: - role = utils.find_resource( - identity_client.roles, - parsed_args.role, + role_id = _find_sdk_id( + identity_client.find_role, + name_or_id=parsed_args.role, domain_id=role_domain_id, ) - user = None + user_id = None if parsed_args.user: - user = common.find_user( - identity_client, - parsed_args.user, - parsed_args.user_domain, + user_id = _find_sdk_id( + identity_client.find_user, + name_or_id=parsed_args.user, + domain_id=parsed_args.user_domain, ) elif parsed_args.authuser: if auth_ref: - user = common.find_user(identity_client, auth_ref.user_id) + user_id = _find_sdk_id( + identity_client.find_user, + name_or_id=auth_ref.user_id, + ) system = None if parsed_args.system: system = parsed_args.system - domain = None + domain_id = None if parsed_args.domain: - domain = common.find_domain( - identity_client, - parsed_args.domain, + domain_id = _find_sdk_id( + identity_client.find_domain, + name_or_id=parsed_args.domain, ) - project = None + project_id = None if parsed_args.project: - project = common.find_project( - identity_client, - common._get_token_resource( + project_id = _find_sdk_id( + identity_client.find_project, + name_or_id=common._get_token_resource( identity_client, 'project', parsed_args.project ), - parsed_args.project_domain, + domain_id=parsed_args.project_domain, ) elif parsed_args.authproject: if auth_ref: - project = common.find_project( - identity_client, auth_ref.project_id + project_id = _find_sdk_id( + identity_client.find_project, + name_or_id=auth_ref.project_id, ) - group = None + group_id = None if parsed_args.group: - group = common.find_group( - identity_client, - parsed_args.group, - parsed_args.group_domain, + group_id = _find_sdk_id( + identity_client.find_group, + name_or_id=parsed_args.group, + domain_id=parsed_args.group_domain, ) - include_names = True if parsed_args.names else False - effective = True if parsed_args.effective else False + include_names = True if parsed_args.names else None columns = ( 'Role', 'User', @@ -174,104 +207,23 @@ def take_action(self, parsed_args): ) inherited_to = 'projects' if parsed_args.inherited else None - data = identity_client.role_assignments.list( - domain=domain, - user=user, - group=group, - project=project, - system=system, - role=role, - effective=effective, - os_inherit_extension_inherited_to=inherited_to, + + data = identity_client.role_assignments( + role_id=role_id, + user_id=user_id, + group_id=group_id, + scope_project_id=project_id, + scope_domain_id=domain_id, + scope_system=system, + effective=parsed_args.effective, include_names=include_names, + inherited_to=inherited_to, ) data_parsed = [] for assignment in data: - # Removing the extra "scope" layer in the assignment json - scope = assignment.scope - if 'project' in scope: - if include_names: - prj = '@'.join( - [ - scope['project']['name'], - scope['project']['domain']['name'], - ] - ) - setattr(assignment, 'project', prj) - else: - setattr(assignment, 'project', scope['project']['id']) - assignment.domain = '' - assignment.system = '' - elif 'domain' in scope: - if include_names: - setattr(assignment, 'domain', scope['domain']['name']) - else: - setattr(assignment, 'domain', scope['domain']['id']) - assignment.project = '' - assignment.system = '' - elif 'system' in scope: - # NOTE(lbragstad): If, or when, keystone supports role - # assignments on subsets of a system, this will have to evolve - # to handle that case instead of hardcoding to the entire - # system. - setattr(assignment, 'system', 'all') - assignment.domain = '' - assignment.project = '' - else: - assignment.system = '' - assignment.domain = '' - assignment.project = '' - - inherited = scope.get('OS-INHERIT:inherited_to') == 'projects' - assignment.inherited = inherited - - del assignment.scope - - if hasattr(assignment, 'user'): - if include_names: - usr = '@'.join( - [ - assignment.user['name'], - assignment.user['domain']['name'], - ] - ) - setattr(assignment, 'user', usr) - else: - setattr(assignment, 'user', assignment.user['id']) - assignment.group = '' - elif hasattr(assignment, 'group'): - if include_names: - grp = '@'.join( - [ - assignment.group['name'], - assignment.group['domain']['name'], - ] - ) - setattr(assignment, 'group', grp) - else: - setattr(assignment, 'group', assignment.group['id']) - assignment.user = '' - else: - assignment.user = '' - assignment.group = '' - - if hasattr(assignment, 'role'): - if include_names: - # TODO(henry-nash): If this is a domain specific role it - # would be good show this as role@domain, although this - # domain info is not yet included in the response from the - # server. Although we could get it by re-reading the role - # from the ID, let's wait until the server does the right - # thing. - setattr(assignment, 'role', assignment.role['name']) - else: - setattr(assignment, 'role', assignment.role['id']) - else: - assignment.role = '' - - # Creating a tuple from data object fields - # (including the blank ones) - data_parsed.append(self._as_tuple(assignment)) + data_parsed.append( + _format_role_assignment_(assignment, include_names) + ) return columns, tuple(data_parsed) diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py index 8607c0573..e3c32e35b 100644 --- a/openstackclient/tests/functional/identity/v3/common.py +++ b/openstackclient/tests/functional/identity/v3/common.py @@ -103,6 +103,15 @@ class IdentityTests(base.TestCase): 'Implied Role ID', 'Implied Role Name', ] + ROLE_ASSIGNMENT_LIST_HEADERS = [ + 'Role', + 'User', + 'Group', + 'Project', + 'Domain', + 'System', + 'Inherited', + ] REGISTERED_LIMIT_FIELDS = [ 'id', 'service_id', diff --git a/openstackclient/tests/functional/identity/v3/test_role_assignment.py b/openstackclient/tests/functional/identity/v3/test_role_assignment.py new file mode 100644 index 000000000..a3927e7e1 --- /dev/null +++ b/openstackclient/tests/functional/identity/v3/test_role_assignment.py @@ -0,0 +1,210 @@ +# 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.identity.v3 import common + + +class RoleAssignmentTests(common.IdentityTests): + def test_role_assignment_list_no_filters(self): + raw_output = self.openstack('role assignment list') + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + + def test_role_assignment_list_user_role_system(self): + role_name = self._create_dummy_role() + username = self._create_dummy_user() + system = 'all' + raw_output = self.openstack( + 'role add ' + '--user %(user)s ' + '--system %(system)s ' + '%(role)s' + % { + 'user': username, + 'system': system, + 'role': role_name, + } + ) + self.addCleanup( + self.openstack, + 'role remove ' + '--user %(user)s ' + '--system %(system)s ' + '%(role)s' + % { + 'user': username, + 'system': system, + 'role': role_name, + }, + ) + self.assertEqual(0, len(raw_output)) + + raw_output = self.openstack( + 'role assignment list ' '--user %(user)s ' % {'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} + ) + 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} + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + + def test_role_assignment_list_group(self): + role_name = self._create_dummy_role() + group = self._create_dummy_group() + system = 'all' + raw_output = self.openstack( + 'role add ' + '--group %(group)s ' + '--system %(system)s ' + '%(role)s' + % { + 'group': group, + 'system': system, + 'role': role_name, + } + ) + self.addCleanup( + self.openstack, + 'role remove ' + '--group %(group)s ' + '--system %(system)s ' + '%(role)s' + % { + 'group': group, + 'system': system, + 'role': role_name, + }, + ) + self.assertEqual(0, len(raw_output)) + raw_output = self.openstack( + 'role assignment list ' '--group %(group)s ' % {'group': group} + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + + def test_role_assignment_list_domain(self): + role_name = self._create_dummy_role() + 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, + } + ) + self.addCleanup( + self.openstack, + 'role remove ' + '--domain %(domain)s ' + '--user %(user)s ' + '%(role)s' + % { + 'domain': self.domain_name, + 'user': username, + 'role': role_name, + }, + ) + self.assertEqual(0, len(raw_output)) + raw_output = self.openstack( + 'role assignment list ' + '--domain %(domain)s ' % {'domain': self.domain_name} + ) + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + + def test_role_assignment_list_project(self): + role_name = self._create_dummy_role() + 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, + } + ) + self.addCleanup( + self.openstack, + 'role remove ' + '--project %(project)s ' + '--user %(user)s ' + '%(role)s' + % { + 'project': self.project_name, + 'user': username, + 'role': role_name, + }, + ) + self.assertEqual(0, len(raw_output)) + raw_output = self.openstack( + 'role assignment list ' + '--project %(project)s ' % {'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') + 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') + 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') + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + + def test_role_assignment_list_inherited(self): + role_name = self._create_dummy_role() + username = self._create_dummy_user() + raw_output = self.openstack( + 'role add ' + '--project %(project)s ' + '--user %(user)s ' + '--inherited ' + '%(role)s' + % { + 'project': self.project_name, + 'user': username, + 'role': role_name, + } + ) + self.assertEqual(0, len(raw_output)) + + raw_output = self.openstack('role assignment list --inherited') + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) + + def test_role_assignment_list_names(self): + raw_output = self.openstack('role assignment list --names') + items = self.parse_listing(raw_output) + self.assert_table_structure(items, self.ROLE_ASSIGNMENT_LIST_HEADERS) diff --git a/openstackclient/tests/unit/identity/v3/test_role_assignment.py b/openstackclient/tests/unit/identity/v3/test_role_assignment.py index fa793bc82..ec7c7da56 100644 --- a/openstackclient/tests/unit/identity/v3/test_role_assignment.py +++ b/openstackclient/tests/unit/identity/v3/test_role_assignment.py @@ -11,20 +11,21 @@ # under the License. # -import copy from unittest import mock +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 role_assignment as _role_assignment +from openstack.identity.v3 import user as _user +from openstack.test import fakes as sdk_fakes from openstackclient.identity.v3 import role_assignment -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestRoleAssignment(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - -class TestRoleAssignmentList(TestRoleAssignment): +class TestRoleAssignmentList(identity_fakes.TestIdentityv3): columns = ( 'Role', 'User', @@ -36,50 +37,61 @@ class TestRoleAssignmentList(TestRoleAssignment): ) def setUp(self): - super(TestRoleAssignment, self).setUp() - - # Get a shortcut to the UserManager Mock - self.users_mock = self.identity_client.users - self.users_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 DomainManager Mock - self.domains_mock = self.identity_client.domains - self.domains_mock.reset_mock() - - # Get a shortcut to the ProjectManager Mock - self.projects_mock = self.identity_client.projects - self.projects_mock.reset_mock() + super().setUp() - # Get a shortcut to the RoleManager Mock - self.roles_mock = self.identity_client.roles - self.roles_mock.reset_mock() + self.user = sdk_fakes.generate_fake_resource(_user.User) + self.group = sdk_fakes.generate_fake_resource(_group.Group) + self.domain = sdk_fakes.generate_fake_resource(_domain.Domain) + self.project = sdk_fakes.generate_fake_resource(_project.Project) + self.role = sdk_fakes.generate_fake_resource(_role.Role) + self.assignment_with_project_id_and_user_id = ( + sdk_fakes.generate_fake_resource( + resource_type=_role_assignment.RoleAssignment, + role={'id': self.role.id}, + scope={'project': {'id': self.project.id}}, + user={'id': self.user.id}, + ) + ) + self.assignment_with_project_id_and_group_id = ( + sdk_fakes.generate_fake_resource( + resource_type=_role_assignment.RoleAssignment, + role={'id': self.role.id}, + scope={'project': {'id': self.project.id}}, + group={'id': self.group.id}, + ) + ) + self.assignment_with_domain_id_and_user_id = ( + sdk_fakes.generate_fake_resource( + resource_type=_role_assignment.RoleAssignment, + role={'id': self.role.id}, + scope={'domain': {'id': self.domain.id}}, + user={'id': self.user.id}, + ) + ) + self.assignment_with_domain_id_and_group_id = ( + sdk_fakes.generate_fake_resource( + resource_type=_role_assignment.RoleAssignment, + role={'id': self.role.id}, + scope={'domain': {'id': self.domain.id}}, + group={'id': self.group.id}, + ) + ) - self.role_assignments_mock = self.identity_client.role_assignments - self.role_assignments_mock.reset_mock() + 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_project.return_value = self.project + self.identity_sdk_client.find_domain.return_value = self.domain + self.identity_sdk_client.find_role.return_value = self.role # Get the command object to test self.cmd = role_assignment.ListRoleAssignment(self.app, None) def test_role_assignment_list_no_filters(self): - self.role_assignments_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID - ), - loaded=True, - ), - fakes.FakeResource( - None, - copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID - ), - loaded=True, - ), + self.identity_sdk_client.role_assignments.return_value = [ + self.assignment_with_project_id_and_user_id, + self.assignment_with_project_id_and_group_id, + self.assignment_with_domain_id_and_user_id, + self.assignment_with_domain_id_and_group_id, ] arglist = [] @@ -91,35 +103,53 @@ def test_role_assignment_list_no_filters(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.role_assignments_mock.list.assert_called_with( - domain=None, - system=None, - group=None, - effective=False, - role=None, - user=None, - project=None, - os_inherit_extension_inherited_to=None, - include_names=False, + self.identity_sdk_client.role_assignments.assert_called_with( + role_id=None, + user_id=None, + 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 = ( ( - identity_fakes.role_id, - identity_fakes.user_id, + self.role.id, + self.user.id, + '', + self.project.id, + '', + '', + False, + ), + ( + self.role.id, + '', + self.group.id, + self.project.id, + '', + '', + False, + ), + ( + self.role.id, + self.user.id, '', - identity_fakes.project_id, '', + self.domain.id, '', False, ), ( - identity_fakes.role_id, + self.role.id, '', - identity_fakes.group_id, - identity_fakes.project_id, + self.group.id, '', + self.domain.id, '', False, ), @@ -127,32 +157,85 @@ def test_role_assignment_list_no_filters(self): self.assertEqual(datalist, tuple(data)) def test_role_assignment_list_user(self): - self.role_assignments_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID - ), - loaded=True, + 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] + verifylist = [ + ('user', self.user.name), + ('group', 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.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, ), - fakes.FakeResource( - None, - copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID - ), - loaded=True, + ( + self.role.id, + self.user.id, + '', + self.project.id, + '', + '', + False, ), + ) + self.assertEqual(datalist, tuple(data)) + + @mock.patch.object(_user.User, 'find') + def test_role_assignment_list_user_not_found(self, find_mock): + self.identity_sdk_client.role_assignments.return_value = [ + self.assignment_with_domain_id_and_user_id, + self.assignment_with_project_id_and_user_id, + ] + self.identity_sdk_client.find_user.side_effect = [ + sdk_exc.ForbiddenException, + sdk_exc.ForbiddenException, ] - arglist = ['--user', identity_fakes.user_name] + arglist = ['--user', self.user.id] verifylist = [ - ('user', identity_fakes.user_name), + ('user', self.user.id), ('group', None), ('system', None), ('domain', None), ('project', None), ('role', None), - ('effective', False), + ('effective', None), ('inherited', False), ('names', False), ] @@ -163,34 +246,34 @@ def test_role_assignment_list_user(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.role_assignments_mock.list.assert_called_with( - domain=None, - system=None, - user=self.users_mock.get(), - group=None, - project=None, - role=None, - effective=False, - os_inherit_extension_inherited_to=None, - include_names=False, + 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 = ( ( - identity_fakes.role_id, - identity_fakes.user_id, + self.role.id, + self.user.id, '', '', - identity_fakes.domain_id, + self.domain.id, '', False, ), ( - identity_fakes.role_id, - identity_fakes.user_id, + self.role.id, + self.user.id, '', - identity_fakes.project_id, + self.project.id, '', '', False, @@ -199,32 +282,20 @@ def test_role_assignment_list_user(self): self.assertEqual(datalist, tuple(data)) def test_role_assignment_list_group(self): - self.role_assignments_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_GROUP_ID - ), - loaded=True, - ), - fakes.FakeResource( - None, - copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID - ), - loaded=True, - ), + 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', identity_fakes.group_name] + arglist = ['--group', self.group.name] verifylist = [ ('user', None), - ('group', identity_fakes.group_name), + ('group', self.group.name), ('system', None), ('domain', None), ('project', None), ('role', None), - ('effective', False), + ('effective', None), ('inherited', False), ('names', False), ] @@ -235,34 +306,34 @@ def test_role_assignment_list_group(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.role_assignments_mock.list.assert_called_with( - domain=None, - system=None, - group=self.groups_mock.get(), - effective=False, - project=None, - role=None, - user=None, - os_inherit_extension_inherited_to=None, - include_names=False, + 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 = ( ( - identity_fakes.role_id, + self.role.id, '', - identity_fakes.group_id, + self.group.id, '', - identity_fakes.domain_id, + self.domain.id, '', False, ), ( - identity_fakes.role_id, + self.role.id, '', - identity_fakes.group_id, - identity_fakes.project_id, + self.group.id, + self.project.id, '', '', False, @@ -271,32 +342,20 @@ def test_role_assignment_list_group(self): self.assertEqual(datalist, tuple(data)) def test_role_assignment_list_domain(self): - self.role_assignments_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID - ), - loaded=True, - ), - fakes.FakeResource( - None, - copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_GROUP_ID - ), - loaded=True, - ), + self.identity_sdk_client.role_assignments.return_value = [ + self.assignment_with_domain_id_and_user_id, + self.assignment_with_domain_id_and_group_id, ] - arglist = ['--domain', identity_fakes.domain_name] + arglist = ['--domain', self.domain.name] verifylist = [ ('user', None), ('group', None), ('system', None), - ('domain', identity_fakes.domain_name), + ('domain', self.domain.name), ('project', None), ('role', None), - ('effective', False), + ('effective', None), ('inherited', False), ('names', False), ] @@ -307,35 +366,35 @@ def test_role_assignment_list_domain(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.role_assignments_mock.list.assert_called_with( - domain=self.domains_mock.get(), - system=None, - group=None, - effective=False, - project=None, - role=None, - user=None, - os_inherit_extension_inherited_to=None, - include_names=False, + self.identity_sdk_client.role_assignments.assert_called_with( + role_id=None, + user_id=None, + group_id=None, + scope_project_id=None, + scope_domain_id=self.domain.id, + scope_system=None, + effective=None, + include_names=None, + inherited_to=None, ) self.assertEqual(self.columns, columns) datalist = ( ( - identity_fakes.role_id, - identity_fakes.user_id, + self.role.id, + self.user.id, '', '', - identity_fakes.domain_id, + self.domain.id, '', False, ), ( - identity_fakes.role_id, + self.role.id, '', - identity_fakes.group_id, + self.group.id, '', - identity_fakes.domain_id, + self.domain.id, '', False, ), @@ -343,32 +402,20 @@ def test_role_assignment_list_domain(self): self.assertEqual(datalist, tuple(data)) def test_role_assignment_list_project(self): - self.role_assignments_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID - ), - loaded=True, - ), - fakes.FakeResource( - None, - copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_GROUP_ID - ), - loaded=True, - ), + 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', identity_fakes.project_name] + arglist = ['--project', self.project.name] verifylist = [ ('user', None), ('group', None), ('system', None), ('domain', None), - ('project', identity_fakes.project_name), + ('project', self.project.name), ('role', None), - ('effective', False), + ('effective', None), ('inherited', False), ('names', False), ] @@ -379,34 +426,34 @@ def test_role_assignment_list_project(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.role_assignments_mock.list.assert_called_with( - domain=None, - system=None, - group=None, - effective=False, - project=self.projects_mock.get(), - role=None, - user=None, - os_inherit_extension_inherited_to=None, - include_names=False, + 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 = ( ( - identity_fakes.role_id, - identity_fakes.user_id, + self.role.id, + self.user.id, '', - identity_fakes.project_id, + self.project.id, '', '', False, ), ( - identity_fakes.role_id, + self.role.id, '', - identity_fakes.group_id, - identity_fakes.project_id, + self.group.id, + self.project.id, '', '', False, @@ -416,17 +463,11 @@ def test_role_assignment_list_project(self): def test_role_assignment_list_def_creds(self): auth_ref = self.app.client_manager.auth_ref = mock.Mock() - auth_ref.project_id.return_value = identity_fakes.project_id - auth_ref.user_id.return_value = identity_fakes.user_id - - self.role_assignments_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID - ), - loaded=True, - ), + auth_ref.project_id.return_value = self.project.id + auth_ref.user_id.return_value = self.user.id + + self.identity_sdk_client.role_assignments.return_value = [ + self.assignment_with_project_id_and_user_id, ] arglist = [ @@ -440,7 +481,7 @@ def test_role_assignment_list_def_creds(self): ('domain', None), ('project', None), ('role', None), - ('effective', False), + ('effective', None), ('inherited', False), ('names', False), ('authuser', True), @@ -453,25 +494,25 @@ def test_role_assignment_list_def_creds(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.role_assignments_mock.list.assert_called_with( - domain=None, - system=None, - user=self.users_mock.get(), - group=None, - project=self.projects_mock.get(), - role=None, - effective=False, - os_inherit_extension_inherited_to=None, - include_names=False, + self.identity_sdk_client.role_assignments.assert_called_with( + role_id=None, + user_id=self.user.id, + 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 = ( ( - identity_fakes.role_id, - identity_fakes.user_id, + self.role.id, + self.user.id, '', - identity_fakes.project_id, + self.project.id, '', '', False, @@ -480,21 +521,9 @@ def test_role_assignment_list_def_creds(self): self.assertEqual(datalist, tuple(data)) def test_role_assignment_list_effective(self): - self.role_assignments_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID - ), - loaded=True, - ), - fakes.FakeResource( - None, - copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID - ), - loaded=True, - ), + self.identity_sdk_client.role_assignments.return_value = [ + self.assignment_with_project_id_and_user_id, + self.assignment_with_domain_id_and_user_id, ] arglist = ['--effective'] @@ -516,35 +545,35 @@ def test_role_assignment_list_effective(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.role_assignments_mock.list.assert_called_with( - domain=None, - system=None, - group=None, + self.identity_sdk_client.role_assignments.assert_called_with( + role_id=None, + user_id=None, + group_id=None, + scope_project_id=None, + scope_domain_id=None, + scope_system=None, effective=True, - project=None, - role=None, - user=None, - os_inherit_extension_inherited_to=None, - include_names=False, + include_names=None, + inherited_to=None, ) self.assertEqual(self.columns, columns) datalist = ( ( - identity_fakes.role_id, - identity_fakes.user_id, + self.role.id, + self.user.id, '', - identity_fakes.project_id, + self.project.id, '', '', False, ), ( - identity_fakes.role_id, - identity_fakes.user_id, + self.role.id, + self.user.id, '', '', - identity_fakes.domain_id, + self.domain.id, '', False, ), @@ -552,15 +581,31 @@ def test_role_assignment_list_effective(self): self.assertEqual(tuple(data), datalist) def test_role_assignment_list_inherited(self): - fake_assignment_a = copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID_INHERITED + assignment_with_project_id_and_user_id_inherited = ( + sdk_fakes.generate_fake_resource( + resource_type=_role_assignment.RoleAssignment, + role={'id': self.role.id}, + scope={ + 'project': {'id': self.project.id}, + 'OS-INHERIT:inherited_to': 'projects', + }, + user={'id': self.user.id}, + ) ) - fake_assignment_b = copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INHERITED + assignment_with_domain_id_and_group_id_inherited = ( + sdk_fakes.generate_fake_resource( + resource_type=_role_assignment.RoleAssignment, + role={'id': self.role.id}, + scope={ + 'domain': {'id': self.domain.id}, + 'OS-INHERIT:inherited_to': 'projects', + }, + group={'id': self.group.id}, + ) ) - self.role_assignments_mock.list.return_value = [ - fakes.FakeResource(None, fake_assignment_a, loaded=True), - fakes.FakeResource(None, fake_assignment_b, loaded=True), + self.identity_sdk_client.role_assignments.return_value = [ + assignment_with_project_id_and_user_id_inherited, + assignment_with_domain_id_and_group_id_inherited, ] arglist = ['--inherited'] @@ -571,7 +616,7 @@ def test_role_assignment_list_inherited(self): ('domain', None), ('project', None), ('role', None), - ('effective', False), + ('effective', None), ('inherited', True), ('names', False), ] @@ -582,35 +627,35 @@ def test_role_assignment_list_inherited(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.role_assignments_mock.list.assert_called_with( - domain=None, - system=None, - group=None, - effective=False, - project=None, - role=None, - user=None, - os_inherit_extension_inherited_to='projects', - include_names=False, + self.identity_sdk_client.role_assignments.assert_called_with( + role_id=None, + user_id=None, + group_id=None, + scope_project_id=None, + scope_domain_id=None, + scope_system=None, + effective=None, + include_names=None, + inherited_to='projects', ) self.assertEqual(self.columns, columns) datalist = ( ( - identity_fakes.role_id, - identity_fakes.user_id, + self.role.id, + self.user.id, '', - identity_fakes.project_id, + self.project.id, '', '', True, ), ( - identity_fakes.role_id, - identity_fakes.user_id, + self.role.id, '', + self.group.id, '', - identity_fakes.domain_id, + self.domain.id, '', True, ), @@ -618,15 +663,45 @@ def test_role_assignment_list_inherited(self): self.assertEqual(datalist, tuple(data)) def test_role_assignment_list_include_names(self): - fake_role_assignment_a = copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_PROJECT_ID_AND_USER_ID_INCLUDE_NAMES + assignment_with_project_id_and_user_id_include_names = ( + sdk_fakes.generate_fake_resource( + resource_type=_role_assignment.RoleAssignment, + role={'id': self.role.id, 'name': self.role.name}, + scope={ + 'project': { + 'domain': { + 'id': self.domain.id, + 'name': self.domain.name, + }, + 'id': self.project.id, + 'name': self.project.name, + } + }, + user={ + 'domain': {'id': self.domain.id, 'name': self.domain.name}, + 'id': self.user.id, + 'name': self.user.name, + }, + ) ) - fake_role_assignment_b = copy.deepcopy( - identity_fakes.ASSIGNMENT_WITH_DOMAIN_ID_AND_USER_ID_INCLUDE_NAMES + assignment_with_domain_id_and_group_id_include_names = ( + sdk_fakes.generate_fake_resource( + resource_type=_role_assignment.RoleAssignment, + role={'id': self.role.id, 'name': self.role.name}, + scope={ + 'domain': {'id': self.domain.id, 'name': self.domain.name} + }, + group={ + 'domain': {'id': self.domain.id, 'name': self.domain.name}, + 'id': self.group.id, + 'name': self.group.name, + }, + ) ) - self.role_assignments_mock.list.return_value = [ - fakes.FakeResource(None, fake_role_assignment_a, loaded=True), - fakes.FakeResource(None, fake_role_assignment_b, loaded=True), + + self.identity_sdk_client.role_assignments.return_value = [ + assignment_with_project_id_and_user_id_include_names, + assignment_with_domain_id_and_group_id_include_names, ] arglist = ['--names'] @@ -637,7 +712,7 @@ def test_role_assignment_list_include_names(self): ('domain', None), ('project', None), ('role', None), - ('effective', False), + ('effective', None), ('inherited', False), ('names', True), ] @@ -650,16 +725,16 @@ def test_role_assignment_list_include_names(self): # correct information columns, data = self.cmd.take_action(parsed_args) - self.role_assignments_mock.list.assert_called_with( - domain=None, - system=None, - group=None, - effective=False, - project=None, - role=None, - user=None, - os_inherit_extension_inherited_to=None, + self.identity_sdk_client.role_assignments.assert_called_with( + role_id=None, + user_id=None, + group_id=None, + scope_project_id=None, + scope_domain_id=None, + scope_system=None, + effective=None, include_names=True, + inherited_to=None, ) collist = ( @@ -673,48 +748,54 @@ def test_role_assignment_list_include_names(self): ) self.assertEqual(columns, collist) - datalist1 = ( + datalist = ( ( - identity_fakes.role_name, - '@'.join( - [identity_fakes.user_name, identity_fakes.domain_name] - ), + self.role.name, + '@'.join([self.user.name, self.domain.name]), '', - '@'.join( - [identity_fakes.project_name, identity_fakes.domain_name] - ), + '@'.join([self.project.name, self.domain.name]), '', '', False, ), ( - identity_fakes.role_name, - '@'.join( - [identity_fakes.user_name, identity_fakes.domain_name] - ), + self.role.name, '', + '@'.join([self.group.name, self.domain.name]), '', - identity_fakes.domain_name, + self.domain.name, '', False, ), ) - self.assertEqual(tuple(data), datalist1) + self.assertEqual(datalist, tuple(data)) def test_role_assignment_list_domain_role(self): - self.role_assignments_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ASSIGNMENT_WITH_DOMAIN_ROLE), - loaded=True, - ), + domain_2 = sdk_fakes.generate_fake_resource(_domain.Domain) + # Create new role with same name but different domain + role_2 = sdk_fakes.generate_fake_resource( + resource_type=_role.Role, + domain_id=domain_2.id, + name=self.role.name, + ) + assignment_with_role_domain_2 = sdk_fakes.generate_fake_resource( + resource_type=_role_assignment.RoleAssignment, + role={'id': role_2.id, 'name': role_2.name}, + scope={'project': {'id': self.project.id}}, + user={'id': self.user.id}, + ) + + self.identity_sdk_client.find_domain.return_value = domain_2 + self.identity_sdk_client.find_role.return_value = role_2 + self.identity_sdk_client.role_assignments.return_value = [ + assignment_with_role_domain_2, ] arglist = [ '--role', - identity_fakes.ROLE_2['name'], + role_2.name, '--role-domain', - identity_fakes.domain_name, + domain_2.name, ] verifylist = [ ('user', None), @@ -722,8 +803,8 @@ def test_role_assignment_list_domain_role(self): ('system', None), ('domain', None), ('project', None), - ('role', identity_fakes.ROLE_2['name']), - ('effective', False), + ('role', role_2.name), + ('effective', None), ('inherited', False), ('names', False), ] @@ -734,26 +815,26 @@ def test_role_assignment_list_domain_role(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.role_assignments_mock.list.assert_called_with( - domain=None, - system=None, - user=None, - group=None, - project=None, - role=self.roles_mock.get(), - effective=False, - os_inherit_extension_inherited_to=None, - include_names=False, + self.identity_sdk_client.role_assignments.assert_called_with( + role_id=role_2.id, + user_id=None, + 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 = ( ( - identity_fakes.ROLE_2['id'], - identity_fakes.user_id, + role_2.id, + self.user.id, '', + self.project.id, '', - identity_fakes.domain_id, '', False, ), diff --git a/releasenotes/notes/migrate-role-assignment-to-sdk-e6e52bef467b4e4c.yaml b/releasenotes/notes/migrate-role-assignment-to-sdk-e6e52bef467b4e4c.yaml new file mode 100644 index 000000000..faf59e937 --- /dev/null +++ b/releasenotes/notes/migrate-role-assignment-to-sdk-e6e52bef467b4e4c.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Migrate ``role assignment`` commands from keystoneclient to SDK. From 49c42c73a29f6381e6dbf94fb1a0f96329c6a1f4 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Tue, 14 May 2024 17:39:56 +0530 Subject: [PATCH 120/403] 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 121/403] 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 122/403] 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 ca81b1acf05d87871ae2c672d7900dfb67d52ec0 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Thu, 27 Jun 2024 00:25:03 +0530 Subject: [PATCH 123/403] Add cluster to volume service list This patch adds the ``Cluster`` and ``Backend State`` columns to the ``openstack volume service list`` command. Note that you need to provide the appropriate microversion to show these columns. Cluster: openstack --os-volume-api-version 3.7 volume service list Backend State: openstack --os-volume-api-version 3.49 volume service list Change-Id: Ie7727d0001307b5d5a40d7ea0348bdb9626f9e03 --- .../tests/unit/volume/v3/test_service.py | 271 ++++++++++++++++++ openstackclient/volume/v3/service.py | 65 +++++ ...ster-to-service-list-5eab3e828de7547e.yaml | 8 + setup.cfg | 2 +- 4 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 openstackclient/tests/unit/volume/v3/test_service.py create mode 100644 openstackclient/volume/v3/service.py create mode 100644 releasenotes/notes/add-cluster-to-service-list-5eab3e828de7547e.yaml diff --git a/openstackclient/tests/unit/volume/v3/test_service.py b/openstackclient/tests/unit/volume/v3/test_service.py new file mode 100644 index 000000000..92f4239bc --- /dev/null +++ b/openstackclient/tests/unit/volume/v3/test_service.py @@ -0,0 +1,271 @@ +# +# 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 cinderclient import api_versions + +from openstackclient.tests.unit.volume.v2 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() + + 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, + ) + + 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] + + arglist = [ + '--host', + cluster_service.host, + '--service', + cluster_service.binary, + ] + verifylist = [ + ('host', cluster_service.host), + ('service', cluster_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 = [ + 'Binary', + 'Host', + 'Zone', + 'Status', + '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, + ), + ) + + # 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( + cluster_service.host, + cluster_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] + + arglist = [ + '--host', + backend_service.host, + '--service', + backend_service.binary, + ] + verifylist = [ + ('host', backend_service.host), + ('service', backend_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 = [ + 'Binary', + 'Host', + 'Zone', + 'Status', + 'State', + '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, + ), + ) + + # 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( + backend_service.host, + backend_service.binary, + ) + + # checking if prohibited columns are present in output + self.assertNotIn("Disabled Reason", columns) + self.assertNotIn(backend_service.disabled_reason, tuple(data)) diff --git a/openstackclient/volume/v3/service.py b/openstackclient/volume/v3/service.py new file mode 100644 index 000000000..900aeab2d --- /dev/null +++ b/openstackclient/volume/v3/service.py @@ -0,0 +1,65 @@ +# +# 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 cinderclient import api_versions +from osc_lib import utils + +from openstackclient.volume.v2 import service as service_v2 + + +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", + ] + + 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") + + data = service_client.services.list( + parsed_args.host, parsed_args.service + ) + return ( + columns, + ( + utils.get_item_properties( + s, + columns, + ) + for s in data + ), + ) diff --git a/releasenotes/notes/add-cluster-to-service-list-5eab3e828de7547e.yaml b/releasenotes/notes/add-cluster-to-service-list-5eab3e828de7547e.yaml new file mode 100644 index 000000000..14418107a --- /dev/null +++ b/releasenotes/notes/add-cluster-to-service-list-5eab3e828de7547e.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Added the ``Cluster`` and ``Backend State`` columns to + ``openstack volume service list`` command. Note that the + ``Cluster`` parameter is available since microversion 3.7 + and ``Backend State`` parameter is available since + microversion 3.49. diff --git a/setup.cfg b/setup.cfg index 093bfe7a1..89edcd9b2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -847,7 +847,7 @@ openstack.volume.v3 = 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_list = openstackclient.volume.v3.service:ListService volume_service_set = openstackclient.volume.v2.service:SetService volume_transfer_request_accept = openstackclient.volume.v2.volume_transfer_request:AcceptTransferRequest From f52e888dabaa75c7dde0faf7e121474ee3699f31 Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Fri, 28 Jun 2024 20:30:56 +0530 Subject: [PATCH 124/403] 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 40ce56201c08c2853bfcf8c54f32b88f12b888db Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 25 Jun 2024 18:38:19 +0100 Subject: [PATCH 125/403] compute: Remove unnecessary try-except We don't need to transform one HTTP Forbidden-related error to another: just use the original one. This also fixes an issue where we would end up with an undefined variable (server_id) if a non-HTTP 403 exception was raised, since that would be blindly ignored. Change-Id: Icdd1764b6f2df4a635e3264ed8f93a115cc52ef2 Signed-off-by: Stephen Finucane Closes-Bug: #2062010 --- openstackclient/compute/v2/server.py | 35 ++++++++++------------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 62785cd0b..a88a79fe6 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -4844,17 +4844,12 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): compute_client = self.app.client_manager.sdk_connection.compute for server in parsed_args.server: - try: - server_id = compute_client.find_server( - server, - ignore_missing=False, - details=False, - all_projects=parsed_args.all_projects, - ).id - except sdk_exceptions.HttpException as exc: - if exc.status_code == 403: - msg = _("Policy doesn't allow passing all-projects") - raise exceptions.Forbidden(msg) + server_id = compute_client.find_server( + server, + ignore_missing=False, + details=False, + all_projects=parsed_args.all_projects, + ).id compute_client.start_server(server_id) @@ -4884,18 +4879,12 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): compute_client = self.app.client_manager.sdk_connection.compute for server in parsed_args.server: - try: - server_id = compute_client.find_server( - server, - ignore_missing=False, - details=False, - all_projects=parsed_args.all_projects, - ).id - except sdk_exceptions.HttpException as exc: - if exc.status_code == 403: - msg = _("Policy doesn't allow passing all-projects") - raise exceptions.Forbidden(msg) - + server_id = compute_client.find_server( + server, + ignore_missing=False, + details=False, + all_projects=parsed_args.all_projects, + ).id compute_client.stop_server(server_id) From 21155be2e4145f36e6255e5172f8134b959aa993 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 1 Jul 2024 12:24:08 +0100 Subject: [PATCH 126/403] docs: Fix indentation Additional paragraphs in a rST list should be indented by the same number of characters as the first paragraph otherwise it renders as a block quote. Correct this for the backwards incompatible changes guide. Change-Id: I918c2a24aead4cc5a317201df9b12ce740612e5a Signed-off-by: Stephen Finucane --- doc/source/cli/backwards-incompatible.rst | 394 +++++++++++----------- 1 file changed, 202 insertions(+), 192 deletions(-) diff --git a/doc/source/cli/backwards-incompatible.rst b/doc/source/cli/backwards-incompatible.rst index 9d43754e0..3fbe65fae 100644 --- a/doc/source/cli/backwards-incompatible.rst +++ b/doc/source/cli/backwards-incompatible.rst @@ -20,119 +20,129 @@ Release 4.0 ----------- 1. Remove ``ip fixed add|remove`` commands. + Use ``server add|remove fixed ip`` commands instead. - * Removed in: 4.0 - * Commit: https://review.opendev.org/612781 + * Removed in: 4.0 + * Commit: https://review.opendev.org/612781 2. Remove ``ip floating add|remove`` commands. + Use ``server add|remove floating ip`` commands instead. - * Removed in: 4.0 - * Commit: https://review.opendev.org/612781 + * Removed in: 4.0 + * Commit: https://review.opendev.org/612781 3. Remove ``service create`` option ``--type``. Service type is a positional argument. - * Removed in: 4.0 - * Commit: https://review.opendev.org/612798 + * Removed in: 4.0 + * Commit: https://review.opendev.org/612798 4. Remove ``role list`` options ``--project`` and ``--user``. + Use ``role assignment list`` options ``--project`` and ``--user`` instead. - * Removed in: 4.0 - * Commit: https://review.opendev.org/612798 + * Removed in: 4.0 + * Commit: https://review.opendev.org/612798 5. Remove ``user role list`` command. + Use ``role assignment list`` options ``--project`` and ``--user`` instead. - * Removed in: 4.0 - * Commit: https://review.opendev.org/612798 + * Removed in: 4.0 + * Commit: https://review.opendev.org/612798 6. Remove ``image create|set`` option ``--owner``. + Use ``--project`` option instead. - * Removed in: 4.0 - * Commit: https://review.opendev.org/659431 + * Removed in: 4.0 + * Commit: https://review.opendev.org/659431 7. Remove ``port create|set`` options ``--device-id`` and ``--host-id``. + Use ``--device`` and ``--host`` instead. - * Removed in: 4.0 - * Commit: https://review.opendev.org/613644 + * Removed in: 4.0 + * Commit: https://review.opendev.org/613644 8. Remove ``router set`` option ``--clear-routes``. + Use ``no-route`` option instead. - * Removed in: 4.0 - * Commit: https://review.opendev.org/613644 + * Removed in: 4.0 + * Commit: https://review.opendev.org/613644 9. Remove ``security group rule create`` options ``--src-ip`` and ``--src-group``. + Use ``--remote-ip`` and ``--remote-group`` options instead. - * Removed in: 4.0 - * Commit: https://review.opendev.org/613644 + * Removed in: 4.0 + * Commit: https://review.opendev.org/613644 10. Remove ``backup`` commands. + Use ``volume backup`` commands instead. - * Removed in: 4.0 - * Commit: https://review.opendev.org/612751 + * Removed in: 4.0 + * Commit: https://review.opendev.org/612751 11. Remove ``snapshot`` commands. Use ``volume snapshot`` commands instead. - * Removed in: 4.0 - * Commit: https://review.opendev.org/612751 + * Removed in: 4.0 + * Commit: https://review.opendev.org/612751 12. Remove ``volume create`` options ``--project``, ``--user``, ``--multi-attach``. - * Removed in: 4.0 - * Commit: https://review.opendev.org/612751 + * Removed in: 4.0 + * Commit: https://review.opendev.org/612751 13. Change ``volume transfer request accept`` to use new option ``--auth-key`` rather than a second positional argument. - * Removed in: 4.0 - * Commit: https://review.opendev.org/612751 + * Removed in: 4.0 + * Commit: https://review.opendev.org/612751 14. Remove 'Token/Endpoint' auth plugin support (type ``token_endpoint``). + This remained as a compatibility for the ``admin_token`` auth type to support the ``--url`` global option. That option is also now removed, use ``--endpoint`` instead. - * Removed in: 4.0 - * Commit: https://review.opendev.org/ + * Removed in: 4.0 + * Commit: https://review.opendev.org/ Release 3.12 ------------ 1. Replace ``Display Name`` by ``Name`` in volume list. - Change column name ``Display Name`` to ``Name`` in ``volume list`` output. - Current ``volume list --name`` command uses ``display_name`` as search_opts - to send to cinder API, and show the result table with ``Display Name`` - as column title. Replace all ``Display Name`` by ``Name`` to be consistent - with other list commands. + Change column name ``Display Name`` to ``Name`` in ``volume list`` output. + Current ``volume list --name`` command uses ``display_name`` as search_opts + to send to cinder API, and show the result table with ``Display Name`` + as column title. Replace all ``Display Name`` by ``Name`` to be consistent + with other list commands. - Support a mapping for volume list -c ``Display Name`` (Volume v1 and v2) - and volume create/show -c ``display_name`` (Volume v1) to maintain backward - compatibility until the next major release. + Support a mapping for volume list -c ``Display Name`` (Volume v1 and v2) + and volume create/show -c ``display_name`` (Volume v1) to maintain backward + compatibility until the next major release. - * In favor of: ``openstack volume list -c Name`` - * As of: 3.12.0 - * Removed in: n/a - * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1657956 - * Commit: https://review.opendev.org/#/c/423081/ + * In favor of: ``openstack volume list -c Name`` + * As of: 3.12.0 + * Removed in: n/a + * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1657956 + * Commit: https://review.opendev.org/#/c/423081/ Release 3.10 ------------ 1. The ``network create`` command now requires the ``--subnet`` option when used - with Nova-network clouds. + with nova-network clouds. - * As of: 3.10 - * Commit: https://review.opendev.org/460679 + * As of: 3.10 + * Commit: https://review.opendev.org/460679 2. The positional argument ```` of the ``volume snapshot create`` command is no longer optional. @@ -142,23 +152,23 @@ Release 3.10 ``--volume`` option is not present now it defaults to the value of ````. - * As of: 3.10 - * Bug: 1659894 - * Commit: https://review.opendev.org/440497 + * As of: 3.10 + * Bug: 1659894 + * Commit: https://review.opendev.org/440497 Release 3.0 ----------- 1. Remove the ``osc_password`` authentication plugin. - This was the 'last-resort' plugin default that worked around an old default - Keystone configuration for the ``admin_endpoint`` and ``public_endpoint``. + This was the 'last-resort' plugin default that worked around an old default + Keystone configuration for the ``admin_endpoint`` and ``public_endpoint``. - * In favor of: ``password`` - * As of: 3.0 - * Removed in: n/a - * Bug: n/a - * Commit: https://review.opendev.org/332938 + * In favor of: ``password`` + * As of: 3.0 + * Removed in: n/a + * Bug: n/a + * Commit: https://review.opendev.org/332938 Releases Before 3.0 @@ -166,209 +176,209 @@ Releases Before 3.0 1. Rename command `openstack project usage list` - The `project` part of the command was pointless. + The `project` part of the command was pointless. - * In favor of: `openstack usage list` instead. - * As of: 1.0.2 - * Removed in: TBD - * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1406654 - * Commit: https://review.opendev.org/#/c/147379/ + * In favor of: `openstack usage list` instead. + * As of: 1.0.2 + * Removed in: TBD + * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1406654 + * Commit: https://review.opendev.org/#/c/147379/ 2. should not be optional for command `openstack service create` - Previously, the command was `openstack service create --type `, - whereas now it is: `openstack service create --name `. - This bug also affected python-keystoneclient, and keystone. + Previously, the command was `openstack service create --type `, + whereas now it is: `openstack service create --name `. + This bug also affected python-keystoneclient, and keystone. - * In favor of: making a positional argument. - * As of: 1.0.2 - * Removed in: TBD - * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1404073 - * Commit: https://review.opendev.org/#/c/143242/ + * In favor of: making a positional argument. + * As of: 1.0.2 + * Removed in: TBD + * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1404073 + * Commit: https://review.opendev.org/#/c/143242/ 3. Command `openstack security group rule delete` now requires rule id - Previously, the command was `openstack security group rule delete --proto - [--src-ip --dst-port ] `, - whereas now it is: `openstack security group rule delete `. + Previously, the command was `openstack security group rule delete --proto + [--src-ip --dst-port ] `, + whereas now it is: `openstack security group rule delete `. - * In favor of: Using `openstack security group rule delete `. - * As of: 1.2.1 - * Removed in: NA - * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1450872 - * Commit: https://review.opendev.org/#/c/179446/ + * In favor of: Using `openstack security group rule delete `. + * As of: 1.2.1 + * Removed in: NA + * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1450872 + * Commit: https://review.opendev.org/#/c/179446/ 4. Command `openstack image create` does not update already existing image - Previously, the image create command updated already existing image if it had - same name. It disabled possibility to create multiple images with same name - and lead to potentially unwanted update of existing images by image create - command. - Now, update code was moved from create action to set action. + Previously, the image create command updated already existing image if it had + same name. It disabled possibility to create multiple images with same name + and lead to potentially unwanted update of existing images by image create + command. + Now, update code was moved from create action to set action. - * In favor of: Create multiple images with same name (as glance does). - * As of: 1.5.0 - * Removed in: NA - * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1461817 - * Commit: https://review.opendev.org/#/c/194654/ + * In favor of: Create multiple images with same name (as glance does). + * As of: 1.5.0 + * Removed in: NA + * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1461817 + * Commit: https://review.opendev.org/#/c/194654/ 5. Command `openstack network list --dhcp` has been removed - The --dhcp option to network list is not a logical use case of listing - networks, it lists agents. Another command should be added in the future - to provide this functionality. It is highly unlikely anyone uses this - feature as we don't support any other agent commands. Use neutron - dhcp-agent-list-hosting-net command instead. + The --dhcp option to network list is not a logical use case of listing + networks, it lists agents. Another command should be added in the future + to provide this functionality. It is highly unlikely anyone uses this + feature as we don't support any other agent commands. Use neutron + dhcp-agent-list-hosting-net command instead. - * In favor of: Create network agent list command in the future - * As of: 1.6.0 - * Removed in: NA - * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/472613 - * Commit: https://review.opendev.org/#/c/194654/ + * In favor of: Create network agent list command in the future + * As of: 1.6.0 + * Removed in: NA + * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/472613 + * Commit: https://review.opendev.org/#/c/194654/ 6. Plugin interface change for default API versions - Previously, the default version was set in the parsed arguments, - but this makes it impossible to tell what has been passed in at the - command line, set in an environment variable or is just the default. - Now, the module should have a DEFAULT_API_VERSION that contains the - value and it will be set after command line argument, environment - and OCC file processing. + Previously, the default version was set in the parsed arguments, + but this makes it impossible to tell what has been passed in at the + command line, set in an environment variable or is just the default. + Now, the module should have a DEFAULT_API_VERSION that contains the + value and it will be set after command line argument, environment + and OCC file processing. - * In favor of: DEFAULT_API_VERSION - * As of: 1.2.1 - * Removed in: NA - * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1453229 - * Commit: https://review.opendev.org/#/c/181514/ + * In favor of: DEFAULT_API_VERSION + * As of: 1.2.1 + * Removed in: NA + * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1453229 + * Commit: https://review.opendev.org/#/c/181514/ 7. `image set` commands will no longer return the modified resource - Previously, modifying an image would result in the new image being displayed - to the user. To keep things consistent with other `set` commands, we will - no longer be showing the modified resource. + Previously, modifying an image would result in the new image being displayed + to the user. To keep things consistent with other `set` commands, we will + no longer be showing the modified resource. - * In favor of: Use `set` then `show` - * As of: NA - * Removed in: NA - * Bug: NA - * Commit: NA + * In favor of: Use `set` then `show` + * As of: NA + * Removed in: NA + * Bug: NA + * Commit: NA 8. `region` commands no longer support `url` - The Keystone team removed support for the `url` attribute from the client - and server side. Changes to the `create`, `set` and `list` commands for - regions have been affected. + The Keystone team removed support for the `url` attribute from the client + and server side. Changes to the `create`, `set` and `list` commands for + regions have been affected. - * In favor of: NA - * As of 1.9.0 - * Removed in: NA - * Bug: https://launchpad.net/bugs/1506841 - * Commit: https://review.opendev.org/#/c/236736/ + * In favor of: NA + * As of 1.9.0 + * Removed in: NA + * Bug: https://launchpad.net/bugs/1506841 + * Commit: https://review.opendev.org/#/c/236736/ 9. `flavor set/unset` commands will no longer return the modified resource - Previously, modifying a flavor would result in the new flavor being displayed - to the user. To keep things consistent with other `set/unset` commands, we - will no longer be showing the modified resource. + Previously, modifying a flavor would result in the new flavor being displayed + to the user. To keep things consistent with other `set/unset` commands, we + will no longer be showing the modified resource. - * In favor of: Use `set/unset` then `show` - * As of: NA - * Removed in: NA - * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1546065 - * Commit: https://review.opendev.org/#/c/280663/ + * In favor of: Use `set/unset` then `show` + * As of: NA + * Removed in: NA + * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1546065 + * Commit: https://review.opendev.org/#/c/280663/ 10. `security group set` commands will no longer return the modified resource - Previously, modifying a security group would result in the new security group - being displayed to the user. To keep things consistent with other `set` - commands, we will no longer be showing the modified resource. + Previously, modifying a security group would result in the new security group + being displayed to the user. To keep things consistent with other `set` + commands, we will no longer be showing the modified resource. - * In favor of: Use `set` then `show` - * As of: NA - * Removed in: NA - * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1546065 - * Commit: https://review.opendev.org/#/c/281087/ + * In favor of: Use `set` then `show` + * As of: NA + * Removed in: NA + * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1546065 + * Commit: https://review.opendev.org/#/c/281087/ 11. `compute agent set` commands will no longer return the modified resource - Previously, modifying an agent would result in the new agent being displayed - to the user. To keep things consistent with other `set` commands, we will - no longer be showing the modified resource. + Previously, modifying an agent would result in the new agent being displayed + to the user. To keep things consistent with other `set` commands, we will + no longer be showing the modified resource. - * In favor of: Use `set` then `show` - * As of: NA - * Removed in: NA - * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1546065 - * Commit: https://review.opendev.org/#/c/281088/ + * In favor of: Use `set` then `show` + * As of: NA + * Removed in: NA + * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1546065 + * Commit: https://review.opendev.org/#/c/281088/ 12. ` ` should be optional for command `openstack compute agent set` - Previously, the command was `openstack compute agent set - `, whereas now it is: `openstack compute agent set --version - --url --md5hash `. + Previously, the command was `openstack compute agent set + `, whereas now it is: `openstack compute agent set --version + --url --md5hash `. - * In favor of: making optional. - * As of: NA - * Removed in: NA - * Bug: NA - * Commit: https://review.opendev.org/#/c/328819/ + * In favor of: making optional. + * As of: NA + * Removed in: NA + * Bug: NA + * Commit: https://review.opendev.org/#/c/328819/ 13. `aggregate set` commands will no longer return the modified resource - Previously, modifying an aggregate would result in the new aggregate being - displayed to the user. To keep things consistent with other `set` commands, - we will no longer be showing the modified resource. + Previously, modifying an aggregate would result in the new aggregate being + displayed to the user. To keep things consistent with other `set` commands, + we will no longer be showing the modified resource. - * In favor of: Use `set` then `show` - * As of: NA - * Removed in: NA - * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1546065 - * Commit: https://review.opendev.org/#/c/281089/ + * In favor of: Use `set` then `show` + * As of: NA + * Removed in: NA + * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1546065 + * Commit: https://review.opendev.org/#/c/281089/ 14. Output of `ip floating list` command has changed. - When using Compute v2, the original output is: + When using Compute v2, the original output is: - .. code-block:: bash + .. code-block:: bash - # ip floating list + # ip floating list - +----+--------+------------+----------+-------------+ - | ID | Pool | IP | Fixed IP | Instance ID | - +----+--------+-----------------------+-------------+ - | 1 | public | 172.24.4.1 | None | None | - +----+--------+------------+----------+-------------+ + +----+--------+------------+----------+-------------+ + | ID | Pool | IP | Fixed IP | Instance ID | + +----+--------+-----------------------+-------------+ + | 1 | public | 172.24.4.1 | None | None | + +----+--------+------------+----------+-------------+ - Now it changes to: + Now it changes to: - .. code-block:: bash + .. code-block:: bash - # ip floating list + # ip floating list - +----+---------------------+------------------+-----------+--------+ - | ID | Floating IP Address | Fixed IP Address | Server ID | Pool | - +----+---------------------+------------------+-----------+--------+ - | 1 | 172.24.4.1 | None | None | public | - +----+---------------------+------------------+-----------+--------+ + +----+---------------------+------------------+-----------+--------+ + | ID | Floating IP Address | Fixed IP Address | Server ID | Pool | + +----+---------------------+------------------+-----------+--------+ + | 1 | 172.24.4.1 | None | None | public | + +----+---------------------+------------------+-----------+--------+ - When using Network v2, which is different from Compute v2. The output is: + When using Network v2, which is different from Compute v2. The output is: - .. code-block:: bash + .. code-block:: bash - # ip floating list + # ip floating list - +--------------------------------------+---------------------+------------------+------+ - | ID | Floating IP Address | Fixed IP Address | Port | - +--------------------------------------+---------------------+------------------+------+ - | 1976df86-e66a-4f96-81bd-c6ffee6407f1 | 172.24.4.3 | None | None | - +--------------------------------------+---------------------+------------------+------+ + +--------------------------------------+---------------------+------------------+------+ + | ID | Floating IP Address | Fixed IP Address | Port | + +--------------------------------------+---------------------+------------------+------+ + | 1976df86-e66a-4f96-81bd-c6ffee6407f1 | 172.24.4.3 | None | None | + +--------------------------------------+---------------------+------------------+------+ - * In favor of: Use `ip floating list` command - * As of: NA - * Removed in: NA - * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1519502 - * Commit: https://review.opendev.org/#/c/277720/ + * In favor of: Use `ip floating list` command + * As of: NA + * Removed in: NA + * Bug: https://bugs.launchpad.net/python-openstackclient/+bug/1519502 + * Commit: https://review.opendev.org/#/c/277720/ For Developers ============== From 205bac3caf5ff983c60da0d171e51c1cc286024a Mon Sep 17 00:00:00 2001 From: Rajat Dhasmana Date: Mon, 1 Jul 2024 21:04:45 +0530 Subject: [PATCH 127/403] Fix: incremental volume backup The incremental volume backup stopped working after we moved from cinderclient to SDK[1]. This happened because SDK accepts the ``is_incremental`` parameter[2] rather than the ``incremental`` parameter which is actually passed in the API request and was previously a valid parameter for cinderclient. This patch fixes the issue by passing the ``is_incremental`` field instead of ``incremental`` from the OSC side which adds the ``incremental`` parameter in the API request. Request body after the fix: '{"backup": {"name": null, "description": null, "volume_id": "", "force": false, "container": null, "incremental": true}}' [1] https://review.opendev.org/c/openstack/python-openstackclient/+/889748 [2] https://opendev.org/openstack/openstacksdk/src/commit/10e5e20fc0eb5264080f0cfdc0523d65883dc693/openstack/block_storage/v2/backup.py#L126-L134 Closes-Bug: #2070080 Change-Id: I89bd3d2751267ec39f4dbd664b7873ab87a9ac6c --- .../tests/unit/volume/v2/test_volume_backup.py | 8 ++++---- openstackclient/volume/v2/volume_backup.py | 2 +- .../notes/fix-backup-incremental-d1c1e6886cf32256.yaml | 7 +++++++ 3 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/fix-backup-incremental-d1c1e6886cf32256.yaml diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index aa0c050cd..e16bdbb81 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -120,7 +120,7 @@ def test_backup_create(self): name=self.new_backup.name, description=self.new_backup.description, force=True, - incremental=True, + is_incremental=True, snapshot_id=self.new_backup.snapshot_id, ) self.assertEqual(self.columns, columns) @@ -150,7 +150,7 @@ def test_backup_create_with_properties(self): name=None, description=None, force=False, - incremental=False, + is_incremental=False, metadata={"foo": "bar", "wow": "much-cool"}, ) self.assertEqual(self.columns, columns) @@ -199,7 +199,7 @@ def test_backup_create_with_availability_zone(self): name=None, description=None, force=False, - incremental=False, + is_incremental=False, availability_zone="my-az", ) self.assertEqual(self.columns, columns) @@ -247,7 +247,7 @@ def test_backup_create_without_name(self): name=None, description=self.new_backup.description, force=False, - incremental=False, + is_incremental=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 7eed05a1d..a4e6ac85a 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -173,7 +173,7 @@ def take_action(self, parsed_args): name=parsed_args.name, description=parsed_args.description, force=parsed_args.force, - incremental=parsed_args.incremental, + is_incremental=parsed_args.incremental, **kwargs, ) data = utils.get_dict_properties(backup, columns) diff --git a/releasenotes/notes/fix-backup-incremental-d1c1e6886cf32256.yaml b/releasenotes/notes/fix-backup-incremental-d1c1e6886cf32256.yaml new file mode 100644 index 000000000..7942a6f29 --- /dev/null +++ b/releasenotes/notes/fix-backup-incremental-d1c1e6886cf32256.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fixed issue with creating incremental volume backup. + Previously, ``incremental`` value was not passed in the + API request which is now included in the backup + create request. From aa5eb881e59de09af262d7d29920c84c0ccf3a7a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 30 Mar 2023 18:58:33 +0100 Subject: [PATCH 128/403] compute: Migrate tests for ShowServer to SDK objects This is a little more realistic. We fix a minor bug along the way and start ignoring some newly added create-only fields. Change-Id: I93eae610e16e2a3a859f684b889546ace3afa683 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 6 +- .../tests/unit/compute/v2/test_server.py | 107 +++++++++++++++--- openstackclient/tests/unit/utils.py | 3 + .../unit/volume/v3/test_volume_group_type.py | 2 - 4 files changed, 97 insertions(+), 21 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index a88a79fe6..e7a135e06 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -193,9 +193,11 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): 'public_v6', # create-only columns 'block_device_mapping', + 'host', 'image_id', 'max_count', 'min_count', + 'personality', 'scheduler_hints', # aliases 'volumes', @@ -283,7 +285,9 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): ) if 'tags' in info: - info.update({'tags': format_columns.ListColumn(info.pop('tags'))}) + info.update( + {'tags': format_columns.ListColumn(info.pop('tags') or [])} + ) # Map 'networks' to 'addresses', if present. Note that the 'networks' key # is used for create responses, otherwise it's 'addresses'. We know it'll diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 89b0bddea..eac3b7ece 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -8222,12 +8222,10 @@ def setUp(self): self.compute_sdk_client.get_server_diagnostics.return_value = { 'test': 'test' } - server_method = { - 'fetch_topology': self.topology, - } - self.server = compute_fakes.create_one_server( - attrs=server_info, methods=server_method + self.server = compute_fakes.create_one_sdk_server( + attrs=server_info, ) + self.server.fetch_topology = mock.MagicMock(return_value=self.topology) # This is the return value for utils.find_resource() self.compute_sdk_client.get_server.return_value = self.server @@ -8238,28 +8236,101 @@ def setUp(self): self.cmd = server.ShowServer(self.app, None) self.columns = ( + 'OS-DCF:diskConfig', + 'OS-EXT-AZ:availability_zone', + 'OS-EXT-SRV-ATTR:host', + 'OS-EXT-SRV-ATTR:hostname', + 'OS-EXT-SRV-ATTR:hypervisor_hostname', + 'OS-EXT-SRV-ATTR:instance_name', + 'OS-EXT-SRV-ATTR:kernel_id', + 'OS-EXT-SRV-ATTR:launch_index', + 'OS-EXT-SRV-ATTR:ramdisk_id', + 'OS-EXT-SRV-ATTR:reservation_id', + 'OS-EXT-SRV-ATTR:root_device_name', + 'OS-EXT-SRV-ATTR:user_data', 'OS-EXT-STS:power_state', + 'OS-EXT-STS:task_state', + 'OS-EXT-STS:vm_state', + 'OS-SRV-USG:launched_at', + 'OS-SRV-USG:terminated_at', + 'accessIPv4', + 'accessIPv6', 'addresses', + 'config_drive', + 'created', + 'description', 'flavor', + 'hostId', + 'host_status', 'id', 'image', + 'key_name', + 'locked', + 'locked_reason', 'name', + 'pinned_availability_zone', + 'progress', 'project_id', 'properties', + 'server_groups', + 'status', + 'tags', + 'trusted_image_certificates', + 'updated', + 'user_id', + 'volumes_attached', ) self.data = ( + None, # OS-DCF:diskConfig + None, # OS-EXT-AZ:availability_zone + None, # OS-EXT-SRV-ATTR:host + None, # OS-EXT-SRV-ATTR:hostname + None, # OS-EXT-SRV-ATTR:hypervisor_hostname + None, # OS-EXT-SRV-ATTR:instance_name + None, # OS-EXT-SRV-ATTR:kernel_id + None, # OS-EXT-SRV-ATTR:launch_index + None, # OS-EXT-SRV-ATTR:ramdisk_id + None, # OS-EXT-SRV-ATTR:reservation_id + None, # OS-EXT-SRV-ATTR:root_device_name + None, # OS-EXT-SRV-ATTR:user_data server.PowerStateColumn( - getattr(self.server, 'OS-EXT-STS:power_state') - ), - self.flavor.name + " (" + self.flavor.id + ")", - self.server.id, - self.image.name + " (" + self.image.id + ")", + self.server.power_state + ), # OS-EXT-STS:power_state # noqa: E501 + None, # OS-EXT-STS:task_state + None, # OS-EXT-STS:vm_state + None, # OS-SRV-USG:launched_at + None, # OS-SRV-USG:terminated_at + None, # accessIPv4 + None, # accessIPv6 + server.AddressesColumn( + {'public': ['10.20.30.40', '2001:db8::f']} + ), # addresses + None, # config_drive + None, # created + None, # description + self.flavor.name + " (" + self.flavor.id + ")", # flavor + None, # hostId + None, # host_status + self.server.id, # id + self.image.name + " (" + self.image.id + ")", # image + None, # key_name + None, # locked + None, # locked_reason self.server.name, - server.AddressesColumn({'public': ['10.20.30.40', '2001:db8::f']}), - 'tenant-id-xxx', - format_columns.DictColumn({}), - ) + None, # pinned_availability_zone + None, # progress + 'tenant-id-xxx', # project_id + format_columns.DictColumn({}), # properties + None, # server_groups + None, # status + format_columns.ListColumn([]), # tags + None, # trusted_image_certificates + None, # updated + None, # user_id + format_columns.ListDictColumn([]), # volumes_attached + ) + self.assertEqual(len(self.columns), len(self.data)) def test_show_no_options(self): arglist = [] @@ -8286,8 +8357,8 @@ def test_show(self): columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, data) + self.assertTupleEqual(self.columns, columns) + self.assertTupleEqual(self.data, data) def test_show_embedded_flavor(self): # Tests using --os-compute-api-version >= 2.47 where the flavor @@ -8301,7 +8372,7 @@ def test_show_embedded_flavor(self): ('server', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.server.info['flavor'] = { + self.server.flavor = { 'ephemeral': 0, 'ram': 512, 'original_name': 'm1.tiny', @@ -8315,7 +8386,7 @@ def test_show_embedded_flavor(self): self.assertEqual(self.columns, columns) # 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[2]._value) + self.assertIn('original_name', data[columns.index('flavor')]._value) def test_show_diagnostics(self): arglist = [ diff --git a/openstackclient/tests/unit/utils.py b/openstackclient/tests/unit/utils.py index df7341f0f..b9f38f950 100644 --- a/openstackclient/tests/unit/utils.py +++ b/openstackclient/tests/unit/utils.py @@ -35,6 +35,9 @@ def __eq__(self, other): class TestCase(testtools.TestCase): + # provide additional context for failures + maxDiff = None + def setUp(self): testtools.TestCase.setUp(self) diff --git a/openstackclient/tests/unit/volume/v3/test_volume_group_type.py b/openstackclient/tests/unit/volume/v3/test_volume_group_type.py index e44b057d1..62c41962d 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_group_type.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_group_type.py @@ -29,8 +29,6 @@ def setUp(self): class TestVolumeGroupTypeCreate(TestVolumeGroupType): - maxDiff = 2000 - fake_volume_group_type = volume_fakes.create_one_volume_group_type() columns = ( From e0f7306011af36e7306242b242b1119836c5d621 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 7 May 2024 11:44:12 +0100 Subject: [PATCH 129/403] network: Migrate 'port list' to compute SDK We use the compute client here to look up server IDs when filtering by 'device_id'. Change-Id: I76515eaa4ce4e7c7d0173d2e0a91d7564ba7041a Signed-off-by: Stephen Finucane --- openstackclient/network/v2/port.py | 7 ++++--- .../tests/unit/network/v2/test_port.py | 17 +++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index fb0ecaff2..b1e60df4e 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -844,9 +844,10 @@ def take_action(self, parsed_args): ) filters['device_id'] = _router.id if parsed_args.server: - compute_client = self.app.client_manager.compute - server = utils.find_resource( - compute_client.servers, parsed_args.server + compute_client = self.app.client_manager.sdk_connection.compute + server = compute_client.find_server( + parsed_args.server, + ignore_missing=False, ) filters['device_id'] = server.id if parsed_args.host: diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index d47f86615..7d1963651 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -15,7 +15,6 @@ from osc_lib.cli import format_columns from osc_lib import exceptions -from osc_lib import utils from openstackclient.network.v2 import port from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes @@ -1190,7 +1189,7 @@ def test_multi_ports_delete_with_exception(self): self.network_client.delete_port.assert_called_once_with(self._ports[0]) -class TestListPort(TestPort): +class TestListPort(compute_fakes.FakeClientMixin, TestPort): _ports = network_fakes.create_ports(count=3) columns = ( @@ -1256,9 +1255,6 @@ def setUp(self): self.network_client.find_router = mock.Mock(return_value=fake_router) self.network_client.find_network = mock.Mock(return_value=fake_network) - self.app.client_manager.compute = mock.Mock() - self.compute_client = self.app.client_manager.compute - # Get the command object to test self.cmd = port.ListPort(self.app, None) @@ -1297,10 +1293,9 @@ def test_port_list_router_opt(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) - @mock.patch.object(utils, 'find_resource') - def test_port_list_with_server_option(self, mock_find): - fake_server = compute_fakes.create_one_server() - mock_find.return_value = fake_server + 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 arglist = [ '--server', @@ -1315,7 +1310,9 @@ def test_port_list_with_server_option(self, mock_find): self.network_client.ports.assert_called_once_with( device_id=fake_server.id, fields=LIST_FIELDS_TO_RETRIEVE ) - mock_find.assert_called_once_with(mock.ANY, 'fake-server-name') + self.compute_sdk_client.find_server.aassert_called_once_with( + mock.ANY, 'fake-server-name' + ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) From 0f07c97e847b8da75208bf7eedf6617d22ed65a5 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 7 May 2024 12:57:55 +0100 Subject: [PATCH 130/403] compute: Migrate 'agent *' to SDK These are not supported by SDK natively (intentionally so) so we use raw HTTP requests to manage this migration. Change-Id: I72fa0d6f87899537a24090995b1ba884bc5f9d4d Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/agent.py | 116 +++--- .../tests/unit/compute/v2/test_agent.py | 330 ++++++++++-------- ...grate-agent-commands-1c50ffcb75f91418.yaml | 4 + 3 files changed, 261 insertions(+), 189 deletions(-) create mode 100644 releasenotes/notes/migrate-agent-commands-1c50ffcb75f91418.yaml diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py index 809c20f98..8f897ca8a 100644 --- a/openstackclient/compute/v2/agent.py +++ b/openstackclient/compute/v2/agent.py @@ -17,6 +17,7 @@ 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 @@ -55,16 +56,26 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute - args = ( - parsed_args.os, - parsed_args.architecture, - parsed_args.version, - parsed_args.url, - parsed_args.md5hash, - parsed_args.hypervisor, + compute_client = self.app.client_manager.sdk_connection.compute + + # doing this since openstacksdk has decided not to support this + # deprecated command + data = { + 'agent': { + 'hypervisor': parsed_args.hypervisor, + 'os': parsed_args.os, + 'architecture': parsed_args.architecture, + 'version': parsed_args.version, + 'url': parsed_args.url, + 'md5hash': parsed_args.md5hash, + }, + } + response = compute_client.post( + '/os-agents', json=data, microversion='2.1' ) - agent = compute_client.agents.create(*args)._info.copy() + sdk_exceptions.raise_from_response(response) + agent = response.json().get('agent') + return zip(*sorted(agent.items())) @@ -84,11 +95,16 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute result = 0 for id in parsed_args.id: try: - compute_client.agents.delete(id) + # doing this since openstacksdk has decided not to support this + # deprecated command + response = compute_client.delete( + f'/os-agents/{id}', microversion='2.1' + ) + sdk_exceptions.raise_from_response(response) except Exception as e: result += 1 LOG.error( @@ -123,7 +139,7 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute columns = ( "Agent ID", "Hypervisor", @@ -133,30 +149,36 @@ def take_action(self, parsed_args): "Md5Hash", "URL", ) - data = compute_client.agents.list(parsed_args.hypervisor) - return ( - columns, - ( - utils.get_item_properties( - s, - columns, - ) - for s in data - ), - ) + + # doing this since openstacksdk has decided not to support this + # deprecated command + path = '/os-agents' + if parsed_args.hypervisor: + path += f'?hypervisor={parsed_args.hypervisor}' + + response = compute_client.get(path, microversion='2.1') + sdk_exceptions.raise_from_response(response) + agents = response.json().get('agents') + + return columns, (utils.get_dict_properties(s, columns) for s in agents) class SetAgent(command.Command): """Set compute agent properties. - The compute agent functionality is hypervisor specific and is only + The compute agent functionality is hypervisor-specific and is only supported by the XenAPI hypervisor driver. It was removed from nova in the 23.0.0 (Wallaby) release. """ def get_parser(self, prog_name): parser = super().get_parser(prog_name) - parser.add_argument("id", metavar="", help=_("ID of the agent")) + parser.add_argument( + "id", + metavar="", + type=int, + help=_("ID of the agent"), + ) parser.add_argument( "--agent-version", dest="version", @@ -172,30 +194,34 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute - data = compute_client.agents.list(hypervisor=None) - agent = {} - - for s in data: - if s.agent_id == int(parsed_args.id): - agent['version'] = s.version - agent['url'] = s.url - agent['md5hash'] = s.md5hash - if agent == {}: + compute_client = self.app.client_manager.sdk_connection.compute + + response = compute_client.get('/os-agents', microversion='2.1') + sdk_exceptions.raise_from_response(response) + agents = response.json().get('agents') + data = {} + for agent in agents: + if agent['agent_id'] == parsed_args.id: + data['version'] = agent['version'] + data['url'] = agent['url'] + data['md5hash'] = agent['md5hash'] + break + else: msg = _("No agent with a ID of '%(id)s' exists.") - raise exceptions.CommandError(msg % parsed_args.id) + raise exceptions.CommandError(msg % {'id': parsed_args.id}) if parsed_args.version: - agent['version'] = parsed_args.version + data['version'] = parsed_args.version if parsed_args.url: - agent['url'] = parsed_args.url + data['url'] = parsed_args.url if parsed_args.md5hash: - agent['md5hash'] = parsed_args.md5hash + data['md5hash'] = parsed_args.md5hash + + data = {'para': data} - args = ( - parsed_args.id, - agent['version'], - agent['url'], - agent['md5hash'], + # doing this since openstacksdk has decided not to support this + # deprecated command + response = compute_client.put( + f'/os-agents/{parsed_args.id}', json=data, microversion='2.1' ) - compute_client.agents.update(*args) + sdk_exceptions.raise_from_response(response) diff --git a/openstackclient/tests/unit/compute/v2/test_agent.py b/openstackclient/tests/unit/compute/v2/test_agent.py index 3969e9d8f..eb8c0774a 100644 --- a/openstackclient/tests/unit/compute/v2/test_agent.py +++ b/openstackclient/tests/unit/compute/v2/test_agent.py @@ -11,153 +11,162 @@ # 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 http +import random from unittest import mock -from unittest.mock import call +import uuid from osc_lib import exceptions from openstackclient.compute.v2 import agent from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit import fakes from openstackclient.tests.unit import utils as tests_utils -class TestAgent(compute_fakes.TestComputev2): - attr = {} - attr['agent_id'] = 1 - fake_agent = compute_fakes.create_one_agent(attr) - - columns = ( - 'agent_id', - 'architecture', - 'hypervisor', - 'md5hash', - 'os', - 'url', - 'version', - ) - - data = ( - fake_agent.agent_id, - fake_agent.architecture, - fake_agent.hypervisor, - fake_agent.md5hash, - fake_agent.os, - fake_agent.url, - fake_agent.version, - ) - - def setUp(self): - super().setUp() - - self.agents_mock = self.compute_client.agents - self.agents_mock.reset_mock() +def _generate_fake_agent(): + return { + 'agent_id': random.randint(1, 1000), + 'os': 'agent-os-' + uuid.uuid4().hex, + 'architecture': 'agent-architecture', + 'version': '8.0', + 'url': 'http://127.0.0.1', + 'md5hash': 'agent-md5hash', + 'hypervisor': 'hypervisor', + } -class TestAgentCreate(TestAgent): +class TestAgentCreate(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.agents_mock.create.return_value = self.fake_agent + self._agent = _generate_fake_agent() + self.columns = ( + 'agent_id', + 'architecture', + 'hypervisor', + 'md5hash', + 'os', + 'url', + 'version', + ) + self.data = ( + self._agent['agent_id'], + self._agent['architecture'], + self._agent['hypervisor'], + self._agent['md5hash'], + self._agent['os'], + self._agent['url'], + self._agent['version'], + ) + + self.compute_sdk_client.post.return_value = fakes.FakeResponse( + data={'agent': self._agent} + ) self.cmd = agent.CreateAgent(self.app, None) def test_agent_create(self): arglist = [ - self.fake_agent.os, - self.fake_agent.architecture, - self.fake_agent.version, - self.fake_agent.url, - self.fake_agent.md5hash, - self.fake_agent.hypervisor, + self._agent['os'], + self._agent['architecture'], + self._agent['version'], + self._agent['url'], + self._agent['md5hash'], + self._agent['hypervisor'], ] - verifylist = [ - ('os', self.fake_agent.os), - ('architecture', self.fake_agent.architecture), - ('version', self.fake_agent.version), - ('url', self.fake_agent.url), - ('md5hash', self.fake_agent.md5hash), - ('hypervisor', self.fake_agent.hypervisor), + ('os', self._agent['os']), + ('architecture', self._agent['architecture']), + ('version', self._agent['version']), + ('url', self._agent['url']), + ('md5hash', self._agent['md5hash']), + ('hypervisor', self._agent['hypervisor']), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.agents_mock.create.assert_called_with( - parsed_args.os, - parsed_args.architecture, - parsed_args.version, - parsed_args.url, - parsed_args.md5hash, - parsed_args.hypervisor, + + self.compute_sdk_client.post.assert_called_with( + '/os-agents', + json={ + 'agent': { + 'hypervisor': parsed_args.hypervisor, + 'os': parsed_args.os, + 'architecture': parsed_args.architecture, + 'version': parsed_args.version, + 'url': parsed_args.url, + 'md5hash': parsed_args.md5hash, + }, + }, + microversion='2.1', ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) -class TestAgentDelete(TestAgent): - fake_agents = compute_fakes.create_agents(count=2) - +class TestAgentDelete(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.agents_mock.get.return_value = self.fake_agents + self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + status_code=http.HTTPStatus.NO_CONTENT + ) + self.cmd = agent.DeleteAgent(self.app, None) def test_delete_one_agent(self): - arglist = [self.fake_agents[0].agent_id] - + arglist = ['123'] verifylist = [ - ('id', [self.fake_agents[0].agent_id]), + ('id', ['123']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.agents_mock.delete.assert_called_with( - self.fake_agents[0].agent_id + + self.compute_sdk_client.delete.assert_called_once_with( + '/os-agents/123', + microversion='2.1', ) self.assertIsNone(result) def test_delete_multiple_agents(self): - arglist = [] - for n in self.fake_agents: - arglist.append(n.agent_id) + arglist = ['1', '2', '3'] verifylist = [ - ('id', arglist), + ('id', ['1', '2', '3']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - calls = [] - for n in self.fake_agents: - calls.append(call(n.agent_id)) - self.agents_mock.delete.assert_has_calls(calls) + calls = [ + mock.call(f'/os-agents/{x}', microversion='2.1') for x in arglist + ] + self.compute_sdk_client.delete.assert_has_calls(calls) self.assertIsNone(result) def test_delete_multiple_agents_exception(self): - arglist = [ - self.fake_agents[0].agent_id, - self.fake_agents[1].agent_id, - 'x-y-z', - ] + arglist = ['1', '2', '999'] verifylist = [ - ('id', arglist), + ('id', ['1', '2', '999']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - ret_delete = [None, None, exceptions.NotFound('404')] - self.agents_mock.delete = mock.Mock(side_effect=ret_delete) + self.compute_sdk_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), + ] self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) calls = [ - call(self.fake_agents[0].agent_id), - call(self.fake_agents[1].agent_id), + mock.call(f'/os-agents/{x}', microversion='2.1') for x in arglist ] - self.agents_mock.delete.assert_has_calls(calls) + self.compute_sdk_client.delete.assert_has_calls(calls) def test_agent_delete_no_input(self): arglist = [] @@ -171,36 +180,37 @@ def test_agent_delete_no_input(self): ) -class TestAgentList(TestAgent): - agents = compute_fakes.create_agents(count=3) - list_columns = ( - "Agent ID", - "Hypervisor", - "OS", - "Architecture", - "Version", - "Md5Hash", - "URL", - ) - - list_data = [] - for _agent in agents: - list_data.append( - ( - _agent.agent_id, - _agent.hypervisor, - _agent.os, - _agent.architecture, - _agent.version, - _agent.md5hash, - _agent.url, - ) - ) - +class TestAgentList(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.agents_mock.list.return_value = self.agents + _agents = [_generate_fake_agent() for _ in range(3)] + + self.columns = ( + "Agent ID", + "Hypervisor", + "OS", + "Architecture", + "Version", + "Md5Hash", + "URL", + ) + self.data = [ + ( + _agent['agent_id'], + _agent['hypervisor'], + _agent['os'], + _agent['architecture'], + _agent['version'], + _agent['md5hash'], + _agent['url'], + ) + for _agent in _agents + ] + + self.compute_sdk_client.get.return_value = fakes.FakeResponse( + data={'agents': _agents}, + ) self.cmd = agent.ListAgent(self.app, None) def test_agent_list(self): @@ -210,8 +220,12 @@ def test_agent_list(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.list_columns, columns) - self.assertEqual(self.list_data, list(data)) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + self.compute_sdk_client.get.assert_called_once_with( + '/os-agents', + microversion='2.1', + ) def test_agent_list_with_hypervisor(self): arglist = [ @@ -225,101 +239,129 @@ def test_agent_list_with_hypervisor(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.list_columns, columns) - self.assertEqual(self.list_data, list(data)) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + self.compute_sdk_client.get.assert_called_once_with( + '/os-agents?hypervisor=hypervisor', + microversion='2.1', + ) -class TestAgentSet(TestAgent): +class TestAgentSet(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.agents_mock.update.return_value = self.fake_agent - self.agents_mock.list.return_value = [self.fake_agent] + self.agent = _generate_fake_agent() + self.compute_sdk_client.get.return_value = fakes.FakeResponse( + data={'agents': [self.agent]}, + ) + self.compute_sdk_client.put.return_value = fakes.FakeResponse() + self.cmd = agent.SetAgent(self.app, None) def test_agent_set_nothing(self): arglist = [ - '1', + str(self.agent['agent_id']), ] verifylist = [ - ('id', '1'), + ('id', self.agent['agent_id']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.agents_mock.update.assert_called_with( - parsed_args.id, - self.fake_agent.version, - self.fake_agent.url, - self.fake_agent.md5hash, + self.compute_sdk_client.put.assert_called_once_with( + f'/os-agents/{self.agent["agent_id"]}', + json={ + 'para': { + 'version': self.agent['version'], + 'url': self.agent['url'], + 'md5hash': self.agent['md5hash'], + }, + }, + microversion='2.1', ) self.assertIsNone(result) def test_agent_set_version(self): arglist = [ - '1', + str(self.agent['agent_id']), '--agent-version', 'new-version', ] verifylist = [ - ('id', '1'), + ('id', self.agent['agent_id']), ('version', 'new-version'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.agents_mock.update.assert_called_with( - parsed_args.id, - parsed_args.version, - self.fake_agent.url, - self.fake_agent.md5hash, + self.compute_sdk_client.put.assert_called_once_with( + f'/os-agents/{self.agent["agent_id"]}', + json={ + 'para': { + 'version': parsed_args.version, + 'url': self.agent['url'], + 'md5hash': self.agent['md5hash'], + }, + }, + microversion='2.1', ) self.assertIsNone(result) def test_agent_set_url(self): arglist = [ - '1', + str(self.agent['agent_id']), '--url', 'new-url', ] verifylist = [ - ('id', '1'), + ('id', self.agent['agent_id']), ('url', 'new-url'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.agents_mock.update.assert_called_with( - parsed_args.id, - self.fake_agent.version, - parsed_args.url, - self.fake_agent.md5hash, + self.compute_sdk_client.put.assert_called_once_with( + f'/os-agents/{self.agent["agent_id"]}', + json={ + 'para': { + 'version': self.agent['version'], + 'url': parsed_args.url, + 'md5hash': self.agent['md5hash'], + }, + }, + microversion='2.1', ) self.assertIsNone(result) def test_agent_set_md5hash(self): arglist = [ - '1', + str(self.agent['agent_id']), '--md5hash', 'new-md5hash', ] verifylist = [ - ('id', '1'), + ('id', self.agent['agent_id']), ('md5hash', 'new-md5hash'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.agents_mock.update.assert_called_with( - parsed_args.id, - self.fake_agent.version, - self.fake_agent.url, - parsed_args.md5hash, + self.compute_sdk_client.put.assert_called_once_with( + f'/os-agents/{self.agent["agent_id"]}', + json={ + 'para': { + 'version': self.agent['version'], + 'url': self.agent['url'], + 'md5hash': parsed_args.md5hash, + }, + }, + microversion='2.1', ) self.assertIsNone(result) diff --git a/releasenotes/notes/migrate-agent-commands-1c50ffcb75f91418.yaml b/releasenotes/notes/migrate-agent-commands-1c50ffcb75f91418.yaml new file mode 100644 index 000000000..7f55cfc30 --- /dev/null +++ b/releasenotes/notes/migrate-agent-commands-1c50ffcb75f91418.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + The ``compute agent *`` commands have been migrated to SDK. From 7252a7a7817f57c2f38d0f5bf10739ff1fa58320 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 7 May 2024 12:14:22 +0100 Subject: [PATCH 131/403] compute: Migrate 'host set' to SDK This was the sole outstanding command to be migrated to SDK. We also clean up the old in-tree wrappers we have in the process and add missing error checks for the 'host list' and 'host show' commands. Change-Id: I5635469b63ab3370fb5118e4f8a1758002381aa5 Signed-off-by: Stephen Finucane --- openstackclient/api/compute_v2.py | 76 +--------- openstackclient/compute/v2/host.py | 69 ++++----- .../tests/unit/api/test_compute_v2.py | 108 --------------- .../tests/unit/compute/v2/fakes.py | 53 ------- .../tests/unit/compute/v2/test_host.py | 131 +++++++++++------- .../migrate-host-set-438997eb6f81f2b1.yaml | 4 + 6 files changed, 122 insertions(+), 319 deletions(-) create mode 100644 releasenotes/notes/migrate-host-set-438997eb6f81f2b1.yaml diff --git a/openstackclient/api/compute_v2.py b/openstackclient/api/compute_v2.py index 1ad572b13..7429a859d 100644 --- a/openstackclient/api/compute_v2.py +++ b/openstackclient/api/compute_v2.py @@ -19,7 +19,7 @@ from osc_lib.i18n import _ -# TODO(dtroyer): Mingrate this to osc-lib +# TODO(dtroyer): Migrate this to osc-lib class InvalidValue(Exception): """An argument value is not valid: wrong type, out of range, etc""" @@ -269,80 +269,6 @@ def floating_ip_pool_list( return self.list(url)["floating_ip_pools"] - # Hosts - - def host_list( - self, - zone=None, - ): - """Lists hypervisor Hosts - - https://docs.openstack.org/api-ref/compute/#list-hosts - Valid for Compute 2.0 - 2.42 - - :param string zone: - Availability zone - :returns: A dict of the floating IP attributes - """ - - url = "/os-hosts" - if zone: - url = '/os-hosts?zone=%s' % zone - - return self.list(url)["hosts"] - - def host_set( - self, host=None, status=None, maintenance_mode=None, **params - ): - """Modify host properties - - https://docs.openstack.org/api-ref/compute/#update-host-status - Valid for Compute 2.0 - 2.42 - - status - maintenance_mode - """ - - url = "/os-hosts" - - params = {} - if status: - params['status'] = status - if maintenance_mode: - params['maintenance_mode'] = maintenance_mode - if params == {}: - # Don't bother calling if nothing given - return None - else: - return self._request( - "PUT", - f"/{url}/{host}", - json=params, - ).json() - - def host_show( - self, - host=None, - ): - """Show host - - https://docs.openstack.org/api-ref/compute/#show-host-details - Valid for Compute 2.0 - 2.42 - """ - - url = "/os-hosts" - - r_host = self.find( - url, - attr='host_name', - value=host, - ) - - data = [] - for h in r_host: - data.append(h['resource']) - return data - # Networks def network_create( diff --git a/openstackclient/compute/v2/host.py b/openstackclient/compute/v2/host.py index 20925682d..27e8e2705 100644 --- a/openstackclient/compute/v2/host.py +++ b/openstackclient/compute/v2/host.py @@ -15,6 +15,7 @@ """Host action implementations""" +from openstack import exceptions as sdk_exceptions from osc_lib.command import command from osc_lib import utils @@ -35,34 +36,26 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): compute_client = self.app.client_manager.sdk_connection.compute - columns = ("Host Name", "Service", "Zone") self.log.warning( - "API has been deprecated. " - "Please consider using 'hypervisor list' instead." + "API has been deprecated; " + "consider using 'hypervisor list' instead." ) # doing this since openstacksdk has decided not to support this # deprecated command - hosts = ( - compute_client.get('/os-hosts', microversion='2.1') - .json() - .get('hosts') - ) - + response = compute_client.get('/os-hosts', microversion='2.1') + sdk_exceptions.raise_from_response(response) + hosts = response.json().get('hosts') if parsed_args.zone is not None: - filtered_hosts = [] - for host in hosts: - if host['zone'] == parsed_args.zone: - filtered_hosts.append(host) - - hosts = filtered_hosts + hosts = [h for h in hosts if h['zone'] == parsed_args.zone] + columns = ("Host Name", "Service", "Zone") return columns, (utils.get_dict_properties(s, columns) for s in hosts) class SetHost(command.Command): - _description = _("Set host properties") + _description = _("DEPRECATED: Set host properties") def get_parser(self, prog_name): parser = super().get_parser(prog_name) @@ -90,20 +83,33 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - kwargs = {} + compute_client = self.app.client_manager.sdk_connection.compute + self.log.warning( + "API has been deprecated; " + "consider using 'compute service set' instead." + ) + + data = {} if parsed_args.enable: - kwargs['status'] = 'enable' + data['status'] = 'enable' if parsed_args.disable: - kwargs['status'] = 'disable' + data['status'] = 'disable' if parsed_args.enable_maintenance: - kwargs['maintenance_mode'] = 'enable' + data['maintenance_mode'] = 'enable' if parsed_args.disable_maintenance: - kwargs['maintenance_mode'] = 'disable' + data['maintenance_mode'] = 'disable' - compute_client = self.app.client_manager.compute + if not data: + # don't bother calling if nothing given + return - compute_client.api.host_set(parsed_args.host, **kwargs) + # doing this since openstacksdk has decided not to support this + # deprecated command + response = compute_client.put( + f'/os-hosts/{parsed_args.host}', json=data, microversion='2.1' + ) + sdk_exceptions.raise_from_response(response) class ShowHost(command.Lister): @@ -116,26 +122,25 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): compute_client = self.app.client_manager.sdk_connection.compute - columns = ("Host", "Project", "CPU", "Memory MB", "Disk GB") self.log.warning( - "API has been deprecated. " - "Please consider using 'hypervisor show' instead." + "API has been deprecated; " + "consider using 'hypervisor show' instead." ) # doing this since openstacksdk has decided not to support this # deprecated command - resources = ( - compute_client.get( - '/os-hosts/' + parsed_args.host, microversion='2.1' - ) - .json() - .get('host') + response = compute_client.get( + f'/os-hosts/{parsed_args.host}', microversion='2.1' ) + sdk_exceptions.raise_from_response(response) + resources = response.json().get('host') data = [] if resources is not None: for resource in resources: data.append(resource['resource']) + columns = ("Host", "Project", "CPU", "Memory MB", "Disk GB") + return columns, (utils.get_dict_properties(s, columns) for s in data) diff --git a/openstackclient/tests/unit/api/test_compute_v2.py b/openstackclient/tests/unit/api/test_compute_v2.py index 3856a7700..3bcd2a912 100644 --- a/openstackclient/tests/unit/api/test_compute_v2.py +++ b/openstackclient/tests/unit/api/test_compute_v2.py @@ -227,114 +227,6 @@ def test_floating_ip_pool_list(self): self.assertEqual(self.LIST_FLOATING_IP_POOL_RESP, ret) -class TestHost(TestComputeAPIv2): - FAKE_HOST_RESP_1 = { - "zone": "internal", - "host_name": "myhost", - "service": "conductor", - } - - FAKE_HOST_RESP_2 = { - "zone": "internal", - "host_name": "myhost", - "service": "scheduler", - } - - FAKE_HOST_RESP_3 = { - "zone": "nova", - "host_name": "myhost", - "service": "compute", - } - - LIST_HOST_RESP = [ - FAKE_HOST_RESP_1, - FAKE_HOST_RESP_2, - FAKE_HOST_RESP_3, - ] - - def test_host_list_no_options(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-hosts', - json={'hosts': self.LIST_HOST_RESP}, - status_code=200, - ) - ret = self.api.host_list() - self.assertEqual(self.LIST_HOST_RESP, ret) - - def test_host_list_zone(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-hosts?zone=nova', - json={'hosts': [self.FAKE_HOST_RESP_3]}, - status_code=200, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-hosts', - json={'hosts': [self.FAKE_HOST_RESP_3]}, - status_code=200, - ) - ret = self.api.host_list(zone='nova') - self.assertEqual([self.FAKE_HOST_RESP_3], ret) - - def test_host_set_none(self): - ret = self.api.host_set(host='myhost') - self.assertIsNone(ret) - - def test_host_set(self): - self.requests_mock.register_uri( - 'PUT', - FAKE_URL + '/os-hosts/myhost', - json={}, - status_code=200, - ) - ret = self.api.host_set(host='myhost', status='enabled') - self.assertEqual({}, ret) - - def test_host_show(self): - FAKE_RESOURCE_1 = { - "cpu": 2, - "disk_gb": 1028, - "host": "c1a7de0ac9d94e4baceae031d05caae3", - "memory_mb": 8192, - "project": "(total)", - } - FAKE_RESOURCE_2 = { - "cpu": 0, - "disk_gb": 0, - "host": "c1a7de0ac9d94e4baceae031d05caae3", - "memory_mb": 512, - "project": "(used_now)", - } - FAKE_RESOURCE_3 = { - "cpu": 0, - "disk_gb": 0, - "host": "c1a7de0ac9d94e4baceae031d05caae3", - "memory_mb": 0, - "project": "(used_max)", - } - FAKE_HOST_RESP = [ - {'resource': FAKE_RESOURCE_1}, - {'resource': FAKE_RESOURCE_2}, - {'resource': FAKE_RESOURCE_3}, - ] - FAKE_HOST_LIST = [ - FAKE_RESOURCE_1, - FAKE_RESOURCE_2, - FAKE_RESOURCE_3, - ] - - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-hosts/myhost', - json={'host': FAKE_HOST_RESP}, - status_code=200, - ) - ret = self.api.host_show(host='myhost') - self.assertEqual(FAKE_HOST_LIST, ret) - - class TestNetwork(TestComputeAPIv2): FAKE_NETWORK_RESP = { 'id': '1', diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 6c201db46..f3042f32b 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -127,9 +127,6 @@ def __init__(self, **kwargs): self.keypairs = mock.Mock() self.keypairs.resource_class = fakes.FakeResource(None, {}) - self.hosts = mock.Mock() - self.hosts.resource_class = fakes.FakeResource(None, {}) - self.server_groups = mock.Mock() self.server_groups.resource_class = fakes.FakeResource(None, {}) @@ -1012,56 +1009,6 @@ def get_networks(networks=None, count=2): return mock.Mock(side_effect=networks) -def create_one_host(attrs=None): - """Create a fake host. - - :param dict attrs: - A dictionary with all attributes - :return: - A FakeResource object, with uuid and other attributes - """ - attrs = attrs or {} - - # Set default attributes. - host_info = { - "service_id": 1, - "host": "host1", - "uuid": 'host-id-' + uuid.uuid4().hex, - "vcpus": 10, - "memory_mb": 100, - "local_gb": 100, - "vcpus_used": 5, - "memory_mb_used": 50, - "local_gb_used": 10, - "hypervisor_type": "xen", - "hypervisor_version": 1, - "hypervisor_hostname": "devstack1", - "free_ram_mb": 50, - "free_disk_gb": 50, - "current_workload": 10, - "running_vms": 1, - "cpu_info": "", - "disk_available_least": 1, - "host_ip": "10.10.10.10", - "supported_instances": "", - "metrics": "", - "pci_stats": "", - "extra_resources": "", - "stats": "", - "numa_topology": "", - "ram_allocation_ratio": 1.0, - "cpu_allocation_ratio": 1.0, - "zone": 'zone-' + uuid.uuid4().hex, - "host_name": 'name-' + uuid.uuid4().hex, - "service": 'service-' + uuid.uuid4().hex, - "cpu": 4, - "disk_gb": 100, - 'project': 'project-' + uuid.uuid4().hex, - } - host_info.update(attrs) - return host_info - - def create_one_usage(attrs=None): """Create a fake usage. diff --git a/openstackclient/tests/unit/compute/v2/test_host.py b/openstackclient/tests/unit/compute/v2/test_host.py index 0ad83289c..afda8e958 100644 --- a/openstackclient/tests/unit/compute/v2/test_host.py +++ b/openstackclient/tests/unit/compute/v2/test_host.py @@ -11,9 +11,8 @@ # 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 +import uuid from openstackclient.compute.v2 import host from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes @@ -21,19 +20,51 @@ from openstackclient.tests.unit import utils as tests_utils -@mock.patch('openstackclient.api.compute_v2.APIv2.host_list') +def _generate_fake_host(): + return { + 'service_id': 1, + 'host': 'host1', + 'uuid': 'host-id-' + uuid.uuid4().hex, + 'vcpus': 10, + 'memory_mb': 100, + 'local_gb': 100, + 'vcpus_used': 5, + 'memory_mb_used': 50, + 'local_gb_used': 10, + 'hypervisor_type': 'xen', + 'hypervisor_version': 1, + 'hypervisor_hostname': 'devstack1', + 'free_ram_mb': 50, + 'free_disk_gb': 50, + 'current_workload': 10, + 'running_vms': 1, + 'cpu_info': '', + 'disk_available_least': 1, + 'host_ip': '10.10.10.10', + 'supported_instances': '', + 'metrics': '', + 'pci_stats': '', + 'extra_resources': '', + 'stats': '', + 'numa_topology': '', + 'ram_allocation_ratio': 1.0, + 'cpu_allocation_ratio': 1.0, + 'zone': 'zone-' + uuid.uuid4().hex, + 'host_name': 'name-' + uuid.uuid4().hex, + 'service': 'service-' + uuid.uuid4().hex, + 'cpu': 4, + 'disk_gb': 100, + 'project': 'project-' + uuid.uuid4().hex, + } + + class TestHostList(compute_fakes.TestComputev2): - _host = compute_fakes.create_one_host() def setUp(self): super().setUp() - self.compute_sdk_client.get.return_value = fakes.FakeResponse( - data={'hosts': [self._host]} - ) - + self._host = _generate_fake_host() self.columns = ('Host Name', 'Service', 'Zone') - self.data = [ ( self._host['host_name'], @@ -42,10 +73,12 @@ def setUp(self): ) ] + self.compute_sdk_client.get.return_value = fakes.FakeResponse( + data={'hosts': [self._host]} + ) self.cmd = host.ListHost(self.app, None) - def test_host_list_no_option(self, h_mock): - h_mock.return_value = [self._host] + def test_host_list_no_option(self): arglist = [] verifylist = [] @@ -59,8 +92,7 @@ def test_host_list_no_option(self, h_mock): self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) - def test_host_list_with_option(self, h_mock): - h_mock.return_value = [self._host] + def test_host_list_with_option(self): arglist = [ '--zone', self._host['zone'], @@ -80,76 +112,60 @@ def test_host_list_with_option(self, h_mock): self.assertEqual(self.data, list(data)) -@mock.patch('openstackclient.api.compute_v2.APIv2.host_set') class TestHostSet(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.host = compute_fakes.create_one_host() + self._host = _generate_fake_host() + self.compute_sdk_client.put.return_value = fakes.FakeResponse() self.cmd = host.SetHost(self.app, None) - def test_host_set_no_option(self, h_mock): - h_mock.return_value = self.host - h_mock.update.return_value = None + def test_host_set_no_option(self): arglist = [ - self.host['host'], + self._host['host'], ] verifylist = [ - ('host', self.host['host']), + ('host', self._host['host']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertIsNone(result) + self.compute_sdk_client.put.assert_not_called() - h_mock.assert_called_with(self.host['host']) - - def test_host_set(self, h_mock): - h_mock.return_value = self.host - h_mock.update.return_value = None + def test_host_set(self): arglist = [ '--enable', '--disable-maintenance', - self.host['host'], + self._host['host'], ] verifylist = [ ('enable', True), ('enable_maintenance', False), - ('host', self.host['host']), + ('host', self._host['host']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - - h_mock.assert_called_with( - self.host['host'], status='enable', maintenance_mode='disable' + self.compute_sdk_client.put.assert_called_with( + f'/os-hosts/{self._host["host"]}', + json={ + 'maintenance_mode': 'disable', + 'status': 'enable', + }, + microversion='2.1', ) -@mock.patch('openstackclient.api.compute_v2.APIv2.host_show') class TestHostShow(compute_fakes.TestComputev2): - _host = compute_fakes.create_one_host() - def setUp(self): super().setUp() - output_data = { - "resource": { - "host": self._host['host'], - "project": self._host['project'], - "cpu": self._host['cpu'], - "memory_mb": self._host['memory_mb'], - "disk_gb": self._host['disk_gb'], - } - } - - self.compute_sdk_client.get.return_value = fakes.FakeResponse( - data={'host': [output_data]} - ) + self._host = _generate_fake_host() self.columns = ( 'Host', @@ -158,7 +174,6 @@ def setUp(self): 'Memory MB', 'Disk GB', ) - self.data = [ ( self._host['host'], @@ -169,10 +184,25 @@ def setUp(self): ) ] + self.compute_sdk_client.get.return_value = fakes.FakeResponse( + data={ + 'host': [ + { + 'resource': { + 'host': self._host['host'], + 'project': self._host['project'], + 'cpu': self._host['cpu'], + 'memory_mb': self._host['memory_mb'], + 'disk_gb': self._host['disk_gb'], + } + } + ], + } + ) + self.cmd = host.ShowHost(self.app, None) - def test_host_show_no_option(self, h_mock): - h_mock.host_show.return_value = [self._host] + def test_host_show_no_option(self): arglist = [] verifylist = [] @@ -185,8 +215,7 @@ def test_host_show_no_option(self, h_mock): verifylist, ) - def test_host_show_with_option(self, h_mock): - h_mock.return_value = [self._host] + def test_host_show_with_option(self): arglist = [ self._host['host_name'], ] diff --git a/releasenotes/notes/migrate-host-set-438997eb6f81f2b1.yaml b/releasenotes/notes/migrate-host-set-438997eb6f81f2b1.yaml new file mode 100644 index 000000000..f93566091 --- /dev/null +++ b/releasenotes/notes/migrate-host-set-438997eb6f81f2b1.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + The ``host set`` command has been migrated to SDK. From 0f006392ac6282c011d4931b8a2f3a5d2351aa36 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 7 May 2024 11:46:10 +0100 Subject: [PATCH 132/403] compute: Migrate remaining tests to SDK objects Change-Id: I33b80007777a0483964cc04357961bf0c5eda6e5 Signed-off-by: Stephen Finucane --- openstackclient/tests/unit/compute/v2/test_console.py | 5 ++--- openstackclient/tests/unit/compute/v2/test_server_event.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/test_console.py b/openstackclient/tests/unit/compute/v2/test_console.py index ed64e0538..a179db5a7 100644 --- a/openstackclient/tests/unit/compute/v2/test_console.py +++ b/openstackclient/tests/unit/compute/v2/test_console.py @@ -21,11 +21,10 @@ class TestConsoleLog(compute_fakes.TestComputev2): - _server = compute_fakes.create_one_server() - def setUp(self): super().setUp() + self._server = compute_fakes.create_one_sdk_server() self.compute_sdk_client.find_server.return_value = self._server self.cmd = console.ShowConsoleLog(self.app, None) @@ -77,7 +76,7 @@ def test_show_lines(self): class TestConsoleUrlShow(compute_fakes.TestComputev2): - _server = compute_fakes.create_one_server() + _server = compute_fakes.create_one_sdk_server() def setUp(self): super().setUp() diff --git a/openstackclient/tests/unit/compute/v2/test_server_event.py b/openstackclient/tests/unit/compute/v2/test_server_event.py index 5f76e2adf..1ce2e4d0e 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_event.py +++ b/openstackclient/tests/unit/compute/v2/test_server_event.py @@ -24,7 +24,7 @@ class TestServerEvent(compute_fakes.TestComputev2): - fake_server = compute_fakes.create_one_server() + fake_server = compute_fakes.create_one_sdk_server() def setUp(self): super().setUp() From 3f781cc3f993d5dbbbe0ba9a47e310aa446b9bd8 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 7 May 2024 11:51:52 +0100 Subject: [PATCH 133/403] docs: Remove references to novaclient Change-Id: Ieb1dc77f311a2e279036587a1f8d575a387494d0 Signed-off-by: Stephen Finucane --- doc/source/contributor/command-errors.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/source/contributor/command-errors.rst b/doc/source/contributor/command-errors.rst index 453ae3a6e..5204e06b4 100644 --- a/doc/source/contributor/command-errors.rst +++ b/doc/source/contributor/command-errors.rst @@ -29,8 +29,9 @@ Example This example is taken from ``keypair create`` where the ``--public-key`` option specifies a file containing the public key to upload. If the file is not found, -the IOError exception is trapped and a more specific CommandError exception is -raised that includes the name of the file that was attempted to be opened. +the ``IOError`` exception is trapped and a more specific ``CommandError`` +exception is raised that includes the name of the file that was attempted to be +opened. .. code-block:: python @@ -40,7 +41,7 @@ raised that includes the name of the file that was attempted to be opened. ## ... def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute public_key = parsed_args.public_key if public_key: @@ -56,8 +57,8 @@ raised that includes the name of the file that was attempted to be opened. msg % (parsed_args.public_key, e), ) - keypair = compute_client.keypairs.create( - parsed_args.name, + keypair = compute_client.create_keypair( + name=parsed_args.name, public_key=public_key, ) From ca91c826e34afc1cf832ccc7d83d3faadd9dc3f4 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 7 May 2024 15:00:23 +0100 Subject: [PATCH 134/403] volume: Add v3-specific volume backup module This makes testing easier. Change-Id: I71a13b34a85350af17612e12c03e6df8cb041f86 Signed-off-by: Stephen Finucane --- openstackclient/tests/unit/volume/v2/fakes.py | 29 +- .../unit/volume/v2/test_volume_backup.py | 371 +------ openstackclient/tests/unit/volume/v3/fakes.py | 84 ++ .../unit/volume/v3/test_volume_backup.py | 918 ++++++++++++++++++ openstackclient/volume/v2/volume_backup.py | 210 ---- openstackclient/volume/v3/volume_backup.py | 670 +++++++++++++ setup.cfg | 14 +- 7 files changed, 1696 insertions(+), 600 deletions(-) create mode 100644 openstackclient/tests/unit/volume/v3/test_volume_backup.py create mode 100644 openstackclient/volume/v3/volume_backup.py diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index 975a6a717..d39ec5a3c 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -20,7 +20,7 @@ # FIXME(stephenfin): We are using v3 resource versions despite being v2 fakes from cinderclient import api_versions from openstack.block_storage.v2 import _proxy as block_storage_v2_proxy -from openstack.block_storage.v3 import backup as _backup +from openstack.block_storage.v2 import backup as _backup from openstack.block_storage.v3 import capabilities as _capabilities from openstack.block_storage.v3 import stats as _stats from openstack.block_storage.v3 import volume as _volume @@ -514,34 +514,29 @@ def create_one_backup(attrs=None): :param dict attrs: A dictionary with all attributes - :return: - A FakeResource object with id, name, volume_id, etc. + :return: A fake + openstack.block_storage.v2.backup.Backup object """ attrs = attrs or {} # Set default attributes. backup_info = { + "availability_zone": 'zone' + uuid.uuid4().hex, + "container": 'container-' + uuid.uuid4().hex, "created_at": 'time-' + uuid.uuid4().hex, "data_timestamp": 'time-' + uuid.uuid4().hex, - "id": 'backup-id-' + uuid.uuid4().hex, - "encryption_key_id": None, + "description": 'description-' + uuid.uuid4().hex, "fail_reason": "Service not found for creating backup.", "has_dependent_backups": False, + "id": 'backup-id-' + uuid.uuid4().hex, "is_incremental": False, - "metadata": {}, - "project_id": uuid.uuid4().hex, - "updated_at": 'time-' + uuid.uuid4().hex, - "user_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), - "is_incremental": False, + "snapshot_id": 'snapshot-id' + uuid.uuid4().hex, "status": "error", - "availability_zone": 'zone' + uuid.uuid4().hex, + "updated_at": 'time-' + uuid.uuid4().hex, + "volume_id": 'volume-id-' + uuid.uuid4().hex, } # Overwrite default attributes. @@ -558,8 +553,8 @@ def create_backups(attrs=None, count=2): A dictionary with all attributes :param int count: The number of backups to fake - :return: - A list of FakeResource objects faking the backups + :return: A list of fake + openstack.block_storage.v2.backup.Backup objects """ backups = [] for i in range(0, count): diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index e16bdbb81..e9a0024fa 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -10,13 +10,9 @@ # 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 cinderclient import api_versions -from openstack import utils as sdk_utils from osc_lib import exceptions from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes @@ -37,29 +33,7 @@ def setUp(self): self.restores_mock.reset_mock() -class TestBackup(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - patcher = mock.patch.object( - sdk_utils, 'supports_microversion', return_value=True - ) - self.addCleanup(patcher.stop) - self.supports_microversion_mock = patcher.start() - self._set_mock_microversion( - self.app.client_manager.volume.api_version.get_string() - ) - - def _set_mock_microversion(self, mock_v): - """Set a specific microversion for the mock supports_microversion().""" - self.supports_microversion_mock.reset_mock(return_value=True) - self.supports_microversion_mock.side_effect = ( - lambda _, v: api_versions.APIVersion(v) - <= api_versions.APIVersion(mock_v) - ) - - -class TestBackupCreate(TestBackup): +class TestBackupCreate(volume_fakes.TestVolume): volume = volume_fakes.create_one_volume() snapshot = volume_fakes.create_one_snapshot() new_backup = volume_fakes.create_one_backup( @@ -126,104 +100,6 @@ def test_backup_create(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - def test_backup_create_with_properties(self): - self._set_mock_microversion('3.43') - - arglist = [ - "--property", - "foo=bar", - "--property", - "wow=much-cool", - self.new_backup.volume_id, - ] - verifylist = [ - ("properties", {"foo": "bar", "wow": "much-cool"}), - ("volume", self.new_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=None, - name=None, - description=None, - force=False, - is_incremental=False, - metadata={"foo": "bar", "wow": "much-cool"}, - ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - def test_backup_create_with_properties_pre_v343(self): - self._set_mock_microversion('3.42') - - arglist = [ - "--property", - "foo=bar", - "--property", - "wow=much-cool", - self.new_backup.volume_id, - ] - verifylist = [ - ("properties", {"foo": "bar", "wow": "much-cool"}), - ("volume", self.new_backup.volume_id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - self.assertIn("--os-volume-api-version 3.43 or greater", str(exc)) - - def test_backup_create_with_availability_zone(self): - self._set_mock_microversion('3.51') - - arglist = [ - "--availability-zone", - "my-az", - self.new_backup.volume_id, - ] - verifylist = [ - ("availability_zone", "my-az"), - ("volume", self.new_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=None, - name=None, - description=None, - force=False, - is_incremental=False, - availability_zone="my-az", - ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - def test_backup_create_with_availability_zone_pre_v351(self): - self._set_mock_microversion('3.50') - - arglist = [ - "--availability-zone", - "my-az", - self.new_backup.volume_id, - ] - verifylist = [ - ("availability_zone", "my-az"), - ("volume", self.new_backup.volume_id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - self.assertIn("--os-volume-api-version 3.51 or greater", str(exc)) - def test_backup_create_without_name(self): arglist = [ "--description", @@ -253,7 +129,7 @@ def test_backup_create_without_name(self): self.assertEqual(self.data, data) -class TestBackupDelete(TestBackup): +class TestBackupDelete(volume_fakes.TestVolume): backups = volume_fakes.create_backups(count=2) def setUp(self): @@ -346,7 +222,7 @@ def test_delete_multiple_backups_with_exception(self): ) -class TestBackupList(TestBackup): +class TestBackupList(volume_fakes.TestVolume): volume = volume_fakes.create_one_volume() backups = volume_fakes.create_backups( attrs={'volume_id': volume.name}, count=3 @@ -479,7 +355,7 @@ def test_backup_list_with_options(self): self.assertCountEqual(self.data_long, list(data)) -class TestBackupRestore(TestBackup): +class TestBackupRestore(volume_fakes.TestVolume): volume = volume_fakes.create_one_volume() backup = volume_fakes.create_one_backup( attrs={'volume_id': volume.id}, @@ -592,91 +468,6 @@ def setUp(self): # Get the command object to test self.cmd = volume_backup.SetVolumeBackup(self.app, None) - def test_backup_set_name(self): - self.volume_client.api_version = api_versions.APIVersion('3.9') - - arglist = [ - '--name', - 'new_name', - self.backup.id, - ] - verifylist = [ - ('name', 'new_name'), - ('backup', self.backup.id), - ] - 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) - - def test_backup_set_name_pre_v39(self): - self.volume_client.api_version = api_versions.APIVersion('3.8') - - arglist = [ - '--name', - 'new_name', - self.backup.id, - ] - verifylist = [ - ('name', 'new_name'), - ('backup', self.backup.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - self.assertIn("--os-volume-api-version 3.9 or greater", str(exc)) - - def test_backup_set_description(self): - self.volume_client.api_version = api_versions.APIVersion('3.9') - - arglist = [ - '--description', - 'new_description', - self.backup.id, - ] - verifylist = [ - ('name', None), - ('description', 'new_description'), - ('backup', self.backup.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - kwargs = {'description': 'new_description'} - self.backups_mock.update.assert_called_once_with( - self.backup.id, **kwargs - ) - self.assertIsNone(result) - - def test_backup_set_description_pre_v39(self): - self.volume_client.api_version = api_versions.APIVersion('3.8') - - arglist = [ - '--description', - 'new_description', - self.backup.id, - ] - verifylist = [ - ('name', None), - ('description', 'new_description'), - ('backup', self.backup.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - self.assertIn("--os-volume-api-version 3.9 or greater", str(exc)) - def test_backup_set_state(self): arglist = ['--state', 'error', self.backup.id] verifylist = [('state', 'error'), ('backup', self.backup.id)] @@ -706,152 +497,8 @@ def test_backup_set_state_failed(self): self.backup.id, 'error' ) - def test_backup_set_no_property(self): - self.volume_client.api_version = api_versions.APIVersion('3.43') - - arglist = [ - '--no-property', - self.backup.id, - ] - verifylist = [ - ('no_property', True), - ('backup', self.backup.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - kwargs = { - 'metadata': {}, - } - self.backups_mock.update.assert_called_once_with( - self.backup.id, **kwargs - ) - self.assertIsNone(result) - - def test_backup_set_no_property_pre_v343(self): - self.volume_client.api_version = api_versions.APIVersion('3.42') - - arglist = [ - '--no-property', - self.backup.id, - ] - verifylist = [ - ('no_property', True), - ('backup', self.backup.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - self.assertIn("--os-volume-api-version 3.43 or greater", str(exc)) - - def test_backup_set_property(self): - self.volume_client.api_version = api_versions.APIVersion('3.43') - - arglist = [ - '--property', - 'foo=bar', - self.backup.id, - ] - verifylist = [ - ('properties', {'foo': 'bar'}), - ('backup', self.backup.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - kwargs = { - 'metadata': {'wow': 'cool', 'foo': 'bar'}, - } - self.backups_mock.update.assert_called_once_with( - self.backup.id, **kwargs - ) - self.assertIsNone(result) - - def test_backup_set_property_pre_v343(self): - self.volume_client.api_version = api_versions.APIVersion('3.42') - - arglist = [ - '--property', - 'foo=bar', - self.backup.id, - ] - verifylist = [ - ('properties', {'foo': 'bar'}), - ('backup', self.backup.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - 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'}}, - ) - - def setUp(self): - super().setUp() - - self.backups_mock.get.return_value = self.backup - - # Get the command object to test - self.cmd = volume_backup.UnsetVolumeBackup(self.app, None) - - def test_backup_unset_property(self): - self.volume_client.api_version = api_versions.APIVersion('3.43') - - arglist = [ - '--property', - 'foo', - self.backup.id, - ] - verifylist = [ - ('properties', ['foo']), - ('backup', self.backup.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - result = self.cmd.take_action(parsed_args) - - # Set expected values - kwargs = { - 'metadata': {}, - } - self.backups_mock.update.assert_called_once_with( - self.backup.id, **kwargs - ) - self.assertIsNone(result) - - def test_backup_unset_property_pre_v343(self): - self.volume_client.api_version = api_versions.APIVersion('3.42') - - arglist = [ - '--property', - 'foo', - self.backup.id, - ] - verifylist = [ - ('properties', ['foo']), - ('backup', self.backup.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - self.assertIn("--os-volume-api-version 3.43 or greater", str(exc)) - -class TestBackupShow(TestBackup): +class TestBackupShow(volume_fakes.TestVolume): backup = volume_fakes.create_one_backup() columns = ( @@ -860,20 +507,16 @@ class TestBackupShow(TestBackup): "created_at", "data_timestamp", "description", - "encryption_key_id", "fail_reason", "has_dependent_backups", "id", "is_incremental", - "metadata", "name", "object_count", - "project_id", "size", "snapshot_id", "status", "updated_at", - "user_id", "volume_id", ) data = ( @@ -882,20 +525,16 @@ class TestBackupShow(TestBackup): 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, ) diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index 8c786bc72..54ac747ec 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -17,6 +17,7 @@ from cinderclient import api_versions from openstack.block_storage.v3 import _proxy from openstack.block_storage.v3 import availability_zone as _availability_zone +from openstack.block_storage.v3 import backup as _backup from openstack.block_storage.v3 import extension as _extension from openstack.block_storage.v3 import resource_filter as _filters from openstack.block_storage.v3 import volume as _volume @@ -35,6 +36,8 @@ def __init__(self, **kwargs): self.availability_zones = mock.Mock() self.availability_zones.resource_class = fakes.FakeResource(None, {}) + self.backups = mock.Mock() + self.backups.resource_class = fakes.FakeResource(None, {}) self.attachments = mock.Mock() self.attachments.resource_class = fakes.FakeResource(None, {}) self.clusters = mock.Mock() @@ -53,6 +56,8 @@ def __init__(self, **kwargs): self.quotas.resource_class = fakes.FakeResource(None, {}) self.resource_filters = mock.Mock() self.resource_filters.resource_class = fakes.FakeResource(None, {}) + self.restores = mock.Mock() + self.restores.resource_class = fakes.FakeResource(None, {}) self.volumes = mock.Mock() self.volumes.resource_class = fakes.FakeResource(None, {}) self.volume_snapshots = mock.Mock() @@ -179,6 +184,85 @@ def create_one_extension(attrs=None): return extension +def create_one_backup(attrs=None): + """Create a fake backup. + + :param dict attrs: + A dictionary with all attributes + :return: A fake + openstack.block_storage.v3.backup.Backup object + """ + attrs = attrs or {} + + # Set default attributes. + backup_info = { + "availability_zone": 'zone' + uuid.uuid4().hex, + "container": 'container-' + uuid.uuid4().hex, + "created_at": 'time-' + uuid.uuid4().hex, + "data_timestamp": 'time-' + uuid.uuid4().hex, + "description": 'description-' + uuid.uuid4().hex, + "encryption_key_id": None, + "fail_reason": "Service not found for creating backup.", + "has_dependent_backups": False, + "id": 'backup-id-' + uuid.uuid4().hex, + "is_incremental": False, + "metadata": {}, + "name": 'backup-name-' + uuid.uuid4().hex, + "object_count": None, + "project_id": uuid.uuid4().hex, + "size": random.randint(1, 20), + "snapshot_id": 'snapshot-id' + uuid.uuid4().hex, + "status": "error", + "updated_at": 'time-' + uuid.uuid4().hex, + "user_id": uuid.uuid4().hex, + "volume_id": 'volume-id-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + backup_info.update(attrs) + + backup = _backup.Backup(**backup_info) + return backup + + +def create_backups(attrs=None, count=2): + """Create multiple fake backups. + + :param dict attrs: + A dictionary with all attributes + :param int count: + The number of backups to fake + :return: A list of fake + openstack.block_storage.v3.backup.Backup objects + """ + 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 backups: + 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) + + def create_one_cluster(attrs=None): """Create a fake service cluster. diff --git a/openstackclient/tests/unit/volume/v3/test_volume_backup.py b/openstackclient/tests/unit/volume/v3/test_volume_backup.py new file mode 100644 index 000000000..dfa3084dd --- /dev/null +++ b/openstackclient/tests/unit/volume/v3/test_volume_backup.py @@ -0,0 +1,918 @@ +# +# 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 cinderclient import api_versions +from openstack import utils as sdk_utils +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 TestBackup(volume_fakes.TestVolume): + def setUp(self): + super().setUp() + + patcher = mock.patch.object( + sdk_utils, 'supports_microversion', return_value=True + ) + self.addCleanup(patcher.stop) + self.supports_microversion_mock = patcher.start() + self._set_mock_microversion( + self.app.client_manager.volume.api_version.get_string() + ) + + def _set_mock_microversion(self, mock_v): + """Set a specific microversion for the mock supports_microversion().""" + self.supports_microversion_mock.reset_mock(return_value=True) + self.supports_microversion_mock.side_effect = ( + lambda _, v: api_versions.APIVersion(v) + <= api_versions.APIVersion(mock_v) + ) + + +class TestBackupCreate(TestBackup): + 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_client.find_volume.return_value = self.volume + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.volume_sdk_client.create_backup.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, + "--force", + "--incremental", + "--snapshot", + self.new_backup.snapshot_id, + self.new_backup.volume_id, + ] + verifylist = [ + ("name", self.new_backup.name), + ("description", self.new_backup.description), + ("container", self.new_backup.container), + ("force", True), + ("incremental", True), + ("snapshot", self.new_backup.snapshot_id), + ("volume", self.new_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, + force=True, + is_incremental=True, + snapshot_id=self.new_backup.snapshot_id, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_backup_create_with_properties(self): + self._set_mock_microversion('3.43') + + arglist = [ + "--property", + "foo=bar", + "--property", + "wow=much-cool", + self.new_backup.volume_id, + ] + verifylist = [ + ("properties", {"foo": "bar", "wow": "much-cool"}), + ("volume", self.new_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=None, + name=None, + description=None, + force=False, + is_incremental=False, + metadata={"foo": "bar", "wow": "much-cool"}, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_backup_create_with_properties_pre_v343(self): + self._set_mock_microversion('3.42') + + arglist = [ + "--property", + "foo=bar", + "--property", + "wow=much-cool", + self.new_backup.volume_id, + ] + verifylist = [ + ("properties", {"foo": "bar", "wow": "much-cool"}), + ("volume", self.new_backup.volume_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn("--os-volume-api-version 3.43 or greater", str(exc)) + + def test_backup_create_with_availability_zone(self): + self._set_mock_microversion('3.51') + + arglist = [ + "--availability-zone", + "my-az", + self.new_backup.volume_id, + ] + verifylist = [ + ("availability_zone", "my-az"), + ("volume", self.new_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=None, + name=None, + description=None, + force=False, + is_incremental=False, + availability_zone="my-az", + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_backup_create_with_availability_zone_pre_v351(self): + self._set_mock_microversion('3.50') + + arglist = [ + "--availability-zone", + "my-az", + self.new_backup.volume_id, + ] + verifylist = [ + ("availability_zone", "my-az"), + ("volume", self.new_backup.volume_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn("--os-volume-api-version 3.51 or greater", str(exc)) + + 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.volume_sdk_client.create_backup.assert_called_with( + volume_id=self.new_backup.volume_id, + container=self.new_backup.container, + name=None, + description=self.new_backup.description, + force=False, + is_incremental=False, + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestBackupDelete(TestBackup): + 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.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): + 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.volume_sdk_client.delete_backup.assert_called_with( + self.backups[0].id, ignore_missing=False, force=False + ) + self.assertIsNone(result) + + def test_backup_delete_with_force(self): + arglist = [ + '--force', + self.backups[0].id, + ] + verifylist = [('force', True), ("backups", [self.backups[0].id])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.delete_backup.assert_called_with( + self.backups[0].id, ignore_missing=False, force=True + ) + 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, ignore_missing=False, force=False)) + self.volume_sdk_client.delete_backup.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] + self.volume_sdk_client.find_backup.side_effect = find_mock_result + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 backups failed to delete.', str(e)) + + self.volume_sdk_client.find_backup.assert_any_call( + self.backups[0].id, ignore_missing=False + ) + self.volume_sdk_client.find_backup.assert_any_call( + 'unexist_backup', ignore_missing=False + ) + + self.assertEqual(2, self.volume_sdk_client.find_backup.call_count) + self.volume_sdk_client.delete_backup.assert_called_once_with( + self.backups[0].id, + ignore_missing=False, + force=False, + ) + + +class TestBackupList(TestBackup): + volume = volume_fakes.create_one_volume() + backups = volume_fakes.create_backups( + attrs={'volume_id': volume.name}, count=3 + ) + + columns = ( + 'ID', + 'Name', + 'Description', + 'Status', + 'Size', + 'Incremental', + ) + columns_long = columns + ( + 'Availability Zone', + 'Volume', + 'Container', + ) + + data = [] + for b in backups: + data.append( + ( + b.id, + b.name, + b.description, + b.status, + b.size, + b.is_incremental, + ) + ) + data_long = [] + for b in backups: + data_long.append( + ( + b.id, + b.name, + b.description, + b.status, + b.size, + b.is_incremental, + b.availability_zone, + volume_backup.VolumeIdColumn(b.volume_id), + b.container, + ) + ) + + def setUp(self): + super().setUp() + + self.volume_sdk_client.volumes.return_value = [self.volume] + 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.cmd = volume_backup.ListVolumeBackup(self.app, None) + + def test_backup_list_without_options(self): + arglist = [] + verifylist = [ + ("long", False), + ("name", None), + ("status", None), + ("volume", None), + ("marker", None), + ("limit", None), + ('all_projects', False), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.find_volume.assert_not_called() + self.volume_sdk_client.find_backup.assert_not_called() + self.volume_sdk_client.backups.assert_called_with( + name=None, + status=None, + volume_id=None, + all_tenants=False, + marker=None, + limit=None, + ) + 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, + "--marker", + self.backups[0].id, + "--all-projects", + "--limit", + "3", + ] + verifylist = [ + ("long", True), + ("name", self.backups[0].name), + ("status", "error"), + ("volume", self.volume.id), + ("marker", self.backups[0].id), + ('all_projects', True), + ("limit", 3), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.find_volume.assert_called_once_with( + self.volume.id, ignore_missing=False + ) + self.volume_sdk_client.find_backup.assert_called_once_with( + self.backups[0].id, ignore_missing=False + ) + self.volume_sdk_client.backups.assert_called_with( + name=self.backups[0].name, + status="error", + volume_id=self.volume.id, + all_tenants=True, + marker=self.backups[0].id, + limit=3, + ) + 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.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']}, + ) + ) + + # Get the command object to mock + self.cmd = volume_backup.RestoreVolumeBackup(self.app, None) + + def test_backup_restore(self): + self.volume_sdk_client.find_volume.side_effect = ( + exceptions.CommandError() + ) + 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.volume_sdk_client.restore_backup.assert_called_with( + self.backup.id, + volume_id=None, + name=None, + ) + self.assertIsNotNone(result) + + def test_backup_restore_with_volume(self): + self.volume_sdk_client.find_volume.side_effect = ( + exceptions.CommandError() + ) + 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.volume_sdk_client.restore_backup.assert_called_with( + self.backup.id, + volume_id=None, + name=self.backup.volume_id, + ) + self.assertIsNotNone(result) + + def test_backup_restore_with_volume_force(self): + arglist = [ + "--force", + self.backup.id, + self.volume.name, + ] + verifylist = [ + ("force", True), + ("backup", self.backup.id), + ("volume", self.volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = 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) + + def test_backup_restore_with_volume_existing(self): + arglist = [ + self.backup.id, + self.volume.name, + ] + verifylist = [ + ("backup", self.backup.id), + ("volume", self.volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + +class TestBackupSet(TestBackupLegacy): + backup = volume_fakes.create_one_backup( + attrs={'metadata': {'wow': 'cool'}}, + ) + + def setUp(self): + super().setUp() + + self.backups_mock.get.return_value = self.backup + + # Get the command object to test + self.cmd = volume_backup.SetVolumeBackup(self.app, None) + + def test_backup_set_name(self): + self.volume_client.api_version = api_versions.APIVersion('3.9') + + arglist = [ + '--name', + 'new_name', + self.backup.id, + ] + verifylist = [ + ('name', 'new_name'), + ('backup', self.backup.id), + ] + 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) + + def test_backup_set_name_pre_v39(self): + self.volume_client.api_version = api_versions.APIVersion('3.8') + + arglist = [ + '--name', + 'new_name', + self.backup.id, + ] + verifylist = [ + ('name', 'new_name'), + ('backup', self.backup.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn("--os-volume-api-version 3.9 or greater", str(exc)) + + def test_backup_set_description(self): + self.volume_client.api_version = api_versions.APIVersion('3.9') + + arglist = [ + '--description', + 'new_description', + self.backup.id, + ] + verifylist = [ + ('name', None), + ('description', 'new_description'), + ('backup', self.backup.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = {'description': 'new_description'} + self.backups_mock.update.assert_called_once_with( + self.backup.id, **kwargs + ) + self.assertIsNone(result) + + def test_backup_set_description_pre_v39(self): + self.volume_client.api_version = api_versions.APIVersion('3.8') + + arglist = [ + '--description', + 'new_description', + self.backup.id, + ] + verifylist = [ + ('name', None), + ('description', 'new_description'), + ('backup', self.backup.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn("--os-volume-api-version 3.9 or greater", str(exc)) + + def test_backup_set_state(self): + arglist = ['--state', 'error', self.backup.id] + verifylist = [('state', 'error'), ('backup', self.backup.id)] + + 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) + + def test_backup_set_state_failed(self): + self.backups_mock.reset_state.side_effect = exceptions.CommandError() + 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' + ) + + def test_backup_set_no_property(self): + self.volume_client.api_version = api_versions.APIVersion('3.43') + + arglist = [ + '--no-property', + self.backup.id, + ] + verifylist = [ + ('no_property', True), + ('backup', self.backup.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'metadata': {}, + } + self.backups_mock.update.assert_called_once_with( + self.backup.id, **kwargs + ) + self.assertIsNone(result) + + def test_backup_set_no_property_pre_v343(self): + self.volume_client.api_version = api_versions.APIVersion('3.42') + + arglist = [ + '--no-property', + self.backup.id, + ] + verifylist = [ + ('no_property', True), + ('backup', self.backup.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn("--os-volume-api-version 3.43 or greater", str(exc)) + + def test_backup_set_property(self): + self.volume_client.api_version = api_versions.APIVersion('3.43') + + arglist = [ + '--property', + 'foo=bar', + self.backup.id, + ] + verifylist = [ + ('properties', {'foo': 'bar'}), + ('backup', self.backup.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'metadata': {'wow': 'cool', 'foo': 'bar'}, + } + self.backups_mock.update.assert_called_once_with( + self.backup.id, **kwargs + ) + self.assertIsNone(result) + + def test_backup_set_property_pre_v343(self): + self.volume_client.api_version = api_versions.APIVersion('3.42') + + arglist = [ + '--property', + 'foo=bar', + self.backup.id, + ] + verifylist = [ + ('properties', {'foo': 'bar'}), + ('backup', self.backup.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + 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'}}, + ) + + def setUp(self): + super().setUp() + + self.backups_mock.get.return_value = self.backup + + # Get the command object to test + self.cmd = volume_backup.UnsetVolumeBackup(self.app, None) + + def test_backup_unset_property(self): + self.volume_client.api_version = api_versions.APIVersion('3.43') + + arglist = [ + '--property', + 'foo', + self.backup.id, + ] + verifylist = [ + ('properties', ['foo']), + ('backup', self.backup.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + # Set expected values + kwargs = { + 'metadata': {}, + } + self.backups_mock.update.assert_called_once_with( + self.backup.id, **kwargs + ) + self.assertIsNone(result) + + def test_backup_unset_property_pre_v343(self): + self.volume_client.api_version = api_versions.APIVersion('3.42') + + arglist = [ + '--property', + 'foo', + self.backup.id, + ] + verifylist = [ + ('properties', ['foo']), + ('backup', self.backup.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn("--os-volume-api-version 3.43 or greater", str(exc)) + + +class TestBackupShow(TestBackup): + backup = volume_fakes.create_one_backup() + + columns = ( + "availability_zone", + "container", + "created_at", + "data_timestamp", + "description", + "encryption_key_id", + "fail_reason", + "has_dependent_backups", + "id", + "is_incremental", + "metadata", + "name", + "object_count", + "project_id", + "size", + "snapshot_id", + "status", + "updated_at", + "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.volume_sdk_client.get_backup.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.volume_sdk_client.get_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 a4e6ac85a..0ed0e7622 100644 --- a/openstackclient/volume/v2/volume_backup.py +++ b/openstackclient/volume/v2/volume_backup.py @@ -14,14 +14,10 @@ """Volume v2 Backup action implementations""" -import copy 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 from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -104,26 +100,6 @@ def get_parser(self, prog_name): action='store_false', help=_("Do not perform an incremental backup"), ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - dest='properties', - help=_( - 'Set a property on this backup ' - '(repeat option to remove multiple values) ' - '(supported by --os-volume-api-version 3.43 or above)' - ), - ) - parser.add_argument( - '--availability-zone', - metavar='', - help=_( - 'AZ where the backup should be stored; by default it will be ' - 'the same as the source ' - '(supported by --os-volume-api-version 3.51 or above)' - ), - ) return parser def take_action(self, parsed_args): @@ -142,26 +118,6 @@ def take_action(self, parsed_args): ignore_missing=False, ).id - if parsed_args.properties: - 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) - - kwargs['metadata'] = parsed_args.properties - - if parsed_args.availability_zone: - if not sdk_utils.supports_microversion(volume_client, '3.51'): - msg = _( - '--os-volume-api-version 3.51 or greater is required to ' - 'support the --availability-zone option' - ) - raise exceptions.CommandError(msg) - - kwargs['availability_zone'] = parsed_args.availability_zone - columns = ( "id", "name", @@ -279,20 +235,6 @@ def get_parser(self, prog_name): default=False, help=_('Include all projects (admin only)'), ) - # TODO(stephenfin): Add once we have an equivalent command for - # 'cinder list-filters' - # parser.add_argument( - # '--filter', - # metavar='', - # action=parseractions.KeyValueAction, - # dest='filters', - # help=_( - # "Filter key and value pairs. Use 'foo' to " - # "check enabled filters from server. Use 'key~=value' for " - # "inexact filtering if the key supports " - # "(supported by --os-volume-api-version 3.33 or above)" - # ), - # ) return parser def take_action(self, parsed_args): @@ -451,22 +393,6 @@ def get_parser(self, prog_name): metavar="", help=_("Backup to modify (name or ID)"), ) - parser.add_argument( - '--name', - metavar='', - help=_( - 'New backup name' - '(supported by --os-volume-api-version 3.9 or above)' - ), - ) - parser.add_argument( - '--description', - metavar='', - help=_( - 'New backup description ' - '(supported by --os-volume-api-version 3.9 or above)' - ), - ) parser.add_argument( '--state', metavar='', @@ -478,27 +404,6 @@ def get_parser(self, prog_name): 'exercise caution when using)' ), ) - parser.add_argument( - '--no-property', - action='store_true', - help=_( - 'Remove all properties from this backup ' - '(specify both --no-property and --property to remove the ' - 'current properties before setting new properties)' - ), - ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - dest='properties', - default={}, - help=_( - 'Set a property on this backup ' - '(repeat option to set multiple values) ' - '(supported by --os-volume-api-version 3.43 or above)' - ), - ) return parser def take_action(self, parsed_args): @@ -513,122 +418,11 @@ def take_action(self, parsed_args): LOG.error(_("Failed to set backup state: %s"), e) result += 1 - kwargs = {} - - if parsed_args.name: - if volume_client.api_version < api_versions.APIVersion('3.9'): - msg = _( - '--os-volume-api-version 3.9 or greater is required to ' - 'support the --name option' - ) - raise exceptions.CommandError(msg) - - kwargs['name'] = parsed_args.name - - if parsed_args.description: - if volume_client.api_version < api_versions.APIVersion('3.9'): - msg = _( - '--os-volume-api-version 3.9 or greater is required to ' - 'support the --description option' - ) - raise exceptions.CommandError(msg) - - kwargs['description'] = parsed_args.description - - if parsed_args.no_property: - if volume_client.api_version < api_versions.APIVersion('3.43'): - msg = _( - '--os-volume-api-version 3.43 or greater is required to ' - 'support the --no-property option' - ) - raise exceptions.CommandError(msg) - - if parsed_args.properties: - if volume_client.api_version < api_versions.APIVersion('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'): - metadata = copy.deepcopy(backup.metadata) - - if parsed_args.no_property: - metadata = {} - - metadata.update(parsed_args.properties) - kwargs['metadata'] = metadata - - if kwargs: - try: - volume_client.backups.update(backup.id, **kwargs) - except Exception as e: - LOG.error("Failed to update backup: %s", e) - result += 1 - if result > 0: msg = _("One or more of the set operations failed") raise exceptions.CommandError(msg) -class UnsetVolumeBackup(command.Command): - """Unset volume backup properties. - - This command requires ``--os-volume-api-version`` 3.43 or greater. - """ - - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'backup', - metavar='', - help=_('Backup to modify (name or ID)'), - ) - parser.add_argument( - '--property', - metavar='', - action='append', - dest='properties', - help=_( - 'Property to remove from this backup ' - '(repeat option to unset multiple values) ' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - - if volume_client.api_version < api_versions.APIVersion('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) - metadata = copy.deepcopy(backup.metadata) - - for key in parsed_args.properties: - if key not in metadata: - # ignore invalid properties but continue - LOG.warning( - "'%s' is not a valid property for backup '%s'", - key, - parsed_args.backup, - ) - continue - - del metadata[key] - - kwargs = { - 'metadata': metadata, - } - - volume_client.backups.update(backup.id, **kwargs) - - class ShowVolumeBackup(command.ShowOne): _description = _("Display volume backup details") @@ -650,20 +444,16 @@ def take_action(self, parsed_args): "created_at", "data_timestamp", "description", - "encryption_key_id", "fail_reason", "has_dependent_backups", "id", "is_incremental", - "metadata", "name", "object_count", - "project_id", "size", "snapshot_id", "status", "updated_at", - "user_id", "volume_id", ) data = utils.get_dict_properties(backup, columns) diff --git a/openstackclient/volume/v3/volume_backup.py b/openstackclient/volume/v3/volume_backup.py new file mode 100644 index 000000000..7052f608a --- /dev/null +++ b/openstackclient/volume/v3/volume_backup.py @@ -0,0 +1,670 @@ +# +# 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 Backup action implementations""" + +import copy +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 +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 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 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( + "--name", metavar="", help=_("Name of the backup") + ) + parser.add_argument( + "--description", + metavar="", + help=_("Description of the backup"), + ) + parser.add_argument( + "--container", + metavar="", + help=_("Optional backup container name"), + ) + parser.add_argument( + "--snapshot", + metavar="", + help=_("Snapshot to backup (name or ID)"), + ) + parser.add_argument( + '--force', + action='store_true', + default=False, + help=_("Allow to back up an in-use volume"), + ) + parser.add_argument( + '--incremental', + action='store_true', + default=False, + help=_("Perform an incremental backup"), + ) + parser.add_argument( + '--no-incremental', + action='store_false', + help=_("Do not perform an incremental backup"), + ) + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + dest='properties', + help=_( + 'Set a property on this backup ' + '(repeat option to remove multiple values) ' + '(supported by --os-volume-api-version 3.43 or above)' + ), + ) + parser.add_argument( + '--availability-zone', + metavar='', + help=_( + 'AZ where the backup should be stored; by default it will be ' + 'the same as the source ' + '(supported by --os-volume-api-version 3.51 or above)' + ), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.sdk_connection.volume + + volume_id = volume_client.find_volume( + parsed_args.volume, + ignore_missing=False, + ).id + + kwargs = {} + + if parsed_args.snapshot: + kwargs['snapshot_id'] = volume_client.find_snapshot( + parsed_args.snapshot, + ignore_missing=False, + ).id + + if parsed_args.properties: + 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) + + kwargs['metadata'] = parsed_args.properties + + if parsed_args.availability_zone: + if not sdk_utils.supports_microversion(volume_client, '3.51'): + msg = _( + '--os-volume-api-version 3.51 or greater is required to ' + 'support the --availability-zone option' + ) + raise exceptions.CommandError(msg) + + kwargs['availability_zone'] = parsed_args.availability_zone + + columns = ( + "id", + "name", + "volume_id", + ) + backup = volume_client.create_backup( + volume_id=volume_id, + container=parsed_args.container, + name=parsed_args.name, + description=parsed_args.description, + force=parsed_args.force, + is_incremental=parsed_args.incremental, + **kwargs, + ) + data = utils.get_dict_properties(backup, columns) + return (columns, data) + + +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)"), + ) + parser.add_argument( + '--force', + action='store_true', + default=False, + help=_("Allow delete in state other than error or available"), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.sdk_connection.volume + result = 0 + + for backup in parsed_args.backups: + try: + backup_id = volume_client.find_backup( + backup, ignore_missing=False + ).id + volume_client.delete_backup( + backup_id, + ignore_missing=False, + force=parsed_args.force, + ) + except Exception as e: + result += 1 + LOG.error( + _( + "Failed to delete backup with " + "name or ID '%(backup)s': %(e)s" + ) + % {'backup': backup, '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, one of: " + "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)" + ), + ) + pagination.add_marker_pagination_option_to_parser(parser) + parser.add_argument( + '--all-projects', + action='store_true', + default=False, + help=_('Include all projects (admin only)'), + ) + # TODO(stephenfin): Add once we have an equivalent command for + # 'cinder list-filters' + # parser.add_argument( + # '--filter', + # metavar='', + # action=parseractions.KeyValueAction, + # dest='filters', + # help=_( + # "Filter key and value pairs. Use 'foo' to " + # "check enabled filters from server. Use 'key~=value' for " + # "inexact filtering if the key supports " + # "(supported by --os-volume-api-version 3.33 or above)" + # ), + # ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.sdk_connection.volume + + columns = ( + 'id', + 'name', + 'description', + 'status', + 'size', + 'is_incremental', + ) + column_headers = ( + 'ID', + 'Name', + 'Description', + 'Status', + 'Size', + 'Incremental', + ) + if parsed_args.long: + columns += ('availability_zone', 'volume_id', 'container') + column_headers += ('Availability Zone', 'Volume', 'Container') + + # Cache the volume list + volume_cache = {} + try: + for s in volume_client.volumes(): + volume_cache[s.id] = s + except Exception: + # Just forget it if there's any trouble + pass # nosec: B110 + + _VolumeIdColumn = functools.partial( + VolumeIdColumn, volume_cache=volume_cache + ) + + filter_volume_id = None + if parsed_args.volume: + try: + filter_volume_id = volume_client.find_volume( + parsed_args.volume, + ignore_missing=False, + ).id + except exceptions.CommandError: + # Volume with that ID does not exist, but search for backups + # for that volume nevertheless + LOG.debug( + "No volume with ID %s existing, continuing to " + "search for backups for that volume ID", + parsed_args.volume, + ) + filter_volume_id = parsed_args.volume + + marker_backup_id = None + if parsed_args.marker: + marker_backup_id = volume_client.find_backup( + parsed_args.marker, + ignore_missing=False, + ).id + + data = volume_client.backups( + name=parsed_args.name, + status=parsed_args.status, + volume_id=filter_volume_id, + all_tenants=parsed_args.all_projects, + marker=marker_backup_id, + limit=parsed_args.limit, + ) + + return ( + column_headers, + ( + utils.get_item_properties( + s, + columns, + formatters={'volume_id': _VolumeIdColumn}, + ) + for s in data + ), + ) + + +class RestoreVolumeBackup(command.ShowOne): + _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 for existing volume, name only for new volume) " + "(default to None)" + ), + ) + parser.add_argument( + "--force", + action="store_true", + help=_( + "Restore the backup to an existing volume " + "(default to False)" + ), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.sdk_connection.volume + + backup = volume_client.find_backup( + parsed_args.backup, + ignore_missing=False, + ) + + volume_name = None + volume_id = None + try: + volume_id = volume_client.find_volume( + parsed_args.volume, + ignore_missing=False, + ).id + except Exception: + volume_name = parsed_args.volume + else: + # If we didn't fail, the volume must already exist. We only allow + # this to work if the user forced things + if not parsed_args.force: + msg = _( + "Volume '%s' already exists; if you want to restore the " + "backup to it you need to specify the '--force' option" + ) + raise exceptions.CommandError(msg % parsed_args.volume) + + return volume_client.restore_backup( + backup.id, + volume_id=volume_id, + name=volume_name, + ) + + +class SetVolumeBackup(command.Command): + _description = _("Set volume backup properties") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + "backup", + metavar="", + help=_("Backup to modify (name or ID)"), + ) + parser.add_argument( + '--name', + metavar='', + help=_( + 'New backup name' + '(supported by --os-volume-api-version 3.9 or above)' + ), + ) + parser.add_argument( + '--description', + metavar='', + help=_( + 'New backup description ' + '(supported by --os-volume-api-version 3.9 or above)' + ), + ) + parser.add_argument( + '--state', + metavar='', + choices=['available', 'error'], + help=_( + '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)' + ), + ) + parser.add_argument( + '--no-property', + action='store_true', + help=_( + 'Remove all properties from this backup ' + '(specify both --no-property and --property to remove the ' + 'current properties before setting new properties)' + ), + ) + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + dest='properties', + default={}, + help=_( + 'Set a property on this backup ' + '(repeat option to set multiple values) ' + '(supported by --os-volume-api-version 3.43 or above)' + ), + ) + 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) + + result = 0 + if parsed_args.state: + try: + volume_client.backups.reset_state(backup.id, parsed_args.state) + except Exception as e: + LOG.error(_("Failed to set backup state: %s"), e) + result += 1 + + kwargs = {} + + if parsed_args.name: + if volume_client.api_version < api_versions.APIVersion('3.9'): + msg = _( + '--os-volume-api-version 3.9 or greater is required to ' + 'support the --name option' + ) + raise exceptions.CommandError(msg) + + kwargs['name'] = parsed_args.name + + if parsed_args.description: + if volume_client.api_version < api_versions.APIVersion('3.9'): + msg = _( + '--os-volume-api-version 3.9 or greater is required to ' + 'support the --description option' + ) + raise exceptions.CommandError(msg) + + kwargs['description'] = parsed_args.description + + if parsed_args.no_property: + if volume_client.api_version < api_versions.APIVersion('3.43'): + msg = _( + '--os-volume-api-version 3.43 or greater is required to ' + 'support the --no-property option' + ) + raise exceptions.CommandError(msg) + + if parsed_args.properties: + if volume_client.api_version < api_versions.APIVersion('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'): + metadata = copy.deepcopy(backup.metadata) + + if parsed_args.no_property: + metadata = {} + + metadata.update(parsed_args.properties) + kwargs['metadata'] = metadata + + if kwargs: + try: + volume_client.backups.update(backup.id, **kwargs) + except Exception as e: + LOG.error("Failed to update backup: %s", e) + result += 1 + + if result > 0: + msg = _("One or more of the set operations failed") + raise exceptions.CommandError(msg) + + +class UnsetVolumeBackup(command.Command): + """Unset volume backup properties. + + This command requires ``--os-volume-api-version`` 3.43 or greater. + """ + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'backup', + metavar='', + help=_('Backup to modify (name or ID)'), + ) + parser.add_argument( + '--property', + metavar='', + action='append', + dest='properties', + help=_( + 'Property to remove from this backup ' + '(repeat option to unset multiple values) ' + ), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + + if volume_client.api_version < api_versions.APIVersion('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) + metadata = copy.deepcopy(backup.metadata) + + for key in parsed_args.properties: + if key not in metadata: + # ignore invalid properties but continue + LOG.warning( + "'%s' is not a valid property for backup '%s'", + key, + parsed_args.backup, + ) + continue + + del metadata[key] + + kwargs = { + 'metadata': metadata, + } + + volume_client.backups.update(backup.id, **kwargs) + + +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.sdk_connection.volume + backup = volume_client.get_backup(parsed_args.backup) + columns = ( + "availability_zone", + "container", + "created_at", + "data_timestamp", + "description", + "encryption_key_id", + "fail_reason", + "has_dependent_backups", + "id", + "is_incremental", + "metadata", + "name", + "object_count", + "project_id", + "size", + "snapshot_id", + "status", + "updated_at", + "user_id", + "volume_id", + ) + data = utils.get_dict_properties(backup, columns) + return (columns, data) diff --git a/setup.cfg b/setup.cfg index 89edcd9b2..1ee4dd465 100644 --- a/setup.cfg +++ b/setup.cfg @@ -780,13 +780,13 @@ openstack.volume.v3 = volume_attachment_set = openstackclient.volume.v3.volume_attachment:SetVolumeAttachment volume_attachment_show = openstackclient.volume.v3.volume_attachment:ShowVolumeAttachment - 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_unset = openstackclient.volume.v2.volume_backup:UnsetVolumeBackup - volume_backup_show = openstackclient.volume.v2.volume_backup:ShowVolumeBackup + 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 From 65cce3943a26b0275266a1d619cd69a349f3d8e4 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 12 Jun 2024 16:02:09 +0100 Subject: [PATCH 135/403] volume: Add v3-specific volume module This makes testing easier. Change-Id: I6b31026ae3c9dc66d828744534b35bb0a0d2ffbe Signed-off-by: Stephen Finucane --- openstackclient/tests/unit/volume/v2/fakes.py | 38 - .../tests/unit/volume/v2/test_volume.py | 99 +- openstackclient/tests/unit/volume/v3/fakes.py | 140 +- .../tests/unit/volume/v3/test_volume.py | 2467 ++++++++++++++--- openstackclient/volume/v2/volume.py | 51 +- openstackclient/volume/v3/volume.py | 948 ++++++- setup.cfg | 10 +- 7 files changed, 3189 insertions(+), 564 deletions(-) diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index d39ec5a3c..1c3a6864e 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -25,7 +25,6 @@ from openstack.block_storage.v3 import stats as _stats from openstack.block_storage.v3 import volume as _volume from openstack.image.v2 import _proxy as image_v2_proxy -from osc_lib.cli import format_columns from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes @@ -472,43 +471,6 @@ def get_volumes(volumes=None, count=2): return mock.Mock(side_effect=volumes) -def get_volume_columns(volume=None): - """Get the volume columns from a faked volume object. - - :param volume: - A FakeResource objects faking volume - :return - A tuple which may include the following keys: - ('id', 'name', 'description', 'status', 'size', 'volume_type', - 'metadata', 'snapshot', 'availability_zone', 'attachments') - """ - if volume is not None: - return tuple(k for k in sorted(volume.keys())) - return tuple([]) - - -def get_volume_data(volume=None): - """Get the volume data from a faked volume object. - - :param volume: - A FakeResource objects faking volume - :return - A tuple which may include the following values: - ('ce26708d', 'fake_volume', 'fake description', 'available', - 20, 'fake_lvmdriver-1', "Alpha='a', Beta='b', Gamma='g'", - 1, 'nova', [{'device': '/dev/ice', 'server_id': '1233'}]) - """ - data_list = [] - if volume is not None: - for x in sorted(volume.keys()): - if x == 'tags': - # The 'tags' should be format_list - data_list.append(format_columns.ListColumn(volume.info.get(x))) - else: - data_list.append(volume.info.get(x)) - return tuple(data_list) - - def create_one_backup(attrs=None): """Create a fake backup. diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index 0176994b6..1667f38cd 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -12,9 +12,7 @@ # under the License. from unittest import mock -from unittest.mock import call -from cinderclient import api_versions from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib import utils @@ -42,9 +40,6 @@ def setUp(self): 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.types_mock = self.volume_client.volume_types self.types_mock.reset_mock() @@ -126,7 +121,6 @@ def test_volume_create_min_options(self): source_volid=None, consistencygroup_id=None, scheduler_hints=None, - backup_id=None, ) self.assertEqual(self.columns, columns) @@ -178,7 +172,6 @@ def test_volume_create_options(self): source_volid=None, consistencygroup_id=consistency_group.id, scheduler_hints={'k': 'v'}, - backup_id=None, ) self.assertEqual(self.columns, columns) @@ -218,7 +211,6 @@ def test_volume_create_properties(self): source_volid=None, consistencygroup_id=None, scheduler_hints=None, - backup_id=None, ) self.assertEqual(self.columns, columns) @@ -259,7 +251,6 @@ def test_volume_create_image_id(self): source_volid=None, consistencygroup_id=None, scheduler_hints=None, - backup_id=None, ) self.assertEqual(self.columns, columns) @@ -300,7 +291,6 @@ def test_volume_create_image_name(self): source_volid=None, consistencygroup_id=None, scheduler_hints=None, - backup_id=None, ) self.assertEqual(self.columns, columns) @@ -339,74 +329,11 @@ def test_volume_create_with_snapshot(self): source_volid=None, consistencygroup_id=None, scheduler_hints=None, - backup_id=None, - ) - - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.datalist, data) - - def test_volume_create_with_backup(self): - backup = volume_fakes.create_one_backup() - self.new_volume.backup_id = backup.id - arglist = [ - '--backup', - self.new_volume.backup_id, - self.new_volume.name, - ] - verifylist = [ - ('backup', self.new_volume.backup_id), - ('name', self.new_volume.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.backups_mock.get.return_value = backup - - self.volume_client.api_version = api_versions.APIVersion('3.47') - - # 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=backup.size, - snapshot_id=None, - name=self.new_volume.name, - description=None, - volume_type=None, - availability_zone=None, - metadata=None, - imageRef=None, - source_volid=None, - consistencygroup_id=None, - scheduler_hints=None, - backup_id=backup.id, ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) - def test_volume_create_with_backup_pre_347(self): - backup = volume_fakes.create_one_backup() - self.new_volume.backup_id = backup.id - arglist = [ - '--backup', - self.new_volume.backup_id, - self.new_volume.name, - ] - verifylist = [ - ('backup', self.new_volume.backup_id), - ('name', self.new_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)) - def test_volume_create_with_source_volume(self): source_vol = "source_vol" arglist = [ @@ -439,7 +366,6 @@ def test_volume_create_with_source_volume(self): source_volid=self.new_volume.id, consistencygroup_id=None, scheduler_hints=None, - backup_id=None, ) self.assertEqual(self.columns, columns) @@ -479,7 +405,6 @@ def test_volume_create_with_bootable_and_readonly(self, mock_wait): source_volid=None, consistencygroup_id=None, scheduler_hints=None, - backup_id=None, ) self.assertEqual(self.columns, columns) @@ -525,7 +450,6 @@ def test_volume_create_with_nonbootable_and_readwrite(self, mock_wait): source_volid=None, consistencygroup_id=None, scheduler_hints=None, - backup_id=None, ) self.assertEqual(self.columns, columns) @@ -580,7 +504,6 @@ def test_volume_create_with_bootable_and_readonly_fail( source_volid=None, consistencygroup_id=None, scheduler_hints=None, - backup_id=None, ) self.assertEqual(2, mock_error.call_count) @@ -632,7 +555,6 @@ def test_volume_create_non_available_with_readonly( source_volid=None, consistencygroup_id=None, scheduler_hints=None, - backup_id=None, ) self.assertEqual(2, mock_error.call_count) @@ -742,7 +664,6 @@ def test_volume_create_hints(self): 'local_to_instance': 'v6', 'different_host': ['v5', 'v7'], }, - backup_id=None, ) self.assertEqual(self.columns, columns) @@ -789,7 +710,7 @@ def test_volume_delete_multi_volumes(self): result = self.cmd.take_action(parsed_args) - calls = [call(v.id, cascade=False) for v in volumes] + calls = [mock.call(v.id, cascade=False) for v in volumes] self.volumes_mock.delete.assert_has_calls(calls) self.assertIsNone(result) @@ -1721,11 +1642,23 @@ def test_volume_show(self): self.volumes_mock.get.assert_called_with(self._volume.id) self.assertEqual( - volume_fakes.get_volume_columns(self._volume), + tuple(sorted(self._volume.keys())), columns, ) - self.assertCountEqual( - volume_fakes.get_volume_data(self._volume), + 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, ) diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index 54ac747ec..b8ebe516a 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +import copy import random from unittest import mock import uuid @@ -21,6 +22,7 @@ from openstack.block_storage.v3 import extension as _extension from openstack.block_storage.v3 import resource_filter as _filters from openstack.block_storage.v3 import volume as _volume +from openstack.image.v2 import _proxy as _image_proxy from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes @@ -34,12 +36,14 @@ def __init__(self, **kwargs): self.management_url = kwargs['endpoint'] self.api_version = api_versions.APIVersion('3.0') + self.attachments = mock.Mock() + self.attachments.resource_class = fakes.FakeResource(None, {}) self.availability_zones = mock.Mock() self.availability_zones.resource_class = fakes.FakeResource(None, {}) self.backups = mock.Mock() self.backups.resource_class = fakes.FakeResource(None, {}) - self.attachments = mock.Mock() - self.attachments.resource_class = fakes.FakeResource(None, {}) + self.consistencygroups = mock.Mock() + self.consistencygroups.resource_class = fakes.FakeResource(None, {}) self.clusters = mock.Mock() self.clusters.resource_class = fakes.FakeResource(None, {}) self.groups = mock.Mock() @@ -106,10 +110,14 @@ def setUp(self): ) 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 + self.app.client_manager.image = mock.Mock(spec=_image_proxy.Proxy) + self.image_client = self.app.client_manager.image + # TODO(stephenfin): Check if the responses are actually the same create_one_snapshot = volume_v2_fakes.create_one_snapshot -create_one_volume = volume_v2_fakes.create_one_volume create_one_volume_type = volume_v2_fakes.create_one_volume_type @@ -153,6 +161,54 @@ def create_availability_zones(attrs=None, count=2): return availability_zones +def create_one_consistency_group(attrs=None): + """Create a fake consistency group. + + :param dict attrs: + A dictionary with all attributes + :return: + A FakeResource object with id, name, description, etc. + """ + attrs = attrs or {} + + # Set default attributes. + consistency_group_info = { + "id": 'backup-id-' + uuid.uuid4().hex, + "name": 'backup-name-' + uuid.uuid4().hex, + "description": 'description-' + uuid.uuid4().hex, + "status": "error", + "availability_zone": 'zone' + uuid.uuid4().hex, + "created_at": 'time-' + uuid.uuid4().hex, + "volume_types": ['volume-type1'], + } + + # Overwrite default attributes. + consistency_group_info.update(attrs) + + consistency_group = fakes.FakeResource( + info=copy.deepcopy(consistency_group_info), loaded=True + ) + return consistency_group + + +def create_consistency_groups(attrs=None, count=2): + """Create multiple fake consistency groups. + + :param dict attrs: + A dictionary with all attributes + :param int count: + The number of consistency groups to fake + :return: + A list of FakeResource objects faking the consistency groups + """ + consistency_groups = [] + for i in range(0, count): + consistency_group = create_one_consistency_group(attrs) + consistency_groups.append(consistency_group) + + return consistency_groups + + def create_one_extension(attrs=None): """Create a fake extension. @@ -349,6 +405,84 @@ def create_resource_filters(attrs=None, count=2): return resource_filters +def create_one_volume(attrs=None): + """Create a fake volume. + + :param dict 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, + 'name': 'volume-name' + uuid.uuid4().hex, + 'description': 'description' + uuid.uuid4().hex, + 'status': random.choice(['available', 'in_use']), + 'size': random.randint(1, 20), + 'volume_type': random.choice(['fake_lvmdriver-1', 'fake_lvmdriver-2']), + 'bootable': random.randint(0, 1), + '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': random.randint(1, 5), + 'availability_zone': 'zone' + uuid.uuid4().hex, + 'attachments': [ + { + 'device': '/dev/' + uuid.uuid4().hex, + 'server_id': 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 dict 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_sdk_volume(attrs=None): """Create a fake volume. diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index 3aaaae00c..5b01ac324 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -23,12 +23,16 @@ from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib import exceptions +from osc_lib import utils -from openstackclient.tests.unit.volume.v3 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 import utils as test_utils +from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes from openstackclient.volume.v3 import volume -class BaseVolumeTest(fakes.TestVolume): +class BaseVolumeTest(volume_fakes.TestVolume): def setUp(self): super().setUp() @@ -50,481 +54,2316 @@ def _set_mock_microversion(self, mock_v): ) -class TestVolumeSummary(BaseVolumeTest): - columns = [ - 'Total Count', - 'Total Size', - ] +# 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() + + columns = ( + 'attachments', + 'availability_zone', + 'bootable', + 'description', + 'id', + 'name', + 'properties', + 'size', + 'snapshot_id', + 'status', + 'type', + ) def setUp(self): super().setUp() - self.volume_a = sdk_fakes.generate_fake_resource(_volume.Volume) - self.volume_b = sdk_fakes.generate_fake_resource(_volume.Volume) - self.summary = sdk_fakes.generate_fake_resource( - _summary.BlockStorageSummary, - total_count=2, - total_size=self.volume_a.size + self.volume_b.size, + 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.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_sdk_client.summary.return_value = self.summary # Get the command object to test - self.cmd = volume.VolumeSummary(self.app, None) + self.cmd = volume.CreateVolume(self.app, None) - def test_volume_summary(self): - self._set_mock_microversion('3.12') + def test_volume_create_min_options(self): arglist = [ - '--all-projects', + '--size', + str(self.new_volume.size), ] verifylist = [ - ('all_projects', True), + ('size', self.new_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.volume_sdk_client.summary.assert_called_once_with(True) + self.volumes_mock.create.assert_called_with( + size=self.new_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, + scheduler_hints=None, + backup_id=None, + ) self.assertEqual(self.columns, columns) + self.assertCountEqual(self.datalist, data) - datalist = (2, self.volume_a.size + self.volume_b.size) - self.assertCountEqual(datalist, tuple(data)) - - def test_volume_summary_pre_312(self): + def test_volume_create_options(self): + consistency_group = volume_fakes.create_one_consistency_group() + self.consistencygroups_mock.get.return_value = consistency_group arglist = [ - '--all-projects', + '--size', + str(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, + '--hint', + 'k=v', + self.new_volume.name, ] verifylist = [ - ('all_projects', True), + ('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), + ('hint', {'k': 'v'}), + ('name', self.new_volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - self.assertIn( - '--os-volume-api-version 3.12 or greater is required', str(exc) - ) - - def test_volume_summary_with_metadata(self): - self._set_mock_microversion('3.36') + # 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) - metadata = {**self.volume_a.metadata, **self.volume_b.metadata} - self.summary = sdk_fakes.generate_fake_resource( - _summary.BlockStorageSummary, - total_count=2, - total_size=self.volume_a.size + self.volume_b.size, - metadata=metadata, + self.volumes_mock.create.assert_called_with( + size=self.new_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, + metadata=None, + imageRef=None, + source_volid=None, + consistencygroup_id=consistency_group.id, + scheduler_hints={'k': 'v'}, + backup_id=None, ) - self.volume_sdk_client.summary.return_value = self.summary - new_cols = copy.deepcopy(self.columns) - new_cols.extend(['Metadata']) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.datalist, data) + def test_volume_create_properties(self): arglist = [ - '--all-projects', + '--property', + 'Alpha=a', + '--property', + 'Beta=b', + '--size', + str(self.new_volume.size), + self.new_volume.name, ] verifylist = [ - ('all_projects', True), + ('property', {'Alpha': 'a', 'Beta': 'b'}), + ('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.volume_sdk_client.summary.assert_called_once_with(True) - - self.assertEqual(new_cols, columns) - - datalist = ( - 2, - self.volume_a.size + self.volume_b.size, - format_columns.DictColumn(metadata), + 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={'Alpha': 'a', 'Beta': 'b'}, + imageRef=None, + source_volid=None, + consistencygroup_id=None, + scheduler_hints=None, + backup_id=None, ) - self.assertCountEqual(datalist, tuple(data)) - - -class TestVolumeRevertToSnapshot(BaseVolumeTest): - def setUp(self): - super().setUp() - self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) - self.snapshot = sdk_fakes.generate_fake_resource( - _snapshot.Snapshot, - volume_id=self.volume.id, - ) - self.volume_sdk_client.find_volume.return_value = self.volume - self.volume_sdk_client.find_snapshot.return_value = self.snapshot + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.datalist, data) - # Get the command object to test - self.cmd = volume.VolumeRevertToSnapshot(self.app, None) + def test_volume_create_image_id(self): + image = image_fakes.create_one_image() + self.image_client.find_image.return_value = image - def test_volume_revert_to_snapshot_pre_340(self): arglist = [ - self.snapshot.id, + '--image', + image.id, + '--size', + str(self.new_volume.size), + self.new_volume.name, ] verifylist = [ - ('snapshot', self.snapshot.id), + ('image', image.id), + ('size', self.new_volume.size), + ('name', self.new_volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - self.assertIn( - '--os-volume-api-version 3.40 or greater is required', str(exc) + # 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, + backup_id=None, ) - def test_volume_revert_to_snapshot(self): - self._set_mock_microversion('3.40') + 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 = [ - self.snapshot.id, + '--image', + image.name, + '--size', + str(self.new_volume.size), + self.new_volume.name, ] verifylist = [ - ('snapshot', self.snapshot.id), + ('image', image.name), + ('size', self.new_volume.size), + ('name', self.new_volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + # 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.volume_sdk_client.revert_volume_to_snapshot.assert_called_once_with( - self.volume, - self.snapshot, - ) - self.volume_sdk_client.find_volume.assert_called_with( - self.volume.id, - ignore_missing=False, - ) - self.volume_sdk_client.find_snapshot.assert_called_with( - self.snapshot.id, - ignore_missing=False, + 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, + backup_id=None, ) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.datalist, data) -class TestVolumeCreate(BaseVolumeTest): - 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 test_volume_create_with_snapshot(self): + snapshot = volume_fakes.create_one_snapshot() + self.new_volume.snapshot_id = snapshot.id + arglist = [ + '--snapshot', + self.new_volume.snapshot_id, + self.new_volume.name, + ] + verifylist = [ + ('snapshot', self.new_volume.snapshot_id), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) - def setUp(self): - super().setUp() + self.snapshots_mock.get.return_value = snapshot - self.new_volume = sdk_fakes.generate_fake_resource( - _volume.Volume, **{'size': 1} - ) + # 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.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, + self.volumes_mock.create.assert_called_once_with( + size=snapshot.size, + snapshot_id=snapshot.id, + name=self.new_volume.name, + description=None, + volume_type=None, + availability_zone=None, + metadata=None, + imageRef=None, + source_volid=None, + consistencygroup_id=None, + scheduler_hints=None, + backup_id=None, ) - # Get the command object to test - self.cmd = volume.CreateVolume(self.app, None) - - def test_volume_create_remote_source(self): - self.volume_sdk_client.manage_volume.return_value = self.new_volume + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.datalist, data) + def test_volume_create_with_backup(self): + backup = volume_fakes.create_one_backup() + self.new_volume.backup_id = backup.id arglist = [ - '--remote-source', - 'key=val', - '--host', - 'fake_host', + '--backup', + self.new_volume.backup_id, self.new_volume.name, ] verifylist = [ - ('remote_source', {'key': 'val'}), - ('host', 'fake_host'), + ('backup', self.new_volume.backup_id), ('name', self.new_volume.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.backups_mock.get.return_value = backup + + self.volume_client.api_version = api_versions.APIVersion('3.47') + + # 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.volume_sdk_client.manage_volume.assert_called_with( - host='fake_host', - ref={'key': 'val'}, - name=parsed_args.name, - description=parsed_args.description, - volume_type=parsed_args.type, - availability_zone=parsed_args.availability_zone, - metadata=parsed_args.property, - bootable=parsed_args.bootable, - cluster=getattr(parsed_args, 'cluster', None), + self.volumes_mock.create.assert_called_once_with( + size=backup.size, + snapshot_id=None, + name=self.new_volume.name, + description=None, + volume_type=None, + availability_zone=None, + metadata=None, + imageRef=None, + source_volid=None, + consistencygroup_id=None, + scheduler_hints=None, + backup_id=backup.id, ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.datalist, data) - def test_volume_create_remote_source_pre_316(self): - self._set_mock_microversion('3.15') + def test_volume_create_with_backup_pre_v347(self): + backup = volume_fakes.create_one_backup() + self.new_volume.backup_id = backup.id arglist = [ - '--remote-source', - 'key=val', - '--cluster', - 'fake_cluster', + '--backup', + self.new_volume.backup_id, self.new_volume.name, ] verifylist = [ - ('remote_source', {'key': 'val'}), - ('cluster', 'fake_cluster'), + ('backup', self.new_volume.backup_id), ('name', self.new_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.16 or greater is required', str(exc) - ) + self.assertIn("--os-volume-api-version 3.47 or greater", str(exc)) - def test_volume_create_remote_source_host_and_cluster(self): - self._set_mock_microversion('3.16') + def test_volume_create_with_source_volume(self): + source_vol = "source_vol" arglist = [ - '--remote-source', - 'key=val', - '--host', - 'fake_host', - '--cluster', - 'fake_cluster', - self.new_volume.name, + '--source', + self.new_volume.id, + source_vol, ] verifylist = [ - ('remote_source', {'key': 'val'}), - ('host', 'fake_host'), - ('cluster', 'fake_cluster'), - ('name', self.new_volume.name), + ('source', self.new_volume.id), + ('name', source_vol), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - self.assertIn( - 'Only one of --host or --cluster needs to be specified', str(exc) + 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, + snapshot_id=None, + name=source_vol, + description=None, + volume_type=None, + availability_zone=None, + metadata=None, + imageRef=None, + source_volid=self.new_volume.id, + consistencygroup_id=None, + scheduler_hints=None, + backup_id=None, ) - def test_volume_create_remote_source_no_host_or_cluster(self): + 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 = [ - '--remote-source', - 'key=val', + '--bootable', + '--read-only', + '--size', + str(self.new_volume.size), self.new_volume.name, ] verifylist = [ - ('remote_source', {'key': 'val'}), + ('bootable', True), + ('non_bootable', False), + ('read_only', True), + ('read_write', False), + ('size', self.new_volume.size), ('name', self.new_volume.name), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args + 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=None, + source_volid=None, + consistencygroup_id=None, + scheduler_hints=None, + backup_id=None, ) - self.assertIn( - 'One of --host or --cluster needs to be specified to ', str(exc) + + 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 ) - def test_volume_create_remote_source_size(self): + @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), - '--remote-source', - 'key=val', self.new_volume.name, ] verifylist = [ + ('bootable', False), + ('non_bootable', True), + ('read_only', False), + ('read_write', True), ('size', self.new_volume.size), - ('remote_source', {'key': 'val'}), ('name', self.new_volume.name), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args + 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=None, + source_volid=None, + consistencygroup_id=None, + scheduler_hints=None, + backup_id=None, ) - self.assertIn( - '--size, --consistency-group, --hint, --read-only and ' - '--read-write options are not supported', - str(exc), + + 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() ) - def test_volume_create_host_no_remote_source(self): arglist = [ + '--bootable', + '--read-only', '--size', str(self.new_volume.size), - '--host', - 'fake_host', self.new_volume.name, ] verifylist = [ + ('bootable', True), + ('non_bootable', False), + ('read_only', True), + ('read_write', False), ('size', self.new_volume.size), - ('host', 'fake_host'), ('name', self.new_volume.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - self.assertIn( - '--host and --cluster options are only supported ', - str(exc), - ) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) -class TestVolumeDelete(BaseVolumeTest): - 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 + columns, data = self.cmd.take_action(parsed_args) - # Get the command object to mock - self.cmd = volume.DeleteVolume(self.app, None) + 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=None, + source_volid=None, + consistencygroup_id=None, + scheduler_hints=None, + backup_id=None, + ) - def test_volume_delete_remote(self): - vol = sdk_fakes.generate_fake_resource(_volume.Volume, **{'size': 1}) - self.volumes_mock.get.return_value = vol + 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 + ) - arglist = ['--remote', vol.id] + @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.name, + ] verifylist = [ - ("remote", True), - ("force", False), - ("purge", False), - ("volumes", [vol.id]), + ('bootable', False), + ('non_bootable', True), + ('read_only', True), + ('read_write', False), + ('size', self.new_volume.size), + ('name', self.new_volume.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.volume_sdk_client.unmanage_volume.assert_called_once_with(vol.id) - self.assertIsNone(result) + columns, data = self.cmd.take_action(parsed_args) - def test_volume_delete_multi_volumes_remote(self): - volumes = sdk_fakes.generate_fake_resources( - _volume.Volume, count=3, attrs={'size': 1} + 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=None, + source_volid=None, + consistencygroup_id=None, + scheduler_hints=None, + backup_id=None, ) - arglist = ['--remote'] - arglist += [v.id for v in volumes] + 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.name, + ] verifylist = [ - ('remote', True), - ('force', False), - ('purge', False), - ('volumes', arglist[1:]), + ('name', self.new_volume.name), ] 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.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + def test_volume_create_with_multi_source(self): arglist = [ - '--remote', - '--purge', - vol.id, + '--image', + 'source_image', + '--source', + 'source_volume', + '--snapshot', + 'source_snapshot', + '--size', + str(self.new_volume.size), + self.new_volume.name, ] verifylist = [ - ('remote', True), - ('force', False), - ('purge', True), - ('volumes', [vol.id]), + ('image', 'source_image'), + ('source', 'source_volume'), + ('snapshot', 'source_snapshot'), + ('size', self.new_volume.size), + ('name', self.new_volume.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - self.assertIn( - "The --force and --purge options are not supported with the " - "--remote parameter.", - str(exc), + self.assertRaises( + test_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist, ) - def test_volume_delete_remote_with_force(self): - vol = sdk_fakes.generate_fake_resource(_volume.Volume, **{'size': 1}) + def test_volume_create_hints(self): + """--hint needs to behave differently based on the given hint + different_host and same_host need to append to a list if given multiple + times. All other parameter are strings. + """ arglist = [ - '--remote', - '--force', - vol.id, + '--size', + str(self.new_volume.size), + '--hint', + 'k=v', + '--hint', + 'k=v2', + '--hint', + 'same_host=v3', + '--hint', + 'same_host=v4', + '--hint', + 'different_host=v5', + '--hint', + 'local_to_instance=v6', + '--hint', + 'different_host=v7', + self.new_volume.name, ] verifylist = [ - ('remote', True), - ('force', True), - ('purge', False), - ('volumes', [vol.id]), + ('size', self.new_volume.size), + ( + 'hint', + { + 'k': 'v2', + 'same_host': ['v3', 'v4'], + 'local_to_instance': 'v6', + 'different_host': ['v5', 'v7'], + }, + ), + ('name', self.new_volume.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args + + # 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=None, + source_volid=None, + consistencygroup_id=None, + scheduler_hints={ + 'k': 'v2', + 'same_host': ['v3', 'v4'], + 'local_to_instance': 'v6', + 'different_host': ['v5', 'v7'], + }, + backup_id=None, ) - self.assertIn( - "The --force and --purge options are not supported with the " - "--remote parameter.", - str(exc), + + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.datalist, data) + + +class TestVolumeCreate(BaseVolumeTest): + 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) + + def test_volume_create_remote_source(self): + self.volume_sdk_client.manage_volume.return_value = self.new_volume + + arglist = [ + '--remote-source', + 'key=val', + '--host', + 'fake_host', + self.new_volume.name, + ] + verifylist = [ + ('remote_source', {'key': 'val'}), + ('host', 'fake_host'), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.manage_volume.assert_called_with( + host='fake_host', + ref={'key': 'val'}, + name=parsed_args.name, + description=parsed_args.description, + volume_type=parsed_args.type, + availability_zone=parsed_args.availability_zone, + metadata=parsed_args.property, + bootable=parsed_args.bootable, + cluster=getattr(parsed_args, 'cluster', None), + ) + + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.datalist, data) + + def test_volume_create_remote_source_pre_v316(self): + self._set_mock_microversion('3.15') + arglist = [ + '--remote-source', + 'key=val', + '--cluster', + 'fake_cluster', + self.new_volume.name, + ] + verifylist = [ + ('remote_source', {'key': 'val'}), + ('cluster', 'fake_cluster'), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + '--os-volume-api-version 3.16 or greater is required', str(exc) + ) + + def test_volume_create_remote_source_host_and_cluster(self): + self._set_mock_microversion('3.16') + arglist = [ + '--remote-source', + 'key=val', + '--host', + 'fake_host', + '--cluster', + 'fake_cluster', + self.new_volume.name, + ] + verifylist = [ + ('remote_source', {'key': 'val'}), + ('host', 'fake_host'), + ('cluster', 'fake_cluster'), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + 'Only one of --host or --cluster needs to be specified', str(exc) + ) + + def test_volume_create_remote_source_no_host_or_cluster(self): + arglist = [ + '--remote-source', + 'key=val', + self.new_volume.name, + ] + verifylist = [ + ('remote_source', {'key': 'val'}), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + 'One of --host or --cluster needs to be specified to ', str(exc) + ) + + def test_volume_create_remote_source_size(self): + arglist = [ + '--size', + str(self.new_volume.size), + '--remote-source', + 'key=val', + self.new_volume.name, + ] + verifylist = [ + ('size', self.new_volume.size), + ('remote_source', {'key': 'val'}), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + '--size, --consistency-group, --hint, --read-only and ' + '--read-write options are not supported', + str(exc), + ) + + def test_volume_create_host_no_remote_source(self): + arglist = [ + '--size', + str(self.new_volume.size), + '--host', + 'fake_host', + self.new_volume.name, + ] + verifylist = [ + ('size', self.new_volume.size), + ('host', 'fake_host'), + ('name', self.new_volume.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + '--host and --cluster options are only supported ', + str(exc), + ) + + +class TestVolumeDeleteLegacy(BaseVolumeTest): + 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) + + # Get the command object to mock + self.cmd = volume.DeleteVolume(self.app, None) + + def test_volume_delete_one_volume(self): + arglist = [self.volumes[0].id] + verifylist = [ + ("force", False), + ("purge", False), + ("volumes", [self.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( + self.volumes[0].id, cascade=False + ) + self.assertIsNone(result) + + def test_volume_delete_multi_volumes(self): + arglist = [v.id for v in self.volumes] + verifylist = [ + ('force', False), + ('purge', False), + ('volumes', arglist), + ] + 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) + + def test_volume_delete_multi_volumes_with_exception(self): + arglist = [ + self.volumes[0].id, + 'unexist_volume', + ] + verifylist = [ + ('force', False), + ('purge', False), + ('volumes', arglist), + ] + 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 + ) + + def test_volume_delete_with_purge(self): + arglist = [ + '--purge', + self.volumes[0].id, + ] + verifylist = [ + ('force', False), + ('purge', True), + ('volumes', [self.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( + self.volumes[0].id, cascade=True + ) + self.assertIsNone(result) + + def test_volume_delete_with_force(self): + arglist = [ + '--force', + self.volumes[0].id, + ] + verifylist = [ + ('force', True), + ('purge', False), + ('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( + self.volumes[0].id + ) + self.assertIsNone(result) + + +class TestVolumeDelete(BaseVolumeTest): + 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) + + 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] + verifylist = [ + ("remote", True), + ("force", False), + ("purge", False), + ("volumes", [vol.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} + ) + + arglist = ['--remote'] + arglist += [v.id for v in volumes] + verifylist = [ + ('remote', True), + ('force', False), + ('purge', False), + ('volumes', arglist[1:]), + ] + 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}) + + arglist = [ + '--remote', + '--purge', + vol.id, + ] + verifylist = [ + ('remote', True), + ('force', False), + ('purge', True), + ('volumes', [vol.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 and --purge options are not supported with the " + "--remote parameter.", + str(exc), + ) + + def test_volume_delete_remote_with_force(self): + vol = sdk_fakes.generate_fake_resource(_volume.Volume, **{'size': 1}) + + arglist = [ + '--remote', + '--force', + vol.id, + ] + verifylist = [ + ('remote', True), + ('force', True), + ('purge', False), + ('volumes', [vol.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 and --purge options are not supported with the " + "--remote parameter.", + str(exc), + ) + + +class TestVolumeList(volume_fakes.TestVolume): + project = identity_fakes.FakeProject.create_one_project() + user = identity_fakes.FakeUser.create_one_user() + + columns = [ + 'ID', + 'Name', + 'Status', + 'Size', + 'Attached to', + ] + + def setUp(self): + super().setUp() + + self.volumes_mock = self.volume_client.volumes + self.volumes_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.mock_volume = volume_fakes.create_one_volume() + self.volumes_mock.list.return_value = [self.mock_volume] + + self.users_mock.get.return_value = self.user + + self.projects_mock.get.return_value = self.project + + # 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), + ('marker', None), + ('limit', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + search_opts = { + 'all_tenants': False, + 'project_id': None, + 'user_id': None, + 'name': None, + 'status': None, + } + self.volumes_mock.list.assert_called_once_with( + search_opts=search_opts, + marker=None, + limit=None, + ) + + self.assertEqual(self.columns, columns) + + datalist = ( + ( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + volume.AttachmentsColumn(self.mock_volume.attachments), + ), + ) + self.assertCountEqual(datalist, tuple(data)) + + def test_volume_list_project(self): + arglist = [ + '--project', + self.project.name, + ] + verifylist = [ + ('project', self.project.name), + ('long', False), + ('all_projects', False), + ('status', None), + ('marker', None), + ('limit', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + search_opts = { + 'all_tenants': True, + 'project_id': self.project.id, + 'user_id': None, + 'name': None, + 'status': None, + } + self.volumes_mock.list.assert_called_once_with( + search_opts=search_opts, + marker=None, + limit=None, + ) + + self.assertEqual(self.columns, columns) + + datalist = ( + ( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + volume.AttachmentsColumn(self.mock_volume.attachments), + ), + ) + self.assertCountEqual(datalist, tuple(data)) + + def test_volume_list_project_domain(self): + arglist = [ + '--project', + self.project.name, + '--project-domain', + self.project.domain_id, + ] + verifylist = [ + ('project', self.project.name), + ('project_domain', self.project.domain_id), + ('long', False), + ('all_projects', False), + ('status', None), + ('marker', None), + ('limit', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + search_opts = { + 'all_tenants': True, + 'project_id': self.project.id, + 'user_id': None, + 'name': None, + 'status': None, + } + self.volumes_mock.list.assert_called_once_with( + search_opts=search_opts, + marker=None, + limit=None, + ) + + self.assertEqual(self.columns, columns) + + datalist = ( + ( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + volume.AttachmentsColumn(self.mock_volume.attachments), + ), + ) + self.assertCountEqual(datalist, tuple(data)) + + def test_volume_list_user(self): + arglist = [ + '--user', + self.user.name, + ] + verifylist = [ + ('user', self.user.name), + ('long', False), + ('all_projects', False), + ('status', None), + ('marker', None), + ('limit', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + search_opts = { + 'all_tenants': False, + 'project_id': None, + 'user_id': self.user.id, + 'name': None, + 'status': None, + } + self.volumes_mock.list.assert_called_once_with( + search_opts=search_opts, + marker=None, + limit=None, + ) + self.assertEqual(self.columns, columns) + + datalist = ( + ( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + volume.AttachmentsColumn(self.mock_volume.attachments), + ), + ) + self.assertCountEqual(datalist, tuple(data)) + + def test_volume_list_user_domain(self): + arglist = [ + '--user', + self.user.name, + '--user-domain', + self.user.domain_id, + ] + verifylist = [ + ('user', self.user.name), + ('user_domain', self.user.domain_id), + ('long', False), + ('all_projects', False), + ('status', None), + ('marker', None), + ('limit', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + search_opts = { + 'all_tenants': False, + 'project_id': None, + 'user_id': self.user.id, + 'name': None, + 'status': None, + } + self.volumes_mock.list.assert_called_once_with( + search_opts=search_opts, + marker=None, + limit=None, + ) + + self.assertEqual(self.columns, columns) + + datalist = ( + ( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + volume.AttachmentsColumn(self.mock_volume.attachments), + ), + ) + self.assertCountEqual(datalist, tuple(data)) + + def test_volume_list_name(self): + arglist = [ + '--name', + self.mock_volume.name, + ] + verifylist = [ + ('long', False), + ('all_projects', False), + ('name', self.mock_volume.name), + ('status', None), + ('marker', None), + ('limit', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + search_opts = { + 'all_tenants': False, + 'project_id': None, + 'user_id': None, + 'name': self.mock_volume.name, + 'status': None, + } + self.volumes_mock.list.assert_called_once_with( + search_opts=search_opts, + marker=None, + limit=None, + ) + + self.assertEqual(self.columns, columns) + + datalist = ( + ( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + volume.AttachmentsColumn(self.mock_volume.attachments), + ), + ) + self.assertCountEqual(datalist, tuple(data)) + + def test_volume_list_status(self): + arglist = [ + '--status', + self.mock_volume.status, + ] + verifylist = [ + ('long', False), + ('all_projects', False), + ('name', None), + ('status', self.mock_volume.status), + ('marker', None), + ('limit', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + search_opts = { + 'all_tenants': False, + 'project_id': None, + 'user_id': None, + 'name': None, + 'status': self.mock_volume.status, + } + self.volumes_mock.list.assert_called_once_with( + search_opts=search_opts, + marker=None, + limit=None, + ) + + self.assertEqual(self.columns, columns) + + datalist = ( + ( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + volume.AttachmentsColumn(self.mock_volume.attachments), + ), + ) + self.assertCountEqual(datalist, tuple(data)) + + def test_volume_list_all_projects(self): + arglist = [ + '--all-projects', + ] + verifylist = [ + ('long', False), + ('all_projects', True), + ('name', None), + ('status', None), + ('marker', None), + ('limit', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + search_opts = { + 'all_tenants': True, + 'project_id': None, + 'user_id': None, + 'name': None, + 'status': None, + } + self.volumes_mock.list.assert_called_once_with( + search_opts=search_opts, + marker=None, + limit=None, + ) + + self.assertEqual(self.columns, columns) + + datalist = ( + ( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + volume.AttachmentsColumn(self.mock_volume.attachments), + ), + ) + self.assertCountEqual(datalist, tuple(data)) + + def test_volume_list_long(self): + arglist = [ + '--long', + ] + verifylist = [ + ('long', True), + ('all_projects', False), + ('name', None), + ('status', None), + ('marker', None), + ('limit', None), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + search_opts = { + 'all_tenants': False, + 'project_id': None, + 'user_id': None, + 'name': None, + 'status': None, + } + self.volumes_mock.list.assert_called_once_with( + search_opts=search_opts, + marker=None, + limit=None, + ) + + collist = [ + 'ID', + 'Name', + 'Status', + 'Size', + 'Type', + 'Bootable', + 'Attached to', + 'Properties', + ] + self.assertEqual(collist, columns) + + datalist = ( + ( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + self.mock_volume.volume_type, + self.mock_volume.bootable, + volume.AttachmentsColumn(self.mock_volume.attachments), + format_columns.DictColumn(self.mock_volume.metadata), + ), + ) + self.assertCountEqual(datalist, tuple(data)) + + def test_volume_list_with_marker_and_limit(self): + arglist = [ + "--marker", + self.mock_volume.id, + "--limit", + "2", + ] + verifylist = [ + ('long', False), + ('all_projects', False), + ('name', None), + ('status', None), + ('marker', self.mock_volume.id), + ('limit', 2), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(self.columns, columns) + + datalist = ( + ( + self.mock_volume.id, + self.mock_volume.name, + self.mock_volume.status, + self.mock_volume.size, + volume.AttachmentsColumn(self.mock_volume.attachments), + ), + ) + + self.volumes_mock.list.assert_called_once_with( + marker=self.mock_volume.id, + limit=2, + search_opts={ + 'status': None, + 'project_id': None, + 'user_id': None, + 'name': None, + 'all_tenants': False, + }, + ) + self.assertCountEqual(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), + ('marker', None), + ('limit', None), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + search_opts = { + 'all_tenants': False, + 'project_id': None, + 'user_id': None, + 'name': None, + 'status': None, + } + self.volumes_mock.list.assert_called_once_with( + search_opts=search_opts, + marker=None, + limit=None, + ) + + self.assertIn('Display Name', columns) + self.assertNotIn('Name', columns) + + for each_volume in data: + self.assertIn(self.mock_volume.name, each_volume) + + +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.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), + ("lock_volume", 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, False + ) + self.assertIsNone(result) + + def test_volume_migrate_with_option(self): + arglist = [ + "--force-host-copy", + "--lock-volume", + "--host", + "host@backend-name#pool", + self._volume.id, + ] + verifylist = [ + ("force_host_copy", True), + ("lock_volume", 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, True + ) + self.assertIsNone(result) + + def test_volume_migrate_without_host(self): + arglist = [ + self._volume.id, + ] + verifylist = [ + ("force_host_copy", False), + ("lock_volume", False), + ("volume", self._volume.id), + ] + + self.assertRaises( + test_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist, + ) + + +class TestVolumeSet(volume_fakes.TestVolume): + volume_type = volume_fakes.create_one_volume_type() + + def setUp(self): + super().setUp() + + self.volumes_mock = self.volume_client.volumes + self.volumes_mock.reset_mock() + + self.types_mock = self.volume_client.volume_types + self.types_mock.reset_mock() + + self.new_volume = volume_fakes.create_one_volume() + self.volumes_mock.get.return_value = self.new_volume + self.types_mock.get.return_value = self.volume_type + + # Get the command object to test + self.cmd = volume.SetVolume(self.app, None) + + def test_volume_set_property(self): + arglist = [ + '--property', + 'a=b', + '--property', + 'c=d', + self.new_volume.id, + ] + verifylist = [ + ('property', {'a': 'b', 'c': 'd'}), + ('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 + ) + + def test_volume_set_image_property(self): + arglist = [ + '--image-property', + 'Alpha=a', + '--image-property', + 'Beta=b', + self.new_volume.id, + ] + verifylist = [ + ('image_property', {'Alpha': 'a', 'Beta': 'b'}), + ('volume', self.new_volume.id), + ('bootable', False), + ('non_bootable', False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # 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 + ) + + def test_volume_set_state(self): + arglist = ['--state', 'error', self.new_volume.id] + verifylist = [ + ('read_only', False), + ('read_write', False), + ('state', 'error'), + ('volume', self.new_volume.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.reset_state.assert_called_with( + self.new_volume.id, 'error' + ) + self.volumes_mock.update_readonly_flag.assert_not_called() + self.assertIsNone(result) + + def test_volume_set_state_failed(self): + self.volumes_mock.reset_state.side_effect = exceptions.CommandError() + arglist = ['--state', 'error', self.new_volume.id] + verifylist = [('state', 'error'), ('volume', self.new_volume.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.volumes_mock.reset_state.assert_called_with( + self.new_volume.id, 'error' + ) + + def test_volume_set_attached(self): + arglist = ['--attached', self.new_volume.id] + verifylist = [ + ('attached', True), + ('detached', False), + ('volume', self.new_volume.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.reset_state.assert_called_with( + self.new_volume.id, attach_status='attached', state=None + ) + self.assertIsNone(result) + + def test_volume_set_detached(self): + arglist = ['--detached', self.new_volume.id] + verifylist = [ + ('attached', False), + ('detached', True), + ('volume', self.new_volume.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.reset_state.assert_called_with( + self.new_volume.id, attach_status='detached', state=None + ) + self.assertIsNone(result) + + 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] + ) + + self.cmd.take_action(parsed_args) + self.volumes_mock.set_bootable.assert_called_with( + self.new_volume.id, verifylist[index][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), + ] + + 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.new_volume.id, True + ) + self.assertIsNone(result) + + 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), + ] + + 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.new_volume.id, False + ) + self.assertIsNone(result) + + def test_volume_set_type(self): + arglist = ['--type', self.volume_type.id, self.new_volume.id] + verifylist = [ + ('retype_policy', None), + ('type', self.volume_type.id), + ('volume', self.new_volume.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.retype.assert_called_once_with( + self.new_volume.id, self.volume_type.id, 'never' + ) + self.assertIsNone(result) + + def test_volume_set_type_with_policy(self): + arglist = [ + '--retype-policy', + 'on-demand', + '--type', + self.volume_type.id, + self.new_volume.id, + ] + verifylist = [ + ('retype_policy', 'on-demand'), + ('type', self.volume_type.id), + ('volume', self.new_volume.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.retype.assert_called_once_with( + self.new_volume.id, self.volume_type.id, 'on-demand' + ) + self.assertIsNone(result) + + @mock.patch.object(volume.LOG, 'warning') + def test_volume_set_with_only_retype_policy(self, mock_warning): + arglist = ['--retype-policy', 'on-demand', self.new_volume.id] + verifylist = [ + ('retype_policy', 'on-demand'), + ('volume', self.new_volume.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + 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" + ) + self.assertIsNone(result) + + +class TestVolumeShow(volume_fakes.TestVolume): + def setUp(self): + super().setUp() + + self.volumes_mock = self.volume_client.volumes + self.volumes_mock.reset_mock() + + 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)] + 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, + ) + + +class TestVolumeUnset(volume_fakes.TestVolume): + def setUp(self): + super().setUp() + + self.volumes_mock = self.volume_client.volumes + self.volumes_mock.reset_mock() + + self.new_volume = volume_fakes.create_one_volume() + self.volumes_mock.get.return_value = self.new_volume + + # Get the command object to set property + self.cmd_set = volume.SetVolume(self.app, None) + + # Get the command object to unset property + self.cmd_unset = volume.UnsetVolume(self.app, None) + + def test_volume_unset_image_property(self): + # Arguments for setting image properties + arglist = [ + '--image-property', + 'Alpha=a', + '--image-property', + 'Beta=b', + self.new_volume.id, + ] + verifylist = [ + ('image_property', {'Alpha': 'a', 'Beta': 'b'}), + ('volume', self.new_volume.id), + ] + parsed_args = self.check_parser(self.cmd_set, arglist, verifylist) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns nothing + self.cmd_set.take_action(parsed_args) + + # Arguments for unsetting image properties + arglist_unset = [ + '--image-property', + 'Alpha', + self.new_volume.id, + ] + verifylist_unset = [ + ('image_property', ['Alpha']), + ('volume', self.new_volume.id), + ] + parsed_args_unset = self.check_parser( + self.cmd_unset, arglist_unset, verifylist_unset + ) + + # In base command class ShowOne in cliff, abstract method take_action() + # returns nothing + 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 + ) + + def test_volume_unset_image_property_fail(self): + self.volumes_mock.delete_image_metadata.side_effect = ( + exceptions.CommandError() + ) + arglist = [ + '--image-property', + 'Alpha', + '--property', + 'Beta', + self.new_volume.id, + ] + verifylist = [ + ('image_property', ['Alpha']), + ('property', ['Beta']), + ('volume', self.new_volume.id), + ] + parsed_args = self.check_parser(self.cmd_unset, arglist, verifylist) + + try: + self.cmd_unset.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual( + '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.volumes_mock.delete_metadata.assert_called_with( + self.new_volume.id, parsed_args.property + ) + + +class TestVolumeSummary(BaseVolumeTest): + columns = [ + 'Total Count', + 'Total Size', + ] + + def setUp(self): + super().setUp() + + self.volume_a = sdk_fakes.generate_fake_resource(_volume.Volume) + self.volume_b = sdk_fakes.generate_fake_resource(_volume.Volume) + self.summary = sdk_fakes.generate_fake_resource( + _summary.BlockStorageSummary, + total_count=2, + total_size=self.volume_a.size + self.volume_b.size, + ) + self.volume_sdk_client.summary.return_value = self.summary + + # Get the command object to test + self.cmd = volume.VolumeSummary(self.app, None) + + def test_volume_summary(self): + self._set_mock_microversion('3.12') + arglist = [ + '--all-projects', + ] + verifylist = [ + ('all_projects', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.summary.assert_called_once_with(True) + + self.assertEqual(self.columns, columns) + + datalist = (2, self.volume_a.size + self.volume_b.size) + self.assertCountEqual(datalist, tuple(data)) + + def test_volume_summary_pre_v312(self): + arglist = [ + '--all-projects', + ] + verifylist = [ + ('all_projects', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + '--os-volume-api-version 3.12 or greater is required', str(exc) + ) + + def test_volume_summary_with_metadata(self): + self._set_mock_microversion('3.36') + + metadata = {**self.volume_a.metadata, **self.volume_b.metadata} + self.summary = sdk_fakes.generate_fake_resource( + _summary.BlockStorageSummary, + total_count=2, + total_size=self.volume_a.size + self.volume_b.size, + metadata=metadata, + ) + self.volume_sdk_client.summary.return_value = self.summary + + new_cols = copy.deepcopy(self.columns) + new_cols.extend(['Metadata']) + + arglist = [ + '--all-projects', + ] + verifylist = [ + ('all_projects', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.volume_sdk_client.summary.assert_called_once_with(True) + + self.assertEqual(new_cols, columns) + + datalist = ( + 2, + self.volume_a.size + self.volume_b.size, + format_columns.DictColumn(metadata), + ) + self.assertCountEqual(datalist, tuple(data)) + + +class TestVolumeRevertToSnapshot(BaseVolumeTest): + def setUp(self): + super().setUp() + + self.volume = sdk_fakes.generate_fake_resource(_volume.Volume) + self.snapshot = sdk_fakes.generate_fake_resource( + _snapshot.Snapshot, + volume_id=self.volume.id, + ) + self.volume_sdk_client.find_volume.return_value = self.volume + self.volume_sdk_client.find_snapshot.return_value = self.snapshot + + # Get the command object to test + self.cmd = volume.VolumeRevertToSnapshot(self.app, None) + + def test_volume_revert_to_snapshot_pre_v340(self): + arglist = [ + self.snapshot.id, + ] + verifylist = [ + ('snapshot', self.snapshot.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + '--os-volume-api-version 3.40 or greater is required', str(exc) + ) + + def test_volume_revert_to_snapshot(self): + self._set_mock_microversion('3.40') + arglist = [ + self.snapshot.id, + ] + verifylist = [ + ('snapshot', self.snapshot.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.volume_sdk_client.revert_volume_to_snapshot.assert_called_once_with( + self.volume, + self.snapshot, + ) + self.volume_sdk_client.find_volume.assert_called_with( + self.volume.id, + ignore_missing=False, + ) + self.volume_sdk_client.find_snapshot.assert_called_with( + self.snapshot.id, + ignore_missing=False, + ) + + +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'] + + 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/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index eaaa40a5a..53a19d429 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -99,12 +99,10 @@ def _check_size_arg(args): volume is not specified. """ - if ( - args.snapshot or args.source or args.backup - ) is None and args.size is None: + if (args.snapshot or args.source) is None and args.size is None: msg = _( - "--size is a required option if snapshot, backup " - "or source volume are not specified." + "--size is a required option if --snapshot or --source are " + "not specified" ) raise exceptions.CommandError(msg) @@ -121,8 +119,8 @@ def _get_parser(self, prog_name): metavar="", type=int, help=_( - "Volume size in GB (required unless --snapshot, " - "--source or --backup is specified)" + "Volume size in GB (required unless --snapshot or " + "--source specified)" ), ) parser.add_argument( @@ -146,14 +144,6 @@ def _get_parser(self, prog_name): metavar="", help=_("Volume to clone (name or ID)"), ) - source_group.add_argument( - "--backup", - metavar="", - help=_( - "Restore backup to a volume (name or ID) " - "(supported by --os-volume-api-version 3.47 or later)" - ), - ) source_group.add_argument( "--source-replicated", metavar="", @@ -222,26 +212,17 @@ def get_parser(self, prog_name): parser, _ = self._get_parser(prog_name) return parser - def _take_action(self, parsed_args): + def take_action(self, parsed_args): CreateVolume._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 - # volume from snapshot, backup or source volume + # volume from snapshot or source volume size = parsed_args.size volume_client = self.app.client_manager.volume image_client = self.app.client_manager.image - if parsed_args.backup and not ( - volume_client.api_version.matches('3.47') - ): - msg = _( - "--os-volume-api-version 3.47 or greater is required " - "to create a volume from backup." - ) - raise exceptions.CommandError(msg) - source_volume = None if parsed_args.source: source_volume_obj = utils.find_resource( @@ -276,15 +257,6 @@ def _take_action(self, parsed_args): # snapshot size. size = max(size or 0, snapshot_obj.size) - backup = None - if parsed_args.backup: - backup_obj = utils.find_resource( - volume_client.backups, parsed_args.backup - ) - backup = backup_obj.id - # As above - size = max(size or 0, backup_obj.size) - volume = volume_client.volumes.create( size=size, snapshot_id=snapshot, @@ -297,7 +269,6 @@ def _take_action(self, parsed_args): source_volid=source_volume, consistencygroup_id=consistency_group, scheduler_hints=parsed_args.hint, - backup_id=backup, ) if parsed_args.bootable or parsed_args.non_bootable: @@ -359,9 +330,6 @@ def _take_action(self, parsed_args): volume._info.pop("links", None) return zip(*sorted(volume._info.items())) - def take_action(self, parsed_args): - return self._take_action(parsed_args) - class DeleteVolume(command.Command): _description = _("Delete volume(s)") @@ -784,10 +752,7 @@ 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 != 'available': msg = ( _( "Volume is in %s state, it must be available " diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index 844831839..e1f025c75 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -14,8 +14,12 @@ """Volume V3 Volume action implementations""" +import argparse +import copy +import functools import logging +from cliff import columns as cliff_columns from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions @@ -23,97 +27,67 @@ 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 from openstackclient.volume.v2 import volume as volume_v2 + LOG = logging.getLogger(__name__) -class VolumeSummary(command.ShowOne): - _description = _("Show a summary of all volumes in this deployment.") +class KeyValueHintAction(argparse.Action): + """Uses KeyValueAction or KeyValueAppendAction based on the given key""" - 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)'), - ) - return parser + APPEND_KEYS = ('same_host', 'different_host') - def take_action(self, parsed_args): - volume_client = self.app.client_manager.sdk_connection.volume + def __init__(self, *args, **kwargs): + self._key_value_action = parseractions.KeyValueAction(*args, **kwargs) + self._key_value_append_action = parseractions.KeyValueAppendAction( + *args, **kwargs + ) + super().__init__(*args, **kwargs) - if not sdk_utils.supports_microversion(volume_client, '3.12'): - msg = _( - "--os-volume-api-version 3.12 or greater is required to " - "support the 'volume summary' command" + def __call__(self, parser, namespace, values, option_string=None): + if values.startswith(self.APPEND_KEYS): + self._key_value_append_action( + parser, namespace, values, option_string=option_string + ) + else: + self._key_value_action( + parser, namespace, values, option_string=option_string ) - raise exceptions.CommandError(msg) - - columns = [ - 'total_count', - 'total_size', - ] - column_headers = [ - 'Total Count', - 'Total Size', - ] - if sdk_utils.supports_microversion(volume_client, '3.36'): - columns.append('metadata') - column_headers.append('Metadata') - # set value of 'all_tenants' when using project option - all_projects = parsed_args.all_projects - vol_summary = volume_client.summary(all_projects) +class AttachmentsColumn(cliff_columns.FormattableColumn): + """Formattable column for attachments column. - return ( - column_headers, - utils.get_item_properties( - vol_summary, - columns, - formatters={'metadata': format_columns.DictColumn}, - ), - ) + 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 {} -class VolumeRevertToSnapshot(command.Command): - _description = _("Revert a volume to a snapshot.") + def human_readable(self): + """Return a formatted string of a volume's attached instances - def get_parser(self, prog_name): - parser = super().get_parser(prog_name) - parser.add_argument( - 'snapshot', - metavar="", - help=_( - 'Name or ID of the snapshot to restore. The snapshot must ' - 'be the most recent one known to cinder.' - ), - ) - return parser - - def take_action(self, parsed_args): - volume_client = self.app.client_manager.sdk_connection.volume - - if not sdk_utils.supports_microversion(volume_client, '3.40'): - msg = _( - "--os-volume-api-version 3.40 or greater is required to " - "support the 'volume revert snapshot' command" - ) - raise exceptions.CommandError(msg) - - snapshot = volume_client.find_snapshot( - parsed_args.snapshot, - ignore_missing=False, - ) - volume = volume_client.find_volume( - snapshot.volume_id, - ignore_missing=False, - ) + :rtype: a string of formatted instances + """ - volume_client.revert_volume_to_snapshot(volume, snapshot) + 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 class CreateVolume(volume_v2.CreateVolume): @@ -123,7 +97,9 @@ class CreateVolume(volume_v2.CreateVolume): def _check_size_arg(args): """Check whether --size option is required or not. - Require size parameter in case if any of the following is not specified: + Require size parameter in case if any of the following is not + specified: + * snapshot * source volume * backup @@ -142,6 +118,14 @@ def _check_size_arg(args): def get_parser(self, prog_name): parser, source_group = self._get_parser(prog_name) + source_group.add_argument( + "--backup", + metavar="", + help=_( + "Restore backup to a volume (name or ID) " + "(supported by --os-volume-api-version 3.47 or later)" + ), + ) source_group.add_argument( "--remote-source", metavar="", @@ -149,8 +133,8 @@ def get_parser(self, prog_name): help=_( "The attribute(s) of the existing remote volume " "(admin required) (repeat option to specify multiple " - "attributes) e.g.: '--remote-source source-name=test_name " - "--remote-source source-id=test_id'" + "attributes, e.g.: '--remote-source source-name=test_name " + "--remote-source source-id=test_id')" ), ) parser.add_argument( @@ -176,8 +160,15 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): CreateVolume._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 + # 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 + image_client = self.app.client_manager.image if ( parsed_args.host or parsed_args.cluster @@ -188,6 +179,15 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) + if parsed_args.backup and not ( + volume_client.api_version.matches('3.47') + ): + msg = _( + "--os-volume-api-version 3.47 or greater is required " + "to create a volume from backup." + ) + raise exceptions.CommandError(msg) + if parsed_args.remote_source: if ( parsed_args.size @@ -236,7 +236,122 @@ def take_action(self, parsed_args): ) return zip(*sorted(volume.items())) - return self._take_action(parsed_args) + source_volume = None + if parsed_args.source: + source_volume_obj = utils.find_resource( + volume_client.volumes, parsed_args.source + ) + 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 + + image = None + if parsed_args.image: + image = image_client.find_image( + parsed_args.image, ignore_missing=False + ).id + + snapshot = None + if parsed_args.snapshot: + snapshot_obj = utils.find_resource( + volume_client.volume_snapshots, parsed_args.snapshot + ) + snapshot = snapshot_obj.id + # Cinder requires a value for size when creating a volume + # even if creating from a snapshot. Cinder will create the + # volume with at least the same size as the snapshot anyway, + # so since we have the object here, just override the size + # value if it's either not given or is smaller than the + # snapshot size. + size = max(size or 0, snapshot_obj.size) + + backup = None + if parsed_args.backup: + backup_obj = utils.find_resource( + volume_client.backups, parsed_args.backup + ) + backup = backup_obj.id + # As above + size = max(size or 0, backup_obj.size) + + volume = volume_client.volumes.create( + size=size, + snapshot_id=snapshot, + name=parsed_args.name, + description=parsed_args.description, + volume_type=parsed_args.type, + availability_zone=parsed_args.availability_zone, + metadata=parsed_args.property, + imageRef=image, + source_volid=source_volume, + consistencygroup_id=consistency_group, + scheduler_hints=parsed_args.hint, + backup_id=backup, + ) + + 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, + ) + + # 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())) class DeleteVolume(volume_v2.DeleteVolume): @@ -291,3 +406,680 @@ def take_action(self, parsed_args): '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( + '--project', + metavar='', + help=_('Filter results by project (name or ID) (admin only)'), + ) + identity_common.add_project_domain_option_to_parser(parser) + parser.add_argument( + '--user', + metavar='', + help=_('Filter results by user (name or ID) (admin only)'), + ) + identity_common.add_user_domain_option_to_parser(parser) + 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_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', + 'Status', + 'Size', + 'Volume Type', + 'Bootable', + 'Attachments', + 'Metadata', + ] + column_headers = copy.deepcopy(columns) + column_headers[4] = 'Type' + column_headers[6] = 'Attached to' + column_headers[7] = 'Properties' + else: + columns = [ + 'ID', + 'Name', + 'Status', + 'Size', + 'Attachments', + ] + column_headers = copy.deepcopy(columns) + column_headers[4] = 'Attached to' + + project_id = None + if parsed_args.project: + project_id = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + + user_id = None + if parsed_args.user: + user_id = identity_common.find_user( + identity_client, parsed_args.user, parsed_args.user_domain + ).id + + # set value of 'all_tenants' when using project option + all_projects = bool(parsed_args.project) or parsed_args.all_projects + + search_opts = { + 'all_tenants': all_projects, + 'project_id': project_id, + 'user_id': user_id, + 'name': parsed_args.name, + 'status': parsed_args.status, + } + + data = volume_client.volumes.list( + search_opts=search_opts, + marker=parsed_args.marker, + limit=parsed_args.limit, + ) + + do_server_list = False + + for vol in data: + if vol.status == 'in-use': + do_server_list = True + break + + # Cache the server list + server_cache = {} + if do_server_list: + try: + compute_client = self.app.client_manager.compute + for s in compute_client.servers.list(): + server_cache[s.id] = s + except Exception: + # Just forget it if there's any trouble + pass # nosec: B110 + AttachmentsColumnWithCache = functools.partial( + AttachmentsColumn, server_cache=server_cache + ) + + 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" + ), + ) + parser.add_argument( + '--lock-volume', + action="store_true", + help=_( + "If specified, the volume state will be locked " + "and will not allow a migration to be aborted " + "(possibly by another operation)" + ), + ) + 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, + parsed_args.lock_volume, + ) + + +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( + '--size', + metavar='', + type=int, + help=_('Extend volume size in GB'), + ) + parser.add_argument( + '--description', + metavar='', + help=_('New volume 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=_( + 'Set a property on this volume ' + '(repeat option to set multiple properties)' + ), + ) + parser.add_argument( + '--image-property', + metavar='', + action=parseractions.KeyValueAction, + help=_( + 'Set an image property on this volume ' + '(repeat option to set multiple image properties)' + ), + ) + parser.add_argument( + "--state", + metavar="", + choices=[ + 'available', + 'error', + 'creating', + 'deleting', + 'in-use', + 'attaching', + 'detaching', + 'error_deleting', + 'maintenance', + ], + help=_( + '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)' + ), + ) + attached_group = parser.add_mutually_exclusive_group() + attached_group.add_argument( + "--attached", + action="store_true", + help=_( + '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)' + ), + ) + attached_group.add_argument( + "--detached", + action="store_true", + help=_( + '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)' + ), + ) + parser.add_argument( + '--type', + metavar='', + help=_('New volume type (name or ID)'), + ) + parser.add_argument( + '--retype-policy', + metavar='', + choices=['never', 'on-demand'], + help=argparse.SUPPRESS, + ) + parser.add_argument( + '--migration-policy', + metavar='', + choices=['never', 'on-demand'], + help=_( + 'Migration policy while re-typing volume ' + '("never" or "on-demand", default is "never" ) ' + '(available only when --type option is specified)' + ), + ) + 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.retype_policy: + msg = _( + "The '--retype-policy' option has been deprecated in favor " + "of '--migration-policy' option. The '--retype-policy' option " + "will be removed in a future release. Please use " + "'--migration-policy' instead." + ) + self.log.warning(msg) + + if parsed_args.size: + try: + if parsed_args.size <= volume.size: + msg = ( + _("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') + ): + msg = ( + _( + "Volume is in %s state, it must be available " + "before size can be extended" + ) + % volume.status + ) + 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.image_property: + try: + volume_client.volumes.set_image_metadata( + volume.id, parsed_args.image_property + ) + except Exception as e: + LOG.error(_("Failed to set image property: %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( + volume.id, state=None, attach_status="attached" + ) + 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( + volume.id, state=None, attach_status="detached" + ) + 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: + 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 + policy = parsed_args.migration_policy or parsed_args.retype_policy + if parsed_args.type: + # get the migration policy + migration_policy = 'never' + if policy: + migration_policy = policy + try: + # find the volume type + volume_type = utils.find_resource( + volume_client.volume_types, parsed_args.type + ) + # reset to the new volume type + volume_client.volumes.retype( + volume.id, volume_type.id, migration_policy + ) + except Exception as e: + LOG.error(_("Failed to set volume type: %s"), e) + result += 1 + elif policy: + # If the "--migration-policy" is specified without "--type" + LOG.warning( + _("'%s' option will not work without '--type' option") + % ( + '--migration-policy' + if parsed_args.migration_policy + else '--retype-policy' + ) + ) + + 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 = _("Display 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) + + # 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())) + + +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)' + ), + ) + parser.add_argument( + '--image-property', + metavar='', + action='append', + help=_( + 'Remove an image property from volume ' + '(repeat option to remove multiple image 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) + + result = 0 + if parsed_args.property: + try: + volume_client.volumes.delete_metadata( + volume.id, parsed_args.property + ) + except Exception as e: + LOG.error(_("Failed to unset volume property: %s"), e) + result += 1 + + if parsed_args.image_property: + try: + volume_client.volumes.delete_image_metadata( + volume.id, parsed_args.image_property + ) + except Exception as e: + LOG.error(_("Failed to unset image property: %s"), e) + result += 1 + + if result > 0: + raise exceptions.CommandError( + _("One or more of the " "unset operations failed") + ) + + +class VolumeSummary(command.ShowOne): + _description = _("Show a summary of all volumes in this deployment.") + + 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)'), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.sdk_connection.volume + + if not sdk_utils.supports_microversion(volume_client, '3.12'): + msg = _( + "--os-volume-api-version 3.12 or greater is required to " + "support the 'volume summary' command" + ) + raise exceptions.CommandError(msg) + + columns = [ + 'total_count', + 'total_size', + ] + column_headers = [ + 'Total Count', + 'Total Size', + ] + if sdk_utils.supports_microversion(volume_client, '3.36'): + columns.append('metadata') + column_headers.append('Metadata') + + # set value of 'all_tenants' when using project option + all_projects = parsed_args.all_projects + + vol_summary = volume_client.summary(all_projects) + + return ( + column_headers, + utils.get_item_properties( + vol_summary, + columns, + formatters={'metadata': format_columns.DictColumn}, + ), + ) + + +class VolumeRevertToSnapshot(command.Command): + _description = _("Revert a volume to a snapshot.") + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'snapshot', + metavar="", + help=_( + 'Name or ID of the snapshot to restore. The snapshot must ' + 'be the most recent one known to cinder.' + ), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.sdk_connection.volume + + if not sdk_utils.supports_microversion(volume_client, '3.40'): + msg = _( + "--os-volume-api-version 3.40 or greater is required to " + "support the 'volume revert snapshot' command" + ) + raise exceptions.CommandError(msg) + + snapshot = volume_client.find_snapshot( + parsed_args.snapshot, + ignore_missing=False, + ) + volume = volume_client.find_volume( + snapshot.volume_id, + ignore_missing=False, + ) + + volume_client.revert_volume_to_snapshot(volume, snapshot) diff --git a/setup.cfg b/setup.cfg index 1ee4dd465..b6f8188df 100644 --- a/setup.cfg +++ b/setup.cfg @@ -767,11 +767,11 @@ openstack.volume.v3 = volume_create = openstackclient.volume.v3.volume:CreateVolume volume_delete = openstackclient.volume.v3.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_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 From f1f390f2aea4a582b30b8cf45646ebb9afce599b Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 7 May 2024 16:47:14 +0100 Subject: [PATCH 136/403] volume: add v3-specific volume type module This makes testing easier. Change-Id: If1ed8d5003160e45d503971ae722fd9983d3dd6d Signed-off-by: Stephen Finucane --- .../tests/unit/volume/v2/test_volume_type.py | 91 +- openstackclient/tests/unit/volume/v3/fakes.py | 133 +- .../tests/unit/volume/v3/test_volume_type.py | 1114 +++++++++++++++++ openstackclient/volume/v2/volume_type.py | 82 -- openstackclient/volume/v3/volume_type.py | 967 ++++++++++++++ setup.cfg | 12 +- 6 files changed, 2222 insertions(+), 177 deletions(-) create mode 100644 openstackclient/tests/unit/volume/v3/test_volume_type.py create mode 100644 openstackclient/volume/v3/volume_type.py diff --git a/openstackclient/tests/unit/volume/v2/test_volume_type.py b/openstackclient/tests/unit/volume/v2/test_volume_type.py index 8b5096ce4..6f50ff2ef 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_type.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_type.py @@ -15,7 +15,6 @@ from unittest import mock from unittest.mock import call -from cinderclient import api_versions from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib import utils @@ -333,7 +332,7 @@ class TestTypeList(TestType): "Name", "Is Public", ] - columns_long = columns + ["Description", "Properties"] + columns_long = columns + ["Description"] data_with_default_type = [(volume_types[0].id, volume_types[0].name, True)] data = [] for t in volume_types: @@ -352,7 +351,6 @@ class TestTypeList(TestType): t.name, t.is_public, t.description, - format_columns.DictColumn(t.extra_specs), ) ) @@ -374,9 +372,7 @@ def test_type_list_without_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_types_mock.list.assert_called_once_with( - search_opts={}, is_public=None - ) + self.volume_types_mock.list.assert_called_once_with(is_public=None) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) @@ -393,9 +389,7 @@ def test_type_list_with_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_types_mock.list.assert_called_once_with( - search_opts={}, is_public=True - ) + self.volume_types_mock.list.assert_called_once_with(is_public=True) self.assertEqual(self.columns_long, columns) self.assertCountEqual(self.data_long, list(data)) @@ -411,9 +405,7 @@ def test_type_list_with_private_option(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_types_mock.list.assert_called_once_with( - search_opts={}, is_public=False - ) + self.volume_types_mock.list.assert_called_once_with(is_public=False) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) @@ -434,77 +426,6 @@ def test_type_list_with_default_option(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data_with_default_type, list(data)) - def test_type_list_with_properties(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.52' - ) - - arglist = [ - "--property", - "foo=bar", - "--multiattach", - "--cacheable", - "--replicated", - "--availability-zone", - "az1", - ] - verifylist = [ - ("encryption_type", False), - ("long", False), - ("is_public", None), - ("default", False), - ("properties", {"foo": "bar"}), - ("multiattach", True), - ("cacheable", True), - ("replicated", True), - ("availability_zones", ["az1"]), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - self.volume_types_mock.list.assert_called_once_with( - search_opts={ - "extra_specs": { - "foo": "bar", - "multiattach": " True", - "cacheable": " True", - "replication_enabled": " True", - "RESKEY:availability_zones": "az1", - } - }, - is_public=None, - ) - self.assertEqual(self.columns, columns) - self.assertCountEqual(self.data, list(data)) - - def test_type_list_with_properties_pre_v352(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.51' - ) - - arglist = [ - "--property", - "foo=bar", - ] - verifylist = [ - ("encryption_type", False), - ("long", False), - ("is_public", None), - ("default", False), - ("properties", {"foo": "bar"}), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - exc = self.assertRaises( - exceptions.CommandError, - self.cmd.take_action, - parsed_args, - ) - self.assertIn( - '--os-volume-api-version 3.52 or greater is required', - str(exc), - ) - 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}, @@ -550,9 +471,7 @@ def test_type_list_with_encryption(self): columns, data = self.cmd.take_action(parsed_args) self.volume_encryption_types_mock.list.assert_called_once_with() - self.volume_types_mock.list.assert_called_once_with( - search_opts={}, is_public=None - ) + self.volume_types_mock.list.assert_called_once_with(is_public=None) self.assertEqual(encryption_columns, columns) self.assertCountEqual(encryption_data, list(data)) diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index b8ebe516a..b014efc43 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -62,12 +62,18 @@ def __init__(self, **kwargs): self.resource_filters.resource_class = fakes.FakeResource(None, {}) self.restores = mock.Mock() self.restores.resource_class = fakes.FakeResource(None, {}) - self.volumes = mock.Mock() - self.volumes.resource_class = fakes.FakeResource(None, {}) + self.volume_encryption_types = mock.Mock() + self.volume_encryption_types.resource_class = fakes.FakeResource( + None, {} + ) self.volume_snapshots = mock.Mock() self.volume_snapshots.resource_class = fakes.FakeResource(None, {}) + self.volume_type_access = mock.Mock() + self.volume_type_access.resource_class = fakes.FakeResource(None, {}) self.volume_types = mock.Mock() self.volume_types.resource_class = fakes.FakeResource(None, {}) + self.volumes = mock.Mock() + self.volumes.resource_class = fakes.FakeResource(None, {}) self.services = mock.Mock() self.services.resource_class = fakes.FakeResource(None, {}) self.workers = mock.Mock() @@ -118,7 +124,6 @@ def setUp(self): # TODO(stephenfin): Check if the responses are actually the same create_one_snapshot = volume_v2_fakes.create_one_snapshot -create_one_volume_type = volume_v2_fakes.create_one_volume_type def create_one_availability_zone(attrs=None): @@ -364,6 +369,34 @@ def create_clusters(attrs=None, count=2): return clusters +def create_one_encryption_volume_type(attrs=None): + """Create a fake encryption volume type. + + :param dict 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_resource_filter(attrs=None): """Create a fake resource filter. @@ -405,6 +438,31 @@ def create_resource_filters(attrs=None, count=2): return resource_filters +def create_one_type_access(attrs=None): + """Create a fake volume type access for project. + + :param dict attrs: + A dictionary with all attributes + :return: + A FakeResource object, with Volume_type_ID and Project_ID. + """ + if attrs is None: + attrs = {} + + # Set default attributes. + type_access_attrs = { + 'volume_type_id': 'volume-type-id-' + uuid.uuid4().hex, + 'project_id': 'project-id-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + type_access_attrs.update(attrs) + + type_access = fakes.FakeResource(None, type_access_attrs, loaded=True) + + return type_access + + def create_one_volume(attrs=None): """Create a fake volume. @@ -821,6 +879,75 @@ def get_volume_attachments(attachments=None, count=2): return mock.Mock(side_effect=attachments) +def create_one_volume_type(attrs=None, methods=None): + """Create a fake volume type. + + :param dict attrs: + A dictionary with all attributes + :param dict 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 volume_types. + + :param dict 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 volume types. + + If volume_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 volume types + :param Integer count: + The number of volume types to be faked + :return + An iterable Mock object with side_effect set to a list of faked + volume types + """ + if volume_types is None: + volume_types = create_volume_types(count) + + return mock.Mock(side_effect=volume_types) + + def create_service_log_level_entry(attrs=None): service_log_level_info = { 'host': 'host_test', diff --git a/openstackclient/tests/unit/volume/v3/test_volume_type.py b/openstackclient/tests/unit/volume/v3/test_volume_type.py new file mode 100644 index 000000000..5f791da25 --- /dev/null +++ b/openstackclient/tests/unit/volume/v3/test_volume_type.py @@ -0,0 +1,1114 @@ +# +# 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 cinderclient import api_versions +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 identity_fakes +from openstackclient.tests.unit import utils as tests_utils +from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes +from openstackclient.volume.v3 import volume_type + + +class TestType(volume_fakes.TestVolume): + def setUp(self): + super().setUp() + + self.volume_types_mock = self.volume_client.volume_types + self.volume_types_mock.reset_mock() + + self.volume_type_access_mock = self.volume_client.volume_type_access + self.volume_type_access_mock.reset_mock() + + self.volume_encryption_types_mock = ( + self.volume_client.volume_encryption_types + ) + self.volume_encryption_types_mock.reset_mock() + + self.projects_mock = self.identity_client.projects + self.projects_mock.reset_mock() + + +class TestTypeCreate(TestType): + def setUp(self): + super().setUp() + + self.new_volume_type = volume_fakes.create_one_volume_type( + methods={'set_keys': None}, + ) + self.project = identity_fakes.FakeProject.create_one_project() + self.columns = ( + 'description', + 'id', + 'is_public', + 'name', + ) + self.data = ( + self.new_volume_type.description, + self.new_volume_type.id, + True, + self.new_volume_type.name, + ) + + self.volume_types_mock.create.return_value = self.new_volume_type + self.projects_mock.get.return_value = self.project + # Get the command object to test + self.cmd = volume_type.CreateVolumeType(self.app, None) + + def test_type_create_public(self): + arglist = [ + "--description", + self.new_volume_type.description, + "--public", + self.new_volume_type.name, + ] + verifylist = [ + ("description", self.new_volume_type.description), + ("is_public", True), + ("name", self.new_volume_type.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volume_types_mock.create.assert_called_with( + self.new_volume_type.name, + description=self.new_volume_type.description, + is_public=True, + ) + + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_type_create_private(self): + arglist = [ + "--description", + self.new_volume_type.description, + "--private", + "--project", + self.project.id, + self.new_volume_type.name, + ] + verifylist = [ + ("description", self.new_volume_type.description), + ("is_public", False), + ("project", self.project.id), + ("name", self.new_volume_type.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volume_types_mock.create.assert_called_with( + self.new_volume_type.name, + description=self.new_volume_type.description, + is_public=False, + ) + + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_type_create_with_properties(self): + arglist = [ + '--property', + 'myprop=myvalue', + # this combination isn't viable server-side but is okay for testing + '--multiattach', + '--cacheable', + '--replicated', + '--availability-zone', + 'az1', + self.new_volume_type.name, + ] + verifylist = [ + ('properties', {'myprop': 'myvalue'}), + ('multiattach', True), + ('cacheable', True), + ('replicated', True), + ('availability_zones', ['az1']), + ('name', self.new_volume_type.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volume_types_mock.create.assert_called_with( + self.new_volume_type.name, description=None + ) + self.new_volume_type.set_keys.assert_called_once_with( + { + 'myprop': 'myvalue', + 'multiattach': ' True', + 'cacheable': ' True', + 'replication_enabled': ' True', + 'RESKEY:availability_zones': 'az1', + } + ) + + self.columns += ('properties',) + self.data += (format_columns.DictColumn(None),) + + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_public_type_create_with_project_public(self): + arglist = [ + '--project', + self.project.id, + self.new_volume_type.name, + ] + verifylist = [ + ('is_public', None), + ('project', self.project.id), + ('name', self.new_volume_type.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + + 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.volume_types_mock.create.return_value = self.new_volume_type + self.volume_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.volume_types_mock.create.assert_called_with( + self.new_volume_type.name, + description=None, + ) + body = { + 'provider': 'LuksEncryptor', + 'cipher': 'aes-xts-plain64', + 'key_size': 128, + 'control_location': 'front-end', + } + self.volume_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.volume_types_mock.get = volume_fakes.get_volume_types( + self.volume_types, + ) + self.volume_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.volume_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.volume_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.volume_types_mock, self.volume_types[0].id + ) + find_mock.assert_any_call(self.volume_types_mock, 'unexist_type') + + self.assertEqual(2, find_mock.call_count) + self.volume_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 = columns + ["Description", "Properties"] + data_with_default_type = [(volume_types[0].id, volume_types[0].name, True)] + 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, + t.description, + format_columns.DictColumn(t.extra_specs), + ) + ) + + def setUp(self): + super().setUp() + + self.volume_types_mock.list.return_value = self.volume_types + self.volume_types_mock.default.return_value = self.volume_types[0] + # get the command to test + self.cmd = volume_type.ListVolumeType(self.app, None) + + def test_type_list_without_options(self): + arglist = [] + verifylist = [ + ("long", False), + ("is_public", None), + ("default", False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volume_types_mock.list.assert_called_once_with( + search_opts={}, is_public=None + ) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, list(data)) + + def test_type_list_with_options(self): + arglist = [ + "--long", + "--public", + ] + verifylist = [ + ("long", True), + ("is_public", True), + ("default", False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volume_types_mock.list.assert_called_once_with( + search_opts={}, is_public=True + ) + self.assertEqual(self.columns_long, columns) + self.assertCountEqual(self.data_long, list(data)) + + def test_type_list_with_private_option(self): + arglist = [ + "--private", + ] + verifylist = [ + ("long", False), + ("is_public", False), + ("default", False), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volume_types_mock.list.assert_called_once_with( + search_opts={}, is_public=False + ) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, list(data)) + + def test_type_list_with_default_option(self): + arglist = [ + "--default", + ] + verifylist = [ + ("encryption_type", False), + ("long", False), + ("is_public", None), + ("default", True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volume_types_mock.default.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data_with_default_type, list(data)) + + def test_type_list_with_properties(self): + self.app.client_manager.volume.api_version = api_versions.APIVersion( + '3.52' + ) + + arglist = [ + "--property", + "foo=bar", + "--multiattach", + "--cacheable", + "--replicated", + "--availability-zone", + "az1", + ] + verifylist = [ + ("encryption_type", False), + ("long", False), + ("is_public", None), + ("default", False), + ("properties", {"foo": "bar"}), + ("multiattach", True), + ("cacheable", True), + ("replicated", True), + ("availability_zones", ["az1"]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volume_types_mock.list.assert_called_once_with( + search_opts={ + "extra_specs": { + "foo": "bar", + "multiattach": " True", + "cacheable": " True", + "replication_enabled": " True", + "RESKEY:availability_zones": "az1", + } + }, + is_public=None, + ) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, list(data)) + + def test_type_list_with_properties_pre_v352(self): + self.app.client_manager.volume.api_version = api_versions.APIVersion( + '3.51' + ) + + arglist = [ + "--property", + "foo=bar", + ] + verifylist = [ + ("encryption_type", False), + ("long", False), + ("is_public", None), + ("default", False), + ("properties", {"foo": "bar"}), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertIn( + '--os-volume-api-version 3.52 or greater is required', + str(exc), + ) + + 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.volume_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.volume_encryption_types_mock.list.assert_called_once_with() + self.volume_types_mock.list.assert_called_once_with( + search_opts={}, is_public=None + ) + self.assertEqual(encryption_columns, columns) + self.assertCountEqual(encryption_data, list(data)) + + +class TestTypeSet(TestType): + def setUp(self): + super().setUp() + + self.project = identity_fakes.FakeProject.create_one_project() + self.projects_mock.get.return_value = self.project + + self.volume_type = volume_fakes.create_one_volume_type( + methods={'set_keys': None}, + ) + self.volume_types_mock.get.return_value = self.volume_type + self.volume_encryption_types_mock.create.return_value = None + self.volume_encryption_types_mock.update.return_value = None + + self.cmd = volume_type.SetVolumeType(self.app, None) + + def test_type_set(self): + arglist = [ + '--name', + 'new_name', + '--description', + 'new_description', + '--private', + self.volume_type.id, + ] + verifylist = [ + ('name', 'new_name'), + ('description', 'new_description'), + ('properties', None), + ('volume_type', self.volume_type.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs = { + 'name': 'new_name', + 'description': 'new_description', + 'is_public': False, + } + self.volume_types_mock.update.assert_called_with( + self.volume_type.id, **kwargs + ) + self.assertIsNone(result) + + self.volume_type_access_mock.add_project_access.assert_not_called() + self.volume_encryption_types_mock.update.assert_not_called() + self.volume_encryption_types_mock.create.assert_not_called() + + def test_type_set_property(self): + arglist = [ + '--property', + 'myprop=myvalue', + # this combination isn't viable server-side but is okay for testing + '--multiattach', + '--cacheable', + '--replicated', + '--availability-zone', + 'az1', + self.volume_type.id, + ] + verifylist = [ + ('name', None), + ('description', None), + ('properties', {'myprop': 'myvalue'}), + ('multiattach', True), + ('cacheable', True), + ('replicated', True), + ('availability_zones', ['az1']), + ('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) + + self.volume_type.set_keys.assert_called_once_with( + { + 'myprop': 'myvalue', + 'multiattach': ' True', + 'cacheable': ' True', + 'replication_enabled': ' True', + 'RESKEY:availability_zones': 'az1', + } + ) + self.volume_type_access_mock.add_project_access.assert_not_called() + self.volume_encryption_types_mock.update.assert_not_called() + self.volume_encryption_types_mock.create.assert_not_called() + + def test_type_set_with_empty_project(self): + arglist = [ + '--project', + '', + self.volume_type.id, + ] + verifylist = [ + ('project', ''), + ('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) + + self.volume_type.set_keys.assert_not_called() + self.volume_type_access_mock.add_project_access.assert_not_called() + self.volume_encryption_types_mock.update.assert_not_called() + self.volume_encryption_types_mock.create.assert_not_called() + + def test_type_set_with_project(self): + arglist = [ + '--project', + self.project.id, + self.volume_type.id, + ] + verifylist = [ + ('project', self.project.id), + ('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) + + self.volume_type.set_keys.assert_not_called() + self.volume_type_access_mock.add_project_access.assert_called_with( + self.volume_type.id, + self.project.id, + ) + self.volume_encryption_types_mock.update.assert_not_called() + self.volume_encryption_types_mock.create.assert_not_called() + + def test_type_set_with_new_encryption(self): + self.volume_encryption_types_mock.update.side_effect = ( + exceptions.NotFound('NotFound') + ) + 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) + self.assertIsNone(result) + + body = { + 'provider': 'LuksEncryptor', + 'cipher': 'aes-xts-plain64', + 'key_size': 128, + 'control_location': 'front-end', + } + self.volume_encryption_types_mock.update.assert_called_with( + self.volume_type, + body, + ) + self.volume_encryption_types_mock.create.assert_called_with( + self.volume_type, + body, + ) + + @mock.patch.object(utils, 'find_resource') + def test_type_set_with_existing_encryption(self, mock_find): + mock_find.side_effect = [self.volume_type, "existing_encryption_type"] + arglist = [ + '--encryption-provider', + 'LuksEncryptor', + '--encryption-cipher', + 'aes-xts-plain64', + '--encryption-control-location', + 'front-end', + self.volume_type.id, + ] + verifylist = [ + ('encryption_provider', 'LuksEncryptor'), + ('encryption_cipher', 'aes-xts-plain64'), + ('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) + self.assertIsNone(result) + + self.volume_type.set_keys.assert_not_called() + self.volume_type_access_mock.add_project_access.assert_not_called() + body = { + 'provider': 'LuksEncryptor', + 'cipher': 'aes-xts-plain64', + 'control_location': 'front-end', + } + self.volume_encryption_types_mock.update.assert_called_with( + self.volume_type, + body, + ) + self.volume_encryption_types_mock.create.assert_not_called() + + def test_type_set_new_encryption_without_provider(self): + self.volume_encryption_types_mock.update.side_effect = ( + exceptions.NotFound('NotFound') + ) + 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) + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual( + "Command Failed: One or more of the operations failed", + str(exc), + ) + + self.volume_type.set_keys.assert_not_called() + self.volume_type_access_mock.add_project_access.assert_not_called() + body = { + 'cipher': 'aes-xts-plain64', + 'key_size': 128, + 'control_location': 'front-end', + } + self.volume_encryption_types_mock.update.assert_called_with( + self.volume_type, + body, + ) + self.volume_encryption_types_mock.create.assert_not_called() + + +class TestTypeShow(TestType): + columns = ( + 'access_project_ids', + 'description', + 'id', + 'is_public', + 'name', + 'properties', + ) + + def setUp(self): + super().setUp() + + self.volume_type = volume_fakes.create_one_volume_type() + self.data = ( + None, + self.volume_type.description, + self.volume_type.id, + True, + self.volume_type.name, + format_columns.DictColumn(self.volume_type.extra_specs), + ) + + self.volume_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 = [ + ("encryption_type", False), + ("volume_type", self.volume_type.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volume_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_access(self): + arglist = [self.volume_type.id] + verifylist = [("volume_type", self.volume_type.id)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + private_type = volume_fakes.create_one_volume_type( + attrs={'is_public': False}, + ) + type_access_list = volume_fakes.create_one_type_access() + with mock.patch.object( + self.volume_types_mock, + 'get', + return_value=private_type, + ): + with mock.patch.object( + self.volume_type_access_mock, + 'list', + return_value=[type_access_list], + ): + columns, data = self.cmd.take_action(parsed_args) + self.volume_types_mock.get.assert_called_once_with( + self.volume_type.id + ) + self.volume_type_access_mock.list.assert_called_once_with( + private_type.id + ) + + self.assertEqual(self.columns, columns) + private_type_data = ( + format_columns.ListColumn([type_access_list.project_id]), + private_type.description, + private_type.id, + private_type.is_public, + private_type.name, + format_columns.DictColumn(private_type.extra_specs), + ) + self.assertCountEqual(private_type_data, data) + + def test_type_show_with_list_access_exec(self): + arglist = [self.volume_type.id] + verifylist = [("volume_type", self.volume_type.id)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + private_type = volume_fakes.create_one_volume_type( + attrs={'is_public': False}, + ) + with mock.patch.object( + self.volume_types_mock, 'get', return_value=private_type + ): + with mock.patch.object( + self.volume_type_access_mock, 'list', side_effect=Exception() + ): + columns, data = self.cmd.take_action(parsed_args) + self.volume_types_mock.get.assert_called_once_with( + self.volume_type.id + ) + self.volume_type_access_mock.list.assert_called_once_with( + private_type.id + ) + + self.assertEqual(self.columns, columns) + private_type_data = ( + None, + private_type.description, + private_type.id, + private_type.is_public, + private_type.name, + format_columns.DictColumn(private_type.extra_specs), + ) + self.assertCountEqual(private_type_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.volume_types_mock.get.return_value = self.volume_type + self.volume_encryption_types_mock.get.return_value = encryption_type + encryption_columns = ( + 'access_project_ids', + 'description', + 'encryption', + 'id', + 'is_public', + 'name', + 'properties', + ) + encryption_data = ( + None, + 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.volume_types_mock.get.assert_called_with(self.volume_type.id) + self.volume_encryption_types_mock.get.assert_called_with( + self.volume_type.id + ) + self.assertEqual(encryption_columns, columns) + self.assertCountEqual(encryption_data, data) + + +class TestTypeUnset(TestType): + project = identity_fakes.FakeProject.create_one_project() + volume_type = volume_fakes.create_one_volume_type( + methods={'unset_keys': None}, + ) + + def setUp(self): + super().setUp() + + self.volume_types_mock.get.return_value = self.volume_type + + # Return a project + self.projects_mock.get.return_value = self.project + + # Get the command object to test + self.cmd = volume_type.UnsetVolumeType(self.app, None) + + def test_type_unset(self): + arglist = [ + '--property', + 'property', + '--property', + 'multi_property', + self.volume_type.id, + ] + verifylist = [ + ('properties', ['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.assertIsNone(result) + + def test_type_unset_project_access(self): + arglist = [ + '--project', + self.project.id, + self.volume_type.id, + ] + verifylist = [ + ('project', self.project.id), + ('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) + + self.volume_type_access_mock.remove_project_access.assert_called_with( + self.volume_type.id, + self.project.id, + ) + + def test_type_unset_not_called_without_project_argument(self): + arglist = [ + '--project', + '', + self.volume_type.id, + ] + verifylist = [ + ('encryption_type', False), + ('project', ''), + ('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) + self.volume_encryption_types_mock.delete.assert_not_called() + self.assertFalse( + self.volume_type_access_mock.remove_project_access.called + ) + + def test_type_unset_failed_with_missing_volume_type_argument(self): + arglist = [ + '--project', + 'identity_fakes.project_id', + ] + verifylist = [ + ('project', 'identity_fakes.project_id'), + ] + + self.assertRaises( + tests_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist, + ) + + 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.volume_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/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index c472a3c28..f4540f4e8 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -17,7 +17,6 @@ import functools import logging -from cinderclient import api_versions from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions @@ -409,59 +408,6 @@ def get_parser(self, prog_name): "(admin only)" ), ) - parser.add_argument( - '--property', - metavar='', - action=parseractions.KeyValueAction, - dest='properties', - help=_( - 'Filter by a property on the volume types ' - '(repeat option to filter by multiple properties) ' - '(admin only except for user-visible extra specs) ' - '(supported by --os-volume-api-version 3.52 or above)' - ), - ) - parser.add_argument( - '--multiattach', - action='store_true', - default=False, - help=_( - "List only volume types with multi-attach enabled " - "(this is an alias for '--property multiattach= True') " - "(supported by --os-volume-api-version 3.52 or above)" - ), - ) - parser.add_argument( - '--cacheable', - action='store_true', - default=False, - help=_( - "List only volume types with caching enabled " - "(this is an alias for '--property cacheable= True') " - "(admin only) " - "(supported by --os-volume-api-version 3.52 or above)" - ), - ) - parser.add_argument( - '--replicated', - action='store_true', - default=False, - help=_( - "List only volume types with replication enabled " - "(this is an alias for '--property replication_enabled= True') " # noqa: E501 - "(supported by --os-volume-api-version 3.52 or above)" - ), - ) - parser.add_argument( - '--availability-zone', - action='append', - dest='availability_zones', - help=_( - "List only volume types with this availability configured " - "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 - "(repeat option to filter on multiple availability zones)" - ), - ) return parser def take_action(self, parsed_args): @@ -473,14 +419,12 @@ def take_action(self, parsed_args): 'Name', 'Is Public', 'Description', - 'Extra Specs', ] column_headers = [ 'ID', 'Name', 'Is Public', 'Description', - 'Properties', ] else: columns = ['ID', 'Name', 'Is Public'] @@ -489,33 +433,7 @@ def take_action(self, parsed_args): if parsed_args.default: data = [volume_client.volume_types.default()] else: - search_opts = {} - properties = {} - if parsed_args.properties: - properties.update(parsed_args.properties) - if parsed_args.multiattach: - properties['multiattach'] = ' True' - if parsed_args.cacheable: - properties['cacheable'] = ' True' - if parsed_args.replicated: - properties['replication_enabled'] = ' True' - if parsed_args.availability_zones: - properties['RESKEY:availability_zones'] = ','.join( - parsed_args.availability_zones - ) - if properties: - if volume_client.api_version < api_versions.APIVersion('3.52'): - msg = _( - "--os-volume-api-version 3.52 or greater is required " - "to use the '--property' option or any of the alias " - "options" - ) - raise exceptions.CommandError(msg) - - search_opts['extra_specs'] = properties - data = volume_client.volume_types.list( - search_opts=search_opts, is_public=parsed_args.is_public, ) diff --git a/openstackclient/volume/v3/volume_type.py b/openstackclient/volume/v3/volume_type.py new file mode 100644 index 000000000..96d7553d4 --- /dev/null +++ b/openstackclient/volume/v3/volume_type.py @@ -0,0 +1,967 @@ +# +# 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 Type action implementations""" + +import functools +import logging + +from cinderclient import api_versions +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 _ +from openstackclient.identity import common as identity_common + + +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 + + +def _set_encryption_type(volume_client, volume_type, parsed_args): + # update the existing encryption type + body = {} + for attr in ['provider', 'cipher', 'key_size', 'control_location']: + info = getattr(parsed_args, 'encryption_' + attr, None) + if info is not None: + body[attr] = info + try: + volume_client.volume_encryption_types.update(volume_type, body) + except Exception as e: + if type(e).__name__ == 'NotFound': + # create new encryption type + LOG.warning( + _( + "No existing encryption type found, creating " + "new encryption type for this volume type ..." + ) + ) + _create_encryption_type(volume_client, volume_type, parsed_args) + + +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( + "--description", + metavar="", + help=_("Volume type description"), + ) + public_group = parser.add_mutually_exclusive_group() + public_group.add_argument( + "--public", + action="store_true", + dest="is_public", + default=None, + help=_("Volume type is accessible to the public"), + ) + public_group.add_argument( + "--private", + action="store_false", + dest="is_public", + default=None, + help=_("Volume type is not accessible to the public"), + ) + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + dest='properties', + help=_( + 'Set a property on this volume type ' + '(repeat option to set multiple properties)' + ), + ) + parser.add_argument( + '--multiattach', + action='store_true', + default=False, + help=_( + "Enable multi-attach for this volume type " + "(this is an alias for '--property multiattach= True') " + "(requires driver support)" + ), + ) + parser.add_argument( + '--cacheable', + action='store_true', + default=False, + help=_( + "Enable caching for this volume type " + "(this is an alias for '--property cacheable= True') " + "(requires driver support)" + ), + ) + parser.add_argument( + '--replicated', + action='store_true', + default=False, + help=_( + "Enabled replication for this volume type " + "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(requires driver support)" + ), + ) + parser.add_argument( + '--availability-zone', + action='append', + dest='availability_zones', + help=_( + "Set an availability zone for this volume type " + "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(repeat option to set multiple availability zones)" + ), + ) + parser.add_argument( + '--project', + metavar='', + help=_( + "Allow to access private type (name or ID) " + "(must be used with --private option)" + ), + ) + identity_common.add_project_domain_option_to_parser(parser) + # 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): + identity_client = self.app.client_manager.identity + volume_client = self.app.client_manager.volume + + if parsed_args.project and parsed_args.is_public is not False: + msg = _("--project is only allowed with --private") + raise exceptions.CommandError(msg) + + kwargs = {} + + if parsed_args.is_public is not None: + kwargs['is_public'] = parsed_args.is_public + + volume_type = volume_client.volume_types.create( + parsed_args.name, + description=parsed_args.description, + **kwargs, + ) + volume_type._info.pop('extra_specs') + + if parsed_args.project: + try: + project_id = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + volume_client.volume_type_access.add_project_access( + volume_type.id, project_id + ) + except Exception as e: + msg = _( + "Failed to add project %(project)s access to " + "type: %(e)s" + ) + LOG.error(msg % {'project': parsed_args.project, 'e': e}) + + properties = {} + if parsed_args.properties: + properties.update(parsed_args.properties) + if parsed_args.multiattach: + properties['multiattach'] = ' True' + if parsed_args.cacheable: + properties['cacheable'] = ' True' + if parsed_args.replicated: + properties['replication_enabled'] = ' True' + if parsed_args.availability_zones: + properties['RESKEY:availability_zones'] = ','.join( + parsed_args.availability_zones + ) + if properties: + result = volume_type.set_keys(properties) + 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'), + ) + public_group = parser.add_mutually_exclusive_group() + public_group.add_argument( + "--default", + action='store_true', + default=False, + help=_('List the default volume type'), + ) + public_group.add_argument( + "--public", + action="store_true", + dest="is_public", + default=None, + help=_("List only public types"), + ) + public_group.add_argument( + "--private", + action="store_false", + dest="is_public", + default=None, + help=_("List only private types (admin only)"), + ) + parser.add_argument( + "--encryption-type", + action="store_true", + help=_( + "Display encryption information for each volume type " + "(admin only)" + ), + ) + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + dest='properties', + help=_( + 'Filter by a property on the volume types ' + '(repeat option to filter by multiple properties) ' + '(admin only except for user-visible extra specs) ' + '(supported by --os-volume-api-version 3.52 or above)' + ), + ) + parser.add_argument( + '--multiattach', + action='store_true', + default=False, + help=_( + "List only volume types with multi-attach enabled " + "(this is an alias for '--property multiattach= True') " + "(supported by --os-volume-api-version 3.52 or above)" + ), + ) + parser.add_argument( + '--cacheable', + action='store_true', + default=False, + help=_( + "List only volume types with caching enabled " + "(this is an alias for '--property cacheable= True') " + "(admin only) " + "(supported by --os-volume-api-version 3.52 or above)" + ), + ) + parser.add_argument( + '--replicated', + action='store_true', + default=False, + help=_( + "List only volume types with replication enabled " + "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(supported by --os-volume-api-version 3.52 or above)" + ), + ) + parser.add_argument( + '--availability-zone', + action='append', + dest='availability_zones', + help=_( + "List only volume types with this availability configured " + "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(repeat option to filter on multiple availability zones)" + ), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + + if parsed_args.long: + columns = [ + 'ID', + 'Name', + 'Is Public', + 'Description', + 'Extra Specs', + ] + column_headers = [ + 'ID', + 'Name', + 'Is Public', + 'Description', + 'Properties', + ] + else: + columns = ['ID', 'Name', 'Is Public'] + column_headers = ['ID', 'Name', 'Is Public'] + + if parsed_args.default: + data = [volume_client.volume_types.default()] + else: + search_opts = {} + properties = {} + if parsed_args.properties: + properties.update(parsed_args.properties) + if parsed_args.multiattach: + properties['multiattach'] = ' True' + if parsed_args.cacheable: + properties['cacheable'] = ' True' + if parsed_args.replicated: + properties['replication_enabled'] = ' True' + if parsed_args.availability_zones: + properties['RESKEY:availability_zones'] = ','.join( + parsed_args.availability_zones + ) + if properties: + if volume_client.api_version < api_versions.APIVersion('3.52'): + msg = _( + "--os-volume-api-version 3.52 or greater is required " + "to use the '--property' option or any of the alias " + "options" + ) + raise exceptions.CommandError(msg) + + search_opts['extra_specs'] = properties + + data = volume_client.volume_types.list( + search_opts=search_opts, + is_public=parsed_args.is_public, + ) + + 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( + '--name', + metavar='', + help=_('Set volume type name'), + ) + parser.add_argument( + '--description', + metavar='', + help=_('Set volume type description'), + ) + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + dest='properties', + help=_( + 'Set a property on this volume type ' + '(repeat option to set multiple properties)' + ), + ) + parser.add_argument( + '--multiattach', + action='store_true', + default=False, + help=_( + "Enable multi-attach for this volume type " + "(this is an alias for '--property multiattach= True') " + "(requires driver support)" + ), + ) + parser.add_argument( + '--cacheable', + action='store_true', + default=False, + help=_( + "Enable caching for this volume type " + "(this is an alias for '--property cacheable= True') " + "(requires driver support)" + ), + ) + parser.add_argument( + '--replicated', + action='store_true', + default=False, + help=_( + "Enabled replication for this volume type " + "(this is an alias for '--property replication_enabled= True') " # noqa: E501 + "(requires driver support)" + ), + ) + parser.add_argument( + '--availability-zone', + action='append', + dest='availability_zones', + help=_( + "Set an availability zone for this volume type " + "(this is an alias for '--property RESKEY:availability_zones:') " # noqa: E501 + "(repeat option to set multiple availability zones)" + ), + ) + parser.add_argument( + '--project', + metavar='', + help=_( + 'Set volume type access to project (name or ID) ' + '(admin only)' + ), + ) + public_group = parser.add_mutually_exclusive_group() + public_group.add_argument( + '--public', + action='store_true', + dest='is_public', + default=None, + help=_('Volume type is accessible to the public'), + ) + public_group.add_argument( + '--private', + action='store_false', + dest='is_public', + default=None, + help=_("Volume type is not accessible to the public"), + ) + identity_common.add_project_domain_option_to_parser(parser) + # 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 for the first time. 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 for the ' + 'first time. 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 + identity_client = self.app.client_manager.identity + + volume_type = utils.find_resource( + volume_client.volume_types, + parsed_args.volume_type, + ) + + result = 0 + kwargs = {} + + if parsed_args.name: + kwargs['name'] = parsed_args.name + + if parsed_args.description: + kwargs['description'] = parsed_args.description + + if parsed_args.is_public is not None: + kwargs['is_public'] = parsed_args.is_public + + if kwargs: + try: + volume_client.volume_types.update(volume_type.id, **kwargs) + except Exception as e: + LOG.error( + _( + "Failed to update volume type name or" + " description: %s" + ), + e, + ) + result += 1 + + properties = {} + + properties = {} + if parsed_args.properties: + properties.update(parsed_args.properties) + if parsed_args.multiattach: + properties['multiattach'] = ' True' + if parsed_args.cacheable: + properties['cacheable'] = ' True' + if parsed_args.replicated: + properties['replication_enabled'] = ' True' + if parsed_args.availability_zones: + properties['RESKEY:availability_zones'] = ','.join( + parsed_args.availability_zones + ) + if properties: + try: + volume_type.set_keys(properties) + except Exception as e: + LOG.error(_("Failed to set volume type properties: %s"), e) + result += 1 + + if parsed_args.project: + project_info = None + try: + project_info = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ) + + volume_client.volume_type_access.add_project_access( + volume_type.id, project_info.id + ) + except Exception as e: + LOG.error( + _("Failed to set volume type access to " "project: %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: + _set_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}) + access_project_ids = None + if not volume_type.is_public: + try: + volume_type_access = volume_client.volume_type_access.list( + volume_type.id + ) + project_ids = [ + utils.get_field(item, 'project_id') + for item in volume_type_access + ] + # TODO(Rui Chen): This format list case can be removed after + # patch https://review.opendev.org/#/c/330223/ merged. + access_project_ids = format_columns.ListColumn(project_ids) + except Exception as e: + msg = _( + 'Failed to get access project list for volume type ' + '%(type)s: %(e)s' + ) + LOG.error(msg % {'type': volume_type.id, 'e': e}) + volume_type._info.update({'access_project_ids': access_project_ids}) + 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', + dest='properties', + help=_( + 'Remove a property from this volume type ' + '(repeat option to remove multiple properties)' + ), + ) + parser.add_argument( + '--project', + metavar='', + help=_( + 'Removes volume type access to project (name or ID) ' + '(admin only)' + ), + ) + identity_common.add_project_domain_option_to_parser(parser) + 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 + identity_client = self.app.client_manager.identity + + volume_type = utils.find_resource( + volume_client.volume_types, + parsed_args.volume_type, + ) + + result = 0 + if parsed_args.properties: + try: + volume_type.unset_keys(parsed_args.properties) + except Exception as e: + LOG.error(_("Failed to unset volume type properties: %s"), e) + result += 1 + + if parsed_args.project: + project_info = None + try: + project_info = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ) + + volume_client.volume_type_access.remove_project_access( + volume_type.id, project_info.id + ) + except Exception as e: + LOG.error( + _( + "Failed to remove volume type access from " + "project: %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/setup.cfg b/setup.cfg index b6f8188df..04e4c9b9e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -831,12 +831,12 @@ openstack.volume.v3 = 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_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 From 3f624295ecbb62a1d3deaa3e54a03258f9cb3e9a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 7 May 2024 16:49:09 +0100 Subject: [PATCH 137/403] volume: Add v3-specific volume transfer module This makes testing easier. Change-Id: I515aa54750439a6c4ad5b16cffe004ba1c7cf5b2 Signed-off-by: Stephen Finucane --- .../volume/v2/test_volume_transfer_request.py | 44 -- openstackclient/tests/unit/volume/v3/fakes.py | 65 +++ .../volume/v3/test_volume_transfer_request.py | 426 ++++++++++++++++++ .../volume/v2/volume_transfer_request.py | 32 -- .../volume/v3/volume_transfer_request.py | 233 ++++++++++ setup.cfg | 10 +- 6 files changed, 729 insertions(+), 81 deletions(-) create mode 100644 openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py create mode 100644 openstackclient/volume/v3/volume_transfer_request.py 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 bedcadd66..1885a1f5e 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py @@ -15,7 +15,6 @@ from unittest import mock from unittest.mock import call -from cinderclient import api_versions from osc_lib import exceptions from osc_lib import utils @@ -176,49 +175,6 @@ def test_transfer_create_with_name(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - def test_transfer_create_with_no_snapshots(self): - self.volume_client.api_version = api_versions.APIVersion('3.55') - - arglist = [ - '--no-snapshots', - self.volume.id, - ] - verifylist = [ - ('name', None), - ('snapshots', False), - ('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, no_snapshots=True - ) - self.assertEqual(self.columns, columns) - self.assertEqual(self.data, data) - - def test_transfer_create_pre_v355(self): - self.volume_client.api_version = api_versions.APIVersion('3.54') - - arglist = [ - '--no-snapshots', - self.volume.id, - ] - verifylist = [ - ('name', None), - ('snapshots', False), - ('volume', self.volume.id), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - exc = self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - self.assertIn( - '--os-volume-api-version 3.55 or greater is required', str(exc) - ) - class TestTransferDelete(TestTransfer): volume_transfers = volume_fakes.create_transfers(count=2) diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index b014efc43..0868de142 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -62,6 +62,8 @@ def __init__(self, **kwargs): self.resource_filters.resource_class = fakes.FakeResource(None, {}) self.restores = mock.Mock() self.restores.resource_class = fakes.FakeResource(None, {}) + self.transfers = mock.Mock() + self.transfers.resource_class = fakes.FakeResource(None, {}) self.volume_encryption_types = mock.Mock() self.volume_encryption_types.resource_class = fakes.FakeResource( None, {} @@ -438,6 +440,69 @@ def create_resource_filters(attrs=None, count=2): return resource_filters +def create_one_transfer(attrs=None): + """Create a fake transfer. + + :param dict 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 dict 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_type_access(attrs=None): """Create a fake volume type access for project. diff --git a/openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py b/openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py new file mode 100644 index 000000000..078124788 --- /dev/null +++ b/openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py @@ -0,0 +1,426 @@ +# +# 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 cinderclient import api_versions +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.tests.unit import utils as test_utils +from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes +from openstackclient.volume.v3 import volume_transfer_request + + +class TestTransfer(volume_fakes.TestVolume): + 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), + ] + + self.assertRaises( + test_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + verifylist, + ) + + +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) + + def test_transfer_create_with_no_snapshots(self): + self.volume_client.api_version = api_versions.APIVersion('3.55') + + arglist = [ + '--no-snapshots', + self.volume.id, + ] + verifylist = [ + ('name', None), + ('snapshots', False), + ('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, no_snapshots=True + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_transfer_create_pre_v355(self): + self.volume_client.api_version = api_versions.APIVersion('3.54') + + arglist = [ + '--no-snapshots', + self.volume.id, + ] + verifylist = [ + ('name', None), + ('snapshots', False), + ('volume', self.volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + self.assertIn( + '--os-volume-api-version 3.55 or greater is required', str(exc) + ) + + +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/volume/v2/volume_transfer_request.py b/openstackclient/volume/v2/volume_transfer_request.py index d531efc6b..18094b626 100644 --- a/openstackclient/volume/v2/volume_transfer_request.py +++ b/openstackclient/volume/v2/volume_transfer_request.py @@ -16,7 +16,6 @@ import logging -from cinderclient import api_versions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -76,25 +75,6 @@ def get_parser(self, prog_name): metavar="", help=_('New transfer request name (default to None)'), ) - parser.add_argument( - '--snapshots', - action='store_true', - dest='snapshots', - help=_( - 'Allow transfer volumes without snapshots (default) ' - '(supported by --os-volume-api-version 3.55 or later)' - ), - default=None, - ) - parser.add_argument( - '--no-snapshots', - action='store_false', - dest='snapshots', - help=_( - 'Disallow transfer volumes without snapshots ' - '(supported by --os-volume-api-version 3.55 or later)' - ), - ) parser.add_argument( 'volume', metavar="", @@ -107,18 +87,6 @@ def take_action(self, parsed_args): kwargs = {} - if parsed_args.snapshots is not None: - if volume_client.api_version < api_versions.APIVersion('3.55'): - msg = _( - "--os-volume-api-version 3.55 or greater is required to " - "support the '--(no-)snapshots' option" - ) - raise exceptions.CommandError(msg) - - # unfortunately this option is negative so we have to reverse - # things - kwargs['no_snapshots'] = not parsed_args.snapshots - volume_id = utils.find_resource( volume_client.volumes, parsed_args.volume, diff --git a/openstackclient/volume/v3/volume_transfer_request.py b/openstackclient/volume/v3/volume_transfer_request.py new file mode 100644 index 000000000..b7add1228 --- /dev/null +++ b/openstackclient/volume/v3/volume_transfer_request.py @@ -0,0 +1,233 @@ +# +# 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 transfer action implementations""" + +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.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="", + required=True, + 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 + + 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( + '--snapshots', + action='store_true', + dest='snapshots', + help=_( + 'Allow transfer volumes without snapshots (default) ' + '(supported by --os-volume-api-version 3.55 or later)' + ), + default=None, + ) + parser.add_argument( + '--no-snapshots', + action='store_false', + dest='snapshots', + help=_( + 'Disallow transfer volumes without snapshots ' + '(supported by --os-volume-api-version 3.55 or later)' + ), + ) + 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 + + kwargs = {} + + if parsed_args.snapshots is not None: + if volume_client.api_version < api_versions.APIVersion('3.55'): + msg = _( + "--os-volume-api-version 3.55 or greater is required to " + "support the '--(no-)snapshots' option" + ) + raise exceptions.CommandError(msg) + + # unfortunately this option is negative so we have to reverse + # things + kwargs['no_snapshots'] = not parsed_args.snapshots + + volume_id = utils.find_resource( + volume_client.volumes, + parsed_args.volume, + ).id + volume_transfer_request = volume_client.transfers.create( + volume_id, + parsed_args.name, + **kwargs, + ) + 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/setup.cfg b/setup.cfg index 04e4c9b9e..d824afb15 100644 --- a/setup.cfg +++ b/setup.cfg @@ -850,11 +850,11 @@ openstack.volume.v3 = volume_service_list = openstackclient.volume.v3.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 + 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 From 88fffeabd796e76250518d332d142e7cb46cebd2 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 7 May 2024 13:51:33 +0100 Subject: [PATCH 138/403] tests: Add ability to configure fake server API version This beats the horrible mess of mocks we have created for ourselves. Change-Id: I8af3ce0a0b10f52e2124ec86f306327ff3474982 Signed-off-by: Stephen Finucane --- .../tests/unit/compute/v2/fakes.py | 24 +++++++++++++++++-- openstackclient/tests/unit/volume/v2/fakes.py | 22 +++++++++++++++++ openstackclient/tests/unit/volume/v3/fakes.py | 23 ++++++++++++++++++ .../volume/v3/test_block_storage_manage.py | 3 +-- .../tests/unit/volume/v3/test_service.py | 2 +- 5 files changed, 69 insertions(+), 5 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index f3042f32b..614bd9028 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -15,9 +15,11 @@ import copy import random +import re from unittest import mock import uuid +from keystoneauth1 import discover from novaclient import api_versions from openstack.compute.v2 import _proxy from openstack.compute.v2 import aggregate as _aggregate @@ -143,8 +145,6 @@ def __init__(self, **kwargs): self.management_url = kwargs['endpoint'] - self.api_version = api_versions.APIVersion('2.1') - class FakeClientMixin: def setUp(self): @@ -169,6 +169,26 @@ def setUp(self): self.compute_sdk_client = ( self.app.client_manager.sdk_connection.compute ) + self.set_compute_api_version() # default to the lowest + + def set_compute_api_version(self, version: str = '2.1'): + """Set a fake server version. + + :param version: The fake microversion to "support". This should be a + string of format '2.xx'. + :returns: None + """ + assert re.match(r'2.\d+', version) + + self.compute_client.api_version = api_versions.APIVersion(version) + + self.compute_sdk_client.default_microversion = version + self.compute_sdk_client.get_endpoint_data.return_value = ( + discover.EndpointData( + min_microversion='2.1', # nova has not bumped this yet + max_microversion=version, + ) + ) class TestComputev2( diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index 1c3a6864e..9ba3b2bf5 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -14,11 +14,13 @@ import copy import random +import typing as ty from unittest import mock import uuid # FIXME(stephenfin): We are using v3 resource versions despite being v2 fakes from cinderclient import api_versions +from keystoneauth1 import discover from openstack.block_storage.v2 import _proxy as block_storage_v2_proxy from openstack.block_storage.v2 import backup as _backup from openstack.block_storage.v3 import capabilities as _capabilities @@ -105,6 +107,26 @@ def setUp(self): spec=block_storage_v2_proxy.Proxy, ) 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): + """Set a fake block storage API version. + + :param version: The fake microversion to "support". This must be None + since cinder v2 didn't support microversions. + :returns: None + """ + assert version is None + + self.volume_client.api_version = None + + self.volume_sdk_client.default_microversion = None + self.volume_sdk_client.get_endpoint_data.return_value = ( + discover.EndpointData( + min_microversion=None, + max_microversion=None, + ) + ) class TestVolume( diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index 0868de142..925f2f773 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -12,10 +12,12 @@ import copy import random +import re from unittest import mock import uuid from cinderclient import api_versions +from keystoneauth1 import discover from openstack.block_storage.v3 import _proxy from openstack.block_storage.v3 import availability_zone as _availability_zone from openstack.block_storage.v3 import backup as _backup @@ -97,6 +99,26 @@ def setUp(self): spec=_proxy.Proxy, ) 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: str = '3.0'): + """Set a fake block storage API version. + + :param version: The fake microversion to "support". This should be a + string of format '3.xx'. + :returns: None + """ + assert re.match(r'3.\d+', version) + + self.volume_client.api_version = api_versions.APIVersion(version) + + self.volume_sdk_client.default_microversion = version + self.volume_sdk_client.get_endpoint_data.return_value = ( + discover.EndpointData( + min_microversion='3.0', # cinder has not bumped this yet + max_microversion=version, + ) + ) class TestVolume( @@ -126,6 +148,7 @@ def setUp(self): # TODO(stephenfin): Check if the responses are actually the same create_one_snapshot = volume_v2_fakes.create_one_snapshot +create_one_service = volume_v2_fakes.create_one_service def create_one_availability_zone(attrs=None): diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py b/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py index 54f484815..ca892f891 100644 --- a/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py +++ b/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py @@ -16,12 +16,11 @@ from osc_lib import exceptions from openstackclient.tests.unit import utils as tests_utils -from openstackclient.tests.unit.volume.v2 import fakes as v2_volume_fakes from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes from openstackclient.volume.v3 import block_storage_manage -class TestBlockStorageManage(v2_volume_fakes.TestVolume): +class TestBlockStorageManage(volume_fakes.TestVolume): def setUp(self): super().setUp() diff --git a/openstackclient/tests/unit/volume/v3/test_service.py b/openstackclient/tests/unit/volume/v3/test_service.py index 92f4239bc..333359551 100644 --- a/openstackclient/tests/unit/volume/v3/test_service.py +++ b/openstackclient/tests/unit/volume/v3/test_service.py @@ -14,7 +14,7 @@ from cinderclient import api_versions -from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes +from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes from openstackclient.volume.v3 import service From 402327f2e4859aef95da677fcf34204b3cc8744e Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 7 May 2024 16:54:40 +0100 Subject: [PATCH 139/403] tests: Migrate to 'set_xxx_api_version' helpers Clean things up somewhat. Change-Id: I868f496fc8285a28e8fd551377f2ae6228051d19 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 6 +- .../tests/unit/compute/v2/test_aggregate.py | 16 +- .../tests/unit/compute/v2/test_flavor.py | 111 +++-- .../tests/unit/compute/v2/test_hypervisor.py | 55 ++- .../tests/unit/compute/v2/test_keypair.py | 89 ++-- .../tests/unit/compute/v2/test_server.py | 379 +++++++----------- .../unit/compute/v2/test_server_event.py | 51 +-- .../unit/compute/v2/test_server_group.py | 57 ++- .../unit/compute/v2/test_server_migration.py | 107 ++--- .../unit/compute/v2/test_server_volume.py | 51 +-- .../tests/unit/compute/v2/test_service.py | 80 ++-- .../volume/v3/test_block_storage_cleanup.py | 5 +- .../volume/v3/test_block_storage_cluster.py | 21 +- .../volume/v3/test_block_storage_log_level.py | 16 +- .../volume/v3/test_block_storage_manage.py | 39 +- .../v3/test_block_storage_resource_filter.py | 38 +- .../tests/unit/volume/v3/test_volume.py | 49 +-- .../unit/volume/v3/test_volume_attachment.py | 29 +- .../unit/volume/v3/test_volume_backup.py | 64 +-- .../tests/unit/volume/v3/test_volume_group.py | 39 +- .../volume/v3/test_volume_group_snapshot.py | 48 +-- .../unit/volume/v3/test_volume_group_type.py | 27 +- .../unit/volume/v3/test_volume_message.py | 19 +- .../volume/v3/test_volume_transfer_request.py | 5 +- .../tests/unit/volume/v3/test_volume_type.py | 9 +- 25 files changed, 578 insertions(+), 832 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index e7a135e06..42caafead 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -4418,7 +4418,7 @@ def take_action(self, parsed_args): ) if parsed_args.description: - if server.api_version < api_versions.APIVersion("2.19"): + if compute_client.api_version < api_versions.APIVersion("2.19"): msg = _( '--os-compute-api-version 2.19 or greater is required to ' 'support the --description option' @@ -4426,7 +4426,7 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) if parsed_args.tags: - if server.api_version < api_versions.APIVersion('2.26'): + if compute_client.api_version < api_versions.APIVersion('2.26'): msg = _( '--os-compute-api-version 2.26 or greater is required to ' 'support the --tag option' @@ -4434,7 +4434,7 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) if parsed_args.hostname: - if server.api_version < api_versions.APIVersion('2.90'): + if compute_client.api_version < api_versions.APIVersion('2.90'): msg = _( '--os-compute-api-version 2.90 or greater is required to ' 'support the --hostname option' diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index a6709f708..6cfe5bc79 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -17,7 +17,6 @@ from unittest.mock import call from openstack import exceptions as sdk_exceptions -from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib import exceptions @@ -608,8 +607,9 @@ def setUp(self): self.cmd = aggregate.CacheImageForAggregate(self.app, None) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_aggregate_not_supported(self, sm_mock): + def test_aggregate_cache_pre_v281(self): + self.set_compute_api_version('2.80') + arglist = ['ag1', 'im1'] verifylist = [ ('aggregate', 'ag1'), @@ -620,8 +620,9 @@ def test_aggregate_not_supported(self, sm_mock): exceptions.CommandError, self.cmd.take_action, parsed_args ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_aggregate_add_single_image(self, sm_mock): + def test_aggregate_cache_add_single_image(self): + self.set_compute_api_version('2.81') + arglist = ['ag1', 'im1'] verifylist = [ ('aggregate', 'ag1'), @@ -636,8 +637,9 @@ def test_aggregate_add_single_image(self, sm_mock): self.fake_ag.id, [self.images[0].id] ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_aggregate_add_multiple_images(self, sm_mock): + def test_aggregate_cache_add_multiple_images(self): + self.set_compute_api_version('2.81') + arglist = [ 'ag1', 'im1', diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py index a6e302c1f..1a93b5fc8 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -16,7 +16,6 @@ from openstack.compute.v2 import flavor as _flavor from openstack import exceptions as sdk_exceptions -from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib import exceptions @@ -118,6 +117,8 @@ def test_flavor_create_default_options(self): self.assertCountEqual(self.data, data) def test_flavor_create_all_options(self): + self.set_compute_api_version('2.55') + arglist = [ '--id', self.flavor.id, @@ -184,22 +185,19 @@ def test_flavor_create_all_options(self): expected_flavor ) - with mock.patch.object( - sdk_utils, 'supports_microversion', return_value=True - ): - 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( - create_flavor, props - ) - self.compute_sdk_client.get_flavor_access.assert_not_called() + 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( + create_flavor, props + ) + self.compute_sdk_client.get_flavor_access.assert_not_called() self.assertEqual(self.columns, columns) self.assertCountEqual(tuple(cmp_data), data) def test_flavor_create_other_options(self): + self.set_compute_api_version('2.55') + self.flavor.is_public = False arglist = [ '--id', @@ -272,10 +270,8 @@ def test_flavor_create_other_options(self): expected_flavor ) - with mock.patch.object( - sdk_utils, 'supports_microversion', return_value=True - ): - columns, data = self.cmd.take_action(parsed_args) + 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.flavor.id, @@ -314,7 +310,9 @@ def test_flavor_create_no_options(self): verifylist, ) - def test_flavor_create_with_description_api_newer(self): + def test_flavor_create_with_description(self): + self.set_compute_api_version('2.55') + arglist = [ '--id', self.flavor.id, @@ -348,10 +346,8 @@ def test_flavor_create_with_description_api_newer(self): ('name', self.flavor.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object( - sdk_utils, 'supports_microversion', return_value=True - ): - columns, data = self.cmd.take_action(parsed_args) + + columns, data = self.cmd.take_action(parsed_args) args = { 'name': self.flavor.name, @@ -371,7 +367,9 @@ def test_flavor_create_with_description_api_newer(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data_private, data) - def test_flavor_create_with_description_api_older(self): + def test_flavor_create_with_description_pre_v255(self): + self.set_compute_api_version('2.54') + arglist = [ '--id', self.flavor.id, @@ -391,12 +389,9 @@ def test_flavor_create_with_description_api_older(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object( - sdk_utils, 'supports_microversion', return_value=False - ): - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) class TestFlavorDelete(TestFlavor): @@ -887,7 +882,9 @@ def test_flavor_set_nothing(self): self.compute_sdk_client.flavor_add_tenant_access.assert_not_called() self.assertIsNone(result) - def test_flavor_set_description_api_newer(self): + def test_flavor_set_description(self): + self.set_compute_api_version('2.55') + arglist = [ '--description', 'description', @@ -899,16 +896,15 @@ def test_flavor_set_description_api_newer(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object( - sdk_utils, 'supports_microversion', return_value=True - ): - result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_flavor.assert_called_with( - flavor=self.flavor.id, description='description' - ) - self.assertIsNone(result) + result = self.cmd.take_action(parsed_args) + self.compute_sdk_client.update_flavor.assert_called_with( + flavor=self.flavor.id, description='description' + ) + self.assertIsNone(result) + + def test_flavor_set_description_pre_v254(self): + self.set_compute_api_version('2.54') - def test_flavor_set_description_api_older(self): arglist = [ '--description', 'description', @@ -920,14 +916,13 @@ def test_flavor_set_description_api_older(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object( - sdk_utils, 'supports_microversion', return_value=False - ): - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) + + def test_flavor_set_description_using_name(self): + self.set_compute_api_version('2.55') - def test_flavor_set_description_using_name_api_newer(self): arglist = [ '--description', 'description', @@ -939,16 +934,15 @@ def test_flavor_set_description_using_name_api_newer(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object( - sdk_utils, 'supports_microversion', return_value=True - ): - result = self.cmd.take_action(parsed_args) - self.compute_sdk_client.update_flavor.assert_called_with( - flavor=self.flavor.id, description='description' - ) - self.assertIsNone(result) + result = self.cmd.take_action(parsed_args) + self.compute_sdk_client.update_flavor.assert_called_with( + flavor=self.flavor.id, description='description' + ) + self.assertIsNone(result) + + def test_flavor_set_description_using_name_pre_v255(self): + self.set_compute_api_version('2.54') - def test_flavor_set_description_using_name_api_older(self): arglist = [ '--description', 'description', @@ -960,12 +954,9 @@ def test_flavor_set_description_using_name_api_older(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object( - sdk_utils, 'supports_microversion', return_value=False - ): - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) class TestFlavorShow(TestFlavor): diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor.py b/openstackclient/tests/unit/compute/v2/test_hypervisor.py index 0499ae59e..ba3d58e8d 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor.py @@ -11,13 +11,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 json -from unittest import mock from novaclient import exceptions as nova_exceptions -from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib import exceptions @@ -164,10 +161,7 @@ def test_hypervisor_list_matching_option_not_found(self): exceptions.NotFound, self.cmd.take_action, parsed_args ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_hypervisor_list_with_matching_and_pagination_options( - self, sm_mock - ): + def test_hypervisor_list_with_matching_and_pagination_options(self): arglist = [ '--matching', self.hypervisors[0].name, @@ -191,8 +185,7 @@ def test_hypervisor_list_with_matching_and_pagination_options( '--matching is not compatible with --marker or --limit', str(ex) ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_hypervisor_list_long_option(self, sm_mock): + def test_hypervisor_list_long_option(self): arglist = [ '--long', ] @@ -210,8 +203,9 @@ def test_hypervisor_list_long_option(self, sm_mock): self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, tuple(data)) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_hypervisor_list_with_limit(self, sm_mock): + def test_hypervisor_list_with_limit(self): + self.set_compute_api_version('2.33') + arglist = [ '--limit', '1', @@ -227,8 +221,9 @@ def test_hypervisor_list_with_limit(self, sm_mock): limit=1, details=True ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_hypervisor_list_with_limit_pre_v233(self, sm_mock): + def test_hypervisor_list_with_limit_pre_v233(self): + self.set_compute_api_version('2.32') + arglist = [ '--limit', '1', @@ -246,8 +241,9 @@ def test_hypervisor_list_with_limit_pre_v233(self, sm_mock): '--os-compute-api-version 2.33 or greater is required', str(ex) ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_hypervisor_list_with_marker(self, sm_mock): + def test_hypervisor_list_with_marker(self): + self.set_compute_api_version('2.33') + arglist = [ '--marker', 'test_hyp', @@ -263,8 +259,9 @@ def test_hypervisor_list_with_marker(self, sm_mock): marker='test_hyp', details=True ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_hypervisor_list_with_marker_pre_v233(self, sm_mock): + def test_hypervisor_list_with_marker_pre_v233(self): + self.set_compute_api_version('2.32') + arglist = [ '--marker', 'test_hyp', @@ -413,8 +410,9 @@ def setUp(self): # Get the command object to test self.cmd = hypervisor.ShowHypervisor(self.app, None) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_hypervisor_show(self, sm_mock): + def test_hypervisor_show(self): + self.set_compute_api_version('2.88') + arglist = [ self.hypervisor.name, ] @@ -431,10 +429,9 @@ def test_hypervisor_show(self, sm_mock): self.assertEqual(self.columns_v288, columns) self.assertCountEqual(self.data_v288, data) - @mock.patch.object( - sdk_utils, 'supports_microversion', side_effect=[False, True, False] - ) - def test_hypervisor_show_pre_v288(self, sm_mock): + def test_hypervisor_show_pre_v288(self): + self.set_compute_api_version('2.87') + arglist = [ self.hypervisor.name, ] @@ -451,8 +448,9 @@ def test_hypervisor_show_pre_v288(self, sm_mock): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_hypervisor_show_pre_v228(self, sm_mock): + def test_hypervisor_show_pre_v228(self): + self.set_compute_api_version('2.27') + # before microversion 2.28, nova returned a stringified version of this # field self.hypervisor.cpu_info = json.dumps(self.hypervisor.cpu_info) @@ -474,10 +472,9 @@ def test_hypervisor_show_pre_v228(self, sm_mock): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) - @mock.patch.object( - sdk_utils, 'supports_microversion', side_effect=[False, True, False] - ) - def test_hypervisor_show_uptime_not_implemented(self, sm_mock): + def test_hypervisor_show_uptime_not_implemented(self): + self.set_compute_api_version('2.87') + arglist = [ self.hypervisor.name, ] diff --git a/openstackclient/tests/unit/compute/v2/test_keypair.py b/openstackclient/tests/unit/compute/v2/test_keypair.py index b7d46aebe..d6be7d0d6 100644 --- a/openstackclient/tests/unit/compute/v2/test_keypair.py +++ b/openstackclient/tests/unit/compute/v2/test_keypair.py @@ -17,7 +17,6 @@ from unittest.mock import call import uuid -from openstack import utils as sdk_utils from osc_lib import exceptions from openstackclient.compute.v2 import keypair @@ -175,8 +174,9 @@ def test_keypair_create_private_key(self, mock_generate): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_keypair_create_with_key_type(self, sm_mock): + 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 @@ -220,8 +220,9 @@ def test_keypair_create_with_key_type(self, sm_mock): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_keypair_create_with_key_type_pre_v22(self, sm_mock): + def test_keypair_create_with_key_type_pre_v22(self): + self.set_compute_api_version('2.1') + for key_type in ['x509', 'ssh']: arglist = [ '--public-key', @@ -257,8 +258,9 @@ def test_keypair_create_with_key_type_pre_v22(self, sm_mock): '_generate_keypair', return_value=keypair.Keypair('private', 'public'), ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_key_pair_create_with_user(self, sm_mock, mock_generate): + def test_key_pair_create_with_user(self, mock_generate): + self.set_compute_api_version('2.10') + arglist = [ '--user', identity_fakes.user_name, @@ -281,8 +283,9 @@ def test_key_pair_create_with_user(self, sm_mock, mock_generate): self.assertEqual({}, columns) self.assertEqual({}, data) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_key_pair_create_with_user_pre_v210(self, sm_mock): + def test_key_pair_create_with_user_pre_v210(self): + self.set_compute_api_version('2.9') + arglist = [ '--user', identity_fakes.user_name, @@ -368,8 +371,9 @@ def test_delete_multiple_keypairs_with_exception(self): calls.append(call(k, ignore_missing=False)) self.compute_sdk_client.delete_keypair.assert_has_calls(calls) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_keypair_delete_with_user(self, sm_mock): + def test_keypair_delete_with_user(self): + self.set_compute_api_version('2.10') + arglist = ['--user', identity_fakes.user_name, self.keypairs[0].name] verifylist = [ ('user', identity_fakes.user_name), @@ -386,8 +390,9 @@ def test_keypair_delete_with_user(self, sm_mock): ignore_missing=False, ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_keypair_delete_with_user_pre_v210(self, sm_mock): + 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] verifylist = [ ('user', identity_fakes.user_name), @@ -415,8 +420,7 @@ def setUp(self): # Get the command object to test self.cmd = keypair.ListKeypair(self.app, None) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_keypair_list_no_options(self, sm_mock): + def test_keypair_list_no_options(self): arglist = [] verifylist = [] @@ -437,8 +441,9 @@ def test_keypair_list_no_options(self, sm_mock): tuple(data), ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_keypair_list_v22(self, sm_mock): + def test_keypair_list_v22(self): + self.set_compute_api_version('2.22') + arglist = [] verifylist = [] @@ -465,8 +470,9 @@ def test_keypair_list_v22(self, sm_mock): tuple(data), ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_keypair_list_with_user(self, sm_mock): + def test_keypair_list_with_user(self): + self.set_compute_api_version('2.35') + users_mock = self.identity_client.users users_mock.reset_mock() users_mock.get.return_value = fakes.FakeResource( @@ -503,8 +509,9 @@ def test_keypair_list_with_user(self, sm_mock): tuple(data), ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_keypair_list_with_user_pre_v210(self, sm_mock): + def test_keypair_list_with_user_pre_v210(self): + self.set_compute_api_version('2.9') + arglist = [ '--user', identity_fakes.user_name, @@ -521,8 +528,9 @@ def test_keypair_list_with_user_pre_v210(self, sm_mock): '--os-compute-api-version 2.10 or greater is required', str(ex) ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_keypair_list_with_project(self, sm_mock): + def test_keypair_list_with_project(self): + self.set_compute_api_version('2.35') + projects_mock = self.identity_client.tenants projects_mock.reset_mock() projects_mock.get.return_value = fakes.FakeResource( @@ -565,8 +573,9 @@ def test_keypair_list_with_project(self, sm_mock): tuple(data), ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_keypair_list_with_project_pre_v210(self, sm_mock): + 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)] @@ -594,10 +603,9 @@ def test_keypair_list_conflicting_user_options(self): None, ) - @mock.patch.object( - sdk_utils, 'supports_microversion', new=mock.Mock(return_value=True) - ) def test_keypair_list_with_limit(self): + self.set_compute_api_version('2.35') + arglist = [ '--limit', '1', @@ -611,10 +619,9 @@ def test_keypair_list_with_limit(self): self.compute_sdk_client.keypairs.assert_called_with(limit=1) - @mock.patch.object( - sdk_utils, 'supports_microversion', new=mock.Mock(return_value=False) - ) def test_keypair_list_with_limit_pre_v235(self): + self.set_compute_api_version('2.34') + arglist = [ '--limit', '1', @@ -632,10 +639,9 @@ def test_keypair_list_with_limit_pre_v235(self): '--os-compute-api-version 2.35 or greater is required', str(ex) ) - @mock.patch.object( - sdk_utils, 'supports_microversion', new=mock.Mock(return_value=True) - ) def test_keypair_list_with_marker(self): + self.set_compute_api_version('2.35') + arglist = [ '--marker', 'test_kp', @@ -649,10 +655,9 @@ def test_keypair_list_with_marker(self): self.compute_sdk_client.keypairs.assert_called_with(marker='test_kp') - @mock.patch.object( - sdk_utils, 'supports_microversion', new=mock.Mock(return_value=False) - ) def test_keypair_list_with_marker_pre_v235(self): + self.set_compute_api_version('2.34') + arglist = [ '--marker', 'test_kp', @@ -743,8 +748,9 @@ def test_keypair_show_public(self): self.assertEqual({}, columns) self.assertEqual({}, data) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_keypair_show_with_user(self, sm_mock): + def test_keypair_show_with_user(self): + self.set_compute_api_version('2.10') + self.keypair = compute_fakes.create_one_keypair() self.compute_sdk_client.find_keypair.return_value = self.keypair @@ -782,8 +788,9 @@ def test_keypair_show_with_user(self, sm_mock): self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_keypair_show_with_user_pre_v210(self, sm_mock): + def test_keypair_show_with_user_pre_v210(self): + self.set_compute_api_version('2.9') + self.keypair = compute_fakes.create_one_keypair() arglist = [ '--user', diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index eac3b7ece..441d69f5c 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -21,9 +21,7 @@ from unittest.mock import call import iso8601 -from novaclient import api_versions from openstack import exceptions as sdk_exceptions -from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib import utils as common_utils @@ -99,24 +97,6 @@ def setUp(self): # Set object methods to be tested. Could be overwritten in subclass. self.methods = {} - patcher = mock.patch.object( - sdk_utils, 'supports_microversion', return_value=True - ) - self.addCleanup(patcher.stop) - self.supports_microversion_mock = patcher.start() - self._set_mock_microversion( - self.compute_client.api_version.get_string() - ) - - def _set_mock_microversion(self, mock_v): - """Set a specific microversion for the mock supports_microversion().""" - self.supports_microversion_mock.reset_mock(return_value=True) - - self.supports_microversion_mock.side_effect = ( - lambda _, v: api_versions.APIVersion(v) - <= api_versions.APIVersion(mock_v) - ) - def setup_servers_mock(self, count): # If we are creating more than one server, make one of them # boot-from-volume @@ -185,9 +165,8 @@ def setUp(self): self.find_network = mock.Mock() self.app.client_manager.network.find_network = self.find_network - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_add_fixed_ip_pre_v249_with_tag(self, sm_mock): - sm_mock.side_effect = [False, True] + def test_server_add_fixed_ip_pre_v249_with_tag(self): + self.set_compute_api_version('2.48') servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() @@ -220,9 +199,8 @@ def test_server_add_fixed_ip_pre_v249_with_tag(self, sm_mock): '--os-compute-api-version 2.49 or greater is required', str(ex) ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_add_fixed_ip(self, sm_mock): - sm_mock.side_effect = [True, False] + def test_server_add_fixed_ip(self): + self.set_compute_api_version('2.49') servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() @@ -268,9 +246,8 @@ def test_server_add_fixed_ip(self, sm_mock): servers[0].id, net_id=network['id'] ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_add_fixed_ip_with_fixed_ip(self, sm_mock): - sm_mock.side_effect = [True, True] + def test_server_add_fixed_ip_with_fixed_ip(self): + self.set_compute_api_version('2.49') servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() @@ -324,9 +301,8 @@ def test_server_add_fixed_ip_with_fixed_ip(self, sm_mock): fixed_ips=[{'ip_address': '5.6.7.8'}], ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_add_fixed_ip_with_tag(self, sm_mock): - sm_mock.side_effect = [True, True, True] + def test_server_add_fixed_ip_with_tag(self): + self.set_compute_api_version('2.49') servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() @@ -386,9 +362,8 @@ def test_server_add_fixed_ip_with_tag(self, sm_mock): tag='tag1', ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_add_fixed_ip_with_fixed_ip_with_tag(self, sm_mock): - sm_mock.side_effect = [True, True] + def test_server_add_fixed_ip_with_fixed_ip_with_tag(self): + self.set_compute_api_version('2.49') servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.create_one_network() @@ -763,8 +738,9 @@ def test_server_add_port_no_neutron(self): self._test_server_add_port('fake-port') self.find_port.assert_not_called() - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_server_add_port_with_tag(self, sm_mock): + 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' arglist = [ @@ -787,8 +763,9 @@ def test_server_add_port_with_tag(self, sm_mock): servers[0], port_id='fake-port', tag='tag1' ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_server_add_port_with_tag_pre_v249(self, sm_mock): + def test_server_add_port_with_tag_pre_v249(self): + self.set_compute_api_version('2.48') + servers = self.setup_servers_mock(count=1) self.find_port.return_value.id = 'fake-port' arglist = [ @@ -843,8 +820,8 @@ def setUp(self): # Get the command object to test self.cmd = server.AddServerVolume(self.app, None) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_server_add_volume(self, sm_mock): + def test_server_add_volume(self): + self.set_compute_api_version('2.48') arglist = [ '--device', '/dev/sdb', @@ -875,14 +852,8 @@ def test_server_add_volume(self, sm_mock): self.servers[0], volumeId=self.volumes[0].id, device='/dev/sdb' ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_add_volume_with_tag(self, sm_mock): - def side_effect(compute_client, version): - if version == '2.49': - return True - return False - - sm_mock.side_effect = side_effect + def test_server_add_volume_with_tag(self): + self.set_compute_api_version('2.49') arglist = [ '--device', @@ -921,8 +892,9 @@ def side_effect(compute_client, version): tag='foo', ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_server_add_volume_with_tag_pre_v249(self, sm_mock): + 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, @@ -943,11 +915,9 @@ def test_server_add_volume_with_tag_pre_v249(self, sm_mock): '--os-compute-api-version 2.49 or greater is required', str(ex) ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_server_add_volume_with_enable_delete_on_termination( - self, - sm_mock, - ): + def test_server_add_volume_with_enable_delete_on_termination(self): + self.set_compute_api_version('2.79') + self.volume_attachment.delete_on_termination = True arglist = [ '--enable-delete-on-termination', @@ -992,11 +962,9 @@ def test_server_add_volume_with_enable_delete_on_termination( delete_on_termination=True, ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_server_add_volume_with_disable_delete_on_termination( - self, - sm_mock, - ): + def test_server_add_volume_with_disable_delete_on_termination(self): + self.set_compute_api_version('2.79') + self.volume_attachment.delete_on_termination = False arglist = [ @@ -1043,17 +1011,10 @@ def test_server_add_volume_with_disable_delete_on_termination( delete_on_termination=False, ) - @mock.patch.object(sdk_utils, 'supports_microversion') def test_server_add_volume_with_enable_delete_on_termination_pre_v279( self, - sm_mock, ): - def side_effect(compute_client, version): - if version == '2.79': - return False - return True - - sm_mock.side_effect = side_effect + self.set_compute_api_version('2.78') arglist = [ self.servers[0].id, @@ -1074,17 +1035,10 @@ def side_effect(compute_client, version): '--os-compute-api-version 2.79 or greater is required', str(ex) ) - @mock.patch.object(sdk_utils, 'supports_microversion') def test_server_add_volume_with_disable_delete_on_termination_pre_v279( self, - sm_mock, ): - def side_effect(compute_client, version): - if version == '2.79': - return False - return True - - sm_mock.side_effect = side_effect + self.set_compute_api_version('2.78') arglist = [ self.servers[0].id, @@ -1105,11 +1059,11 @@ def side_effect(compute_client, version): '--os-compute-api-version 2.79 or greater is required', str(ex) ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) def test_server_add_volume_with_disable_and_enable_delete_on_termination( self, - sm_mock, ): + self.set_compute_api_version('2.78') + arglist = [ '--enable-delete-on-termination', '--disable-delete-on-termination', @@ -1214,8 +1168,9 @@ def test_server_add_network_no_neutron(self): self._test_server_add_network('fake-network') self.find_network.assert_not_called() - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_server_add_network_with_tag(self, sm_mock): + 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' @@ -1239,8 +1194,9 @@ def test_server_add_network_with_tag(self, sm_mock): servers[0], net_id='fake-network', tag='tag1' ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_server_add_network_with_tag_pre_v249(self, sm_mock): + 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' @@ -1774,7 +1730,7 @@ def test_server_create_with_network(self): self.assertEqual(self.datalist(), data) def test_server_create_with_network_tag(self): - self.compute_client.api_version = api_versions.APIVersion('2.43') + self.set_compute_api_version('2.43') arglist = [ '--image', @@ -1850,7 +1806,7 @@ def test_server_create_with_network_tag(self): self.app.client_manager.network.find_network.assert_called_once() def test_server_create_with_network_tag_pre_v243(self): - self.compute_client.api_version = api_versions.APIVersion('2.42') + self.set_compute_api_version('2.42') arglist = [ '--image', @@ -1886,7 +1842,7 @@ def test_server_create_with_network_tag_pre_v243(self): def _test_server_create_with_auto_network(self, arglist): # requires API microversion 2.37 or later - self.compute_client.api_version = api_versions.APIVersion('2.37') + self.set_compute_api_version('2.37') verifylist = [ ('image', 'image1'), @@ -1952,7 +1908,7 @@ def test_server_create_with_auto_network(self): def test_server_create_with_auto_network_pre_v237(self): # use an API microversion that's too old - self.compute_client.api_version = api_versions.APIVersion('2.36') + self.set_compute_api_version('2.36') arglist = [ '--image', @@ -1988,7 +1944,7 @@ def test_server_create_with_auto_network_pre_v237(self): def test_server_create_with_auto_network_default_v2_37(self): """Tests creating a server without specifying --nic using 2.37.""" # requires API microversion 2.37 or later - self.compute_client.api_version = api_versions.APIVersion('2.37') + self.set_compute_api_version('2.37') arglist = [ '--image', @@ -2034,7 +1990,7 @@ def test_server_create_with_auto_network_default_v2_37(self): def _test_server_create_with_none_network(self, arglist): # requires API microversion 2.37 or later - self.compute_client.api_version = api_versions.APIVersion('2.37') + self.set_compute_api_version('2.37') verifylist = [ ('image', 'image1'), @@ -2100,7 +2056,7 @@ def test_server_create_with_none_network(self): def test_server_create_with_none_network_pre_v237(self): # use an API microversion that's too old - self.compute_client.api_version = api_versions.APIVersion('2.36') + self.set_compute_api_version('2.36') arglist = [ '--image', @@ -2585,7 +2541,7 @@ def test_server_create_with_block_device(self): self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_full(self): - self.compute_client.api_version = api_versions.APIVersion('2.67') + self.set_compute_api_version('2.67') block_device = ( f'uuid={self.volume.id},source_type=volume,' @@ -2686,7 +2642,7 @@ def test_server_create_with_block_device_full(self): self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_from_file(self): - self.compute_client.api_version = api_versions.APIVersion('2.67') + self.set_compute_api_version('2.67') block_device = { 'uuid': self.volume.id, @@ -2840,7 +2796,7 @@ def test_server_create_with_block_device_invalid_shutdown(self): ) def test_server_create_with_block_device_tag_pre_v242(self): - self.compute_client.api_version = api_versions.APIVersion('2.41') + self.set_compute_api_version('2.41') block_device = f'uuid={self.volume.name},tag=foo' arglist = [ @@ -2861,7 +2817,7 @@ def test_server_create_with_block_device_tag_pre_v242(self): ) def test_server_create_with_block_device_volume_type_pre_v267(self): - self.compute_client.api_version = api_versions.APIVersion('2.66') + self.set_compute_api_version('2.66') block_device = f'uuid={self.volume.name},volume_type=foo' arglist = [ @@ -3824,9 +3780,9 @@ def test_server_create_invalid_hint(self): [], ) - def test_server_create_with_description_api_newer(self): + def test_server_create_with_description(self): # Description is supported for nova api version 2.19 or above - self.compute_client.api_version = 2.19 + self.set_compute_api_version('2.19') arglist = [ '--image', @@ -3846,11 +3802,7 @@ def test_server_create_with_description_api_newer(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object(api_versions, 'APIVersion', return_value=2.19): - # 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) + columns, data = self.cmd.take_action(parsed_args) # Set expected values kwargs = dict( @@ -3865,7 +3817,7 @@ def test_server_create_with_description_api_newer(self): availability_zone=None, admin_pass=None, block_device_mapping_v2=[], - nics='auto', + nics=[], scheduler_hints={}, config_drive=None, description='description1', @@ -3880,9 +3832,9 @@ def test_server_create_with_description_api_newer(self): self.assertFalse(self.image_client.images.called) self.assertFalse(self.flavors_mock.called) - def test_server_create_with_description_api_older(self): + def test_server_create_with_description_pre_v219(self): # Description is not supported for nova api version below 2.19 - self.compute_client.api_version = 2.18 + self.set_compute_api_version('2.18') arglist = [ '--image', @@ -3902,13 +3854,12 @@ def test_server_create_with_description_api_older(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object(api_versions, 'APIVersion', return_value=2.19): - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) def test_server_create_with_tag(self): - self.compute_client.api_version = api_versions.APIVersion('2.52') + self.set_compute_api_version('2.52') arglist = [ '--image', @@ -3961,7 +3912,7 @@ def test_server_create_with_tag(self): self.assertFalse(self.flavors_mock.called) def test_server_create_with_tag_pre_v252(self): - self.compute_client.api_version = api_versions.APIVersion('2.51') + self.set_compute_api_version('2.51') arglist = [ '--image', @@ -3992,7 +3943,7 @@ def test_server_create_with_tag_pre_v252(self): def test_server_create_with_host_v274(self): # Explicit host is supported for nova api version 2.74 or above - self.compute_client.api_version = 2.74 + self.set_compute_api_version('2.74') arglist = [ '--image', @@ -4012,11 +3963,7 @@ def test_server_create_with_host_v274(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object(api_versions, 'APIVersion', return_value=2.74): - # 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) + columns, data = self.cmd.take_action(parsed_args) # Set expected values kwargs = dict( @@ -4048,7 +3995,7 @@ def test_server_create_with_host_v274(self): def test_server_create_with_host_pre_v274(self): # Host is not supported for nova api version below 2.74 - self.compute_client.api_version = 2.73 + self.set_compute_api_version('2.73') arglist = [ '--image', @@ -4068,15 +4015,14 @@ def test_server_create_with_host_pre_v274(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object(api_versions, 'APIVersion', return_value=2.74): - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) def test_server_create_with_hypervisor_hostname_v274(self): # Explicit hypervisor_hostname is supported for nova api version # 2.74 or above - self.compute_client.api_version = 2.74 + self.set_compute_api_version('2.74') arglist = [ '--image', @@ -4096,11 +4042,7 @@ def test_server_create_with_hypervisor_hostname_v274(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object(api_versions, 'APIVersion', return_value=2.74): - # 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) + columns, data = self.cmd.take_action(parsed_args) # Set expected values kwargs = dict( @@ -4132,7 +4074,7 @@ def test_server_create_with_hypervisor_hostname_v274(self): def test_server_create_with_hypervisor_hostname_pre_v274(self): # Hypervisor_hostname is not supported for nova api version below 2.74 - self.compute_client.api_version = 2.73 + self.set_compute_api_version('2.73') arglist = [ '--image', @@ -4152,15 +4094,14 @@ def test_server_create_with_hypervisor_hostname_pre_v274(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object(api_versions, 'APIVersion', return_value=2.74): - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args + ) def test_server_create_with_host_and_hypervisor_hostname_v274(self): # Explicit host and hypervisor_hostname is supported for nova api # version 2.74 or above - self.compute_client.api_version = 2.74 + self.set_compute_api_version('2.74') arglist = [ '--image', @@ -4183,11 +4124,7 @@ def test_server_create_with_host_and_hypervisor_hostname_v274(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object(api_versions, 'APIVersion', return_value=2.74): - # 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) + columns, data = self.cmd.take_action(parsed_args) # Set expected values kwargs = dict( @@ -4219,7 +4156,7 @@ def test_server_create_with_host_and_hypervisor_hostname_v274(self): self.assertFalse(self.flavors_mock.called) def test_server_create_with_hostname_v290(self): - self.compute_client.api_version = api_versions.APIVersion('2.90') + self.set_compute_api_version('2.90') arglist = [ '--image', @@ -4268,7 +4205,7 @@ def test_server_create_with_hostname_v290(self): self.assertFalse(self.flavors_mock.called) def test_server_create_with_hostname_pre_v290(self): - self.compute_client.api_version = api_versions.APIVersion('2.89') + self.set_compute_api_version('2.89') arglist = [ '--image', @@ -4293,7 +4230,7 @@ def test_server_create_with_hostname_pre_v290(self): ) def test_server_create_with_trusted_image_cert(self): - self.compute_client.api_version = api_versions.APIVersion('2.63') + self.set_compute_api_version('2.63') arglist = [ '--image', @@ -4344,7 +4281,7 @@ def test_server_create_with_trusted_image_cert(self): self.assertFalse(self.flavors_mock.called) def test_server_create_with_trusted_image_cert_prev263(self): - self.compute_client.api_version = api_versions.APIVersion('2.62') + self.set_compute_api_version('2.62') arglist = [ '--image', @@ -4371,7 +4308,7 @@ def test_server_create_with_trusted_image_cert_prev263(self): ) def test_server_create_with_trusted_image_cert_from_volume(self): - self.compute_client.api_version = api_versions.APIVersion('2.63') + self.set_compute_api_version('2.63') arglist = [ '--volume', 'volume1', @@ -4397,7 +4334,7 @@ def test_server_create_with_trusted_image_cert_from_volume(self): ) def test_server_create_with_trusted_image_cert_from_snapshot(self): - self.compute_client.api_version = api_versions.APIVersion('2.63') + self.set_compute_api_version('2.63') arglist = [ '--snapshot', 'snapshot1', @@ -4423,7 +4360,7 @@ def test_server_create_with_trusted_image_cert_from_snapshot(self): ) def test_server_create_with_trusted_image_cert_boot_from_volume(self): - self.compute_client.api_version = api_versions.APIVersion('2.63') + self.set_compute_api_version('2.63') arglist = [ '--image', 'image1', @@ -5019,7 +4956,7 @@ def test_server_list_with_invalid_changes_since(self, mock_parse_isotime): mock_parse_isotime.assert_called_once_with('Invalid time value') def test_server_list_with_tag(self): - self._set_mock_microversion('2.26') + self.set_compute_api_version('2.26') arglist = [ '--tag', @@ -5042,7 +4979,7 @@ def test_server_list_with_tag(self): self.assertEqual(self.data, tuple(data)) def test_server_list_with_tag_pre_v225(self): - self._set_mock_microversion('2.25') + self.set_compute_api_version('2.25') arglist = [ '--tag', @@ -5063,7 +5000,7 @@ def test_server_list_with_tag_pre_v225(self): ) def test_server_list_with_not_tag(self): - self._set_mock_microversion('2.26') + self.set_compute_api_version('2.26') arglist = [ '--not-tag', 'tag1', @@ -5085,7 +5022,7 @@ def test_server_list_with_not_tag(self): self.assertEqual(self.data, tuple(data)) def test_server_list_with_not_tag_pre_v226(self): - self._set_mock_microversion('2.25') + self.set_compute_api_version('2.25') arglist = [ '--not-tag', @@ -5254,7 +5191,7 @@ def test_server_list_with_power_state(self): self.assertEqual(tuple(self.data), tuple(data)) def test_server_list_long_with_host_status_v216(self): - self._set_mock_microversion('2.16') + self.set_compute_api_version('2.16') self.data1 = tuple( ( s.id, @@ -5423,7 +5360,7 @@ def test_server_list_with_locked_pre_v273(self): ) def test_server_list_with_locked(self): - self._set_mock_microversion('2.73') + self.set_compute_api_version('2.73') arglist = ['--locked'] verifylist = [('locked', True)] @@ -5437,7 +5374,7 @@ def test_server_list_with_locked(self): self.assertCountEqual(self.data, tuple(data)) def test_server_list_with_unlocked_v273(self): - self._set_mock_microversion('2.73') + self.set_compute_api_version('2.73') arglist = ['--unlocked'] verifylist = [('unlocked', True)] @@ -5452,7 +5389,7 @@ def test_server_list_with_unlocked_v273(self): self.assertCountEqual(self.data, tuple(data)) def test_server_list_with_locked_and_unlocked(self): - self._set_mock_microversion('2.73') + self.set_compute_api_version('2.73') arglist = ['--locked', '--unlocked'] verifylist = [('locked', True), ('unlocked', True)] @@ -5466,7 +5403,7 @@ def test_server_list_with_locked_and_unlocked(self): self.assertIn('Argument parse failed', str(ex)) def test_server_list_with_changes_before(self): - self._set_mock_microversion('2.66') + self.set_compute_api_version('2.66') arglist = ['--changes-before', '2016-03-05T06:27:59Z', '--deleted'] verifylist = [ ('changes_before', '2016-03-05T06:27:59Z'), @@ -5486,7 +5423,7 @@ def test_server_list_with_changes_before(self): @mock.patch.object(iso8601, 'parse_date', side_effect=iso8601.ParseError) def test_server_list_with_invalid_changes_before(self, mock_parse_isotime): - self._set_mock_microversion('2.66') + self.set_compute_api_version('2.66') arglist = [ '--changes-before', 'Invalid time value', @@ -5506,7 +5443,7 @@ def test_server_list_with_invalid_changes_before(self, mock_parse_isotime): mock_parse_isotime.assert_called_once_with('Invalid time value') def test_server_with_changes_before_pre_v266(self): - self._set_mock_microversion('2.65') + self.set_compute_api_version('2.65') arglist = ['--changes-before', '2016-03-05T06:27:59Z', '--deleted'] verifylist = [ @@ -5521,7 +5458,7 @@ def test_server_with_changes_before_pre_v266(self): ) def test_server_list_v269_with_partial_constructs(self): - self._set_mock_microversion('2.69') + self.set_compute_api_version('2.69') arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -5575,19 +5512,15 @@ def setUp(self): # Get the command object to test self.cmd = server.LockServer(self.app, None) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_lock(self, sm_mock): - sm_mock.return_value = False + def test_server_lock(self): self.run_method_with_sdk_servers('lock_server', 1) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_lock_multi_servers(self, sm_mock): - sm_mock.return_value = False + def test_server_lock_multi_servers(self): self.run_method_with_sdk_servers('lock_server', 3) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_lock_with_reason(self, sm_mock): - sm_mock.return_value = True + def test_server_lock_with_reason(self): + self.set_compute_api_version('2.73') + arglist = [ self.server.id, '--reason', @@ -5608,9 +5541,9 @@ def test_server_lock_with_reason(self, sm_mock): locked_reason="blah", ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_lock_with_reason_multi_servers(self, sm_mock): - sm_mock.return_value = True + def test_server_lock_with_reason_multi_servers(self): + self.set_compute_api_version('2.73') + server2 = compute_fakes.create_one_sdk_server() arglist = [ self.server.id, @@ -5631,9 +5564,9 @@ def test_server_lock_with_reason_multi_servers(self, sm_mock): ) self.assertEqual(2, self.compute_sdk_client.lock_server.call_count) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_lock_with_reason_pre_v273(self, sm_mock): - sm_mock.return_value = False + 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, @@ -5711,7 +5644,7 @@ def test_server_migrate_with_host_2_56(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_client.api_version = api_versions.APIVersion('2.56') + self.set_compute_api_version('2.56') result = self.cmd.take_action(parsed_args) @@ -5835,7 +5768,7 @@ def test_server_live_migrate_with_host(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_client.api_version = api_versions.APIVersion('2.30') + self.set_compute_api_version('2.30') result = self.cmd.take_action(parsed_args) @@ -5895,7 +5828,7 @@ def test_server_block_live_migrate(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_client.api_version = api_versions.APIVersion('2.24') + self.set_compute_api_version('2.24') result = self.cmd.take_action(parsed_args) @@ -5920,7 +5853,7 @@ def test_server_live_migrate_with_disk_overcommit(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_client.api_version = api_versions.APIVersion('2.24') + self.set_compute_api_version('2.24') result = self.cmd.take_action(parsed_args) @@ -5945,7 +5878,7 @@ def test_server_live_migrate_with_disk_overcommit_post_v224(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.compute_client.api_version = api_versions.APIVersion('2.25') + self.set_compute_api_version('2.25') with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) @@ -6301,7 +6234,7 @@ def test_rebuild_with_password(self): self.server.rebuild.assert_called_with(self.image, password) def test_rebuild_with_description(self): - self.compute_client.api_version = api_versions.APIVersion('2.19') + self.set_compute_api_version('2.19') description = 'description1' arglist = [self.server.id, '--description', description] @@ -6317,7 +6250,7 @@ def test_rebuild_with_description(self): ) def test_rebuild_with_description_pre_v219(self): - self.compute_client.api_version = api_versions.APIVersion('2.18') + self.set_compute_api_version('2.18') description = 'description1' arglist = [self.server.id, '--description', description] @@ -6487,7 +6420,7 @@ def test_rebuild_with_property(self): ) def test_rebuild_with_keypair_name(self): - self.compute_client.api_version = api_versions.APIVersion('2.54') + self.set_compute_api_version('2.54') self.server.key_name = 'mykey' arglist = [ @@ -6510,7 +6443,7 @@ def test_rebuild_with_keypair_name(self): ) def test_rebuild_with_keypair_name_pre_v254(self): - self.compute_client.api_version = api_versions.APIVersion('2.53') + self.set_compute_api_version('2.53') self.server.key_name = 'mykey' arglist = [ @@ -6529,7 +6462,7 @@ def test_rebuild_with_keypair_name_pre_v254(self): ) def test_rebuild_with_no_keypair_name(self): - self.compute_client.api_version = api_versions.APIVersion('2.54') + self.set_compute_api_version('2.54') self.server.key_name = 'mykey' arglist = [ @@ -6568,7 +6501,7 @@ def test_rebuild_with_keypair_name_and_unset(self): @mock.patch('openstackclient.compute.v2.server.open') def test_rebuild_with_user_data(self, mock_open): - self.compute_client.api_version = api_versions.APIVersion('2.57') + self.set_compute_api_version('2.57') mock_file = mock.Mock(name='File') mock_open.return_value = mock_file @@ -6602,7 +6535,7 @@ def test_rebuild_with_user_data(self, mock_open): ) def test_rebuild_with_user_data_pre_v257(self): - self.compute_client.api_version = api_versions.APIVersion('2.56') + self.set_compute_api_version('2.56') arglist = [ self.server.id, @@ -6620,7 +6553,7 @@ def test_rebuild_with_user_data_pre_v257(self): ) def test_rebuild_with_no_user_data(self): - self.compute_client.api_version = api_versions.APIVersion('2.54') + self.set_compute_api_version('2.54') self.server.key_name = 'mykey' arglist = [ @@ -6639,7 +6572,7 @@ def test_rebuild_with_no_user_data(self): self.server.rebuild.assert_called_with(self.image, None, userdata=None) def test_rebuild_with_no_user_data_pre_v254(self): - self.compute_client.api_version = api_versions.APIVersion('2.53') + self.set_compute_api_version('2.53') arglist = [ self.server.id, @@ -6671,7 +6604,7 @@ def test_rebuild_with_user_data_and_unset(self): ) def test_rebuild_with_trusted_image_cert(self): - self.compute_client.api_version = api_versions.APIVersion('2.63') + self.set_compute_api_version('2.63') arglist = [ self.server.id, @@ -6695,7 +6628,7 @@ def test_rebuild_with_trusted_image_cert(self): ) def test_rebuild_with_trusted_image_cert_pre_v263(self): - self.compute_client.api_version = api_versions.APIVersion('2.62') + self.set_compute_api_version('2.62') arglist = [ self.server.id, @@ -6715,7 +6648,7 @@ def test_rebuild_with_trusted_image_cert_pre_v263(self): ) def test_rebuild_with_no_trusted_image_cert(self): - self.compute_client.api_version = api_versions.APIVersion('2.63') + self.set_compute_api_version('2.63') arglist = [ self.server.id, @@ -6735,7 +6668,7 @@ def test_rebuild_with_no_trusted_image_cert(self): ) def test_rebuild_with_no_trusted_image_cert_pre_v263(self): - self.compute_client.api_version = api_versions.APIVersion('2.62') + self.set_compute_api_version('2.62') arglist = [ self.server.id, @@ -6752,7 +6685,7 @@ def test_rebuild_with_no_trusted_image_cert_pre_v263(self): ) def test_rebuild_with_hostname(self): - self.compute_client.api_version = api_versions.APIVersion('2.90') + self.set_compute_api_version('2.90') arglist = [self.server.id, '--hostname', 'new-hostname'] verifylist = [('server', self.server.id), ('hostname', 'new-hostname')] @@ -6767,7 +6700,7 @@ def test_rebuild_with_hostname(self): ) def test_rebuild_with_hostname_pre_v290(self): - self.compute_client.api_version = api_versions.APIVersion('2.89') + self.set_compute_api_version('2.89') arglist = [ self.server.id, @@ -6812,7 +6745,7 @@ def setUp(self): self.cmd = server.RebuildServer(self.app, None) def test_rebuild_with_reimage_boot_volume(self): - self.compute_client.api_version = api_versions.APIVersion('2.93') + self.set_compute_api_version('2.93') arglist = [ self.server.id, @@ -6833,7 +6766,7 @@ def test_rebuild_with_reimage_boot_volume(self): self.server.rebuild.assert_called_with(self.new_image, None) def test_rebuild_with_no_reimage_boot_volume(self): - self.compute_client.api_version = api_versions.APIVersion('2.93') + self.set_compute_api_version('2.93') arglist = [ self.server.id, @@ -6854,7 +6787,7 @@ def test_rebuild_with_no_reimage_boot_volume(self): self.assertIn('--reimage-boot-volume is required', str(exc)) def test_rebuild_with_reimage_boot_volume_pre_v293(self): - self.compute_client.api_version = api_versions.APIVersion('2.92') + self.set_compute_api_version('2.92') arglist = [ self.server.id, @@ -6947,7 +6880,7 @@ def test_evacuate_with_password(self): self._test_evacuate(args, verify_args, evac_args) def test_evacuate_with_host(self): - self.compute_client.api_version = api_versions.APIVersion('2.29') + self.set_compute_api_version('2.29') host = 'target-host' args = [ @@ -6964,7 +6897,7 @@ def test_evacuate_with_host(self): self._test_evacuate(args, verify_args, evac_args) def test_evacuate_with_host_pre_v229(self): - self.compute_client.api_version = api_versions.APIVersion('2.28') + self.set_compute_api_version('2.28') args = [ self.server.id, @@ -6982,7 +6915,7 @@ def test_evacuate_with_host_pre_v229(self): ) def test_evacuate_without_share_storage(self): - self.compute_client.api_version = api_versions.APIVersion('2.13') + self.set_compute_api_version('2.13') args = [self.server.id, '--shared-storage'] verify_args = [ @@ -6997,7 +6930,7 @@ def test_evacuate_without_share_storage(self): self._test_evacuate(args, verify_args, evac_args) def test_evacuate_without_share_storage_post_v213(self): - self.compute_client.api_version = api_versions.APIVersion('2.14') + self.set_compute_api_version('2.14') args = [self.server.id, '--shared-storage'] verify_args = [ @@ -7957,8 +7890,7 @@ def test_server_set_with_root_password(self, mock_getpass): self.assertIsNone(result) def test_server_set_with_description(self): - # Description is supported for nova api version 2.19 or above - self.fake_servers[0].api_version = api_versions.APIVersion('2.19') + self.set_compute_api_version('2.19') arglist = [ '--description', @@ -7977,8 +7909,7 @@ def test_server_set_with_description(self): self.assertIsNone(result) def test_server_set_with_description_pre_v219(self): - # Description is not supported for nova api version below 2.19 - self.fake_servers[0].api_version = api_versions.APIVersion('2.18') + self.set_compute_api_version('2.18') arglist = [ '--description', @@ -7995,7 +7926,7 @@ def test_server_set_with_description_pre_v219(self): ) def test_server_set_with_tag(self): - self.fake_servers[0].api_version = api_versions.APIVersion('2.26') + self.set_compute_api_version('2.26') arglist = [ '--tag', @@ -8021,7 +7952,7 @@ def test_server_set_with_tag(self): self.assertIsNone(result) def test_server_set_with_tag_pre_v226(self): - self.fake_servers[0].api_version = api_versions.APIVersion('2.25') + self.set_compute_api_version('2.25') arglist = [ '--tag', @@ -8044,7 +7975,7 @@ def test_server_set_with_tag_pre_v226(self): ) def test_server_set_with_hostname(self): - self.fake_servers[0].api_version = api_versions.APIVersion('2.90') + self.set_compute_api_version('2.90') arglist = [ '--hostname', @@ -8063,7 +7994,7 @@ def test_server_set_with_hostname(self): self.assertIsNone(result) def test_server_set_with_hostname_pre_v290(self): - self.fake_servers[0].api_version = api_versions.APIVersion('2.89') + self.set_compute_api_version('2.89') arglist = [ '--hostname', @@ -8406,7 +8337,7 @@ def test_show_diagnostics(self): self.assertEqual(('test',), data) def test_show_topology(self): - self._set_mock_microversion('2.78') + self.set_compute_api_version('2.78') arglist = [ '--topology', @@ -8428,7 +8359,7 @@ def test_show_topology(self): self.assertCountEqual(self.data, data) def test_show_topology_pre_v278(self): - self._set_mock_microversion('2.77') + self.set_compute_api_version('2.77') arglist = [ '--topology', @@ -8719,9 +8650,9 @@ def test_server_unset_with_property(self): ) self.assertIsNone(result) - def test_server_unset_with_description_api_newer(self): + def test_server_unset_with_description(self): # Description is supported for nova api version 2.19 or above - self.compute_client.api_version = 2.19 + self.set_compute_api_version('2.19') arglist = [ '--description', @@ -8733,16 +8664,16 @@ def test_server_unset_with_description_api_newer(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - with mock.patch.object(api_versions, 'APIVersion', return_value=2.19): - result = self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) + self.servers_mock.update.assert_called_once_with( self.fake_server, description="" ) self.assertIsNone(result) - def test_server_unset_with_description_api_older(self): + def test_server_unset_with_description_pre_v219(self): # Description is not supported for nova api version below 2.19 - self.compute_client.api_version = api_versions.APIVersion('2.18') + self.set_compute_api_version('2.18') arglist = [ '--description', @@ -8762,7 +8693,7 @@ def test_server_unset_with_description_api_older(self): ) def test_server_unset_with_tag(self): - self.compute_client.api_version = api_versions.APIVersion('2.26') + self.set_compute_api_version('2.26') arglist = [ '--tag', @@ -8788,7 +8719,7 @@ def test_server_unset_with_tag(self): ) def test_server_unset_with_tag_pre_v226(self): - self.compute_client.api_version = api_versions.APIVersion('2.25') + self.set_compute_api_version('2.25') arglist = [ '--tag', @@ -8845,7 +8776,7 @@ def test_unshelve(self): ) def test_unshelve_with_az(self): - self._set_mock_microversion('2.77') + self.set_compute_api_version('2.77') arglist = [ '--availability-zone', @@ -8870,7 +8801,7 @@ def test_unshelve_with_az(self): ) def test_unshelve_with_az_pre_v277(self): - self._set_mock_microversion('2.76') + self.set_compute_api_version('2.76') arglist = [ self.server.id, @@ -8894,7 +8825,7 @@ def test_unshelve_with_az_pre_v277(self): ) def test_unshelve_with_host(self): - self._set_mock_microversion('2.91') + self.set_compute_api_version('2.91') arglist = [ '--host', @@ -8916,7 +8847,7 @@ def test_unshelve_with_host(self): ) def test_unshelve_with_host_pre_v291(self): - self._set_mock_microversion('2.90') + self.set_compute_api_version('2.90') arglist = [ '--host', @@ -8938,7 +8869,7 @@ def test_unshelve_with_host_pre_v291(self): ) def test_unshelve_with_no_az(self): - self._set_mock_microversion('2.91') + self.set_compute_api_version('2.91') arglist = [ '--no-availability-zone', @@ -8962,7 +8893,7 @@ def test_unshelve_with_no_az(self): ) def test_unshelve_with_no_az_pre_v291(self): - self._set_mock_microversion('2.90') + self.set_compute_api_version('2.90') arglist = [ '--no-availability-zone', @@ -8986,7 +8917,7 @@ def test_unshelve_with_no_az_pre_v291(self): ) def test_unshelve_with_no_az_and_az_conflict(self): - self._set_mock_microversion('2.91') + self.set_compute_api_version('2.91') arglist = [ '--availability-zone', diff --git a/openstackclient/tests/unit/compute/v2/test_server_event.py b/openstackclient/tests/unit/compute/v2/test_server_event.py index 1ce2e4d0e..728d9f842 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_event.py +++ b/openstackclient/tests/unit/compute/v2/test_server_event.py @@ -15,40 +15,14 @@ from unittest import mock import iso8601 -from novaclient import api_versions -from openstack import utils as sdk_utils from osc_lib import exceptions from openstackclient.compute.v2 import server_event from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -class TestServerEvent(compute_fakes.TestComputev2): +class TestListServerEvent(compute_fakes.TestComputev2): fake_server = compute_fakes.create_one_sdk_server() - - def setUp(self): - super().setUp() - - patcher = mock.patch.object( - sdk_utils, 'supports_microversion', return_value=True - ) - self.addCleanup(patcher.stop) - self.supports_microversion_mock = patcher.start() - self._set_mock_microversion( - self.compute_client.api_version.get_string() - ) - - def _set_mock_microversion(self, mock_v): - """Set a specific microversion for the mock supports_microversion().""" - self.supports_microversion_mock.reset_mock(return_value=True) - - self.supports_microversion_mock.side_effect = ( - lambda _, v: api_versions.APIVersion(v) - <= api_versions.APIVersion(mock_v) - ) - - -class TestListServerEvent(TestServerEvent): fake_event = compute_fakes.create_one_server_action() columns = ( @@ -145,7 +119,7 @@ def test_server_event_list_long(self): self.assertEqual(self.long_data, tuple(data)) def test_server_event_list_with_changes_since(self): - self._set_mock_microversion('2.58') + self.set_compute_api_version('2.58') arglist = [ '--changes-since', @@ -177,7 +151,7 @@ def test_server_event_list_with_changes_since_invalid( self, mock_parse_isotime, ): - self._set_mock_microversion('2.58') + self.set_compute_api_version('2.58') arglist = [ '--changes-since', @@ -200,7 +174,7 @@ def test_server_event_list_with_changes_since_invalid( mock_parse_isotime.assert_called_once_with('Invalid time value') def test_server_event_list_with_changes_since_pre_v258(self): - self._set_mock_microversion('2.57') + self.set_compute_api_version('2.57') arglist = [ '--changes-since', @@ -225,7 +199,7 @@ def test_server_event_list_with_changes_since_pre_v258(self): ) def test_server_event_list_with_changes_before(self): - self._set_mock_microversion('2.66') + self.set_compute_api_version('2.66') arglist = [ '--changes-before', @@ -257,7 +231,7 @@ def test_server_event_list_with_changes_before_invalid( self, mock_parse_isotime, ): - self._set_mock_microversion('2.66') + self.set_compute_api_version('2.66') arglist = [ '--changes-before', @@ -278,7 +252,7 @@ def test_server_event_list_with_changes_before_invalid( mock_parse_isotime.assert_called_once_with('Invalid time value') def test_server_event_list_with_changes_before_pre_v266(self): - self._set_mock_microversion('2.65') + self.set_compute_api_version('2.65') arglist = [ '--changes-before', @@ -301,7 +275,7 @@ def test_server_event_list_with_changes_before_pre_v266(self): ) def test_server_event_list_with_limit(self): - self._set_mock_microversion('2.58') + self.set_compute_api_version('2.58') arglist = [ '--limit', @@ -323,7 +297,7 @@ def test_server_event_list_with_limit(self): ) def test_server_event_list_with_limit_pre_v258(self): - self._set_mock_microversion('2.57') + self.set_compute_api_version('2.57') arglist = [ '--limit', @@ -348,7 +322,7 @@ def test_server_event_list_with_limit_pre_v258(self): ) def test_server_event_list_with_marker(self): - self._set_mock_microversion('2.58') + self.set_compute_api_version('2.58') arglist = [ '--marker', @@ -369,7 +343,7 @@ def test_server_event_list_with_marker(self): ) def test_server_event_list_with_marker_pre_v258(self): - self._set_mock_microversion('2.57') + self.set_compute_api_version('2.57') arglist = [ '--marker', @@ -391,7 +365,8 @@ def test_server_event_list_with_marker_pre_v258(self): ) -class TestShowServerEvent(TestServerEvent): +class TestShowServerEvent(compute_fakes.TestComputev2): + fake_server = compute_fakes.create_one_sdk_server() fake_event = compute_fakes.create_one_server_action() columns = ( 'action', diff --git a/openstackclient/tests/unit/compute/v2/test_server_group.py b/openstackclient/tests/unit/compute/v2/test_server_group.py index ab27288ea..2bef02796 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_group.py +++ b/openstackclient/tests/unit/compute/v2/test_server_group.py @@ -13,9 +13,6 @@ # under the License. # -from unittest import mock - -from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib import exceptions @@ -57,8 +54,9 @@ def setUp(self): ) self.cmd = server_group.CreateServerGroup(self.app, None) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_server_group_create(self, sm_mock): + def test_server_group_create(self): + self.set_compute_api_version('2.64') + arglist = [ '--policy', 'anti-affinity', @@ -78,8 +76,9 @@ def test_server_group_create(self, sm_mock): self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_server_group_create_with_soft_policies(self, sm_mock): + def test_server_group_create_with_soft_policies(self): + self.set_compute_api_version('2.64') + arglist = [ '--policy', 'soft-anti-affinity', @@ -99,8 +98,9 @@ def test_server_group_create_with_soft_policies(self, sm_mock): self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_server_group_create_with_soft_policies_pre_v215(self, sm_mock): + def test_server_group_create_with_soft_policies_pre_v215(self): + self.set_compute_api_version('2.14') + arglist = [ '--policy', 'soft-anti-affinity', @@ -118,8 +118,9 @@ def test_server_group_create_with_soft_policies_pre_v215(self, sm_mock): '--os-compute-api-version 2.15 or greater is required', str(ex) ) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_server_group_create_with_rules(self, sm_mock): + def test_server_group_create_with_rules(self): + self.set_compute_api_version('2.64') + arglist = [ '--policy', 'soft-anti-affinity', @@ -143,10 +144,9 @@ def test_server_group_create_with_rules(self, sm_mock): self.assertCountEqual(self.columns, columns) self.assertCountEqual(self.data, data) - @mock.patch.object( - sdk_utils, 'supports_microversion', side_effect=[True, False] - ) - def test_server_group_create_with_rules_pre_v264(self, sm_mock): + def test_server_group_create_with_rules_pre_v264(self): + self.set_compute_api_version('2.63') + arglist = [ '--policy', 'soft-anti-affinity', @@ -346,8 +346,7 @@ def setUp(self): ] self.cmd = server_group.ListServerGroup(self.app, None) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_server_group_list(self, sm_mock): + def test_server_group_list(self): arglist = [] verifylist = [ ('all_projects', False), @@ -363,8 +362,7 @@ def test_server_group_list(self, sm_mock): self.assertCountEqual(self.list_columns, columns) self.assertCountEqual(self.list_data, tuple(data)) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) - def test_server_group_list_with_all_projects_and_long(self, sm_mock): + def test_server_group_list_with_all_projects_and_long(self): arglist = [ '--all-projects', '--long', @@ -384,8 +382,7 @@ def test_server_group_list_with_all_projects_and_long(self, sm_mock): self.assertCountEqual(self.list_columns_long, columns) self.assertCountEqual(self.list_data_long, tuple(data)) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_server_group_list_with_limit(self, sm_mock): + def test_server_group_list_with_limit(self): arglist = [ '--limit', '1', @@ -402,8 +399,7 @@ def test_server_group_list_with_limit(self, sm_mock): self.compute_sdk_client.server_groups.assert_called_once_with(limit=1) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_server_group_list_with_offset(self, sm_mock): + def test_server_group_list_with_offset(self): arglist = [ '--offset', '5', @@ -420,8 +416,9 @@ def test_server_group_list_with_offset(self, sm_mock): self.compute_sdk_client.server_groups.assert_called_once_with(offset=5) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_server_group_list_v264(self, sm_mock): + def test_server_group_list_v264(self): + self.set_compute_api_version('2.64') + arglist = [] verifylist = [ ('all_projects', False), @@ -434,8 +431,9 @@ def test_server_group_list_v264(self, sm_mock): self.assertCountEqual(self.list_columns_v264, columns) self.assertCountEqual(self.list_data_v264, tuple(data)) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_server_group_list_with_all_projects_and_long_v264(self, sm_mock): + def test_server_group_list_with_all_projects_and_long_v264(self): + self.set_compute_api_version('2.64') + arglist = [ '--all-projects', '--long', @@ -463,8 +461,9 @@ def setUp(self): ) self.cmd = server_group.ShowServerGroup(self.app, None) - @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) - def test_server_group_show(self, sm_mock): + def test_server_group_show(self): + self.set_compute_api_version('2.64') + arglist = [ 'affinity_group', ] diff --git a/openstackclient/tests/unit/compute/v2/test_server_migration.py b/openstackclient/tests/unit/compute/v2/test_server_migration.py index 6638ca168..09ceae6fe 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_migration.py +++ b/openstackclient/tests/unit/compute/v2/test_server_migration.py @@ -10,10 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock - -from novaclient import api_versions -from openstack import utils as sdk_utils from osc_lib import exceptions from osc_lib import utils as common_utils @@ -22,34 +18,7 @@ from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestServerMigration(compute_fakes.TestComputev2): - def setUp(self): - super().setUp() - - # Get a shortcut to the compute client ServerManager Mock - self.servers_mock = self.compute_client.servers - self.servers_mock.reset_mock() - - # Get a shortcut to the compute client ServerMigrationsManager Mock - self.server_migrations_mock = self.compute_client.server_migrations - self.server_migrations_mock.reset_mock() - - patcher = mock.patch.object( - sdk_utils, 'supports_microversion', return_value=True - ) - self.addCleanup(patcher.stop) - self.supports_microversion_mock = patcher.start() - - def _set_mock_microversion(self, mock_v): - """Set a specific microversion for the mock supports_microversion().""" - self.supports_microversion_mock.reset_mock(return_value=True) - self.supports_microversion_mock.side_effect = ( - lambda _, v: api_versions.APIVersion(v) - <= api_versions.APIVersion(mock_v) - ) - - -class TestListMigration(TestServerMigration): +class TestListMigration(compute_fakes.TestComputev2): """Test fetch all migrations.""" MIGRATION_COLUMNS = [ @@ -83,8 +52,6 @@ class TestListMigration(TestServerMigration): def setUp(self): super().setUp() - self._set_mock_microversion('2.1') - self.server = compute_fakes.create_one_sdk_server() self.compute_sdk_client.find_server.return_value = self.server @@ -189,7 +156,7 @@ class TestListMigrationV223(TestListMigration): def setUp(self): super().setUp() - self._set_mock_microversion('2.23') + self.set_compute_api_version('2.23') def test_server_migration_list(self): arglist = ['--status', 'migrating'] @@ -249,7 +216,7 @@ class TestListMigrationV259(TestListMigration): def setUp(self): super().setUp() - self._set_mock_microversion('2.59') + self.set_compute_api_version('2.59') def test_server_migration_list(self): arglist = [ @@ -286,7 +253,8 @@ def test_server_migration_list(self): self.assertEqual(tuple(self.data), tuple(data)) def test_server_migration_list_with_limit_pre_v259(self): - self._set_mock_microversion('2.58') + self.set_compute_api_version('2.58') + arglist = ['--status', 'migrating', '--limit', '1'] verifylist = [('status', 'migrating'), ('limit', 1)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -298,7 +266,8 @@ def test_server_migration_list_with_limit_pre_v259(self): ) def test_server_migration_list_with_marker_pre_v259(self): - self._set_mock_microversion('2.58') + self.set_compute_api_version('2.58') + arglist = ['--status', 'migrating', '--marker', 'test_kp'] verifylist = [('status', 'migrating'), ('marker', 'test_kp')] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -310,7 +279,8 @@ def test_server_migration_list_with_marker_pre_v259(self): ) def test_server_migration_list_with_changes_since_pre_v259(self): - self._set_mock_microversion('2.58') + self.set_compute_api_version('2.58') + arglist = [ '--status', 'migrating', @@ -371,7 +341,7 @@ class TestListMigrationV266(TestListMigration): def setUp(self): super().setUp() - self._set_mock_microversion('2.66') + self.set_compute_api_version('2.66') def test_server_migration_list_with_changes_before(self): arglist = [ @@ -412,7 +382,8 @@ def test_server_migration_list_with_changes_before(self): self.assertEqual(tuple(self.data), tuple(data)) def test_server_migration_list_with_changes_before_pre_v266(self): - self._set_mock_microversion('2.65') + self.set_compute_api_version('2.65') + arglist = [ '--status', 'migrating', @@ -485,7 +456,7 @@ def setUp(self): self.projects_mock.get.return_value = self.project self.users_mock.get.return_value = self.user - self._set_mock_microversion('2.80') + self.set_compute_api_version('2.80') def test_server_migration_list_with_project(self): arglist = [ @@ -540,7 +511,8 @@ def test_server_migration_list_with_project(self): self.MIGRATION_FIELDS.remove('project_id') def test_get_migrations_with_project_pre_v280(self): - self._set_mock_microversion('2.79') + self.set_compute_api_version('2.79') + arglist = [ '--status', 'migrating', @@ -612,7 +584,8 @@ def test_server_migration_list_with_user(self): self.MIGRATION_FIELDS.remove('user_id') def test_get_migrations_with_user_pre_v280(self): - self._set_mock_microversion('2.79') + self.set_compute_api_version('2.79') + arglist = [ '--status', 'migrating', @@ -690,7 +663,8 @@ def test_server_migration_list_with_project_and_user(self): self.MIGRATION_FIELDS.remove('user_id') def test_get_migrations_with_project_and_user_pre_v280(self): - self._set_mock_microversion('2.79') + self.set_compute_api_version('2.79') + arglist = [ '--status', 'migrating', @@ -716,7 +690,7 @@ def test_get_migrations_with_project_and_user_pre_v280(self): ) -class TestServerMigrationShow(TestServerMigration): +class TestServerMigrationShow(compute_fakes.TestComputev2): def setUp(self): super().setUp() @@ -793,12 +767,12 @@ def _test_server_migration_show(self): ) def test_server_migration_show(self): - self._set_mock_microversion('2.24') + self.set_compute_api_version('2.24') self._test_server_migration_show() def test_server_migration_show_v259(self): - self._set_mock_microversion('2.59') + self.set_compute_api_version('2.59') self.columns += ('UUID',) self.data += (self.server_migration.uuid,) @@ -806,7 +780,7 @@ def test_server_migration_show_v259(self): self._test_server_migration_show() def test_server_migration_show_v280(self): - self._set_mock_microversion('2.80') + self.set_compute_api_version('2.80') self.columns += ('UUID', 'User ID', 'Project ID') self.data += ( @@ -818,7 +792,7 @@ def test_server_migration_show_v280(self): self._test_server_migration_show() def test_server_migration_show_pre_v224(self): - self._set_mock_microversion('2.23') + self.set_compute_api_version('2.23') arglist = [ self.server.id, @@ -835,7 +809,7 @@ def test_server_migration_show_pre_v224(self): ) def test_server_migration_show_by_uuid(self): - self._set_mock_microversion('2.59') + self.set_compute_api_version('2.59') self.compute_sdk_client.server_migrations.return_value = iter( [self.server_migration] @@ -865,7 +839,8 @@ def test_server_migration_show_by_uuid(self): self.compute_sdk_client.get_server_migration.assert_not_called() def test_server_migration_show_by_uuid_no_matches(self): - self._set_mock_microversion('2.59') + self.set_compute_api_version('2.59') + self.compute_sdk_client.server_migrations.return_value = iter([]) arglist = [ @@ -884,7 +859,7 @@ def test_server_migration_show_by_uuid_no_matches(self): ) def test_server_migration_show_by_uuid_pre_v259(self): - self._set_mock_microversion('2.58') + self.set_compute_api_version('2.58') arglist = [ self.server.id, @@ -901,7 +876,7 @@ def test_server_migration_show_by_uuid_pre_v259(self): ) def test_server_migration_show_invalid_id(self): - self._set_mock_microversion('2.24') + self.set_compute_api_version('2.24') arglist = [ self.server.id, @@ -918,7 +893,7 @@ def test_server_migration_show_invalid_id(self): ) -class TestServerMigrationAbort(TestServerMigration): +class TestServerMigrationAbort(compute_fakes.TestComputev2): def setUp(self): super().setUp() @@ -931,7 +906,7 @@ def setUp(self): self.cmd = server_migration.AbortMigration(self.app, None) def test_migration_abort(self): - self._set_mock_microversion('2.24') + self.set_compute_api_version('2.24') arglist = [ self.server.id, @@ -951,7 +926,7 @@ def test_migration_abort(self): self.assertIsNone(result) def test_migration_abort_pre_v224(self): - self._set_mock_microversion('2.23') + self.set_compute_api_version('2.23') arglist = [ self.server.id, @@ -968,7 +943,7 @@ def test_migration_abort_pre_v224(self): ) def test_server_migration_abort_by_uuid(self): - self._set_mock_microversion('2.59') + 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( @@ -996,7 +971,7 @@ def test_server_migration_abort_by_uuid(self): self.assertIsNone(result) def test_server_migration_abort_by_uuid_no_matches(self): - self._set_mock_microversion('2.59') + self.set_compute_api_version('2.59') self.compute_sdk_client.server_migrations.return_value = iter([]) @@ -1016,7 +991,7 @@ def test_server_migration_abort_by_uuid_no_matches(self): ) def test_server_migration_abort_by_uuid_pre_v259(self): - self._set_mock_microversion('2.58') + self.set_compute_api_version('2.58') arglist = [ self.server.id, @@ -1033,7 +1008,7 @@ def test_server_migration_abort_by_uuid_pre_v259(self): ) -class TestServerMigrationForceComplete(TestServerMigration): +class TestServerMigrationForceComplete(compute_fakes.TestComputev2): def setUp(self): super().setUp() @@ -1046,7 +1021,7 @@ def setUp(self): self.cmd = server_migration.ForceCompleteMigration(self.app, None) def test_migration_force_complete(self): - self._set_mock_microversion('2.22') + self.set_compute_api_version('2.22') arglist = [ self.server.id, @@ -1066,7 +1041,7 @@ def test_migration_force_complete(self): self.assertIsNone(result) def test_migration_force_complete_pre_v222(self): - self._set_mock_microversion('2.21') + self.set_compute_api_version('2.21') arglist = [ self.server.id, @@ -1083,7 +1058,7 @@ def test_migration_force_complete_pre_v222(self): ) def test_server_migration_force_complete_by_uuid(self): - self._set_mock_microversion('2.59') + 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( @@ -1111,7 +1086,7 @@ def test_server_migration_force_complete_by_uuid(self): self.assertIsNone(result) def test_server_migration_force_complete_by_uuid_no_matches(self): - self._set_mock_microversion('2.59') + self.set_compute_api_version('2.59') self.compute_sdk_client.server_migrations.return_value = iter([]) @@ -1131,7 +1106,7 @@ def test_server_migration_force_complete_by_uuid_no_matches(self): ) def test_server_migration_force_complete_by_uuid_pre_v259(self): - self._set_mock_microversion('2.58') + self.set_compute_api_version('2.58') 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 439ff048d..e59853228 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_volume.py +++ b/openstackclient/tests/unit/compute/v2/test_server_volume.py @@ -10,9 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock - -from openstack import utils as sdk_utils from osc_lib import exceptions from openstackclient.compute.v2 import server_volume @@ -35,10 +32,7 @@ def setUp(self): # Get the command object to test self.cmd = server_volume.ListServerVolume(self.app, None) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_volume_list(self, sm_mock): - sm_mock.side_effect = [False, False, False, False] - + def test_server_volume_list(self): arglist = [ self.server.id, ] @@ -71,9 +65,8 @@ def test_server_volume_list(self, sm_mock): self.server, ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_volume_list_with_tags(self, sm_mock): - sm_mock.side_effect = [False, True, False, False] + def test_server_volume_list_with_tags(self): + self.set_compute_api_version('2.70') arglist = [ self.server.id, @@ -118,9 +111,9 @@ def test_server_volume_list_with_tags(self, sm_mock): self.server, ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_volume_list_with_delete_on_attachment(self, sm_mock): - sm_mock.side_effect = [False, True, True, False] + def test_server_volume_list_with_delete_on_attachment(self): + self.set_compute_api_version('2.79') + arglist = [ self.server.id, ] @@ -167,9 +160,9 @@ def test_server_volume_list_with_delete_on_attachment(self, sm_mock): self.server, ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_volume_list_with_attachment_ids(self, sm_mock): - sm_mock.side_effect = [True, True, True, True] + def test_server_volume_list_with_attachment_ids(self): + self.set_compute_api_version('2.89') + arglist = [ self.server.id, ] @@ -251,9 +244,8 @@ def test_server_volume_update(self): self.compute_sdk_client.update_volume_attachment.assert_not_called() self.assertIsNone(result) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_volume_update_with_delete_on_termination(self, sm_mock): - sm_mock.return_value = True + def test_server_volume_update_with_delete_on_termination(self): + self.set_compute_api_version('2.85') arglist = [ self.server.id, @@ -276,9 +268,8 @@ def test_server_volume_update_with_delete_on_termination(self, sm_mock): ) self.assertIsNone(result) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_volume_update_with_preserve_on_termination(self, sm_mock): - sm_mock.return_value = True + def test_server_volume_update_with_preserve_on_termination(self): + self.set_compute_api_version('2.85') arglist = [ self.server.id, @@ -299,12 +290,8 @@ def test_server_volume_update_with_preserve_on_termination(self, sm_mock): ) self.assertIsNone(result) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_volume_update_with_delete_on_termination_pre_v285( - self, - sm_mock, - ): - sm_mock.return_value = False + def test_server_volume_update_with_delete_on_termination_pre_v285(self): + self.set_compute_api_version('2.84') arglist = [ self.server.id, @@ -325,12 +312,8 @@ def test_server_volume_update_with_delete_on_termination_pre_v285( ) self.compute_sdk_client.update_volume_attachment.assert_not_called() - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_volume_update_with_preserve_on_termination_pre_v285( - self, - sm_mock, - ): - sm_mock.return_value = False + def test_server_volume_update_with_preserve_on_termination_pre_v285(self): + self.set_compute_api_version('2.84') arglist = [ self.server.id, diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py index efb8c28b9..2997fec79 100644 --- a/openstackclient/tests/unit/compute/v2/test_service.py +++ b/openstackclient/tests/unit/compute/v2/test_service.py @@ -13,9 +13,7 @@ # under the License. from unittest import mock -from unittest.mock import call -from openstack import utils as sdk_utils from osc_lib import exceptions from openstackclient.compute.v2 import service @@ -62,7 +60,7 @@ def test_multi_services_delete(self): calls = [] for s in self.services: - calls.append(call(s.binary, ignore_missing=False)) + calls.append(mock.call(s.binary, ignore_missing=False)) self.compute_sdk_client.delete_service.assert_has_calls(calls) self.assertIsNone(result) @@ -156,10 +154,7 @@ def test_service_list(self): self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_list_with_long_option(self, sm_mock): - sm_mock.return_value = False - + def test_service_list_with_long_option(self): arglist = [ '--host', self.service.host, @@ -187,9 +182,8 @@ def test_service_list_with_long_option(self, sm_mock): self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, list(data)) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_list_with_long_option_2_11(self, sm_mock): - sm_mock.return_value = True + def test_service_list_with_long_option_2_11(self): + self.set_compute_api_version('2.11') arglist = [ '--host', @@ -234,9 +228,7 @@ def setUp(self): self.cmd = service.SetService(self.app, None) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_set_nothing(self, sm_mock): - sm_mock.return_value = False + def test_set_nothing(self): arglist = [ self.service.host, self.service.binary, @@ -252,9 +244,7 @@ def test_set_nothing(self, sm_mock): self.compute_sdk_client.disable_service.assert_not_called() self.assertIsNone(result) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_set_enable(self, sm_mock): - sm_mock.return_value = False + def test_service_set_enable(self): arglist = [ '--enable', self.service.host, @@ -274,9 +264,7 @@ def test_service_set_enable(self, sm_mock): ) self.assertIsNone(result) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_set_disable(self, sm_mock): - sm_mock.return_value = False + def test_service_set_disable(self): arglist = [ '--disable', self.service.host, @@ -296,9 +284,7 @@ def test_service_set_disable(self, sm_mock): ) self.assertIsNone(result) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_set_disable_with_reason(self, sm_mock): - sm_mock.return_value = False + def test_service_set_disable_with_reason(self): reason = 'earthquake' arglist = [ '--disable', @@ -322,9 +308,7 @@ def test_service_set_disable_with_reason(self, sm_mock): ) self.assertIsNone(result) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_set_only_with_disable_reason(self, sm_mock): - sm_mock.return_value = False + def test_service_set_only_with_disable_reason(self): reason = 'earthquake' arglist = [ '--disable-reason', @@ -348,9 +332,7 @@ def test_service_set_only_with_disable_reason(self, sm_mock): str(e), ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_set_enable_with_disable_reason(self, sm_mock): - sm_mock.return_value = False + def test_service_set_enable_with_disable_reason(self): reason = 'earthquake' arglist = [ '--enable', @@ -376,9 +358,9 @@ def test_service_set_enable_with_disable_reason(self, sm_mock): str(e), ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_set_state_up(self, sm_mock): - sm_mock.side_effect = [False, True] + def test_service_set_state_up(self): + self.set_compute_api_version('2.11') + arglist = [ '--up', self.service.host, @@ -398,9 +380,9 @@ def test_service_set_state_up(self, sm_mock): self.assertNotCalled(self.compute_sdk_client.disable_service) self.assertIsNone(result) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_set_state_down(self, sm_mock): - sm_mock.side_effect = [False, True] + def test_service_set_state_down(self): + self.set_compute_api_version('2.11') + arglist = [ '--down', self.service.host, @@ -420,9 +402,9 @@ def test_service_set_state_down(self, sm_mock): self.assertNotCalled(self.compute_sdk_client.disable_service) self.assertIsNone(result) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_set_enable_and_state_down(self, sm_mock): - sm_mock.side_effect = [False, True] + def test_service_set_enable_and_state_down(self): + self.set_compute_api_version('2.11') + arglist = [ '--enable', '--down', @@ -445,9 +427,9 @@ def test_service_set_enable_and_state_down(self, sm_mock): ) self.assertIsNone(result) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_set_enable_and_state_down_with_exception(self, sm_mock): - sm_mock.side_effect = [False, True] + def test_service_set_enable_and_state_down_with_exception(self): + self.set_compute_api_version('2.11') + arglist = [ '--enable', '--down', @@ -472,11 +454,11 @@ def test_service_set_enable_and_state_down_with_exception(self, sm_mock): None, self.service.host, self.service.binary, True ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_set_2_53_disable_down(self, sm_mock): + def test_service_set_disable_down(self): # Tests disabling and forcing down a compute service with microversion # 2.53 which requires looking up the service by host and binary. - sm_mock.return_value = True + self.set_compute_api_version('2.53') + arglist = [ '--disable', '--down', @@ -503,11 +485,11 @@ def test_service_set_2_53_disable_down(self, sm_mock): ) self.assertIsNone(result) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_set_2_53_disable_reason(self, sm_mock): + def test_service_set_disable_reason(self): # Tests disabling with reason a compute service with microversion # 2.53 which requires looking up the service by host and binary. - sm_mock.return_value = True + self.set_compute_api_version('2.53') + reason = 'earthquake' arglist = [ '--disable', @@ -533,11 +515,11 @@ def test_service_set_2_53_disable_reason(self, sm_mock): ) self.assertIsNone(result) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_service_set_2_53_enable_up(self, sm_mock): + def test_service_set_enable_up(self): # Tests enabling and bringing up a compute service with microversion # 2.53 which requires looking up the service by host and binary. - sm_mock.return_value = True + self.set_compute_api_version('2.53') + arglist = [ '--enable', '--up', diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_cleanup.py b/openstackclient/tests/unit/volume/v3/test_block_storage_cleanup.py index 756ea3257..425b3875c 100644 --- a/openstackclient/tests/unit/volume/v3/test_block_storage_cleanup.py +++ b/openstackclient/tests/unit/volume/v3/test_block_storage_cleanup.py @@ -12,7 +12,6 @@ import uuid -from cinderclient import api_versions from osc_lib import exceptions from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes @@ -40,7 +39,7 @@ def setUp(self): self.cmd = block_storage_cleanup.BlockStorageCleanup(self.app, None) def test_cleanup(self): - self.volume_client.api_version = api_versions.APIVersion('3.24') + self.set_volume_api_version('3.24') arglist = [] verifylist = [ @@ -96,7 +95,7 @@ def test_block_storage_cleanup_pre_324(self): ) def test_cleanup_with_args(self): - self.volume_client.api_version = api_versions.APIVersion('3.24') + self.set_volume_api_version('3.24') fake_cluster = 'fake-cluster' fake_host = 'fake-host' diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py b/openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py index f572b060d..758105c56 100644 --- a/openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py +++ b/openstackclient/tests/unit/volume/v3/test_block_storage_cluster.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from cinderclient import api_versions from osc_lib import exceptions from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes @@ -41,7 +40,7 @@ def setUp(self): ) def test_cluster_list(self): - self.volume_client.api_version = api_versions.APIVersion('3.7') + self.set_volume_api_version('3.7') arglist = [] verifylist = [ @@ -82,7 +81,7 @@ def test_cluster_list(self): ) def test_cluster_list_with_full_options(self): - self.volume_client.api_version = api_versions.APIVersion('3.7') + self.set_volume_api_version('3.7') arglist = [ '--cluster', @@ -152,7 +151,7 @@ def test_cluster_list_with_full_options(self): ) def test_cluster_list_pre_v37(self): - self.volume_client.api_version = api_versions.APIVersion('3.6') + self.set_volume_api_version('3.6') arglist = [] verifylist = [ @@ -215,7 +214,7 @@ def setUp(self): self.cmd = block_storage_cluster.SetBlockStorageCluster(self.app, None) def test_cluster_set(self): - self.volume_client.api_version = api_versions.APIVersion('3.7') + self.set_volume_api_version('3.7') arglist = [ '--enable', @@ -242,7 +241,7 @@ def test_cluster_set(self): ) def test_cluster_set_disable_with_reason(self): - self.volume_client.api_version = api_versions.APIVersion('3.7') + self.set_volume_api_version('3.7') arglist = [ '--binary', @@ -272,7 +271,7 @@ def test_cluster_set_disable_with_reason(self): ) def test_cluster_set_only_with_disable_reason(self): - self.volume_client.api_version = api_versions.APIVersion('3.7') + self.set_volume_api_version('3.7') arglist = [ '--disable-reason', @@ -295,7 +294,7 @@ def test_cluster_set_only_with_disable_reason(self): ) def test_cluster_set_enable_with_disable_reason(self): - self.volume_client.api_version = api_versions.APIVersion('3.7') + self.set_volume_api_version('3.7') arglist = [ '--enable', @@ -319,7 +318,7 @@ def test_cluster_set_enable_with_disable_reason(self): ) def test_cluster_set_pre_v37(self): - self.volume_client.api_version = api_versions.APIVersion('3.6') + self.set_volume_api_version('3.6') arglist = [ '--enable', @@ -385,7 +384,7 @@ def setUp(self): ) def test_cluster_show(self): - self.volume_client.api_version = api_versions.APIVersion('3.7') + self.set_volume_api_version('3.7') arglist = [ '--binary', @@ -409,7 +408,7 @@ def test_cluster_show(self): ) def test_cluster_show_pre_v37(self): - self.volume_client.api_version = api_versions.APIVersion('3.6') + self.set_volume_api_version('3.6') arglist = [ '--binary', 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 028be2af7..58394e18b 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 @@ -12,7 +12,6 @@ # under the License. # -from cinderclient import api_versions import ddt from osc_lib import exceptions @@ -42,7 +41,8 @@ def setUp(self): self.cmd = service.BlockStorageLogLevelList(self.app, None) def test_block_storage_log_level_list(self): - self.volume_client.api_version = api_versions.APIVersion('3.32') + self.set_volume_api_version('3.32') + arglist = [ '--host', self.service_log.host, @@ -113,7 +113,8 @@ def test_block_storage_log_level_list_pre_332(self): ) def test_block_storage_log_level_list_invalid_service_name(self): - self.volume_client.api_version = api_versions.APIVersion('3.32') + self.set_volume_api_version('3.32') + arglist = [ '--host', self.service_log.host, @@ -148,7 +149,8 @@ def setUp(self): self.cmd = service.BlockStorageLogLevelSet(self.app, None) def test_block_storage_log_level_set(self): - self.volume_client.api_version = api_versions.APIVersion('3.32') + self.set_volume_api_version('3.32') + arglist = [ 'ERROR', '--host', @@ -202,7 +204,8 @@ def test_block_storage_log_level_set_pre_332(self): ) def test_block_storage_log_level_set_invalid_service_name(self): - self.volume_client.api_version = api_versions.APIVersion('3.32') + self.set_volume_api_version('3.32') + arglist = [ 'ERROR', '--host', @@ -229,7 +232,8 @@ def test_block_storage_log_level_set_invalid_service_name(self): @ddt.data('WARNING', 'info', 'Error', 'debuG', 'fake-log-level') def test_block_storage_log_level_set_log_level(self, log_level): - self.volume_client.api_version = api_versions.APIVersion('3.32') + self.set_volume_api_version('3.32') + arglist = [ log_level, '--host', diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py b/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py index ca892f891..55d7baf03 100644 --- a/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py +++ b/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py @@ -12,7 +12,6 @@ from unittest import mock -from cinderclient import api_versions from osc_lib import exceptions from openstackclient.tests.unit import utils as tests_utils @@ -46,7 +45,8 @@ def setUp(self): ) def test_block_storage_volume_manage_list(self): - self.volume_client.api_version = api_versions.APIVersion('3.8') + self.set_volume_api_version('3.8') + arglist = [ 'fake_host', ] @@ -87,6 +87,8 @@ def test_block_storage_volume_manage_list(self): ) def test_block_storage_volume_manage_list__pre_v38(self): + self.set_volume_api_version('3.7') + arglist = [ 'fake_host', ] @@ -103,7 +105,8 @@ def test_block_storage_volume_manage_list__pre_v38(self): ) def test_block_storage_volume_manage_list__pre_v317(self): - self.volume_client.api_version = api_versions.APIVersion('3.16') + self.set_volume_api_version('3.16') + arglist = [ '--cluster', 'fake_cluster', @@ -122,7 +125,8 @@ def test_block_storage_volume_manage_list__pre_v317(self): self.assertIn('--cluster', str(exc)) def test_block_storage_volume_manage_list__host_and_cluster(self): - self.volume_client.api_version = api_versions.APIVersion('3.17') + self.set_volume_api_version('3.17') + arglist = [ 'fake_host', '--cluster', @@ -145,7 +149,8 @@ def test_block_storage_volume_manage_list__host_and_cluster(self): def test_block_storage_volume_manage_list__detailed(self): """This option is deprecated.""" - self.volume_client.api_version = api_versions.APIVersion('3.8') + self.set_volume_api_version('3.8') + arglist = [ '--detailed', 'True', @@ -205,9 +210,8 @@ def test_block_storage_volume_manage_list__detailed(self): ) def test_block_storage_volume_manage_list__all_args(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.8' - ) + self.set_volume_api_version('3.8') + arglist = [ 'fake_host', '--long', @@ -285,7 +289,8 @@ def setUp(self): ) def test_block_storage_snapshot_manage_list(self): - self.volume_client.api_version = api_versions.APIVersion('3.8') + self.set_volume_api_version('3.8') + arglist = [ 'fake_host', ] @@ -328,6 +333,8 @@ def test_block_storage_snapshot_manage_list(self): ) def test_block_storage_snapshot_manage_list__pre_v38(self): + self.set_volume_api_version('3.7') + arglist = [ 'fake_host', ] @@ -344,7 +351,8 @@ def test_block_storage_snapshot_manage_list__pre_v38(self): ) def test_block_storage_snapshot_manage_list__pre_v317(self): - self.volume_client.api_version = api_versions.APIVersion('3.16') + self.set_volume_api_version('3.16') + arglist = [ '--cluster', 'fake_cluster', @@ -363,7 +371,8 @@ def test_block_storage_snapshot_manage_list__pre_v317(self): self.assertIn('--cluster', str(exc)) def test_block_storage_snapshot_manage_list__host_and_cluster(self): - self.volume_client.api_version = api_versions.APIVersion('3.17') + self.set_volume_api_version('3.17') + arglist = [ 'fake_host', '--cluster', @@ -385,7 +394,8 @@ def test_block_storage_snapshot_manage_list__host_and_cluster(self): ) def test_block_storage_snapshot_manage_list__detailed(self): - self.volume_client.api_version = api_versions.APIVersion('3.8') + self.set_volume_api_version('3.8') + arglist = [ '--detailed', 'True', @@ -447,9 +457,8 @@ def test_block_storage_snapshot_manage_list__detailed(self): ) def test_block_storage_snapshot_manage_list__all_args(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.8' - ) + self.set_volume_api_version('3.8') + arglist = [ '--long', '--marker', diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py b/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py index 70d8911c4..609458f74 100644 --- a/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py +++ b/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py @@ -10,10 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock - -from cinderclient import api_versions -from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib import exceptions @@ -21,29 +17,7 @@ from openstackclient.volume.v3 import block_storage_resource_filter -class TestBlockStorageResourceFilter(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - patcher = mock.patch.object( - sdk_utils, 'supports_microversion', return_value=True - ) - self.addCleanup(patcher.stop) - self.supports_microversion_mock = patcher.start() - self._set_mock_microversion( - self.app.client_manager.volume.api_version.get_string() - ) - - def _set_mock_microversion(self, mock_v): - """Set a specific microversion for the mock supports_microversion().""" - self.supports_microversion_mock.reset_mock(return_value=True) - self.supports_microversion_mock.side_effect = ( - lambda _, v: api_versions.APIVersion(v) - <= api_versions.APIVersion(mock_v) - ) - - -class TestBlockStorageResourceFilterList(TestBlockStorageResourceFilter): +class TestBlockStorageResourceFilterList(volume_fakes.TestVolume): # The resource filters to be listed fake_resource_filters = volume_fakes.create_resource_filters() @@ -62,7 +36,7 @@ def setUp(self): ) def test_resource_filter_list(self): - self._set_mock_microversion('3.33') + self.set_volume_api_version('3.33') arglist = [] verifylist = [] @@ -85,7 +59,7 @@ def test_resource_filter_list(self): self.volume_sdk_client.resource_filters.assert_called_with() def test_resource_filter_list_pre_v333(self): - self._set_mock_microversion('3.32') + self.set_volume_api_version('3.32') arglist = [] verifylist = [] @@ -99,7 +73,7 @@ def test_resource_filter_list_pre_v333(self): ) -class TestBlockStorageResourceFilterShow(TestBlockStorageResourceFilter): +class TestBlockStorageResourceFilterShow(volume_fakes.TestVolume): # The resource filters to be listed fake_resource_filter = volume_fakes.create_one_resource_filter() @@ -118,7 +92,7 @@ def setUp(self): ) def test_resource_filter_show(self): - self._set_mock_microversion('3.33') + self.set_volume_api_version('3.33') arglist = [ self.fake_resource_filter.resource, @@ -144,7 +118,7 @@ def test_resource_filter_show(self): ) def test_resource_filter_show_pre_v333(self): - self._set_mock_microversion('3.32') + self.set_volume_api_version('3.32') arglist = [ self.fake_resource_filter.resource, diff --git a/openstackclient/tests/unit/volume/v3/test_volume.py b/openstackclient/tests/unit/volume/v3/test_volume.py index 5b01ac324..076de3ac3 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume.py +++ b/openstackclient/tests/unit/volume/v3/test_volume.py @@ -10,17 +10,14 @@ # 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 cinderclient import api_versions 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.test import fakes as sdk_fakes -from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib import utils @@ -32,28 +29,6 @@ from openstackclient.volume.v3 import volume -class BaseVolumeTest(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - patcher = mock.patch.object( - sdk_utils, 'supports_microversion', return_value=True - ) - self.addCleanup(patcher.stop) - self.supports_microversion_mock = patcher.start() - self._set_mock_microversion( - self.volume_client.api_version.get_string() - ) - - def _set_mock_microversion(self, mock_v): - """Set a specific microversion for the mock supports_microversion().""" - self.supports_microversion_mock.reset_mock(return_value=True) - self.supports_microversion_mock.side_effect = ( - lambda _, v: api_versions.APIVersion(v) - <= api_versions.APIVersion(mock_v) - ) - - # TODO(stephenfin): Combine these two test classes class TestVolumeCreateLegacy(volume_fakes.TestVolume): project = identity_fakes.FakeProject.create_one_project() @@ -355,6 +330,8 @@ def test_volume_create_with_snapshot(self): self.assertCountEqual(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 arglist = [ @@ -370,8 +347,6 @@ def test_volume_create_with_backup(self): self.backups_mock.get.return_value = backup - self.volume_client.api_version = api_versions.APIVersion('3.47') - # 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. @@ -758,7 +733,7 @@ def test_volume_create_hints(self): self.assertCountEqual(self.datalist, data) -class TestVolumeCreate(BaseVolumeTest): +class TestVolumeCreate(volume_fakes.TestVolume): columns = ( 'attachments', 'availability_zone', @@ -872,7 +847,7 @@ def test_volume_create_remote_source(self): self.assertCountEqual(self.datalist, data) def test_volume_create_remote_source_pre_v316(self): - self._set_mock_microversion('3.15') + self.set_volume_api_version('3.15') arglist = [ '--remote-source', 'key=val', @@ -895,7 +870,7 @@ def test_volume_create_remote_source_pre_v316(self): ) def test_volume_create_remote_source_host_and_cluster(self): - self._set_mock_microversion('3.16') + self.set_volume_api_version('3.16') arglist = [ '--remote-source', 'key=val', @@ -987,7 +962,7 @@ def test_volume_create_host_no_remote_source(self): ) -class TestVolumeDeleteLegacy(BaseVolumeTest): +class TestVolumeDeleteLegacy(volume_fakes.TestVolume): def setUp(self): super().setUp() @@ -1101,7 +1076,7 @@ def test_volume_delete_with_force(self): self.assertIsNone(result) -class TestVolumeDelete(BaseVolumeTest): +class TestVolumeDelete(volume_fakes.TestVolume): def setUp(self): super().setUp() @@ -2189,7 +2164,7 @@ def test_volume_unset_image_property_fail(self): ) -class TestVolumeSummary(BaseVolumeTest): +class TestVolumeSummary(volume_fakes.TestVolume): columns = [ 'Total Count', 'Total Size', @@ -2211,7 +2186,7 @@ def setUp(self): self.cmd = volume.VolumeSummary(self.app, None) def test_volume_summary(self): - self._set_mock_microversion('3.12') + self.set_volume_api_version('3.12') arglist = [ '--all-projects', ] @@ -2246,7 +2221,7 @@ def test_volume_summary_pre_v312(self): ) def test_volume_summary_with_metadata(self): - self._set_mock_microversion('3.36') + self.set_volume_api_version('3.36') metadata = {**self.volume_a.metadata, **self.volume_b.metadata} self.summary = sdk_fakes.generate_fake_resource( @@ -2282,7 +2257,7 @@ def test_volume_summary_with_metadata(self): self.assertCountEqual(datalist, tuple(data)) -class TestVolumeRevertToSnapshot(BaseVolumeTest): +class TestVolumeRevertToSnapshot(volume_fakes.TestVolume): def setUp(self): super().setUp() @@ -2314,7 +2289,7 @@ def test_volume_revert_to_snapshot_pre_v340(self): ) def test_volume_revert_to_snapshot(self): - self._set_mock_microversion('3.40') + self.set_volume_api_version('3.40') arglist = [ self.snapshot.id, ] diff --git a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py index 10d6a9862..320cd7969 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py @@ -10,7 +10,6 @@ # License for the specific language governing permissions and limitations # under the License. -from cinderclient import api_versions from osc_lib.cli import format_columns from osc_lib import exceptions @@ -78,7 +77,7 @@ def setUp(self): self.cmd = volume_attachment.CreateVolumeAttachment(self.app, None) def test_volume_attachment_create(self): - self.volume_client.api_version = api_versions.APIVersion('3.27') + self.set_volume_api_version('3.27') arglist = [ self.volume.id, @@ -113,7 +112,7 @@ def test_volume_attachment_create(self): self.assertCountEqual(self.data, data) def test_volume_attachment_create_with_connect(self): - self.volume_client.api_version = api_versions.APIVersion('3.54') + self.set_volume_api_version('3.54') arglist = [ self.volume.id, @@ -176,7 +175,7 @@ def test_volume_attachment_create_with_connect(self): self.assertCountEqual(self.data, data) def test_volume_attachment_create_pre_v327(self): - self.volume_client.api_version = api_versions.APIVersion('3.26') + self.set_volume_api_version('3.26') arglist = [ self.volume.id, @@ -196,7 +195,7 @@ def test_volume_attachment_create_pre_v327(self): ) def test_volume_attachment_create_with_mode_pre_v354(self): - self.volume_client.api_version = api_versions.APIVersion('3.53') + self.set_volume_api_version('3.53') arglist = [ self.volume.id, @@ -219,7 +218,7 @@ def test_volume_attachment_create_with_mode_pre_v354(self): ) def test_volume_attachment_create_with_connect_missing_arg(self): - self.volume_client.api_version = api_versions.APIVersion('3.54') + self.set_volume_api_version('3.54') arglist = [ self.volume.id, @@ -254,7 +253,7 @@ def setUp(self): self.cmd = volume_attachment.DeleteVolumeAttachment(self.app, None) def test_volume_attachment_delete(self): - self.volume_client.api_version = api_versions.APIVersion('3.27') + self.set_volume_api_version('3.27') arglist = [ self.volume_attachment.id, @@ -272,7 +271,7 @@ def test_volume_attachment_delete(self): self.assertIsNone(result) def test_volume_attachment_delete_pre_v327(self): - self.volume_client.api_version = api_versions.APIVersion('3.26') + self.set_volume_api_version('3.26') arglist = [ self.volume_attachment.id, @@ -324,7 +323,7 @@ def setUp(self): self.cmd = volume_attachment.SetVolumeAttachment(self.app, None) def test_volume_attachment_set(self): - self.volume_client.api_version = api_versions.APIVersion('3.27') + self.set_volume_api_version('3.27') arglist = [ self.volume_attachment.id, @@ -376,7 +375,7 @@ def test_volume_attachment_set(self): self.assertCountEqual(self.data, data) def test_volume_attachment_set_pre_v327(self): - self.volume_client.api_version = api_versions.APIVersion('3.26') + self.set_volume_api_version('3.26') arglist = [ self.volume_attachment.id, @@ -408,7 +407,7 @@ def setUp(self): self.cmd = volume_attachment.CompleteVolumeAttachment(self.app, None) def test_volume_attachment_complete(self): - self.volume_client.api_version = api_versions.APIVersion('3.44') + self.set_volume_api_version('3.44') arglist = [ self.volume_attachment.id, @@ -426,7 +425,7 @@ def test_volume_attachment_complete(self): self.assertIsNone(result) def test_volume_attachment_complete_pre_v344(self): - self.volume_client.api_version = api_versions.APIVersion('3.43') + self.set_volume_api_version('3.43') arglist = [ self.volume_attachment.id, @@ -475,7 +474,7 @@ def setUp(self): self.cmd = volume_attachment.ListVolumeAttachment(self.app, None) def test_volume_attachment_list(self): - self.volume_client.api_version = api_versions.APIVersion('3.27') + self.set_volume_api_version('3.27') arglist = [] verifylist = [ @@ -504,7 +503,7 @@ def test_volume_attachment_list(self): self.assertCountEqual(tuple(self.data), data) def test_volume_attachment_list_with_options(self): - self.volume_client.api_version = api_versions.APIVersion('3.27') + self.set_volume_api_version('3.27') arglist = [ '--project', @@ -544,7 +543,7 @@ def test_volume_attachment_list_with_options(self): self.assertCountEqual(tuple(self.data), data) def test_volume_attachment_list_pre_v327(self): - self.volume_client.api_version = api_versions.APIVersion('3.26') + self.set_volume_api_version('3.26') arglist = [] verifylist = [ diff --git a/openstackclient/tests/unit/volume/v3/test_volume_backup.py b/openstackclient/tests/unit/volume/v3/test_volume_backup.py index dfa3084dd..d4e7e7924 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_backup.py @@ -10,13 +10,9 @@ # 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 cinderclient import api_versions -from openstack import utils as sdk_utils from osc_lib import exceptions from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes @@ -37,29 +33,7 @@ def setUp(self): self.restores_mock.reset_mock() -class TestBackup(volume_fakes.TestVolume): - def setUp(self): - super().setUp() - - patcher = mock.patch.object( - sdk_utils, 'supports_microversion', return_value=True - ) - self.addCleanup(patcher.stop) - self.supports_microversion_mock = patcher.start() - self._set_mock_microversion( - self.app.client_manager.volume.api_version.get_string() - ) - - def _set_mock_microversion(self, mock_v): - """Set a specific microversion for the mock supports_microversion().""" - self.supports_microversion_mock.reset_mock(return_value=True) - self.supports_microversion_mock.side_effect = ( - lambda _, v: api_versions.APIVersion(v) - <= api_versions.APIVersion(mock_v) - ) - - -class TestBackupCreate(TestBackup): +class TestBackupCreate(volume_fakes.TestVolume): volume = volume_fakes.create_one_volume() snapshot = volume_fakes.create_one_snapshot() new_backup = volume_fakes.create_one_backup( @@ -127,7 +101,7 @@ def test_backup_create(self): self.assertEqual(self.data, data) def test_backup_create_with_properties(self): - self._set_mock_microversion('3.43') + self.set_volume_api_version('3.43') arglist = [ "--property", @@ -157,7 +131,7 @@ def test_backup_create_with_properties(self): self.assertEqual(self.data, data) def test_backup_create_with_properties_pre_v343(self): - self._set_mock_microversion('3.42') + self.set_volume_api_version('3.42') arglist = [ "--property", @@ -178,7 +152,7 @@ def test_backup_create_with_properties_pre_v343(self): self.assertIn("--os-volume-api-version 3.43 or greater", str(exc)) def test_backup_create_with_availability_zone(self): - self._set_mock_microversion('3.51') + self.set_volume_api_version('3.51') arglist = [ "--availability-zone", @@ -206,7 +180,7 @@ def test_backup_create_with_availability_zone(self): self.assertEqual(self.data, data) def test_backup_create_with_availability_zone_pre_v351(self): - self._set_mock_microversion('3.50') + self.set_volume_api_version('3.50') arglist = [ "--availability-zone", @@ -253,7 +227,7 @@ def test_backup_create_without_name(self): self.assertEqual(self.data, data) -class TestBackupDelete(TestBackup): +class TestBackupDelete(volume_fakes.TestVolume): backups = volume_fakes.create_backups(count=2) def setUp(self): @@ -346,7 +320,7 @@ def test_delete_multiple_backups_with_exception(self): ) -class TestBackupList(TestBackup): +class TestBackupList(volume_fakes.TestVolume): volume = volume_fakes.create_one_volume() backups = volume_fakes.create_backups( attrs={'volume_id': volume.name}, count=3 @@ -479,7 +453,7 @@ def test_backup_list_with_options(self): self.assertCountEqual(self.data_long, list(data)) -class TestBackupRestore(TestBackup): +class TestBackupRestore(volume_fakes.TestVolume): volume = volume_fakes.create_one_volume() backup = volume_fakes.create_one_backup( attrs={'volume_id': volume.id}, @@ -593,7 +567,7 @@ def setUp(self): self.cmd = volume_backup.SetVolumeBackup(self.app, None) def test_backup_set_name(self): - self.volume_client.api_version = api_versions.APIVersion('3.9') + self.set_volume_api_version('3.9') arglist = [ '--name', @@ -615,7 +589,7 @@ def test_backup_set_name(self): self.assertIsNone(result) def test_backup_set_name_pre_v39(self): - self.volume_client.api_version = api_versions.APIVersion('3.8') + self.set_volume_api_version('3.8') arglist = [ '--name', @@ -634,7 +608,7 @@ def test_backup_set_name_pre_v39(self): self.assertIn("--os-volume-api-version 3.9 or greater", str(exc)) def test_backup_set_description(self): - self.volume_client.api_version = api_versions.APIVersion('3.9') + self.set_volume_api_version('3.9') arglist = [ '--description', @@ -658,7 +632,7 @@ def test_backup_set_description(self): self.assertIsNone(result) def test_backup_set_description_pre_v39(self): - self.volume_client.api_version = api_versions.APIVersion('3.8') + self.set_volume_api_version('3.8') arglist = [ '--description', @@ -707,7 +681,7 @@ def test_backup_set_state_failed(self): ) def test_backup_set_no_property(self): - self.volume_client.api_version = api_versions.APIVersion('3.43') + self.set_volume_api_version('3.43') arglist = [ '--no-property', @@ -731,7 +705,7 @@ def test_backup_set_no_property(self): self.assertIsNone(result) def test_backup_set_no_property_pre_v343(self): - self.volume_client.api_version = api_versions.APIVersion('3.42') + self.set_volume_api_version('3.42') arglist = [ '--no-property', @@ -749,7 +723,7 @@ def test_backup_set_no_property_pre_v343(self): self.assertIn("--os-volume-api-version 3.43 or greater", str(exc)) def test_backup_set_property(self): - self.volume_client.api_version = api_versions.APIVersion('3.43') + self.set_volume_api_version('3.43') arglist = [ '--property', @@ -774,7 +748,7 @@ def test_backup_set_property(self): self.assertIsNone(result) def test_backup_set_property_pre_v343(self): - self.volume_client.api_version = api_versions.APIVersion('3.42') + self.set_volume_api_version('3.42') arglist = [ '--property', @@ -807,7 +781,7 @@ def setUp(self): self.cmd = volume_backup.UnsetVolumeBackup(self.app, None) def test_backup_unset_property(self): - self.volume_client.api_version = api_versions.APIVersion('3.43') + self.set_volume_api_version('3.43') arglist = [ '--property', @@ -832,7 +806,7 @@ def test_backup_unset_property(self): self.assertIsNone(result) def test_backup_unset_property_pre_v343(self): - self.volume_client.api_version = api_versions.APIVersion('3.42') + self.set_volume_api_version('3.42') arglist = [ '--property', @@ -851,7 +825,7 @@ def test_backup_unset_property_pre_v343(self): self.assertIn("--os-volume-api-version 3.43 or greater", str(exc)) -class TestBackupShow(TestBackup): +class TestBackupShow(volume_fakes.TestVolume): backup = volume_fakes.create_one_backup() columns = ( diff --git a/openstackclient/tests/unit/volume/v3/test_volume_group.py b/openstackclient/tests/unit/volume/v3/test_volume_group.py index 444ea707f..3b64ad95c 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_group.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_group.py @@ -12,7 +12,6 @@ from unittest import mock -from cinderclient import api_versions from osc_lib import exceptions from openstackclient.tests.unit import utils as tests_utils @@ -96,7 +95,7 @@ def setUp(self): self.cmd = volume_group.CreateVolumeGroup(self.app, None) def test_volume_group_create(self): - self.volume_client.api_version = api_versions.APIVersion('3.13') + self.set_volume_api_version('3.13') arglist = [ '--volume-group-type', @@ -132,7 +131,7 @@ def test_volume_group_create(self): self.assertCountEqual(self.data, data) def test_volume_group_create__legacy(self): - self.volume_client.api_version = api_versions.APIVersion('3.13') + self.set_volume_api_version('3.13') arglist = [ self.fake_volume_group_type.id, @@ -172,7 +171,7 @@ def test_volume_group_create__legacy(self): ) def test_volume_group_create_no_volume_type(self): - self.volume_client.api_version = api_versions.APIVersion('3.13') + self.set_volume_api_version('3.13') arglist = [ '--volume-group-type', @@ -194,7 +193,7 @@ def test_volume_group_create_no_volume_type(self): ) def test_volume_group_create_with_options(self): - self.volume_client.api_version = api_versions.APIVersion('3.13') + self.set_volume_api_version('3.13') arglist = [ '--volume-group-type', @@ -236,7 +235,7 @@ def test_volume_group_create_with_options(self): self.assertCountEqual(self.data, data) def test_volume_group_create_pre_v313(self): - self.volume_client.api_version = api_versions.APIVersion('3.12') + self.set_volume_api_version('3.12') arglist = [ '--volume-group-type', @@ -261,7 +260,7 @@ def test_volume_group_create_pre_v313(self): ) def test_volume_group_create_from_source_group(self): - self.volume_client.api_version = api_versions.APIVersion('3.14') + self.set_volume_api_version('3.14') arglist = [ '--source-group', @@ -290,7 +289,7 @@ def test_volume_group_create_from_source_group(self): self.assertCountEqual(self.data, data) def test_volume_group_create_from_group_snapshot(self): - self.volume_client.api_version = api_versions.APIVersion('3.14') + self.set_volume_api_version('3.14') arglist = [ '--group-snapshot', @@ -319,7 +318,7 @@ def test_volume_group_create_from_group_snapshot(self): self.assertCountEqual(self.data, data) def test_volume_group_create_from_src_pre_v314(self): - self.volume_client.api_version = api_versions.APIVersion('3.13') + self.set_volume_api_version('3.13') arglist = [ '--source-group', @@ -338,7 +337,7 @@ def test_volume_group_create_from_src_pre_v314(self): ) def test_volume_group_create_from_src_source_group_group_snapshot(self): - self.volume_client.api_version = api_versions.APIVersion('3.14') + self.set_volume_api_version('3.14') arglist = [ '--source-group', @@ -376,7 +375,7 @@ def setUp(self): self.cmd = volume_group.DeleteVolumeGroup(self.app, None) def test_volume_group_delete(self): - self.volume_client.api_version = api_versions.APIVersion('3.13') + self.set_volume_api_version('3.13') arglist = [ self.fake_volume_group.id, @@ -397,7 +396,7 @@ def test_volume_group_delete(self): self.assertIsNone(result) def test_volume_group_delete_pre_v313(self): - self.volume_client.api_version = api_versions.APIVersion('3.12') + self.set_volume_api_version('3.12') arglist = [ self.fake_volume_group.id, @@ -455,7 +454,7 @@ def setUp(self): self.cmd = volume_group.SetVolumeGroup(self.app, None) def test_volume_group_set(self): - self.volume_client.api_version = api_versions.APIVersion('3.13') + self.set_volume_api_version('3.13') arglist = [ self.fake_volume_group.id, @@ -482,7 +481,7 @@ def test_volume_group_set(self): self.assertCountEqual(self.data, data) def test_volume_group_with_enable_replication_option(self): - self.volume_client.api_version = api_versions.APIVersion('3.38') + self.set_volume_api_version('3.38') arglist = [ self.fake_volume_group.id, @@ -503,7 +502,7 @@ def test_volume_group_with_enable_replication_option(self): self.assertCountEqual(self.data, data) def test_volume_group_set_pre_v313(self): - self.volume_client.api_version = api_versions.APIVersion('3.12') + self.set_volume_api_version('3.12') arglist = [ self.fake_volume_group.id, @@ -527,7 +526,7 @@ def test_volume_group_set_pre_v313(self): ) def test_volume_group_with_enable_replication_option_pre_v338(self): - self.volume_client.api_version = api_versions.APIVersion('3.37') + self.set_volume_api_version('3.37') arglist = [ self.fake_volume_group.id, @@ -572,7 +571,7 @@ def setUp(self): self.cmd = volume_group.ListVolumeGroup(self.app, None) def test_volume_group_list(self): - self.volume_client.api_version = api_versions.APIVersion('3.13') + self.set_volume_api_version('3.13') arglist = [ '--all-projects', @@ -593,7 +592,7 @@ def test_volume_group_list(self): self.assertCountEqual(tuple(self.data), data) def test_volume_group_list_pre_v313(self): - self.volume_client.api_version = api_versions.APIVersion('3.12') + self.set_volume_api_version('3.12') arglist = [ '--all-projects', @@ -623,7 +622,7 @@ def setUp(self): self.cmd = volume_group.FailoverVolumeGroup(self.app, None) def test_volume_group_failover(self): - self.volume_client.api_version = api_versions.APIVersion('3.38') + self.set_volume_api_version('3.38') arglist = [ self.fake_volume_group.id, @@ -648,7 +647,7 @@ def test_volume_group_failover(self): self.assertIsNone(result) def test_volume_group_failover_pre_v338(self): - self.volume_client.api_version = api_versions.APIVersion('3.37') + self.set_volume_api_version('3.37') arglist = [ self.fake_volume_group.id, diff --git a/openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py b/openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py index baa50980b..23ba4ec97 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_group_snapshot.py @@ -10,28 +10,15 @@ # License for the specific language governing permissions and limitations # under the License. -from unittest import mock - -from keystoneauth1 import discover from openstack.block_storage.v3 import group as _group from openstack.block_storage.v3 import group_snapshot as _group_snapshot from openstack.test import fakes as sdk_fakes -from openstack import utils as sdk_utils from osc_lib import exceptions from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes from openstackclient.volume.v3 import volume_group_snapshot -def fake_supports_microversion(mocked_version): - def supports_microversion(adapter, microversion, raise_exception=False): - required = discover.normalize_version_number(microversion) - candidate = discover.normalize_version_number(mocked_version) - return discover.version_match(required, candidate) - - return supports_microversion - - class TestVolumeGroupSnapshotCreate(volume_fakes.TestVolume): fake_volume_group = sdk_fakes.generate_fake_resource(_group.Group) fake_volume_group_snapshot = sdk_fakes.generate_fake_resource( @@ -70,9 +57,8 @@ def setUp(self): self.app, None ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_volume_group_snapshot_create(self, mock_mv): - mock_mv.side_effect = fake_supports_microversion('3.14') + def test_volume_group_snapshot_create(self): + self.set_volume_api_version('3.14') arglist = [ self.fake_volume_group.id, @@ -99,9 +85,8 @@ def test_volume_group_snapshot_create(self, mock_mv): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_volume_group_snapshot_create_with_options(self, mock_mv): - mock_mv.side_effect = fake_supports_microversion('3.14') + def test_volume_group_snapshot_create_with_options(self): + self.set_volume_api_version('3.14') arglist = [ self.fake_volume_group.id, @@ -132,9 +117,8 @@ def test_volume_group_snapshot_create_with_options(self, mock_mv): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_volume_group_snapshot_create_pre_v314(self, mock_mv): - mock_mv.side_effect = fake_supports_microversion('3.13') + def test_volume_group_snapshot_create_pre_v314(self): + self.set_volume_api_version('3.13') arglist = [ self.fake_volume_group.id, @@ -174,9 +158,8 @@ def setUp(self): self.app, None ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_volume_group_snapshot_delete(self, mock_mv): - mock_mv.side_effect = fake_supports_microversion('3.14') + def test_volume_group_snapshot_delete(self): + self.set_volume_api_version('3.14') arglist = [ self.fake_volume_group_snapshot.id, @@ -193,9 +176,8 @@ def test_volume_group_snapshot_delete(self, mock_mv): ) self.assertIsNone(result) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_volume_group_snapshot_delete_pre_v314(self, mock_mv): - mock_mv.side_effect = fake_supports_microversion('3.13') + def test_volume_group_snapshot_delete_pre_v314(self): + self.set_volume_api_version('3.13') arglist = [ self.fake_volume_group_snapshot.id, @@ -249,9 +231,8 @@ def setUp(self): self.app, None ) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_volume_group_snapshot_list(self, mock_mv): - mock_mv.side_effect = fake_supports_microversion('3.14') + def test_volume_group_snapshot_list(self): + self.set_volume_api_version('3.14') arglist = [ '--all-projects', @@ -269,9 +250,8 @@ def test_volume_group_snapshot_list(self, mock_mv): self.assertEqual(self.columns, columns) self.assertCountEqual(tuple(self.data), data) - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_volume_group_snapshot_list_pre_v314(self, mock_mv): - mock_mv.side_effect = fake_supports_microversion('3.13') + def test_volume_group_snapshot_list_pre_v314(self): + self.set_volume_api_version('3.13') arglist = [] verifylist = [ diff --git a/openstackclient/tests/unit/volume/v3/test_volume_group_type.py b/openstackclient/tests/unit/volume/v3/test_volume_group_type.py index 62c41962d..21db03d77 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_group_type.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_group_type.py @@ -12,7 +12,6 @@ from unittest import mock -from cinderclient import api_versions from osc_lib.cli import format_columns from osc_lib import exceptions @@ -56,7 +55,7 @@ def setUp(self): self.cmd = volume_group_type.CreateVolumeGroupType(self.app, None) def test_volume_group_type_create(self): - self.volume_client.api_version = api_versions.APIVersion('3.11') + self.set_volume_api_version('3.11') arglist = [ self.fake_volume_group_type.name, @@ -77,7 +76,7 @@ def test_volume_group_type_create(self): self.assertCountEqual(self.data, data) def test_volume_group_type_create_with_options(self): - self.volume_client.api_version = api_versions.APIVersion('3.11') + self.set_volume_api_version('3.11') arglist = [ self.fake_volume_group_type.name, @@ -101,7 +100,7 @@ def test_volume_group_type_create_with_options(self): self.assertCountEqual(self.data, data) def test_volume_group_type_create_pre_v311(self): - self.volume_client.api_version = api_versions.APIVersion('3.10') + self.set_volume_api_version('3.10') arglist = [ self.fake_volume_group_type.name, @@ -135,7 +134,7 @@ def setUp(self): self.cmd = volume_group_type.DeleteVolumeGroupType(self.app, None) def test_volume_group_type_delete(self): - self.volume_client.api_version = api_versions.APIVersion('3.11') + self.set_volume_api_version('3.11') arglist = [ self.fake_volume_group_type.id, @@ -153,7 +152,7 @@ def test_volume_group_type_delete(self): self.assertIsNone(result) def test_volume_group_type_delete_pre_v311(self): - self.volume_client.api_version = api_versions.APIVersion('3.10') + self.set_volume_api_version('3.10') arglist = [ self.fake_volume_group_type.id, @@ -208,7 +207,7 @@ def setUp(self): self.cmd = volume_group_type.SetVolumeGroupType(self.app, None) def test_volume_group_type_set(self): - self.volume_client.api_version = api_versions.APIVersion('3.11') + self.set_volume_api_version('3.11') self.fake_volume_group_type.set_keys.return_value = None @@ -247,7 +246,7 @@ def test_volume_group_type_set(self): self.assertCountEqual(self.data, data) def test_volume_group_type_with_no_property_option(self): - self.volume_client.api_version = api_versions.APIVersion('3.11') + self.set_volume_api_version('3.11') arglist = [ self.fake_volume_group_type.id, @@ -278,7 +277,7 @@ def test_volume_group_type_with_no_property_option(self): self.assertCountEqual(self.data, data) def test_volume_group_type_set_pre_v311(self): - self.volume_client.api_version = api_versions.APIVersion('3.10') + self.set_volume_api_version('3.10') arglist = [ self.fake_volume_group_type.id, @@ -335,7 +334,7 @@ def setUp(self): self.cmd = volume_group_type.UnsetVolumeGroupType(self.app, None) def test_volume_group_type_unset(self): - self.volume_client.api_version = api_versions.APIVersion('3.11') + self.set_volume_api_version('3.11') arglist = [ self.fake_volume_group_type.id, @@ -363,7 +362,7 @@ def test_volume_group_type_unset(self): self.assertCountEqual(self.data, data) def test_volume_group_type_unset_pre_v311(self): - self.volume_client.api_version = api_versions.APIVersion('3.10') + self.set_volume_api_version('3.10') arglist = [ self.fake_volume_group_type.id, @@ -416,7 +415,7 @@ def setUp(self): self.cmd = volume_group_type.ListVolumeGroupType(self.app, None) def test_volume_group_type_list(self): - self.volume_client.api_version = api_versions.APIVersion('3.11') + self.set_volume_api_version('3.11') arglist = [] verifylist = [ @@ -431,7 +430,7 @@ def test_volume_group_type_list(self): self.assertCountEqual(tuple(self.data), data) def test_volume_group_type_list_with_default_option(self): - self.volume_client.api_version = api_versions.APIVersion('3.11') + self.set_volume_api_version('3.11') arglist = [ '--default', @@ -448,7 +447,7 @@ def test_volume_group_type_list_with_default_option(self): self.assertCountEqual(tuple([self.data[0]]), data) def test_volume_group_type_list_pre_v311(self): - self.volume_client.api_version = api_versions.APIVersion('3.10') + self.set_volume_api_version('3.10') arglist = [] verifylist = [] diff --git a/openstackclient/tests/unit/volume/v3/test_volume_message.py b/openstackclient/tests/unit/volume/v3/test_volume_message.py index 63349f49d..4b4f6e41d 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_message.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_message.py @@ -12,7 +12,6 @@ from unittest.mock import call -from cinderclient import api_versions from osc_lib import exceptions from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes @@ -46,7 +45,7 @@ def setUp(self): self.cmd = volume_message.DeleteMessage(self.app, None) def test_message_delete(self): - self.volume_client.api_version = api_versions.APIVersion('3.3') + self.set_volume_api_version('3.3') arglist = [ self.fake_messages[0].id, @@ -64,7 +63,7 @@ def test_message_delete(self): self.assertIsNone(result) def test_message_delete_multiple_messages(self): - self.volume_client.api_version = api_versions.APIVersion('3.3') + self.set_volume_api_version('3.3') arglist = [ self.fake_messages[0].id, @@ -84,7 +83,7 @@ def test_message_delete_multiple_messages(self): self.assertIsNone(result) def test_message_delete_multiple_messages_with_exception(self): - self.volume_client.api_version = api_versions.APIVersion('3.3') + self.set_volume_api_version('3.3') arglist = [ self.fake_messages[0].id, @@ -114,7 +113,7 @@ def test_message_delete_multiple_messages_with_exception(self): self.assertEqual(2, self.volume_messages_mock.delete.call_count) def test_message_delete_pre_v33(self): - self.volume_client.api_version = api_versions.APIVersion('3.2') + self.set_volume_api_version('3.2') arglist = [ self.fake_messages[0].id, @@ -172,7 +171,7 @@ def setUp(self): self.cmd = volume_message.ListMessages(self.app, None) def test_message_list(self): - self.volume_client.api_version = api_versions.APIVersion('3.3') + self.set_volume_api_version('3.3') arglist = [] verifylist = [ @@ -196,7 +195,7 @@ def test_message_list(self): self.assertCountEqual(self.data, list(data)) def test_message_list_with_options(self): - self.volume_client.api_version = api_versions.APIVersion('3.3') + self.set_volume_api_version('3.3') arglist = [ '--project', @@ -227,7 +226,7 @@ def test_message_list_with_options(self): self.assertCountEqual(self.data, list(data)) def test_message_list_pre_v33(self): - self.volume_client.api_version = api_versions.APIVersion('3.2') + self.set_volume_api_version('3.2') arglist = [] verifylist = [ @@ -280,7 +279,7 @@ def setUp(self): self.cmd = volume_message.ShowMessage(self.app, None) def test_message_show(self): - self.volume_client.api_version = api_versions.APIVersion('3.3') + self.set_volume_api_version('3.3') arglist = [self.fake_message.id] verifylist = [('message_id', self.fake_message.id)] @@ -293,7 +292,7 @@ def test_message_show(self): self.assertEqual(self.data, data) def test_message_show_pre_v33(self): - self.volume_client.api_version = api_versions.APIVersion('3.2') + self.set_volume_api_version('3.2') arglist = [self.fake_message.id] verifylist = [('message_id', self.fake_message.id)] 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 078124788..1871bde73 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_transfer_request.py @@ -15,7 +15,6 @@ from unittest import mock from unittest.mock import call -from cinderclient import api_versions from osc_lib import exceptions from osc_lib import utils @@ -177,7 +176,7 @@ def test_transfer_create_with_name(self): self.assertEqual(self.data, data) def test_transfer_create_with_no_snapshots(self): - self.volume_client.api_version = api_versions.APIVersion('3.55') + self.set_volume_api_version('3.55') arglist = [ '--no-snapshots', @@ -199,7 +198,7 @@ def test_transfer_create_with_no_snapshots(self): self.assertEqual(self.data, data) def test_transfer_create_pre_v355(self): - self.volume_client.api_version = api_versions.APIVersion('3.54') + self.set_volume_api_version('3.54') arglist = [ '--no-snapshots', diff --git a/openstackclient/tests/unit/volume/v3/test_volume_type.py b/openstackclient/tests/unit/volume/v3/test_volume_type.py index 5f791da25..828f8b090 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_type.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_type.py @@ -14,7 +14,6 @@ from unittest import mock from unittest.mock import call -from cinderclient import api_versions from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib import utils @@ -434,9 +433,7 @@ def test_type_list_with_default_option(self): self.assertCountEqual(self.data_with_default_type, list(data)) def test_type_list_with_properties(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.52' - ) + self.set_volume_api_version('3.52') arglist = [ "--property", @@ -477,9 +474,7 @@ def test_type_list_with_properties(self): self.assertCountEqual(self.data, list(data)) def test_type_list_with_properties_pre_v352(self): - self.app.client_manager.volume.api_version = api_versions.APIVersion( - '3.51' - ) + self.set_volume_api_version('3.51') arglist = [ "--property", From 9f30ee9af2f43d0754956454ab08cb0a10e847f4 Mon Sep 17 00:00:00 2001 From: whoami-rajat Date: Wed, 12 Jul 2023 17:04:11 +0530 Subject: [PATCH 140/403] volume: Migrate 'volume attachment *' to SDK This patch migrates the volume attachment create, get, list, delete, update and complete commands to SDK. Change-Id: Ib237d25cc1c3fc72946b9d088ff3447433162130 --- openstackclient/tests/unit/volume/v3/fakes.py | 17 +++-- .../unit/volume/v3/test_volume_attachment.py | 73 +++++++++---------- .../volume/v3/volume_attachment.py | 60 +++++++-------- ...-attachment-commands-4309409bca1ca5d4.yaml | 4 + 4 files changed, 79 insertions(+), 75 deletions(-) create mode 100644 releasenotes/notes/migrate-volume-attachment-commands-4309409bca1ca5d4.yaml diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index 925f2f773..6a9f1bb0e 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -24,6 +24,7 @@ from openstack.block_storage.v3 import extension as _extension from openstack.block_storage.v3 import resource_filter as _filters from openstack.block_storage.v3 import volume as _volume +from openstack.compute.v2 import _proxy as _compute_proxy from openstack.image.v2 import _proxy as _image_proxy from openstackclient.tests.unit import fakes @@ -129,16 +130,16 @@ class TestVolume( def setUp(self): super().setUp() - # avoid circular imports - from openstackclient.tests.unit.compute.v2 import ( - fakes as compute_fakes, + # 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.sdk_connection.compute = mock.Mock( + _compute_proxy.Proxy ) - - self.app.client_manager.compute = compute_fakes.FakeComputev2Client( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, + self.compute_sdk_client = ( + self.app.client_manager.sdk_connection.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 320cd7969..aca2e3399 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_attachment.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_attachment.py @@ -23,22 +23,12 @@ class TestVolumeAttachment(volume_fakes.TestVolume): def setUp(self): super().setUp() - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - - self.volume_attachments_mock = self.volume_client.attachments - self.volume_attachments_mock.reset_mock() - - self.projects_mock = self.identity_client.projects - self.projects_mock.reset_mock() - - self.servers_mock = self.compute_client.servers - self.servers_mock.reset_mock() + self.projects_mock = self.app.client_manager.identity.projects class TestVolumeAttachmentCreate(TestVolumeAttachment): volume = volume_fakes.create_one_volume() - server = compute_fakes.create_one_server() + server = compute_fakes.create_one_sdk_server() volume_attachment = volume_fakes.create_one_volume_attachment( attrs={'instance': server.id, 'volume_id': volume.id}, ) @@ -67,12 +57,11 @@ class TestVolumeAttachmentCreate(TestVolumeAttachment): def setUp(self): super().setUp() - self.volumes_mock.get.return_value = self.volume - self.servers_mock.get.return_value = self.server - # VolumeAttachmentManager.create returns a dict - self.volume_attachments_mock.create.return_value = ( + self.volume_sdk_client.find_volume.return_value = self.volume + self.volume_sdk_client.create_attachment.return_value = ( self.volume_attachment.to_dict() ) + self.compute_sdk_client.find_server.return_value = self.server self.cmd = volume_attachment.CreateVolumeAttachment(self.app, None) @@ -100,13 +89,17 @@ def test_volume_attachment_create(self): columns, data = self.cmd.take_action(parsed_args) - self.volumes_mock.get.assert_called_once_with(self.volume.id) - self.servers_mock.get.assert_called_once_with(self.server.id) - self.volume_attachments_mock.create.assert_called_once_with( + 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.server.id, ignore_missing=False + ) + self.volume_sdk_client.create_attachment.assert_called_once_with( self.volume.id, - {}, - self.server.id, - None, + connector={}, + instance=self.server.id, + mode=None, ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) @@ -163,13 +156,17 @@ def test_volume_attachment_create_with_connect(self): ] ) - self.volumes_mock.get.assert_called_once_with(self.volume.id) - self.servers_mock.get.assert_called_once_with(self.server.id) - self.volume_attachments_mock.create.assert_called_once_with( + 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.server.id, ignore_missing=False + ) + self.volume_sdk_client.create_attachment.assert_called_once_with( self.volume.id, - connect_info, - self.server.id, - 'null', + connector=connect_info, + instance=self.server.id, + mode='null', ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) @@ -248,7 +245,7 @@ class TestVolumeAttachmentDelete(TestVolumeAttachment): def setUp(self): super().setUp() - self.volume_attachments_mock.delete.return_value = None + self.volume_sdk_client.delete_attachment.return_value = None self.cmd = volume_attachment.DeleteVolumeAttachment(self.app, None) @@ -265,7 +262,7 @@ def test_volume_attachment_delete(self): result = self.cmd.take_action(parsed_args) - self.volume_attachments_mock.delete.assert_called_once_with( + self.volume_sdk_client.delete_attachment.assert_called_once_with( self.volume_attachment.id, ) self.assertIsNone(result) @@ -316,7 +313,7 @@ class TestVolumeAttachmentSet(TestVolumeAttachment): def setUp(self): super().setUp() - self.volume_attachments_mock.update.return_value = ( + self.volume_sdk_client.update_attachment.return_value = ( self.volume_attachment ) @@ -367,9 +364,9 @@ def test_volume_attachment_set(self): ] ) - self.volume_attachments_mock.update.assert_called_once_with( + self.volume_sdk_client.update_attachment.assert_called_once_with( self.volume_attachment.id, - connect_info, + connector=connect_info, ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) @@ -402,7 +399,7 @@ class TestVolumeAttachmentComplete(TestVolumeAttachment): def setUp(self): super().setUp() - self.volume_attachments_mock.complete.return_value = None + self.volume_sdk_client.complete_attachment.return_value = None self.cmd = volume_attachment.CompleteVolumeAttachment(self.app, None) @@ -419,7 +416,7 @@ def test_volume_attachment_complete(self): result = self.cmd.take_action(parsed_args) - self.volume_attachments_mock.complete.assert_called_once_with( + self.volume_sdk_client.complete_attachment.assert_called_once_with( self.volume_attachment.id, ) self.assertIsNone(result) @@ -467,7 +464,7 @@ def setUp(self): super().setUp() self.projects_mock.get.return_value = self.project - self.volume_attachments_mock.list.return_value = ( + self.volume_sdk_client.attachments.return_value = ( self.volume_attachments ) @@ -489,7 +486,7 @@ def test_volume_attachment_list(self): columns, data = self.cmd.take_action(parsed_args) - self.volume_attachments_mock.list.assert_called_once_with( + self.volume_sdk_client.attachments.assert_called_once_with( search_opts={ 'all_tenants': False, 'project_id': None, @@ -529,7 +526,7 @@ def test_volume_attachment_list_with_options(self): columns, data = self.cmd.take_action(parsed_args) - self.volume_attachments_mock.list.assert_called_once_with( + self.volume_sdk_client.attachments.assert_called_once_with( search_opts={ 'all_tenants': True, 'project_id': self.project.id, diff --git a/openstackclient/volume/v3/volume_attachment.py b/openstackclient/volume/v3/volume_attachment.py index 8adbac46a..fe4765c7b 100644 --- a/openstackclient/volume/v3/volume_attachment.py +++ b/openstackclient/volume/v3/volume_attachment.py @@ -12,7 +12,7 @@ import logging -from cinderclient import api_versions +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 @@ -170,10 +170,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - volume_client = self.app.client_manager.volume - compute_client = self.app.client_manager.compute + volume_client = self.app.client_manager.sdk_connection.volume + compute_client = self.app.client_manager.sdk_connection.compute - if volume_client.api_version < api_versions.APIVersion('3.27'): + if not sdk_utils.supports_microversion(volume_client, '3.27'): msg = _( "--os-volume-api-version 3.27 or greater is required to " "support the 'volume attachment create' command" @@ -181,7 +181,7 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) if parsed_args.mode: - if volume_client.api_version < api_versions.APIVersion('3.54'): + if not sdk_utils.supports_microversion(volume_client, '3.54'): msg = _( "--os-volume-api-version 3.54 or greater is required to " "support the '--mode' option" @@ -218,17 +218,18 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - volume = utils.find_resource( - volume_client.volumes, - parsed_args.volume, + volume = volume_client.find_volume( + parsed_args.volume, ignore_missing=False ) - server = utils.find_resource( - compute_client.servers, - parsed_args.server, + server = compute_client.find_server( + parsed_args.server, ignore_missing=False ) - attachment = volume_client.attachments.create( - volume.id, connector, server.id, parsed_args.mode + attachment = volume_client.create_attachment( + volume.id, + connector=connector, + instance=server.id, + mode=parsed_args.mode, ) return _format_attachment(attachment) @@ -256,16 +257,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 - if volume_client.api_version < api_versions.APIVersion('3.27'): + if not sdk_utils.supports_microversion(volume_client, '3.27'): msg = _( "--os-volume-api-version 3.27 or greater is required to " "support the 'volume attachment delete' command" ) raise exceptions.CommandError(msg) - volume_client.attachments.delete(parsed_args.attachment) + volume_client.delete_attachment(parsed_args.attachment) class SetVolumeAttachment(command.ShowOne): @@ -330,9 +331,9 @@ 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.27'): + if not sdk_utils.supports_microversion(volume_client, '3.27'): msg = _( "--os-volume-api-version 3.27 or greater is required to " "support the 'volume attachment set' command" @@ -349,8 +350,9 @@ def take_action(self, parsed_args): 'mountpoint': parsed_args.mountpoint, } - attachment = volume_client.attachments.update( - parsed_args.attachment, connector + attachment = volume_client.update_attachment( + parsed_args.attachment, + connector=connector, ) return _format_attachment(attachment) @@ -369,16 +371,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 - if volume_client.api_version < api_versions.APIVersion('3.44'): + if not sdk_utils.supports_microversion(volume_client, '3.44'): msg = _( "--os-volume-api-version 3.44 or greater is required to " "support the 'volume attachment complete' command" ) raise exceptions.CommandError(msg) - volume_client.attachments.complete(parsed_args.attachment) + volume_client.complete_attachment(parsed_args.attachment) class ListVolumeAttachment(command.Lister): @@ -429,10 +431,10 @@ 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 - if volume_client.api_version < api_versions.APIVersion('3.27'): + if not sdk_utils.supports_microversion(volume_client, '3.27'): msg = _( "--os-volume-api-version 3.27 or greater is required to " "support the 'volume attachment list' command" @@ -458,7 +460,7 @@ def take_action(self, parsed_args): # search_opts.update(shell_utils.extract_filters(AppendFilters.filters)) # TODO(stephenfin): Implement sorting - attachments = volume_client.attachments.list( + attachments = volume_client.attachments( search_opts=search_opts, marker=parsed_args.marker, limit=parsed_args.limit, @@ -496,15 +498,15 @@ 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.27'): + if not sdk_utils.supports_microversion(volume_client, '3.27'): msg = _( "--os-volume-api-version 3.27 or greater is required to " "support the 'volume attachment show' command" ) raise exceptions.CommandError(msg) - attachment = volume_client.attachments.show(parsed_args.attachment) + attachment = volume_client.get_attachment(parsed_args.attachment) return _format_attachment(attachment) diff --git a/releasenotes/notes/migrate-volume-attachment-commands-4309409bca1ca5d4.yaml b/releasenotes/notes/migrate-volume-attachment-commands-4309409bca1ca5d4.yaml new file mode 100644 index 000000000..b02abfd8c --- /dev/null +++ b/releasenotes/notes/migrate-volume-attachment-commands-4309409bca1ca5d4.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Migrated volume attachment commands to SDK. From 55cbb84e60fe3fec275da605c4d7a4ddc171777d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 7 May 2024 15:46:35 +0100 Subject: [PATCH 141/403] volume: Migrate 'volume list' to compute SDK Change-Id: Iae662aa6b391ba6ae5f569184c7d19fb3654be35 Signed-off-by: Stephen Finucane --- openstackclient/volume/v1/volume.py | 4 ++-- openstackclient/volume/v2/volume.py | 7 ++++--- openstackclient/volume/v3/volume.py | 7 ++++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index 3f1eea16b..c60625ee9 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -378,7 +378,6 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): volume_client = self.app.client_manager.volume - compute_client = self.app.client_manager.compute if parsed_args.long: columns = ( @@ -420,7 +419,8 @@ def take_action(self, parsed_args): # Cache the server list server_cache = {} try: - for s in compute_client.servers.list(): + compute_client = self.app.client_manager.sdk_connection.compute + for s in compute_client.servers(): server_cache[s.id] = s except Exception: # Just forget it if there's any trouble diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 53a19d429..b5a4a1ffc 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -20,6 +20,7 @@ import logging from cliff import columns as cliff_columns +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 @@ -507,10 +508,10 @@ def take_action(self, parsed_args): server_cache = {} if do_server_list: try: - compute_client = self.app.client_manager.compute - for s in compute_client.servers.list(): + compute_client = self.app.client_manager.sdk_connection.compute + for s in compute_client.servers(): server_cache[s.id] = s - except Exception: + except sdk_exceptions.SDKException: # Just forget it if there's any trouble pass # nosec: B110 AttachmentsColumnWithCache = functools.partial( diff --git a/openstackclient/volume/v3/volume.py b/openstackclient/volume/v3/volume.py index e1f025c75..3bb3cdaed 100644 --- a/openstackclient/volume/v3/volume.py +++ b/openstackclient/volume/v3/volume.py @@ -20,6 +20,7 @@ import logging from cliff import columns as cliff_columns +from openstack import exceptions as sdk_exceptions from openstack import utils as sdk_utils from osc_lib.cli import format_columns from osc_lib.cli import parseractions @@ -522,10 +523,10 @@ def take_action(self, parsed_args): server_cache = {} if do_server_list: try: - compute_client = self.app.client_manager.compute - for s in compute_client.servers.list(): + compute_client = self.app.client_manager.sdk_connection.compute + for s in compute_client.servers(): server_cache[s.id] = s - except Exception: + except sdk_exceptions.SDKException: # Just forget it if there's any trouble pass # nosec: B110 AttachmentsColumnWithCache = functools.partial( From 9d394372823053e6a4038e14f81e14d1a32a6e3b Mon Sep 17 00:00:00 2001 From: whoami-rajat Date: Mon, 17 Jul 2023 15:34:44 +0530 Subject: [PATCH 142/403] common: Migrate 'limits show' to SDK This is done for both the compute and block storage services even though the compute service hasn't supported rate limits since API v2.1 was introduced [1]. [1] https://github.com/openstack/nova/commit/ca4ec762804 Change-Id: Idd9f4a1c23952a6087f08c03ac8b5bebd5a0c86d Co-authored-by: Stephen Finucane Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/918519 --- openstackclient/common/clientmanager.py | 12 +- openstackclient/common/limits.py | 96 ++++++++++----- .../tests/unit/common/test_limits.py | 108 +++++++++++------ .../tests/unit/compute/v2/fakes.py | 89 +++----------- openstackclient/tests/unit/volume/v2/fakes.py | 111 ------------------ openstackclient/tests/unit/volume/v3/fakes.py | 53 +++++++++ .../migrate-limits-show-f586c9762dfd7d0c.yaml | 4 + 7 files changed, 220 insertions(+), 253 deletions(-) create mode 100644 releasenotes/notes/migrate-limits-show-f586c9762dfd7d0c.yaml diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 8a67dc0c0..8caf94a5d 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -133,7 +133,17 @@ def is_volume_endpoint_enabled(self, volume_client): # 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() - volume_version = volume_client.api_version.ver_major + 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 diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py index 7050d2fd0..043bf5321 100644 --- a/openstackclient/common/limits.py +++ b/openstackclient/common/limits.py @@ -24,6 +24,31 @@ from openstackclient.identity import common as identity_common +def _format_absolute_limit(absolute_limits): + info = {} + + for key in set(absolute_limits): + if key in ('id', 'name', 'location'): + continue + + info[key] = absolute_limits[key] + + return info + + +def _format_rate_limit(rate_limits): + # flatten this: + # + # {'uri': '', 'limit': [{'value': '', ...], ...} + # + # to this: + # + # {'uri': '', 'value': '', ...}, ...} + return itertools.chain( + *[[{'uri': x['uri'], **y} for y in x['limit']] for x in rate_limits] + ) + + class ShowLimits(command.Lister): _description = _("Show compute and block storage limits") @@ -42,36 +67,42 @@ def get_parser(self, prog_name): dest="is_rate", action="store_true", default=False, - help=_("Show rate limits"), + help=_( + 'Show rate limits. This is not supported by the compute ' + 'service since the 12.0.0 (Liberty) release and is only ' + 'supported by the block storage service when the ' + 'rate-limiting middleware is enabled. It is therefore a no-op ' + 'in most deployments.' + ), ) parser.add_argument( "--reserved", dest="is_reserved", action="store_true", default=False, - help=_("Include reservations count [only valid with --absolute]"), + help=_("Include reservations count (only valid with --absolute)"), ) parser.add_argument( '--project', metavar='', help=_( - 'Show limits for a specific project (name or ID)' - ' [only valid with --absolute]' + 'Show limits for a specific project (name or ID) ' + '(only valid with --absolute)' ), ) parser.add_argument( '--domain', metavar='', help=_( - 'Domain the project belongs to (name or ID)' - ' [only valid with --absolute]' + 'Domain the project belongs to (name or ID) ' + '(only valid with --absolute)' ), ) return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute - volume_client = self.app.client_manager.volume + 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: @@ -94,33 +125,30 @@ def take_action(self, parsed_args): volume_limits = None if self.app.client_manager.is_compute_endpoint_enabled(): - compute_limits = compute_client.limits.get( - parsed_args.is_reserved, tenant_id=project_id + 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): - volume_limits = volume_client.limits.get() + volume_limits = volume_client.get_limits( + project_id=project_id, + ) - data = [] if parsed_args.is_absolute: + columns = ["Name", "Value"] + info = {} if compute_limits: - data.append(compute_limits.absolute) + info.update(_format_absolute_limit(compute_limits.absolute)) if volume_limits: - data.append(volume_limits.absolute) - columns = ["Name", "Value"] - return ( - columns, - ( - utils.get_item_properties(s, columns) - for s in itertools.chain(*data) - ), - ) + info.update(_format_absolute_limit(volume_limits.absolute)) - elif parsed_args.is_rate: + return (columns, sorted(info.items(), key=lambda x: x[0])) + else: # parsed_args.is_rate + data = [] if compute_limits: - data.append(compute_limits.rate) + data.extend(_format_rate_limit(compute_limits.rate)) if volume_limits: - data.append(volume_limits.rate) + data.extend(_format_rate_limit(volume_limits.rate)) columns = [ "Verb", "URI", @@ -129,12 +157,18 @@ def take_action(self, parsed_args): "Unit", "Next Available", ] + return ( columns, - ( - utils.get_item_properties(s, columns) - for s in itertools.chain(*data) - ), + [ + ( + s['verb'], + s['uri'], + s['value'], + s['remaining'], + s['unit'], + s.get('next-available') or s['next_available'], + ) + for s in data + ], ) - else: - return {}, {} diff --git a/openstackclient/tests/unit/common/test_limits.py b/openstackclient/tests/unit/common/test_limits.py index 86b17b182..b7309ab7c 100644 --- a/openstackclient/tests/unit/common/test_limits.py +++ b/openstackclient/tests/unit/common/test_limits.py @@ -13,23 +13,62 @@ from openstackclient.common import limits from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes +from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes class TestComputeLimits(compute_fakes.TestComputev2): - absolute_columns = [ - 'Name', - 'Value', - ] - + absolute_columns = ['Name', 'Value'] rate_columns = ["Verb", "URI", "Value", "Remain", "Unit", "Next Available"] def setUp(self): super().setUp() self.app.client_manager.volume_endpoint_enabled = False - self.fake_limits = compute_fakes.FakeLimits() - self.compute_client.limits.get.return_value = self.fake_limits + self.fake_limits = compute_fakes.create_limits() + + self.absolute_data = [ + ('floating_ips', 10), + ('floating_ips_used', 0), + ('image_meta', 128), + ('instances', 10), + ('instances_used', 0), + ('keypairs', 100), + ('max_image_meta', 128), + ('max_security_group_rules', 20), + ('max_security_groups', 10), + ('max_server_group_members', 10), + ('max_server_groups', 10), + ('max_server_meta', 128), + ('max_total_cores', 20), + ('max_total_floating_ips', 10), + ('max_total_instances', 10), + ('max_total_keypairs', 100), + ('max_total_ram_size', 51200), + ('personality', 5), + ('personality_size', 10240), + ('security_group_rules', 20), + ('security_groups', 10), + ('security_groups_used', 0), + ('server_group_members', 10), + ('server_groups', 10), + ('server_groups_used', 0), + ('server_meta', 128), + ('total_cores', 20), + ('total_cores_used', 0), + ('total_floating_ips_used', 0), + ('total_instances_used', 0), + ('total_ram', 51200), + ('total_ram_used', 0), + ('total_security_groups_used', 0), + ('total_server_groups_used', 0), + ] + self.rate_data = [ + ('POST', '*', 10, 2, 'MINUTE', '2011-12-15T22:42:45Z'), + ('PUT', '*', 10, 2, 'MINUTE', '2011-12-15T22:42:45Z'), + ('DELETE', '*', 100, 100, 'MINUTE', '2011-12-15T22:42:45Z'), + ] + + self.compute_sdk_client.get_limits.return_value = self.fake_limits def test_compute_show_absolute(self): arglist = ['--absolute'] @@ -39,12 +78,8 @@ def test_compute_show_absolute(self): columns, data = cmd.take_action(parsed_args) - ret_limits = list(data) - compute_reference_limits = self.fake_limits.absolute_limits() - self.assertEqual(self.absolute_columns, columns) - self.assertEqual(compute_reference_limits, ret_limits) - self.assertEqual(19, len(ret_limits)) + self.assertEqual(self.absolute_data, data) def test_compute_show_rate(self): arglist = ['--rate'] @@ -54,28 +89,39 @@ def test_compute_show_rate(self): columns, data = cmd.take_action(parsed_args) - ret_limits = list(data) - compute_reference_limits = self.fake_limits.rate_limits() - self.assertEqual(self.rate_columns, columns) - self.assertEqual(compute_reference_limits, ret_limits) - self.assertEqual(3, len(ret_limits)) + self.assertEqual(self.rate_data, data) class TestVolumeLimits(volume_fakes.TestVolume): - absolute_columns = [ - 'Name', - 'Value', - ] - + absolute_columns = ['Name', 'Value'] rate_columns = ["Verb", "URI", "Value", "Remain", "Unit", "Next Available"] def setUp(self): super().setUp() self.app.client_manager.compute_endpoint_enabled = False - self.fake_limits = volume_fakes.FakeLimits() - self.volume_client.limits.get.return_value = self.fake_limits + self.fake_limits = volume_fakes.create_limits() + + self.absolute_data = [ + ('max_total_backup_gigabytes', 1000), + ('max_total_backups', 10), + ('max_total_snapshots', 10), + ('max_total_volume_gigabytes', 1000), + ('max_total_volumes', 10), + ('total_backup_gigabytes_used', 0), + ('total_backups_used', 0), + ('total_gigabytes_used', 35), + ('total_snapshots_used', 1), + ('total_volumes_used', 4), + ] + self.rate_data = [ + ('POST', '*', 10, 2, 'MINUTE', '2011-12-15T22:42:45Z'), + ('PUT', '*', 10, 2, 'MINUTE', '2011-12-15T22:42:45Z'), + ('DELETE', '*', 100, 100, 'MINUTE', '2011-12-15T22:42:45Z'), + ] + + self.volume_sdk_client.get_limits.return_value = self.fake_limits def test_volume_show_absolute(self): arglist = ['--absolute'] @@ -85,12 +131,8 @@ def test_volume_show_absolute(self): columns, data = cmd.take_action(parsed_args) - ret_limits = list(data) - compute_reference_limits = self.fake_limits.absolute_limits() - self.assertEqual(self.absolute_columns, columns) - self.assertEqual(compute_reference_limits, ret_limits) - self.assertEqual(10, len(ret_limits)) + self.assertEqual(self.absolute_data, data) def test_volume_show_rate(self): arglist = ['--rate'] @@ -100,9 +142,5 @@ def test_volume_show_rate(self): columns, data = cmd.take_action(parsed_args) - ret_limits = list(data) - compute_reference_limits = self.fake_limits.rate_limits() - self.assertEqual(self.rate_columns, columns) - self.assertEqual(compute_reference_limits, ret_limits) - self.assertEqual(3, len(ret_limits)) + self.assertEqual(self.rate_data, data) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 614bd9028..3e89d4905 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -28,6 +28,7 @@ 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 from openstack.compute.v2 import server_action as _server_action @@ -91,9 +92,6 @@ def __init__(self, **kwargs): self.images = mock.Mock() self.images.resource_class = fakes.FakeResource(None, {}) - self.limits = mock.Mock() - self.limits.resource_class = fakes.FakeResource(None, {}) - self.servers = mock.Mock() self.servers.resource_class = fakes.FakeResource(None, {}) @@ -1177,11 +1175,12 @@ def create_one_comp_detailed_quota(attrs=None): return quota -class FakeLimits: - """Fake limits""" +def create_limits(attrs=None): + """Create a fake limits object.""" + attrs = attrs or {} - def __init__(self, absolute_attrs=None, rate_attrs=None): - self.absolute_limits_attrs = { + limits_attrs = { + 'absolute': { 'maxServerMeta': 128, 'maxTotalInstances': 10, 'maxPersonality': 5, @@ -1201,11 +1200,8 @@ def __init__(self, absolute_attrs=None, rate_attrs=None): 'maxTotalFloatingIps': 10, 'totalSecurityGroupsUsed': 0, 'maxTotalCores': 20, - } - absolute_attrs = absolute_attrs or {} - self.absolute_limits_attrs.update(absolute_attrs) - - self.rate_limits_attrs = [ + }, + 'rate': [ { "uri": "*", "limit": [ @@ -1232,69 +1228,12 @@ def __init__(self, absolute_attrs=None, rate_attrs=None): }, ], } - ] - - @property - def absolute(self): - for name, value in self.absolute_limits_attrs.items(): - yield FakeAbsoluteLimit(name, value) - - def absolute_limits(self): - reference_data = [] - for name, value in self.absolute_limits_attrs.items(): - reference_data.append((name, value)) - return reference_data - - @property - def rate(self): - for group in self.rate_limits_attrs: - uri = group['uri'] - for rate in group['limit']: - yield FakeRateLimit( - rate['verb'], - uri, - rate['value'], - rate['remaining'], - rate['unit'], - rate['next-available'], - ) - - def rate_limits(self): - reference_data = [] - for group in self.rate_limits_attrs: - uri = group['uri'] - for rate in group['limit']: - reference_data.append( - ( - rate['verb'], - uri, - rate['value'], - rate['remaining'], - rate['unit'], - rate['next-available'], - ) - ) - return reference_data - - -class FakeAbsoluteLimit: - """Data model that represents an absolute limit""" - - def __init__(self, name, value): - self.name = name - self.value = value - - -class FakeRateLimit: - """Data model that represents a flattened view of a single rate limit""" - - def __init__(self, verb, uri, value, remain, unit, next_available): - self.verb = verb - self.uri = uri - self.value = value - self.remain = remain - self.unit = unit - self.next_available = next_available + ], + } + limits_attrs.update(attrs) + + limits = _limits.Limits(**limits_attrs) + return limits def create_one_migration(attrs=None): diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index 9ba3b2bf5..389b10b0f 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -1062,114 +1062,3 @@ def create_one_detailed_quota(attrs=None): quota = fakes.FakeResource(info=copy.deepcopy(quota_attrs), loaded=True) return quota - - -class FakeLimits: - """Fake limits""" - - def __init__(self, absolute_attrs=None): - self.absolute_limits_attrs = { - 'totalSnapshotsUsed': 1, - 'maxTotalBackups': 10, - 'maxTotalVolumeGigabytes': 1000, - 'maxTotalSnapshots': 10, - 'maxTotalBackupGigabytes': 1000, - 'totalBackupGigabytesUsed': 0, - 'maxTotalVolumes': 10, - 'totalVolumesUsed': 4, - 'totalBackupsUsed': 0, - 'totalGigabytesUsed': 35, - } - absolute_attrs = absolute_attrs or {} - self.absolute_limits_attrs.update(absolute_attrs) - - self.rate_limits_attrs = [ - { - "uri": "*", - "limit": [ - { - "value": 10, - "verb": "POST", - "remaining": 2, - "unit": "MINUTE", - "next-available": "2011-12-15T22:42:45Z", - }, - { - "value": 10, - "verb": "PUT", - "remaining": 2, - "unit": "MINUTE", - "next-available": "2011-12-15T22:42:45Z", - }, - { - "value": 100, - "verb": "DELETE", - "remaining": 100, - "unit": "MINUTE", - "next-available": "2011-12-15T22:42:45Z", - }, - ], - } - ] - - @property - def absolute(self): - for name, value in self.absolute_limits_attrs.items(): - yield FakeAbsoluteLimit(name, value) - - def absolute_limits(self): - reference_data = [] - for name, value in self.absolute_limits_attrs.items(): - reference_data.append((name, value)) - return reference_data - - @property - def rate(self): - for group in self.rate_limits_attrs: - uri = group['uri'] - for rate in group['limit']: - yield FakeRateLimit( - rate['verb'], - uri, - rate['value'], - rate['remaining'], - rate['unit'], - rate['next-available'], - ) - - def rate_limits(self): - reference_data = [] - for group in self.rate_limits_attrs: - uri = group['uri'] - for rate in group['limit']: - reference_data.append( - ( - rate['verb'], - uri, - rate['value'], - rate['remaining'], - rate['unit'], - rate['next-available'], - ) - ) - return reference_data - - -class FakeAbsoluteLimit: - """Data model that represents an absolute limit.""" - - def __init__(self, name, value): - self.name = name - self.value = value - - -class FakeRateLimit: - """Data model that represents a flattened view of a single rate limit.""" - - def __init__(self, verb, uri, value, remain, unit, next_available): - self.verb = verb - self.uri = uri - self.value = value - self.remain = remain - self.unit = unit - self.next_available = next_available diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index 6a9f1bb0e..e79282704 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -22,6 +22,7 @@ from openstack.block_storage.v3 import availability_zone as _availability_zone from openstack.block_storage.v3 import backup as _backup from openstack.block_storage.v3 import extension as _extension +from openstack.block_storage.v3 import limits as _limits from openstack.block_storage.v3 import resource_filter as _filters from openstack.block_storage.v3 import volume as _volume from openstack.compute.v2 import _proxy as _compute_proxy @@ -423,6 +424,58 @@ def create_one_encryption_volume_type(attrs=None): return encryption_type +def create_limits(attrs=None): + """Create a fake limits object.""" + attrs = attrs or {} + + limits_attrs = { + 'absolute': { + 'totalSnapshotsUsed': 1, + 'maxTotalBackups': 10, + 'maxTotalVolumeGigabytes': 1000, + 'maxTotalSnapshots': 10, + 'maxTotalBackupGigabytes': 1000, + 'totalBackupGigabytesUsed': 0, + 'maxTotalVolumes': 10, + 'totalVolumesUsed': 4, + 'totalBackupsUsed': 0, + 'totalGigabytesUsed': 35, + }, + 'rate': [ + { + "uri": "*", + "limit": [ + { + "value": 10, + "verb": "POST", + "remaining": 2, + "unit": "MINUTE", + "next-available": "2011-12-15T22:42:45Z", + }, + { + "value": 10, + "verb": "PUT", + "remaining": 2, + "unit": "MINUTE", + "next-available": "2011-12-15T22:42:45Z", + }, + { + "value": 100, + "verb": "DELETE", + "remaining": 100, + "unit": "MINUTE", + "next-available": "2011-12-15T22:42:45Z", + }, + ], + } + ], + } + limits_attrs.update(attrs) + + limits = _limits.Limit(**limits_attrs) + return limits + + def create_one_resource_filter(attrs=None): """Create a fake resource filter. diff --git a/releasenotes/notes/migrate-limits-show-f586c9762dfd7d0c.yaml b/releasenotes/notes/migrate-limits-show-f586c9762dfd7d0c.yaml new file mode 100644 index 000000000..81a5119ad --- /dev/null +++ b/releasenotes/notes/migrate-limits-show-f586c9762dfd7d0c.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + The ``limits show`` command has been migrated to SDK. From abef798f307026159e86ff1064b488f0e88305c3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 8 May 2024 12:36:17 +0100 Subject: [PATCH 143/403] compute: Migrate 'server add/remove security group' to SDK We need to work around SDK's (intentional) lack of support for Nova's deprecated '/os-security-groups' API but it's nothing too crazy. Change-Id: I03ca1a73f98aa77b288148607baff336dae69fb1 Signed-off-by: Stephen Finucane --- openstackclient/api/compute_v2.py | 46 +++++- openstackclient/compute/v2/server.py | 49 ++++--- .../tests/unit/api/test_compute_v2.py | 115 +++++++++++++++ .../tests/unit/compute/v2/test_server.py | 138 ++++++++++++------ 4 files changed, 282 insertions(+), 66 deletions(-) diff --git a/openstackclient/api/compute_v2.py b/openstackclient/api/compute_v2.py index 7429a859d..af0c69e58 100644 --- a/openstackclient/api/compute_v2.py +++ b/openstackclient/api/compute_v2.py @@ -11,9 +11,16 @@ # under the License. # -"""Compute v2 API Library""" +"""Compute v2 API Library + +A collection of wrappers for deprecated Compute v2 APIs that are not +intentionally supported by SDK. Most of these are proxy APIs. +""" + +import http from keystoneauth1 import exceptions as ksa_exceptions +from openstack import exceptions as sdk_exceptions from osc_lib.api import api from osc_lib import exceptions from osc_lib.i18n import _ @@ -577,3 +584,40 @@ def security_group_rule_delete( return self.delete(f'/{url}/{security_group_rule_id}') return None + + +def find_security_group(compute_client, name_or_id): + """Find the name for a given security group name or ID + + https://docs.openstack.org/api-ref/compute/#show-security-group-details + + :param compute_client: A compute client + :param name_or_id: The name or ID of the security group to look up + :returns: A security group object + :raises exception.NotFound: If a matching security group could not be + found or more than one match was found + """ + response = compute_client.get( + f'/os-security-groups/{name_or_id}', microversion='2.1' + ) + if response.status_code != http.HTTPStatus.NOT_FOUND: + # there might be other, non-404 errors + sdk_exceptions.raise_from_response(response) + return response.json()['security_group'] + + response = compute_client.get('/os-security-groups', microversion='2.1') + sdk_exceptions.raise_from_response(response) + found = None + security_groups = response.json()['security_groups'] + for security_group in security_groups: + if security_group['name'] == name_or_id: + if found: + raise exceptions.NotFound( + f'multiple matches found for {name_or_id}' + ) + found = security_group + + if not found: + raise exceptions.NotFound(f'{name_or_id} not found') + + return found diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 42caafead..63640a0e1 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -32,6 +32,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.api import compute_v2 from openstackclient.common import pagination from openstackclient.i18n import _ from openstackclient.identity import common as identity_common @@ -677,17 +678,21 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute - server = utils.find_resource( - compute_client.servers, - parsed_args.server, - ) - security_group = compute_client.api.security_group_find( - parsed_args.group, + server = compute_client.find_server( + parsed_args.server, ignore_missing=False ) - - server.add_security_group(security_group['id']) + 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 + 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) class AddServerVolume(command.ShowOne): @@ -3960,28 +3965,34 @@ def get_parser(self, prog_name): parser.add_argument( 'server', metavar='', - help=_('Name or ID of server to use'), + help=_('Server (name or ID)'), ) parser.add_argument( 'group', metavar='', - help=_('Name or ID of security group to remove from server'), + help=_('Security group to remove (name or ID)'), ) return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute - server = utils.find_resource( - compute_client.servers, - parsed_args.server, + server = compute_client.find_server( + parsed_args.server, ignore_missing=False ) - security_group = compute_client.api.security_group_find( - parsed_args.group, + 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 + 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 ) - server.remove_security_group(security_group['id']) - class RemoveServerVolume(command.Command): _description = _( diff --git a/openstackclient/tests/unit/api/test_compute_v2.py b/openstackclient/tests/unit/api/test_compute_v2.py index 3bcd2a912..5a8442602 100644 --- a/openstackclient/tests/unit/api/test_compute_v2.py +++ b/openstackclient/tests/unit/api/test_compute_v2.py @@ -13,11 +13,17 @@ """Compute v2 API Library Tests""" +import http +from unittest import mock +import uuid + from keystoneauth1 import session +from openstack.compute.v2 import _proxy from osc_lib import exceptions as osc_lib_exceptions from requests_mock.contrib import fixture from openstackclient.api import compute_v2 as compute +from openstackclient.tests.unit import fakes from openstackclient.tests.unit import utils @@ -648,3 +654,112 @@ def test_security_group_rule_delete(self): ret = self.api.security_group_rule_delete('1') self.assertEqual(202, ret.status_code) self.assertEqual("", ret.text) + + +class TestFindSecurityGroup(utils.TestCase): + + def setUp(self): + super().setUp() + + self.compute_sdk_client = mock.Mock(_proxy.Proxy) + + def test_find_security_group_by_id(self): + sg_id = uuid.uuid4().hex + sg_name = 'name-' + uuid.uuid4().hex + data = { + 'security_group': { + 'id': sg_id, + 'name': sg_name, + 'description': 'description-' + uuid.uuid4().hex, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + 'rules': [], + } + } + self.compute_sdk_client.get.side_effect = [ + fakes.FakeResponse(data=data), + ] + + result = compute.find_security_group(self.compute_sdk_client, sg_id) + + self.compute_sdk_client.get.assert_has_calls( + [ + mock.call(f'/os-security-groups/{sg_id}', microversion='2.1'), + ] + ) + self.assertEqual(data['security_group'], result) + + def test_find_security_group_by_name(self): + sg_id = uuid.uuid4().hex + sg_name = 'name-' + uuid.uuid4().hex + data = { + 'security_groups': [ + { + 'id': sg_id, + 'name': sg_name, + 'description': 'description-' + uuid.uuid4().hex, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + 'rules': [], + } + ], + } + self.compute_sdk_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) + + self.compute_sdk_client.get.assert_has_calls( + [ + mock.call( + f'/os-security-groups/{sg_name}', microversion='2.1' + ), + mock.call('/os-security-groups', microversion='2.1'), + ] + ) + self.assertEqual(data['security_groups'][0], result) + + def test_find_security_group_not_found(self): + data = {'security_groups': []} + self.compute_sdk_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, + 'invalid-sg', + ) + + def test_find_security_group_by_name_duplicate(self): + sg_name = 'name-' + uuid.uuid4().hex + data = { + 'security_groups': [ + { + 'id': uuid.uuid4().hex, + 'name': sg_name, + 'description': 'description-' + uuid.uuid4().hex, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + 'rules': [], + }, + { + 'id': uuid.uuid4().hex, + 'name': sg_name, + 'description': 'description-' + uuid.uuid4().hex, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + 'rules': [], + }, + ], + } + self.compute_sdk_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, + sg_name, + ) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 441d69f5c..ec1190c7e 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -18,7 +18,6 @@ import json import tempfile from unittest import mock -from unittest.mock import call import iso8601 from openstack import exceptions as sdk_exceptions @@ -26,6 +25,7 @@ from osc_lib import exceptions from osc_lib import utils as common_utils +from openstackclient.api import compute_v2 from openstackclient.compute.v2 import server from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes from openstackclient.tests.unit.image.v2 import fakes as image_fakes @@ -148,7 +148,7 @@ def run_method_with_sdk_servers(self, method_name, server_count): result = self.cmd.take_action(parsed_args) - calls = [call(s.id) for s in servers] + 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) @@ -1221,42 +1221,65 @@ def test_server_add_network_with_tag_pre_v249(self): ) -@mock.patch('openstackclient.api.compute_v2.APIv2.security_group_find') -class TestServerAddSecurityGroup(TestServer): +class TestServerAddSecurityGroup(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.security_group = compute_fakes.create_one_security_group() - - attrs = {'security_groups': [{'name': self.security_group['id']}]} - methods = { - 'add_security_group': None, - } - - self.server = compute_fakes.create_one_server( - attrs=attrs, methods=methods + 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 ) - # This is the return value for utils.find_resource() for server - self.servers_mock.get.return_value = self.server # Get the command object to test self.cmd = server.AddServerSecurityGroup(self.app, None) - def test_server_add_security_group(self, sg_find_mock): - sg_find_mock.return_value = self.security_group - arglist = [self.server.id, self.security_group['id']] + def test_server_add_security_group__nova_network(self): + arglist = [self.server.id, 'fake_sg'] + verifylist = [ + ('server', self.server.id), + ('group', 'fake_sg'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + with mock.patch.object( + self.app.client_manager, + 'is_network_endpoint_enabled', + return_value=False, + ): + with mock.patch.object( + compute_v2, + 'find_security_group', + return_value={'name': 'fake_sg'}, + ) as mock_find_nova_net_sg: + result = self.cmd.take_action(parsed_args) + + self.compute_sdk_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.server, 'fake_sg' + ) + mock_find_nova_net_sg.assert_called_once_with( + self.compute_sdk_client, 'fake_sg' + ) + self.assertIsNone(result) + + def test_server_add_security_group(self): + arglist = [self.server.id, 'fake_sg'] verifylist = [ ('server', self.server.id), - ('group', self.security_group['id']), + ('group', 'fake_sg'), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - sg_find_mock.assert_called_with( - self.security_group['id'], + + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False ) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.add_security_group.assert_called_with( - self.security_group['id'], + self.compute_sdk_client.add_security_group_to_server.assert_called_once_with( + self.server, 'fake_sg' ) self.assertIsNone(result) @@ -4452,7 +4475,7 @@ def test_server_delete_multi_servers(self): calls = [] for s in servers: - calls.append(call(s.id)) + calls.append(mock.call(s.id)) self.servers_mock.delete.assert_has_calls(calls) self.assertIsNone(result) @@ -7260,42 +7283,65 @@ def test_server_remove_network_no_neutron(self): self.find_network.assert_not_called() -@mock.patch('openstackclient.api.compute_v2.APIv2.security_group_find') class TestServerRemoveSecurityGroup(TestServer): def setUp(self): super().setUp() - self.security_group = compute_fakes.create_one_security_group() - - attrs = {'security_groups': [{'name': self.security_group['id']}]} - methods = { - 'remove_security_group': None, - } - - self.server = compute_fakes.create_one_server( - attrs=attrs, methods=methods + 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 ) - # This is the return value for utils.find_resource() for server - self.servers_mock.get.return_value = self.server # Get the command object to test self.cmd = server.RemoveServerSecurityGroup(self.app, None) - def test_server_remove_security_group(self, sg_find_mock): - sg_find_mock.return_value = self.security_group - arglist = [self.server.id, self.security_group['id']] + def test_server_remove_security_group__nova_network(self): + arglist = [self.server.id, 'fake_sg'] + verifylist = [ + ('server', self.server.id), + ('group', 'fake_sg'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + with mock.patch.object( + self.app.client_manager, + 'is_network_endpoint_enabled', + return_value=False, + ): + with mock.patch.object( + compute_v2, + 'find_security_group', + return_value={'name': 'fake_sg'}, + ) as mock_find_nova_net_sg: + result = self.cmd.take_action(parsed_args) + + self.compute_sdk_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.server, 'fake_sg' + ) + mock_find_nova_net_sg.assert_called_once_with( + self.compute_sdk_client, 'fake_sg' + ) + self.assertIsNone(result) + + def test_server_remove_security_group(self): + arglist = [self.server.id, 'fake_sg'] verifylist = [ ('server', self.server.id), - ('group', self.security_group['id']), + ('group', 'fake_sg'), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - sg_find_mock.assert_called_with( - self.security_group['id'], + + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False ) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.remove_security_group.assert_called_with( - self.security_group['id'], + self.compute_sdk_client.remove_security_group_from_server.assert_called_once_with( + self.server, 'fake_sg' ) self.assertIsNone(result) From c8621e5b8b22ce786045cb9517ba155ffc677a82 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 9 May 2024 13:22:13 +0100 Subject: [PATCH 144/403] compute: Migrate 'server migrate' to SDK Change-Id: I56d31c2fd4f8bf19eedd8f9eecd8a41cdafc5b55 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 37 ++- .../tests/unit/compute/v2/test_server.py | 214 +++++++++++------- 2 files changed, 151 insertions(+), 100 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 63640a0e1..dc85e21f7 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -3139,11 +3139,10 @@ def _show_progress(progress): self.app.stdout.write('\rProgress: %s' % progress) self.app.stdout.flush() - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute - server = utils.find_resource( - compute_client.servers, - parsed_args.server, + server = compute_client.find_server( + parsed_args.server, ignore_missing=False ) if parsed_args.live_migration: @@ -3151,9 +3150,7 @@ def _show_progress(progress): block_migration = parsed_args.block_migration if block_migration is None: - if compute_client.api_version < api_versions.APIVersion( - '2.25' - ): + if not sdk_utils.supports_microversion(compute_client, '2.25'): block_migration = False else: block_migration = 'auto' @@ -3166,10 +3163,8 @@ def _show_progress(progress): # want to support, so if the user is using --live-migration # and --host, we want to enforce that they are using version # 2.30 or greater. - if ( - parsed_args.host - and compute_client.api_version - < api_versions.APIVersion('2.30') + if parsed_args.host and not sdk_utils.supports_microversion( + compute_client, '2.30' ): raise exceptions.CommandError( '--os-compute-api-version 2.30 or greater is required ' @@ -3179,13 +3174,13 @@ def _show_progress(progress): # The host parameter is required in the API even if None. kwargs['host'] = parsed_args.host - if compute_client.api_version < api_versions.APIVersion('2.25'): - kwargs['disk_over_commit'] = parsed_args.disk_overcommit + if not sdk_utils.supports_microversion(compute_client, '2.25'): + kwargs['disk_overcommit'] = parsed_args.disk_overcommit # We can't use an argparse default value because then we can't # distinguish between explicit 'False' and unset for the below # case (microversion >= 2.25) - if kwargs['disk_over_commit'] is None: - kwargs['disk_over_commit'] = False + if kwargs['disk_overcommit'] is None: + kwargs['disk_overcommit'] = False elif parsed_args.disk_overcommit is not None: # TODO(stephenfin): Raise an error here in OSC 7.0 msg = _( @@ -3196,7 +3191,7 @@ def _show_progress(progress): ) self.log.warning(msg) - server.live_migrate(**kwargs) + compute_client.live_migrate_server(server, **kwargs) else: # cold migration if parsed_args.block_migration or parsed_args.disk_overcommit: raise exceptions.CommandError( @@ -3205,9 +3200,7 @@ def _show_progress(progress): "specified" ) if parsed_args.host: - if compute_client.api_version < api_versions.APIVersion( - '2.56' - ): + if not sdk_utils.supports_microversion(compute_client, '2.56'): msg = _( '--os-compute-api-version 2.56 or greater is ' 'required to use --host without --live-migration.' @@ -3215,13 +3208,13 @@ def _show_progress(progress): raise exceptions.CommandError(msg) kwargs = {'host': parsed_args.host} if parsed_args.host else {} - server.migrate(**kwargs) + compute_client.migrate_server(server, **kwargs) if parsed_args.wait: if utils.wait_for_status( - compute_client.servers.get, + compute_client.get_server, server.id, - success_status=['active', 'verify_resize'], + success_status=('active', 'verify_resize'), callback=_show_progress, ): self.app.stdout.write( diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index ec1190c7e..f460c0513 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -5616,17 +5616,10 @@ class TestServerMigrate(TestServer): def setUp(self): super().setUp() - methods = { - 'migrate': None, - 'live_migrate': None, - } - self.server = compute_fakes.create_one_server(methods=methods) - - # This is the return value for utils.find_resource() - self.servers_mock.get.return_value = self.server - - self.servers_mock.migrate.return_value = None - self.servers_mock.live_migrate.return_value = None + 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 # Get the command object to test self.cmd = server.MigrateServer(self.app, None) @@ -5641,18 +5634,24 @@ def test_server_migrate_no_options(self): ('disk_overcommit', None), ('wait', False), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.migrate.assert_called_with() - self.assertNotCalled(self.servers_mock.live_migrate) + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False + ) + self.compute_sdk_client.migrate_server.assert_called_once_with( + self.server, + ) + self.compute_sdk_client.live_migrate_server.assert_not_called() self.assertIsNone(result) - def test_server_migrate_with_host_2_56(self): + def test_server_migrate_with_host(self): # Tests that --host is allowed for a cold migration # for microversion 2.56 and greater. + self.set_compute_api_version('2.56') + arglist = [ '--host', 'fakehost', @@ -5665,15 +5664,17 @@ def test_server_migrate_with_host_2_56(self): ('disk_overcommit', None), ('wait', False), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.set_compute_api_version('2.56') + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.migrate.assert_called_with(host='fakehost') - self.assertNotCalled(self.servers_mock.live_migrate) + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False + ) + self.compute_sdk_client.migrate_server.assert_called_once_with( + self.server, host='fakehost' + ) + self.compute_sdk_client.live_migrate_server.assert_not_called() self.assertIsNone(result) def test_server_migrate_with_block_migration(self): @@ -5687,15 +5688,17 @@ def test_server_migrate_with_block_migration(self): ('disk_overcommit', None), ('wait', False), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.servers_mock.get.assert_called_with(self.server.id) - self.assertNotCalled(self.servers_mock.live_migrate) - self.assertNotCalled(self.servers_mock.migrate) + self.compute_sdk_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() def test_server_migrate_with_disk_overcommit(self): arglist = [ @@ -5708,19 +5711,23 @@ def test_server_migrate_with_disk_overcommit(self): ('disk_overcommit', True), ('wait', False), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.servers_mock.get.assert_called_with(self.server.id) - self.assertNotCalled(self.servers_mock.live_migrate) - self.assertNotCalled(self.servers_mock.migrate) + self.compute_sdk_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() def test_server_migrate_with_host_pre_v256(self): # Tests that --host is not allowed for a cold migration # before microversion 2.56 (the test defaults to 2.1). + self.set_compute_api_version('2.55') + arglist = [ '--host', 'fakehost', @@ -5746,9 +5753,11 @@ def test_server_migrate_with_host_pre_v256(self): str(ex), ) - self.servers_mock.get.assert_called_with(self.server.id) - self.assertNotCalled(self.servers_mock.live_migrate) - self.assertNotCalled(self.servers_mock.migrate) + self.compute_sdk_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() def test_server_live_migrate(self): # Tests the --live-migration option without --host or --live. @@ -5763,19 +5772,26 @@ def test_server_live_migrate(self): ('disk_overcommit', None), ('wait', False), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.live_migrate.assert_called_with( - block_migration=False, disk_over_commit=False, host=None + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False ) - self.assertNotCalled(self.servers_mock.migrate) + self.compute_sdk_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.assertIsNone(result) def test_server_live_migrate_with_host(self): # This requires --os-compute-api-version >= 2.30 so the test uses 2.30. + self.set_compute_api_version('2.30') + arglist = [ '--live-migration', '--host', @@ -5789,24 +5805,28 @@ def test_server_live_migrate_with_host(self): ('disk_overcommit', None), ('wait', False), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.set_compute_api_version('2.30') + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) + self.compute_sdk_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.server.live_migrate.assert_called_with( - block_migration='auto', host='fakehost' + self.compute_sdk_client.live_migrate_server.assert_called_once_with( + self.server, + block_migration='auto', + host='fakehost', ) - self.assertNotCalled(self.servers_mock.migrate) + self.compute_sdk_client.migrate_server.assert_not_called() self.assertIsNone(result) def test_server_live_migrate_with_host_pre_v230(self): # Tests that the --host option is not supported for --live-migration # before microversion 2.30 (the test defaults to 2.1). + self.set_compute_api_version('2.29') + arglist = [ '--live-migration', '--host', @@ -5820,12 +5840,11 @@ def test_server_live_migrate_with_host_pre_v230(self): ('disk_overcommit', None), ('wait', False), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) ex = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - # Make sure it's the error we expect. self.assertIn( '--os-compute-api-version 2.30 or greater is required ' @@ -5833,11 +5852,15 @@ def test_server_live_migrate_with_host_pre_v230(self): str(ex), ) - self.servers_mock.get.assert_called_with(self.server.id) - self.assertNotCalled(self.servers_mock.live_migrate) - self.assertNotCalled(self.servers_mock.migrate) + self.compute_sdk_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() def test_server_block_live_migrate(self): + self.set_compute_api_version('2.24') + arglist = [ '--live-migration', '--block-migration', @@ -5851,18 +5874,25 @@ def test_server_block_live_migrate(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.set_compute_api_version('2.24') - result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.live_migrate.assert_called_with( - block_migration=True, disk_over_commit=False, host=None + self.compute_sdk_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.server, + block_migration=True, + disk_overcommit=False, + host=None, ) - self.assertNotCalled(self.servers_mock.migrate) + self.compute_sdk_client.migrate_server.assert_not_called() self.assertIsNone(result) def test_server_live_migrate_with_disk_overcommit(self): + self.set_compute_api_version('2.24') + arglist = [ '--live-migration', '--disk-overcommit', @@ -5874,20 +5904,25 @@ def test_server_live_migrate_with_disk_overcommit(self): ('disk_overcommit', True), ('wait', False), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.set_compute_api_version('2.24') + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.live_migrate.assert_called_with( - block_migration=False, disk_over_commit=True, host=None + self.compute_sdk_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.server, + block_migration=False, + disk_overcommit=True, + host=None, ) - self.assertNotCalled(self.servers_mock.migrate) + self.compute_sdk_client.migrate_server.assert_not_called() self.assertIsNone(result) def test_server_live_migrate_with_disk_overcommit_post_v224(self): + self.set_compute_api_version('2.25') + arglist = [ '--live-migration', '--disk-overcommit', @@ -5899,20 +5934,23 @@ def test_server_live_migrate_with_disk_overcommit_post_v224(self): ('disk_overcommit', True), ('wait', False), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.set_compute_api_version('2.25') + parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False + ) # There should be no 'disk_over_commit' value present - self.server.live_migrate.assert_called_with( - block_migration='auto', host=None + self.compute_sdk_client.live_migrate_server.assert_called_once_with( + self.server, + block_migration='auto', + host=None, ) - self.assertNotCalled(self.servers_mock.migrate) + self.compute_sdk_client.migrate_server.assert_not_called() self.assertIsNone(result) + # A warning should have been logged for using --disk-overcommit. mock_warning.assert_called_once() self.assertIn( @@ -5932,13 +5970,23 @@ def test_server_migrate_with_wait(self, mock_wait_for_status): ('disk_overcommit', None), ('wait', True), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.migrate.assert_called_with() - self.assertNotCalled(self.servers_mock.live_migrate) + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False + ) + self.compute_sdk_client.migrate_server.assert_called_once_with( + self.server, + ) + self.compute_sdk_client.live_migrate_server.assert_not_called() + mock_wait_for_status.assert_called_once_with( + self.compute_sdk_client.get_server, + self.server.id, + success_status=('active', 'verify_resize'), + callback=mock.ANY, + ) self.assertIsNone(result) @mock.patch.object(common_utils, 'wait_for_status', return_value=False) @@ -5953,15 +6001,25 @@ def test_server_migrate_with_wait_fails(self, mock_wait_for_status): ('disk_overcommit', None), ('wait', True), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.migrate.assert_called_with() - self.assertNotCalled(self.servers_mock.live_migrate) + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False + ) + self.compute_sdk_client.migrate_server.assert_called_once_with( + self.server, + ) + self.compute_sdk_client.live_migrate_server.assert_not_called() + mock_wait_for_status.assert_called_once_with( + self.compute_sdk_client.get_server, + self.server.id, + success_status=('active', 'verify_resize'), + callback=mock.ANY, + ) class TestServerReboot(TestServer): From bcaf2ab559e82d571cf46ed50aba60abf0b41637 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 8 May 2024 18:36:53 +0100 Subject: [PATCH 145/403] compute: Migrate 'server set', 'server unset' commands Change-Id: I2c249e9ca3952100dcf7f97fcafa879b733d34c6 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 87 +++--- .../tests/unit/compute/v2/test_server.py | 287 +++++++++++------- ...ver-set-unset-to-sdk-ae32ebcced845b06.yaml | 4 + requirements.txt | 2 +- 4 files changed, 243 insertions(+), 137 deletions(-) create mode 100644 releasenotes/notes/migrate-server-set-unset-to-sdk-ae32ebcced845b06.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index dc85e21f7..026feaccd 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -4415,14 +4415,13 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute - server = utils.find_resource( - compute_client.servers, - parsed_args.server, + compute_client = self.app.client_manager.sdk_connection.compute + server = compute_client.find_server( + parsed_args.server, ignore_missing=False ) if parsed_args.description: - if compute_client.api_version < api_versions.APIVersion("2.19"): + if not sdk_utils.supports_microversion(compute_client, '2.19'): msg = _( '--os-compute-api-version 2.19 or greater is required to ' 'support the --description option' @@ -4430,7 +4429,7 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) if parsed_args.tags: - if compute_client.api_version < api_versions.APIVersion('2.26'): + if not sdk_utils.supports_microversion(compute_client, '2.26'): msg = _( '--os-compute-api-version 2.26 or greater is required to ' 'support the --tag option' @@ -4438,7 +4437,7 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) if parsed_args.hostname: - if compute_client.api_version < api_versions.APIVersion('2.90'): + if not sdk_utils.supports_microversion(compute_client, '2.90'): msg = _( '--os-compute-api-version 2.90 or greater is required to ' 'support the --hostname option' @@ -4457,30 +4456,32 @@ def take_action(self, parsed_args): update_kwargs['hostname'] = parsed_args.hostname if update_kwargs: - server.update(**update_kwargs) + compute_client.update_server(server, **update_kwargs) if parsed_args.properties: - compute_client.servers.set_meta(server, parsed_args.properties) + compute_client.set_server_metadata( + server, **parsed_args.properties + ) if parsed_args.state: - server.reset_state(state=parsed_args.state) + compute_client.reset_server_state(server, state=parsed_args.state) if parsed_args.root_password: p1 = getpass.getpass(_('New password: ')) p2 = getpass.getpass(_('Retype new password: ')) if p1 == p2: - server.change_password(p1) + compute_client.change_server_password(server, p1) else: msg = _("Passwords do not match, password unchanged") raise exceptions.CommandError(msg) elif parsed_args.password: - server.change_password(parsed_args.password) + compute_client.change_server_password(server, parsed_args.password) elif parsed_args.no_password: - server.clear_password() + compute_client.clear_server_password(server) if parsed_args.tags: for tag in parsed_args.tags: - server.add_tag(tag=tag) + compute_client.add_tag_to_server(server, tag=tag) class ShelveServer(command.Command): @@ -4995,7 +4996,8 @@ def get_parser(self, prog_name): metavar='', help=_('Server (name or ID)'), ) - parser.add_argument( + property_group = parser.add_mutually_exclusive_group() + property_group.add_argument( '--property', metavar='', action='append', @@ -5006,16 +5008,22 @@ def get_parser(self, prog_name): '(repeat option to remove multiple values)' ), ) + property_group.add_argument( + '--all-properties', + action='store_true', + help=_('Remove all properties'), + ) parser.add_argument( '--description', dest='description', action='store_true', help=_( - 'Unset server description (supported by ' - '--os-compute-api-version 2.19 or above)' + 'Unset server description ' + '(supported by --os-compute-api-version 2.19 or above)' ), ) - parser.add_argument( + tag_group = parser.add_mutually_exclusive_group() + tag_group.add_argument( '--tag', metavar='', action='append', @@ -5027,32 +5035,40 @@ def get_parser(self, prog_name): '(supported by --os-compute-api-version 2.26 or above)' ), ) + tag_group.add_argument( + '--all-tags', + action='store_true', + help=_( + 'Remove all tags ' + '(supported by --os-compute-api-version 2.26 or above)' + ), + ) return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute - server = utils.find_resource( - compute_client.servers, - parsed_args.server, + compute_client = self.app.client_manager.sdk_connection.compute + + server = compute_client.find_server( + parsed_args.server, ignore_missing=False ) - if parsed_args.properties: - compute_client.servers.delete_meta(server, parsed_args.properties) + if parsed_args.properties or parsed_args.all_properties: + compute_client.delete_server_metadata( + server, parsed_args.properties or None + ) if parsed_args.description: - if compute_client.api_version < api_versions.APIVersion("2.19"): + if not sdk_utils.supports_microversion(compute_client, '2.19'): msg = _( - '--os-compute-api-version 2.19 or greater is ' - 'required to support the --description option' + '--os-compute-api-version 2.19 or greater is required to ' + 'support the --description option' ) raise exceptions.CommandError(msg) - compute_client.servers.update( - server, - description="", - ) - if parsed_args.tags: - if compute_client.api_version < api_versions.APIVersion('2.26'): + compute_client.update_server(server, description="") + + if parsed_args.tags or parsed_args.all_tags: + if not sdk_utils.supports_microversion(compute_client, '2.26'): msg = _( '--os-compute-api-version 2.26 or greater is required to ' 'support the --tag option' @@ -5060,7 +5076,10 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) for tag in parsed_args.tags: - compute_client.servers.delete_tag(server, tag=tag) + compute_client.remove_tag_from_server(server, tag) + + if parsed_args.all_tags: + compute_client.remove_tags_from_server(server) class UnshelveServer(command.Command): diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index f460c0513..1384dfe8a 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -7843,62 +7843,60 @@ class TestServerSet(TestServer): def setUp(self): super().setUp() - self.attrs = { - 'api_version': None, - } - - self.methods = { - 'update': None, - 'reset_state': None, - 'change_password': None, - 'clear_password': None, - 'add_tag': None, - 'set_tags': None, - } - - self.fake_servers = self.setup_servers_mock(2) + self.server = compute_fakes.create_one_sdk_server() + self.compute_sdk_client.find_server.return_value = self.server # Get the command object to test self.cmd = server.SetServer(self.app, None) def test_server_set_no_option(self): - arglist = ['foo_vm'] - verifylist = [('server', 'foo_vm')] + arglist = [self.server.id] + verifylist = [('server', self.server.id)] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.assertNotCalled(self.fake_servers[0].update) - self.assertNotCalled(self.fake_servers[0].reset_state) - self.assertNotCalled(self.fake_servers[0].change_password) - self.assertNotCalled(self.servers_mock.set_meta) + + 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.assertIsNone(result) def test_server_set_with_state(self): - for index, state in enumerate(['active', 'error']): - arglist = [ - '--state', - state, - 'foo_vm', - ] - verifylist = [ - ('state', state), - ('server', 'foo_vm'), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - self.fake_servers[index].reset_state.assert_called_once_with( - state=state - ) - self.assertIsNone(result) + arglist = [ + '--state', + 'active', + self.server.id, + ] + verifylist = [ + ('state', 'active'), + ('server', self.server.id), + ] + + 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.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.assertIsNone(result) def test_server_set_with_invalid_state(self): arglist = [ '--state', 'foo_state', - 'foo_vm', + self.server.id, ] verifylist = [ ('state', 'foo_state'), - ('server', 'foo_vm'), + ('server', self.server.id), ] self.assertRaises( test_utils.ParserException, @@ -7912,15 +7910,24 @@ def test_server_set_with_name(self): arglist = [ '--name', 'foo_name', - 'foo_vm', + self.server.id, ] verifylist = [ ('name', 'foo_name'), - ('server', 'foo_vm'), + ('server', self.server.id), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.fake_servers[0].update.assert_called_once_with(name='foo_name') + + self.compute_sdk_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.assertIsNone(result) def test_server_set_with_property(self): @@ -7929,49 +7936,72 @@ def test_server_set_with_property(self): 'key1=value1', '--property', 'key2=value2', - 'foo_vm', + self.server.id, ] verifylist = [ ('properties', {'key1': 'value1', 'key2': 'value2'}), - ('server', 'foo_vm'), + ('server', self.server.id), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.servers_mock.set_meta.assert_called_once_with( - self.fake_servers[0], parsed_args.properties + + self.compute_sdk_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.assertIsNone(result) def test_server_set_with_password(self): arglist = [ '--password', 'foo', - 'foo_vm', + self.server.id, ] verifylist = [ ('password', 'foo'), - ('server', 'foo_vm'), + ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) - self.fake_servers[0].change_password.assert_called_once_with('foo') + self.compute_sdk_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.assertIsNone(result) def test_server_set_with_no_password(self): arglist = [ '--no-password', - 'foo_vm', + self.server.id, ] verifylist = [ ('no_password', True), - ('server', 'foo_vm'), + ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) - self.fake_servers[0].clear_password.assert_called_once_with() + self.compute_sdk_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.assertIsNone(result) # TODO(stephenfin): Remove this in a future major version @mock.patch.object( @@ -7980,17 +8010,24 @@ def test_server_set_with_no_password(self): def test_server_set_with_root_password(self, mock_getpass): arglist = [ '--root-password', - 'foo_vm', + self.server.id, ] verifylist = [ ('root_password', True), - ('server', 'foo_vm'), + ('server', self.server.id), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.fake_servers[0].change_password.assert_called_once_with( - mock.sentinel.fake_pass + + self.compute_sdk_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.assertIsNone(result) def test_server_set_with_description(self): @@ -7999,17 +8036,24 @@ def test_server_set_with_description(self): arglist = [ '--description', 'foo_description', - 'foo_vm', + self.server.id, ] verifylist = [ ('description', 'foo_description'), - ('server', 'foo_vm'), + ('server', self.server.id), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.fake_servers[0].update.assert_called_once_with( - description='foo_description' + + self.compute_sdk_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.assertIsNone(result) def test_server_set_with_description_pre_v219(self): @@ -8018,12 +8062,13 @@ def test_server_set_with_description_pre_v219(self): arglist = [ '--description', 'foo_description', - 'foo_vm', + self.server.id, ] verifylist = [ ('description', 'foo_description'), - ('server', 'foo_vm'), + ('server', self.server.id), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args @@ -8037,22 +8082,27 @@ def test_server_set_with_tag(self): 'tag1', '--tag', 'tag2', - 'foo_vm', + self.server.id, ] verifylist = [ ('tags', ['tag1', 'tag2']), - ('server', 'foo_vm'), + ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.fake_servers[0].add_tag.assert_has_calls( + self.compute_sdk_client.add_tag_to_server.assert_has_calls( [ - mock.call(tag='tag1'), - mock.call(tag='tag2'), + 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.assertIsNone(result) def test_server_set_with_tag_pre_v226(self): @@ -8063,14 +8113,14 @@ def test_server_set_with_tag_pre_v226(self): 'tag1', '--tag', 'tag2', - 'foo_vm', + self.server.id, ] verifylist = [ ('tags', ['tag1', 'tag2']), - ('server', 'foo_vm'), + ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) ex = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) @@ -8084,17 +8134,24 @@ def test_server_set_with_hostname(self): arglist = [ '--hostname', 'foo-hostname', - 'foo_vm', + self.server.id, ] verifylist = [ ('hostname', 'foo-hostname'), - ('server', 'foo_vm'), + ('server', self.server.id), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.fake_servers[0].update.assert_called_once_with( - hostname='foo-hostname' + + self.compute_sdk_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.assertIsNone(result) def test_server_set_with_hostname_pre_v290(self): @@ -8103,11 +8160,11 @@ def test_server_set_with_hostname_pre_v290(self): arglist = [ '--hostname', 'foo-hostname', - 'foo_vm', + self.server.id, ] verifylist = [ ('hostname', 'foo-hostname'), - ('server', 'foo_vm'), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( @@ -8718,21 +8775,29 @@ class TestServerUnset(TestServer): def setUp(self): super().setUp() - self.fake_server = self.setup_servers_mock(1)[0] + self.server = compute_fakes.create_one_sdk_server() + self.compute_sdk_client.find_server.return_value = self.server # Get the command object to test self.cmd = server.UnsetServer(self.app, None) def test_server_unset_no_option(self): arglist = [ - 'foo_vm', + self.server.id, ] verifylist = [ - ('server', 'foo_vm'), + ('server', self.server.id), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.assertNotCalled(self.servers_mock.delete_meta) + + 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.assertIsNone(result) def test_server_unset_with_property(self): @@ -8741,17 +8806,25 @@ def test_server_unset_with_property(self): 'key1', '--property', 'key2', - 'foo_vm', + self.server.id, ] verifylist = [ ('properties', ['key1', 'key2']), - ('server', 'foo_vm'), + ('server', self.server.id), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.servers_mock.delete_meta.assert_called_once_with( - self.fake_server, ['key1', 'key2'] + + self.compute_sdk_client.find_server( + self.server.id, ignore_missing=False + ) + self.compute_sdk_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.assertIsNone(result) def test_server_unset_with_description(self): @@ -8760,19 +8833,24 @@ def test_server_unset_with_description(self): arglist = [ '--description', - 'foo_vm', + self.server.id, ] verifylist = [ ('description', True), - ('server', 'foo_vm'), + ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.servers_mock.update.assert_called_once_with( - self.fake_server, description="" + self.compute_sdk_client.find_server( + self.server.id, ignore_missing=False + ) + self.compute_sdk_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.assertIsNone(result) def test_server_unset_with_description_pre_v219(self): @@ -8781,11 +8859,11 @@ def test_server_unset_with_description_pre_v219(self): arglist = [ '--description', - 'foo_vm', + self.server.id, ] verifylist = [ ('description', True), - ('server', 'foo_vm'), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -8804,23 +8882,28 @@ def test_server_unset_with_tag(self): 'tag1', '--tag', 'tag2', - 'foo_vm', + self.server.id, ] verifylist = [ ('tags', ['tag1', 'tag2']), - ('server', 'foo_vm'), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.servers_mock.delete_tag.assert_has_calls( + self.compute_sdk_client.find_server( + self.server.id, ignore_missing=False + ) + self.compute_sdk_client.remove_tag_from_server.assert_has_calls( [ - mock.call(self.fake_server, tag='tag1'), - mock.call(self.fake_server, tag='tag2'), + 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() def test_server_unset_with_tag_pre_v226(self): self.set_compute_api_version('2.25') @@ -8830,11 +8913,11 @@ def test_server_unset_with_tag_pre_v226(self): 'tag1', '--tag', 'tag2', - 'foo_vm', + self.server.id, ] verifylist = [ ('tags', ['tag1', 'tag2']), - ('server', 'foo_vm'), + ('server', self.server.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/releasenotes/notes/migrate-server-set-unset-to-sdk-ae32ebcced845b06.yaml b/releasenotes/notes/migrate-server-set-unset-to-sdk-ae32ebcced845b06.yaml new file mode 100644 index 000000000..d01c09a90 --- /dev/null +++ b/releasenotes/notes/migrate-server-set-unset-to-sdk-ae32ebcced845b06.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + The ``server set`` and ``server unset`` commands have been migrated to SDK. diff --git a/requirements.txt b/requirements.txt index 0cc6c7e88..a20ce69f8 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>=2.0.0 # Apache-2.0 +openstacksdk>=3.2.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 628ac48901b8d50d165ece98588e510b5a703612 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 9 May 2024 13:57:38 +0100 Subject: [PATCH 146/403] compute: Always use SDK client to display server This affects the 'server create', 'server show', 'server rebuild' and 'server set' commands. We also fix a few mistakes around the fields shown. Change-Id: I9946e12146efff39f9ba1591c90a4a9bccd46919 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 65 ++-- .../functional/compute/v2/test_server.py | 8 +- .../tests/unit/compute/v2/test_server.py | 300 +++++++++++++++--- 3 files changed, 287 insertions(+), 86 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 026feaccd..079e752a7 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -128,23 +128,26 @@ def _get_ip_address(addresses, address_type, ip_address_family): ) -def _prep_server_detail(compute_client, image_client, server, refresh=True): +def _prep_server_detail(compute_client, image_client, server, *, refresh=True): """Prepare the detailed server dict for printing :param compute_client: a compute client instance :param image_client: an image client instance :param server: a Server resource :param refresh: Flag indicating if ``server`` is already the latest version - or if it needs to be refreshed, for example when showing - the latest details of a server after creating it. + or if it needs to be refreshed, for example when showing the latest + details of a server after creating it. :rtype: a dict of server details """ - # Note: Some callers of this routine pass a novaclient server, and others - # pass an SDK server. Column names may be different across those cases. info = server.to_dict() + if refresh: - server = utils.find_resource(compute_client.servers, info['id']) - info.update(server.to_dict()) + server = compute_client.get_server(info['id']) + # we only update if the field is not empty, to avoid overwriting + # existing values + info.update( + **{x: y for x, y in server.to_dict().items() if x not in info or y} + ) # Some commands using this routine were originally implemented with the # nova python wrappers, and were later migrated to use the SDK. Map the @@ -159,7 +162,6 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): 'compute_host': 'OS-EXT-SRV-ATTR:host', 'created_at': 'created', 'disk_config': 'OS-DCF:diskConfig', - 'flavor_id': 'flavorRef', 'has_config_drive': 'config_drive', 'host_id': 'hostId', 'fault': 'fault', @@ -194,10 +196,12 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): 'public_v6', # create-only columns 'block_device_mapping', + 'flavor_id', 'host', 'image_id', 'max_count', 'min_count', + 'networks', 'personality', 'scheduler_hints', # aliases @@ -208,11 +212,12 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): # Some columns are only present in certain responses and should not be # shown otherwise. optional_columns = { - 'admin_password', # removed in 2.14 - 'fault', # only present in errored servers - 'flavor_id', # removed in 2.47 - 'networks', # only present in create responses - 'security_groups', # only present in create, detail responses + # only in create responses if '[api] enable_instance_password' is set + 'admin_password', + # only present in errored servers + 'fault', + # only present in create, detail responses + 'security_groups', } data = {} @@ -252,7 +257,9 @@ def _prep_server_detail(compute_client, image_client, server, refresh=True): if flavor_info.get('original_name') is None: # microversion < 2.47 flavor_id = flavor_info.get('id', '') try: - flavor = utils.find_resource(compute_client.flavors, flavor_id) + flavor = compute_client.find_flavor( + flavor_id, ignore_missing=False + ) info['flavor'] = f"{flavor.name} ({flavor_id})" except Exception: info['flavor'] = flavor_id @@ -2056,8 +2063,10 @@ def _match_image(image_api, wanted_properties): msg = _('Error creating server: %s') % parsed_args.server_name raise exceptions.CommandError(msg) - details = _prep_server_detail(compute_client, image_client, server) - return zip(*sorted(details.items())) + # TODO(stephenfin): Remove when the whole command is using SDK + compute_client = self.app.client_manager.sdk_connection.compute + data = _prep_server_detail(compute_client, image_client, server) + return zip(*sorted(data.items())) class CreateServerDump(command.Command): @@ -3681,10 +3690,12 @@ def _show_progress(progress): msg = _('Error rebuilding server: %s') % server.id raise exceptions.CommandError(msg) - details = _prep_server_detail( + # TODO(stephenfin): Remove when the whole command is using SDK + compute_client = self.app.client_manager.sdk_connection.compute + data = _prep_server_detail( compute_client, image_client, server, refresh=False ) - return zip(*sorted(details.items())) + return zip(*sorted(data.items())) class EvacuateServer(command.ShowOne): @@ -3803,10 +3814,10 @@ def _show_progress(progress): msg = _('Error evacuating server: %s') % server.id raise exceptions.CommandError(msg) - details = _prep_server_detail( - compute_client, image_client, server, refresh=True - ) - return zip(*sorted(details.items())) + # TODO(stephenfin): Remove when the whole command is using SDK + compute_client = self.app.client_manager.sdk_connection.compute + data = _prep_server_detail(compute_client, image_client, server) + return zip(*sorted(data.items())) class RemoveFixedIP(command.Command): @@ -4629,6 +4640,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): compute_client = self.app.client_manager.sdk_connection.compute + image_client = self.app.client_manager.image # Find by name or ID, then get the full details of the server server = compute_client.find_server( @@ -4652,17 +4664,10 @@ def take_action(self, parsed_args): topology = server.fetch_topology(compute_client) data = _prep_server_detail( - # TODO(dannosliwcd): Replace these clients with SDK clients after - # all callers of _prep_server_detail() are using the SDK. - self.app.client_manager.compute, - self.app.client_manager.image, - server, - refresh=False, + compute_client, image_client, server, refresh=False ) - if topology: data['topology'] = format_columns.DictColumn(topology) - return zip(*sorted(data.items())) diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py index e465acc4a..d6638d2f7 100644 --- a/openstackclient/tests/functional/compute/v2/test_server.py +++ b/openstackclient/tests/functional/compute/v2/test_server.py @@ -210,10 +210,10 @@ def test_server_set(self): self.flavor_name, flavor['name'], ) - self.assertEqual( - '{} ({})'.format(flavor['name'], flavor['id']), - cmd_output["flavor"], - ) + # assume the v2.47+ output format + self.assertIsInstance(cmd_output['flavor'], dict) + self.assertIn('name', cmd_output['flavor']) + self.assertEqual(flavor['name'], cmd_output['flavor']['name']) image = self.openstack( 'image show ' + self.image_name, parse_output=True, diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 1384dfe8a..bc3b3e887 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -1286,49 +1286,125 @@ def test_server_add_security_group(self): class TestServerCreate(TestServer): columns = ( + 'OS-DCF:diskConfig', + 'OS-EXT-AZ:availability_zone', + 'OS-EXT-SRV-ATTR:host', + 'OS-EXT-SRV-ATTR:hostname', + 'OS-EXT-SRV-ATTR:hypervisor_hostname', + 'OS-EXT-SRV-ATTR:instance_name', + 'OS-EXT-SRV-ATTR:kernel_id', + 'OS-EXT-SRV-ATTR:launch_index', + 'OS-EXT-SRV-ATTR:ramdisk_id', + 'OS-EXT-SRV-ATTR:reservation_id', + 'OS-EXT-SRV-ATTR:root_device_name', + 'OS-EXT-SRV-ATTR:user_data', 'OS-EXT-STS:power_state', + 'OS-EXT-STS:task_state', + 'OS-EXT-STS:vm_state', + 'OS-SRV-USG:launched_at', + 'OS-SRV-USG:terminated_at', + 'accessIPv4', + 'accessIPv6', 'addresses', + 'config_drive', + 'created', + 'description', 'flavor', + 'hostId', + 'host_status', 'id', 'image', + 'key_name', + 'locked', + 'locked_reason', 'name', + 'pinned_availability_zone', + 'progress', + 'project_id', 'properties', + 'server_groups', + 'status', + 'tags', + 'trusted_image_certificates', + 'updated', + 'user_id', + 'volumes_attached', ) def datalist(self): - datalist = ( + return ( + None, # OS-DCF:diskConfig + None, # OS-EXT-AZ:availability_zone + None, # OS-EXT-SRV-ATTR:host + None, # OS-EXT-SRV-ATTR:hostname + None, # OS-EXT-SRV-ATTR:hypervisor_hostname + None, # OS-EXT-SRV-ATTR:instance_name + None, # OS-EXT-SRV-ATTR:kernel_id + None, # OS-EXT-SRV-ATTR:launch_index + None, # OS-EXT-SRV-ATTR:ramdisk_id + None, # OS-EXT-SRV-ATTR:reservation_id + None, # OS-EXT-SRV-ATTR:root_device_name + None, # OS-EXT-SRV-ATTR:user_data server.PowerStateColumn( - getattr(self.new_server, 'OS-EXT-STS:power_state') - ), - format_columns.DictListColumn({}), - self.flavor.name + ' (' + self.new_server.flavor.get('id') + ')', - self.new_server.id, - self.image.name + ' (' + self.new_server.image.get('id') + ')', - self.new_server.name, - format_columns.DictColumn(self.new_server.metadata), + self.server.power_state + ), # OS-EXT-STS:power_state # noqa: E501 + None, # OS-EXT-STS:task_state + None, # OS-EXT-STS:vm_state + None, # OS-SRV-USG:launched_at + None, # OS-SRV-USG:terminated_at + None, # accessIPv4 + None, # accessIPv6 + server.AddressesColumn({}), # addresses + None, # config_drive + None, # created + None, # description + self.flavor.name + " (" + self.flavor.id + ")", # flavor + None, # hostId + None, # host_status + self.server.id, # id + self.image.name + " (" + self.image.id + ")", # image + None, # key_name + None, # locked + None, # locked_reason + self.server.name, + None, # pinned_availability_zone + None, # progress + None, # project_id + format_columns.DictColumn({}), # properties + None, # server_groups + None, # status + format_columns.ListColumn([]), # tags + None, # trusted_image_certificates + None, # updated + None, # user_id + format_columns.ListDictColumn([]), # volumes_attached ) - return datalist def setUp(self): super().setUp() - attrs = { - 'networks': {}, - } - self.new_server = compute_fakes.create_one_server(attrs=attrs) - - # This is the return value for utils.find_resource(). - # This is for testing --wait option. - self.servers_mock.get.return_value = self.new_server - - self.servers_mock.create.return_value = self.new_server - self.image = image_fakes.create_one_image() self.image_client.find_image.return_value = self.image self.image_client.get_image.return_value = self.image self.flavor = compute_fakes.create_one_flavor() self.flavors_mock.get.return_value = self.flavor + self.compute_sdk_client.find_flavor.return_value = self.flavor + + attrs = { + 'addresses': {}, + 'networks': {}, + 'image': self.image, + 'flavor': self.flavor, + } + self.new_server = compute_fakes.create_one_server(attrs=attrs) + self.servers_mock.create.return_value = self.new_server + + # We need an SDK-style server object also for the get_server call in + # _prep_server_detail + # TODO(stephenfin): Remove when the whole command is using SDK + self.server = compute_fakes.create_one_sdk_server(attrs=attrs) + self.compute_sdk_client.get_server.return_value = self.server self.volume = volume_fakes.create_one_volume() self.volume_alt = volume_fakes.create_one_volume() @@ -4706,9 +4782,6 @@ def test_server_list_no_option(self): self.compute_sdk_client.servers.assert_called_with(**self.kwargs) self.image_client.images.assert_called() self.compute_sdk_client.flavors.assert_called() - # we did not pass image or flavor, so gets on those must be absent - self.assertFalse(self.flavors_mock.get.call_count) - self.assertFalse(self.image_client.get_image.call_count) self.assertEqual(self.columns, columns) self.assertEqual(self.data, tuple(data)) @@ -6890,7 +6963,7 @@ def test_rebuild_with_reimage_boot_volume_pre_v293(self): ) -class TestEvacuateServer(TestServer): +class TestServerEvacuate(TestServer): def setUp(self): super().setUp() @@ -6918,6 +6991,12 @@ def setUp(self): # Return value for utils.find_resource for server. self.servers_mock.get.return_value = self.server + # We need an SDK-style server object also for the get_server call in + # _prep_server_detail + # TODO(stephenfin): Remove when the whole command is using SDK + self.sdk_server = compute_fakes.create_one_sdk_server(attrs=attrs) + self.compute_sdk_client.get_server.return_value = self.sdk_server + self.cmd = server.EvacuateServer(self.app, None) def _test_evacuate(self, args, verify_args, evac_args): @@ -8300,7 +8379,11 @@ def setUp(self): super().setUp() self.image = image_fakes.create_one_image() + 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.topology = { 'nodes': [{'vcpu_set': [0, 1]}, {'vcpu_set': [2, 3]}], 'pagesize_kb': None, @@ -8318,11 +8401,7 @@ def setUp(self): attrs=server_info, ) self.server.fetch_topology = mock.MagicMock(return_value=self.topology) - - # This is the return value for utils.find_resource() self.compute_sdk_client.get_server.return_value = self.server - self.image_client.get_image.return_value = self.image - self.flavors_mock.get.return_value = self.flavor # Get the command object to test self.cmd = server.ShowServer(self.app, None) @@ -9241,15 +9320,13 @@ def test_get_ip_address(self): [6], ) - @mock.patch('osc_lib.utils.find_resource') - def test_prep_server_detail(self, find_resource): - # Setup mock method return value. utils.find_resource() will be called - # three times in _prep_server_detail(): - # - The first time, return server info. - # - The second time, return image info. - # - The third time, return flavor info. + def test_prep_server_detail(self): _image = image_fakes.create_one_image() + self.image_client.get_image.return_value = _image + _flavor = compute_fakes.create_one_flavor() + self.compute_sdk_client.find_flavor.return_value = _flavor + server_info = { 'image': {'id': _image.id}, 'flavor': {'id': _flavor.id}, @@ -9259,31 +9336,150 @@ def test_prep_server_detail(self, find_resource): 'properties': '', 'volumes_attached': [{"id": "6344fe9d-ef20-45b2-91a6"}], } - _server = compute_fakes.create_one_server(attrs=server_info) - find_resource.side_effect = [_server, _flavor] - self.image_client.get_image.return_value = _image - - # Prepare result data. - info = { - 'id': _server.id, - 'name': _server.name, - 'image': f'{_image.name} ({_image.id})', - 'flavor': f'{_flavor.name} ({_flavor.id})', + _server = compute_fakes.create_one_sdk_server(server_info) + self.compute_sdk_client.get_server.return_value = _server + + expected = { + 'OS-DCF:diskConfig': None, + 'OS-EXT-AZ:availability_zone': None, + 'OS-EXT-SRV-ATTR:host': None, + 'OS-EXT-SRV-ATTR:hostname': None, + 'OS-EXT-SRV-ATTR:hypervisor_hostname': None, + 'OS-EXT-SRV-ATTR:instance_name': None, + 'OS-EXT-SRV-ATTR:kernel_id': None, + 'OS-EXT-SRV-ATTR:launch_index': None, + 'OS-EXT-SRV-ATTR:ramdisk_id': None, + 'OS-EXT-SRV-ATTR:reservation_id': None, + 'OS-EXT-SRV-ATTR:root_device_name': None, + 'OS-EXT-SRV-ATTR:user_data': None, 'OS-EXT-STS:power_state': server.PowerStateColumn( - getattr(_server, 'OS-EXT-STS:power_state') + _server.power_state ), + 'OS-EXT-STS:task_state': None, + 'OS-EXT-STS:vm_state': None, + 'OS-SRV-USG:launched_at': None, + 'OS-SRV-USG:terminated_at': None, + 'accessIPv4': None, + 'accessIPv6': None, + 'addresses': format_columns.DictListColumn(_server.addresses), + 'config_drive': None, + 'created': None, + 'description': None, + 'flavor': f'{_flavor.name} ({_flavor.id})', + 'hostId': None, + 'host_status': None, + 'id': _server.id, + 'image': f'{_image.name} ({_image.id})', + 'key_name': None, + 'locked': None, + 'locked_reason': None, + 'name': _server.name, + 'pinned_availability_zone': None, + 'progress': None, + 'project_id': 'tenant-id-xxx', + 'properties': format_columns.DictColumn({}), + 'server_groups': None, + 'status': None, + 'tags': format_columns.ListColumn([]), + 'trusted_image_certificates': None, + 'updated': None, + 'user_id': None, + 'volumes_attached': format_columns.ListDictColumn([]), + } + + actual = server._prep_server_detail( + self.compute_sdk_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( + _flavor.id, ignore_missing=False + ) + + def test_prep_server_detail_v247(self): + _image = image_fakes.create_one_image() + self.image_client.get_image.return_value = _image + + _flavor = compute_fakes.create_one_flavor() + self.compute_sdk_client.find_flavor.return_value = _flavor + + server_info = { + 'image': {'id': _image.id}, + 'flavor': { + 'vcpus': _flavor.vcpus, + 'ram': _flavor.ram, + 'disk': _flavor.disk, + 'ephemeral': _flavor.ephemeral, + 'swap': _flavor.swap, + 'original_name': _flavor.name, + 'extra_specs': {}, + }, + 'tenant_id': 'tenant-id-xxx', + 'addresses': {'public': ['10.20.30.40', '2001:db8::f']}, + 'links': 'http://xxx.yyy.com', 'properties': '', '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 + + expected = { + 'OS-DCF:diskConfig': None, + 'OS-EXT-AZ:availability_zone': None, + 'OS-EXT-SRV-ATTR:host': None, + 'OS-EXT-SRV-ATTR:hostname': None, + 'OS-EXT-SRV-ATTR:hypervisor_hostname': None, + 'OS-EXT-SRV-ATTR:instance_name': None, + 'OS-EXT-SRV-ATTR:kernel_id': None, + 'OS-EXT-SRV-ATTR:launch_index': None, + 'OS-EXT-SRV-ATTR:ramdisk_id': None, + 'OS-EXT-SRV-ATTR:reservation_id': None, + 'OS-EXT-SRV-ATTR:root_device_name': None, + 'OS-EXT-SRV-ATTR:user_data': None, + 'OS-EXT-STS:power_state': server.PowerStateColumn( + _server.power_state + ), + 'OS-EXT-STS:task_state': None, + 'OS-EXT-STS:vm_state': None, + 'OS-SRV-USG:launched_at': None, + 'OS-SRV-USG:terminated_at': None, + 'accessIPv4': None, + 'accessIPv6': None, 'addresses': format_columns.DictListColumn(_server.addresses), + 'config_drive': None, + 'created': None, + 'description': None, + 'flavor': f'{_flavor.name} ({_flavor.id})', + 'hostId': None, + 'host_status': None, + 'id': _server.id, + 'image': f'{_image.name} ({_image.id})', + 'key_name': None, + 'locked': None, + 'locked_reason': None, + 'name': _server.name, + 'pinned_availability_zone': None, + 'progress': None, 'project_id': 'tenant-id-xxx', + 'properties': format_columns.DictColumn({}), + 'server_groups': None, + 'status': None, + 'tags': format_columns.ListColumn([]), + 'trusted_image_certificates': None, + 'updated': None, + 'user_id': None, + 'volumes_attached': format_columns.ListDictColumn([]), } - # Call _prep_server_detail(). - server_detail = server._prep_server_detail( - self.compute_client, + actual = server._prep_server_detail( + self.compute_sdk_client, self.image_client, _server, ) - # Check the results. - self.assertCountEqual(info, server_detail) + 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() From 2057462120f36ce35efd3adff17003f89019547e Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 9 May 2024 13:48:43 +0100 Subject: [PATCH 147/403] compute: Avoid third API call during 'server show' We can use a detailed list instead when looking up by name. We also improve tests somewhat. Change-Id: I18b38e7fbcac813190b304c4d67d8ea03d8c1a80 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 6 ++-- .../tests/unit/compute/v2/test_server.py | 29 ++++++++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 079e752a7..5999ad4fe 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -4642,11 +4642,11 @@ def take_action(self, parsed_args): compute_client = self.app.client_manager.sdk_connection.compute image_client = self.app.client_manager.image - # Find by name or ID, then get the full details of the server server = compute_client.find_server( - parsed_args.server, ignore_missing=False + parsed_args.server, + ignore_missing=False, + details=True, ) - server = compute_client.get_server(server) if parsed_args.diagnostics: data = compute_client.get_server_diagnostics(server) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index bc3b3e887..5878d1530 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -8401,7 +8401,7 @@ def setUp(self): attrs=server_info, ) self.server.fetch_topology = mock.MagicMock(return_value=self.topology) - self.compute_sdk_client.get_server.return_value = self.server + self.compute_sdk_client.find_server.return_value = self.server # Get the command object to test self.cmd = server.ShowServer(self.app, None) @@ -8530,6 +8530,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.server.name, ignore_missing=False, details=True + ) + self.compute_sdk_client.get_server.assert_not_called() def test_show_embedded_flavor(self): # Tests using --os-compute-api-version >= 2.47 where the flavor @@ -8558,6 +8562,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.server.name, ignore_missing=False, details=True + ) + self.compute_sdk_client.get_server.assert_not_called() def test_show_diagnostics(self): arglist = [ @@ -8575,6 +8583,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.server.name, ignore_missing=False, details=True + ) + self.compute_sdk_client.get_server_diagnostics.assert_called_once_with( + self.server + ) + self.compute_sdk_client.get_server.assert_not_called() def test_show_topology(self): self.set_compute_api_version('2.78') @@ -8597,6 +8612,13 @@ 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.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() def test_show_topology_pre_v278(self): self.set_compute_api_version('2.77') @@ -8615,6 +8637,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.server.name, ignore_missing=False, details=True + ) + self.server.fetch_topology.assert_not_called() + self.compute_sdk_client.get_server.assert_not_called() @mock.patch('openstackclient.compute.v2.server.os.system') From 8d904a9efb52dfe5686150e1213e01b9fc36b458 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 17 May 2024 12:24:14 +0100 Subject: [PATCH 148/403] compute: Migrate 'server rebuild' to SDK Change-Id: Ic7cdb05327a4a74364f08451e531d02c631b7633 Signed-off-by: Stephen Finucane Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/918730 --- openstackclient/compute/v2/server.py | 55 +- .../tests/unit/compute/v2/test_server.py | 477 +++++++++++------- 2 files changed, 327 insertions(+), 205 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 5999ad4fe..aa4accf4e 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -16,6 +16,7 @@ """Compute v2 Server action implementations""" import argparse +import base64 import getpass import json import logging @@ -3505,11 +3506,11 @@ def _show_progress(progress): self.app.stdout.write('\rProgress: %s' % progress) self.app.stdout.flush() - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute image_client = self.app.client_manager.image - server = utils.find_resource( - compute_client.servers, parsed_args.server + server = compute_client.find_server( + parsed_args.server, ignore_missing=False ) # If parsed_args.image is not set and if the instance is image backed, @@ -3521,7 +3522,7 @@ def _show_progress(progress): parsed_args.image, ignore_missing=False ) else: - if not server.image: + if not server.image or not server.image.id: msg = _( 'The --image option is required when rebuilding a ' 'volume-backed server' @@ -3538,10 +3539,10 @@ def _show_progress(progress): kwargs['preserve_ephemeral'] = parsed_args.preserve_ephemeral if parsed_args.properties: - kwargs['meta'] = parsed_args.properties + kwargs['metadata'] = parsed_args.properties if parsed_args.description: - if compute_client.api_version < api_versions.APIVersion('2.19'): + if not sdk_utils.supports_microversion(compute_client, '2.19'): msg = _( '--os-compute-api-version 2.19 or greater is required to ' 'support the --description option' @@ -3551,7 +3552,7 @@ def _show_progress(progress): kwargs['description'] = parsed_args.description if parsed_args.key_name: - if compute_client.api_version < api_versions.APIVersion('2.54'): + if not sdk_utils.supports_microversion(compute_client, '2.54'): msg = _( '--os-compute-api-version 2.54 or greater is required to ' 'support the --key-name option' @@ -3560,7 +3561,7 @@ def _show_progress(progress): kwargs['key_name'] = parsed_args.key_name elif parsed_args.no_key_name: - if compute_client.api_version < api_versions.APIVersion('2.54'): + if not sdk_utils.supports_microversion(compute_client, '2.54'): msg = _( '--os-compute-api-version 2.54 or greater is required to ' 'support the --no-key-name option' @@ -3569,9 +3570,8 @@ def _show_progress(progress): kwargs['key_name'] = None - userdata = None if parsed_args.user_data: - if compute_client.api_version < api_versions.APIVersion('2.54'): + if not sdk_utils.supports_microversion(compute_client, '2.54'): msg = _( '--os-compute-api-version 2.54 or greater is required to ' 'support the --user-data option' @@ -3579,27 +3579,29 @@ def _show_progress(progress): raise exceptions.CommandError(msg) try: - userdata = open(parsed_args.user_data) + with open(parsed_args.user_data, 'rb') as fh: + # TODO(stephenfin): SDK should do this for us + user_data = base64.b64encode(fh.read()).decode('utf-8') except OSError as e: msg = _("Can't open '%(data)s': %(exception)s") raise exceptions.CommandError( msg % {'data': parsed_args.user_data, 'exception': e} ) - kwargs['userdata'] = userdata + kwargs['user_data'] = user_data elif parsed_args.no_user_data: - if compute_client.api_version < api_versions.APIVersion('2.54'): + if not sdk_utils.supports_microversion(compute_client, '2.54'): msg = _( '--os-compute-api-version 2.54 or greater is required to ' 'support the --no-user-data option' ) raise exceptions.CommandError(msg) - kwargs['userdata'] = None + kwargs['user_data'] = None # TODO(stephenfin): Handle OS_TRUSTED_IMAGE_CERTIFICATE_IDS if parsed_args.trusted_image_certs: - if compute_client.api_version < api_versions.APIVersion('2.63'): + if not sdk_utils.supports_microversion(compute_client, '2.63'): msg = _( '--os-compute-api-version 2.63 or greater is required to ' 'support the --trusted-certs option' @@ -3609,7 +3611,7 @@ def _show_progress(progress): certs = parsed_args.trusted_image_certs kwargs['trusted_image_certificates'] = certs elif parsed_args.no_trusted_image_certs: - if compute_client.api_version < api_versions.APIVersion('2.63'): + if not sdk_utils.supports_microversion(compute_client, '2.63'): msg = _( '--os-compute-api-version 2.63 or greater is required to ' 'support the --no-trusted-certs option' @@ -3619,7 +3621,7 @@ def _show_progress(progress): kwargs['trusted_image_certificates'] = None if parsed_args.hostname: - if compute_client.api_version < api_versions.APIVersion('2.90'): + if not sdk_utils.supports_microversion(compute_client, '2.90'): msg = _( '--os-compute-api-version 2.90 or greater is required to ' 'support the --hostname option' @@ -3628,9 +3630,8 @@ def _show_progress(progress): kwargs['hostname'] = parsed_args.hostname - v2_93 = api_versions.APIVersion('2.93') if parsed_args.reimage_boot_volume: - if compute_client.api_version < v2_93: + if not sdk_utils.supports_microversion(compute_client, '2.93'): msg = _( '--os-compute-api-version 2.93 or greater is required to ' 'support the --reimage-boot-volume option' @@ -3639,8 +3640,8 @@ def _show_progress(progress): else: # force user to explicitly request reimaging of volume-backed # server - if not server.image: - if compute_client.api_version >= v2_93: + if not server.image or not server.image.id: + if sdk_utils.supports_microversion(compute_client, '2.93'): msg = ( '--reimage-boot-volume is required to rebuild a ' 'volume-backed server' @@ -3672,15 +3673,13 @@ def _show_progress(progress): msg = _("The server status is not ACTIVE, SHUTOFF or ERROR.") raise exceptions.CommandError(msg) - try: - server = server.rebuild(image, parsed_args.password, **kwargs) - finally: - if userdata and hasattr(userdata, 'close'): - userdata.close() + server = compute_client.rebuild_server( + server, image, admin_password=parsed_args.password, **kwargs + ) if parsed_args.wait: if utils.wait_for_status( - compute_client.servers.get, + compute_client.get_server, server.id, callback=_show_progress, success_status=success_status, @@ -3690,8 +3689,6 @@ def _show_progress(progress): msg = _('Error rebuilding server: %s') % server.id raise exceptions.CommandError(msg) - # TODO(stephenfin): Remove when the whole command is using SDK - compute_client = self.app.client_manager.sdk_connection.compute data = _prep_server_detail( compute_client, image_client, server, refresh=False ) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 5878d1530..9bac34bb9 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import base64 import collections import copy import getpass @@ -6227,59 +6228,47 @@ class TestServerRebuild(TestServer): def setUp(self): super().setUp() - # Return value for utils.find_resource for image self.image = image_fakes.create_one_image() self.image_client.get_image.return_value = self.image - # Fake the rebuilt new server. attrs = { + 'status': 'ACTIVE', 'image': {'id': self.image.id}, - 'networks': {}, - 'adminPass': 'passw0rd', - } - new_server = compute_fakes.create_one_server(attrs=attrs) - - # Fake the server to be rebuilt. The IDs of them should be the same. - attrs['id'] = new_server.id - attrs['status'] = 'ACTIVE' - methods = { - 'rebuild': new_server, } - self.server = compute_fakes.create_one_server( - attrs=attrs, methods=methods - ) - - # Return value for utils.find_resource for server. - self.servers_mock.get.return_value = self.server + 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.cmd = server.RebuildServer(self.app, None) def test_rebuild_with_image_name(self): image_name = 'my-custom-image' - user_image = image_fakes.create_one_image(attrs={'name': image_name}) - self.image_client.find_image.return_value = user_image + image = image_fakes.create_one_image(attrs={'name': image_name}) + self.image_client.find_image.return_value = image - attrs = { - 'image': {'id': user_image.id}, - 'networks': {}, - 'adminPass': 'passw0rd', - } - new_server = compute_fakes.create_one_server(attrs=attrs) - self.server.rebuild.return_value = new_server + arglist = [ + self.server.id, + '--image', + image_name, + ] + verifylist = [ + ('server', self.server.id), + ('image', image_name), + ] - arglist = [self.server.id, '--image', image_name] - verifylist = [('server', self.server.id), ('image', image_name)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # Get the command object to test. self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) + self.compute_sdk_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(user_image.id) - self.server.rebuild.assert_called_with(user_image, None) + 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 + ) def test_rebuild_with_current_image(self): arglist = [ @@ -6291,10 +6280,16 @@ def test_rebuild_with_current_image(self): # Get the command object to test. self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) + self.compute_sdk_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_with(self.image.id) - self.server.rebuild.assert_called_with(self.image, None) + 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.server, self.image, admin_password=None + ) def test_rebuild_with_volume_backed_server_no_image(self): # the volume-backed server will have the image attribute set to an @@ -6307,8 +6302,8 @@ def test_rebuild_with_volume_backed_server_no_image(self): verifylist = [ ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) @@ -6325,14 +6320,20 @@ def test_rebuild_with_name(self): ('server', self.server.id), ('name', name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # Get the command object to test + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with(self.image, None, name=name) + self.compute_sdk_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.server, self.image, admin_password=None, name=name + ) def test_rebuild_with_preserve_ephemeral(self): arglist = [ @@ -6343,15 +6344,22 @@ def test_rebuild_with_preserve_ephemeral(self): ('server', self.server.id), ('preserve_ephemeral', True), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # Get the command object to test + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with( - self.image, None, preserve_ephemeral=True + self.compute_sdk_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.server, + self.image, + admin_password=None, + preserve_ephemeral=True, ) def test_rebuild_with_no_preserve_ephemeral(self): @@ -6368,24 +6376,40 @@ def test_rebuild_with_no_preserve_ephemeral(self): # Get the command object to test self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with( - self.image, None, preserve_ephemeral=False + self.compute_sdk_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.server, + self.image, + admin_password=None, + preserve_ephemeral=False, ) def test_rebuild_with_password(self): password = 'password-xxx' arglist = [self.server.id, '--password', password] verifylist = [('server', self.server.id), ('password', password)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # Get the command object to test + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with(self.image, password) + self.compute_sdk_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.server, + self.image, + admin_password=password, + ) def test_rebuild_with_description(self): self.set_compute_api_version('2.19') @@ -6393,14 +6417,22 @@ def test_rebuild_with_description(self): description = 'description1' arglist = [self.server.id, '--description', description] verifylist = [('server', self.server.id), ('description', description)] - 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.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with( - self.image, None, description=description + self.compute_sdk_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.server, + self.image, + admin_password=None, + description=description, ) def test_rebuild_with_description_pre_v219(self): @@ -6409,8 +6441,8 @@ def test_rebuild_with_description_pre_v219(self): description = 'description1' arglist = [self.server.id, '--description', description] verifylist = [('server', self.server.id), ('description', description)] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) @@ -6425,25 +6457,30 @@ def test_rebuild_with_wait_ok(self, mock_wait_for_status): ('wait', True), ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # Get the command object to test. + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - # kwargs = dict(success_status=['active', 'verify_resize'],) + self.compute_sdk_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.server, + self.image, + admin_password=None, + ) mock_wait_for_status.assert_called_once_with( - self.servers_mock.get, + self.compute_sdk_client.get_server, self.server.id, callback=mock.ANY, success_status=['active'], - # **kwargs ) - self.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with(self.image, None) - @mock.patch.object(common_utils, 'wait_for_status', return_value=False) def test_rebuild_with_wait_fails(self, mock_wait_for_status): arglist = [ @@ -6454,23 +6491,30 @@ def test_rebuild_with_wait_fails(self, mock_wait_for_status): ('wait', True), ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.compute_sdk_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.server, + self.image, + admin_password=None, + ) + mock_wait_for_status.assert_called_once_with( - self.servers_mock.get, + self.compute_sdk_client.get_server, self.server.id, callback=mock.ANY, success_status=['active'], ) - self.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with(self.image, None) - @mock.patch.object(common_utils, 'wait_for_status', return_value=True) def test_rebuild_with_wait_shutoff_status(self, mock_wait_for_status): self.server.status = 'SHUTOFF' @@ -6482,25 +6526,30 @@ def test_rebuild_with_wait_shutoff_status(self, mock_wait_for_status): ('wait', True), ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # Get the command object to test. + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - # kwargs = dict(success_status=['active', 'verify_resize'],) + self.compute_sdk_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.server, + self.image, + admin_password=None, + ) mock_wait_for_status.assert_called_once_with( - self.servers_mock.get, + self.compute_sdk_client.get_server, self.server.id, callback=mock.ANY, success_status=['shutoff'], - # **kwargs ) - self.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with(self.image, None) - @mock.patch.object(common_utils, 'wait_for_status', return_value=True) def test_rebuild_with_wait_error_status(self, mock_wait_for_status): self.server.status = 'ERROR' @@ -6512,25 +6561,30 @@ def test_rebuild_with_wait_error_status(self, mock_wait_for_status): ('wait', True), ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # Get the command object to test. + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - # kwargs = dict(success_status=['active', 'verify_resize'],) + self.compute_sdk_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.server, + self.image, + admin_password=None, + ) mock_wait_for_status.assert_called_once_with( - self.servers_mock.get, + self.compute_sdk_client.get_server, self.server.id, callback=mock.ANY, success_status=['active'], - # **kwargs ) - self.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with(self.image, None) - def test_rebuild_wrong_status_fails(self): self.server.status = 'SHELVED' arglist = [ @@ -6539,15 +6593,18 @@ def test_rebuild_wrong_status_fails(self): verifylist = [ ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_not_called() + self.compute_sdk_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() def test_rebuild_with_property(self): arglist = [ @@ -6562,15 +6619,22 @@ def test_rebuild_with_property(self): ('server', self.server.id), ('properties', expected_properties), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # Get the command object to test + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with( - self.image, None, meta=expected_properties + self.compute_sdk_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.server, + self.image, + admin_password=None, + metadata=expected_properties, ) def test_rebuild_with_keypair_name(self): @@ -6586,14 +6650,22 @@ def test_rebuild_with_keypair_name(self): ('server', self.server.id), ('key_name', self.server.key_name), ] - 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.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with( - self.image, None, key_name=self.server.key_name + self.compute_sdk_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.server, + self.image, + admin_password=None, + key_name=self.server.key_name, ) def test_rebuild_with_keypair_name_pre_v254(self): @@ -6626,12 +6698,23 @@ def test_rebuild_with_no_keypair_name(self): verifylist = [ ('server', self.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.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with(self.image, None, key_name=None) + + self.compute_sdk_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.server, + self.image, + admin_password=None, + key_name=None, + ) def test_rebuild_with_keypair_name_and_unset(self): self.server.key_name = 'mykey' @@ -6653,14 +6736,10 @@ def test_rebuild_with_keypair_name_and_unset(self): verifylist, ) - @mock.patch('openstackclient.compute.v2.server.open') - def test_rebuild_with_user_data(self, mock_open): + def test_rebuild_with_user_data(self): self.set_compute_api_version('2.57') - mock_file = mock.Mock(name='File') - mock_open.return_value = mock_file - mock_open.read.return_value = '#!/bin/sh' - + user_data = b'#!/bin/sh' arglist = [ self.server.id, '--user-data', @@ -6670,22 +6749,29 @@ def test_rebuild_with_user_data(self, mock_open): ('server', self.server.id), ('user_data', 'userdata.sh'), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + with mock.patch( + 'openstackclient.compute.v2.server.open', + mock.mock_open(read_data=user_data), + ) as mock_file: + self.cmd.take_action(parsed_args) # Ensure the userdata file is opened - mock_open.assert_called_with('userdata.sh') + mock_file.assert_called_with('userdata.sh', 'rb') - # Ensure the userdata file is closed - mock_file.close.assert_called_with() - - self.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with( + self.compute_sdk_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.server, self.image, - None, - userdata=mock_file, + admin_password=None, + user_data=base64.b64encode(user_data).decode('utf-8'), ) def test_rebuild_with_user_data_pre_v257(self): @@ -6700,8 +6786,8 @@ def test_rebuild_with_user_data_pre_v257(self): ('server', self.server.id), ('user_data', 'userdata.sh'), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) @@ -6718,12 +6804,23 @@ def test_rebuild_with_no_user_data(self): ('server', self.server.id), ('no_user_data', True), ] - 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.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with(self.image, None, userdata=None) + + self.compute_sdk_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.server, + self.image, + admin_password=None, + user_data=None, + ) def test_rebuild_with_no_user_data_pre_v254(self): self.set_compute_api_version('2.53') @@ -6736,8 +6833,8 @@ def test_rebuild_with_no_user_data_pre_v254(self): ('server', self.server.id), ('no_user_data', True), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) @@ -6771,14 +6868,22 @@ def test_rebuild_with_trusted_image_cert(self): ('server', self.server.id), ('trusted_image_certs', ['foo', 'bar']), ] - 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.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with( - self.image, None, trusted_image_certificates=['foo', 'bar'] + self.compute_sdk_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.server, + self.image, + admin_password=None, + trusted_image_certificates=['foo', 'bar'], ) def test_rebuild_with_trusted_image_cert_pre_v263(self): @@ -6795,8 +6900,8 @@ def test_rebuild_with_trusted_image_cert_pre_v263(self): ('server', self.server.id), ('trusted_image_certs', ['foo', 'bar']), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) @@ -6812,13 +6917,22 @@ def test_rebuild_with_no_trusted_image_cert(self): ('server', self.server.id), ('no_trusted_image_certs', True), ] - 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.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with( - self.image, None, trusted_image_certificates=None + + self.compute_sdk_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.server, + self.image, + admin_password=None, + trusted_image_certificates=None, ) def test_rebuild_with_no_trusted_image_cert_pre_v263(self): @@ -6841,16 +6955,31 @@ def test_rebuild_with_no_trusted_image_cert_pre_v263(self): def test_rebuild_with_hostname(self): self.set_compute_api_version('2.90') - arglist = [self.server.id, '--hostname', 'new-hostname'] - verifylist = [('server', self.server.id), ('hostname', 'new-hostname')] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + arglist = [ + self.server.id, + '--hostname', + 'new-hostname', + ] + verifylist = [ + ('server', self.server.id), + ('hostname', 'new-hostname'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.image_client.get_image.assert_called_with(self.image.id) - self.server.rebuild.assert_called_with( - self.image, None, hostname='new-hostname' + self.compute_sdk_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.server, + self.image, + admin_password=None, + hostname='new-hostname', ) def test_rebuild_with_hostname_pre_v290(self): @@ -6877,24 +7006,12 @@ def setUp(self): self.image_client.find_image.return_value = self.new_image attrs = { + 'status': 'ACTIVE', 'image': '', - 'networks': {}, - 'adminPass': 'passw0rd', - } - new_server = compute_fakes.create_one_server(attrs=attrs) - - # Fake the server to be rebuilt. The IDs of them should be the same. - attrs['id'] = new_server.id - attrs['status'] = 'ACTIVE' - methods = { - 'rebuild': new_server, } - self.server = compute_fakes.create_one_server( - attrs=attrs, methods=methods - ) - - # Return value for utils.find_resource for server. - self.servers_mock.get.return_value = self.server + 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.cmd = server.RebuildServer(self.app, None) @@ -6912,12 +7029,20 @@ def test_rebuild_with_reimage_boot_volume(self): ('reimage_boot_volume', True), ('image', self.new_image.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.servers_mock.get.assert_called_with(self.server.id) - self.server.rebuild.assert_called_with(self.new_image, None) + self.compute_sdk_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.server, self.new_image, admin_password=None + ) def test_rebuild_with_no_reimage_boot_volume(self): self.set_compute_api_version('2.93') From b9b5e7615aa08402434548926f387f12c02bd4c5 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 8 May 2024 13:12:45 +0100 Subject: [PATCH 149/403] compute: Migrate remaining server actions to SDK Migrate the following commands: - 'server delete' - 'server rescue' - 'server unrescue' - 'server resize' - 'server resize confirm' - 'server resize revert' - 'server migrate confirm' - 'server migrate revert' - 'server remove fixed ip' ('server add fixed ip' was already migrated) - 'server ssh' That leaves a few commands to migrate, all of which are called out with TODOs. Change-Id: Idb769a93609da522c458e719bc69a63ff2ab107b Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 141 ++-- .../tests/unit/compute/v2/test_server.py | 641 ++++++++++-------- 2 files changed, 411 insertions(+), 371 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index aa4accf4e..65f34c186 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -493,16 +493,16 @@ def update_parser_common(self, parser): return parser def take_action_network(self, client, parsed_args): - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute attrs = {} obj = client.find_ip( parsed_args.ip_address, ignore_missing=False, ) - server = utils.find_resource( - compute_client.servers, - parsed_args.server, + + server = compute_client.find_server( + parsed_args.server, ignore_missing=False ) ports = list(client.ports(device_id=server.id)) if not ports: @@ -1062,6 +1062,7 @@ def __call__(self, parser, namespace, values, option_string=None): super().__call__(parser, namespace, values, option_string) +# TODO(stephenfin): Migrate to SDK class CreateServer(command.ShowOne): _description = _("Create a new server") @@ -2137,25 +2138,22 @@ def _show_progress(progress): self.app.stdout.write('\rProgress: %s' % progress) self.app.stdout.flush() - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute for server in parsed_args.server: - server_obj = utils.find_resource( - compute_client.servers, + server_obj = compute_client.find_server( server, - all_tenants=parsed_args.all_projects, + ignore_missing=False, + all_projects=parsed_args.all_projects, ) - if parsed_args.force: - compute_client.servers.force_delete(server_obj.id) - else: - compute_client.servers.delete(server_obj.id) + compute_client.delete_server(server_obj, force=parsed_args.force) if parsed_args.wait: - if not utils.wait_for_delete( - compute_client.servers, - server_obj.id, - callback=_show_progress, - ): + try: + compute_client.wait_for_delete( + server_obj, callback=_show_progress + ) + except sdk_exceptions.ResourceTimeout: msg = _('Error deleting server: %s') % server_obj.id raise exceptions.CommandError(msg) @@ -2476,7 +2474,7 @@ def get_parser(self, prog_name): action='store_true', default=False, help=_( - 'When looking up flavor and image names, look them up' + 'When looking up flavor and image names, look them up ' 'one by one as needed instead of all together (default). ' 'Mutually exclusive with "--no-name-lookup|-n" option.' ), @@ -3695,6 +3693,7 @@ def _show_progress(progress): return zip(*sorted(data.items())) +# TODO(stephenfin): Migrate to SDK class EvacuateServer(command.ShowOne): _description = _( """Evacuate a server to a different host. @@ -3835,13 +3834,14 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute - server = utils.find_resource( - compute_client.servers, parsed_args.server + server = compute_client.find_server( + parsed_args.server, ignore_missing=False + ) + compute_client.remove_fixed_ip_from_server( + server, parsed_args.ip_address ) - - server.remove_fixed_ip(parsed_args.ip_address) class RemoveFloatingIP(network_common.NetworkAndComputeCommand): @@ -4056,34 +4056,36 @@ def get_parser(self, prog_name): '--image', metavar='', help=_( - 'Image (name or ID) to use for the rescue mode.' - ' Defaults to the currently used one.' + 'Image (name or ID) to use for the rescue mode ' + '(defaults to the currently used one)' ), ) parser.add_argument( '--password', metavar='', help=_( - 'Set the password on the rescued instance. ' - 'This option requires cloud support.' + 'Set the password on the rescued instance ' + '(requires cloud support)' ), ) return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute image_client = self.app.client_manager.image - image = None + image_ref = None if parsed_args.image: - image = image_client.find_image( + image_ref = image_client.find_image( parsed_args.image, ignore_missing=False - ) + ).id - utils.find_resource( - compute_client.servers, - parsed_args.server, - ).rescue(image=image, password=parsed_args.password) + server = compute_client.find_server( + parsed_args.server, ignore_missing=False + ) + compute_client.rescue_server( + server, admin_pass=parsed_args.password, image_ref=image_ref + ) class ResizeServer(command.Command): @@ -4099,12 +4101,12 @@ class ResizeServer(command.Command): def get_parser(self, prog_name): parser = super().get_parser(prog_name) - phase_group = parser.add_mutually_exclusive_group() parser.add_argument( 'server', metavar='', help=_('Server (name or ID)'), ) + phase_group = parser.add_mutually_exclusive_group() phase_group.add_argument( '--flavor', metavar='', @@ -4141,16 +4143,11 @@ def _show_progress(progress): self.app.stdout.write('\rProgress: %s' % progress) self.app.stdout.flush() - compute_client = self.app.client_manager.compute - server = utils.find_resource( - compute_client.servers, - parsed_args.server, + compute_client = self.app.client_manager.sdk_connection.compute + server = compute_client.find_server( + parsed_args.server, ignore_missing=False ) if parsed_args.flavor: - flavor = utils.find_resource( - compute_client.flavors, - parsed_args.flavor, - ) if not server.image: self.log.warning( _( @@ -4158,18 +4155,21 @@ def _show_progress(progress): "while booting from a persistent volume." ) ) - compute_client.servers.resize(server, flavor) + flavor = compute_client.find_flavor( + parsed_args.flavor, ignore_missing=False + ) + compute_client.resize_server(server, flavor) if parsed_args.wait: - if utils.wait_for_status( - compute_client.servers.get, + if not utils.wait_for_status( + compute_client.get_server, server.id, - success_status=['active', 'verify_resize'], + success_status=('active', 'verify_resize'), callback=_show_progress, ): - self.app.stdout.write(_('Complete\n')) - else: msg = _('Error resizing server: %s') % server.id raise exceptions.CommandError(msg) + + self.app.stdout.write(_('Complete\n')) elif parsed_args.confirm: self.log.warning( _( @@ -4177,7 +4177,7 @@ def _show_progress(progress): "'openstack server resize confirm' command instead." ) ) - compute_client.servers.confirm_resize(server) + compute_client.confirm_server_resize(server) elif parsed_args.revert: self.log.warning( _( @@ -4185,7 +4185,7 @@ def _show_progress(progress): "'openstack server resize revert' command instead." ) ) - compute_client.servers.revert_resize(server) + compute_client.revert_server_resize(server) class ResizeConfirm(command.Command): @@ -4205,12 +4205,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute - server = utils.find_resource( - compute_client.servers, - parsed_args.server, + compute_client = self.app.client_manager.sdk_connection.compute + server = compute_client.find_server( + parsed_args.server, ignore_missing=False ) - server.confirm_resize() + compute_client.confirm_server_resize(server) # TODO(stephenfin): Remove in OSC 7.0 @@ -4254,12 +4253,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute - server = utils.find_resource( - compute_client.servers, - parsed_args.server, + compute_client = self.app.client_manager.sdk_connection.compute + server = compute_client.find_server( + parsed_args.server, ignore_missing=False ) - server.revert_resize() + compute_client.revert_server_resize(server) # TODO(stephenfin): Remove in OSC 7.0 @@ -4766,11 +4764,10 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute - server = utils.find_resource( - compute_client.servers, - parsed_args.server, + server = compute_client.find_server( + parsed_args.server, ignore_missing=False ) # first, handle the deprecated options @@ -4846,7 +4843,7 @@ def get_parser(self, prog_name): action='store_true', default=boolenv('ALL_PROJECTS'), help=_( - 'Start server(s) in another project by name (admin only)' + 'Start server(s) in another project by name (admin only) ' '(can be specified using the ALL_PROJECTS envvar)' ), ) @@ -4981,11 +4978,11 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute - utils.find_resource( - compute_client.servers, - parsed_args.server, - ).unrescue() + compute_client = self.app.client_manager.sdk_connection.compute + server = compute_client.find_server( + parsed_args.server, ignore_missing=False + ) + compute_client.unrescue_server(server) class UnsetServer(command.Command): diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 9bac34bb9..4eb78bcbd 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -486,24 +486,25 @@ class TestServerAddFloatingIPNetwork( def setUp(self): super().setUp() + self.server = compute_fakes.create_one_sdk_server() + self.compute_sdk_client.find_server.return_value = self.server + self.network_client.update_ip = mock.Mock(return_value=None) # Get the command object to test self.cmd = server.AddFloatingIP(self.app, None) def test_server_add_floating_ip(self): - _server = compute_fakes.create_one_server() - self.servers_mock.get.return_value = _server _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]) arglist = [ - _server.id, + self.server.id, _floating_ip['floating_ip_address'], ] verifylist = [ - ('server', _server.id), + ('server', self.server.id), ('ip_address', _floating_ip['floating_ip_address']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -519,26 +520,24 @@ def test_server_add_floating_ip(self): ignore_missing=False, ) self.network_client.ports.assert_called_once_with( - device_id=_server.id, + device_id=self.server.id, ) self.network_client.update_ip.assert_called_once_with( _floating_ip, **attrs ) def test_server_add_floating_ip_no_ports(self): - server = compute_fakes.create_one_server() floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() - self.servers_mock.get.return_value = server self.network_client.find_ip = mock.Mock(return_value=floating_ip) self.network_client.ports = mock.Mock(return_value=[]) arglist = [ - server.id, + self.server.id, floating_ip['floating_ip_address'], ] verifylist = [ - ('server', server.id), + ('server', self.server.id), ('ip_address', floating_ip['floating_ip_address']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -555,12 +554,10 @@ def test_server_add_floating_ip_no_ports(self): ignore_missing=False, ) self.network_client.ports.assert_called_once_with( - device_id=server.id, + device_id=self.server.id, ) def test_server_add_floating_ip_no_external_gateway(self, success=False): - _server = compute_fakes.create_one_server() - self.servers_mock.get.return_value = _server _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) @@ -575,11 +572,11 @@ def test_server_add_floating_ip_no_external_gateway(self, success=False): side_effect.append(None) self.network_client.update_ip = mock.Mock(side_effect=side_effect) arglist = [ - _server.id, + self.server.id, _floating_ip['floating_ip_address'], ] verifylist = [ - ('server', _server.id), + ('server', self.server.id), ('ip_address', _floating_ip['floating_ip_address']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -602,7 +599,7 @@ def test_server_add_floating_ip_no_external_gateway(self, success=False): ignore_missing=False, ) self.network_client.ports.assert_called_once_with( - device_id=_server.id, + device_id=self.server.id, ) if success: self.assertEqual(2, self.network_client.update_ip.call_count) @@ -617,8 +614,6 @@ def test_server_add_floating_ip_one_external_gateway(self): self.test_server_add_floating_ip_no_external_gateway(success=True) def test_server_add_floating_ip_with_fixed_ip(self): - _server = compute_fakes.create_one_server() - self.servers_mock.get.return_value = _server _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) @@ -628,12 +623,12 @@ def test_server_add_floating_ip_with_fixed_ip(self): arglist = [ '--fixed-ip-address', _port.fixed_ips[0]['ip_address'], - _server.id, + self.server.id, _floating_ip['floating_ip_address'], ] verifylist = [ ('fixed_ip_address', _port.fixed_ips[0]['ip_address']), - ('server', _server.id), + ('server', self.server.id), ('ip_address', _floating_ip['floating_ip_address']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -652,15 +647,13 @@ def test_server_add_floating_ip_with_fixed_ip(self): ignore_missing=False, ) self.network_client.ports.assert_called_once_with( - device_id=_server.id, + device_id=self.server.id, ) self.network_client.update_ip.assert_called_once_with( _floating_ip, **attrs ) def test_server_add_floating_ip_with_fixed_ip_no_port_found(self): - _server = compute_fakes.create_one_server() - self.servers_mock.get.return_value = _server _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) @@ -671,12 +664,12 @@ def test_server_add_floating_ip_with_fixed_ip_no_port_found(self): arglist = [ '--fixed-ip-address', nonexistent_ip, - _server.id, + self.server.id, _floating_ip['floating_ip_address'], ] verifylist = [ ('fixed_ip_address', nonexistent_ip), - ('server', _server.id), + ('server', self.server.id), ('ip_address', _floating_ip['floating_ip_address']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -690,7 +683,7 @@ def test_server_add_floating_ip_with_fixed_ip_no_port_found(self): ignore_missing=False, ) self.network_client.ports.assert_called_once_with( - device_id=_server.id, + device_id=self.server.id, ) self.network_client.update_ip.assert_not_called() @@ -4489,136 +4482,156 @@ def test_server_create_with_trusted_image_cert_boot_from_volume(self): ) -class TestServerDelete(TestServer): +class TestServerDelete(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.servers_mock.delete.return_value = None - self.servers_mock.force_delete.return_value = None + 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 # Get the command object to test self.cmd = server.DeleteServer(self.app, None) def test_server_delete_no_options(self): - servers = self.setup_servers_mock(count=1) - 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) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.servers_mock.delete.assert_called_with(servers[0].id) - self.servers_mock.force_delete.assert_not_called() + self.compute_sdk_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.server, force=False + ) self.assertIsNone(result) def test_server_delete_with_force(self): - servers = self.setup_servers_mock(count=1) - arglist = [ - servers[0].id, + self.server.id, '--force', ] verifylist = [ - ('server', [servers[0].id]), + ('server', [self.server.id]), ('force', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - self.servers_mock.force_delete.assert_called_with(servers[0].id) - self.servers_mock.delete.assert_not_called() + self.compute_sdk_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.server, force=True + ) self.assertIsNone(result) def test_server_delete_multi_servers(self): - servers = self.setup_servers_mock(count=3) + 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 arglist = [] verifylist = [] - for s in servers: arglist.append(s.id) verifylist = [ ('server', arglist), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - calls = [] - for s in servers: - calls.append(mock.call(s.id)) - self.servers_mock.delete.assert_has_calls(calls) - self.assertIsNone(result) - - @mock.patch.object(common_utils, 'find_resource') - def test_server_delete_with_all_projects(self, mock_find_resource): - servers = self.setup_servers_mock(count=1) - mock_find_resource.side_effect = compute_fakes.get_servers( - servers, - 0, + self.compute_sdk_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( + [mock.call(s, force=False) for s in servers] ) + self.assertIsNone(result) + def test_server_delete_with_all_projects(self): arglist = [ - servers[0].id, + self.server.id, '--all-projects', ] verifylist = [ - ('server', [servers[0].id]), + ('server', [self.server.id]), + ('all_projects', True), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) - mock_find_resource.assert_called_once_with( - mock.ANY, - servers[0].id, - all_tenants=True, + self.compute_sdk_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.server, force=False + ) + self.assertIsNone(result) - @mock.patch.object(common_utils, 'wait_for_delete', return_value=True) - def test_server_delete_wait_ok(self, mock_wait_for_delete): - servers = self.setup_servers_mock(count=1) - - arglist = [servers[0].id, '--wait'] + def test_server_delete_wait_ok(self): + arglist = [ + self.server.id, + '--wait', + ] verifylist = [ - ('server', [servers[0].id]), + ('server', [self.server.id]), + ('wait', True), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.servers_mock.delete.assert_called_with(servers[0].id) - mock_wait_for_delete.assert_called_once_with( - self.servers_mock, - servers[0].id, + self.compute_sdk_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.server, force=False + ) + self.compute_sdk_client.wait_for_delete.assert_called_once_with( + self.server, callback=mock.ANY, ) self.assertIsNone(result) - @mock.patch.object(common_utils, 'wait_for_delete', return_value=False) - def test_server_delete_wait_fails(self, mock_wait_for_delete): - servers = self.setup_servers_mock(count=1) + def test_server_delete_wait_fails(self): + self.compute_sdk_client.wait_for_delete.side_effect = ( + sdk_exceptions.ResourceTimeout() + ) - arglist = [servers[0].id, '--wait'] + arglist = [ + self.server.id, + '--wait', + ] verifylist = [ - ('server', [servers[0].id]), + ('server', [self.server.id]), + ('wait', True), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.servers_mock.delete.assert_called_with(servers[0].id) - mock_wait_for_delete.assert_called_once_with( - self.servers_mock, - servers[0].id, + self.compute_sdk_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.server, force=False + ) + self.compute_sdk_client.wait_for_delete.assert_called_once_with( + self.server, callback=mock.ANY, ) @@ -7251,83 +7264,66 @@ def test_evacuate_with_wait_ok(self, mock_wait_for_status): ) -class TestServerRemoveFixedIP(TestServer): +class TestServerRemoveFixedIP(compute_fakes.TestComputev2): def setUp(self): super().setUp() + self.server = compute_fakes.create_one_sdk_server() + # Get the command object to test self.cmd = server.RemoveFixedIP(self.app, None) - # Set method to be tested. - self.methods = { - 'remove_fixed_ip': None, - } - def test_server_remove_fixed_ip(self): - servers = self.setup_servers_mock(count=1) - arglist = [ - servers[0].id, + self.server.id, '1.2.3.4', ] verifylist = [ - ('server', servers[0].id), + ('server', self.server.id), ('ip_address', '1.2.3.4'), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - servers[0].remove_fixed_ip.assert_called_once_with('1.2.3.4') + self.compute_sdk_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.assertIsNone(result) -class TestServerRescue(TestServer): +class TestServerRescue(compute_fakes.TestComputev2): def setUp(self): super().setUp() - # Return value for utils.find_resource for image - self.image = image_fakes.create_one_image() - self.image_client.get_image.return_value = self.image - - new_server = compute_fakes.create_one_server() - attrs = { - 'id': new_server.id, - 'image': { - 'id': self.image.id, - }, - 'networks': {}, - 'adminPass': 'passw0rd', - } - methods = { - 'rescue': new_server, - } - self.server = compute_fakes.create_one_server( - attrs=attrs, - methods=methods, - ) - - # Return value for utils.find_resource for server - self.servers_mock.get.return_value = self.server + self.server = compute_fakes.create_one_sdk_server() + self.compute_sdk_client.find_server.return_value = self.server self.cmd = server.RescueServer(self.app, None) - def test_rescue_with_current_image(self): + def test_rescue(self): arglist = [ self.server.id, ] verifylist = [ ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # Get the command object to test - self.cmd.take_action(parsed_args) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.rescue.assert_called_with(image=None, password=None) + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False + ) + self.compute_sdk_client.rescue_server.assert_called_once_with( + self.server, admin_pass=None, image_ref=None + ) + self.assertIsNone(result) - def test_rescue_with_new_image(self): + def test_rescue_with_image(self): new_image = image_fakes.create_one_image() self.image_client.find_image.return_value = new_image arglist = [ @@ -7339,18 +7335,22 @@ def test_rescue_with_new_image(self): ('image', new_image.id), ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # Get the command object to test - self.cmd.take_action(parsed_args) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) self.image_client.find_image.assert_called_with( new_image.id, ignore_missing=False ) - self.server.rescue.assert_called_with(image=new_image, password=None) + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False + ) + self.compute_sdk_client.rescue_server.assert_called_once_with( + self.server, admin_pass=None, image_ref=new_image.id + ) + self.assertIsNone(result) - def test_rescue_with_current_image_and_password(self): + def test_rescue_with_password(self): password = 'password-xxx' arglist = [ '--password', @@ -7361,13 +7361,17 @@ def test_rescue_with_current_image_and_password(self): ('password', password), ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # Get the command object to test - self.cmd.take_action(parsed_args) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.rescue.assert_called_with(image=None, password=password) + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False + ) + self.compute_sdk_client.rescue_server.assert_called_once_with( + self.server, admin_pass=password, image_ref=None + ) + self.assertIsNone(result) @mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_remove') @@ -7411,15 +7415,14 @@ def setUp(self): self.cmd = server.RemoveFloatingIP(self.app, None) def test_server_remove_floating_ip_default(self): - _server = compute_fakes.create_one_server() _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip() self.network_client.find_ip = mock.Mock(return_value=_floating_ip) arglist = [ - _server.id, + 'fake_server', _floating_ip['ip'], ] verifylist = [ - ('server', _server.id), + ('server', 'fake_server'), ('ip_address', _floating_ip['ip']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -7608,22 +7611,17 @@ def test_server_remove_security_group(self): self.assertIsNone(result) -class TestServerResize(TestServer): +class TestServerResize(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.server = compute_fakes.create_one_server() - - # This is the return value for utils.find_resource() - self.servers_mock.get.return_value = self.server - - self.servers_mock.resize.return_value = None - self.servers_mock.confirm_resize.return_value = None - self.servers_mock.revert_resize.return_value = None - - # This is the return value for utils.find_resource() - self.flavors_get_return_value = compute_fakes.create_one_flavor() - self.flavors_mock.get.return_value = self.flavors_get_return_value + self.server = compute_fakes.create_one_sdk_server() + self.compute_sdk_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 # Get the command object to test self.cmd = server.ResizeServer(self.app, None) @@ -7637,43 +7635,44 @@ def test_server_resize_no_options(self): ('revert', False), ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - - self.assertNotCalled(self.servers_mock.resize) - self.assertNotCalled(self.servers_mock.confirm_resize) - self.assertNotCalled(self.servers_mock.revert_resize) + self.compute_sdk_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.assertIsNone(result) def test_server_resize(self): arglist = [ '--flavor', - self.flavors_get_return_value.id, + self.flavor.id, self.server.id, ] verifylist = [ - ('flavor', self.flavors_get_return_value.id), + ('flavor', self.flavor.id), ('confirm', False), ('revert', False), ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.flavors_mock.get.assert_called_with( - self.flavors_get_return_value.id, + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False ) - self.servers_mock.resize.assert_called_with( - self.server, - self.flavors_get_return_value, + self.compute_sdk_client.find_flavor.assert_called_once_with( + self.flavor.id, ignore_missing=False + ) + self.compute_sdk_client.resize_server.assert_called_once_with( + self.server, self.flavor ) - self.assertNotCalled(self.servers_mock.confirm_resize) - self.assertNotCalled(self.servers_mock.revert_resize) + self.compute_sdk_client.confirm_server_resize.assert_not_called() + self.compute_sdk_client.revert_server_resize.assert_not_called() self.assertIsNone(result) def test_server_resize_confirm(self): @@ -7686,16 +7685,22 @@ def test_server_resize_confirm(self): ('revert', False), ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.assertNotCalled(self.servers_mock.resize) - self.servers_mock.confirm_resize.assert_called_with(self.server) - self.assertNotCalled(self.servers_mock.revert_resize) + self.compute_sdk_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.server + ) + self.compute_sdk_client.revert_server_resize.assert_not_called() self.assertIsNone(result) + # A warning should have been logged for using --confirm. mock_warning.assert_called_once() self.assertIn( @@ -7713,15 +7718,20 @@ def test_server_resize_revert(self): ('revert', True), ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch.object(self.cmd.log, 'warning') as mock_warning: result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.assertNotCalled(self.servers_mock.resize) - self.assertNotCalled(self.servers_mock.confirm_resize) - self.servers_mock.revert_resize.assert_called_with(self.server) + self.compute_sdk_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.server + ) self.assertIsNone(result) # A warning should have been logged for using --revert. mock_warning.assert_called_once() @@ -7734,92 +7744,88 @@ def test_server_resize_revert(self): def test_server_resize_with_wait_ok(self, mock_wait_for_status): arglist = [ '--flavor', - self.flavors_get_return_value.id, + self.flavor.id, '--wait', self.server.id, ] - verifylist = [ - ('flavor', self.flavors_get_return_value.id), + ('flavor', self.flavor.id), ('confirm', False), ('revert', False), ('wait', True), ('server', self.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.servers_mock.get.assert_called_with( - self.server.id, + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False ) - - kwargs = dict( - success_status=['active', 'verify_resize'], + self.compute_sdk_client.find_flavor.assert_called_once_with( + self.flavor.id, ignore_missing=False ) - - mock_wait_for_status.assert_called_once_with( - self.servers_mock.get, self.server.id, callback=mock.ANY, **kwargs + self.compute_sdk_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.servers_mock.resize.assert_called_with( - self.server, self.flavors_get_return_value + mock_wait_for_status.assert_called_once_with( + self.compute_sdk_client.get_server, + self.server.id, + success_status=('active', 'verify_resize'), + callback=mock.ANY, ) - self.assertNotCalled(self.servers_mock.confirm_resize) - self.assertNotCalled(self.servers_mock.revert_resize) @mock.patch.object(common_utils, 'wait_for_status', return_value=False) def test_server_resize_with_wait_fails(self, mock_wait_for_status): arglist = [ '--flavor', - self.flavors_get_return_value.id, + self.flavor.id, '--wait', self.server.id, ] - verifylist = [ - ('flavor', self.flavors_get_return_value.id), + ('flavor', self.flavor.id), ('confirm', False), ('revert', False), ('wait', True), ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.servers_mock.get.assert_called_with( - self.server.id, + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False ) - - kwargs = dict( - success_status=['active', 'verify_resize'], + self.compute_sdk_client.find_flavor.assert_called_once_with( + self.flavor.id, ignore_missing=False ) - - mock_wait_for_status.assert_called_once_with( - self.servers_mock.get, self.server.id, callback=mock.ANY, **kwargs + self.compute_sdk_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.servers_mock.resize.assert_called_with( - self.server, self.flavors_get_return_value + mock_wait_for_status.assert_called_once_with( + self.compute_sdk_client.get_server, + self.server.id, + success_status=('active', 'verify_resize'), + callback=mock.ANY, ) -class TestServerResizeConfirm(TestServer): +class TestServerResizeConfirm(compute_fakes.TestComputev2): def setUp(self): super().setUp() - methods = { - 'confirm_resize': None, - } - self.server = compute_fakes.create_one_server(methods=methods) - - # This is the return value for utils.find_resource() - self.servers_mock.get.return_value = self.server - - self.servers_mock.confirm_resize.return_value = None + 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 # Get the command object to test self.cmd = server.ResizeConfirm(self.app, None) @@ -7831,28 +7837,27 @@ def test_resize_confirm(self): verifylist = [ ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.confirm_resize.assert_called_with() + self.compute_sdk_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.server + ) + self.assertIsNone(result) # TODO(stephenfin): Remove in OSC 7.0 -class TestServerMigrateConfirm(TestServer): +class TestServerMigrateConfirm(compute_fakes.TestComputev2): def setUp(self): super().setUp() - methods = { - 'confirm_resize': None, - } - self.server = compute_fakes.create_one_server(methods=methods) - - # This is the return value for utils.find_resource() - self.servers_mock.get.return_value = self.server - - self.servers_mock.confirm_resize.return_value = None + 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 # Get the command object to test self.cmd = server.MigrateConfirm(self.app, None) @@ -7867,10 +7872,15 @@ def test_migrate_confirm(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch.object(self.cmd.log, 'warning') as mock_warning: - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.confirm_resize.assert_called_with() + self.compute_sdk_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.server + ) + self.assertIsNone(result) mock_warning.assert_called_once() self.assertIn( @@ -7879,19 +7889,13 @@ def test_migrate_confirm(self): ) -class TestServerConfirmMigration(TestServerResizeConfirm): +class TestServerConfirmMigration(compute_fakes.TestComputev2): def setUp(self): super().setUp() - methods = { - 'confirm_resize': None, - } - self.server = compute_fakes.create_one_server(methods=methods) - - # This is the return value for utils.find_resource() - self.servers_mock.get.return_value = self.server - - self.servers_mock.confirm_resize.return_value = None + 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 # Get the command object to test self.cmd = server.ConfirmMigration(self.app, None) @@ -7903,27 +7907,26 @@ def test_migration_confirm(self): verifylist = [ ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.confirm_resize.assert_called_with() + self.compute_sdk_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.server + ) + self.assertIsNone(result) -class TestServerResizeRevert(TestServer): +class TestServerResizeRevert(compute_fakes.TestComputev2): def setUp(self): super().setUp() - methods = { - 'revert_resize': None, - } - self.server = compute_fakes.create_one_server(methods=methods) - - # This is the return value for utils.find_resource() - self.servers_mock.get.return_value = self.server - - self.servers_mock.revert_resize.return_value = None + 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 # Get the command object to test self.cmd = server.ResizeRevert(self.app, None) @@ -7935,28 +7938,27 @@ def test_resize_revert(self): verifylist = [ ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.revert_resize.assert_called_with() + self.compute_sdk_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.server + ) + self.assertIsNone(result) # TODO(stephenfin): Remove in OSC 7.0 -class TestServerMigrateRevert(TestServer): +class TestServerMigrateRevert(compute_fakes.TestComputev2): def setUp(self): super().setUp() - methods = { - 'revert_resize': None, - } - self.server = compute_fakes.create_one_server(methods=methods) - - # This is the return value for utils.find_resource() - self.servers_mock.get.return_value = self.server - - self.servers_mock.revert_resize.return_value = None + 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 # Get the command object to test self.cmd = server.MigrateRevert(self.app, None) @@ -7968,13 +7970,18 @@ def test_migrate_revert(self): verifylist = [ ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch.object(self.cmd.log, 'warning') as mock_warning: - self.cmd.take_action(parsed_args) + result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.revert_resize.assert_called_with() + self.compute_sdk_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.server + ) + self.assertIsNone(result) mock_warning.assert_called_once() self.assertIn( @@ -7983,19 +7990,13 @@ def test_migrate_revert(self): ) -class TestServerRevertMigration(TestServer): +class TestServerRevertMigration(compute_fakes.TestComputev2): def setUp(self): super().setUp() - methods = { - 'revert_resize': None, - } - self.server = compute_fakes.create_one_server(methods=methods) - - # This is the return value for utils.find_resource() - self.servers_mock.get.return_value = self.server - - self.servers_mock.revert_resize.return_value = None + 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 # Get the command object to test self.cmd = server.RevertMigration(self.app, None) @@ -8007,12 +8008,17 @@ def test_migration_revert(self): verifylist = [ ('server', self.server.id), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.cmd.take_action(parsed_args) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.revert_resize.assert_called_with() + self.compute_sdk_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.server + ) + self.assertIsNone(result) class TestServerRestore(TestServer): @@ -8791,11 +8797,10 @@ def setUp(self): ], }, } - self.server = compute_fakes.create_one_server( + self.server = compute_fakes.create_one_sdk_server( attrs=self.attrs, - methods=self.methods, ) - self.servers_mock.get.return_value = self.server + self.compute_sdk_client.find_server.return_value = self.server def test_server_ssh_no_opts(self, mock_exec): arglist = [ @@ -8818,6 +8823,9 @@ 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.server.name, ignore_missing=False + ) self.assertIsNone(result) mock_exec.assert_called_once_with('ssh 192.168.1.30 -l cloud') mock_warning.assert_not_called() @@ -8848,6 +8856,9 @@ 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.server.name, ignore_missing=False + ) self.assertIsNone(result) mock_exec.assert_called_once_with( 'ssh 192.168.1.30 -l username -p 2222' @@ -8879,6 +8890,9 @@ 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.server.name, ignore_missing=False + ) self.assertIsNone(result) mock_exec.assert_called_once_with( 'ssh 192.168.1.30 -p 2222 -l username' @@ -9002,6 +9016,35 @@ def test_server_unpause_multi_servers(self): self.run_method_with_sdk_servers('unpause_server', 3) +class TestServerUnrescue(compute_fakes.TestComputev2): + def setUp(self): + super().setUp() + + self.server = compute_fakes.create_one_sdk_server() + self.compute_sdk_client.find_server.return_value = self.server + + self.cmd = server.UnrescueServer(self.app, None) + + def test_rescue(self): + arglist = [ + self.server.id, + ] + verifylist = [ + ('server', self.server.id), + ] + + 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.server.id, ignore_missing=False + ) + self.compute_sdk_client.unrescue_server.assert_called_once_with( + self.server + ) + self.assertIsNone(result) + + class TestServerUnset(TestServer): def setUp(self): super().setUp() From e6dc0f39c0891ea551867b77663a463b5f76987c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 17 May 2024 13:14:45 +0100 Subject: [PATCH 150/403] compute: Migrate 'server evacuate' to SDK Change-Id: I8ea2da0921c5fd306271f03fa733c0f9787afb82 Signed-off-by: Stephen Finucane Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/918745 --- openstackclient/compute/v2/server.py | 21 ++++------ .../tests/unit/compute/v2/test_server.py | 41 ++++++++----------- ...rver-evacuate-to-sdk-a0415988ef5451b2.yaml | 4 ++ 3 files changed, 28 insertions(+), 38 deletions(-) create mode 100644 releasenotes/notes/migrate-server-evacuate-to-sdk-a0415988ef5451b2.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 65f34c186..ed5fe0fa9 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -3693,7 +3693,6 @@ def _show_progress(progress): return zip(*sorted(data.items())) -# TODO(stephenfin): Migrate to SDK class EvacuateServer(command.ShowOne): _description = _( """Evacuate a server to a different host. @@ -3719,7 +3718,6 @@ def get_parser(self, prog_name): metavar='', help=_('Server (name or ID)'), ) - parser.add_argument( '--wait', action='store_true', @@ -3766,11 +3764,11 @@ def _show_progress(progress): self.app.stdout.write('\rProgress: %s' % progress) self.app.stdout.flush() - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute image_client = self.app.client_manager.image if parsed_args.host: - if compute_client.api_version < api_versions.APIVersion('2.29'): + if not sdk_utils.supports_microversion(compute_client, '2.29'): msg = _( '--os-compute-api-version 2.29 or later is required ' 'to specify a preferred host.' @@ -3778,7 +3776,7 @@ def _show_progress(progress): raise exceptions.CommandError(msg) if parsed_args.shared_storage: - if compute_client.api_version > api_versions.APIVersion('2.13'): + if sdk_utils.supports_microversion(compute_client, '2.14'): msg = _( '--os-compute-api-version 2.13 or earlier is required ' 'to specify shared-storage.' @@ -3790,18 +3788,17 @@ def _show_progress(progress): 'password': parsed_args.password, } - if compute_client.api_version <= api_versions.APIVersion('2.13'): + if not sdk_utils.supports_microversion(compute_client, '2.14'): kwargs['on_shared_storage'] = parsed_args.shared_storage - server = utils.find_resource( - compute_client.servers, parsed_args.server + server = compute_client.find_server( + parsed_args.server, ignore_missing=False ) - - server.evacuate(**kwargs) + compute_client.evacuate_server(server, **kwargs) if parsed_args.wait: if utils.wait_for_status( - compute_client.servers.get, + compute_client.get_server, server.id, callback=_show_progress, ): @@ -3810,8 +3807,6 @@ def _show_progress(progress): msg = _('Error evacuating server: %s') % server.id raise exceptions.CommandError(msg) - # TODO(stephenfin): Remove when the whole command is using SDK - compute_client = self.app.client_manager.sdk_connection.compute data = _prep_server_detail(compute_client, image_client, server) return zip(*sorted(data.items())) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 4eb78bcbd..8dddc3255 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -7105,46 +7105,37 @@ class TestServerEvacuate(TestServer): def setUp(self): super().setUp() - # Return value for utils.find_resource for image self.image = image_fakes.create_one_image() self.image_client.get_image.return_value = self.image - # Fake the rebuilt new server. attrs = { - 'image': {'id': self.image.id}, + 'image': self.image, 'networks': {}, 'adminPass': 'passw0rd', } - new_server = compute_fakes.create_one_server(attrs=attrs) - - # Fake the server to be rebuilt. The IDs of them should be the same. - attrs['id'] = new_server.id - methods = { - 'evacuate': new_server, - } - self.server = compute_fakes.create_one_server( - attrs=attrs, methods=methods - ) + self.server = compute_fakes.create_one_sdk_server(attrs=attrs) + attrs['id'] = self.server.id + self.new_server = compute_fakes.create_one_sdk_server(attrs=attrs) # Return value for utils.find_resource for server. - self.servers_mock.get.return_value = self.server - - # We need an SDK-style server object also for the get_server call in - # _prep_server_detail - # TODO(stephenfin): Remove when the whole command is using SDK - self.sdk_server = compute_fakes.create_one_sdk_server(attrs=attrs) - self.compute_sdk_client.get_server.return_value = self.sdk_server + self.compute_sdk_client.find_server.return_value = self.server + self.compute_sdk_client.get_server.return_value = self.server self.cmd = server.EvacuateServer(self.app, None) def _test_evacuate(self, args, verify_args, evac_args): parsed_args = self.check_parser(self.cmd, args, verify_args) - - # Get the command object to test self.cmd.take_action(parsed_args) - self.servers_mock.get.assert_called_with(self.server.id) - self.server.evacuate.assert_called_with(**evac_args) + self.compute_sdk_client.find_server.assert_called_once_with( + self.server.id, ignore_missing=False + ) + self.compute_sdk_client.evacuate_server.assert_called_once_with( + self.server, **evac_args + ) + self.compute_sdk_client.get_server.assert_called_once_with( + self.server.id + ) def test_evacuate(self): args = [ @@ -7258,7 +7249,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.servers_mock.get, + self.compute_sdk_client.get_server, self.server.id, callback=mock.ANY, ) diff --git a/releasenotes/notes/migrate-server-evacuate-to-sdk-a0415988ef5451b2.yaml b/releasenotes/notes/migrate-server-evacuate-to-sdk-a0415988ef5451b2.yaml new file mode 100644 index 000000000..0b334b4f9 --- /dev/null +++ b/releasenotes/notes/migrate-server-evacuate-to-sdk-a0415988ef5451b2.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + The ``server evacuate`` command has been migrated to SDK. From d22f26446ad49b6eec099d57298bf9af42ea161d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 9 Jul 2024 14:23:31 +0100 Subject: [PATCH 151/403] compute: Prevent use of conflicting v*-fixed-ip for 'server create --nic' Currently this check is handled by novaclient. In the future, we won't have that so we need to do it ourselves. Do so now, fixing a typo along the way and adding tests to prevent regressions. Change-Id: Iaa9c087d846390b6a4f95ed3fa121dd8dc640903 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 13 +++++-- .../tests/unit/compute/v2/test_server.py | 39 +++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index ed5fe0fa9..ceac614d6 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -931,10 +931,17 @@ def __call__(self, parser, namespace, values, option_string=None): if info['net-id'] and info['port-id']: msg = _( - 'Invalid argument %s; either network or port should be ' - 'specified but not both' + "Invalid argument %s; either 'network' or 'port' should be " + "specified but not both" ) - raise argparse.ArgumenteError(self, msg % values) + raise argparse.ArgumentError(self, msg % values) + + if info['v4-fixed-ip'] and info['v6-fixed-ip']: + msg = _( + "Invalid argument %s; either 'v4-fixed-ip' or 'v6-fixed-ip' " + "should be specified but not both" + ) + raise argparse.ArgumentError(self, msg % values) getattr(namespace, self.dest).append(info) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 8dddc3255..858e4eca6 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -2308,7 +2308,46 @@ def test_server_create_with_only_network_key(self): arglist, [], ) + self.assertNotCalled(self.servers_mock.create) + def test_server_create_with_conflicting_net_port_filters(self): + arglist = [ + '--image', + 'image1', + '--flavor', + 'flavor1', + '--nic', + 'net-id=abc,port-id=xyz', + self.new_server.name, + ] + exc = self.assertRaises( + test_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + [], + ) + self.assertIn("either 'network' or 'port'", str(exc)) + self.assertNotCalled(self.servers_mock.create) + + def test_server_create_with_conflicting_fixed_ip_filters(self): + arglist = [ + '--image', + 'image1', + '--flavor', + 'flavor1', + '--nic', + 'net-id=abc,v4-fixed-ip=1.2.3.4,v6-fixed-ip=2001:db8:abcd', + self.new_server.name, + ] + exc = self.assertRaises( + test_utils.ParserException, + self.check_parser, + self.cmd, + arglist, + [], + ) + self.assertIn("either 'v4-fixed-ip' or 'v6-fixed-ip'", str(exc)) self.assertNotCalled(self.servers_mock.create) @mock.patch.object(common_utils, 'wait_for_status', return_value=True) From 717f242881ff58f1ddec994ffc68b82103ca7dab Mon Sep 17 00:00:00 2001 From: Antonia Gaete Date: Tue, 23 Jan 2024 18:35:49 +0000 Subject: [PATCH 152/403] identity: Migrate 'service' commands to SDK Change-Id: I37d07a6c5cdc98680b8d65d596521cad2b049500 --- openstackclient/identity/common.py | 34 ++++ openstackclient/identity/v3/service.py | 70 +++++--- .../tests/functional/identity/v3/common.py | 4 +- .../functional/identity/v3/test_limit.py | 4 +- .../identity/v3/test_registered_limit.py | 2 +- .../functional/identity/v3/test_service.py | 6 +- .../tests/unit/identity/v3/test_service.py | 150 +++++++++--------- ...grate-service-to-sdk-6ff62ebf7e41db7c.yaml | 10 ++ 8 files changed, 179 insertions(+), 101 deletions(-) create mode 100644 releasenotes/notes/migrate-service-to-sdk-6ff62ebf7e41db7c.yaml diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py index a9d8afa6f..4e5f083de 100644 --- a/openstackclient/identity/common.py +++ b/openstackclient/identity/common.py @@ -20,6 +20,7 @@ from keystoneclient.v3 import groups from keystoneclient.v3 import projects from keystoneclient.v3 import users +from openstack import exceptions as sdk_exceptions from osc_lib import exceptions from osc_lib import utils @@ -73,6 +74,39 @@ def find_service(identity_client, name_type_or_id): raise exceptions.CommandError(msg % name_type_or_id) +def find_service_sdk(identity_client, name_type_or_id): + """Find a service by id, name or type.""" + + try: + # search for service name or ID + return identity_client.find_service( + name_type_or_id, ignore_missing=False + ) + except sdk_exceptions.ResourceNotFound: + pass + except sdk_exceptions.DuplicateResource as e: + 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) + + return result + + def get_resource(manager, name_type_or_id): # NOTE (vishakha): Due to bug #1799153 and for any another related case # where GET resource API does not support the filter by name, diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index 42ee1e474..b7d4befc9 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -28,6 +28,31 @@ LOG = logging.getLogger(__name__) +def _format_service(service): + columns = ( + 'id', + 'name', + 'type', + 'is_enabled', + 'description', + ) + column_headers = ( + 'ID', + 'Name', + 'Type', + 'Enabled', + 'Description', + ) + + return ( + column_headers, + utils.get_item_properties( + service, + columns, + ), + ) + + class CreateService(command.ShowOne): _description = _("Create new service") @@ -66,17 +91,16 @@ 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 - service = identity_client.services.create( + service = identity_client.create_service( name=parsed_args.name, type=parsed_args.type, description=parsed_args.description, - enabled=parsed_args.is_enabled, + is_enabled=parsed_args.is_enabled, ) - service._info.pop('links') - return zip(*sorted(service._info.items())) + return _format_service(service) class DeleteService(command.Command): @@ -93,12 +117,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.service: try: - service = common.find_service(identity_client, i) - identity_client.services.delete(service.id) + service = common.find_service_sdk(identity_client, i) + identity_client.delete_service(service.id) except Exception as e: result += 1 LOG.error( @@ -131,13 +155,19 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): + identity_client = self.app.client_manager.sdk_connection.identity + if parsed_args.long: - columns = ('ID', 'Name', 'Type', 'Description', 'Enabled') + columns = ('id', 'name', 'type', 'description', 'is_enabled') + column_headers = ('ID', 'Name', 'Type', 'Description', 'Enabled') else: - columns = ('ID', 'Name', 'Type') - data = self.app.client_manager.identity.services.list() + columns = ('id', 'name', 'type') + column_headers = ('ID', 'Name', 'Type') + + data = identity_client.services() + return ( - columns, + column_headers, (utils.get_item_properties(s, columns) for s in data), ) @@ -185,9 +215,9 @@ 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 - service = common.find_service(identity_client, parsed_args.service) + service = common.find_service_sdk(identity_client, parsed_args.service) kwargs = {} if parsed_args.type: kwargs['type'] = parsed_args.type @@ -195,10 +225,9 @@ 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['enabled'] = parsed_args.is_enabled + kwargs['is_enabled'] = parsed_args.is_enabled - identity_client.services.update(service.id, **kwargs) + identity_client.update_service(service.id, **kwargs) class ShowService(command.ShowOne): @@ -214,9 +243,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 - service = common.find_service(identity_client, parsed_args.service) + service = common.find_service_sdk(identity_client, parsed_args.service) - service._info.pop('links') - return zip(*sorted(service._info.items())) + return _format_service(service) diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py index 8607c0573..7ae56b8de 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', @@ -367,7 +367,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 32c65107d..d096762d2 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 6e3c30aeb..ba7f8cbd4 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 98fa349d1..76009bf48 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 57de1a1f7..d44712124 100644 --- a/openstackclient/tests/unit/identity/v3/test_service.py +++ b/openstackclient/tests/unit/identity/v3/test_service.py @@ -13,43 +13,37 @@ # under the License. # -from keystoneclient import exceptions as identity_exc +from openstack import exceptions as sdk_exceptions +from openstack.identity.v3 import service as _service +from openstack.test import fakes as sdk_fakes from osc_lib import exceptions from openstackclient.identity.v3 import service from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestService(identity_fakes.TestIdentityv3): - def setUp(self): - super().setUp() - - # Get a shortcut to the ServiceManager Mock - self.services_mock = self.identity_client.services - self.services_mock.reset_mock() - - -class TestServiceCreate(TestService): +class TestServiceCreate(identity_fakes.TestIdentityv3): columns = ( - 'description', - 'enabled', - 'id', - 'name', - 'type', + 'ID', + 'Name', + 'Type', + 'Enabled', + 'Description', ) def setUp(self): super().setUp() - self.service = identity_fakes.FakeService.create_one_service() + self.service = sdk_fakes.generate_fake_resource(_service.Service) + self.datalist = ( - self.service.description, - True, self.service.id, self.service.name, self.service.type, + True, + self.service.description, ) - self.services_mock.create.return_value = self.service + self.identity_sdk_client.create_service.return_value = self.service # Get the command object to test self.cmd = service.CreateService(self.app, None) @@ -73,12 +67,11 @@ def test_service_create_name(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # ServiceManager.create(name=, type=, enabled=, **kwargs) - self.services_mock.create.assert_called_with( + self.identity_sdk_client.create_service.assert_called_with( name=self.service.name, type=self.service.type, description=None, - enabled=True, + is_enabled=True, ) self.assertEqual(self.columns, columns) @@ -103,12 +96,11 @@ def test_service_create_description(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # ServiceManager.create(name=, type=, enabled=, **kwargs) - self.services_mock.create.assert_called_with( + self.identity_sdk_client.create_service.assert_called_with( name=None, type=self.service.type, description=self.service.description, - enabled=True, + is_enabled=True, ) self.assertEqual(self.columns, columns) @@ -132,12 +124,11 @@ def test_service_create_enable(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # ServiceManager.create(name=, type=, enabled=, **kwargs) - self.services_mock.create.assert_called_with( + self.identity_sdk_client.create_service.assert_called_with( name=None, type=self.service.type, description=None, - enabled=True, + is_enabled=True, ) self.assertEqual(self.columns, columns) @@ -161,27 +152,28 @@ def test_service_create_disable(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # ServiceManager.create(name=, type=, enabled=, **kwargs) - self.services_mock.create.assert_called_with( + self.identity_sdk_client.create_service.assert_called_with( name=None, type=self.service.type, description=None, - enabled=False, + is_enabled=False, ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) -class TestServiceDelete(TestService): - service = identity_fakes.FakeService.create_one_service() +class TestServiceDelete(identity_fakes.TestIdentityv3): + service = sdk_fakes.generate_fake_resource(_service.Service) def setUp(self): super().setUp() - self.services_mock.get.side_effect = identity_exc.NotFound(None) - self.services_mock.find.return_value = self.service - self.services_mock.delete.return_value = None + self.identity_sdk_client.get_service.side_effect = ( + sdk_exceptions.ResourceNotFound + ) + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.delete_service.return_value = None # Get the command object to test self.cmd = service.DeleteService(self.app, None) @@ -197,19 +189,19 @@ def test_service_delete_no_options(self): result = self.cmd.take_action(parsed_args) - self.services_mock.delete.assert_called_with( + self.identity_sdk_client.delete_service.assert_called_with( self.service.id, ) self.assertIsNone(result) -class TestServiceList(TestService): - service = identity_fakes.FakeService.create_one_service() +class TestServiceList(identity_fakes.TestIdentityv3): + service = sdk_fakes.generate_fake_resource(_service.Service) def setUp(self): super().setUp() - self.services_mock.list.return_value = [self.service] + self.identity_sdk_client.services.return_value = [self.service] # Get the command object to test self.cmd = service.ListService(self.app, None) @@ -224,7 +216,7 @@ def test_service_list_no_options(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.services_mock.list.assert_called_with() + self.identity_sdk_client.services.assert_called_with() collist = ('ID', 'Name', 'Type') self.assertEqual(collist, columns) @@ -251,7 +243,7 @@ def test_service_list_long(self): # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.services_mock.list.assert_called_with() + self.identity_sdk_client.services.assert_called_with() collist = ('ID', 'Name', 'Type', 'Description', 'Enabled') self.assertEqual(collist, columns) @@ -267,15 +259,17 @@ def test_service_list_long(self): self.assertEqual(datalist, tuple(data)) -class TestServiceSet(TestService): - service = identity_fakes.FakeService.create_one_service() +class TestServiceSet(identity_fakes.TestIdentityv3): + service = sdk_fakes.generate_fake_resource(_service.Service) def setUp(self): super().setUp() - self.services_mock.get.side_effect = identity_exc.NotFound(None) - self.services_mock.find.return_value = self.service - self.services_mock.update.return_value = self.service + self.identity_sdk_client.get_service.side_effect = ( + sdk_exceptions.ResourceNotFound + ) + self.identity_sdk_client.find_service.return_value = self.service + self.identity_sdk_client.update_service.return_value = self.service # Get the command object to test self.cmd = service.SetService(self.app, None) @@ -317,9 +311,11 @@ def test_service_set_type(self): # Set expected values kwargs = { 'type': self.service.type, + 'is_enabled': None, } - # ServiceManager.update(service, name=, type=, enabled=, **kwargs) - self.services_mock.update.assert_called_with(self.service.id, **kwargs) + self.identity_sdk_client.update_service.assert_called_with( + self.service.id, **kwargs + ) self.assertIsNone(result) def test_service_set_name(self): @@ -342,9 +338,11 @@ def test_service_set_name(self): # Set expected values kwargs = { 'name': self.service.name, + 'is_enabled': None, } - # ServiceManager.update(service, name=, type=, enabled=, **kwargs) - self.services_mock.update.assert_called_with(self.service.id, **kwargs) + self.identity_sdk_client.update_service.assert_called_with( + self.service.id, **kwargs + ) self.assertIsNone(result) def test_service_set_description(self): @@ -367,9 +365,11 @@ def test_service_set_description(self): # Set expected values kwargs = { 'description': self.service.description, + 'is_enabled': None, } - # ServiceManager.update(service, name=, type=, enabled=, **kwargs) - self.services_mock.update.assert_called_with(self.service.id, **kwargs) + self.identity_sdk_client.update_service.assert_called_with( + self.service.id, **kwargs + ) self.assertIsNone(result) def test_service_set_enable(self): @@ -390,10 +390,11 @@ def test_service_set_enable(self): # Set expected values kwargs = { - 'enabled': True, + 'is_enabled': True, } - # ServiceManager.update(service, name=, type=, enabled=, **kwargs) - self.services_mock.update.assert_called_with(self.service.id, **kwargs) + self.identity_sdk_client.update_service.assert_called_with( + self.service.id, **kwargs + ) self.assertIsNone(result) def test_service_set_disable(self): @@ -414,21 +415,24 @@ def test_service_set_disable(self): # Set expected values kwargs = { - 'enabled': False, + 'is_enabled': False, } - # ServiceManager.update(service, name=, type=, enabled=, **kwargs) - self.services_mock.update.assert_called_with(self.service.id, **kwargs) + self.identity_sdk_client.update_service.assert_called_with( + self.service.id, **kwargs + ) self.assertIsNone(result) -class TestServiceShow(TestService): - service = identity_fakes.FakeService.create_one_service() +class TestServiceShow(identity_fakes.TestIdentityv3): + service = sdk_fakes.generate_fake_resource(_service.Service) def setUp(self): super().setUp() - self.services_mock.get.side_effect = identity_exc.NotFound(None) - self.services_mock.find.return_value = self.service + self.identity_sdk_client.get_service.side_effect = ( + sdk_exceptions.ResourceNotFound + ) + self.identity_sdk_client.find_service.return_value = self.service # Get the command object to test self.cmd = service.ShowService(self.app, None) @@ -447,22 +451,25 @@ def test_service_show(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # ServiceManager.get(id) - self.services_mock.find.assert_called_with(name=self.service.name) + self.identity_sdk_client.find_service.assert_called_with( + self.service.name, ignore_missing=False + ) - collist = ('description', 'enabled', 'id', 'name', 'type') + collist = ('ID', 'Name', 'Type', 'Enabled', 'Description') self.assertEqual(collist, columns) datalist = ( - self.service.description, - True, self.service.id, self.service.name, self.service.type, + True, + self.service.description, ) self.assertEqual(datalist, data) def test_service_show_nounique(self): - self.services_mock.find.side_effect = identity_exc.NoUniqueMatch(None) + self.identity_sdk_client.find_service.side_effect = ( + sdk_exceptions.DuplicateResource(None) + ) arglist = [ 'nounique_service', ] @@ -476,7 +483,6 @@ def test_service_show_nounique(self): self.fail('CommandError should be raised.') except exceptions.CommandError as e: self.assertEqual( - "Multiple service matches found for 'nounique_service'," - " use an ID to be more specific.", + "DuplicateResource", str(e), ) diff --git a/releasenotes/notes/migrate-service-to-sdk-6ff62ebf7e41db7c.yaml b/releasenotes/notes/migrate-service-to-sdk-6ff62ebf7e41db7c.yaml new file mode 100644 index 000000000..90a55fcaa --- /dev/null +++ b/releasenotes/notes/migrate-service-to-sdk-6ff62ebf7e41db7c.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + The following commands have been migrated to SDK: + + - ``service create`` + - ``service delete`` + - ``service set`` + - ``service list`` + - ``service show`` From bef8a7a63098f903bcd2a9d42f2f37458602340f Mon Sep 17 00:00:00 2001 From: Antonia Gaete Date: Wed, 26 Jun 2024 15:54:44 +0000 Subject: [PATCH 153/403] identity: Migrate 'application credential' commands to SDK Change-Id: Iba3fee2672d32266623c6f367beaabe84bd3d24e --- .../identity/v3/application_credential.py | 162 +++++-- .../v3/test_application_credential.py | 14 +- .../v3/test_application_credential.py | 439 +++++++++--------- ...on-credential-to-sdk-c79d8dfc3c8e1d9f.yaml | 9 + 4 files changed, 346 insertions(+), 278 deletions(-) create mode 100644 releasenotes/notes/migrate-application-credential-to-sdk-c79d8dfc3c8e1d9f.yaml diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py index cffab9927..035be2d21 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 uuid from osc_lib.command import command from osc_lib import exceptions @@ -30,6 +31,30 @@ LOG = logging.getLogger(__name__) +# 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. + + :param val: Value to verify + :type val: string + :returns: bool + + .. versionchanged:: 1.1.1 + Support non-lowercase UUIDs. + """ + try: + formatted_value = ( + value.replace('urn:', '') + .replace('uuid:', '') + .strip('{}') + .replace('-', '') + .lower() + ) + return str(uuid.UUID(value)).replace('-', '') == formatted_value + except (TypeError, ValueError, AttributeError): + return False + + class CreateApplicationCredential(command.ShowOne): _description = _("Create new application credential") @@ -105,19 +130,16 @@ 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) role_ids = [] for role in parsed_args.role: - # A user can only create an application credential for themself, - # not for another user even as an admin, and only on the project to - # which they are currently scoped with a subset of the role - # assignments they have on that project. Don't bother trying to - # look up roles via keystone, just introspect the token. - role_id = common._get_token_resource( - identity_client, "roles", role - ) - role_ids.append(role_id) + if is_uuid_like(role): + role_ids.append({'id': role}) + else: + role_ids.append({'name': role}) expires_at = None if parsed_args.expiration: @@ -144,10 +166,10 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) else: - access_rules = None + access_rules = [] - app_cred_manager = identity_client.application_credentials - application_credential = app_cred_manager.create( + application_credential = identity_client.create_application_credential( + user_id, parsed_args.name, roles=role_ids, expires_at=expires_at, @@ -157,14 +179,32 @@ def take_action(self, parsed_args): access_rules=access_rules, ) - application_credential._info.pop('links', None) - # Format roles into something sensible - roles = application_credential._info.pop('roles') - msg = ' '.join(r['name'] for r in roles) - application_credential._info['roles'] = msg - - return zip(*sorted(application_credential._info.items())) + 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, + ) + ), + ) class DeleteApplicationCredential(command.Command): @@ -181,15 +221,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 + 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.application_credential: try: - app_cred = utils.find_resource( - identity_client.application_credentials, ac + app_cred = identity_client.find_application_credential( + user_id, ac + ) + identity_client.delete_application_credential( + user_id, app_cred.id ) - identity_client.application_credentials.delete(app_cred.id) except Exception as e: errors += 1 LOG.error( @@ -223,16 +267,36 @@ 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 - - columns = ('ID', 'Name', 'Project ID', 'Description', 'Expires At') - data = identity_client.application_credentials.list(user=user_id) + 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, ( @@ -241,7 +305,7 @@ def take_action(self, parsed_args): columns, formatters={}, ) - for s in data + for s in data_formatted ), ) @@ -259,17 +323,35 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - app_cred = utils.find_resource( - identity_client.application_credentials, - parsed_args.application_credential, - ) + 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) - app_cred._info.pop('links', None) + app_cred = identity_client.find_application_credential( + user_id, parsed_args.application_credential + ) # Format roles into something sensible - roles = app_cred._info.pop('roles') + roles = app_cred['roles'] msg = ' '.join(r['name'] for r in roles) - app_cred._info['roles'] = msg - - return zip(*sorted(app_cred._info.items())) + 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, + ) + ), + ) diff --git a/openstackclient/tests/functional/identity/v3/test_application_credential.py b/openstackclient/tests/functional/identity/v3/test_application_credential.py index 22f2b90bb..9c8b0462f 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 d9c3531dd..277204dd4 100644 --- a/openstackclient/tests/unit/identity/v3/test_application_credential.py +++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py @@ -13,37 +13,58 @@ # under the License. # -import copy -import json +import datetime from unittest import mock +from unittest.mock import call from osc_lib import exceptions -from osc_lib import utils +from openstack import exceptions as sdk_exceptions +from openstack.identity.v3 import ( + application_credential as _application_credential, +) +from openstack.identity.v3 import role as _role +from openstack.test import fakes as sdk_fakes from openstackclient.identity.v3 import application_credential -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes -class TestApplicationCredential(identity_fakes.TestIdentityv3): +class TestApplicationCredentialCreate(identity_fakes.TestIdentityv3): + columns = ( + 'ID', + 'Name', + 'Description', + 'Project ID', + 'Roles', + 'Unrestricted', + 'Access Rules', + 'Expires At', + 'Secret', + ) + def setUp(self): super().setUp() - identity_manager = self.identity_client - self.app_creds_mock = identity_manager.application_credentials - self.app_creds_mock.reset_mock() - self.roles_mock = identity_manager.roles - self.roles_mock.reset_mock() - + self.roles = sdk_fakes.generate_fake_resource(_role.Role) + self.application_credential = sdk_fakes.generate_fake_resource( + resource_type=_application_credential.ApplicationCredential, + roles=[], + ) -class TestApplicationCredentialCreate(TestApplicationCredential): - def setUp(self): - super().setUp() + self.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.application_credential.secret, + ) - self.roles_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ROLE), - loaded=True, + self.identity_sdk_client.create_application_credential.return_value = ( + self.application_credential ) # Get the command object to test @@ -52,17 +73,14 @@ def setUp(self): ) def test_application_credential_create_basic(self): - self.app_creds_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.APP_CRED_BASIC), - loaded=True, - ) - - name = identity_fakes.app_cred_name + name = self.application_credential.name arglist = [name] - verifylist = [('name', identity_fakes.app_cred_name)] + verifylist = [('name', self.application_credential.name)] 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) + # 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. @@ -70,68 +88,45 @@ def test_application_credential_create_basic(self): # Set expected values kwargs = { - 'secret': None, 'roles': [], 'expires_at': None, 'description': None, + 'secret': None, 'unrestricted': False, - 'access_rules': None, + 'access_rules': [], } - self.app_creds_mock.create.assert_called_with(name, **kwargs) - - collist = ( - 'access_rules', - 'description', - 'expires_at', - 'id', - 'name', - 'project_id', - 'roles', - 'secret', - 'unrestricted', + self.identity_sdk_client.create_application_credential.assert_called_with( + user_id, name, **kwargs ) - self.assertEqual(collist, columns) - datalist = ( - None, - None, - None, - identity_fakes.app_cred_id, - identity_fakes.app_cred_name, - identity_fakes.project_id, - identity_fakes.role_name, - identity_fakes.app_cred_secret, - False, - ) - self.assertEqual(datalist, data) - def test_application_credential_create_with_options(self): - name = identity_fakes.app_cred_name - self.app_creds_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.APP_CRED_OPTIONS), - loaded=True, - ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) + def test_application_credential_create_with_options(self): + name = self.application_credential.name arglist = [ name, '--secret', 'moresecuresecret', '--role', - identity_fakes.role_id, + self.roles.id, '--expiration', - identity_fakes.app_cred_expires_str, + '2024-01-01T00:00:00', '--description', 'credential for testing', ] verifylist = [ - ('name', identity_fakes.app_cred_name), + ('name', self.application_credential.name), ('secret', 'moresecuresecret'), - ('role', [identity_fakes.role_id]), - ('expiration', identity_fakes.app_cred_expires_str), + ('role', [self.roles.id]), + ('expiration', '2024-01-01T00:00:00'), ('description', 'credential for testing'), ] 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) + # 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. @@ -139,172 +134,119 @@ def test_application_credential_create_with_options(self): # Set expected values kwargs = { - 'secret': 'moresecuresecret', - 'roles': [identity_fakes.role_id], - 'expires_at': identity_fakes.app_cred_expires, + 'roles': [{'id': self.roles.id}], + 'expires_at': datetime.datetime(2024, 1, 1, 0, 0), 'description': 'credential for testing', + 'secret': 'moresecuresecret', 'unrestricted': False, - 'access_rules': None, + 'access_rules': [], } - self.app_creds_mock.create.assert_called_with(name, **kwargs) - - collist = ( - 'access_rules', - 'description', - 'expires_at', - 'id', - 'name', - 'project_id', - 'roles', - 'secret', - 'unrestricted', - ) - self.assertEqual(collist, columns) - datalist = ( - None, - identity_fakes.app_cred_description, - identity_fakes.app_cred_expires_str, - identity_fakes.app_cred_id, - identity_fakes.app_cred_name, - identity_fakes.project_id, - identity_fakes.role_name, - identity_fakes.app_cred_secret, - False, + self.identity_sdk_client.create_application_credential.assert_called_with( + user_id, name, **kwargs ) - self.assertEqual(datalist, data) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) def test_application_credential_create_with_access_rules_string(self): - name = identity_fakes.app_cred_name - self.app_creds_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.APP_CRED_ACCESS_RULES), - loaded=True, - ) + name = self.application_credential.name arglist = [ name, '--access-rules', - identity_fakes.app_cred_access_rules, + '[{"path": "/v2.1/servers", "method": "GET", "service": "compute"}]', ] verifylist = [ - ('name', identity_fakes.app_cred_name), - ('access_rules', identity_fakes.app_cred_access_rules), + ('name', self.application_credential.name), + ( + 'access_rules', + '[{"path": "/v2.1/servers", "method": "GET", "service": "compute"}]', + ), ] 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) # Set expected values kwargs = { - 'secret': None, 'roles': [], 'expires_at': None, 'description': None, + 'secret': None, 'unrestricted': False, - 'access_rules': json.loads(identity_fakes.app_cred_access_rules), + 'access_rules': [ + { + "path": "/v2.1/servers", + "method": "GET", + "service": "compute", + } + ], } - self.app_creds_mock.create.assert_called_with(name, **kwargs) - - collist = ( - 'access_rules', - 'description', - 'expires_at', - 'id', - 'name', - 'project_id', - 'roles', - 'secret', - 'unrestricted', - ) - self.assertEqual(collist, columns) - datalist = ( - identity_fakes.app_cred_access_rules, - None, - None, - identity_fakes.app_cred_id, - identity_fakes.app_cred_name, - identity_fakes.project_id, - identity_fakes.role_name, - identity_fakes.app_cred_secret, - False, + self.identity_sdk_client.create_application_credential.assert_called_with( + user_id, name, **kwargs ) - self.assertEqual(datalist, data) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) @mock.patch('openstackclient.identity.v3.application_credential.json.load') @mock.patch('openstackclient.identity.v3.application_credential.open') def test_application_credential_create_with_access_rules_file( self, _, mock_json_load ): - mock_json_load.return_value = identity_fakes.app_cred_access_rules - - name = identity_fakes.app_cred_name - self.app_creds_mock.create.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.APP_CRED_ACCESS_RULES), - loaded=True, - ) + mock_json_load.return_value = '/tmp/access_rules.json' + name = self.application_credential.name arglist = [ name, '--access-rules', - identity_fakes.app_cred_access_rules_path, + '/tmp/access_rules.json', ] verifylist = [ - ('name', identity_fakes.app_cred_name), - ('access_rules', identity_fakes.app_cred_access_rules_path), + ('name', self.application_credential.name), + ('access_rules', '/tmp/access_rules.json'), ] 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) # Set expected values kwargs = { - 'secret': None, 'roles': [], 'expires_at': None, 'description': None, + 'secret': None, 'unrestricted': False, - 'access_rules': identity_fakes.app_cred_access_rules, + 'access_rules': '/tmp/access_rules.json', } - self.app_creds_mock.create.assert_called_with(name, **kwargs) - - collist = ( - 'access_rules', - 'description', - 'expires_at', - 'id', - 'name', - 'project_id', - 'roles', - 'secret', - 'unrestricted', + self.identity_sdk_client.create_application_credential.assert_called_with( + user_id, name, **kwargs ) - self.assertEqual(collist, columns) - datalist = ( - identity_fakes.app_cred_access_rules, - None, - None, - identity_fakes.app_cred_id, - identity_fakes.app_cred_name, - identity_fakes.project_id, - identity_fakes.role_name, - identity_fakes.app_cred_secret, - False, - ) - self.assertEqual(datalist, data) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist, data) -class TestApplicationCredentialDelete(TestApplicationCredential): + +class TestApplicationCredentialDelete(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - # This is the return value for utils.find_resource() - self.app_creds_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.APP_CRED_BASIC), - loaded=True, + self.application_credential = sdk_fakes.generate_fake_resource( + resource_type=_application_credential.ApplicationCredential, + roles=[], + ) + self.identity_sdk_client.find_application_credential.return_value = ( + self.application_credential + ) + self.identity_sdk_client.delete_application_credential.return_value = ( + None ) - self.app_creds_mock.delete.return_value = None # Get the command object to test self.cmd = application_credential.DeleteApplicationCredential( @@ -313,26 +255,31 @@ def setUp(self): def test_application_credential_delete(self): arglist = [ - identity_fakes.app_cred_id, + self.application_credential.id, + ] + verifylist = [ + ('application_credential', [self.application_credential.id]) ] - verifylist = [('application_credential', [identity_fakes.app_cred_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.app_creds_mock.delete.assert_called_with( - identity_fakes.app_cred_id, + self.identity_sdk_client.delete_application_credential.assert_called_with( + user_id, + self.application_credential.id, ) self.assertIsNone(result) - @mock.patch.object(utils, 'find_resource') - def test_delete_multi_app_creds_with_exception(self, find_mock): - find_mock.side_effect = [ - self.app_creds_mock.get.return_value, - exceptions.CommandError, + def test_delete_multi_app_creds_with_exception(self): + self.identity_sdk_client.find_application_credential.side_effect = [ + self.application_credential, + sdk_exceptions.NotFoundException, ] arglist = [ - identity_fakes.app_cred_id, + self.application_credential.id, 'nonexistent_app_cred', ] verifylist = [ @@ -340,6 +287,9 @@ def test_delete_multi_app_creds_with_exception(self, find_mock): ] 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.') @@ -348,27 +298,32 @@ def test_delete_multi_app_creds_with_exception(self, find_mock): '1 of 2 application credentials failed to' ' delete.', str(e) ) - find_mock.assert_any_call( - self.app_creds_mock, identity_fakes.app_cred_id + calls = [] + for a in arglist: + calls.append(call(user_id, a)) + + self.identity_sdk_client.find_application_credential.assert_has_calls( + calls ) - find_mock.assert_any_call(self.app_creds_mock, 'nonexistent_app_cred') - self.assertEqual(2, find_mock.call_count) - self.app_creds_mock.delete.assert_called_once_with( - identity_fakes.app_cred_id + self.assertEqual( + 2, self.identity_sdk_client.find_application_credential.call_count + ) + self.identity_sdk_client.delete_application_credential.assert_called_once_with( + user_id, self.application_credential.id ) -class TestApplicationCredentialList(TestApplicationCredential): +class TestApplicationCredentialList(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.app_creds_mock.list.return_value = [ - fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.APP_CRED_BASIC), - loaded=True, - ), + self.application_credential = sdk_fakes.generate_fake_resource( + resource_type=_application_credential.ApplicationCredential, + roles=[], + ) + self.identity_sdk_client.application_credentials.return_value = [ + self.application_credential ] # Get the command object to test @@ -381,35 +336,54 @@ def test_application_credential_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) + # 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.app_creds_mock.list.assert_called_with(user=None) + self.identity_sdk_client.application_credentials.assert_called_with( + user=user_id + ) - collist = ('ID', 'Name', 'Project ID', 'Description', 'Expires At') + collist = ( + 'ID', + 'Name', + 'Description', + 'Project ID', + 'Roles', + 'Unrestricted', + 'Access Rules', + 'Expires At', + ) self.assertEqual(collist, columns) datalist = ( ( - identity_fakes.app_cred_id, - identity_fakes.app_cred_name, - identity_fakes.project_id, - None, - None, + 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, tuple(data)) -class TestApplicationCredentialShow(TestApplicationCredential): +class TestApplicationCredentialShow(identity_fakes.TestIdentityv3): def setUp(self): super().setUp() - self.app_creds_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.APP_CRED_BASIC), - loaded=True, + self.application_credential = sdk_fakes.generate_fake_resource( + resource_type=_application_credential.ApplicationCredential, + roles=[], + ) + self.identity_sdk_client.find_application_credential.return_value = ( + self.application_credential ) # Get the command object to test @@ -419,41 +393,44 @@ def setUp(self): def test_application_credential_show(self): arglist = [ - identity_fakes.app_cred_id, + self.application_credential.id, ] verifylist = [ - ('application_credential', identity_fakes.app_cred_id), + ('application_credential', self.application_credential.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) + # 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.app_creds_mock.get.assert_called_with(identity_fakes.app_cred_id) + self.identity_sdk_client.find_application_credential.assert_called_with( + user_id, self.application_credential.id + ) collist = ( - 'access_rules', - 'description', - 'expires_at', - 'id', - 'name', - 'project_id', - 'roles', - 'secret', - 'unrestricted', + 'ID', + 'Name', + 'Description', + 'Project ID', + 'Roles', + 'Unrestricted', + 'Access Rules', + 'Expires At', ) self.assertEqual(collist, columns) datalist = ( - None, - None, - None, - identity_fakes.app_cred_id, - identity_fakes.app_cred_name, - identity_fakes.project_id, - identity_fakes.role_name, - identity_fakes.app_cred_secret, - False, + 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) diff --git a/releasenotes/notes/migrate-application-credential-to-sdk-c79d8dfc3c8e1d9f.yaml b/releasenotes/notes/migrate-application-credential-to-sdk-c79d8dfc3c8e1d9f.yaml new file mode 100644 index 000000000..22b7a2971 --- /dev/null +++ b/releasenotes/notes/migrate-application-credential-to-sdk-c79d8dfc3c8e1d9f.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + The following commands have been migrated to SDK: + + - ``application credential create`` + - ``application credential delete`` + - ``application credential list`` + - ``application credential show`` From 30a64579b600362d5915877a8c80da6a24b380e2 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 9 Jul 2024 14:37:07 +0100 Subject: [PATCH 154/403] compute: Migrate 'server create' to SDK The final step. Future changes will clean up the remnants of the novaclient usage. This is a rather large patch, owing to the number of things that novaclient was handling for us which SDK does not, but the combination of unit and functional tests mean we should be handling all of these differences. Change-Id: I623e8c772235438a3d1590e1bbd832748d6e62ea Signed-off-by: Stephen Finucane --- openstackclient/api/compute_v2.py | 37 + openstackclient/compute/v2/server.py | 240 +- .../tests/unit/api/test_compute_v2.py | 99 + .../tests/unit/compute/v2/test_server.py | 2553 ++++++++--------- .../tests/unit/network/v2/fakes.py | 52 +- 5 files changed, 1530 insertions(+), 1451 deletions(-) diff --git a/openstackclient/api/compute_v2.py b/openstackclient/api/compute_v2.py index af0c69e58..4fcf3f134 100644 --- a/openstackclient/api/compute_v2.py +++ b/openstackclient/api/compute_v2.py @@ -621,3 +621,40 @@ def find_security_group(compute_client, name_or_id): raise exceptions.NotFound(f'{name_or_id} not found') return found + + +def find_network(compute_client, name_or_id): + """Find the ID for a given network name or ID + + https://docs.openstack.org/api-ref/compute/#show-network-details + + :param compute_client: A compute client + :param name_or_id: The name or ID of the network to look up + :returns: A network object + :raises exception.NotFound: If a matching network could not be found or + more than one match was found + """ + response = compute_client.get( + f'/os-networks/{name_or_id}', microversion='2.1' + ) + if response.status_code != http.HTTPStatus.NOT_FOUND: + # there might be other, non-404 errors + sdk_exceptions.raise_from_response(response) + return response.json()['network'] + + response = compute_client.get('/os-networks', microversion='2.1') + sdk_exceptions.raise_from_response(response) + found = None + networks = response.json()['networks'] + for network in networks: + if network['label'] == name_or_id: + if found: + raise exceptions.NotFound( + f'multiple matches found for {name_or_id}' + ) + found = network + + if not found: + raise exceptions.NotFound(f'{name_or_id} not found') + + return found diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index ceac614d6..22ab4f11c 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -21,10 +21,10 @@ import json import logging import os +import typing as ty from cliff import columns as cliff_columns import iso8601 -from novaclient import api_versions from openstack import exceptions as sdk_exceptions from openstack import utils as sdk_utils from osc_lib.cli import format_columns @@ -82,7 +82,7 @@ def human_readable(self): def machine_readable(self): return { k: [i['addr'] for i in v if 'addr' in i] - for k, v in self._value.items() + for k, v in (self._value.items() if self._value else []) } @@ -1069,7 +1069,6 @@ def __call__(self, parser, namespace, values, option_string=None): super().__call__(parser, namespace, values, option_string) -# TODO(stephenfin): Migrate to SDK class CreateServer(command.ShowOne): _description = _("Create a new server") @@ -1173,7 +1172,7 @@ def get_parser(self, prog_name): ) parser.add_argument( '--block-device', - metavar='', + metavar='', action=BDMAction, dest='block_devices', default=[], @@ -1507,7 +1506,7 @@ def _show_progress(progress): self.app.stdout.write('\rProgress: %s' % progress) self.app.stdout.flush() - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute volume_client = self.app.client_manager.volume image_client = self.app.client_manager.image @@ -1602,12 +1601,12 @@ def _match_image(image_api, wanted_properties): parsed_args.snapshot, ).id - flavor = utils.find_resource( - compute_client.flavors, parsed_args.flavor + flavor = compute_client.find_flavor( + parsed_args.flavor, ignore_missing=False ) if parsed_args.file: - if compute_client.api_version >= api_versions.APIVersion('2.57'): + if sdk_utils.supports_microversion(compute_client, '2.57'): msg = _( 'Personality files are deprecated and are not supported ' 'for --os-compute-api-version greater than 2.56; use ' @@ -1638,10 +1637,12 @@ def _match_image(image_api, wanted_properties): msg = _("max instances should be > 0") raise exceptions.CommandError(msg) - userdata = None + user_data = None if parsed_args.user_data: try: - userdata = open(parsed_args.user_data) + with open(parsed_args.user_data, 'rb') as fh: + # TODO(stephenfin): SDK should do this for us + user_data = base64.b64encode(fh.read()).decode('utf-8') except OSError as e: msg = _("Can't open '%(data)s': %(exception)s") raise exceptions.CommandError( @@ -1649,7 +1650,7 @@ def _match_image(image_api, wanted_properties): ) if parsed_args.description: - if compute_client.api_version < api_versions.APIVersion("2.19"): + if not sdk_utils.supports_microversion(compute_client, '2.19'): msg = _( '--os-compute-api-version 2.19 or greater is ' 'required to support the --description option' @@ -1657,26 +1658,7 @@ def _match_image(image_api, wanted_properties): raise exceptions.CommandError(msg) block_device_mapping_v2 = [] - if volume: - block_device_mapping_v2 = [ - { - 'uuid': volume, - 'boot_index': 0, - 'source_type': 'volume', - 'destination_type': 'volume', - } - ] - elif snapshot: - block_device_mapping_v2 = [ - { - 'uuid': snapshot, - 'boot_index': 0, - 'source_type': 'snapshot', - 'destination_type': 'volume', - 'delete_on_termination': False, - } - ] - elif parsed_args.boot_from_volume: + if parsed_args.boot_from_volume: # Tell nova to create a root volume from the image provided. if not image: msg = _( @@ -1695,6 +1677,35 @@ def _match_image(image_api, wanted_properties): ] # If booting from volume we do not pass an image to compute. image = None + elif image: + block_device_mapping_v2 = [ + { + 'uuid': image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + } + ] + elif volume: + block_device_mapping_v2 = [ + { + 'uuid': volume, + 'boot_index': 0, + 'source_type': 'volume', + 'destination_type': 'volume', + } + ] + elif snapshot: + block_device_mapping_v2 = [ + { + 'uuid': snapshot, + 'boot_index': 0, + 'source_type': 'snapshot', + 'destination_type': 'volume', + 'delete_on_termination': False, + } + ] if parsed_args.swap: block_device_mapping_v2.append( @@ -1770,7 +1781,7 @@ def _match_image(image_api, wanted_properties): raise exceptions.CommandError(msg) if 'tag' in mapping and ( - compute_client.api_version < api_versions.APIVersion('2.42') + not sdk_utils.supports_microversion(compute_client, '2.42') ): msg = _( '--os-compute-api-version 2.42 or greater is ' @@ -1779,7 +1790,7 @@ def _match_image(image_api, wanted_properties): raise exceptions.CommandError(msg) if 'volume_type' in mapping and ( - compute_client.api_version < api_versions.APIVersion('2.67') + not sdk_utils.supports_microversion(compute_client, '2.67') ): msg = _( '--os-compute-api-version 2.67 or greater is ' @@ -1835,7 +1846,7 @@ def _match_image(image_api, wanted_properties): block_device_mapping_v2.append(mapping) - if not image and not any( + if not any( [bdm.get('boot_index') == 0 for bdm in block_device_mapping_v2] ): msg = _( @@ -1844,10 +1855,12 @@ def _match_image(image_api, wanted_properties): ) raise exceptions.CommandError(msg) - nics = parsed_args.nics + # 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] = [] - if 'auto' in nics or 'none' in nics: - if len(nics) > 1: + if 'auto' in parsed_args.nics or 'none' in parsed_args.nics: + if len(parsed_args.nics) > 1: msg = _( 'Specifying a --nic of auto or none cannot ' 'be used with any other --nic, --network ' @@ -1855,7 +1868,7 @@ def _match_image(image_api, wanted_properties): ) raise exceptions.CommandError(msg) - if compute_client.api_version < api_versions.APIVersion('2.37'): + if not sdk_utils.supports_microversion(compute_client, '2.37'): msg = _( '--os-compute-api-version 2.37 or greater is ' 'required to support explicit auto-allocation of a ' @@ -1863,12 +1876,12 @@ def _match_image(image_api, wanted_properties): ) raise exceptions.CommandError(msg) - nics = nics[0] + networks = parsed_args.nics[0] else: - for nic in nics: + for nic in parsed_args.nics: if 'tag' in nic: - if compute_client.api_version < api_versions.APIVersion( - '2.43' + if not sdk_utils.supports_microversion( + compute_client, '2.43' ): msg = _( '--os-compute-api-version 2.43 or greater is ' @@ -1894,9 +1907,11 @@ def _match_image(image_api, wanted_properties): nic['port-id'] = port.id else: if nic['net-id']: - nic['net-id'] = compute_client.api.network_find( + net = compute_v2.find_network( + compute_client, nic['net-id'], - )['id'] + ) + nic['net-id'] = net['id'] if nic['port-id']: msg = _( @@ -1905,18 +1920,35 @@ def _match_image(image_api, wanted_properties): ) raise exceptions.CommandError(msg) - if not nics: + # convert from the novaclient-derived "NIC" view to the actual + # "network" view + network = {} + + if nic['net-id']: + network['uuid'] = nic['net-id'] + + if nic['port-id']: + network['port'] = nic['port-id'] + + if nic['v4-fixed-ip']: + network['fixed'] = nic['v4-fixed-ip'] + elif nic['v6-fixed-ip']: + network['fixed'] = nic['v6-fixed-ip'] + + if nic.get('tag'): # tags are optional + network['tag'] = nic['tag'] + + networks.append(network) + + if not parsed_args.nics and sdk_utils.supports_microversion( + compute_client, '2.37' + ): # Compute API version >= 2.37 requires a value, so default to # 'auto' to maintain legacy behavior if a nic wasn't specified. - if compute_client.api_version >= api_versions.APIVersion('2.37'): - nics = 'auto' - else: - # Default to empty list if nothing was specified and let nova - # decide the default behavior. - nics = [] + networks = 'auto' # Check security group exist and convert ID to name - security_group_names = [] + 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: @@ -1925,12 +1957,12 @@ def _match_image(image_api, wanted_properties): ) # Use security group ID to avoid multiple security group have # same name in neutron networking backend - security_group_names.append(sg.id) + security_groups.append({'name': sg.id}) else: # Handle nova-network case for each_sg in parsed_args.security_group: - sg = compute_client.api.security_group_find(each_sg) - security_group_names.append(sg['name']) + sg = compute_v2.find_security_group(compute_client, each_sg) + security_groups.append({'name': sg['name']}) hints = {} for key, values in parsed_args.hints.items(): @@ -1941,9 +1973,8 @@ def _match_image(image_api, wanted_properties): hints[key] = values if parsed_args.server_group: - server_group_obj = utils.find_resource( - compute_client.server_groups, - parsed_args.server_group, + server_group_obj = compute_client.find_server_group( + parsed_args.server_group, ignore_missing=False ) hints['group'] = server_group_obj.id @@ -1965,69 +1996,89 @@ def _match_image(image_api, wanted_properties): else: config_drive = parsed_args.config_drive - boot_args = [parsed_args.server_name, image, flavor] - - boot_kwargs = dict( - meta=parsed_args.properties, - files=files, - reservation_id=None, - min_count=parsed_args.min, - max_count=parsed_args.max, - security_groups=security_group_names, - userdata=userdata, - key_name=parsed_args.key_name, - availability_zone=parsed_args.availability_zone, - admin_pass=parsed_args.password, - block_device_mapping_v2=block_device_mapping_v2, - nics=nics, - scheduler_hints=hints, - config_drive=config_drive, - ) + kwargs = { + 'name': parsed_args.server_name, + 'image_id': image.id if image else '', + 'flavor_id': flavor.id, + 'min_count': parsed_args.min, + 'max_count': parsed_args.max, + } if parsed_args.description: - boot_kwargs['description'] = parsed_args.description + kwargs['description'] = parsed_args.description + + if parsed_args.availability_zone: + kwargs['availability_zone'] = parsed_args.availability_zone + + if parsed_args.password: + kwargs['admin_password'] = parsed_args.password + + if parsed_args.properties: + kwargs['metadata'] = parsed_args.properties + + if parsed_args.key_name: + kwargs['key_name'] = parsed_args.key_name + + if user_data: + kwargs['user_data'] = user_data + + if files: + kwargs['personality'] = files + + if security_groups: + kwargs['security_groups'] = security_groups + + if block_device_mapping_v2: + kwargs['block_device_mapping'] = block_device_mapping_v2 + + if hints: + kwargs['scheduler_hints'] = hints + + if networks is not None: + kwargs['networks'] = networks + + if config_drive is not None: + kwargs['config_drive'] = config_drive if parsed_args.tags: - if compute_client.api_version < api_versions.APIVersion('2.52'): + if not sdk_utils.supports_microversion(compute_client, '2.52'): msg = _( '--os-compute-api-version 2.52 or greater is required to ' 'support the --tag option' ) raise exceptions.CommandError(msg) - boot_kwargs['tags'] = parsed_args.tags + kwargs['tags'] = parsed_args.tags if parsed_args.host: - if compute_client.api_version < api_versions.APIVersion("2.74"): + if not sdk_utils.supports_microversion(compute_client, '2.74'): msg = _( '--os-compute-api-version 2.74 or greater is required to ' 'support the --host option' ) raise exceptions.CommandError(msg) - boot_kwargs['host'] = parsed_args.host + kwargs['host'] = parsed_args.host if parsed_args.hypervisor_hostname: - if compute_client.api_version < api_versions.APIVersion("2.74"): + if not sdk_utils.supports_microversion(compute_client, '2.74'): msg = _( '--os-compute-api-version 2.74 or greater is required to ' 'support the --hypervisor-hostname option' ) raise exceptions.CommandError(msg) - boot_kwargs['hypervisor_hostname'] = ( - parsed_args.hypervisor_hostname - ) + kwargs['hypervisor_hostname'] = parsed_args.hypervisor_hostname if parsed_args.hostname: - if compute_client.api_version < api_versions.APIVersion("2.90"): + if not sdk_utils.supports_microversion(compute_client, '2.90'): msg = _( '--os-compute-api-version 2.90 or greater is required to ' 'support the --hostname option' ) raise exceptions.CommandError(msg) - boot_kwargs['hostname'] = parsed_args.hostname + kwargs['hostname'] = parsed_args.hostname # TODO(stephenfin): Handle OS_TRUSTED_IMAGE_CERTIFICATE_IDS if parsed_args.trusted_image_certs: @@ -2037,7 +2088,7 @@ def _match_image(image_api, wanted_properties): 'servers booted directly from images' ) raise exceptions.CommandError(msg) - if compute_client.api_version < api_versions.APIVersion('2.63'): + if not sdk_utils.supports_microversion(compute_client, '2.63'): msg = _( '--os-compute-api-version 2.63 or greater is required to ' 'support the --trusted-image-cert option' @@ -2045,25 +2096,22 @@ def _match_image(image_api, wanted_properties): raise exceptions.CommandError(msg) certs = parsed_args.trusted_image_certs - boot_kwargs['trusted_image_certificates'] = certs + kwargs['trusted_image_certificates'] = certs - LOG.debug('boot_args: %s', boot_args) - LOG.debug('boot_kwargs: %s', boot_kwargs) + LOG.debug('boot_kwargs: %s', kwargs) # Wrap the call to catch exceptions in order to close files try: - server = compute_client.servers.create(*boot_args, **boot_kwargs) + server = compute_client.create_server(**kwargs) finally: # Clean up open files - make sure they are not strings for f in files: if hasattr(f, 'close'): f.close() - if hasattr(userdata, 'close'): - userdata.close() if parsed_args.wait: if utils.wait_for_status( - compute_client.servers.get, + compute_client.get_server, server.id, callback=_show_progress, ): @@ -2072,8 +2120,6 @@ def _match_image(image_api, wanted_properties): msg = _('Error creating server: %s') % parsed_args.server_name raise exceptions.CommandError(msg) - # TODO(stephenfin): Remove when the whole command is using SDK - compute_client = self.app.client_manager.sdk_connection.compute data = _prep_server_detail(compute_client, image_client, server) return zip(*sorted(data.items())) diff --git a/openstackclient/tests/unit/api/test_compute_v2.py b/openstackclient/tests/unit/api/test_compute_v2.py index 5a8442602..fd2c521c7 100644 --- a/openstackclient/tests/unit/api/test_compute_v2.py +++ b/openstackclient/tests/unit/api/test_compute_v2.py @@ -763,3 +763,102 @@ def test_find_security_group_by_name_duplicate(self): self.compute_sdk_client, sg_name, ) + + +class TestFindNetwork(utils.TestCase): + + def setUp(self): + super().setUp() + + self.compute_sdk_client = mock.Mock(_proxy.Proxy) + + def test_find_network_by_id(self): + net_id = uuid.uuid4().hex + net_name = 'name-' + uuid.uuid4().hex + data = { + 'network': { + 'id': net_id, + 'label': net_name, + # other fields omitted for brevity + } + } + self.compute_sdk_client.get.side_effect = [ + fakes.FakeResponse(data=data), + ] + + result = compute.find_network(self.compute_sdk_client, net_id) + + self.compute_sdk_client.get.assert_has_calls( + [ + mock.call(f'/os-networks/{net_id}', microversion='2.1'), + ] + ) + self.assertEqual(data['network'], result) + + def test_find_network_by_name(self): + net_id = uuid.uuid4().hex + net_name = 'name-' + uuid.uuid4().hex + data = { + 'networks': [ + { + 'id': net_id, + 'label': net_name, + # other fields omitted for brevity + } + ], + } + self.compute_sdk_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) + + self.compute_sdk_client.get.assert_has_calls( + [ + mock.call(f'/os-networks/{net_name}', microversion='2.1'), + mock.call('/os-networks', microversion='2.1'), + ] + ) + self.assertEqual(data['networks'][0], result) + + def test_find_network_not_found(self): + data = {'networks': []} + self.compute_sdk_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, + 'invalid-net', + ) + + def test_find_network_by_name_duplicate(self): + net_name = 'name-' + uuid.uuid4().hex + data = { + 'networks': [ + { + 'id': uuid.uuid4().hex, + 'label': net_name, + # other fields omitted for brevity + }, + { + 'id': uuid.uuid4().hex, + 'label': net_name, + # other fields omitted for brevity + }, + ], + } + self.compute_sdk_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, + net_name, + ) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 858e4eca6..8902b6501 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -14,11 +14,11 @@ import base64 import collections -import copy import getpass import json import tempfile from unittest import mock +import uuid import iso8601 from openstack import exceptions as sdk_exceptions @@ -64,10 +64,6 @@ class TestServer(compute_fakes.TestComputev2): def setUp(self): super().setUp() - # Get a shortcut to the compute client ServerManager Mock - self.servers_mock = self.compute_client.servers - self.servers_mock.reset_mock() - # Get a shortcut to the compute client ServerMigrationsManager Mock self.server_migrations_mock = self.compute_client.server_migrations self.server_migrations_mock.reset_mock() @@ -84,41 +80,12 @@ def setUp(self): self.flavors_mock = self.compute_client.flavors self.flavors_mock.reset_mock() - # Get a shortcut to the volume client VolumeManager Mock - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - - # Get a shortcut to the volume client VolumeManager Mock - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - # Set object attributes to be tested. Could be overwritten in subclass. self.attrs = {} # Set object methods to be tested. Could be overwritten in subclass. self.methods = {} - def setup_servers_mock(self, count): - # If we are creating more than one server, make one of them - # boot-from-volume - include_bfv = count > 1 - servers = compute_fakes.create_servers( - attrs=self.attrs, - methods=self.methods, - count=count - 1 if include_bfv else count, - ) - if include_bfv: - attrs = copy.deepcopy(self.attrs) - attrs['image'] = '' - bfv_server = compute_fakes.create_one_server( - attrs=attrs, methods=self.methods - ) - servers.append(bfv_server) - - # This is the return value for utils.find_resource() - self.servers_mock.get = compute_fakes.get_servers(servers, 0) - return servers - def setup_sdk_servers_mock(self, count): servers = compute_fakes.create_sdk_servers( attrs=self.attrs, @@ -760,7 +727,7 @@ def test_server_add_port_with_tag(self): def test_server_add_port_with_tag_pre_v249(self): self.set_compute_api_version('2.48') - servers = self.setup_servers_mock(count=1) + servers = self.setup_sdk_servers_mock(count=1) self.find_port.return_value.id = 'fake-port' arglist = [ servers[0].id, @@ -1341,7 +1308,7 @@ def datalist(self): None, # OS-EXT-SRV-ATTR:user_data server.PowerStateColumn( self.server.power_state - ), # OS-EXT-STS:power_state # noqa: E501 + ), # OS-EXT-STS:power_state None, # OS-EXT-STS:task_state None, # OS-EXT-STS:vm_state None, # OS-SRV-USG:launched_at @@ -1382,7 +1349,6 @@ def setUp(self): self.image_client.get_image.return_value = self.image self.flavor = compute_fakes.create_one_flavor() - self.flavors_mock.get.return_value = self.flavor self.compute_sdk_client.find_flavor.return_value = self.flavor attrs = { @@ -1391,31 +1357,23 @@ def setUp(self): 'image': self.image, 'flavor': self.flavor, } - self.new_server = compute_fakes.create_one_server(attrs=attrs) - self.servers_mock.create.return_value = self.new_server - - # We need an SDK-style server object also for the get_server call in - # _prep_server_detail - # TODO(stephenfin): Remove when the whole command is using SDK 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.volume = volume_fakes.create_one_volume() - self.volume_alt = volume_fakes.create_one_volume() - self.volumes_mock.get.return_value = self.volume - self.snapshot = volume_fakes.create_one_snapshot() - self.snapshots_mock.get.return_value = self.snapshot # Get the command object to test self.cmd = server.CreateServer(self.app, None) def test_server_create_no_options(self): arglist = [ - self.new_server.name, + self.server.name, ] verifylist = [ - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] self.assertRaises( @@ -1429,63 +1387,65 @@ def test_server_create_no_options(self): def test_server_create_minimal(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', - self.new_server.name, + self.flavor.id, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.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. + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.compute_sdk_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( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + 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) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) def test_server_create_with_options(self): + server_group = compute_fakes.create_one_server_group() + self.compute_sdk_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 + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--key-name', 'keyname', '--property', 'Beta=b', '--security-group', - 'securitygroup', + security_group.id, '--use-config-drive', '--password', 'passw0rd', @@ -1494,329 +1454,283 @@ def test_server_create_with_options(self): '--hint', 'a=c', '--server-group', - 'servergroup', - self.new_server.name, + server_group.id, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('key_name', 'keyname'), ('properties', {'Beta': 'b'}), - ('security_group', ['securitygroup']), + ('security_group', [security_group.id]), ('hints', {'a': ['b', 'c']}), - ('server_group', 'servergroup'), + ('server_group', server_group.id), ('config_drive', True), ('password', 'passw0rd'), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - fake_server_group = compute_fakes.create_one_server_group() - self.compute_client.server_groups.get.return_value = fake_server_group - - fake_sg = network_fakes.FakeSecurityGroup.create_security_groups() - mock_find_sg = network_fakes.FakeSecurityGroup.get_security_groups( - fake_sg - ) - self.app.client_manager.network.find_security_group = mock_find_sg - - # 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. + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - mock_find_sg.assert_called_once_with( - 'securitygroup', ignore_missing=False + 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_called_once_with( + security_group.id, ignore_missing=False + ) + self.image_client.find_image.assert_called_once_with( + self.image.id, ignore_missing=False ) - # Set expected values - kwargs = dict( - meta={'Beta': 'b'}, - files={}, - reservation_id=None, + self.compute_sdk_client.find_server_group.assert_called_once_with( + server_group.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, + metadata={'Beta': 'b'}, min_count=1, max_count=1, - security_groups=[fake_sg[0].id], - userdata=None, + security_groups=[{'name': security_group.id}], key_name='keyname', - availability_zone=None, - admin_pass='passw0rd', - block_device_mapping_v2=[], - nics=[], - scheduler_hints={'a': ['b', 'c'], 'group': fake_server_group.id}, + admin_password='passw0rd', + networks=[], + scheduler_hints={'a': ['b', 'c'], 'group': server_group.id}, config_drive=True, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + 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_not_exist_security_group(self): + self.network_client.find_security_group.side_effect = ( + sdk_exceptions.NotFoundException() + ) + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--key-name', 'keyname', '--security-group', - 'securitygroup', - '--security-group', 'not_exist_sg', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('key_name', 'keyname'), - ('security_group', ['securitygroup', 'not_exist_sg']), - ('server_name', self.new_server.name), + ('security_group', ['not_exist_sg']), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - fake_sg = network_fakes.FakeSecurityGroup.create_security_groups( - count=1 - ) - fake_sg.append(exceptions.NotFound(code=404)) - mock_find_sg = network_fakes.FakeSecurityGroup.get_security_groups( - fake_sg - ) - self.app.client_manager.network.find_security_group = mock_find_sg - self.assertRaises( - exceptions.NotFound, self.cmd.take_action, parsed_args + sdk_exceptions.NotFoundException, self.cmd.take_action, parsed_args + ) + self.network_client.find_security_group.assert_called_once_with( + 'not_exist_sg', ignore_missing=False ) - mock_find_sg.assert_called_with('not_exist_sg', ignore_missing=False) def test_server_create_with_security_group_in_nova_network(self): + sg_name = 'nova-net-sec-group' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', - '--key-name', - 'keyname', + self.flavor.id, '--security-group', - 'securitygroup', - self.new_server.name, + sg_name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), - ('key_name', 'keyname'), - ('security_group', ['securitygroup']), - ('server_name', self.new_server.name), + ('image', self.image.id), + ('flavor', self.flavor.id), + ('security_group', [sg_name]), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch.object( self.app.client_manager, 'is_network_endpoint_enabled', return_value=False, ): with mock.patch.object( - self.compute_client.api, - 'security_group_find', - return_value={'name': 'fake_sg'}, + compute_v2, + 'find_security_group', + return_value={'name': sg_name}, ) as mock_find: columns, data = self.cmd.take_action(parsed_args) - mock_find.assert_called_once_with('securitygroup') - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + mock_find.assert_called_once_with(self.compute_sdk_client, sg_name) + 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=['fake_sg'], - userdata=None, - key_name='keyname', - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + security_groups=[{'name': sg_name}], + 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() + network_auto = network_fakes.create_one_network({'name': 'auto'}) + port_port1 = network_fakes.create_one_port() + port_port2 = network_fakes.create_one_port() + + def find_network(name_or_id, ignore_missing): + assert ignore_missing is False + return { + network_net1.id: network_net1, + network_net2.id: network_net2, + network_auto.name: network_auto, + }[name_or_id] + + def find_port(name_or_id, ignore_missing): + assert ignore_missing is False + return { + port_port1.name: port_port1, + port_port2.id: port_port2, + }[name_or_id] + + self.app.client_manager.network.find_network.side_effect = find_network + self.app.client_manager.network.find_port.side_effect = find_port + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--network', - 'net1', + network_net1.id, '--nic', - 'net-id=net1,v4-fixed-ip=10.0.0.2', + f'net-id={network_net2.id},v4-fixed-ip=10.0.0.2', '--port', - 'port1', - '--network', - 'net1', + port_port1.name, '--network', - 'auto', # this is a network called 'auto' + network_auto.name, '--nic', - 'port-id=port2', - self.new_server.name, + f'port-id={port_port2.id}', + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ( 'nics', [ { - 'net-id': 'net1', + 'net-id': network_net1.id, 'port-id': '', 'v4-fixed-ip': '', 'v6-fixed-ip': '', }, { - 'net-id': 'net1', + 'net-id': network_net2.id, 'port-id': '', 'v4-fixed-ip': '10.0.0.2', 'v6-fixed-ip': '', }, { 'net-id': '', - 'port-id': 'port1', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - }, - { - 'net-id': 'net1', - 'port-id': '', + 'port-id': port_port1.name, 'v4-fixed-ip': '', 'v6-fixed-ip': '', }, { - 'net-id': 'auto', + 'net-id': network_auto.name, 'port-id': '', 'v4-fixed-ip': '', 'v6-fixed-ip': '', }, { 'net-id': '', - 'port-id': 'port2', + 'port-id': port_port2.id, 'v4-fixed-ip': '', 'v6-fixed-ip': '', }, ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - get_endpoints = mock.Mock() - get_endpoints.return_value = {'network': []} - self.app.client_manager.auth_ref = mock.Mock() - self.app.client_manager.auth_ref.service_catalog = mock.Mock() - self.app.client_manager.auth_ref.service_catalog.get_endpoints = ( - get_endpoints - ) - - network_resource = mock.Mock(id='net1_uuid') - port1_resource = mock.Mock(id='port1_uuid') - port2_resource = mock.Mock(id='port2_uuid') - self.network_client.find_network.return_value = network_resource - self.network_client.find_port.side_effect = ( - lambda port_id, ignore_missing: { - "port1": port1_resource, - "port2": port2_resource, - }[port_id] - ) - - # Mock sdk APIs. - _network_1 = mock.Mock(id='net1_uuid') - _network_auto = mock.Mock(id='auto_uuid') - _port1 = mock.Mock(id='port1_uuid') - _port2 = mock.Mock(id='port2_uuid') - find_network = mock.Mock() - find_port = mock.Mock() - find_network.side_effect = lambda net_id, ignore_missing: { - "net1": _network_1, - "auto": _network_auto, - }[net_id] - find_port.side_effect = lambda port_id, ignore_missing: { - "port1": _port1, - "port2": _port2, - }[port_id] - self.app.client_manager.network.find_network = find_network - self.app.client_manager.network.find_port = find_port - - # 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. + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.network_client.find_network.assert_has_calls( + [ + mock.call(network_net1.id, ignore_missing=False), + mock.call(network_net2.id, ignore_missing=False), + mock.call(network_auto.name, ignore_missing=False), + ] + ) + self.network_client.find_port.assert_has_calls( + [ + mock.call(port_port1.name, ignore_missing=False), + mock.call(port_port2.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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[ + networks=[ { - 'net-id': 'net1_uuid', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - 'port-id': '', + 'uuid': network_net1.id, }, { - 'net-id': 'net1_uuid', - 'v4-fixed-ip': '10.0.0.2', - 'v6-fixed-ip': '', - 'port-id': '', + 'uuid': network_net2.id, + 'fixed': '10.0.0.2', }, { - 'net-id': '', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - 'port-id': 'port1_uuid', + 'port': port_port1.id, }, { - 'net-id': 'net1_uuid', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - 'port-id': '', + 'uuid': network_auto.id, }, { - 'net-id': 'auto_uuid', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - 'port-id': '', + 'port': port_port2.id, }, + ], + block_device_mapping=[ { - 'net-id': '', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - 'port-id': 'port2_uuid', + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, }, ], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs ) self.assertEqual(self.columns, columns) @@ -1825,23 +1739,26 @@ def test_server_create_with_network(self): def test_server_create_with_network_tag(self): self.set_compute_api_version('2.43') + network = network_fakes.create_one_network() + self.app.client_manager.network.find_network.return_value = network + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', - 'net-id=net1,tag=foo', - self.new_server.name, + f'net-id={network.id},tag=foo', + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ( 'nics', [ { - 'net-id': 'net1', + 'net-id': network.id, 'port-id': '', 'v4-fixed-ip': '', 'v6-fixed-ip': '', @@ -1849,70 +1766,55 @@ def test_server_create_with_network_tag(self): }, ], ), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # Mock sdk APIs. - _network = mock.Mock(id='net1_uuid') - self.network_client.find_network.return_value = _network - - # 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) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.network_client.find_network.assert_called_once_with( + network.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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[ + networks=[ { - 'net-id': 'net1_uuid', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - 'port-id': '', + 'uuid': network.id, 'tag': 'foo', }, ], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + 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) - self.network_client.find_network.assert_called_once() - self.app.client_manager.network.find_network.assert_called_once() - def test_server_create_with_network_tag_pre_v243(self): self.set_compute_api_version('2.42') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'net-id=net1,tag=foo', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ( 'nics', [ @@ -1925,49 +1827,48 @@ def test_server_create_with_network_tag_pre_v243(self): }, ], ), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( 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() def _test_server_create_with_auto_network(self, arglist): # requires API microversion 2.37 or later self.set_compute_api_version('2.37') verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('nics', ['auto']), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.network_client.find_network.assert_not_called() + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks='auto', + 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) @@ -1979,23 +1880,23 @@ def _test_server_create_with_auto_network(self, arglist): def test_server_create_with_auto_network_legacy(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'auto', - self.new_server.name, + self.server.name, ] self._test_server_create_with_auto_network(arglist) def test_server_create_with_auto_network(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--auto-network', - self.new_server.name, + self.server.name, ] self._test_server_create_with_auto_network(arglist) @@ -2005,19 +1906,19 @@ def test_server_create_with_auto_network_pre_v237(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'auto', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('nics', ['auto']), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2032,50 +1933,48 @@ def test_server_create_with_auto_network_pre_v237(self): 'allocation', str(exc), ) - self.assertNotCalled(self.servers_mock.create) + self.compute_sdk_client.create_server.assert_not_called() - def test_server_create_with_auto_network_default_v2_37(self): + def test_server_create_with_auto_network_default(self): """Tests creating a server without specifying --nic using 2.37.""" # requires API microversion 2.37 or later self.set_compute_api_version('2.37') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', - self.new_server.name, + self.flavor.id, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), + ('nics', []), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.network_client.find_network.assert_not_called() + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks='auto', + 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) @@ -2086,36 +1985,33 @@ def _test_server_create_with_none_network(self, arglist): self.set_compute_api_version('2.37') verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('nics', ['none']), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.network_client.find_network.assert_not_called() + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='none', - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks='none', + 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) @@ -2127,23 +2023,23 @@ def _test_server_create_with_none_network(self, arglist): def test_server_create_with_none_network_legacy(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'none', - self.new_server.name, + self.server.name, ] self._test_server_create_with_none_network(arglist) def test_server_create_with_none_network(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--no-network', - self.new_server.name, + self.server.name, ] self._test_server_create_with_none_network(arglist) @@ -2153,23 +2049,22 @@ def test_server_create_with_none_network_pre_v237(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'none', - self.new_server.name, + self.server.name, ] - verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('nics', ['none']), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, @@ -2181,25 +2076,25 @@ def test_server_create_with_none_network_pre_v237(self): 'allocation', str(exc), ) - self.assertNotCalled(self.servers_mock.create) + self.compute_sdk_client.create_server.assert_not_called() - def test_server_create_with_conflict_network_options(self): + def test_server_create_with_conflicting_network_options(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'none', '--nic', 'auto', '--nic', 'port-id=port1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ( 'nics', [ @@ -2214,101 +2109,181 @@ def test_server_create_with_conflict_network_options(self): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - get_endpoints = mock.Mock() - get_endpoints.return_value = {'network': []} - self.app.client_manager.auth_ref = mock.Mock() - self.app.client_manager.auth_ref.service_catalog = mock.Mock() - self.app.client_manager.auth_ref.service_catalog.get_endpoints = ( - get_endpoints - ) - - port_resource = mock.Mock(id='port1_uuid') - self.network_client.find_port.return_value = port_resource - - self.assertRaises( + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.assertNotCalled(self.servers_mock.create) + self.assertIn( + 'Specifying a --nic of auto or none cannot be used with any ' + 'other --nic, --network or --port value.', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_invalid_network_options(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'abcdefgh', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) - self.assertNotCalled(self.servers_mock.create) + self.assertIn( + 'Invalid argument abcdefgh; argument must be of form ', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_invalid_network_key(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'abcdefgh=12324', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) - self.assertNotCalled(self.servers_mock.create) + self.assertIn( + 'Invalid argument abcdefgh=12324; argument must be of form ', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_empty_network_key_value(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'net-id=', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) - self.assertNotCalled(self.servers_mock.create) + self.assertIn( + 'Invalid argument net-id=; argument must be of form ', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_only_network_key(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'net-id', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) - self.assertNotCalled(self.servers_mock.create) + self.assertIn( + 'Invalid argument net-id; argument must be of form ', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() + + def test_server_create_with_network_in_nova_network(self): + net_name = 'nova-net-net' + net_id = uuid.uuid4().hex + + arglist = [ + '--image', + self.image.id, + '--flavor', + self.flavor.id, + '--network', + net_name, + self.server.name, + ] + verifylist = [ + ('image', self.image.id), + ('flavor', self.flavor.id), + ( + 'nics', + [ + { + 'net-id': net_name, + 'port-id': '', + 'v4-fixed-ip': '', + 'v6-fixed-ip': '', + }, + ], + ), + ('config_drive', False), + ('server_name', self.server.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + with mock.patch.object( + self.app.client_manager, + 'is_network_endpoint_enabled', + return_value=False, + ): + with mock.patch.object( + compute_v2, + 'find_network', + return_value={'id': net_id, 'name': net_name}, + ) 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( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + networks=[ + { + 'uuid': net_id, + }, + ], + 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_conflicting_net_port_filters(self): arglist = [ @@ -2318,7 +2293,7 @@ def test_server_create_with_conflicting_net_port_filters(self): 'flavor1', '--nic', 'net-id=abc,port-id=xyz', - self.new_server.name, + self.server.name, ] exc = self.assertRaises( test_utils.ParserException, @@ -2328,7 +2303,7 @@ def test_server_create_with_conflicting_net_port_filters(self): [], ) self.assertIn("either 'network' or 'port'", str(exc)) - self.assertNotCalled(self.servers_mock.create) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_conflicting_fixed_ip_filters(self): arglist = [ @@ -2338,7 +2313,7 @@ def test_server_create_with_conflicting_fixed_ip_filters(self): 'flavor1', '--nic', 'net-id=abc,v4-fixed-ip=1.2.3.4,v6-fixed-ip=2001:db8:abcd', - self.new_server.name, + self.server.name, ] exc = self.assertRaises( test_utils.ParserException, @@ -2348,53 +2323,52 @@ def test_server_create_with_conflicting_fixed_ip_filters(self): [], ) self.assertIn("either 'v4-fixed-ip' or 'v6-fixed-ip'", str(exc)) - self.assertNotCalled(self.servers_mock.create) + self.compute_sdk_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): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--wait', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('config_drive', False), ('wait', True), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - mock_wait_for_status.assert_called_once_with( - self.servers_mock.get, - self.new_server.id, - callback=mock.ANY, - ) - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - scheduler_hints={}, - config_drive=None, + networks=[], + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + mock_wait_for_status.assert_called_once_with( + self.compute_sdk_client.get_server, + self.server.id, + callback=mock.ANY, ) + self.assertEqual(self.columns, columns) self.assertTupleEqual(self.datalist(), data) @@ -2402,143 +2376,126 @@ def test_server_create_with_wait_ok(self, mock_wait_for_status): def test_server_create_with_wait_fails(self, mock_wait_for_status): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--wait', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('config_drive', False), ('wait', True), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - mock_wait_for_status.assert_called_once_with( - self.servers_mock.get, - self.new_server.id, - callback=mock.ANY, - ) - - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - scheduler_hints={}, - config_drive=None, + networks=[], + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + mock_wait_for_status.assert_called_once_with( + self.compute_sdk_client.get_server, + self.server.id, + callback=mock.ANY, ) - @mock.patch('openstackclient.compute.v2.server.open') - def test_server_create_userdata(self, mock_open): - mock_file = mock.Mock(name='File') - mock_open.return_value = mock_file - mock_open.read.return_value = '#!/bin/sh' - + def test_server_create_userdata(self): + user_data = b'#!/bin/sh' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--user-data', 'userdata.sh', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('user_data', 'userdata.sh'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.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) - - # Ensure the userdata file is opened - mock_open.assert_called_with('userdata.sh') - # Ensure the userdata file is closed - mock_file.close.assert_called_with() + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + with mock.patch( + 'openstackclient.compute.v2.server.open', + mock.mock_open(read_data=user_data), + ) as mock_file: + columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + mock_file.assert_called_with('userdata.sh', 'rb') + 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=[], - userdata=mock_file, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], + user_data=base64.b64encode(user_data).decode('utf-8'), + 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_volume(self): + self.volume_client.volumes.get.return_value = self.volume + arglist = [ '--flavor', self.flavor.id, '--volume', self.volume.name, - self.new_server.name, + self.server.name, ] verifylist = [ ('flavor', self.flavor.id), ('volume', self.volume.name), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + self.volume_client.volumes.get.assert_called_once_with( + self.volume.name + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id='', + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + block_device_mapping=[ { 'uuid': self.volume.id, 'boot_index': 0, @@ -2546,50 +2503,41 @@ def test_server_create_with_volume(self): 'destination_type': 'volume', } ], - 'nics': [], - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, None, self.flavor, **kwargs + networks=[], ) - self.volumes_mock.get.assert_called_once_with(self.volume.name) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_snapshot(self): + self.volume_client.volume_snapshots.get.return_value = self.snapshot + arglist = [ '--flavor', self.flavor.id, '--snapshot', self.snapshot.name, - self.new_server.name, + self.server.name, ] verifylist = [ ('flavor', self.flavor.id), ('snapshot', self.snapshot.name), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + self.volume_client.volume_snapshots.get.assert_called_once_with( + self.snapshot.name + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id='', + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + block_device_mapping=[ { 'uuid': self.snapshot.id, 'boot_index': 0, @@ -2598,15 +2546,8 @@ def test_server_create_with_snapshot(self): 'delete_on_termination': False, } ], - 'nics': [], - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, None, self.flavor, **kwargs + networks=[], ) - self.snapshots_mock.get.assert_called_once_with(self.snapshot.name) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) @@ -2618,7 +2559,7 @@ def test_server_create_with_block_device(self): self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] verifylist = [ ('image', None), @@ -2633,40 +2574,29 @@ def test_server_create_with_block_device(self): }, ], ), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + # 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( + name=self.server.name, + image_id='', + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + block_device_mapping=[ { 'uuid': self.volume.id, + 'boot_index': 0, 'source_type': 'volume', 'destination_type': 'volume', - 'boot_index': 0, - }, + } ], - 'nics': [], - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, None, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) @@ -2675,6 +2605,7 @@ def test_server_create_with_block_device(self): def test_server_create_with_block_device_full(self): self.set_compute_api_version('2.67') + self.volume_alt = volume_fakes.create_one_volume() block_device = ( f'uuid={self.volume.id},source_type=volume,' f'destination_type=volume,disk_bus=ide,device_type=disk,' @@ -2686,17 +2617,17 @@ def test_server_create_with_block_device_full(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, '--block-device', block_device_alt, - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_devices', @@ -2721,26 +2652,28 @@ def test_server_create_with_block_device_full(self): }, ], ), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + # 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( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'uuid': self.volume.id, 'source_type': 'volume', @@ -2761,13 +2694,7 @@ def test_server_create_with_block_device_full(self): 'destination_type': 'volume', }, ], - 'nics': 'auto', - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks='auto', ) self.assertEqual(self.columns, columns) @@ -2797,37 +2724,38 @@ def test_server_create_with_block_device_from_file(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', fp.name, - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ('block_devices', [block_device]), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # CreateServer.take_action() returns two tuples columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + # 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( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'uuid': self.volume.id, 'source_type': 'volume', @@ -2841,15 +2769,9 @@ def test_server_create_with_block_device_from_file(self): 'delete_on_termination': True, 'tag': 'foo', 'volume_type': 'foo', - } + }, ], - 'nics': 'auto', - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks='auto', ) self.assertEqual(self.columns, columns) @@ -2861,63 +2783,66 @@ def test_server_create_with_block_device_invalid_boot_index(self): ) arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] parsed_args = self.check_parser(self.cmd, arglist, []) ex = self.assertRaises( 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() def test_server_create_with_block_device_invalid_source_type(self): block_device = f'uuid={self.volume.name},source_type=foo' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] parsed_args = self.check_parser(self.cmd, arglist, []) ex = self.assertRaises( 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() def test_server_create_with_block_device_invalid_destination_type(self): block_device = f'uuid={self.volume.name},destination_type=foo' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] parsed_args = self.check_parser(self.cmd, arglist, []) ex = self.assertRaises( 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() def test_server_create_with_block_device_invalid_shutdown(self): block_device = f'uuid={self.volume.name},delete_on_termination=foo' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] parsed_args = self.check_parser(self.cmd, arglist, []) ex = self.assertRaises( @@ -2926,6 +2851,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() def test_server_create_with_block_device_tag_pre_v242(self): self.set_compute_api_version('2.41') @@ -2933,12 +2859,12 @@ def test_server_create_with_block_device_tag_pre_v242(self): block_device = f'uuid={self.volume.name},tag=foo' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] parsed_args = self.check_parser(self.cmd, arglist, []) ex = self.assertRaises( @@ -2947,6 +2873,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() def test_server_create_with_block_device_volume_type_pre_v267(self): self.set_compute_api_version('2.66') @@ -2954,12 +2881,12 @@ def test_server_create_with_block_device_volume_type_pre_v267(self): block_device = f'uuid={self.volume.name},volume_type=foo' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] parsed_args = self.check_parser(self.cmd, arglist, []) ex = self.assertRaises( @@ -2968,19 +2895,22 @@ 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() def test_server_create_with_block_device_mapping(self): + self.volume_client.volumes.get.return_value = self.volume + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'vda=' + self.volume.name + ':::false', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_device_mapping', @@ -2995,58 +2925,57 @@ def test_server_create_with_block_device_mapping(self): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.volume_client.volumes.get.assert_called_once_with( + self.volume.name + ) + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[ + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'device_name': 'vda', 'uuid': self.volume.id, 'destination_type': 'volume', 'source_type': 'volume', 'delete_on_termination': 'false', - } + }, ], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_mapping_min_input(self): + self.volume_client.volumes.get.return_value = self.volume + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'vdf=' + self.volume.name, - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_device_mapping', @@ -3060,57 +2989,56 @@ def test_server_create_with_block_device_mapping_min_input(self): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.volume_client.volumes.get.assert_called_once_with( + self.volume.name + ) + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[ + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'device_name': 'vdf', 'uuid': self.volume.id, 'destination_type': 'volume', 'source_type': 'volume', - } + }, ], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_mapping_default_input(self): + self.volume_client.volumes.get.return_value = self.volume + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'vdf=' + self.volume.name + ':::', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_device_mapping', @@ -3124,57 +3052,56 @@ def test_server_create_with_block_device_mapping_default_input(self): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.volume_client.volumes.get.assert_called_once_with( + self.volume.name + ) + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[ + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'device_name': 'vdf', 'uuid': self.volume.id, 'destination_type': 'volume', 'source_type': 'volume', - } + }, ], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_mapping_full_input(self): + self.volume_client.volumes.get.return_value = self.volume + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'vde=' + self.volume.name + ':volume:3:true', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_device_mapping', @@ -3190,26 +3117,29 @@ def test_server_create_with_block_device_mapping_full_input(self): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.volume_client.volumes.get.assert_called_once_with( + self.volume.name + ) + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[ + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'device_name': 'vde', 'uuid': self.volume.id, @@ -3217,39 +3147,36 @@ def test_server_create_with_block_device_mapping_full_input(self): 'source_type': 'volume', 'delete_on_termination': 'true', 'volume_size': '3', - } + }, ], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_mapping_snapshot(self): + self.snapshot = volume_fakes.create_one_snapshot() + self.volume_client.volume_snapshots.get.return_value = self.snapshot + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', - 'vds=' + self.volume.name + ':snapshot:5:true', - self.new_server.name, + 'vds=' + self.snapshot.name + ':snapshot:5:true', + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_device_mapping', [ { 'device_name': 'vds', - 'uuid': self.volume.name, + 'uuid': self.snapshot.name, 'source_type': 'snapshot', 'volume_size': '5', 'destination_type': 'volume', @@ -3258,26 +3185,29 @@ def test_server_create_with_block_device_mapping_snapshot(self): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.volume_client.volume_snapshots.get.assert_called_once_with( + self.snapshot.name + ) + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[ + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'device_name': 'vds', 'uuid': self.snapshot.id, @@ -3285,34 +3215,30 @@ def test_server_create_with_block_device_mapping_snapshot(self): 'source_type': 'snapshot', 'delete_on_termination': 'true', 'volume_size': '5', - } + }, ], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_mapping_multiple(self): + self.volume_client.volumes.get.return_value = self.volume + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'vdb=' + self.volume.name + ':::false', '--block-device-mapping', 'vdc=' + self.volume.name + ':::true', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_device_mapping', @@ -3334,26 +3260,29 @@ def test_server_create_with_block_device_mapping_multiple(self): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.volume_client.volumes.get.assert_has_calls( + [mock.call(self.volume.name)] * 2 + ) + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[ + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'device_name': 'vdb', 'uuid': self.volume.id, @@ -3369,13 +3298,7 @@ def test_server_create_with_block_device_mapping_multiple(self): 'delete_on_termination': 'true', }, ], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) @@ -3385,56 +3308,68 @@ def test_server_create_with_block_device_mapping_invalid_format(self): # block device mapping don't contain equal sign "=" arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'not_contain_equal_sign', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn( + 'argument --block-device-mapping: Invalid argument ', str(exc) + ) + self.compute_sdk_client.create_server.assert_not_called() # block device mapping don't contain device name "=uuid:::true" arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', '=uuid:::true', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn( + 'argument --block-device-mapping: Invalid argument ', str(exc) + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_block_device_mapping_no_uuid(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'vdb=', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn( + 'argument --block-device-mapping: Invalid argument ', str(exc) + ) + self.compute_sdk_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 @@ -3449,14 +3384,14 @@ def test_server_create_volume_boot_from_volume_conflict(self): 'volume1', '--boot-from-volume', '1', - self.new_server.name, + self.server.name, ] verifylist = [ ('flavor', self.flavor.id), ('volume', 'volume1'), ('boot_from_volume', 1), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -3467,6 +3402,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() def test_server_create_boot_from_volume_no_image(self): # Test --boot-from-volume option without --image or @@ -3476,218 +3412,202 @@ def test_server_create_boot_from_volume_no_image(self): self.flavor.id, '--boot-from-volume', '1', - self.new_server.name, + self.server.name, ] verifylist = [ ('flavor', self.flavor.id), ('boot_from_volume', 1), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) ex = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - # Assert it is the error we expect. self.assertIn( 'An image (--image or --image-property) is required ' 'to support --boot-from-volume option', str(ex), ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_image_property(self): + image = image_fakes.create_one_image({'hypervisor_type': 'qemu'}) + self.image_client.images.return_value = [image] + arglist = [ '--image-property', 'hypervisor_type=qemu', '--flavor', - 'flavor1', - self.new_server.name, + self.flavor.id, + self.server.name, ] verifylist = [ ('image_properties', {'hypervisor_type': 'qemu'}), - ('flavor', 'flavor1'), + ('flavor', self.flavor.id), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - # create a image_info as the side_effect of the fake image_list() - image_info = { - 'hypervisor_type': 'qemu', - } - - _image = image_fakes.create_one_image(image_info) - self.image_client.images.return_value = [_image] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - files={}, - reservation_id=None, + self.image_client.images.assert_called_once_with() + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - meta=None, - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, _image, self.flavor, **kwargs + networks=[], + block_device_mapping=[ + { + 'uuid': 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_image_property_multi(self): + image = image_fakes.create_one_image( + {'hypervisor_type': 'qemu', 'hw_disk_bus': 'ide'} + ) + self.image_client.images.return_value = [image] + arglist = [ '--image-property', 'hypervisor_type=qemu', '--image-property', 'hw_disk_bus=ide', '--flavor', - 'flavor1', - self.new_server.name, + self.flavor.id, + self.server.name, ] verifylist = [ ( 'image_properties', {'hypervisor_type': 'qemu', 'hw_disk_bus': 'ide'}, ), - ('flavor', 'flavor1'), + ('flavor', self.flavor.id), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - # create a image_info as the side_effect of the fake image_list() - image_info = { - 'hypervisor_type': 'qemu', - 'hw_disk_bus': 'ide', - } - _image = image_fakes.create_one_image(image_info) - self.image_client.images.return_value = [_image] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - files={}, - reservation_id=None, + self.image_client.images.assert_called_once_with() + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - meta=None, - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, _image, self.flavor, **kwargs + networks=[], + block_device_mapping=[ + { + 'uuid': 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_image_property_missed(self): + image = image_fakes.create_one_image( + {'hypervisor_type': 'qemu', 'hw_disk_bus': 'ide'} + ) + self.image_client.images.return_value = [image] + arglist = [ '--image-property', 'hypervisor_type=qemu', + # note the mismatch in the 'hw_disk_bus' property '--image-property', 'hw_disk_bus=virtio', '--flavor', - 'flavor1', - self.new_server.name, + self.flavor.id, + self.server.name, ] verifylist = [ ( 'image_properties', {'hypervisor_type': 'qemu', 'hw_disk_bus': 'virtio'}, ), - ('flavor', 'flavor1'), + ('flavor', self.flavor.id), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - # create a image_info as the side_effect of the fake image_list() - image_info = { - 'hypervisor_type': 'qemu', - 'hw_disk_bus': 'ide', - } - - _image = image_fakes.create_one_image(image_info) - self.image_client.images.return_value = [_image] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + 'No images match the property expected by --image-property', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_image_property_with_image_list(self): + target_image = image_fakes.create_one_image( + { + 'properties': { + 'owner_specified.openstack.object': 'image/cirros' + } + } + ) + another_image = image_fakes.create_one_image() + self.image_client.images.return_value = [target_image, another_image] + arglist = [ '--image-property', 'owner_specified.openstack.object=image/cirros', '--flavor', - 'flavor1', - self.new_server.name, + self.flavor.id, + self.server.name, ] - verifylist = [ ( 'image_properties', {'owner_specified.openstack.object': 'image/cirros'}, ), - ('flavor', 'flavor1'), - ('server_name', self.new_server.name), + ('flavor', self.flavor.id), + ('server_name', self.server.name), ] - # create a image_info as the side_effect of the fake image_list() - image_info = { - 'properties': {'owner_specified.openstack.object': 'image/cirros'} - } - - target_image = image_fakes.create_one_image(image_info) - another_image = image_fakes.create_one_image({}) - self.image_client.images.return_value = [target_image, another_image] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - files={}, - reservation_id=None, + self.image_client.images.assert_called_once_with() + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=target_image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - meta=None, - scheduler_hints={}, - config_drive=None, - ) - - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, target_image, self.flavor, **kwargs + networks=[], + block_device_mapping=[ + { + 'uuid': target_image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) @@ -3700,7 +3620,7 @@ def test_server_create_no_boot_device(self): block_device, '--flavor', self.flavor.id, - self.new_server.name, + self.server.name, ] verifylist = [ ('image', None), @@ -3715,7 +3635,7 @@ def test_server_create_no_boot_device(self): }, ], ), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) exc = self.assertRaises( @@ -3728,41 +3648,42 @@ 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() def test_server_create_with_swap(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--swap', '1024', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ('swap', 1024), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + 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, + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'boot_index': -1, 'source_type': 'blank', @@ -3770,15 +3691,9 @@ def test_server_create_with_swap(self): 'guest_format': 'swap', 'volume_size': 1024, 'delete_on_termination': True, - } + }, ], - 'nics': [], - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) @@ -3787,37 +3702,37 @@ def test_server_create_with_swap(self): def test_server_create_with_ephemeral(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--ephemeral', 'size=1024,format=ext4', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ('ephemerals', [{'size': '1024', 'format': 'ext4'}]), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + 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, + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'boot_index': -1, 'source_type': 'blank', @@ -3825,15 +3740,9 @@ def test_server_create_with_ephemeral(self): 'guest_format': 'ext4', 'volume_size': '1024', 'delete_on_termination': True, - } + }, ], - 'nics': [], - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) @@ -3842,75 +3751,83 @@ def test_server_create_with_ephemeral(self): def test_server_create_with_ephemeral_missing_key(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--ephemeral', 'format=ext3', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn('Argument parse failed', str(exc)) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_ephemeral_invalid_key(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--ephemeral', 'size=1024,foo=bar', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn('Argument parse failed', str(exc)) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_invalid_hint(self): # Not a key-value pair arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--hint', 'a0cf03a5-d921-4877-bb5c-86d26cf818e1', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn('Argument parse failed', str(exc)) + self.compute_sdk_client.create_server.assert_not_called() # Empty key arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--hint', '=a0cf03a5-d921-4877-bb5c-86d26cf818e1', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn('Argument parse failed', str(exc)) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_description(self): # Description is supported for nova api version 2.19 or above @@ -3918,51 +3835,45 @@ def test_server_create_with_description(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--description', 'description1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('description', 'description1'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - scheduler_hints={}, - config_drive=None, + networks=[], description='description1', - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + 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) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) def test_server_create_with_description_pre_v219(self): # Description is not supported for nova api version below 2.19 @@ -3970,160 +3881,149 @@ def test_server_create_with_description_pre_v219(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--description', 'description1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('description', 'description1'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_tag(self): self.set_compute_api_version('2.52') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--tag', 'tag1', '--tag', 'tag2', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('tags', ['tag1', 'tag2']), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'block_device_mapping_v2': [], - 'admin_pass': None, - 'nics': 'auto', - 'scheduler_hints': {}, - 'config_drive': None, - 'tags': ['tag1', 'tag2'], - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + 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, + networks='auto', + tags=['tag1', 'tag2'], + 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) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) def test_server_create_with_tag_pre_v252(self): self.set_compute_api_version('2.51') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--tag', 'tag1', '--tag', 'tag2', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('tags', ['tag1', 'tag2']), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - ex = self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn( - '--os-compute-api-version 2.52 or greater is required', str(ex) + '--os-compute-api-version 2.52 or greater is required', str(exc) ) + self.compute_sdk_client.create_server.assert_not_called() - def test_server_create_with_host_v274(self): + def test_server_create_with_host(self): # Explicit host is supported for nova api version 2.74 or above self.set_compute_api_version('2.74') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--host', 'host1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('host', 'host1'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, + networks='auto', host='host1', + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs - ) - self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) def test_server_create_with_host_pre_v274(self): # Host is not supported for nova api version below 2.74 @@ -4131,78 +4031,75 @@ def test_server_create_with_host_pre_v274(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--host', 'host1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('host', 'host1'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + '--os-compute-api-version 2.74 or greater is required', str(exc) + ) + self.compute_sdk_client.create_server.assert_not_called() - def test_server_create_with_hypervisor_hostname_v274(self): + def test_server_create_with_hypervisor_hostname(self): # Explicit hypervisor_hostname is supported for nova api version # 2.74 or above self.set_compute_api_version('2.74') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--hypervisor-hostname', 'node1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('hypervisor_hostname', 'node1'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, + networks='auto', hypervisor_hostname='node1', + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs - ) - self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) def test_server_create_with_hypervisor_hostname_pre_v274(self): # Hypervisor_hostname is not supported for nova api version below 2.74 @@ -4210,315 +4107,281 @@ def test_server_create_with_hypervisor_hostname_pre_v274(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--hypervisor-hostname', 'node1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('hypervisor_hostname', 'node1'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( - exceptions.CommandError, self.cmd.take_action, parsed_args - ) - - def test_server_create_with_host_and_hypervisor_hostname_v274(self): - # Explicit host and hypervisor_hostname is supported for nova api - # version 2.74 or above - self.set_compute_api_version('2.74') - arglist = [ - '--image', - 'image1', - '--flavor', - 'flavor1', - '--host', - 'host1', - '--hypervisor-hostname', - 'node1', - self.new_server.name, - ] - verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), - ('host', 'host1'), - ('hypervisor_hostname', 'node1'), - ('config_drive', False), - ('server_name', self.new_server.name), - ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, - min_count=1, - max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, - host='host1', - hypervisor_hostname='node1', + exc = self.assertRaises( + exceptions.CommandError, self.cmd.take_action, parsed_args ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + self.assertIn( + '--os-compute-api-version 2.74 or greater is required', str(exc) ) + self.compute_sdk_client.create_server.assert_not_called() - self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist(), data) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) - - def test_server_create_with_hostname_v290(self): + def test_server_create_with_hostname(self): self.set_compute_api_version('2.90') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--hostname', 'hostname', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('hostname', 'hostname'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, - self.image, - self.flavor, - meta=None, - files={}, - reservation_id=None, + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, + networks='auto', hostname='hostname', + 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) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) def test_server_create_with_hostname_pre_v290(self): self.set_compute_api_version('2.89') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--hostname', 'hostname', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('hostname', 'hostname'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + '--os-compute-api-version 2.90 or greater is required', str(exc) + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert(self): self.set_compute_api_version('2.63') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--trusted-image-cert', 'foo', '--trusted-image-cert', 'bar', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('config_drive', False), ('trusted_image_certs', ['foo', 'bar']), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + 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=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, + networks='auto', trusted_image_certificates=['foo', 'bar'], - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + 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) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) - def test_server_create_with_trusted_image_cert_prev263(self): + def test_server_create_with_trusted_image_cert_pre_v263(self): self.set_compute_api_version('2.62') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--trusted-image-cert', 'foo', '--trusted-image-cert', 'bar', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('config_drive', False), ('trusted_image_certs', ['foo', 'bar']), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + '--os-compute-api-version 2.63 or greater is required', str(exc) + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert_from_volume(self): self.set_compute_api_version('2.63') + arglist = [ '--volume', 'volume1', '--flavor', - 'flavor1', + self.flavor.id, '--trusted-image-cert', 'foo', '--trusted-image-cert', 'bar', - self.new_server.name, + self.server.name, ] verifylist = [ ('volume', 'volume1'), - ('flavor', 'flavor1'), + ('flavor', self.flavor.id), ('config_drive', False), ('trusted_image_certs', ['foo', 'bar']), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + '--trusted-image-cert option is only supported for servers booted ' + 'directly from images', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert_from_snapshot(self): self.set_compute_api_version('2.63') + arglist = [ '--snapshot', 'snapshot1', '--flavor', - 'flavor1', + self.flavor.id, '--trusted-image-cert', 'foo', '--trusted-image-cert', 'bar', - self.new_server.name, + self.server.name, ] verifylist = [ ('snapshot', 'snapshot1'), - ('flavor', 'flavor1'), + ('flavor', self.flavor.id), ('config_drive', False), ('trusted_image_certs', ['foo', 'bar']), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + '--trusted-image-cert option is only supported for servers booted ' + 'directly from images', + str(exc), + ) + self.compute_sdk_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') + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--boot-from-volume', '1', '--trusted-image-cert', 'foo', '--trusted-image-cert', 'bar', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('boot_from_volume', 1), ('config_drive', False), ('trusted_image_certs', ['foo', 'bar']), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + '--trusted-image-cert option is only supported for servers booted ' + 'directly from images', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() class TestServerDelete(compute_fakes.TestComputev2): @@ -8983,7 +8846,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): - servers = self.setup_servers_mock(count=1) + servers = self.setup_sdk_servers_mock(count=1) arglist = [ servers[0].id, @@ -9586,7 +9449,7 @@ def test_prep_server_detail(self): 'OS-SRV-USG:terminated_at': None, 'accessIPv4': None, 'accessIPv6': None, - 'addresses': format_columns.DictListColumn(_server.addresses), + 'addresses': server.AddressesColumn(_server.addresses), 'config_drive': None, 'created': None, 'description': None, @@ -9673,7 +9536,7 @@ def test_prep_server_detail_v247(self): 'OS-SRV-USG:terminated_at': None, 'accessIPv4': None, 'accessIPv6': None, - 'addresses': format_columns.DictListColumn(_server.addresses), + 'addresses': server.AddressesColumn(_server.addresses), 'config_drive': None, 'created': None, 'description': None, diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index 4874bf856..28af24287 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -32,8 +32,9 @@ from openstack.network.v2 import network_segment_range as _segment_range from openstack.network.v2 import port as _port 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 -from openstack.network.v2 import service_profile as _flavor_profile +from openstack.network.v2 import service_profile as _service_profile from openstack.network.v2 import trunk as _trunk from openstackclient.tests.unit import fakes @@ -1943,11 +1944,44 @@ def get_network_rbacs(rbac_policies=None, count=2): return mock.Mock(side_effect=rbac_policies) +def create_one_security_group(attrs=None): + """Create a security group.""" + attrs = attrs or {} + + security_group_attrs = { + 'name': 'security-group-name-' + uuid.uuid4().hex, + 'id': 'security-group-id-' + uuid.uuid4().hex, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'description': 'security-group-description-' + uuid.uuid4().hex, + 'location': 'MUNCHMUNCHMUNCH', + } + + security_group_attrs.update(attrs) + + security_group = _security_group.SecurityGroup(**security_group_attrs) + + return security_group + + +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 + """ + security_groups = [] + for i in range(0, count): + security_groups.append(create_one_security_group(attrs)) + + return security_groups + + def create_one_service_profile(attrs=None): - """Create flavor profile.""" + """Create service profile.""" attrs = attrs or {} - flavor_profile_attrs = { + service_profile_attrs = { 'id': 'flavor-profile-id' + uuid.uuid4().hex, 'description': 'flavor-profile-description-' + uuid.uuid4().hex, 'project_id': 'project-id-' + uuid.uuid4().hex, @@ -1957,20 +1991,20 @@ def create_one_service_profile(attrs=None): 'location': 'MUNCHMUNCHMUNCH', } - flavor_profile_attrs.update(attrs) + service_profile_attrs.update(attrs) - flavor_profile = _flavor_profile.ServiceProfile(**flavor_profile_attrs) + flavor_profile = _service_profile.ServiceProfile(**service_profile_attrs) return flavor_profile def create_service_profile(attrs=None, count=2): - """Create multiple flavor profiles.""" + """Create multiple service profiles.""" - flavor_profiles = [] + service_profiles = [] for i in range(0, count): - flavor_profiles.append(create_one_service_profile(attrs)) - return flavor_profiles + service_profiles.append(create_one_service_profile(attrs)) + return service_profiles def get_service_profile(flavor_profile=None, count=2): From 209f8e9e171981f2714f3ac63e8ad947409a60d6 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 16 May 2024 12:01:44 +0100 Subject: [PATCH 155/403] network: Replace use of in-tree API client None of these are actually supported by openstacksdk (intentionally so) so we add our own manual implementations. Change-Id: Ifd24f04ae4d1e56e0ce5ba0afe63828403bb7a6f Signed-off-by: Stephen Finucane --- openstackclient/api/compute_v2.py | 815 +++++---------- openstackclient/compute/client.py | 20 - openstackclient/compute/v2/server.py | 11 +- openstackclient/network/common.py | 7 +- openstackclient/network/v2/floating_ip.py | 14 +- .../network/v2/floating_ip_pool.py | 21 +- openstackclient/network/v2/network.py | 11 +- openstackclient/network/v2/security_group.py | 30 +- .../network/v2/security_group_rule.py | 33 +- .../tests/unit/api/test_compute_v2.py | 961 ++++++------------ .../tests/unit/compute/v2/fakes.py | 6 - .../tests/unit/compute/v2/test_server.py | 78 +- .../tests/unit/network/test_common.py | 4 +- .../network/v2/test_floating_ip_compute.py | 70 +- .../v2/test_floating_ip_pool_compute.py | 9 +- .../unit/network/v2/test_network_compute.py | 123 +-- .../network/v2/test_security_group_compute.py | 104 +- .../v2/test_security_group_rule_compute.py | 94 +- 18 files changed, 913 insertions(+), 1498 deletions(-) diff --git a/openstackclient/api/compute_v2.py b/openstackclient/api/compute_v2.py index 4fcf3f134..95521eae9 100644 --- a/openstackclient/api/compute_v2.py +++ b/openstackclient/api/compute_v2.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. -# """Compute v2 API Library @@ -19,571 +18,49 @@ import http -from keystoneauth1 import exceptions as ksa_exceptions from openstack import exceptions as sdk_exceptions -from osc_lib.api import api from osc_lib import exceptions -from osc_lib.i18n import _ - - -# TODO(dtroyer): Migrate this to osc-lib -class InvalidValue(Exception): - """An argument value is not valid: wrong type, out of range, etc""" - - message = "Supplied value is not valid" - - -class APIv2(api.BaseAPI): - """Compute v2 API""" - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - # Overrides - - def _check_integer(self, value, msg=None): - """Attempt to convert value to an integer - - Raises InvalidValue on failure - - :param value: - Convert this to an integer. None is converted to 0 (zero). - :param msg: - An alternate message for the exception, must include exactly - one substitution to receive the attempted value. - """ - - if value is None: - return 0 - - try: - value = int(value) - except (TypeError, ValueError): - if not msg: - msg = _("%s is not an integer") % value - raise InvalidValue(msg) - return value - - # TODO(dtroyer): Override find() until these fixes get into an osc-lib - # minimum release - def find( - self, - path, - value=None, - attr=None, - ): - """Find a single resource by name or ID - - :param string path: - The API-specific portion of the URL path - :param string value: - search expression (required, really) - :param string attr: - name of attribute for secondary search - """ - - try: - ret = self._request('GET', f"/{path}/{value}").json() - if isinstance(ret, dict): - # strip off the enclosing dict - key = list(ret.keys())[0] - ret = ret[key] - except ( - ksa_exceptions.NotFound, - ksa_exceptions.BadRequest, - ): - kwargs = {attr: value} - try: - ret = self.find_one(path, **kwargs) - except ksa_exceptions.NotFound: - msg = _("%s not found") % value - raise exceptions.NotFound(msg) - - return ret - - # Floating IPs - - def floating_ip_add( - self, - server, - address, - fixed_address=None, - ): - """Add a floating IP to a server - - :param server: - The :class:`Server` (or its ID) to add an IP to. - :param address: - The FloatingIP or string floating address to add. - :param fixed_address: - The FixedIP the floatingIP should be associated with (optional) - """ - - url = '/servers' - - server = self.find( - url, - attr='name', - value=server, - ) - - address = address.ip if hasattr(address, 'ip') else address - if fixed_address: - if hasattr(fixed_address, 'ip'): - fixed_address = fixed_address.ip - - body = { - 'address': address, - 'fixed_address': fixed_address, - } - else: - body = { - 'address': address, - } - - return self._request( - "POST", - "/{}/{}/action".format(url, server['id']), - json={'addFloatingIp': body}, - ) - - def floating_ip_create( - self, - pool=None, - ): - """Create a new floating ip - - https://docs.openstack.org/api-ref/compute/#create-allocate-floating-ip-address - - :param pool: Name of floating IP pool - """ - - url = "/os-floating-ips" - - try: - return self.create( - url, - json={'pool': pool}, - )['floating_ip'] - except ( - ksa_exceptions.NotFound, - ksa_exceptions.BadRequest, - ): - msg = _("%s not found") % pool - raise exceptions.NotFound(msg) - - def floating_ip_delete( - self, - floating_ip_id=None, - ): - """Delete a floating IP - - https://docs.openstack.org/api-ref/compute/#delete-deallocate-floating-ip-address - - :param string floating_ip_id: - Floating IP ID - """ - - url = "/os-floating-ips" - - if floating_ip_id is not None: - return self.delete(f'/{url}/{floating_ip_id}') - - return None - - def floating_ip_find( - self, - floating_ip=None, - ): - """Return a security group given name or ID - - https://docs.openstack.org/api-ref/compute/#list-floating-ip-addresses - - :param string floating_ip: - Floating IP address - :returns: A dict of the floating IP attributes - """ - - url = "/os-floating-ips" - - return self.find( - url, - attr='ip', - value=floating_ip, - ) - - def floating_ip_list( - self, - ): - """Get floating IPs - - https://docs.openstack.org/api-ref/compute/#show-floating-ip-address-details - - :returns: - list of floating IPs - """ - - url = "/os-floating-ips" - - return self.list(url)["floating_ips"] - - def floating_ip_remove( - self, - server, - address, - ): - """Remove a floating IP from a server - - :param server: - The :class:`Server` (or its ID) to add an IP to. - :param address: - The FloatingIP or string floating address to add. - """ - - url = '/servers' - - server = self.find( - url, - attr='name', - value=server, - ) - - address = address.ip if hasattr(address, 'ip') else address - body = { - 'address': address, - } - - return self._request( - "POST", - "/{}/{}/action".format(url, server['id']), - json={'removeFloatingIp': body}, - ) - - # Floating IP Pools - - def floating_ip_pool_list( - self, - ): - """Get floating IP pools - - https://docs.openstack.org/api-ref/compute/?expanded=#list-floating-ip-pools - - :returns: - list of floating IP pools - """ - - url = "/os-floating-ip-pools" - - return self.list(url)["floating_ip_pools"] - - # Networks - - def network_create( - self, - name=None, - subnet=None, - share_subnet=None, - ): - """Create a new network - - https://docs.openstack.org/api-ref/compute/#create-network - :param string name: - Network label (required) - :param integer subnet: - Subnet for IPv4 fixed addresses in CIDR notation (required) - :param integer share_subnet: - Shared subnet between projects, True or False - :returns: A dict of the network attributes - """ - url = "/os-networks" +# security groups - params = { - 'label': name, - 'cidr': subnet, - } - if share_subnet is not None: - params['share_address'] = share_subnet - return self.create( - url, - json={'network': params}, - )['network'] +def create_security_group(compute_client, name=None, description=None): + """Create a new security group - def network_delete( - self, - network=None, - ): - """Delete a network + https://docs.openstack.org/api-ref/compute/#create-security-group - https://docs.openstack.org/api-ref/compute/#delete-network - - :param string network: - Network name or ID - """ - - url = "/os-networks" - - network = self.find( - url, - attr='label', - value=network, - )['id'] - if network is not None: - return self.delete(f'/{url}/{network}') + :param compute_client: A compute client + :param str name: Security group name + :param str description: Security group description + :returns: A security group object + """ + data = { + 'name': name, + 'description': description, + } + response = compute_client.post( + '/os-security-groups', data=data, microversion='2.1' + ) + sdk_exceptions.raise_from_response(response) + return response.json()['security_group'] - return None - - def network_find( - self, - network=None, - ): - """Return a network given name or ID - https://docs.openstack.org/api-ref/compute/#show-network-details - - :param string network: - Network name or ID - :returns: A dict of the network attributes - """ +def list_security_groups(compute_client, all_projects=None): + """Get all security groups - url = "/os-networks" - - return self.find( - url, - attr='label', - value=network, - ) + https://docs.openstack.org/api-ref/compute/#list-security-groups - def network_list( - self, - ): - """Get networks - - https://docs.openstack.org/api-ref/compute/#list-networks - - :returns: - list of networks - """ - - url = "/os-networks" - - return self.list(url)["networks"] - - # Security Groups - - def security_group_create( - self, - name=None, - description=None, - ): - """Create a new security group - - https://docs.openstack.org/api-ref/compute/#create-security-group - - :param string name: - Security group name - :param integer description: - Security group description - """ - - url = "/os-security-groups" - - params = { - 'name': name, - 'description': description, - } - - return self.create( - url, - json={'security_group': params}, - )['security_group'] - - def security_group_delete( - self, - security_group=None, - ): - """Delete a security group - - https://docs.openstack.org/api-ref/compute/#delete-security-group - - :param string security_group: - Security group name or ID - """ - - url = "/os-security-groups" - - security_group = self.find( - url, - attr='name', - value=security_group, - )['id'] - if security_group is not None: - return self.delete(f'/{url}/{security_group}') - - return None - - def security_group_find( - self, - security_group=None, - ): - """Return a security group given name or ID - - https://docs.openstack.org/api-ref/compute/#show-security-group-details - - :param string security_group: - Security group name or ID - :returns: A dict of the security group attributes - """ - - url = "/os-security-groups" - - return self.find( - url, - attr='name', - value=security_group, - ) - - def security_group_list( - self, - limit=None, - marker=None, - search_opts=None, - ): - """Get security groups - - https://docs.openstack.org/api-ref/compute/#list-security-groups - - :param integer limit: - query return count limit - :param string marker: - query marker - :param search_opts: - (undocumented) Search filter dict - all_tenants: True|False - return all projects - :returns: - list of security groups names - """ - - params = {} - if search_opts is not None: - params = {k: v for (k, v) in search_opts.items() if v} - if limit: - params['limit'] = limit - if marker: - params['offset'] = marker - - url = "/os-security-groups" - return self.list(url, **params)["security_groups"] - - def security_group_set( - self, - security_group=None, - # name=None, - # description=None, - **params, - ): - """Update a security group - - https://docs.openstack.org/api-ref/compute/#update-security-group - - :param string security_group: - Security group name or ID - - TODO(dtroyer): Create an update method in osc-lib - """ - - # Short-circuit no-op - if params is None: - return None - - url = "/os-security-groups" - - security_group = self.find( - url, - attr='name', - value=security_group, - ) - if security_group is not None: - for k, v in params.items(): - # Only set a value if it is already present - if k in security_group: - security_group[k] = v - return self._request( - "PUT", - "/{}/{}".format(url, security_group['id']), - json={'security_group': security_group}, - ).json()['security_group'] - return None - - # Security Group Rules - - def security_group_rule_create( - self, - security_group_id=None, - ip_protocol=None, - from_port=None, - to_port=None, - remote_ip=None, - remote_group=None, - ): - """Create a new security group rule - - https://docs.openstack.org/api-ref/compute/#create-security-group-rule - - :param string security_group_id: - Security group ID - :param ip_protocol: - IP protocol, 'tcp', 'udp' or 'icmp' - :param from_port: - Source port - :param to_port: - Destination port - :param remote_ip: - Source IP address in CIDR notation - :param remote_group: - Remote security group - """ - - url = "/os-security-group-rules" - - if ip_protocol.lower() not in ['icmp', 'tcp', 'udp']: - raise InvalidValue( - "%(s) is not one of 'icmp', 'tcp', or 'udp'" % ip_protocol - ) - - params = { - 'parent_group_id': security_group_id, - 'ip_protocol': ip_protocol, - 'from_port': self._check_integer(from_port), - 'to_port': self._check_integer(to_port), - 'cidr': remote_ip, - 'group_id': remote_group, - } - - return self.create( - url, - json={'security_group_rule': params}, - )['security_group_rule'] - - def security_group_rule_delete( - self, - security_group_rule_id=None, - ): - """Delete a security group rule - - https://docs.openstack.org/api-ref/compute/#delete-security-group-rule - - :param string security_group_rule_id: - Security group rule ID - """ - - url = "/os-security-group-rules" - if security_group_rule_id is not None: - return self.delete(f'/{url}/{security_group_rule_id}') - - return None + :param compute_client: A compute client + :param bool all_projects: If true, list from all projects + :returns: A list of security group objects + """ + url = '/os-security-groups' + if all_projects is not None: + url += f'?all_tenants={all_projects}' + response = compute_client.get(url, microversion='2.1') + sdk_exceptions.raise_from_response(response) + return response.json()['security_groups'] def find_security_group(compute_client, name_or_id): @@ -623,6 +100,145 @@ def find_security_group(compute_client, name_or_id): return found +def update_security_group( + compute_client, security_group_id, name=None, description=None +): + """Update an existing security group + + https://docs.openstack.org/api-ref/compute/#update-security-group + + :param compute_client: A compute client + :param str security_group_id: The ID of the security group to update + :param str name: Security group name + :param str description: Security group description + :returns: A security group object + """ + data = {} + if name: + data['name'] = name + if description: + data['description'] = description + response = compute_client.put( + f'/os-security-groups/{security_group_id}', + data=data, + microversion='2.1', + ) + sdk_exceptions.raise_from_response(response) + return response.json()['security_group'] + + +def delete_security_group(compute_client, security_group_id=None): + """Delete a security group + + https://docs.openstack.org/api-ref/compute/#delete-security-group + + :param compute_client: A compute client + :param str security_group_id: Security group ID + :returns: None + """ + response = compute_client.delete( + f'/os-security-groups/{security_group_id}', microversion='2.1' + ) + sdk_exceptions.raise_from_response(response) + + +# security group rules + + +def create_security_group_rule( + compute_client, + security_group_id=None, + ip_protocol=None, + from_port=None, + to_port=None, + remote_ip=None, + remote_group=None, +): + """Create a new security group rule + + https://docs.openstack.org/api-ref/compute/#create-security-group-rule + + :param compute_client: A compute client + :param str security_group_id: Security group ID + :param str ip_protocol: IP protocol, 'tcp', 'udp' or 'icmp' + :param int from_port: Source port + :param int to_port: Destination port + :param str remote_ip: Source IP address in CIDR notation + :param str remote_group: Remote security group + :returns: A security group object + """ + data = { + 'parent_group_id': security_group_id, + 'ip_protocol': ip_protocol, + 'from_port': from_port, + 'to_port': to_port, + 'cidr': remote_ip, + 'group_id': remote_group, + } + response = compute_client.post( + '/os-security-group-rules', data=data, microversion='2.1' + ) + sdk_exceptions.raise_from_response(response) + return response.json()['security_group_rule'] + + +def delete_security_group_rule(compute_client, security_group_rule_id=None): + """Delete a security group rule + + https://docs.openstack.org/api-ref/compute/#delete-security-group-rule + + :param compute_client: A compute client + :param str security_group_rule_id: Security group rule ID + :returns: None + """ + response = compute_client.delete( + f'/os-security-group-rules/{security_group_rule_id}', + microversion='2.1', + ) + sdk_exceptions.raise_from_response(response) + + +# networks + + +def create_network(compute_client, name, subnet, share_subnet=None): + """Create a new network + + https://docs.openstack.org/api-ref/compute/#create-network + + :param compute_client: A compute client + :param str name: Network label + :param int subnet: Subnet for IPv4 fixed addresses in CIDR notation + :param bool share_subnet: Shared subnet between projects + :returns: A network object + """ + data = { + 'label': name, + 'cidr': subnet, + } + if share_subnet is not None: + data['share_address'] = share_subnet + + response = compute_client.post( + '/os-networks', data=data, microversion='2.1' + ) + sdk_exceptions.raise_from_response(response) + return response.json()['network'] + + +def list_networks(compute_client): + """Get all networks + + https://docs.openstack.org/api-ref/compute/#list-networks + + :param compute_client: A compute client + :returns: A list of network objects + """ + response = compute_client.get('/os-networks', microversion='2.1') + sdk_exceptions.raise_from_response(response) + return response.json()['networks'] + + def find_network(compute_client, name_or_id): """Find the ID for a given network name or ID @@ -658,3 +274,94 @@ def find_network(compute_client, name_or_id): raise exceptions.NotFound(f'{name_or_id} not found') return found + + +def delete_network(compute_client, network_id): + """Delete a network + + https://docs.openstack.org/api-ref/compute/#delete-network + + :param compute_client: A compute client + :param string network_id: The network ID + :returns: None + """ + response = compute_client.delete( + f'/os-networks/{network_id}', microversion='2.1' + ) + sdk_exceptions.raise_from_response(response) + + +# floating ips + + +def create_floating_ip(compute_client, network): + """Create a new floating ip + + https://docs.openstack.org/api-ref/compute/#create-allocate-floating-ip-address + + :param network: Name of floating IP pool + :returns: A floating IP object + """ + response = compute_client.post( + '/os-floating-ips', data={'pool': network}, microversion='2.1' + ) + sdk_exceptions.raise_from_response(response) + return response.json()['floating_ip'] + + +def list_floating_ips(compute_client): + """Get all floating IPs + + https://docs.openstack.org/api-ref/compute/#list-floating-ip-addresses + + :returns: A list of floating IP objects + """ + response = compute_client.get('/os-floating-ips', microversion='2.1') + sdk_exceptions.raise_from_response(response) + return response.json()['floating_ips'] + + +def get_floating_ip(compute_client, floating_ip_id): + """Get a floating IP + + https://docs.openstack.org/api-ref/compute/#show-floating-ip-address-details + + :param string floating_ip_id: The floating IP address + :returns: A floating IP object + """ + response = compute_client.get( + f'/os-floating-ips/{floating_ip_id}', microversion='2.1' + ) + sdk_exceptions.raise_from_response(response) + return response.json()['floating_ip'] + + +def delete_floating_ip(compute_client, floating_ip_id): + """Delete a floating IP + + https://docs.openstack.org/api-ref/compute/#delete-deallocate-floating-ip-address + + :param string floating_ip_id: The floating IP address + :returns: None + """ + response = compute_client.delete( + f'/os-floating-ips/{floating_ip_id}', microversion='2.1' + ) + sdk_exceptions.raise_from_response(response) + + +# floating ip pools + + +def list_floating_ip_pools(compute_client): + """Get all floating IP pools + + https://docs.openstack.org/api-ref/compute/#list-floating-ip-pools + + :param compute_client: A compute client + :returns: A list of floating IP pool objects + """ + response = compute_client.get('/os-floating-ip-pools', microversion='2.1') + sdk_exceptions.raise_from_response(response) + + return response.json()['floating_ip_pools'] diff --git a/openstackclient/compute/client.py b/openstackclient/compute/client.py index e833d53b8..157e0dddc 100644 --- a/openstackclient/compute/client.py +++ b/openstackclient/compute/client.py @@ -31,7 +31,6 @@ "2.1": "novaclient.client", } -COMPUTE_API_TYPE = 'compute' COMPUTE_API_VERSIONS = { '2': 'openstackclient.api.compute_v2.APIv2', } @@ -63,15 +62,6 @@ def make_client(instance): # fallback to use the max version of novaclient side. version = novaclient.API_MAX_VERSION - LOG.debug('Instantiating compute client for %s', version) - - compute_api = utils.get_client_class( - API_NAME, - version.ver_major, - COMPUTE_API_VERSIONS, - ) - LOG.debug('Instantiating compute api: %s', compute_api) - # Set client http_log_debug to True if verbosity level is high enough http_log_debug = utils.get_effective_log_level() <= logging.DEBUG @@ -94,16 +84,6 @@ def make_client(instance): **kwargs ) - client.api = compute_api( - session=instance.session, - service_type=COMPUTE_API_TYPE, - endpoint=instance.get_endpoint_for_service_type( - COMPUTE_API_TYPE, - region_name=instance.region_name, - interface=instance.interface, - ), - ) - return client diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 22ab4f11c..78ab16d72 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -552,8 +552,9 @@ def take_action_network(self, client, parsed_args): raise error def take_action_compute(self, client, parsed_args): - client.api.floating_ip_add( - parsed_args.server, + server = client.find_server(parsed_args.server, ignore_missing=False) + client.add_floating_ip_to_server( + server, parsed_args.ip_address, fixed_address=parsed_args.fixed_ip_address, ) @@ -3921,10 +3922,8 @@ def take_action_network(self, client, parsed_args): client.update_ip(obj, **attrs) def take_action_compute(self, client, parsed_args): - client.api.floating_ip_remove( - parsed_args.server, - parsed_args.ip_address, - ) + server = client.find_server(parsed_args.server, ignore_missing=False) + client.remove_floating_ip_from_server(server, parsed_args.ip_address) class RemovePort(command.Command): diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index 0dc291c92..4c01f90ef 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -161,7 +161,7 @@ def take_action(self, parsed_args): ) elif self.is_nova_network: return self.take_action_compute( - self.app.client_manager.compute, parsed_args + self.app.client_manager.sdk_connection.compute, parsed_args ) def take_action_network(self, client, parsed_args): @@ -210,7 +210,8 @@ def take_action(self, parsed_args): ) else: self.take_action_compute( - self.app.client_manager.compute, parsed_args + self.app.client_manager.sdk_connection.compute, + parsed_args, ) except Exception as e: msg = _( @@ -267,7 +268,7 @@ def take_action(self, parsed_args): ) else: return self.take_action_compute( - self.app.client_manager.compute, parsed_args + self.app.client_manager.sdk_connection.compute, parsed_args ) except openstack.exceptions.HttpException as exc: msg = _("Error while executing command: %s") % exc.message diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index fef7b7d27..301ae716d 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -9,18 +9,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. -# """IP Floating action implementations""" from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient.api import compute_v2 from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common - _formatters = { 'port_details': utils.format_dict, } @@ -200,7 +199,7 @@ def take_action_network(self, client, parsed_args): return (display_columns, data) def take_action_compute(self, client, parsed_args): - obj = client.api.floating_ip_create(parsed_args.network) + obj = compute_v2.create_floating_ip(client, parsed_args.network) columns = _get_columns(obj) data = utils.get_dict_properties(obj, columns) return (columns, data) @@ -230,7 +229,7 @@ def take_action_network(self, client, parsed_args): client.delete_ip(obj) def take_action_compute(self, client, parsed_args): - client.api.floating_ip_delete(self.r) + compute_v2.delete_floating_ip(client, self.r) class ListFloatingIP(common.NetworkAndComputeLister): @@ -421,8 +420,7 @@ def take_action_compute(self, client, parsed_args): 'Pool', ) - data = client.api.floating_ip_list() - + objs = compute_v2.list_floating_ips(client) return ( headers, ( @@ -431,7 +429,7 @@ def take_action_compute(self, client, parsed_args): columns, formatters={}, ) - for s in data + for s in objs ), ) @@ -538,7 +536,7 @@ def take_action_network(self, client, parsed_args): return (display_columns, data) def take_action_compute(self, client, parsed_args): - obj = client.api.floating_ip_find(parsed_args.floating_ip) + obj = compute_v2.get_floating_ip(client, parsed_args.floating_ip) columns = _get_columns(obj) data = utils.get_dict_properties(obj, columns) return (columns, data) diff --git a/openstackclient/network/v2/floating_ip_pool.py b/openstackclient/network/v2/floating_ip_pool.py index 43d7ac858..2030eb8ad 100644 --- a/openstackclient/network/v2/floating_ip_pool.py +++ b/openstackclient/network/v2/floating_ip_pool.py @@ -9,14 +9,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. -# """Floating IP Pool action implementations""" - from osc_lib import exceptions -from osc_lib import utils +from openstackclient.api import compute_v2 from openstackclient.i18n import _ from openstackclient.network import common @@ -33,15 +31,8 @@ def take_action_network(self, client, parsed_args): def take_action_compute(self, client, parsed_args): columns = ('Name',) - data = client.api.floating_ip_pool_list() - - return ( - columns, - ( - utils.get_dict_properties( - s, - columns, - ) - for s in data - ), - ) + data = [ + (x['name'],) for x in compute_v2.list_floating_ip_pools(client) + ] + + return (columns, data) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 86883b35f..de1910ea7 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.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. -# """Network action implementations""" @@ -18,6 +17,7 @@ from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient.api import compute_v2 from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -388,7 +388,7 @@ def take_action_network(self, client, parsed_args): def take_action_compute(self, client, parsed_args): attrs = _get_attrs_compute(self.app.client_manager, parsed_args) - obj = client.api.network_create(**attrs) + obj = compute_v2.create_network(client, **attrs) display_columns, columns = _get_columns_compute(obj) data = utils.get_dict_properties(obj, columns) return (display_columns, data) @@ -416,7 +416,8 @@ def take_action_network(self, client, parsed_args): client.delete_network(obj) def take_action_compute(self, client, parsed_args): - client.api.network_delete(self.r) + network = compute_v2.find_network(client, self.r) + compute_v2.delete_network(client, network['id']) # TODO(sindhu): Use the SDK resource mapped attribute names once the @@ -673,7 +674,7 @@ def take_action_compute(self, client, parsed_args): 'Subnet', ) - data = client.api.network_list() + data = compute_v2.list_networks(client) return ( column_headers, @@ -828,7 +829,7 @@ def take_action_network(self, client, parsed_args): return (display_columns, data) def take_action_compute(self, client, parsed_args): - obj = client.api.network_find(parsed_args.network) + obj = compute_v2.find_network(client, parsed_args.network) display_columns, columns = _get_columns_compute(obj) data = utils.get_dict_properties(obj, columns) return (display_columns, data) diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index 90b09cae8..556eff2c0 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -20,6 +20,7 @@ from osc_lib import utils from osc_lib.utils import tags as _tag +from openstackclient.api import compute_v2 from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -180,7 +181,8 @@ def take_action_network(self, client, parsed_args): def take_action_compute(self, client, parsed_args): description = self._get_description(parsed_args) - obj = client.api.security_group_create( + obj = compute_v2.create_security_group( + client, parsed_args.name, description, ) @@ -212,7 +214,8 @@ def take_action_network(self, client, parsed_args): client.delete_security_group(obj) def take_action_compute(self, client, parsed_args): - client.api.security_group_delete(self.r) + security_group = compute_v2.find_security_group(client, self.r) + compute_v2.delete_security_group(client, security_group['id']) # TODO(rauta): Use the SDK resource mapped attribute names once @@ -291,10 +294,10 @@ def take_action_network(self, client, parsed_args): ) def take_action_compute(self, client, parsed_args): - search = {'all_tenants': parsed_args.all_projects} - data = client.api.security_group_list( + data = compute_v2.list_security_groups( # TODO(dtroyer): add limit, marker - search_opts=search, + client, + all_projects=parsed_args.all_projects, ) columns = ( @@ -383,20 +386,21 @@ def take_action_network(self, client, parsed_args): _tag.update_tags_for_set(client, obj, parsed_args) def take_action_compute(self, client, parsed_args): - data = client.api.security_group_find(parsed_args.group) + security_group = compute_v2.find_security_group( + client, parsed_args.group + ) + params = {} if parsed_args.name is not None: - data['name'] = parsed_args.name + params['name'] = parsed_args.name if parsed_args.description is not None: - data['description'] = parsed_args.description + params['description'] = parsed_args.description # NOTE(rtheis): Previous behavior did not raise a CommandError # if there were no updates. Maintain this behavior and issue # the update. - client.api.security_group_set( - data, - data['name'], - data['description'], + compute_v2.update_security_group( + client, security_group['id'], **params ) @@ -422,7 +426,7 @@ def take_action_network(self, client, parsed_args): return (display_columns, data) def take_action_compute(self, client, parsed_args): - obj = client.api.security_group_find(parsed_args.group) + obj = compute_v2.find_security_group(client, parsed_args.group) display_columns, property_columns = _get_columns(obj) 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 5e0de120b..80e5ff085 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -20,6 +20,7 @@ from osc_lib import exceptions from osc_lib import utils +from openstackclient.api import compute_v2 from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common @@ -91,7 +92,7 @@ def update_parser_common(self, parser): "ending port range: 137:139. Required for IP protocols TCP " "and UDP. Ignored for ICMP IP protocols." ), - **dst_port_default + **dst_port_default, ) # NOTE(rtheis): Support either protocol option name for now. @@ -125,7 +126,7 @@ def update_parser_common(self, parser): metavar='', type=network_utils.convert_to_lowercase, help=protocol_help, - **proto_choices + **proto_choices, ) if not self.is_docs_build: protocol_group.add_argument( @@ -133,7 +134,7 @@ def update_parser_common(self, parser): metavar='', type=network_utils.convert_to_lowercase, help=argparse.SUPPRESS, - **proto_choices + **proto_choices, ) return parser @@ -292,7 +293,7 @@ def take_action_network(self, client, parsed_args): return (display_columns, data) def take_action_compute(self, client, parsed_args): - group = client.api.security_group_find(parsed_args.group) + group = compute_v2.find_security_group(client, parsed_args.group) protocol = network_utils.get_protocol( parsed_args, default_protocol='tcp' ) @@ -303,15 +304,16 @@ def take_action_compute(self, client, parsed_args): remote_ip = None if parsed_args.remote_group is not None: - parsed_args.remote_group = client.api.security_group_find( - parsed_args.remote_group, + parsed_args.remote_group = compute_v2.find_security_group( + client, parsed_args.remote_group )['id'] if parsed_args.remote_ip is not None: remote_ip = parsed_args.remote_ip else: remote_ip = '0.0.0.0/0' - obj = client.api.security_group_rule_create( + obj = compute_v2.create_security_group_rule( + client, security_group_id=group['id'], ip_protocol=protocol, from_port=from_port, @@ -343,7 +345,7 @@ def take_action_network(self, client, parsed_args): client.delete_security_group_rule(obj) def take_action_compute(self, client, parsed_args): - client.api.security_group_rule_delete(self.r) + compute_v2.delete_security_group_rule(client, self.r) class ListSecurityGroupRule(common.NetworkAndComputeLister): @@ -532,15 +534,16 @@ def take_action_compute(self, client, parsed_args): rules_to_list = [] if parsed_args.group is not None: - group = client.api.security_group_find( - parsed_args.group, + security_group = compute_v2.find_security_group( + client, parsed_args.group ) - rules_to_list = group['rules'] + rules_to_list = security_group['rules'] else: columns = columns + ('parent_group_id',) - search = {'all_tenants': parsed_args.all_projects} - for group in client.api.security_group_list(search_opts=search): - rules_to_list.extend(group['rules']) + for security_group in compute_v2.list_security_groups( + client, all_projects=parsed_args.all_projects + ): + rules_to_list.extend(security_group['rules']) # NOTE(rtheis): Turn the raw rules into resources. rules = [] @@ -596,7 +599,7 @@ def take_action_compute(self, client, parsed_args): # the requested rule. obj = None security_group_rules = [] - for security_group in client.api.security_group_list(): + for security_group in compute_v2.list_security_groups(client): security_group_rules.extend(security_group['rules']) for security_group_rule in security_group_rules: if parsed_args.rule == str(security_group_rule.get('id')): diff --git a/openstackclient/tests/unit/api/test_compute_v2.py b/openstackclient/tests/unit/api/test_compute_v2.py index fd2c521c7..01ebf7b92 100644 --- a/openstackclient/tests/unit/api/test_compute_v2.py +++ b/openstackclient/tests/unit/api/test_compute_v2.py @@ -17,651 +17,70 @@ from unittest import mock import uuid -from keystoneauth1 import session from openstack.compute.v2 import _proxy from osc_lib import exceptions as osc_lib_exceptions -from requests_mock.contrib import fixture from openstackclient.api import compute_v2 as compute from openstackclient.tests.unit import fakes from openstackclient.tests.unit import utils -FAKE_PROJECT = 'xyzpdq' -FAKE_URL = 'http://gopher.com/v2' +class TestSecurityGroup(utils.TestCase): - -class TestComputeAPIv2(utils.TestCase): def setUp(self): super().setUp() - sess = session.Session() - self.api = compute.APIv2(session=sess, endpoint=FAKE_URL) - self.requests_mock = self.useFixture(fixture.Fixture()) - - -class TestFloatingIP(TestComputeAPIv2): - FAKE_FLOATING_IP_RESP = { - 'id': 1, - 'ip': '203.0.113.11', # TEST-NET-3 - 'fixed_ip': '198.51.100.11', # TEST-NET-2 - 'pool': 'nova', - 'instance_id': None, - } - FAKE_FLOATING_IP_RESP_2 = { - 'id': 2, - 'ip': '203.0.113.12', # TEST-NET-3 - 'fixed_ip': '198.51.100.12', # TEST-NET-2 - 'pool': 'nova', - 'instance_id': None, - } - LIST_FLOATING_IP_RESP = [ - FAKE_FLOATING_IP_RESP, - FAKE_FLOATING_IP_RESP_2, - ] - - FAKE_SERVER_RESP_1 = { - 'id': 1, - 'name': 'server1', - } - - def test_floating_ip_add_id(self): - self.requests_mock.register_uri( - 'POST', - FAKE_URL + '/servers/1/action', - json={'server': {}}, - status_code=200, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/servers/1', - json={'server': self.FAKE_SERVER_RESP_1}, - status_code=200, - ) - ret = self.api.floating_ip_add('1', '1.0.1.0') - self.assertEqual(200, ret.status_code) - - def test_floating_ip_add_name(self): - self.requests_mock.register_uri( - 'POST', - FAKE_URL + '/servers/1/action', - json={'server': {}}, - status_code=200, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/servers/server1', - json={'server': self.FAKE_SERVER_RESP_1}, - status_code=200, - ) - ret = self.api.floating_ip_add('server1', '1.0.1.0') - self.assertEqual(200, ret.status_code) - - def test_floating_ip_create(self): - self.requests_mock.register_uri( - 'POST', - FAKE_URL + '/os-floating-ips', - json={'floating_ip': self.FAKE_FLOATING_IP_RESP}, - status_code=200, - ) - ret = self.api.floating_ip_create('nova') - self.assertEqual(self.FAKE_FLOATING_IP_RESP, ret) - - def test_floating_ip_create_not_found(self): - self.requests_mock.register_uri( - 'POST', - FAKE_URL + '/os-floating-ips', - status_code=404, - ) - self.assertRaises( - osc_lib_exceptions.NotFound, - self.api.floating_ip_create, - 'not-nova', - ) - - def test_floating_ip_delete(self): - self.requests_mock.register_uri( - 'DELETE', - FAKE_URL + '/os-floating-ips/1', - status_code=202, - ) - ret = self.api.floating_ip_delete('1') - self.assertEqual(202, ret.status_code) - self.assertEqual("", ret.text) - - def test_floating_ip_delete_none(self): - ret = self.api.floating_ip_delete() - self.assertIsNone(ret) - - def test_floating_ip_find_id(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-floating-ips/1', - json={'floating_ip': self.FAKE_FLOATING_IP_RESP}, - status_code=200, - ) - ret = self.api.floating_ip_find('1') - self.assertEqual(self.FAKE_FLOATING_IP_RESP, ret) - - def test_floating_ip_find_ip(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-floating-ips/' + self.FAKE_FLOATING_IP_RESP['ip'], - status_code=404, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-floating-ips', - json={'floating_ips': self.LIST_FLOATING_IP_RESP}, - status_code=200, - ) - ret = self.api.floating_ip_find(self.FAKE_FLOATING_IP_RESP['ip']) - self.assertEqual(self.FAKE_FLOATING_IP_RESP, ret) - - def test_floating_ip_find_not_found(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-floating-ips/1.2.3.4', - status_code=404, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-floating-ips', - json={'floating_ips': self.LIST_FLOATING_IP_RESP}, - status_code=200, - ) - self.assertRaises( - osc_lib_exceptions.NotFound, - self.api.floating_ip_find, - '1.2.3.4', - ) - - def test_floating_ip_list(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-floating-ips', - json={'floating_ips': self.LIST_FLOATING_IP_RESP}, - status_code=200, - ) - ret = self.api.floating_ip_list() - self.assertEqual(self.LIST_FLOATING_IP_RESP, ret) - - def test_floating_ip_remove_id(self): - self.requests_mock.register_uri( - 'POST', - FAKE_URL + '/servers/1/action', - status_code=200, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/servers/1', - json={'server': self.FAKE_SERVER_RESP_1}, - status_code=200, - ) - ret = self.api.floating_ip_remove('1', '1.0.1.0') - self.assertEqual(200, ret.status_code) - - def test_floating_ip_remove_name(self): - self.requests_mock.register_uri( - 'POST', - FAKE_URL + '/servers/1/action', - status_code=200, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/servers/server1', - json={'server': self.FAKE_SERVER_RESP_1}, - status_code=200, - ) - ret = self.api.floating_ip_remove('server1', '1.0.1.0') - self.assertEqual(200, ret.status_code) - - -class TestFloatingIPPool(TestComputeAPIv2): - LIST_FLOATING_IP_POOL_RESP = [ - {"name": "tide"}, - {"name": "press"}, - ] - - def test_floating_ip_pool_list(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-floating-ip-pools', - json={'floating_ip_pools': self.LIST_FLOATING_IP_POOL_RESP}, - status_code=200, - ) - ret = self.api.floating_ip_pool_list() - self.assertEqual(self.LIST_FLOATING_IP_POOL_RESP, ret) - - -class TestNetwork(TestComputeAPIv2): - FAKE_NETWORK_RESP = { - 'id': '1', - 'label': 'label1', - 'cidr': '1.2.3.0/24', - } - - FAKE_NETWORK_RESP_2 = { - 'id': '2', - 'label': 'label2', - 'cidr': '4.5.6.0/24', - } - - LIST_NETWORK_RESP = [ - FAKE_NETWORK_RESP, - FAKE_NETWORK_RESP_2, - ] - - def test_network_create_default(self): - self.requests_mock.register_uri( - 'POST', - FAKE_URL + '/os-networks', - json={'network': self.FAKE_NETWORK_RESP}, - status_code=200, - ) - ret = self.api.network_create('label1') - self.assertEqual(self.FAKE_NETWORK_RESP, ret) - - def test_network_create_options(self): - self.requests_mock.register_uri( - 'POST', - FAKE_URL + '/os-networks', - json={'network': self.FAKE_NETWORK_RESP}, - status_code=200, - ) - ret = self.api.network_create( - name='label1', - subnet='1.2.3.0/24', - ) - self.assertEqual(self.FAKE_NETWORK_RESP, ret) - - def test_network_delete_id(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-networks/1', - json={'network': self.FAKE_NETWORK_RESP}, - status_code=200, - ) - self.requests_mock.register_uri( - 'DELETE', - FAKE_URL + '/os-networks/1', - status_code=202, - ) - ret = self.api.network_delete('1') - self.assertEqual(202, ret.status_code) - self.assertEqual("", ret.text) - - def test_network_delete_name(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-networks/label1', - status_code=404, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-networks', - json={'networks': self.LIST_NETWORK_RESP}, - status_code=200, - ) - self.requests_mock.register_uri( - 'DELETE', - FAKE_URL + '/os-networks/1', - status_code=202, - ) - ret = self.api.network_delete('label1') - self.assertEqual(202, ret.status_code) - self.assertEqual("", ret.text) - - def test_network_delete_not_found(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-networks/label3', - status_code=404, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-networks', - json={'networks': self.LIST_NETWORK_RESP}, - status_code=200, - ) - self.assertRaises( - osc_lib_exceptions.NotFound, - self.api.network_delete, - 'label3', - ) - - def test_network_find_id(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-networks/1', - json={'network': self.FAKE_NETWORK_RESP}, - status_code=200, - ) - ret = self.api.network_find('1') - self.assertEqual(self.FAKE_NETWORK_RESP, ret) - - def test_network_find_name(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-networks/label2', - status_code=404, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-networks', - json={'networks': self.LIST_NETWORK_RESP}, - status_code=200, - ) - ret = self.api.network_find('label2') - self.assertEqual(self.FAKE_NETWORK_RESP_2, ret) - - def test_network_find_not_found(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-networks/label3', - status_code=404, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-networks', - json={'networks': self.LIST_NETWORK_RESP}, - status_code=200, - ) - self.assertRaises( - osc_lib_exceptions.NotFound, - self.api.network_find, - 'label3', - ) - - def test_network_list_no_options(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-networks', - json={'networks': self.LIST_NETWORK_RESP}, - status_code=200, - ) - ret = self.api.network_list() - self.assertEqual(self.LIST_NETWORK_RESP, ret) - - -class TestSecurityGroup(TestComputeAPIv2): - FAKE_SECURITY_GROUP_RESP = { - 'id': '1', - 'name': 'sg1', - 'description': 'test security group', - 'tenant_id': '0123456789', - 'rules': [], - } - FAKE_SECURITY_GROUP_RESP_2 = { - 'id': '2', - 'name': 'sg2', - 'description': 'another test security group', - 'tenant_id': '0123456789', - 'rules': [], - } - LIST_SECURITY_GROUP_RESP = [ - FAKE_SECURITY_GROUP_RESP_2, - FAKE_SECURITY_GROUP_RESP, - ] - - def test_security_group_create_default(self): - self.requests_mock.register_uri( - 'POST', - FAKE_URL + '/os-security-groups', - json={'security_group': self.FAKE_SECURITY_GROUP_RESP}, - status_code=200, - ) - ret = self.api.security_group_create('sg1') - self.assertEqual(self.FAKE_SECURITY_GROUP_RESP, ret) - - def test_security_group_create_options(self): - self.requests_mock.register_uri( - 'POST', - FAKE_URL + '/os-security-groups', - json={'security_group': self.FAKE_SECURITY_GROUP_RESP}, - status_code=200, - ) - ret = self.api.security_group_create( - name='sg1', - description='desc', - ) - self.assertEqual(self.FAKE_SECURITY_GROUP_RESP, ret) - - def test_security_group_delete_id(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups/1', - json={'security_group': self.FAKE_SECURITY_GROUP_RESP}, - status_code=200, - ) - self.requests_mock.register_uri( - 'DELETE', - FAKE_URL + '/os-security-groups/1', - status_code=202, - ) - ret = self.api.security_group_delete('1') - self.assertEqual(202, ret.status_code) - self.assertEqual("", ret.text) - - def test_security_group_delete_name(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups/sg1', - status_code=404, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups', - json={'security_groups': self.LIST_SECURITY_GROUP_RESP}, - status_code=200, - ) - self.requests_mock.register_uri( - 'DELETE', - FAKE_URL + '/os-security-groups/1', - status_code=202, - ) - ret = self.api.security_group_delete('sg1') - self.assertEqual(202, ret.status_code) - self.assertEqual("", ret.text) - - def test_security_group_delete_not_found(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups/sg3', - status_code=404, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups', - json={'security_groups': self.LIST_SECURITY_GROUP_RESP}, - status_code=200, - ) - self.assertRaises( - osc_lib_exceptions.NotFound, - self.api.security_group_delete, - 'sg3', - ) - - def test_security_group_find_id(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups/1', - json={'security_group': self.FAKE_SECURITY_GROUP_RESP}, - status_code=200, - ) - ret = self.api.security_group_find('1') - self.assertEqual(self.FAKE_SECURITY_GROUP_RESP, ret) - - def test_security_group_find_name(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups/sg2', - status_code=404, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups', - json={'security_groups': self.LIST_SECURITY_GROUP_RESP}, - status_code=200, - ) - ret = self.api.security_group_find('sg2') - self.assertEqual(self.FAKE_SECURITY_GROUP_RESP_2, ret) - - def test_security_group_find_not_found(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups/sg3', - status_code=404, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups', - json={'security_groups': self.LIST_SECURITY_GROUP_RESP}, - status_code=200, - ) - self.assertRaises( - osc_lib_exceptions.NotFound, - self.api.security_group_find, - 'sg3', - ) - - def test_security_group_list_no_options(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups', - json={'security_groups': self.LIST_SECURITY_GROUP_RESP}, - status_code=200, - ) - ret = self.api.security_group_list() - self.assertEqual(self.LIST_SECURITY_GROUP_RESP, ret) - - def test_security_group_set_options_id(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups/1', - json={'security_group': self.FAKE_SECURITY_GROUP_RESP}, - status_code=200, - ) - self.requests_mock.register_uri( - 'PUT', - FAKE_URL + '/os-security-groups/1', - json={'security_group': self.FAKE_SECURITY_GROUP_RESP}, - status_code=200, - ) - ret = self.api.security_group_set( - security_group='1', description='desc2' - ) - self.assertEqual(self.FAKE_SECURITY_GROUP_RESP, ret) - - def test_security_group_set_options_name(self): - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups/sg2', - status_code=404, - ) - self.requests_mock.register_uri( - 'GET', - FAKE_URL + '/os-security-groups', - json={'security_groups': self.LIST_SECURITY_GROUP_RESP}, - status_code=200, - ) - self.requests_mock.register_uri( - 'PUT', - FAKE_URL + '/os-security-groups/2', - json={'security_group': self.FAKE_SECURITY_GROUP_RESP_2}, - status_code=200, - ) - ret = self.api.security_group_set( - security_group='sg2', description='desc2' - ) - self.assertEqual(self.FAKE_SECURITY_GROUP_RESP_2, ret) - - -class TestSecurityGroupRule(TestComputeAPIv2): - FAKE_SECURITY_GROUP_RULE_RESP = { - 'id': '1', - 'name': 'sgr1', - 'tenant_id': 'proj-1', - 'ip_protocol': 'TCP', - 'from_port': 1, - 'to_port': 22, - 'group': {}, - # 'ip_range': , - # 'cidr': , - # 'parent_group_id': , - } - - def test_security_group_create_no_options(self): - self.requests_mock.register_uri( - 'POST', - FAKE_URL + '/os-security-group-rules', - json={'security_group_rule': self.FAKE_SECURITY_GROUP_RULE_RESP}, - status_code=200, - ) - ret = self.api.security_group_rule_create( - security_group_id='1', - ip_protocol='tcp', - ) - self.assertEqual(self.FAKE_SECURITY_GROUP_RULE_RESP, ret) - def test_security_group_create_options(self): - self.requests_mock.register_uri( - 'POST', - FAKE_URL + '/os-security-group-rules', - json={'security_group_rule': self.FAKE_SECURITY_GROUP_RULE_RESP}, - status_code=200, - ) - ret = self.api.security_group_rule_create( - security_group_id='1', - ip_protocol='tcp', - from_port=22, - to_port=22, - remote_ip='1.2.3.4/24', - ) - self.assertEqual(self.FAKE_SECURITY_GROUP_RULE_RESP, ret) + self.compute_sdk_client = mock.Mock(_proxy.Proxy) - def test_security_group_create_port_errors(self): - self.requests_mock.register_uri( - 'POST', - FAKE_URL + '/os-security-group-rules', - json={'security_group_rule': self.FAKE_SECURITY_GROUP_RULE_RESP}, - status_code=200, - ) - self.assertRaises( - compute.InvalidValue, - self.api.security_group_rule_create, - security_group_id='1', - ip_protocol='tcp', - from_port='', - to_port=22, - remote_ip='1.2.3.4/24', - ) - self.assertRaises( - compute.InvalidValue, - self.api.security_group_rule_create, - security_group_id='1', - ip_protocol='tcp', - from_port=0, - to_port=[], - remote_ip='1.2.3.4/24', + def test_create_security_group(self): + sg_name = 'name-' + uuid.uuid4().hex + sg_description = 'description-' + uuid.uuid4().hex + data = { + 'security_group': { + 'id': uuid.uuid4().hex, + 'name': sg_name, + 'description': sg_description, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + 'rules': [], + } + } + self.compute_sdk_client.post.return_value = fakes.FakeResponse( + data=data ) - def test_security_group_rule_delete(self): - self.requests_mock.register_uri( - 'DELETE', - FAKE_URL + '/os-security-group-rules/1', - status_code=202, + result = compute.create_security_group( + self.compute_sdk_client, sg_name, sg_description ) - ret = self.api.security_group_rule_delete('1') - self.assertEqual(202, ret.status_code) - self.assertEqual("", ret.text) + self.compute_sdk_client.post.assert_called_once_with( + '/os-security-groups', + data={'name': sg_name, 'description': sg_description}, + microversion='2.1', + ) + self.assertEqual(data['security_group'], result) -class TestFindSecurityGroup(utils.TestCase): + def test_list_security_groups(self): + data = { + 'security_groups': [ + { + 'id': uuid.uuid4().hex, + 'name': uuid.uuid4().hex, + 'description': 'description-' + uuid.uuid4().hex, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + 'rules': [], + } + ], + } + self.compute_sdk_client.get.return_value = fakes.FakeResponse( + data=data + ) - def setUp(self): - super().setUp() + result = compute.list_security_groups(self.compute_sdk_client) - self.compute_sdk_client = mock.Mock(_proxy.Proxy) + self.compute_sdk_client.get.assert_called_once_with( + '/os-security-groups', microversion='2.1' + ) + self.assertEqual(data['security_groups'], result) def test_find_security_group_by_id(self): sg_id = uuid.uuid4().hex @@ -764,14 +183,174 @@ def test_find_security_group_by_name_duplicate(self): sg_name, ) + def test_update_security_group(self): + sg_id = uuid.uuid4().hex + sg_name = 'name-' + uuid.uuid4().hex + sg_description = 'description-' + uuid.uuid4().hex + data = { + 'security_group': { + 'id': sg_id, + 'name': sg_name, + 'description': sg_description, + 'tenant_id': 'project-id-' + uuid.uuid4().hex, + 'rules': [], + } + } + self.compute_sdk_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_sdk_client.put.assert_called_once_with( + f'/os-security-groups/{sg_id}', + data={'name': sg_name, 'description': sg_description}, + microversion='2.1', + ) + self.assertEqual(data['security_group'], result) + + def test_delete_security_group(self): + sg_id = uuid.uuid4().hex + self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + status_code=http.HTTPStatus.NO_CONTENT + ) + + result = compute.delete_security_group(self.compute_sdk_client, sg_id) + + self.compute_sdk_client.delete.assert_called_once_with( + f'/os-security-groups/{sg_id}', + microversion='2.1', + ) + self.assertIsNone(result) + + +class TestSecurityGroupRule(utils.TestCase): + + def setUp(self): + super().setUp() + + self.compute_sdk_client = mock.Mock(_proxy.Proxy) + + def test_create_security_group_rule(self): + sg_id = uuid.uuid4().hex + data = { + 'security_group_rule': { + 'parent_group_id': sg_id, + 'ip_protocol': 'tcp', + 'from_port': 22, + 'to_port': 22, + 'cidr': '10.0.0.0/24', + } + } + self.compute_sdk_client.post.return_value = fakes.FakeResponse( + data=data + ) + + result = compute.create_security_group_rule( + self.compute_sdk_client, + security_group_id=sg_id, + ip_protocol='tcp', + from_port=22, + to_port=22, + remote_ip='10.0.0.0/24', + remote_group=None, + ) + + self.compute_sdk_client.post.assert_called_once_with( + '/os-security-group-rules', + data={ + 'parent_group_id': sg_id, + 'ip_protocol': 'tcp', + 'from_port': 22, + 'to_port': 22, + 'cidr': '10.0.0.0/24', + 'group_id': None, + }, + microversion='2.1', + ) + self.assertEqual(data['security_group_rule'], result) + + def test_delete_security_group_rule(self): + sg_id = uuid.uuid4().hex + self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + status_code=http.HTTPStatus.NO_CONTENT + ) + + result = compute.delete_security_group_rule( + self.compute_sdk_client, sg_id + ) + + self.compute_sdk_client.delete.assert_called_once_with( + f'/os-security-group-rules/{sg_id}', + microversion='2.1', + ) + self.assertIsNone(result) -class TestFindNetwork(utils.TestCase): + +class TestNetwork(utils.TestCase): def setUp(self): super().setUp() self.compute_sdk_client = mock.Mock(_proxy.Proxy) + def test_create_network(self): + net_name = 'name-' + uuid.uuid4().hex + net_subnet = '10.0.0.0/24' + data = { + 'network': { + 'id': uuid.uuid4().hex, + 'label': net_name, + 'cidr': net_subnet, + 'share_address': True, + # other fields omitted for brevity + } + } + self.compute_sdk_client.post.return_value = fakes.FakeResponse( + data=data + ) + + result = compute.create_network( + self.compute_sdk_client, + name=net_name, + subnet=net_subnet, + share_subnet=True, + ) + + self.compute_sdk_client.post.assert_called_once_with( + '/os-networks', + data={ + 'label': net_name, + 'cidr': net_subnet, + 'share_address': True, + }, + microversion='2.1', + ) + self.assertEqual(data['network'], result) + + def test_list_networks(self): + data = { + 'networks': [ + { + 'id': uuid.uuid4().hex, + 'label': f'name-{uuid.uuid4().hex}', + # other fields omitted for brevity + } + ], + } + self.compute_sdk_client.get.return_value = fakes.FakeResponse( + data=data + ) + + result = compute.list_networks(self.compute_sdk_client) + + self.compute_sdk_client.get.assert_called_once_with( + '/os-networks', microversion='2.1' + ) + self.assertEqual(data['networks'], result) + def test_find_network_by_id(self): net_id = uuid.uuid4().hex net_name = 'name-' + uuid.uuid4().hex @@ -862,3 +441,133 @@ def test_find_network_by_name_duplicate(self): self.compute_sdk_client, net_name, ) + + def test_delete_network(self): + net_id = uuid.uuid4().hex + self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + status_code=http.HTTPStatus.NO_CONTENT + ) + + result = compute.delete_network(self.compute_sdk_client, net_id) + + self.compute_sdk_client.delete.assert_called_once_with( + f'/os-networks/{net_id}', microversion='2.1' + ) + self.assertIsNone(result) + + +class TestFloatingIP(utils.TestCase): + + def setUp(self): + super().setUp() + + self.compute_sdk_client = mock.Mock(_proxy.Proxy) + + def test_create_floating_ip(self): + network = 'network-' + uuid.uuid4().hex + data = { + 'floating_ip': { + 'fixed_ip': None, + 'id': uuid.uuid4().hex, + 'instance_id': None, + 'ip': '172.24.4.17', + 'pool': network, + } + } + self.compute_sdk_client.post.return_value = fakes.FakeResponse( + data=data + ) + + result = compute.create_floating_ip( + self.compute_sdk_client, network=network + ) + + self.compute_sdk_client.post.assert_called_once_with( + '/os-floating-ips', data={'pool': network}, microversion='2.1' + ) + self.assertEqual(data['floating_ip'], result) + + def test_list_floating_ips(self): + data = { + 'floating_ips': [ + { + 'fixed_ip': None, + 'id': uuid.uuid4().hex, + 'instance_id': None, + 'ip': '172.24.4.17', + 'pool': f'network-{uuid.uuid4().hex}', + } + ], + } + self.compute_sdk_client.get.return_value = fakes.FakeResponse( + data=data + ) + + result = compute.list_floating_ips(self.compute_sdk_client) + + self.compute_sdk_client.get.assert_called_once_with( + '/os-floating-ips', microversion='2.1' + ) + self.assertEqual(data['floating_ips'], result) + + def test_get_floating_ip(self): + fip_id = uuid.uuid4().hex + data = { + 'floating_ip': { + 'fixed_ip': None, + 'id': fip_id, + 'instance_id': None, + 'ip': '172.24.4.17', + 'pool': f'network-{uuid.uuid4().hex}', + } + } + self.compute_sdk_client.get.side_effect = [ + fakes.FakeResponse(data=data), + ] + + result = compute.get_floating_ip(self.compute_sdk_client, fip_id) + + self.compute_sdk_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( + status_code=http.HTTPStatus.NO_CONTENT + ) + + result = compute.delete_floating_ip(self.compute_sdk_client, fip_id) + + self.compute_sdk_client.delete.assert_called_once_with( + f'/os-floating-ips/{fip_id}', microversion='2.1' + ) + self.assertIsNone(result) + + +class TestFloatingIPPool(utils.TestCase): + + def setUp(self): + super().setUp() + + self.compute_sdk_client = mock.Mock(_proxy.Proxy) + + def test_list_floating_ip_pools(self): + data = { + 'floating_ip_pools': [ + { + 'name': f'pool-{uuid.uuid4().hex}', + } + ], + } + self.compute_sdk_client.get.return_value = fakes.FakeResponse( + data=data + ) + + result = compute.list_floating_ip_pools(self.compute_sdk_client) + + self.compute_sdk_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/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 3e89d4905..5a19ad56a 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -39,7 +39,6 @@ from openstack.compute.v2 import usage as _usage from openstack.compute.v2 import volume_attachment as _volume_attachment -from openstackclient.api import compute_v2 from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes from openstackclient.tests.unit.image.v2 import fakes as image_fakes @@ -154,11 +153,6 @@ def setUp(self): ) self.compute_client = self.app.client_manager.compute - self.compute_client.api = compute_v2.APIv2( - session=self.app.client_manager.session, - endpoint=fakes.AUTH_URL, - ) - # TODO(stephenfin): Rename to 'compute_client' once all commands are # migrated to SDK self.app.client_manager.sdk_connection.compute = mock.Mock( diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 8902b6501..4b87dc85b 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -392,57 +392,57 @@ def test_server_add_fixed_ip_with_fixed_ip_with_tag(self): ) -@mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_add') class TestServerAddFloatingIPCompute(compute_fakes.TestComputev2): def setUp(self): super().setUp() 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 - # Get the command object to test self.cmd = server.AddFloatingIP(self.app, None) - def test_server_add_floating_ip_default(self, fip_mock): - _floating_ip = compute_fakes.create_one_floating_ip() + def test_server_add_floating_ip_default(self): arglist = [ - 'server1', - _floating_ip['ip'], + self.server.name, + '1.2.3.4', ] verifylist = [ - ('server', 'server1'), - ('ip_address', _floating_ip['ip']), + ('server', self.server.name), + ('ip_address', '1.2.3.4'), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - fip_mock.assert_called_once_with( - 'server1', - _floating_ip['ip'], - fixed_address=None, + self.compute_sdk_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.server, '1.2.3.4', fixed_address=None ) - def test_server_add_floating_ip_fixed(self, fip_mock): - _floating_ip = compute_fakes.create_one_floating_ip() + def test_server_add_floating_ip_fixed(self): arglist = [ '--fixed-ip-address', - _floating_ip['fixed_ip'], - 'server1', - _floating_ip['ip'], + '5.6.7.8', + self.server.name, + '1.2.3.4', ] verifylist = [ - ('fixed_ip_address', _floating_ip['fixed_ip']), - ('server', 'server1'), - ('ip_address', _floating_ip['ip']), + ('fixed_ip_address', '5.6.7.8'), + ('server', self.server.name), + ('ip_address', '1.2.3.4'), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - fip_mock.assert_called_once_with( - 'server1', - _floating_ip['ip'], - fixed_address=_floating_ip['fixed_ip'], + self.compute_sdk_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.server, '1.2.3.4', fixed_address='5.6.7.8' ) @@ -7267,34 +7267,34 @@ def test_rescue_with_password(self): self.assertIsNone(result) -@mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_remove') class TestServerRemoveFloatingIPCompute(compute_fakes.TestComputev2): def setUp(self): super().setUp() 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 - # Get the command object to test self.cmd = server.RemoveFloatingIP(self.app, None) - def test_server_remove_floating_ip(self, fip_mock): - _floating_ip = compute_fakes.create_one_floating_ip() - + def test_server_remove_floating_ip(self): arglist = [ - 'server1', - _floating_ip['ip'], + self.server.name, + '1.2.3.4', ] verifylist = [ - ('server', 'server1'), - ('ip_address', _floating_ip['ip']), + ('server', self.server.name), + ('ip_address', '1.2.3.4'), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - fip_mock.assert_called_once_with( - 'server1', - _floating_ip['ip'], + self.compute_sdk_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.server, '1.2.3.4' ) diff --git a/openstackclient/tests/unit/network/test_common.py b/openstackclient/tests/unit/network/test_common.py index c1ad9b28d..a84697b2c 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.compute = mock.Mock() - self.compute_client = self.app.client_manager.compute + self.app.client_manager.sdk_connection.compute = mock.Mock() + self.compute_client = self.app.client_manager.sdk_connection.compute self.compute_client.compute_action = mock.Mock( return_value='take_action_compute' ) 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 b7ca58751..d310f199a 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip_compute.py @@ -12,21 +12,17 @@ # from unittest import mock -from unittest.mock import call from osc_lib import exceptions +from openstackclient.api import compute_v2 from openstackclient.network.v2 import floating_ip as fip from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes from openstackclient.tests.unit import utils as tests_utils -# Tests for Nova network - - -@mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_create') +@mock.patch.object(compute_v2, 'create_floating_ip') class TestCreateFloatingIPCompute(compute_fakes.TestComputev2): - # The floating ip to be deleted. _floating_ip = compute_fakes.create_one_floating_ip() columns = ( @@ -50,9 +46,6 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - # self.compute_client.floating_ips.create.return_value = self.floating_ip - - # Get the command object to test self.cmd = fip.CreateFloatingIP(self.app, None) def test_floating_ip_create_no_arg(self, fip_mock): @@ -79,14 +72,15 @@ def test_floating_ip_create_default(self, fip_mock): columns, data = self.cmd.take_action(parsed_args) - fip_mock.assert_called_once_with(self._floating_ip['pool']) + fip_mock.assert_called_once_with( + self.compute_sdk_client, self._floating_ip['pool'] + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) -@mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_delete') +@mock.patch.object(compute_v2, 'delete_floating_ip') class TestDeleteFloatingIPCompute(compute_fakes.TestComputev2): - # The floating ips to be deleted. _floating_ips = compute_fakes.create_floating_ips(count=2) def setUp(self): @@ -94,7 +88,6 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - # Get the command object to test self.cmd = fip.DeleteFloatingIP(self.app, None) def test_floating_ip_delete(self, fip_mock): @@ -109,27 +102,34 @@ def test_floating_ip_delete(self, fip_mock): result = self.cmd.take_action(parsed_args) - fip_mock.assert_called_once_with(self._floating_ips[0]['id']) + fip_mock.assert_called_once_with( + self.compute_sdk_client, self._floating_ips[0]['id'] + ) self.assertIsNone(result) def test_floating_ip_delete_multi(self, fip_mock): fip_mock.return_value = mock.Mock(return_value=None) - arglist = [] - verifylist = [] - - for f in self._floating_ips: - arglist.append(f['id']) + arglist = [ + self._floating_ips[0]['id'], + self._floating_ips[1]['id'], + ] verifylist = [ ('floating_ip', arglist), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - calls = [] - for f in self._floating_ips: - calls.append(call(f['id'])) - fip_mock.assert_has_calls(calls) + 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'] + ), + ] + ) self.assertIsNone(result) def test_floating_ip_delete_multi_exception(self, fip_mock): @@ -156,13 +156,16 @@ def test_floating_ip_delete_multi_exception(self, fip_mock): except exceptions.CommandError as e: self.assertEqual('1 of 2 floating_ips failed to delete.', str(e)) - fip_mock.assert_any_call(self._floating_ips[0]['id']) - fip_mock.assert_any_call('unexist_floating_ip') + 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' + ) -@mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_list') +@mock.patch.object(compute_v2, 'list_floating_ips') class TestListFloatingIPCompute(compute_fakes.TestComputev2): - # The floating ips to be list up _floating_ips = compute_fakes.create_floating_ips(count=3) columns = ( @@ -190,7 +193,6 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - # Get the command object to test self.cmd = fip.ListFloatingIP(self.app, None) def test_floating_ip_list(self, fip_mock): @@ -201,14 +203,13 @@ def test_floating_ip_list(self, fip_mock): columns, data = self.cmd.take_action(parsed_args) - fip_mock.assert_called_once_with() + fip_mock.assert_called_once_with(self.compute_sdk_client) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) -@mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_find') +@mock.patch.object(compute_v2, 'get_floating_ip') class TestShowFloatingIPCompute(compute_fakes.TestComputev2): - # The floating ip to display. _floating_ip = compute_fakes.create_one_floating_ip() columns = ( @@ -232,7 +233,6 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - # Get the command object to test self.cmd = fip.ShowFloatingIP(self.app, None) def test_floating_ip_show(self, fip_mock): @@ -247,6 +247,8 @@ def test_floating_ip_show(self, fip_mock): columns, data = self.cmd.take_action(parsed_args) - fip_mock.assert_called_once_with(self._floating_ip['id']) + fip_mock.assert_called_once_with( + self.compute_sdk_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 29084c15b..26fc3918e 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 @@ -13,14 +13,12 @@ from unittest import mock +from openstackclient.api import compute_v2 from openstackclient.network.v2 import floating_ip_pool from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes -# Tests for Compute network - - -@mock.patch('openstackclient.api.compute_v2.APIv2.floating_ip_pool_list') +@mock.patch.object(compute_v2, 'list_floating_ip_pools') class TestListFloatingIPPoolCompute(compute_fakes.TestComputev2): # The floating ip pools to list up _floating_ip_pools = compute_fakes.create_floating_ip_pools(count=3) @@ -36,7 +34,6 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - # Get the command object to test self.cmd = floating_ip_pool.ListFloatingIPPool(self.app, None) def test_floating_ip_list(self, fipp_mock): @@ -47,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() + fipp_mock.assert_called_once_with(self.compute_sdk_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 651bf72a5..045fa1f6b 100644 --- a/openstackclient/tests/unit/network/v2/test_network_compute.py +++ b/openstackclient/tests/unit/network/v2/test_network_compute.py @@ -12,22 +12,17 @@ # from unittest import mock -from unittest.mock import call from osc_lib import exceptions +from openstackclient.api import compute_v2 from openstackclient.network.v2 import network from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes from openstackclient.tests.unit import utils as tests_utils -# Tests for Nova network -# - - -@mock.patch('openstackclient.api.compute_v2.APIv2.network_create') +@mock.patch.object(compute_v2, 'create_network') class TestCreateNetworkCompute(compute_fakes.TestComputev2): - # The network to create. _network = compute_fakes.create_one_network() columns = ( @@ -105,7 +100,6 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - # Get the command object to test self.cmd = network.CreateNetwork(self.app, None) def test_network_create_no_options(self, net_mock): @@ -113,7 +107,6 @@ def test_network_create_no_options(self, net_mock): arglist = [] verifylist = [] - # Missing required args should raise exception here self.assertRaises( tests_utils.ParserException, self.check_parser, @@ -131,7 +124,6 @@ def test_network_create_missing_options(self, net_mock): ('name', self._network['label']), ] - # Missing required args should raise exception here self.assertRaises( tests_utils.ParserException, self.check_parser, @@ -156,35 +148,29 @@ def test_network_create_default_options(self, net_mock): columns, data = self.cmd.take_action(parsed_args) net_mock.assert_called_once_with( - **{ - 'subnet': self._network['cidr'], - 'name': self._network['label'], - } + self.compute_sdk_client, + subnet=self._network['cidr'], + name=self._network['label'], ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) -@mock.patch('openstackclient.api.compute_v2.APIv2.network_delete') +@mock.patch.object(compute_v2, 'delete_network') +@mock.patch.object(compute_v2, 'find_network') class TestDeleteNetworkCompute(compute_fakes.TestComputev2): def setUp(self): super().setUp() self.app.client_manager.network_endpoint_enabled = False - # The networks to delete self._networks = compute_fakes.create_networks(count=3) - # Return value of utils.find_resource() - self.compute_client.api.network_find = compute_fakes.get_networks( - networks=self._networks - ) - - # Get the command object to test self.cmd = network.DeleteNetwork(self.app, None) - def test_network_delete_one(self, net_mock): - net_mock.return_value = mock.Mock(return_value=None) + def test_network_delete_one(self, find_net_mock, delete_net_mock): + find_net_mock.side_effect = self._networks + delete_net_mock.return_value = mock.Mock(return_value=None) arglist = [ self._networks[0]['label'], ] @@ -195,35 +181,44 @@ def test_network_delete_one(self, net_mock): result = self.cmd.take_action(parsed_args) - net_mock.assert_called_once_with( - self._networks[0]['label'], + delete_net_mock.assert_called_once_with( + self.compute_sdk_client, + self._networks[0]['id'], ) self.assertIsNone(result) - def test_network_delete_multi(self, net_mock): - net_mock.return_value = mock.Mock(return_value=None) - arglist = [] - for n in self._networks: - arglist.append(n['id']) + def test_network_delete_multi(self, find_net_mock, delete_net_mock): + find_net_mock.side_effect = self._networks + delete_net_mock.return_value = mock.Mock(return_value=None) + arglist = [ + self._networks[0]['id'], + self._networks[1]['id'], + ] verifylist = [ ('network', arglist), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - calls = [] - for n in self._networks: - calls.append(call(n['id'])) - net_mock.assert_has_calls(calls) + 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']), + ] + ) self.assertIsNone(result) - def test_network_delete_multi_with_exception(self, net_mock): - net_mock.return_value = mock.Mock(return_value=None) - net_mock.side_effect = [ - mock.Mock(return_value=None), - exceptions.CommandError, + def test_network_delete_multi_with_exception( + self, find_net_mock, delete_net_mock + ): + find_net_mock.side_effect = [ + self._networks[0], + exceptions.NotFound('foo'), + self._networks[1], ] + delete_net_mock.return_value = mock.Mock(return_value=None) + arglist = [ self._networks[0]['id'], 'xxxx-yyyy-zzzz', @@ -234,20 +229,30 @@ def test_network_delete_multi_with_exception(self, net_mock): ] 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('2 of 3 networks failed to delete.', str(e)) - - net_mock.assert_any_call(self._networks[0]['id']) - net_mock.assert_any_call(self._networks[1]['id']) - net_mock.assert_any_call('xxxx-yyyy-zzzz') + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual('1 of 3 networks failed to delete.', str(exc)) + + 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']), + ] + ) + 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.patch('openstackclient.api.compute_v2.APIv2.network_list') +@mock.patch.object(compute_v2, 'list_networks') class TestListNetworkCompute(compute_fakes.TestComputev2): - # The networks going to be listed up. _networks = compute_fakes.create_networks(count=3) columns = ( @@ -271,7 +276,6 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - # Get the command object to test self.cmd = network.ListNetwork(self.app, None) def test_network_list_no_options(self, net_mock): @@ -280,19 +284,15 @@ def test_network_list_no_options(self, net_mock): 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) - net_mock.assert_called_once_with() + net_mock.assert_called_once_with(self.compute_sdk_client) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) -@mock.patch('openstackclient.api.compute_v2.APIv2.network_find') +@mock.patch.object(compute_v2, 'find_network') class TestShowNetworkCompute(compute_fakes.TestComputev2): - # The network to show. _network = compute_fakes.create_one_network() columns = ( @@ -370,7 +370,6 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - # Get the command object to test self.cmd = network.ShowNetwork(self.app, None) def test_show_no_options(self, net_mock): @@ -398,6 +397,8 @@ def test_show_all_options(self, net_mock): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - net_mock.assert_called_once_with(self._network['label']) + net_mock.assert_called_once_with( + self.compute_sdk_client, self._network['label'] + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) 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 806878810..243f83021 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_compute.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_compute.py @@ -12,17 +12,17 @@ # from unittest import mock -from unittest.mock import call from osc_lib import exceptions +from openstackclient.api import compute_v2 from openstackclient.network.v2 import security_group 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 -@mock.patch('openstackclient.api.compute_v2.APIv2.security_group_create') +@mock.patch.object(compute_v2, 'create_security_group') class TestCreateSecurityGroupCompute(compute_fakes.TestComputev2): project = identity_fakes.FakeProject.create_one_project() domain = identity_fakes.FakeDomain.create_one_domain() @@ -72,6 +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._security_group['name'], self._security_group['name'], ) @@ -94,6 +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._security_group['name'], self._security_group['description'], ) @@ -101,7 +103,7 @@ def test_security_group_create_all_options(self, sg_mock): self.assertCountEqual(self.data, data) -@mock.patch('openstackclient.api.compute_v2.APIv2.security_group_delete') +@mock.patch.object(compute_v2, 'delete_security_group') class TestDeleteSecurityGroupCompute(compute_fakes.TestComputev2): # The security groups to be deleted. _security_groups = compute_fakes.create_security_groups() @@ -111,8 +113,8 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - self.compute_client.api.security_group_find = ( - compute_fakes.get_security_groups(self._security_groups) + compute_v2.find_security_group = mock.Mock( + side_effect=self._security_groups ) # Get the command object to test @@ -131,59 +133,68 @@ 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._security_groups[0]['id'], ) self.assertIsNone(result) def test_security_group_multi_delete(self, sg_mock): sg_mock.return_value = mock.Mock(return_value=None) - arglist = [] - verifylist = [] - - for s in self._security_groups: - arglist.append(s['id']) + arglist = [ + self._security_groups[0]['id'], + self._security_groups[1]['id'], + ] verifylist = [ ('group', arglist), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - calls = [] - for s in self._security_groups: - calls.append(call(s['id'])) - sg_mock.assert_has_calls(calls) + 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'] + ), + ] + ) self.assertIsNone(result) def test_security_group_multi_delete_with_exception(self, sg_mock): sg_mock.return_value = mock.Mock(return_value=None) - sg_mock.side_effect = [ - mock.Mock(return_value=None), - exceptions.CommandError, + compute_v2.find_security_group.side_effect = [ + self._security_groups[0], + exceptions.NotFound('foo'), ] arglist = [ self._security_groups[0]['id'], 'unexist_security_group', ] verifylist = [ - ( - 'group', - [self._security_groups[0]['id'], 'unexist_security_group'], - ), + ('group', arglist), ] - 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('1 of 2 groups failed to delete.', str(e)) - sg_mock.assert_any_call(self._security_groups[0]['id']) - sg_mock.assert_any_call('unexist_security_group') + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertEqual('1 of 2 groups failed to delete.', str(exc)) + + sg_mock.assert_has_calls( + [ + mock.call( + self.compute_sdk_client, self._security_groups[0]['id'] + ), + ] + ) -@mock.patch('openstackclient.api.compute_v2.APIv2.security_group_list') +@mock.patch.object(compute_v2, 'list_security_groups') class TestListSecurityGroupCompute(compute_fakes.TestComputev2): # The security group to be listed. _security_groups = compute_fakes.create_security_groups(count=3) @@ -238,8 +249,9 @@ def test_security_group_list_no_options(self, sg_mock): columns, data = self.cmd.take_action(parsed_args) - kwargs = {'search_opts': {'all_tenants': False}} - sg_mock.assert_called_once_with(**kwargs) + sg_mock.assert_called_once_with( + self.compute_sdk_client, all_projects=False + ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) @@ -255,13 +267,14 @@ def test_security_group_list_all_projects(self, sg_mock): columns, data = self.cmd.take_action(parsed_args) - kwargs = {'search_opts': {'all_tenants': True}} - sg_mock.assert_called_once_with(**kwargs) + sg_mock.assert_called_once_with( + self.compute_sdk_client, all_projects=True + ) self.assertEqual(self.columns_all_projects, columns) self.assertCountEqual(self.data_all_projects, list(data)) -@mock.patch('openstackclient.api.compute_v2.APIv2.security_group_set') +@mock.patch.object(compute_v2, 'update_security_group') class TestSetSecurityGroupCompute(compute_fakes.TestComputev2): # The security group to be set. _security_group = compute_fakes.create_one_security_group() @@ -271,7 +284,7 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - self.compute_client.api.security_group_find = mock.Mock( + compute_v2.find_security_group = mock.Mock( return_value=self._security_group ) @@ -296,9 +309,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._security_group, - self._security_group['name'], - self._security_group['description'], + self.compute_sdk_client, self._security_group['id'] ) self.assertIsNone(result) @@ -323,12 +334,15 @@ def test_security_group_set_all_options(self, sg_mock): result = self.cmd.take_action(parsed_args) sg_mock.assert_called_once_with( - self._security_group, new_name, new_description + self.compute_sdk_client, + self._security_group['id'], + name=new_name, + description=new_description, ) self.assertIsNone(result) -@mock.patch('openstackclient.api.compute_v2.APIv2.security_group_find') +@mock.patch.object(compute_v2, 'find_security_group') class TestShowSecurityGroupCompute(compute_fakes.TestComputev2): # The security group rule to be shown with the group. _security_group_rule = compute_fakes.create_one_security_group_rule() @@ -379,6 +393,8 @@ 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._security_group['id']) + sg_mock.assert_called_once_with( + self.compute_sdk_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 cc780d54a..fc3c0ddb1 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 @@ -9,13 +9,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 unittest.mock import call from osc_lib import exceptions +from openstackclient.api import compute_v2 from openstackclient.network import utils as network_utils from openstackclient.network.v2 import security_group_rule from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes @@ -23,7 +22,7 @@ from openstackclient.tests.unit import utils as tests_utils -@mock.patch('openstackclient.api.compute_v2.APIv2.security_group_rule_create') +@mock.patch.object(compute_v2, 'create_security_group_rule') class TestCreateSecurityGroupRuleCompute(compute_fakes.TestComputev2): project = identity_fakes.FakeProject.create_one_project() domain = identity_fakes.FakeDomain.create_one_domain() @@ -51,7 +50,7 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - self.compute_client.api.security_group_find = mock.Mock( + compute_v2.find_security_group = mock.Mock( return_value=self._security_group, ) @@ -159,9 +158,8 @@ def test_security_group_rule_create_default_rule(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) - # TODO(dtroyer): save this for the security group rule changes - # self.compute_client.api.security_group_rule_create.assert_called_once_with( sgr_mock.assert_called_once_with( + self.compute_sdk_client, security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], from_port=self._security_group_rule['from_port'], @@ -203,9 +201,8 @@ def test_security_group_rule_create_remote_group(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) - # TODO(dtroyer): save this for the security group rule changes - # self.compute_client.api.security_group_rule_create.assert_called_once_with( sgr_mock.assert_called_once_with( + self.compute_sdk_client, security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], from_port=self._security_group_rule['from_port'], @@ -242,9 +239,8 @@ def test_security_group_rule_create_remote_ip(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) - # TODO(dtroyer): save this for the security group rule changes - # self.compute_client.api.security_group_rule_create.assert_called_once_with( sgr_mock.assert_called_once_with( + self.compute_sdk_client, security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], from_port=self._security_group_rule['from_port'], @@ -282,9 +278,8 @@ def test_security_group_rule_create_proto_option(self, sgr_mock): columns, data = self.cmd.take_action(parsed_args) - # TODO(dtroyer): save this for the security group rule changes - # self.compute_client.api.security_group_rule_create.assert_called_once_with( sgr_mock.assert_called_once_with( + self.compute_sdk_client, security_group_id=self._security_group['id'], ip_protocol=self._security_group_rule['ip_protocol'], from_port=self._security_group_rule['from_port'], @@ -296,7 +291,7 @@ def test_security_group_rule_create_proto_option(self, sgr_mock): self.assertEqual(expected_data, data) -@mock.patch('openstackclient.api.compute_v2.APIv2.security_group_rule_delete') +@mock.patch.object(compute_v2, 'delete_security_group_rule') class TestDeleteSecurityGroupRuleCompute(compute_fakes.TestComputev2): # The security group rule to be deleted. _security_group_rules = compute_fakes.create_security_group_rules(count=2) @@ -320,26 +315,35 @@ def test_security_group_rule_delete(self, sgr_mock): result = self.cmd.take_action(parsed_args) - sgr_mock.assert_called_once_with(self._security_group_rules[0]['id']) + sgr_mock.assert_called_once_with( + self.compute_sdk_client, self._security_group_rules[0]['id'] + ) self.assertIsNone(result) def test_security_group_rule_delete_multi(self, sgr_mock): - arglist = [] - verifylist = [] - - for s in self._security_group_rules: - arglist.append(s['id']) + arglist = [ + self._security_group_rules[0]['id'], + self._security_group_rules[1]['id'], + ] verifylist = [ ('rule', arglist), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - calls = [] - for s in self._security_group_rules: - calls.append(call(s['id'])) - sgr_mock.assert_has_calls(calls) + sgr_mock.assert_has_calls( + [ + mock.call( + self.compute_sdk_client, + self._security_group_rules[0]['id'], + ), + mock.call( + self.compute_sdk_client, + self._security_group_rules[1]['id'], + ), + ] + ) self.assertIsNone(result) def test_security_group_rule_delete_multi_with_exception(self, sgr_mock): @@ -348,12 +352,11 @@ def test_security_group_rule_delete_multi_with_exception(self, sgr_mock): 'unexist_rule', ] verifylist = [ - ('rule', [self._security_group_rules[0]['id'], 'unexist_rule']), + ('rule', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - find_mock_result = [None, exceptions.CommandError] - sgr_mock.side_effect = find_mock_result + sgr_mock.side_effect = [None, exceptions.NotFound('foo')] try: self.cmd.take_action(parsed_args) @@ -361,8 +364,15 @@ def test_security_group_rule_delete_multi_with_exception(self, sgr_mock): except exceptions.CommandError as e: self.assertEqual('1 of 2 rules failed to delete.', str(e)) - sgr_mock.assert_any_call(self._security_group_rules[0]['id']) - sgr_mock.assert_any_call('unexist_rule') + sgr_mock.assert_has_calls( + [ + mock.call( + self.compute_sdk_client, + self._security_group_rules[0]['id'], + ), + mock.call(self.compute_sdk_client, 'unexist_rule'), + ] + ) class TestListSecurityGroupRuleCompute(compute_fakes.TestComputev2): @@ -432,10 +442,10 @@ def setUp(self): self.app.client_manager.network_endpoint_enabled = False - self.compute_client.api.security_group_find = mock.Mock( + compute_v2.find_security_group = mock.Mock( return_value=self._security_group, ) - self.compute_client.api.security_group_list = mock.Mock( + compute_v2.list_security_groups = mock.Mock( return_value=[self._security_group], ) @@ -446,8 +456,8 @@ def test_security_group_rule_list_default(self): parsed_args = self.check_parser(self.cmd, [], []) columns, data = self.cmd.take_action(parsed_args) - self.compute_client.api.security_group_list.assert_called_once_with( - search_opts={'all_tenants': False} + compute_v2.list_security_groups.assert_called_once_with( + self.compute_sdk_client, all_projects=False ) self.assertEqual(self.expected_columns_no_group, columns) self.assertEqual(self.expected_data_no_group, list(data)) @@ -462,8 +472,8 @@ def test_security_group_rule_list_with_group(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_client.api.security_group_find.assert_called_once_with( - self._security_group['id'] + compute_v2.find_security_group.assert_called_once_with( + self.compute_sdk_client, self._security_group['id'] ) self.assertEqual(self.expected_columns_with_group, columns) self.assertEqual(self.expected_data_with_group, list(data)) @@ -478,8 +488,8 @@ def test_security_group_rule_list_all_projects(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_client.api.security_group_list.assert_called_once_with( - search_opts={'all_tenants': True} + compute_v2.list_security_groups.assert_called_once_with( + self.compute_sdk_client, all_projects=True ) self.assertEqual(self.expected_columns_no_group, columns) self.assertEqual(self.expected_data_no_group, list(data)) @@ -494,8 +504,8 @@ def test_security_group_rule_list_with_ignored_options(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.compute_client.api.security_group_list.assert_called_once_with( - search_opts={'all_tenants': False} + compute_v2.list_security_groups.assert_called_once_with( + self.compute_sdk_client, all_projects=False ) self.assertEqual(self.expected_columns_no_group, columns) self.assertEqual(self.expected_data_no_group, list(data)) @@ -517,7 +527,7 @@ def setUp(self): # Build a security group fake customized for this test. security_group_rules = [self._security_group_rule] security_group = {'rules': security_group_rules} - self.compute_client.api.security_group_list = mock.Mock( + compute_v2.list_security_groups = mock.Mock( return_value=[security_group], ) @@ -540,6 +550,8 @@ def test_security_group_rule_show_all_options(self): columns, data = self.cmd.take_action(parsed_args) - self.compute_client.api.security_group_list.assert_called_once_with() + compute_v2.list_security_groups.assert_called_once_with( + self.compute_sdk_client + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) From 75b9214f6c1b04700ec5fa542e53604fd488936f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 10 May 2024 13:59:34 +0100 Subject: [PATCH 156/403] tests: Remove references to novaclient methods These should have been cleaned up as we went but weren't. Fix things now. Change-Id: I80b29c0fd6e4f557c2efc45474279306f50c4fd7 Signed-off-by: Stephen Finucane --- .../tests/unit/compute/v2/test_server.py | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 4b87dc85b..c96e0fada 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -64,28 +64,9 @@ class TestServer(compute_fakes.TestComputev2): def setUp(self): super().setUp() - # Get a shortcut to the compute client ServerMigrationsManager Mock - self.server_migrations_mock = self.compute_client.server_migrations - self.server_migrations_mock.reset_mock() - - # Get a shortcut to the compute client VolumeManager mock - self.servers_volumes_mock = self.compute_client.volumes - self.servers_volumes_mock.reset_mock() - - # Get a shortcut to the compute client MigrationManager mock - self.migrations_mock = self.compute_client.migrations - self.migrations_mock.reset_mock() - - # Get a shortcut to the compute client FlavorManager Mock - self.flavors_mock = self.compute_client.flavors - self.flavors_mock.reset_mock() - # Set object attributes to be tested. Could be overwritten in subclass. self.attrs = {} - # Set object methods to be tested. Could be overwritten in subclass. - self.methods = {} - def setup_sdk_servers_mock(self, count): servers = compute_fakes.create_sdk_servers( attrs=self.attrs, @@ -662,11 +643,6 @@ def setUp(self): # Get the command object to test self.cmd = server.AddPort(self.app, None) - # Set add_fixed_ip method to be tested. - self.methods = { - 'interface_attach': None, - } - self.find_port = mock.Mock() self.app.client_manager.network.find_port = self.find_port @@ -754,10 +730,6 @@ class TestServerVolume(TestServer): def setUp(self): super().setUp() - self.methods = { - 'create_volume_attachment': None, - } - self.servers = self.setup_sdk_servers_mock(count=1) self.volumes = self.setup_sdk_volumes_mock(count=1) @@ -1092,11 +1064,6 @@ def setUp(self): # Get the command object to test self.cmd = server.AddNetwork(self.app, None) - # Set add_fixed_ip method to be tested. - self.methods = { - 'interface_attach': None, - } - self.find_network = mock.Mock() self.app.client_manager.network.find_network = self.find_network @@ -7342,11 +7309,6 @@ def setUp(self): # Get the command object to test self.cmd = server.RemovePort(self.app, None) - # Set method to be tested. - self.methods = { - 'delete_server_interface': None, - } - self.find_port = mock.Mock() self.app.client_manager.network.find_port = self.find_port @@ -7392,10 +7354,6 @@ def setUp(self): # Set method to be tested. self.fake_inf = mock.Mock() - self.methods = { - 'server_interfaces': [self.fake_inf], - 'delete_server_interface': None, - } self.find_network = mock.Mock() self.app.client_manager.network.find_network = self.find_network From f5f543b8de67362339a957e10e98c3d111f779e9 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 10 Jul 2024 11:22:27 +0100 Subject: [PATCH 157/403] quota: Move nova-network-related quota This was incorrectly grouped as a general compute quota, when in fact it's a nova-network quota. Move it. Change-Id: If93c95bc181ab766137b61943a09821810345300 Signed-off-by: Stephen Finucane --- openstackclient/common/quota.py | 6 ++---- openstackclient/tests/unit/common/test_quota.py | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 78688dfa8..4eb44b393 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -33,7 +33,6 @@ COMPUTE_QUOTAS = { 'cores': 'cores', - 'fixed_ips': 'fixed-ips', 'injected_file_content_bytes': 'injected-file-size', 'injected_file_path_bytes': 'injected-path-size', 'injected_files': 'injected-files', @@ -41,8 +40,8 @@ 'key_pairs': 'key-pairs', 'metadata_items': 'properties', 'ram': 'ram', - 'server_groups': 'server-groups', 'server_group_members': 'server-group-members', + 'server_groups': 'server-groups', } VOLUME_QUOTAS = { @@ -61,6 +60,7 @@ ] NOVA_NETWORK_QUOTAS = { + 'fixed_ips': 'fixed-ips', 'floating_ips': 'floating-ips', 'security_group_rules': 'secgroup-rules', 'security_groups': 'secgroups', @@ -405,7 +405,6 @@ def take_action(self, parsed_args): columns = ( 'id', 'cores', - 'fixed_ips', 'injected_files', 'injected_file_content_bytes', 'injected_file_path_bytes', @@ -419,7 +418,6 @@ def take_action(self, parsed_args): column_headers = ( 'Project ID', 'Cores', - 'Fixed IPs', 'Injected Files', 'Injected File Content Bytes', 'Injected File Path Bytes', diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index 7edddbb19..b07d6e015 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -73,7 +73,6 @@ class TestQuotaList(TestQuota): compute_column_header = ( 'Project ID', 'Cores', - 'Fixed IPs', 'Injected Files', 'Injected File Content Bytes', 'Injected File Path Bytes', @@ -130,7 +129,6 @@ def setUp(self): self.compute_reference_data = ( self.projects[0].id, self.compute_quotas[0].cores, - self.compute_quotas[0].fixed_ips, self.compute_quotas[0].injected_files, self.compute_quotas[0].injected_file_content_bytes, self.compute_quotas[0].injected_file_path_bytes, From ba2d2358e614b8def13834c760ba551e973b33b1 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 10 Jul 2024 12:01:35 +0100 Subject: [PATCH 158/403] quota: Remove deprecated quota options These are all deprecated for over 18 months (change I0dd38e5cb252a01d5817ed168be040b21b35e348). It's time to remove them and simplify this code. Change-Id: I9ee3bfebbad21eec3eb1b475a813bcbc450edea4 Signed-off-by: Stephen Finucane Change-Id: Ibdd329a6db8bd176af065d7f5190f0901d3c3f8d --- openstackclient/common/quota.py | 221 +++--------------- .../tests/functional/common/test_quota.py | 100 ++++---- .../tests/unit/common/test_quota.py | 200 ---------------- ...ta-show-class-option-2109a6ff7ac18e80.yaml | 8 + 4 files changed, 80 insertions(+), 449 deletions(-) create mode 100644 releasenotes/notes/remove-deprecated-quota-show-class-option-2109a6ff7ac18e80.yaml diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 4eb44b393..d412e05cf 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.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. -# """Quota action implementations""" @@ -130,18 +129,12 @@ def get_compute_quotas( app, project_id, *, - quota_class=False, detail=False, default=False, ): try: client = app.client_manager.compute - if quota_class: - # NOTE(stephenfin): The 'project' argument here could be anything - # as the nova API doesn't care what you pass in. We only pass the - # project in to avoid weirding people out :) - quota = client.quota_classes.get(project_id) - elif default: + if default: quota = client.quotas.defaults(project_id) else: quota = client.quotas.get(project_id, detail=detail) @@ -156,15 +149,12 @@ def get_volume_quotas( app, project_id, *, - quota_class=False, detail=False, default=False, ): try: client = app.client_manager.volume - if quota_class: - quota = client.quota_classes.get(project_id) - elif default: + if default: quota = client.quotas.defaults(project_id) else: quota = client.quotas.get(project_id, usage=detail) @@ -180,7 +170,6 @@ def get_network_quotas( app, project_id, *, - quota_class=False, detail=False, default=False, ): @@ -207,11 +196,6 @@ def _network_quota_to_dict(network_quota, detail=False): return result - # neutron doesn't have the concept of quota classes and if we're using - # nova-network we already fetched this - if quota_class: - return {} - # we have nothing to return if we are not using neutron if not app.client_manager.is_network_endpoint_enabled(): return {} @@ -227,34 +211,14 @@ def _network_quota_to_dict(network_quota, detail=False): class ListQuota(command.Lister): - _description = _( - "List quotas for all projects with non-default quota values or " - "list detailed quota information for requested project" - ) + """List quotas for all projects with non-default quota values. + + Empty output means all projects are using default quotas, which can be + inspected with 'openstack quota show --default'. + """ def get_parser(self, prog_name): parser = super().get_parser(prog_name) - # TODO(stephenfin): Remove in OSC 8.0 - parser.add_argument( - '--project', - metavar='', - help=_( - "**Deprecated** List quotas for this project " - "(name or ID). " - "Use 'quota show' instead." - ), - ) - # TODO(stephenfin): Remove in OSC 8.0 - parser.add_argument( - '--detail', - dest='detail', - action='store_true', - default=False, - help=_( - "**Deprecated** Show details about quotas usage. " - "Use 'quota show --usage' instead." - ), - ) option = parser.add_mutually_exclusive_group(required=True) option.add_argument( '--compute', @@ -276,102 +240,13 @@ def get_parser(self, prog_name): ) return parser - def _get_detailed_quotas(self, parsed_args): - project_info = get_project(self.app, parsed_args.project) - project = project_info['id'] - - quotas = {} - - if parsed_args.compute: - quotas.update( - get_compute_quotas( - self.app, - project, - detail=parsed_args.detail, - ) - ) - - if parsed_args.network: - quotas.update( - get_network_quotas( - self.app, - project, - detail=parsed_args.detail, - ) - ) - - if parsed_args.volume: - quotas.update( - get_volume_quotas( - self.app, - parsed_args, - detail=parsed_args.detail, - ), - ) - - result = [] - for resource, values in quotas.items(): - # NOTE(slaweq): there is no detailed quotas info for some resources - # and it shouldn't be displayed here - if isinstance(values, dict): - result.append( - { - 'resource': resource, - 'in_use': values.get('in_use'), - 'reserved': values.get('reserved'), - 'limit': values.get('limit'), - } - ) - - columns = ( - 'resource', - 'in_use', - 'reserved', - 'limit', - ) - column_headers = ( - 'Resource', - 'In Use', - 'Reserved', - 'Limit', - ) - - return ( - column_headers, - (utils.get_dict_properties(s, columns) for s in result), - ) - def take_action(self, parsed_args): - if parsed_args.detail: - msg = _( - "The --detail option has been deprecated. " - "Use 'openstack quota show --usage' instead." - ) - self.log.warning(msg) - elif parsed_args.project: # elif to avoid being too noisy - msg = _( - "The --project option has been deprecated. " - "Use 'openstack quota show' instead." - ) - self.log.warning(msg) - result = [] - project_ids = [] - if parsed_args.project is None: - for p in self.app.client_manager.identity.projects.list(): - project_ids.append(getattr(p, 'id', '')) - else: - identity_client = self.app.client_manager.identity - project = utils.find_resource( - identity_client.projects, - parsed_args.project, - ) - project_ids.append(getattr(project, 'id', '')) + project_ids = [ + p.id for p in self.app.client_manager.identity.projects.list() + ] if parsed_args.compute: - if parsed_args.detail: - return self._get_detailed_quotas(parsed_args) - compute_client = self.app.client_manager.compute for p in project_ids: try: @@ -434,9 +309,6 @@ def take_action(self, parsed_args): ) if parsed_args.volume: - if parsed_args.detail: - return self._get_detailed_quotas(parsed_args) - volume_client = self.app.client_manager.volume for p in project_ids: try: @@ -488,9 +360,6 @@ def take_action(self, parsed_args): ) if parsed_args.network: - if parsed_args.detail: - return self._get_detailed_quotas(parsed_args) - client = self.app.client_manager.network for p in project_ids: try: @@ -728,22 +597,24 @@ def take_action(self, parsed_args): "Network quotas are ignored since quota classes are not " "supported." ) - else: - project = utils.find_resource( - identity_client.projects, - parsed_args.project, - ).id - if compute_kwargs: - compute_client.quotas.update(project, **compute_kwargs) - if volume_kwargs: - volume_client.quotas.update(project, **volume_kwargs) - if ( - network_kwargs - and self.app.client_manager.is_network_endpoint_enabled() - ): - network_client = self.app.client_manager.network - network_client.update_quota(project, **network_kwargs) + return + + project = utils.find_resource( + identity_client.projects, + parsed_args.project, + ).id + + if compute_kwargs: + compute_client.quotas.update(project, **compute_kwargs) + if volume_kwargs: + volume_client.quotas.update(project, **volume_kwargs) + if ( + network_kwargs + and self.app.client_manager.is_network_endpoint_enabled() + ): + network_client = self.app.client_manager.network + network_client.update_quota(project, **network_kwargs) class ShowQuota(command.Lister): @@ -758,29 +629,14 @@ def get_parser(self, prog_name): parser = super().get_parser(prog_name) parser.add_argument( 'project', - metavar='', + metavar='', nargs='?', help=_( - 'Show quotas for this project or class (name or ID) ' + 'Show quotas for this project (name or ID) ' '(defaults to current project)' ), ) type_group = parser.add_mutually_exclusive_group() - # TODO(stephenfin): Remove in OSC 8.0 - type_group.add_argument( - '--class', - dest='quota_class', - action='store_true', - default=False, - help=_( - '**Deprecated** Show quotas for . ' - 'Deprecated as quota classes were never fully implemented ' - 'and only the default class is supported. ' - 'Use --default instead which is also supported by the network ' - 'service. ' - '(compute and volume only)' - ), - ) type_group.add_argument( '--default', dest='default', @@ -832,20 +688,8 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - project = parsed_args.project - - if parsed_args.quota_class: - msg = _( - "The '--class' option has been deprecated. Quota classes were " - "never fully implemented and the compute and volume services " - "only support a single 'default' quota class while the " - "network service does not support quota classes at all. " - "Please use 'openstack quota show --default' instead." - ) - self.log.warning(msg) - else: - project_info = get_project(self.app, parsed_args.project) - project = project_info['id'] + project_info = get_project(self.app, parsed_args.project) + project = project_info['id'] compute_quota_info = {} volume_quota_info = {} @@ -861,7 +705,6 @@ def take_action(self, parsed_args): self.app, project, detail=parsed_args.usage, - quota_class=parsed_args.quota_class, default=parsed_args.default, ) if parsed_args.service in {'all', 'volume'}: @@ -869,7 +712,6 @@ def take_action(self, parsed_args): self.app, project, detail=parsed_args.usage, - quota_class=parsed_args.quota_class, default=parsed_args.default, ) if parsed_args.service in {'all', 'network'}: @@ -877,7 +719,6 @@ def take_action(self, parsed_args): self.app, project, detail=parsed_args.usage, - quota_class=parsed_args.quota_class, default=parsed_args.default, ) diff --git a/openstackclient/tests/functional/common/test_quota.py b/openstackclient/tests/functional/common/test_quota.py index 0ee27c84e..e46d237db 100644 --- a/openstackclient/tests/functional/common/test_quota.py +++ b/openstackclient/tests/functional/common/test_quota.py @@ -39,40 +39,6 @@ def tearDownClass(cls): cls.openstack(f'project delete {cls.PROJECT_NAME}') super().tearDownClass() - def test_quota_list_details_compute(self): - expected_headers = ["Resource", "In Use", "Reserved", "Limit"] - cmd_output = self.openstack( - 'quota list --detail --compute', - parse_output=True, - ) - self.assertIsNotNone(cmd_output) - resources = [] - for row in cmd_output: - row_headers = [str(r) for r in row.keys()] - self.assertEqual(sorted(expected_headers), sorted(row_headers)) - resources.append(row['Resource']) - # Ensure that returned quota is compute quota - self.assertIn("instances", resources) - # and that there is no network quota here - self.assertNotIn("networks", resources) - - def test_quota_list_details_network(self): - expected_headers = ["Resource", "In Use", "Reserved", "Limit"] - cmd_output = self.openstack( - 'quota list --detail --network', - parse_output=True, - ) - self.assertIsNotNone(cmd_output) - resources = [] - for row in cmd_output: - row_headers = [str(r) for r in row.keys()] - self.assertEqual(sorted(expected_headers), sorted(row_headers)) - resources.append(row['Resource']) - # Ensure that returned quota is network quota - self.assertIn("networks", resources) - # and that there is no compute quota here - self.assertNotIn("instances", resources) - def test_quota_list_network_option(self): if not self.haz_network: self.skipTest("No Network service present") @@ -155,41 +121,23 @@ def test_quota_set_project(self): if self.haz_network: self.assertTrue(cmd_output["routers"] >= 0) - def test_quota_set_class(self): + def test_quota_set_default(self): self.openstack( - 'quota set --key-pairs 33 --snapshots 43 ' + '--class default' + 'quota set --key-pairs 33 --snapshots 43 --class default' ) cmd_output = self.openstack( - 'quota show --class default', - parse_output=True, - ) - self.assertIsNotNone(cmd_output) - cmd_output = {x['Resource']: x['Limit'] for x in cmd_output} - self.assertEqual( - 33, - cmd_output["key-pairs"], - ) - self.assertEqual( - 43, - cmd_output["snapshots"], - ) - - # Check default quota class - cmd_output = self.openstack( - 'quota show --class', + 'quota show --default', parse_output=True, ) self.assertIsNotNone(cmd_output) - # We don't necessarily know the default quotas, we're checking the - # returned attributes cmd_output = {x['Resource']: x['Limit'] for x in cmd_output} - self.assertTrue(cmd_output["key-pairs"] >= 0) - self.assertTrue(cmd_output["snapshots"] >= 0) + self.assertEqual(33, cmd_output["key-pairs"]) + self.assertEqual(43, cmd_output["snapshots"]) def _restore_quota_limit(self, resource, limit, project): self.openstack(f'quota set --{resource} {limit} {project}') - def test_quota_network_set_with_no_force(self): + def test_quota_set_network_with_no_force(self): if not self.haz_network: self.skipTest('No Network service present') if not self.is_extension_enabled('quota-check-limit'): @@ -227,7 +175,7 @@ def test_quota_network_set_with_no_force(self): 'quota set --networks 1 --no-force ' + self.PROJECT_NAME, ) - def test_quota_network_set_with_force(self): + def test_quota_set_network_with_force(self): self.skipTest('story 2010110') if not self.haz_network: self.skipTest('No Network service present') @@ -274,3 +222,37 @@ def test_quota_network_set_with_force(self): ) self.assertIsNotNone(cmd_output) self.assertEqual(1, cmd_output[0]['Networks']) + + def test_quota_show(self): + expected_headers = ["Resource", "Limit"] + cmd_output = self.openstack( + 'quota show', + parse_output=True, + ) + self.assertIsNotNone(cmd_output) + resources = [] + for row in cmd_output: + row_headers = [str(r) for r in row.keys()] + self.assertEqual(sorted(expected_headers), sorted(row_headers)) + resources.append(row['Resource']) + # Ensure that returned quota has network quota... + self.assertIn("networks", resources) + # ...and compute quota + self.assertIn("instances", resources) + + def test_quota_show_usage_option(self): + expected_headers = ["Resource", "Limit", "In Use", "Reserved"] + cmd_output = self.openstack( + 'quota show --usage', + parse_output=True, + ) + self.assertIsNotNone(cmd_output) + resources = [] + for row in cmd_output: + row_headers = [str(r) for r in row.keys()] + self.assertEqual(sorted(expected_headers), sorted(row_headers)) + resources.append(row['Resource']) + # Ensure that returned quota has network quota... + self.assertIn("networks", resources) + # ...and compute quota + self.assertIn("instances", resources) diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index b07d6e015..f18e917b2 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -189,115 +189,6 @@ def setUp(self): self.cmd = quota.ListQuota(self.app, None) - @staticmethod - def _get_detailed_reference_data(quota): - reference_data = [] - for name, values in quota.to_dict().items(): - if type(values) is dict: - if 'used' in values: - # For network quota it's "used" key instead of "in_use" - in_use = values['used'] - else: - in_use = values['in_use'] - resource_values = [in_use, values['reserved'], values['limit']] - reference_data.append(tuple([name] + resource_values)) - return reference_data - - def test_quota_list_details_compute(self): - detailed_quota = compute_fakes.create_one_comp_detailed_quota() - - detailed_column_header = ( - 'Resource', - 'In Use', - 'Reserved', - 'Limit', - ) - detailed_reference_data = self._get_detailed_reference_data( - detailed_quota - ) - - self.compute_client.quotas.get = mock.Mock(return_value=detailed_quota) - - arglist = [ - '--detail', - '--compute', - ] - verifylist = [ - ('detail', True), - ('compute', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - ret_quotas = list(data) - - self.assertEqual(detailed_column_header, columns) - self.assertEqual(sorted(detailed_reference_data), sorted(ret_quotas)) - - def test_quota_list_details_network(self): - detailed_quota = ( - network_fakes.FakeQuota.create_one_net_detailed_quota() - ) - - detailed_column_header = ( - 'Resource', - 'In Use', - 'Reserved', - 'Limit', - ) - detailed_reference_data = self._get_detailed_reference_data( - detailed_quota - ) - - self.network_client.get_quota = mock.Mock(return_value=detailed_quota) - - arglist = [ - '--detail', - '--network', - ] - verifylist = [ - ('detail', True), - ('network', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - ret_quotas = list(data) - - self.assertEqual(detailed_column_header, columns) - self.assertEqual(sorted(detailed_reference_data), sorted(ret_quotas)) - - def test_quota_list_details_volume(self): - detailed_quota = volume_fakes.create_one_detailed_quota() - - detailed_column_header = ( - 'Resource', - 'In Use', - 'Reserved', - 'Limit', - ) - detailed_reference_data = self._get_detailed_reference_data( - detailed_quota - ) - - self.volume_client.quotas.get = mock.Mock(return_value=detailed_quota) - - arglist = [ - '--detail', - '--volume', - ] - verifylist = [ - ('detail', True), - ('volume', True), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - ret_quotas = list(data) - - self.assertEqual(detailed_column_header, columns) - self.assertEqual(sorted(detailed_reference_data), sorted(ret_quotas)) - def test_quota_list_compute(self): # Two projects with non-default quotas self.compute_client.quotas.get = mock.Mock( @@ -414,30 +305,6 @@ def test_quota_list_compute_no_project_5xx(self): parsed_args, ) - def test_quota_list_compute_by_project(self): - # Two projects with non-default quotas - self.compute_client.quotas.get = mock.Mock( - side_effect=self.compute_quotas, - ) - - arglist = [ - '--compute', - '--project', - self.projects[0].name, - ] - verifylist = [ - ('compute', True), - ('project', self.projects[0].name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - ret_quotas = list(data) - - self.assertEqual(self.compute_column_header, columns) - self.assertEqual(self.compute_reference_data, ret_quotas[0]) - self.assertEqual(1, len(ret_quotas)) - def test_quota_list_network(self): # Two projects with non-default quotas self.network_client.get_quota = mock.Mock( @@ -507,30 +374,6 @@ def test_quota_list_network_no_project(self): self.assertEqual(self.network_reference_data, ret_quotas[0]) self.assertEqual(1, len(ret_quotas)) - def test_quota_list_network_by_project(self): - # Two projects with non-default quotas - self.network_client.get_quota = mock.Mock( - side_effect=self.network_quotas, - ) - - arglist = [ - '--network', - '--project', - self.projects[0].name, - ] - verifylist = [ - ('network', True), - ('project', self.projects[0].name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - ret_quotas = list(data) - - self.assertEqual(self.network_column_header, columns) - self.assertEqual(self.network_reference_data, ret_quotas[0]) - self.assertEqual(1, len(ret_quotas)) - def test_quota_list_volume(self): # Two projects with non-default quotas self.volume_client.quotas.get = mock.Mock( @@ -600,30 +443,6 @@ def test_quota_list_volume_no_project(self): self.assertEqual(self.volume_reference_data, ret_quotas[0]) self.assertEqual(1, len(ret_quotas)) - def test_quota_list_volume_by_project(self): - # Two projects with non-default quotas - self.volume_client.quotas.get = mock.Mock( - side_effect=self.volume_quotas, - ) - - arglist = [ - '--volume', - '--project', - self.projects[0].name, - ] - verifylist = [ - ('volume', True), - ('project', self.projects[0].name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - ret_quotas = list(data) - - self.assertEqual(self.volume_column_header, columns) - self.assertEqual(self.volume_reference_data, ret_quotas[0]) - self.assertEqual(1, len(ret_quotas)) - class TestQuotaSet(TestQuota): def setUp(self): @@ -1227,25 +1046,6 @@ def test_quota_show__with_default(self): ) self.assertNotCalled(self.network_client.get_quota) - def test_quota_show__with_class(self): - arglist = [ - '--class', - 'default', - ] - verifylist = [ - ('quota_class', True), - ('project', 'default'), # project is actually a class here - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.cmd.take_action(parsed_args) - - self.compute_quotas_class_mock.get.assert_called_once_with('default') - self.volume_quotas_class_mock.get.assert_called_once_with('default') - # neutron doesn't have the concept of quota classes - self.assertNotCalled(self.network_client.get_quota) - self.assertNotCalled(self.network_client.get_quota_default) - def test_quota_show__with_usage(self): # update mocks to return detailed quota instead self.compute_quota = compute_fakes.create_one_comp_detailed_quota() diff --git a/releasenotes/notes/remove-deprecated-quota-show-class-option-2109a6ff7ac18e80.yaml b/releasenotes/notes/remove-deprecated-quota-show-class-option-2109a6ff7ac18e80.yaml new file mode 100644 index 000000000..ab1ce5863 --- /dev/null +++ b/releasenotes/notes/remove-deprecated-quota-show-class-option-2109a6ff7ac18e80.yaml @@ -0,0 +1,8 @@ +--- +upgrade: + - | + The ``--class`` options of the ``quota show`` command, which was deprecated + in 6.1.0 (Antelope), has now been removed in favour of the ``--default`` + option. Quota classes were never fully implemented and the compute and + volume services only support a single ``default`` quota class while the + network service does not support quota classes at all. From 7d8baa87bba1003d61f08f0d20a4748513e1c45a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 11 Jul 2024 11:10:50 +0100 Subject: [PATCH 159/403] quota: Add 'quota set --default' option This should have been added as a counterpart to the 'quota show --default' option way back when we added that. Better late than never! Change-Id: I0e3719e585353664fea6f23ec658a330086db3df Signed-off-by: Stephen Finucane --- openstackclient/common/quota.py | 32 ++++--- .../tests/unit/common/test_quota.py | 83 +++++++++++++++++++ ...a-set-default-option-bc26d37dc150533b.yaml | 7 ++ 3 files changed, 111 insertions(+), 11 deletions(-) create mode 100644 releasenotes/notes/quota-set-default-option-bc26d37dc150533b.yaml diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index d412e05cf..abbbe3cdb 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -461,10 +461,15 @@ def get_parser(self, prog_name): parser.add_argument( 'project', metavar='', - help=_('Set quotas for this project or class (name or ID)'), + nargs='?', + help=_( + 'Set quotas for this project or class (name or ID) ' + '(defaults to current project)' + ), ) # TODO(stephenfin): Remove in OSC 8.0 - parser.add_argument( + type_group = parser.add_mutually_exclusive_group() + type_group.add_argument( '--class', dest='quota_class', action='store_true', @@ -476,6 +481,13 @@ def get_parser(self, prog_name): '(compute and volume only)' ), ) + type_group.add_argument( + '--default', + dest='default', + action='store_true', + default=False, + help=_('Set default quotas for '), + ) for k, v, h in self._build_options_list(): parser.add_argument( '--%s' % v, @@ -529,13 +541,13 @@ def take_action(self, parsed_args): "never fully implemented and the compute and volume services " "only support a single 'default' quota class while the " "network service does not support quota classes at all. " - "Please use 'openstack quota show --default' instead." + "Please use 'openstack quota set --default' instead." ) self.log.warning(msg) - identity_client = self.app.client_manager.identity compute_client = self.app.client_manager.compute volume_client = self.app.client_manager.volume + compute_kwargs = {} for k, v in COMPUTE_QUOTAS.items(): value = getattr(parsed_args, k, None) @@ -581,15 +593,15 @@ def take_action(self, parsed_args): if value is not None: compute_kwargs[k] = value - if parsed_args.quota_class: + if parsed_args.quota_class or parsed_args.default: if compute_kwargs: compute_client.quota_classes.update( - parsed_args.project, + parsed_args.project or 'default', **compute_kwargs, ) if volume_kwargs: volume_client.quota_classes.update( - parsed_args.project, + parsed_args.project or 'default', **volume_kwargs, ) if network_kwargs: @@ -600,10 +612,8 @@ def take_action(self, parsed_args): return - project = utils.find_resource( - identity_client.projects, - parsed_args.project, - ).id + project_info = get_project(self.app, parsed_args.project) + project = project_info['id'] if compute_kwargs: compute_client.quotas.update(project, **compute_kwargs) diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index f18e917b2..78c84e163 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -788,6 +788,89 @@ def test_quota_set_with_class(self): self.assertNotCalled(self.network_client.update_quota) self.assertIsNone(result) + def test_quota_set_default(self): + arglist = [ + '--injected-files', + str(compute_fakes.injected_file_num), + '--injected-file-size', + str(compute_fakes.injected_file_size_num), + '--injected-path-size', + str(compute_fakes.injected_path_size_num), + '--key-pairs', + str(compute_fakes.key_pair_num), + '--cores', + str(compute_fakes.core_num), + '--ram', + str(compute_fakes.ram_num), + '--instances', + str(compute_fakes.instance_num), + '--properties', + str(compute_fakes.property_num), + '--server-groups', + str(compute_fakes.servgroup_num), + '--server-group-members', + str(compute_fakes.servgroup_members_num), + '--gigabytes', + str(compute_fakes.floating_ip_num), + '--snapshots', + str(compute_fakes.fix_ip_num), + '--volumes', + str(volume_fakes.QUOTA['volumes']), + '--network', + str(network_fakes.QUOTA['network']), + '--default', + ] + verifylist = [ + ('injected_files', compute_fakes.injected_file_num), + ( + 'injected_file_content_bytes', + compute_fakes.injected_file_size_num, + ), + ('injected_file_path_bytes', compute_fakes.injected_path_size_num), + ('key_pairs', compute_fakes.key_pair_num), + ('cores', compute_fakes.core_num), + ('ram', compute_fakes.ram_num), + ('instances', compute_fakes.instance_num), + ('metadata_items', compute_fakes.property_num), + ('server_groups', compute_fakes.servgroup_num), + ('server_group_members', compute_fakes.servgroup_members_num), + ('gigabytes', compute_fakes.floating_ip_num), + ('snapshots', compute_fakes.fix_ip_num), + ('volumes', volume_fakes.QUOTA['volumes']), + ('network', network_fakes.QUOTA['network']), + ('default', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs_compute = { + 'injected_files': compute_fakes.injected_file_num, + 'injected_file_content_bytes': compute_fakes.injected_file_size_num, # noqa: E501 + 'injected_file_path_bytes': compute_fakes.injected_path_size_num, + 'key_pairs': compute_fakes.key_pair_num, + 'cores': compute_fakes.core_num, + 'ram': compute_fakes.ram_num, + 'instances': compute_fakes.instance_num, + 'metadata_items': compute_fakes.property_num, + 'server_groups': compute_fakes.servgroup_num, + 'server_group_members': compute_fakes.servgroup_members_num, + } + kwargs_volume = { + 'gigabytes': compute_fakes.floating_ip_num, + 'snapshots': compute_fakes.fix_ip_num, + 'volumes': volume_fakes.QUOTA['volumes'], + } + + self.compute_quotas_class_mock.update.assert_called_with( + 'default', **kwargs_compute + ) + self.volume_quotas_class_mock.update.assert_called_with( + 'default', **kwargs_volume + ) + self.assertNotCalled(self.network_client.update_quota) + self.assertIsNone(result) + def test_quota_set_with_force(self): arglist = [ '--cores', diff --git a/releasenotes/notes/quota-set-default-option-bc26d37dc150533b.yaml b/releasenotes/notes/quota-set-default-option-bc26d37dc150533b.yaml new file mode 100644 index 000000000..d3ba34d65 --- /dev/null +++ b/releasenotes/notes/quota-set-default-option-bc26d37dc150533b.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + The ``quota set`` command now supports a ``--default`` option. When + provided, this will allow you to set quotas for the default quota class + which is the only quota class supported by the Compute and Block Storage + services. This replaces the deprecated ``quota set --class`` option. From da7eda66e96a72e7f51a9c6bebb4722503cc9662 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 10 Jul 2024 12:05:06 +0100 Subject: [PATCH 160/403] quota: Default network quotas to not force The existing default behavior has been deprecated for over a 18 months (change I25828a3d68e2e900f498e17a0d01fb70be77548e). It's time for a new default. Change-Id: Iaf4fa931dcbf16c22933f63629c6a4d443ac5310 Signed-off-by: Stephen Finucane --- openstackclient/common/quota.py | 47 +++++++++---------- .../tests/functional/common/test_quota.py | 4 +- .../tests/unit/common/test_quota.py | 4 +- ...ota-no-force-default-0975bdf15655070c.yaml | 6 +++ 4 files changed, 33 insertions(+), 28 deletions(-) create mode 100644 releasenotes/notes/network-quota-no-force-default-0975bdf15655070c.yaml diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index abbbe3cdb..50324f865 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -20,6 +20,7 @@ import sys from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils from openstackclient.i18n import _ @@ -506,22 +507,19 @@ def get_parser(self, prog_name): '--force', action='store_true', dest='force', - # TODO(stephenfin): Change the default to False in Z or later - default=None, + default=False, help=_( - 'Force quota update (only supported by compute and network) ' - '(default for network)' + 'Force quota update (only supported by compute and network)' ), ) force_group.add_argument( '--no-force', action='store_false', dest='force', - default=None, + default=False, help=_( 'Do not force quota update ' - '(only supported by compute and network) ' - '(default for compute)' + '(only supported by compute and network) (default)' ), ) # kept here for backwards compatibility/to keep the neutron folks happy @@ -529,7 +527,7 @@ def get_parser(self, prog_name): '--check-limit', action='store_false', dest='force', - default=None, + default=False, help=argparse.SUPPRESS, ) return parser @@ -545,6 +543,12 @@ def take_action(self, parsed_args): ) self.log.warning(msg) + if ( + parsed_args.quota_class or parsed_args.default + ) and parsed_args.force: + msg = _('--force cannot be used with --class or --default') + raise exceptions.CommandError(msg) + compute_client = self.app.client_manager.compute volume_client = self.app.client_manager.volume @@ -554,7 +558,7 @@ def take_action(self, parsed_args): if value is not None: compute_kwargs[k] = value - if parsed_args.force is not None: + if parsed_args.force is True: compute_kwargs['force'] = parsed_args.force volume_kwargs = {} @@ -566,22 +570,6 @@ def take_action(self, parsed_args): volume_kwargs[k] = value network_kwargs = {} - if parsed_args.force is True: - # Unlike compute, network doesn't provide a simple boolean option. - # Instead, it provides two options: 'force' and 'check_limit' - # (a.k.a. 'not force') - network_kwargs['force'] = True - elif parsed_args.force is False: - network_kwargs['check_limit'] = True - else: - msg = _( - "This command currently defaults to '--force' when modifying " - "network quotas. This behavior will change in a future " - "release. Consider explicitly providing '--force' or " - "'--no-force' options to avoid changes in behavior." - ) - self.log.warning(msg) - if self.app.client_manager.is_network_endpoint_enabled(): for k, v in NETWORK_QUOTAS.items(): value = getattr(parsed_args, k, None) @@ -593,6 +581,15 @@ def take_action(self, parsed_args): if value is not None: compute_kwargs[k] = value + if network_kwargs: + if parsed_args.force is True: + # Unlike compute, network doesn't provide a simple boolean + # option. Instead, it provides two options: 'force' and + # 'check_limit' (a.k.a. 'not force') + network_kwargs['force'] = True + else: + network_kwargs['check_limit'] = True + if parsed_args.quota_class or parsed_args.default: if compute_kwargs: compute_client.quota_classes.update( diff --git a/openstackclient/tests/functional/common/test_quota.py b/openstackclient/tests/functional/common/test_quota.py index e46d237db..8aa93a929 100644 --- a/openstackclient/tests/functional/common/test_quota.py +++ b/openstackclient/tests/functional/common/test_quota.py @@ -137,7 +137,7 @@ def test_quota_set_default(self): def _restore_quota_limit(self, resource, limit, project): self.openstack(f'quota set --{resource} {limit} {project}') - def test_quota_set_network_with_no_force(self): + def test_quota_set_network(self): if not self.haz_network: self.skipTest('No Network service present') if not self.is_extension_enabled('quota-check-limit'): @@ -172,7 +172,7 @@ def test_quota_set_network_with_no_force(self): self.assertRaises( exceptions.CommandFailed, self.openstack, - 'quota set --networks 1 --no-force ' + self.PROJECT_NAME, + 'quota set --networks 1 ' + self.PROJECT_NAME, ) def test_quota_set_network_with_force(self): diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index 78c84e163..be85d27b2 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -522,6 +522,7 @@ def test_quota_set(self): ('security_groups', compute_fakes.secgroup_num), ('server_groups', compute_fakes.servgroup_num), ('server_group_members', compute_fakes.servgroup_members_num), + ('force', False), ('project', self.projects[0].name), ] self.app.client_manager.network_endpoint_enabled = False @@ -682,12 +683,14 @@ def test_quota_set_network(self): ('router', network_fakes.QUOTA['router']), ('rbac_policy', network_fakes.QUOTA['rbac_policy']), ('port', network_fakes.QUOTA['port']), + ('force', False), ('project', self.projects[0].name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { + 'check_limit': True, 'subnet': network_fakes.QUOTA['subnet'], 'network': network_fakes.QUOTA['network'], 'floatingip': network_fakes.QUOTA['floatingip'], @@ -948,7 +951,6 @@ def test_quota_set_with_no_force(self): kwargs_compute = { 'cores': compute_fakes.core_num, - 'force': False, } kwargs_volume = { 'volumes': volume_fakes.QUOTA['volumes'], diff --git a/releasenotes/notes/network-quota-no-force-default-0975bdf15655070c.yaml b/releasenotes/notes/network-quota-no-force-default-0975bdf15655070c.yaml new file mode 100644 index 000000000..e1a135905 --- /dev/null +++ b/releasenotes/notes/network-quota-no-force-default-0975bdf15655070c.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - The ``openstack quota set`` command previously defaulted to ``--force`` + behavior for network quotas. This behavior has now changed and the command + now defaults to ``--no-force`` behavior. Users should specify the + ``--force`` option if they wish to retain previous behavior. From b4f30a15832466704d9b2b77ff6ff4bcade9084a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 10 Jul 2024 12:29:56 +0100 Subject: [PATCH 161/403] quota: Split up 'quota list' command This is really three separate commands rolled into one. Split the logic up more to simplify it somewhat. Change-Id: Ief3c3c413f791dda076f90e5ec76a033d3a9e12b Signed-off-by: Stephen Finucane --- openstackclient/common/quota.py | 338 +++++++++++++++++--------------- 1 file changed, 175 insertions(+), 163 deletions(-) diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 50324f865..5591069b9 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -241,182 +241,194 @@ def get_parser(self, prog_name): ) return parser - def take_action(self, parsed_args): + def _list_quota_compute(self, parsed_args, project_ids): + compute_client = self.app.client_manager.compute result = [] - project_ids = [ - p.id for p in self.app.client_manager.identity.projects.list() - ] - if parsed_args.compute: - compute_client = self.app.client_manager.compute - for p in project_ids: - try: - data = compute_client.quotas.get(p) - except Exception as ex: - if ( - type(ex).__name__ == 'NotFound' - or ex.http_status >= 400 - and ex.http_status <= 499 - ): - # Project not found, move on to next one - LOG.warning(f"Project {p} not found: {ex}") - continue - else: - raise - - result_data = _xform_get_quota( - data, - p, - COMPUTE_QUOTAS.keys(), - ) - default_data = compute_client.quotas.defaults(p) - result_default = _xform_get_quota( - default_data, - p, - COMPUTE_QUOTAS.keys(), - ) - if result_default != result_data: - result += result_data - - columns = ( - 'id', - 'cores', - 'injected_files', - 'injected_file_content_bytes', - 'injected_file_path_bytes', - 'instances', - 'key_pairs', - 'metadata_items', - 'ram', - 'server_groups', - 'server_group_members', + for p in project_ids: + try: + data = compute_client.quotas.get(p) + except Exception as ex: + if ( + type(ex).__name__ == 'NotFound' + or ex.http_status >= 400 + and ex.http_status <= 499 + ): + # Project not found, move on to next one + LOG.warning(f"Project {p} not found: {ex}") + continue + else: + raise + + result_data = _xform_get_quota( + data, + p, + COMPUTE_QUOTAS.keys(), ) - column_headers = ( - 'Project ID', - 'Cores', - 'Injected Files', - 'Injected File Content Bytes', - 'Injected File Path Bytes', - 'Instances', - 'Key Pairs', - 'Metadata Items', - 'Ram', - 'Server Groups', - 'Server Group Members', - ) - return ( - column_headers, - (utils.get_dict_properties(s, columns) for s in result), + default_data = compute_client.quotas.defaults(p) + result_default = _xform_get_quota( + default_data, + p, + COMPUTE_QUOTAS.keys(), ) + if result_default != result_data: + result += result_data - if parsed_args.volume: - volume_client = self.app.client_manager.volume - for p in project_ids: - try: - data = volume_client.quotas.get(p) - except Exception as ex: - if type(ex).__name__ == 'NotFound': - # Project not found, move on to next one - LOG.warning(f"Project {p} not found: {ex}") - continue - else: - raise - - result_data = _xform_get_quota( - data, - p, - VOLUME_QUOTAS.keys(), - ) - default_data = volume_client.quotas.defaults(p) - result_default = _xform_get_quota( - default_data, - p, - VOLUME_QUOTAS.keys(), - ) - if result_default != result_data: - result += result_data - - columns = ( - 'id', - 'backups', - 'backup_gigabytes', - 'gigabytes', - 'per_volume_gigabytes', - 'snapshots', - 'volumes', + columns = ( + 'id', + 'cores', + 'injected_files', + 'injected_file_content_bytes', + 'injected_file_path_bytes', + 'instances', + 'key_pairs', + 'metadata_items', + 'ram', + 'server_groups', + 'server_group_members', + ) + column_headers = ( + 'Project ID', + 'Cores', + 'Injected Files', + 'Injected File Content Bytes', + 'Injected File Path Bytes', + 'Instances', + 'Key Pairs', + 'Metadata Items', + 'Ram', + 'Server Groups', + 'Server Group Members', + ) + return ( + column_headers, + (utils.get_dict_properties(s, columns) for s in result), + ) + + def _list_quota_volume(self, parsed_args, project_ids): + volume_client = self.app.client_manager.volume + result = [] + + for p in project_ids: + try: + data = volume_client.quotas.get(p) + except Exception as ex: + if type(ex).__name__ == 'NotFound': + # Project not found, move on to next one + LOG.warning(f"Project {p} not found: {ex}") + continue + else: + raise + + result_data = _xform_get_quota( + data, + p, + VOLUME_QUOTAS.keys(), ) - column_headers = ( - 'Project ID', - 'Backups', - 'Backup Gigabytes', - 'Gigabytes', - 'Per Volume Gigabytes', - 'Snapshots', - 'Volumes', + default_data = volume_client.quotas.defaults(p) + result_default = _xform_get_quota( + default_data, + p, + VOLUME_QUOTAS.keys(), ) + if result_default != result_data: + result += result_data - return ( - column_headers, - (utils.get_dict_properties(s, columns) for s in result), - ) + columns = ( + 'id', + 'backups', + 'backup_gigabytes', + 'gigabytes', + 'per_volume_gigabytes', + 'snapshots', + 'volumes', + ) + column_headers = ( + 'Project ID', + 'Backups', + 'Backup Gigabytes', + 'Gigabytes', + 'Per Volume Gigabytes', + 'Snapshots', + 'Volumes', + ) - if parsed_args.network: - client = self.app.client_manager.network - for p in project_ids: - try: - data = client.get_quota(p) - except Exception as ex: - if type(ex).__name__ == 'NotFound': - # Project not found, move on to next one - LOG.warning(f"Project {p} not found: {ex}") - continue - else: - raise - - result_data = _xform_get_quota( - data, - p, - NETWORK_KEYS, - ) - default_data = client.get_quota_default(p) - result_default = _xform_get_quota( - default_data, - p, - NETWORK_KEYS, - ) - if result_default != result_data: - result += result_data - - columns = ( - 'id', - 'floating_ips', - 'networks', - 'ports', - 'rbac_policies', - 'routers', - 'security_groups', - 'security_group_rules', - 'subnets', - 'subnet_pools', + return ( + column_headers, + (utils.get_dict_properties(s, columns) for s in result), + ) + + def _list_quota_network(self, parsed_args, project_ids): + client = self.app.client_manager.network + result = [] + + for p in project_ids: + try: + data = client.get_quota(p) + except Exception as ex: + if type(ex).__name__ == 'NotFound': + # Project not found, move on to next one + LOG.warning(f"Project {p} not found: {ex}") + continue + else: + raise + + result_data = _xform_get_quota( + data, + p, + NETWORK_KEYS, ) - column_headers = ( - 'Project ID', - 'Floating IPs', - 'Networks', - 'Ports', - 'RBAC Policies', - 'Routers', - 'Security Groups', - 'Security Group Rules', - 'Subnets', - 'Subnet Pools', + default_data = client.get_quota_default(p) + result_default = _xform_get_quota( + default_data, + p, + NETWORK_KEYS, ) + if result_default != result_data: + result += result_data - return ( - column_headers, - (utils.get_dict_properties(s, columns) for s in result), - ) + columns = ( + 'id', + 'floating_ips', + 'networks', + 'ports', + 'rbac_policies', + 'routers', + 'security_groups', + 'security_group_rules', + 'subnets', + 'subnet_pools', + ) + column_headers = ( + 'Project ID', + 'Floating IPs', + 'Networks', + 'Ports', + 'RBAC Policies', + 'Routers', + 'Security Groups', + 'Security Group Rules', + 'Subnets', + 'Subnet Pools', + ) + + return ( + column_headers, + (utils.get_dict_properties(s, columns) for s in result), + ) + + def take_action(self, parsed_args): + project_ids = [ + p.id for p in self.app.client_manager.identity.projects.list() + ] + if parsed_args.compute: + return self._list_quota_compute(parsed_args, project_ids) + elif parsed_args.volume: + return self._list_quota_volume(parsed_args, project_ids) + elif parsed_args.network: + return self._list_quota_network(parsed_args, project_ids) + # will never get here return ((), ()) From 6127b44d0ae4449bc2bf744e8092bb612b5559cb Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 11 Jul 2024 11:43:56 +0100 Subject: [PATCH 162/403] quota: Migrate 'quota *' to SDK This is done for both the compute and block storage services. The network service was already using SDK. Change-Id: Id7a6943b8b4d7d1330e7d7f13705f027d1b67189 Signed-off-by: Stephen Finucane Depends-on: https://review.opendev.org/c/openstack/openstacksdk/+/923864 --- openstackclient/common/quota.py | 219 +++-- .../tests/unit/common/test_quota.py | 913 +++++++++--------- .../tests/unit/compute/v2/fakes.py | 134 --- .../tests/unit/network/v2/fakes.py | 92 -- openstackclient/tests/unit/volume/v2/fakes.py | 88 -- openstackclient/tests/unit/volume/v3/fakes.py | 4 - requirements.txt | 2 +- 7 files changed, 562 insertions(+), 890 deletions(-) diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 5591069b9..c408e97ba 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -19,6 +19,7 @@ import logging import sys +from openstack import exceptions as sdk_exceptions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -104,11 +105,8 @@ def _xform_get_quota(data, value, keys): def get_project(app, project): if project is not None: - identity_client = app.client_manager.identity - project = utils.find_resource( - identity_client.projects, - project, - ) + identity_client = app.client_manager.sdk_connection.identity + project = identity_client.find_project(project, ignore_missing=False) project_id = project.id project_name = project.name elif app.client_manager.auth_ref: @@ -134,16 +132,18 @@ def get_compute_quotas( default=False, ): try: - client = app.client_manager.compute + client = app.client_manager.sdk_connection.compute if default: - quota = client.quotas.defaults(project_id) + quota = client.get_quota_set_defaults(project_id) else: - quota = client.quotas.get(project_id, detail=detail) - except Exception as e: - if type(e).__name__ == 'EndpointNotFound': - return {} - raise - return quota._info + quota = client.get_quota_set(project_id, usage=detail) + except sdk_exceptions.EndpointNotFound: + return {} + data = quota.to_dict() + if not detail: + del data['usage'] + del data['reservation'] + return data def get_volume_quotas( @@ -154,17 +154,18 @@ def get_volume_quotas( default=False, ): try: - client = app.client_manager.volume + client = app.client_manager.sdk_connection.volume if default: - quota = client.quotas.defaults(project_id) + quota = client.get_quota_set_defaults(project_id) else: - quota = client.quotas.get(project_id, usage=detail) - except Exception as e: - if type(e).__name__ == 'EndpointNotFound': - return {} - else: - raise - return quota._info + quota = client.get_quota_set(project_id, usage=detail) + except sdk_exceptions.EndpointNotFound: + return {} + data = quota.to_dict() + if not detail: + del data['usage'] + del data['reservation'] + return data def get_network_quotas( @@ -242,37 +243,35 @@ def get_parser(self, prog_name): return parser def _list_quota_compute(self, parsed_args, project_ids): - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute result = [] - for p in project_ids: + for project_id in project_ids: try: - data = compute_client.quotas.get(p) - except Exception as ex: - if ( - type(ex).__name__ == 'NotFound' - or ex.http_status >= 400 - and ex.http_status <= 499 - ): - # Project not found, move on to next one - LOG.warning(f"Project {p} not found: {ex}") - continue - else: - raise - - result_data = _xform_get_quota( - data, - p, + project_data = compute_client.get_quota_set(project_id) + except ( + sdk_exceptions.NotFoundException, + sdk_exceptions.ForbiddenException, + ) as exc: + # Project not found, move on to next one + LOG.warning(f"Project {project_id} not found: {exc}") + continue + + project_result = _xform_get_quota( + project_data, + project_id, COMPUTE_QUOTAS.keys(), ) - default_data = compute_client.quotas.defaults(p) - result_default = _xform_get_quota( + + default_data = compute_client.get_quota_set_defaults(project_id) + default_result = _xform_get_quota( default_data, - p, + project_id, COMPUTE_QUOTAS.keys(), ) - if result_default != result_data: - result += result_data + + if default_result != project_result: + result += project_result columns = ( 'id', @@ -306,33 +305,35 @@ def _list_quota_compute(self, parsed_args, project_ids): ) def _list_quota_volume(self, parsed_args, project_ids): - volume_client = self.app.client_manager.volume + volume_client = self.app.client_manager.sdk_connection.volume result = [] - for p in project_ids: + for project_id in project_ids: try: - data = volume_client.quotas.get(p) - except Exception as ex: - if type(ex).__name__ == 'NotFound': - # Project not found, move on to next one - LOG.warning(f"Project {p} not found: {ex}") - continue - else: - raise - - result_data = _xform_get_quota( - data, - p, + project_data = volume_client.get_quota_set(project_id) + except ( + sdk_exceptions.NotFoundException, + sdk_exceptions.ForbiddenException, + ) as exc: + # Project not found, move on to next one + LOG.warning(f"Project {project_id} not found: {exc}") + continue + + project_result = _xform_get_quota( + project_data, + project_id, VOLUME_QUOTAS.keys(), ) - default_data = volume_client.quotas.defaults(p) - result_default = _xform_get_quota( + + default_data = volume_client.get_quota_set_defaults(project_id) + default_result = _xform_get_quota( default_data, - p, + project_id, VOLUME_QUOTAS.keys(), ) - if result_default != result_data: - result += result_data + + if default_result != project_result: + result += project_result columns = ( 'id', @@ -359,33 +360,35 @@ def _list_quota_volume(self, parsed_args, project_ids): ) def _list_quota_network(self, parsed_args, project_ids): - client = self.app.client_manager.network + network_client = self.app.client_manager.network result = [] - for p in project_ids: + for project_id in project_ids: try: - data = client.get_quota(p) - except Exception as ex: - if type(ex).__name__ == 'NotFound': - # Project not found, move on to next one - LOG.warning(f"Project {p} not found: {ex}") - continue - else: - raise - - result_data = _xform_get_quota( - data, - p, + project_data = network_client.get_quota(project_id) + except ( + sdk_exceptions.NotFoundException, + sdk_exceptions.ForbiddenException, + ) as exc: + # Project not found, move on to next one + LOG.warning(f"Project {project_id} not found: {exc}") + continue + + project_result = _xform_get_quota( + project_data, + project_id, NETWORK_KEYS, ) - default_data = client.get_quota_default(p) - result_default = _xform_get_quota( + + default_data = network_client.get_quota_default(project_id) + default_result = _xform_get_quota( default_data, - p, + project_id, NETWORK_KEYS, ) - if result_default != result_data: - result += result_data + + if default_result != project_result: + result += project_result columns = ( 'id', @@ -419,7 +422,8 @@ def _list_quota_network(self, parsed_args, project_ids): def take_action(self, parsed_args): project_ids = [ - p.id for p in self.app.client_manager.identity.projects.list() + p.id + for p in self.app.client_manager.sdk_connection.identity.projects() ] if parsed_args.compute: return self._list_quota_compute(parsed_args, project_ids) @@ -561,8 +565,8 @@ 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.compute - volume_client = self.app.client_manager.volume + 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(): @@ -570,7 +574,7 @@ def take_action(self, parsed_args): if value is not None: compute_kwargs[k] = value - if parsed_args.force is True: + if compute_kwargs and parsed_args.force is True: compute_kwargs['force'] = parsed_args.force volume_kwargs = {} @@ -604,12 +608,12 @@ def take_action(self, parsed_args): if parsed_args.quota_class or parsed_args.default: if compute_kwargs: - compute_client.quota_classes.update( + compute_client.update_quota_class_set( parsed_args.project or 'default', **compute_kwargs, ) if volume_kwargs: - volume_client.quota_classes.update( + volume_client.update_quota_class_set( parsed_args.project or 'default', **volume_kwargs, ) @@ -625,9 +629,9 @@ def take_action(self, parsed_args): project = project_info['id'] if compute_kwargs: - compute_client.quotas.update(project, **compute_kwargs) + compute_client.update_quota_set(project, **compute_kwargs) if volume_kwargs: - volume_client.quotas.update(project, **volume_kwargs) + volume_client.update_quota_set(project, **volume_kwargs) if ( network_kwargs and self.app.client_manager.is_network_endpoint_enabled() @@ -765,14 +769,24 @@ def take_action(self, parsed_args): if 'id' in info: del info['id'] - # Remove the 'location' field for resources from openstacksdk - if 'location' in info: - del info['location'] + # Remove the sdk-derived fields + for field in ('location', 'name', 'force'): + if field in info: + del info[field] if not parsed_args.usage: result = [{'resource': k, 'limit': v} for k, v in info.items()] else: - result = [{'resource': k, **v} for k, v in info.items()] + result = [ + { + 'resource': k, + 'limit': v or 0, + 'in_use': info['usage'].get(k, 0), + 'reserved': info['reservation'].get(k, 0), + } + for k, v in info.items() + if k not in ('usage', 'reservation') + ] columns = ( 'resource', @@ -850,21 +864,20 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - identity_client = self.app.client_manager.identity - project = utils.find_resource( - identity_client.projects, - parsed_args.project, + identity_client = self.app.client_manager.sdk_connection.identity + project = identity_client.find_project( + parsed_args.project, ignore_missing=False ) # compute quotas if parsed_args.service in {'all', 'compute'}: - compute_client = self.app.client_manager.compute - compute_client.quotas.delete(project.id) + compute_client = self.app.client_manager.sdk_connection.compute + compute_client.revert_quota_set(project.id) # volume quotas if parsed_args.service in {'all', 'volume'}: - volume_client = self.app.client_manager.volume - volume_client.quotas.delete(project.id) + volume_client = self.app.client_manager.sdk_connection.volume + volume_client.revert_quota_set(project.id) # network quotas (but only if we're not using nova-network, otherwise # we already deleted the quotas in the compute step) diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index be85d27b2..e18161d4e 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -10,61 +10,38 @@ # License for the specific language governing permissions and limitations # under the License. -import copy from unittest import mock -from osc_lib import exceptions +from openstack.block_storage.v3 import quota_set as _volume_quota_set +from openstack.compute.v2 import quota_set as _compute_quota_set +from openstack.identity.v3 import project as _project +from openstack.network.v2 import quota as _network_quota_set +from openstack.test import fakes as sdk_fakes +from openstack import exceptions as sdk_exceptions from openstackclient.common import quota 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.identity.v3 import fakes as identity_fakes_v3 +from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit.network.v2 import fakes as network_fakes -from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes +from openstackclient.tests.unit import utils +from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes -class FakeQuotaResource(fakes.FakeResource): - _keys = {'property': 'value'} - - def set_keys(self, args): - self._keys.update(args) - - def unset_keys(self, keys): - for key in keys: - self._keys.pop(key, None) - - def get_keys(self): - return self._keys - - -class TestQuota(compute_fakes.TestComputev2): +class TestQuota( + identity_fakes.FakeClientMixin, + compute_fakes.FakeClientMixin, + network_fakes.FakeClientMixin, + volume_fakes.FakeClientMixin, + utils.TestCommand, +): def setUp(self): super().setUp() - # Set up common projects - self.projects = identity_fakes_v3.FakeProject.create_projects(count=2) - self.projects_mock = self.identity_client.projects - self.projects_mock.reset_mock() - self.projects_mock.get.return_value = self.projects[0] - - self.compute_quotas_mock = self.compute_client.quotas - self.compute_quotas_mock.reset_mock() - self.compute_quotas_class_mock = self.compute_client.quota_classes - self.compute_quotas_class_mock.reset_mock() - - self.volume_quotas_mock = self.volume_client.quotas - self.volume_quotas_mock.reset_mock() - self.volume_quotas_class_mock = self.volume_client.quota_classes - self.volume_quotas_class_mock.reset_mock() - - self.app.client_manager.auth_ref = mock.Mock() - self.app.client_manager.auth_ref.service_catalog = mock.Mock() - self.service_catalog_mock = ( - self.app.client_manager.auth_ref.service_catalog + self.projects = list( + sdk_fakes.generate_fake_resources(_project.Project, count=2) ) - self.service_catalog_mock.reset_mock() - self.app.client_manager.auth_ref.project_id = identity_fakes.project_id + self.app.client_manager.auth_ref = mock.Mock() + self.app.client_manager.auth_ref.project_id = self.projects[1].id class TestQuotaList(TestQuota): @@ -110,22 +87,20 @@ class TestQuotaList(TestQuota): def setUp(self): super().setUp() - # Work with multiple projects in this class - self.projects_mock.get.side_effect = self.projects - self.projects_mock.list.return_value = self.projects + self.identity_sdk_client.get_project.side_effect = self.projects[0] + self.identity_sdk_client.projects.return_value = self.projects self.compute_quotas = [ - compute_fakes.create_one_comp_quota(), - compute_fakes.create_one_comp_quota(), - ] - self.compute_default_quotas = [ - compute_fakes.create_one_default_comp_quota(), - compute_fakes.create_one_default_comp_quota(), + sdk_fakes.generate_fake_resource(_compute_quota_set.QuotaSet), + sdk_fakes.generate_fake_resource(_compute_quota_set.QuotaSet), ] - self.compute_client.quotas.defaults = mock.Mock( - side_effect=self.compute_default_quotas, + self.default_compute_quotas = sdk_fakes.generate_fake_resource( + _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.default_compute_quotas ) - self.compute_reference_data = ( self.projects[0].id, self.compute_quotas[0].cores, @@ -141,17 +116,16 @@ def setUp(self): ) self.network_quotas = [ - network_fakes.FakeQuota.create_one_net_quota(), - network_fakes.FakeQuota.create_one_net_quota(), - ] - self.network_default_quotas = [ - network_fakes.FakeQuota.create_one_default_net_quota(), - network_fakes.FakeQuota.create_one_default_net_quota(), + sdk_fakes.generate_fake_resource(_network_quota_set.Quota), + sdk_fakes.generate_fake_resource(_network_quota_set.Quota), ] - self.network_client.get_quota_default = mock.Mock( - side_effect=self.network_default_quotas, + self.default_network_quotas = sdk_fakes.generate_fake_resource( + _network_quota_set.QuotaDefault + ) + # the defaults are global hence use of return_value here + self.network_client.get_quota_default.return_value = ( + self.default_network_quotas ) - self.network_reference_data = ( self.projects[0].id, self.network_quotas[0].floating_ips, @@ -166,17 +140,16 @@ def setUp(self): ) self.volume_quotas = [ - volume_fakes.create_one_vol_quota(), - volume_fakes.create_one_vol_quota(), + sdk_fakes.generate_fake_resource(_volume_quota_set.QuotaSet), + sdk_fakes.generate_fake_resource(_volume_quota_set.QuotaSet), ] - self.volume_default_quotas = [ - volume_fakes.create_one_default_vol_quota(), - volume_fakes.create_one_default_vol_quota(), - ] - self.volume_client.quotas.defaults = mock.Mock( - side_effect=self.volume_default_quotas, + self.default_volume_quotas = sdk_fakes.generate_fake_resource( + _volume_quota_set.QuotaSet + ) + # the defaults are global hence use of return_value here + self.volume_sdk_client.get_quota_set_defaults.return_value = ( + self.default_volume_quotas ) - self.volume_reference_data = ( self.projects[0].id, self.volume_quotas[0].backups, @@ -191,9 +164,7 @@ def setUp(self): def test_quota_list_compute(self): # Two projects with non-default quotas - self.compute_client.quotas.get = mock.Mock( - side_effect=self.compute_quotas, - ) + self.compute_sdk_client.get_quota_set.side_effect = self.compute_quotas arglist = [ '--compute', @@ -212,12 +183,10 @@ def test_quota_list_compute(self): def test_quota_list_compute_default(self): # One of the projects is at defaults - self.compute_client.quotas.get = mock.Mock( - side_effect=[ - self.compute_quotas[0], - compute_fakes.create_one_default_comp_quota(), - ], - ) + self.compute_sdk_client.get_quota_set.side_effect = [ + self.compute_quotas[0], + self.default_compute_quotas, + ] arglist = [ '--compute', @@ -234,14 +203,12 @@ def test_quota_list_compute_default(self): self.assertEqual(self.compute_reference_data, ret_quotas[0]) self.assertEqual(1, len(ret_quotas)) - def test_quota_list_compute_no_project_not_found(self): + def test_quota_list_compute_project_not_found(self): # Make one of the projects disappear - self.compute_client.quotas.get = mock.Mock( - side_effect=[ - self.compute_quotas[0], - exceptions.NotFound("NotFound"), - ], - ) + self.compute_sdk_client.get_quota_set.side_effect = [ + self.compute_quotas[0], + sdk_exceptions.NotFoundException("NotFound"), + ] arglist = [ '--compute', @@ -258,14 +225,12 @@ def test_quota_list_compute_no_project_not_found(self): self.assertEqual(self.compute_reference_data, ret_quotas[0]) self.assertEqual(1, len(ret_quotas)) - def test_quota_list_compute_no_project_4xx(self): - # Make one of the projects disappear - self.compute_client.quotas.get = mock.Mock( - side_effect=[ - self.compute_quotas[0], - exceptions.BadRequest("Bad request"), - ], - ) + 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_quotas[0], + sdk_exceptions.ForbiddenException("Forbidden"), + ] arglist = [ '--compute', @@ -282,13 +247,10 @@ def test_quota_list_compute_no_project_4xx(self): self.assertEqual(self.compute_reference_data, ret_quotas[0]) self.assertEqual(1, len(ret_quotas)) - def test_quota_list_compute_no_project_5xx(self): - # Make one of the projects disappear - self.compute_client.quotas.get = mock.Mock( - side_effect=[ - self.compute_quotas[0], - exceptions.HTTPNotImplemented("Not implemented??"), - ], + def test_quota_list_compute_server_error(self): + # Make the server "break" + self.compute_sdk_client.get_quota_set.side_effect = ( + sdk_exceptions.HttpException("Not implemented?") ) arglist = [ @@ -300,16 +262,14 @@ def test_quota_list_compute_no_project_5xx(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( - exceptions.HTTPNotImplemented, + sdk_exceptions.HttpException, self.cmd.take_action, parsed_args, ) def test_quota_list_network(self): # Two projects with non-default quotas - self.network_client.get_quota = mock.Mock( - side_effect=self.network_quotas, - ) + self.network_client.get_quota.side_effect = self.network_quotas arglist = [ '--network', @@ -328,12 +288,10 @@ def test_quota_list_network(self): def test_quota_list_network_default(self): # Two projects with non-default quotas - self.network_client.get_quota = mock.Mock( - side_effect=[ - self.network_quotas[0], - network_fakes.FakeQuota.create_one_default_net_quota(), - ], - ) + self.network_client.get_quota.side_effect = [ + self.network_quotas[0], + self.default_network_quotas, + ] arglist = [ '--network', @@ -352,12 +310,10 @@ def test_quota_list_network_default(self): def test_quota_list_network_no_project(self): # Two projects with non-default quotas - self.network_client.get_quota = mock.Mock( - side_effect=[ - self.network_quotas[0], - exceptions.NotFound("NotFound"), - ], - ) + self.network_client.get_quota.side_effect = [ + self.network_quotas[0], + sdk_exceptions.NotFoundException("NotFound"), + ] arglist = [ '--network', @@ -376,9 +332,7 @@ def test_quota_list_network_no_project(self): def test_quota_list_volume(self): # Two projects with non-default quotas - self.volume_client.quotas.get = mock.Mock( - side_effect=self.volume_quotas, - ) + self.volume_sdk_client.get_quota_set.side_effect = self.volume_quotas arglist = [ '--volume', @@ -397,36 +351,10 @@ def test_quota_list_volume(self): def test_quota_list_volume_default(self): # Two projects with non-default quotas - self.volume_client.quotas.get = mock.Mock( - side_effect=[ - self.volume_quotas[0], - volume_fakes.create_one_default_vol_quota(), - ], - ) - - arglist = [ - '--volume', - ] - verifylist = [ - ('volume', True), + self.volume_sdk_client.get_quota_set.side_effect = [ + self.volume_quotas[0], + self.default_volume_quotas, ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - ret_quotas = list(data) - - self.assertEqual(self.volume_column_header, columns) - self.assertEqual(self.volume_reference_data, ret_quotas[0]) - self.assertEqual(1, len(ret_quotas)) - - def test_quota_list_volume_no_project(self): - # Two projects with non-default quotas - self.volume_client.quotas.get = mock.Mock( - side_effect=[ - self.volume_quotas[0], - volume_fakes.create_one_default_vol_quota(), - ], - ) arglist = [ '--volume', @@ -448,80 +376,75 @@ class TestQuotaSet(TestQuota): def setUp(self): super().setUp() - self.compute_quotas_mock.update.return_value = FakeQuotaResource( - None, - copy.deepcopy(compute_fakes.QUOTA), - loaded=True, - ) - self.compute_quotas_class_mock.update.return_value = FakeQuotaResource( - None, - copy.deepcopy(compute_fakes.QUOTA), - loaded=True, - ) - - self.volume_quotas_mock.update.return_value = FakeQuotaResource( - None, - copy.deepcopy(compute_fakes.QUOTA), - loaded=True, - ) - self.volume_quotas_class_mock.update.return_value = FakeQuotaResource( - None, - copy.deepcopy(compute_fakes.QUOTA), - loaded=True, - ) + self.identity_sdk_client.find_project.return_value = self.projects[0] self.cmd = quota.SetQuota(self.app, None) def test_quota_set(self): + floating_ip_num = 100 + fix_ip_num = 100 + injected_file_num = 100 + injected_file_size_num = 10240 + injected_path_size_num = 255 + key_pair_num = 100 + core_num = 20 + ram_num = 51200 + instance_num = 10 + property_num = 128 + secgroup_rule_num = 20 + secgroup_num = 10 + servgroup_num = 10 + servgroup_members_num = 10 + arglist = [ '--floating-ips', - str(compute_fakes.floating_ip_num), + str(floating_ip_num), '--fixed-ips', - str(compute_fakes.fix_ip_num), + str(fix_ip_num), '--injected-files', - str(compute_fakes.injected_file_num), + str(injected_file_num), '--injected-file-size', - str(compute_fakes.injected_file_size_num), + str(injected_file_size_num), '--injected-path-size', - str(compute_fakes.injected_path_size_num), + str(injected_path_size_num), '--key-pairs', - str(compute_fakes.key_pair_num), + str(key_pair_num), '--cores', - str(compute_fakes.core_num), + str(core_num), '--ram', - str(compute_fakes.ram_num), + str(ram_num), '--instances', - str(compute_fakes.instance_num), + str(instance_num), '--properties', - str(compute_fakes.property_num), + str(property_num), '--secgroup-rules', - str(compute_fakes.secgroup_rule_num), + str(secgroup_rule_num), '--secgroups', - str(compute_fakes.secgroup_num), + str(secgroup_num), '--server-groups', - str(compute_fakes.servgroup_num), + str(servgroup_num), '--server-group-members', - str(compute_fakes.servgroup_members_num), + str(servgroup_members_num), self.projects[0].name, ] verifylist = [ - ('floating_ips', compute_fakes.floating_ip_num), - ('fixed_ips', compute_fakes.fix_ip_num), - ('injected_files', compute_fakes.injected_file_num), + ('floating_ips', floating_ip_num), + ('fixed_ips', fix_ip_num), + ('injected_files', injected_file_num), ( 'injected_file_content_bytes', - compute_fakes.injected_file_size_num, + injected_file_size_num, ), - ('injected_file_path_bytes', compute_fakes.injected_path_size_num), - ('key_pairs', compute_fakes.key_pair_num), - ('cores', compute_fakes.core_num), - ('ram', compute_fakes.ram_num), - ('instances', compute_fakes.instance_num), - ('metadata_items', compute_fakes.property_num), - ('security_group_rules', compute_fakes.secgroup_rule_num), - ('security_groups', compute_fakes.secgroup_num), - ('server_groups', compute_fakes.servgroup_num), - ('server_group_members', compute_fakes.servgroup_members_num), + ('injected_file_path_bytes', injected_path_size_num), + ('key_pairs', key_pair_num), + ('cores', core_num), + ('ram', ram_num), + ('instances', instance_num), + ('metadata_items', property_num), + ('security_group_rules', secgroup_rule_num), + ('security_groups', secgroup_num), + ('server_groups', servgroup_num), + ('server_group_members', servgroup_members_num), ('force', False), ('project', self.projects[0].name), ] @@ -531,52 +454,59 @@ def test_quota_set(self): result = self.cmd.take_action(parsed_args) kwargs = { - 'floating_ips': compute_fakes.floating_ip_num, - 'fixed_ips': compute_fakes.fix_ip_num, - 'injected_files': compute_fakes.injected_file_num, - 'injected_file_content_bytes': compute_fakes.injected_file_size_num, # noqa: E501 - 'injected_file_path_bytes': compute_fakes.injected_path_size_num, - 'key_pairs': compute_fakes.key_pair_num, - 'cores': compute_fakes.core_num, - 'ram': compute_fakes.ram_num, - 'instances': compute_fakes.instance_num, - 'metadata_items': compute_fakes.property_num, - 'security_group_rules': compute_fakes.secgroup_rule_num, - 'security_groups': compute_fakes.secgroup_num, - 'server_groups': compute_fakes.servgroup_num, - 'server_group_members': compute_fakes.servgroup_members_num, + 'floating_ips': floating_ip_num, + 'fixed_ips': fix_ip_num, + 'injected_files': injected_file_num, + 'injected_file_content_bytes': injected_file_size_num, # noqa: E501 + 'injected_file_path_bytes': injected_path_size_num, + 'key_pairs': key_pair_num, + 'cores': core_num, + 'ram': ram_num, + 'instances': instance_num, + 'metadata_items': property_num, + 'security_group_rules': secgroup_rule_num, + 'security_groups': secgroup_num, + 'server_groups': servgroup_num, + 'server_group_members': servgroup_members_num, } - self.compute_quotas_mock.update.assert_called_once_with( + self.compute_sdk_client.update_quota_set.assert_called_once_with( self.projects[0].id, **kwargs ) self.assertIsNone(result) def test_quota_set_volume(self): + gigabytes = 1000 + volumes = 11 + snapshots = 10 + backups = 10 + backup_gigabytes = 1000 + per_volume_gigabytes = -1 + arglist = [ '--gigabytes', - str(volume_fakes.QUOTA['gigabytes']), + str(gigabytes), '--snapshots', - str(volume_fakes.QUOTA['snapshots']), + str(snapshots), '--volumes', - str(volume_fakes.QUOTA['volumes']), + str(volumes), '--backups', - str(volume_fakes.QUOTA['backups']), + str(backups), '--backup-gigabytes', - str(volume_fakes.QUOTA['backup_gigabytes']), + str(backup_gigabytes), '--per-volume-gigabytes', - str(volume_fakes.QUOTA['per_volume_gigabytes']), + str(per_volume_gigabytes), self.projects[0].name, ] verifylist = [ - ('gigabytes', volume_fakes.QUOTA['gigabytes']), - ('snapshots', volume_fakes.QUOTA['snapshots']), - ('volumes', volume_fakes.QUOTA['volumes']), - ('backups', volume_fakes.QUOTA['backups']), - ('backup_gigabytes', volume_fakes.QUOTA['backup_gigabytes']), + ('gigabytes', gigabytes), + ('snapshots', snapshots), + ('volumes', volumes), + ('backups', backups), + ('backup_gigabytes', backup_gigabytes), ( 'per_volume_gigabytes', - volume_fakes.QUOTA['per_volume_gigabytes'], + per_volume_gigabytes, ), ('project', self.projects[0].name), ] @@ -585,47 +515,54 @@ def test_quota_set_volume(self): result = self.cmd.take_action(parsed_args) kwargs = { - 'gigabytes': volume_fakes.QUOTA['gigabytes'], - 'snapshots': volume_fakes.QUOTA['snapshots'], - 'volumes': volume_fakes.QUOTA['volumes'], - 'backups': volume_fakes.QUOTA['backups'], - 'backup_gigabytes': volume_fakes.QUOTA['backup_gigabytes'], - 'per_volume_gigabytes': volume_fakes.QUOTA['per_volume_gigabytes'], + 'gigabytes': gigabytes, + 'snapshots': snapshots, + 'volumes': volumes, + 'backups': backups, + 'backup_gigabytes': backup_gigabytes, + 'per_volume_gigabytes': per_volume_gigabytes, } - self.volume_quotas_mock.update.assert_called_once_with( + self.volume_sdk_client.update_quota_set.assert_called_once_with( self.projects[0].id, **kwargs ) self.assertIsNone(result) def test_quota_set_volume_with_volume_type(self): + gigabytes = 1000 + volumes = 11 + snapshots = 10 + backups = 10 + backup_gigabytes = 1000 + per_volume_gigabytes = -1 + arglist = [ '--gigabytes', - str(volume_fakes.QUOTA['gigabytes']), + str(gigabytes), '--snapshots', - str(volume_fakes.QUOTA['snapshots']), + str(snapshots), '--volumes', - str(volume_fakes.QUOTA['volumes']), + str(volumes), '--backups', - str(volume_fakes.QUOTA['backups']), + str(backups), '--backup-gigabytes', - str(volume_fakes.QUOTA['backup_gigabytes']), + str(backup_gigabytes), '--per-volume-gigabytes', - str(volume_fakes.QUOTA['per_volume_gigabytes']), + str(per_volume_gigabytes), '--volume-type', 'volume_type_backend', self.projects[0].name, ] verifylist = [ - ('gigabytes', volume_fakes.QUOTA['gigabytes']), - ('snapshots', volume_fakes.QUOTA['snapshots']), - ('volumes', volume_fakes.QUOTA['volumes']), - ('backups', volume_fakes.QUOTA['backups']), - ('backup_gigabytes', volume_fakes.QUOTA['backup_gigabytes']), + ('gigabytes', gigabytes), + ('snapshots', snapshots), + ('volumes', volumes), + ('backups', backups), + ('backup_gigabytes', backup_gigabytes), ( 'per_volume_gigabytes', - volume_fakes.QUOTA['per_volume_gigabytes'], + per_volume_gigabytes, ), ('volume_type', 'volume_type_backend'), ('project', self.projects[0].name), @@ -635,54 +572,64 @@ def test_quota_set_volume_with_volume_type(self): result = self.cmd.take_action(parsed_args) kwargs = { - 'gigabytes_volume_type_backend': volume_fakes.QUOTA['gigabytes'], - 'snapshots_volume_type_backend': volume_fakes.QUOTA['snapshots'], - 'volumes_volume_type_backend': volume_fakes.QUOTA['volumes'], - 'backups': volume_fakes.QUOTA['backups'], - 'backup_gigabytes': volume_fakes.QUOTA['backup_gigabytes'], - 'per_volume_gigabytes': volume_fakes.QUOTA['per_volume_gigabytes'], + 'gigabytes_volume_type_backend': gigabytes, + 'snapshots_volume_type_backend': snapshots, + 'volumes_volume_type_backend': volumes, + 'backups': backups, + 'backup_gigabytes': backup_gigabytes, + 'per_volume_gigabytes': per_volume_gigabytes, } - self.volume_quotas_mock.update.assert_called_once_with( + self.volume_sdk_client.update_quota_set.assert_called_once_with( self.projects[0].id, **kwargs ) self.assertIsNone(result) def test_quota_set_network(self): + subnet = 10 + network = 10 + floatingip = 50 + subnetpool = -1 + security_group_rule = 100 + security_group = 10 + router = 10 + rbac_policy = -1 + port = 50 + arglist = [ '--subnets', - str(network_fakes.QUOTA['subnet']), + str(subnet), '--networks', - str(network_fakes.QUOTA['network']), + str(network), '--floating-ips', - str(network_fakes.QUOTA['floatingip']), + str(floatingip), '--subnetpools', - str(network_fakes.QUOTA['subnetpool']), + str(subnetpool), '--secgroup-rules', - str(network_fakes.QUOTA['security_group_rule']), + str(security_group_rule), '--secgroups', - str(network_fakes.QUOTA['security_group']), + str(security_group), '--routers', - str(network_fakes.QUOTA['router']), + str(router), '--rbac-policies', - str(network_fakes.QUOTA['rbac_policy']), + str(rbac_policy), '--ports', - str(network_fakes.QUOTA['port']), + str(port), self.projects[0].name, ] verifylist = [ - ('subnet', network_fakes.QUOTA['subnet']), - ('network', network_fakes.QUOTA['network']), - ('floatingip', network_fakes.QUOTA['floatingip']), - ('subnetpool', network_fakes.QUOTA['subnetpool']), + ('subnet', subnet), + ('network', network), + ('floatingip', floatingip), + ('subnetpool', subnetpool), ( 'security_group_rule', - network_fakes.QUOTA['security_group_rule'], + security_group_rule, ), - ('security_group', network_fakes.QUOTA['security_group']), - ('router', network_fakes.QUOTA['router']), - ('rbac_policy', network_fakes.QUOTA['rbac_policy']), - ('port', network_fakes.QUOTA['port']), + ('security_group', security_group), + ('router', router), + ('rbac_policy', rbac_policy), + ('port', port), ('force', False), ('project', self.projects[0].name), ] @@ -691,15 +638,15 @@ def test_quota_set_network(self): result = self.cmd.take_action(parsed_args) kwargs = { 'check_limit': True, - 'subnet': network_fakes.QUOTA['subnet'], - 'network': network_fakes.QUOTA['network'], - 'floatingip': network_fakes.QUOTA['floatingip'], - 'subnetpool': network_fakes.QUOTA['subnetpool'], - 'security_group_rule': network_fakes.QUOTA['security_group_rule'], - 'security_group': network_fakes.QUOTA['security_group'], - 'router': network_fakes.QUOTA['router'], - 'rbac_policy': network_fakes.QUOTA['rbac_policy'], - 'port': network_fakes.QUOTA['port'], + 'subnet': subnet, + 'network': network, + 'floatingip': floatingip, + 'subnetpool': subnetpool, + 'security_group_rule': security_group_rule, + 'security_group': security_group, + 'router': router, + 'rbac_policy': rbac_policy, + 'port': port, } self.network_client.update_quota.assert_called_once_with( self.projects[0].id, **kwargs @@ -707,56 +654,71 @@ def test_quota_set_network(self): self.assertIsNone(result) def test_quota_set_with_class(self): + floating_ip_num = 100 + fix_ip_num = 100 + injected_file_num = 100 + injected_file_size_num = 10240 + injected_path_size_num = 255 + key_pair_num = 100 + core_num = 20 + ram_num = 51200 + instance_num = 10 + property_num = 128 + servgroup_num = 10 + servgroup_members_num = 10 + volumes = 11 + network = 10 + arglist = [ '--injected-files', - str(compute_fakes.injected_file_num), + str(injected_file_num), '--injected-file-size', - str(compute_fakes.injected_file_size_num), + str(injected_file_size_num), '--injected-path-size', - str(compute_fakes.injected_path_size_num), + str(injected_path_size_num), '--key-pairs', - str(compute_fakes.key_pair_num), + str(key_pair_num), '--cores', - str(compute_fakes.core_num), + str(core_num), '--ram', - str(compute_fakes.ram_num), + str(ram_num), '--instances', - str(compute_fakes.instance_num), + str(instance_num), '--properties', - str(compute_fakes.property_num), + str(property_num), '--server-groups', - str(compute_fakes.servgroup_num), + str(servgroup_num), '--server-group-members', - str(compute_fakes.servgroup_members_num), + str(servgroup_members_num), '--gigabytes', - str(compute_fakes.floating_ip_num), + str(floating_ip_num), '--snapshots', - str(compute_fakes.fix_ip_num), + str(fix_ip_num), '--volumes', - str(volume_fakes.QUOTA['volumes']), + str(volumes), '--network', - str(network_fakes.QUOTA['network']), + str(network), '--class', self.projects[0].name, ] verifylist = [ - ('injected_files', compute_fakes.injected_file_num), + ('injected_files', injected_file_num), ( 'injected_file_content_bytes', - compute_fakes.injected_file_size_num, + injected_file_size_num, ), - ('injected_file_path_bytes', compute_fakes.injected_path_size_num), - ('key_pairs', compute_fakes.key_pair_num), - ('cores', compute_fakes.core_num), - ('ram', compute_fakes.ram_num), - ('instances', compute_fakes.instance_num), - ('metadata_items', compute_fakes.property_num), - ('server_groups', compute_fakes.servgroup_num), - ('server_group_members', compute_fakes.servgroup_members_num), - ('gigabytes', compute_fakes.floating_ip_num), - ('snapshots', compute_fakes.fix_ip_num), - ('volumes', volume_fakes.QUOTA['volumes']), - ('network', network_fakes.QUOTA['network']), + ('injected_file_path_bytes', injected_path_size_num), + ('key_pairs', key_pair_num), + ('cores', core_num), + ('ram', ram_num), + ('instances', instance_num), + ('metadata_items', property_num), + ('server_groups', servgroup_num), + ('server_group_members', servgroup_members_num), + ('gigabytes', floating_ip_num), + ('snapshots', fix_ip_num), + ('volumes', volumes), + ('network', network), ('quota_class', True), ('project', self.projects[0].name), ] @@ -765,82 +727,97 @@ def test_quota_set_with_class(self): result = self.cmd.take_action(parsed_args) kwargs_compute = { - 'injected_files': compute_fakes.injected_file_num, - 'injected_file_content_bytes': compute_fakes.injected_file_size_num, # noqa: E501 - 'injected_file_path_bytes': compute_fakes.injected_path_size_num, - 'key_pairs': compute_fakes.key_pair_num, - 'cores': compute_fakes.core_num, - 'ram': compute_fakes.ram_num, - 'instances': compute_fakes.instance_num, - 'metadata_items': compute_fakes.property_num, - 'server_groups': compute_fakes.servgroup_num, - 'server_group_members': compute_fakes.servgroup_members_num, + 'injected_files': injected_file_num, + 'injected_file_content_bytes': injected_file_size_num, # noqa: E501 + 'injected_file_path_bytes': injected_path_size_num, + 'key_pairs': key_pair_num, + 'cores': core_num, + 'ram': ram_num, + 'instances': instance_num, + 'metadata_items': property_num, + 'server_groups': servgroup_num, + 'server_group_members': servgroup_members_num, } kwargs_volume = { - 'gigabytes': compute_fakes.floating_ip_num, - 'snapshots': compute_fakes.fix_ip_num, - 'volumes': volume_fakes.QUOTA['volumes'], + 'gigabytes': floating_ip_num, + 'snapshots': fix_ip_num, + 'volumes': volumes, } - self.compute_quotas_class_mock.update.assert_called_with( + self.compute_sdk_client.update_quota_class_set.assert_called_with( self.projects[0].name, **kwargs_compute ) - self.volume_quotas_class_mock.update.assert_called_with( + self.volume_sdk_client.update_quota_class_set.assert_called_with( self.projects[0].name, **kwargs_volume ) self.assertNotCalled(self.network_client.update_quota) self.assertIsNone(result) def test_quota_set_default(self): + floating_ip_num = 100 + fix_ip_num = 100 + injected_file_num = 100 + injected_file_size_num = 10240 + injected_path_size_num = 255 + key_pair_num = 100 + core_num = 20 + ram_num = 51200 + instance_num = 10 + property_num = 128 + servgroup_num = 10 + servgroup_members_num = 10 + volumes = 11 + network = 10 + arglist = [ '--injected-files', - str(compute_fakes.injected_file_num), + str(injected_file_num), '--injected-file-size', - str(compute_fakes.injected_file_size_num), + str(injected_file_size_num), '--injected-path-size', - str(compute_fakes.injected_path_size_num), + str(injected_path_size_num), '--key-pairs', - str(compute_fakes.key_pair_num), + str(key_pair_num), '--cores', - str(compute_fakes.core_num), + str(core_num), '--ram', - str(compute_fakes.ram_num), + str(ram_num), '--instances', - str(compute_fakes.instance_num), + str(instance_num), '--properties', - str(compute_fakes.property_num), + str(property_num), '--server-groups', - str(compute_fakes.servgroup_num), + str(servgroup_num), '--server-group-members', - str(compute_fakes.servgroup_members_num), + str(servgroup_members_num), '--gigabytes', - str(compute_fakes.floating_ip_num), + str(floating_ip_num), '--snapshots', - str(compute_fakes.fix_ip_num), + str(fix_ip_num), '--volumes', - str(volume_fakes.QUOTA['volumes']), + str(volumes), '--network', - str(network_fakes.QUOTA['network']), + str(network), '--default', ] verifylist = [ - ('injected_files', compute_fakes.injected_file_num), + ('injected_files', injected_file_num), ( 'injected_file_content_bytes', - compute_fakes.injected_file_size_num, + injected_file_size_num, ), - ('injected_file_path_bytes', compute_fakes.injected_path_size_num), - ('key_pairs', compute_fakes.key_pair_num), - ('cores', compute_fakes.core_num), - ('ram', compute_fakes.ram_num), - ('instances', compute_fakes.instance_num), - ('metadata_items', compute_fakes.property_num), - ('server_groups', compute_fakes.servgroup_num), - ('server_group_members', compute_fakes.servgroup_members_num), - ('gigabytes', compute_fakes.floating_ip_num), - ('snapshots', compute_fakes.fix_ip_num), - ('volumes', volume_fakes.QUOTA['volumes']), - ('network', network_fakes.QUOTA['network']), + ('injected_file_path_bytes', injected_path_size_num), + ('key_pairs', key_pair_num), + ('cores', core_num), + ('ram', ram_num), + ('instances', instance_num), + ('metadata_items', property_num), + ('server_groups', servgroup_num), + ('server_group_members', servgroup_members_num), + ('gigabytes', floating_ip_num), + ('snapshots', fix_ip_num), + ('volumes', volumes), + ('network', network), ('default', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -848,53 +825,59 @@ def test_quota_set_default(self): result = self.cmd.take_action(parsed_args) kwargs_compute = { - 'injected_files': compute_fakes.injected_file_num, - 'injected_file_content_bytes': compute_fakes.injected_file_size_num, # noqa: E501 - 'injected_file_path_bytes': compute_fakes.injected_path_size_num, - 'key_pairs': compute_fakes.key_pair_num, - 'cores': compute_fakes.core_num, - 'ram': compute_fakes.ram_num, - 'instances': compute_fakes.instance_num, - 'metadata_items': compute_fakes.property_num, - 'server_groups': compute_fakes.servgroup_num, - 'server_group_members': compute_fakes.servgroup_members_num, + 'injected_files': injected_file_num, + 'injected_file_content_bytes': injected_file_size_num, # noqa: E501 + 'injected_file_path_bytes': injected_path_size_num, + 'key_pairs': key_pair_num, + 'cores': core_num, + 'ram': ram_num, + 'instances': instance_num, + 'metadata_items': property_num, + 'server_groups': servgroup_num, + 'server_group_members': servgroup_members_num, } kwargs_volume = { - 'gigabytes': compute_fakes.floating_ip_num, - 'snapshots': compute_fakes.fix_ip_num, - 'volumes': volume_fakes.QUOTA['volumes'], + 'gigabytes': floating_ip_num, + 'snapshots': fix_ip_num, + 'volumes': volumes, } - self.compute_quotas_class_mock.update.assert_called_with( + self.compute_sdk_client.update_quota_class_set.assert_called_with( 'default', **kwargs_compute ) - self.volume_quotas_class_mock.update.assert_called_with( + self.volume_sdk_client.update_quota_class_set.assert_called_with( 'default', **kwargs_volume ) self.assertNotCalled(self.network_client.update_quota) self.assertIsNone(result) def test_quota_set_with_force(self): + core_num = 20 + ram_num = 51200 + instance_num = 10 + volumes = 11 + subnet = 10 + arglist = [ '--cores', - str(compute_fakes.core_num), + str(core_num), '--ram', - str(compute_fakes.ram_num), + str(ram_num), '--instances', - str(compute_fakes.instance_num), + str(instance_num), '--volumes', - str(volume_fakes.QUOTA['volumes']), + str(volumes), '--subnets', - str(network_fakes.QUOTA['subnet']), + str(subnet), '--force', self.projects[0].name, ] verifylist = [ - ('cores', compute_fakes.core_num), - ('ram', compute_fakes.ram_num), - ('instances', compute_fakes.instance_num), - ('volumes', volume_fakes.QUOTA['volumes']), - ('subnet', network_fakes.QUOTA['subnet']), + ('cores', core_num), + ('ram', ram_num), + ('instances', instance_num), + ('volumes', volumes), + ('subnet', subnet), ('force', True), ('project', self.projects[0].name), ] @@ -904,22 +887,22 @@ def test_quota_set_with_force(self): result = self.cmd.take_action(parsed_args) kwargs_compute = { - 'cores': compute_fakes.core_num, - 'ram': compute_fakes.ram_num, - 'instances': compute_fakes.instance_num, + 'cores': core_num, + 'ram': ram_num, + 'instances': instance_num, 'force': True, } kwargs_volume = { - 'volumes': volume_fakes.QUOTA['volumes'], + 'volumes': volumes, } kwargs_network = { - 'subnet': network_fakes.QUOTA['subnet'], + 'subnet': subnet, 'force': True, } - self.compute_quotas_mock.update.assert_called_once_with( + self.compute_sdk_client.update_quota_set.assert_called_once_with( self.projects[0].id, **kwargs_compute ) - self.volume_quotas_mock.update.assert_called_once_with( + self.volume_sdk_client.update_quota_set.assert_called_once_with( self.projects[0].id, **kwargs_volume ) self.network_client.update_quota.assert_called_once_with( @@ -930,18 +913,18 @@ def test_quota_set_with_force(self): def test_quota_set_with_no_force(self): arglist = [ '--subnets', - str(network_fakes.QUOTA['subnet']), + str(10), '--volumes', - str(volume_fakes.QUOTA['volumes']), + str(30), '--cores', - str(compute_fakes.core_num), + str(20), '--no-force', self.projects[0].name, ] verifylist = [ - ('subnet', network_fakes.QUOTA['subnet']), - ('volumes', volume_fakes.QUOTA['volumes']), - ('cores', compute_fakes.core_num), + ('subnet', 10), + ('volumes', 30), + ('cores', 20), ('force', False), ('project', self.projects[0].name), ] @@ -950,19 +933,19 @@ def test_quota_set_with_no_force(self): result = self.cmd.take_action(parsed_args) kwargs_compute = { - 'cores': compute_fakes.core_num, + 'cores': 20, } kwargs_volume = { - 'volumes': volume_fakes.QUOTA['volumes'], + 'volumes': 30, } kwargs_network = { - 'subnet': network_fakes.QUOTA['subnet'], + 'subnet': 10, 'check_limit': True, } - self.compute_quotas_mock.update.assert_called_once_with( + self.compute_sdk_client.update_quota_set.assert_called_once_with( self.projects[0].id, **kwargs_compute ) - self.volume_quotas_mock.update.assert_called_once_with( + self.volume_sdk_client.update_quota_set.assert_called_once_with( self.projects[0].id, **kwargs_volume ) self.network_client.update_quota.assert_called_once_with( @@ -975,47 +958,36 @@ class TestQuotaShow(TestQuota): def setUp(self): super().setUp() - self.compute_quota = compute_fakes.create_one_comp_quota() - self.compute_quotas_mock.get.return_value = self.compute_quota - self.compute_default_quota = ( - compute_fakes.create_one_default_comp_quota() + self.identity_sdk_client.find_project.return_value = self.projects[0] + + self.compute_sdk_client.get_quota_set.return_value = ( + sdk_fakes.generate_fake_resource(_compute_quota_set.QuotaSet) ) - self.compute_quotas_mock.defaults.return_value = ( - self.compute_default_quota + self.default_compute_quotas = sdk_fakes.generate_fake_resource( + _compute_quota_set.QuotaSet ) - self.compute_quotas_class_mock.get.return_value = FakeQuotaResource( - None, - copy.deepcopy(compute_fakes.QUOTA), - loaded=True, + self.compute_sdk_client.get_quota_set_defaults.return_value = ( + self.default_compute_quotas ) - self.volume_quota = volume_fakes.create_one_vol_quota() - self.volume_quotas_mock.get.return_value = self.volume_quota - self.volume_default_quota = volume_fakes.create_one_default_vol_quota() - self.volume_quotas_mock.defaults.return_value = ( - self.volume_default_quota + self.volume_sdk_client.get_quota_set.return_value = ( + sdk_fakes.generate_fake_resource(_volume_quota_set.QuotaSet) ) - self.volume_quotas_class_mock.get.return_value = FakeQuotaResource( - None, - copy.deepcopy(volume_fakes.QUOTA), - loaded=True, + self.default_volume_quotas = sdk_fakes.generate_fake_resource( + _volume_quota_set.QuotaSet ) - - fake_network_endpoint = fakes.FakeResource( - None, - copy.deepcopy(identity_fakes.ENDPOINT), - loaded=True, + self.volume_sdk_client.get_quota_set_defaults.return_value = ( + self.default_volume_quotas ) - self.service_catalog_mock.get_endpoints.return_value = { - 'network': fake_network_endpoint - } - - self.network_client.get_quota = mock.Mock( - return_value=network_fakes.QUOTA, + self.network_client.get_quota.return_value = ( + sdk_fakes.generate_fake_resource(_network_quota_set.Quota) + ) + self.default_network_quotas = sdk_fakes.generate_fake_resource( + _network_quota_set.QuotaDefault ) - self.network_client.get_quota_default = mock.Mock( - return_value=network_fakes.QUOTA, + self.network_client.get_quota_default.return_value = ( + self.default_network_quotas ) self.cmd = quota.ShowQuota(self.app, None) @@ -1032,11 +1004,11 @@ def test_quota_show(self): self.cmd.take_action(parsed_args) - self.compute_quotas_mock.get.assert_called_once_with( + self.compute_sdk_client.get_quota_set.assert_called_once_with( self.projects[0].id, - detail=False, + usage=False, ) - self.volume_quotas_mock.get.assert_called_once_with( + self.volume_sdk_client.get_quota_set.assert_called_once_with( self.projects[0].id, usage=False, ) @@ -1059,11 +1031,11 @@ def test_quota_show__with_compute(self): self.cmd.take_action(parsed_args) - self.compute_quotas_mock.get.assert_called_once_with( + self.compute_sdk_client.get_quota_set.assert_called_once_with( self.projects[0].id, - detail=False, + usage=False, ) - self.volume_quotas_mock.get.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_volume(self): @@ -1079,8 +1051,8 @@ def test_quota_show__with_volume(self): self.cmd.take_action(parsed_args) - self.compute_quotas_mock.get.assert_not_called() - self.volume_quotas_mock.get.assert_called_once_with( + self.compute_sdk_client.get_quota_set.assert_not_called() + self.volume_sdk_client.get_quota_set.assert_called_once_with( self.projects[0].id, usage=False, ) @@ -1099,8 +1071,8 @@ def test_quota_show__with_network(self): self.cmd.take_action(parsed_args) - self.compute_quotas_mock.get.assert_not_called() - self.volume_quotas_mock.get.assert_not_called() + self.compute_sdk_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=False, @@ -1120,10 +1092,10 @@ def test_quota_show__with_default(self): self.cmd.take_action(parsed_args) - self.compute_quotas_mock.defaults.assert_called_once_with( + self.compute_sdk_client.get_quota_set_defaults.assert_called_once_with( self.projects[0].id, ) - self.volume_quotas_mock.defaults.assert_called_once_with( + self.volume_sdk_client.get_quota_set_defaults.assert_called_once_with( self.projects[0].id, ) self.network_client.get_quota_default.assert_called_once_with( @@ -1132,15 +1104,6 @@ def test_quota_show__with_default(self): self.assertNotCalled(self.network_client.get_quota) def test_quota_show__with_usage(self): - # update mocks to return detailed quota instead - self.compute_quota = compute_fakes.create_one_comp_detailed_quota() - self.compute_quotas_mock.get.return_value = self.compute_quota - self.volume_quota = volume_fakes.create_one_detailed_quota() - self.volume_quotas_mock.get.return_value = self.volume_quota - self.network_client.get_quota.return_value = ( - network_fakes.FakeQuota.create_one_net_detailed_quota() - ) - arglist = [ '--usage', self.projects[0].name, @@ -1153,11 +1116,11 @@ def test_quota_show__with_usage(self): self.cmd.take_action(parsed_args) - self.compute_quotas_mock.get.assert_called_once_with( + self.compute_sdk_client.get_quota_set.assert_called_once_with( self.projects[0].id, - detail=True, + usage=True, ) - self.volume_quotas_mock.get.assert_called_once_with( + self.volume_sdk_client.get_quota_set.assert_called_once_with( self.projects[0].id, usage=True, ) @@ -1175,14 +1138,14 @@ def test_quota_show__no_project(self): self.cmd.take_action(parsed_args) - self.compute_quotas_mock.get.assert_called_once_with( - identity_fakes.project_id, detail=False + self.compute_sdk_client.get_quota_set.assert_called_once_with( + self.projects[1].id, usage=False ) - self.volume_quotas_mock.get.assert_called_once_with( - identity_fakes.project_id, usage=False + self.volume_sdk_client.get_quota_set.assert_called_once_with( + self.projects[1].id, usage=False ) self.network_client.get_quota.assert_called_once_with( - identity_fakes.project_id, details=False + self.projects[1].id, details=False ) self.assertNotCalled(self.network_client.get_quota_default) @@ -1193,6 +1156,12 @@ class TestQuotaDelete(TestQuota): def setUp(self): super().setUp() + self.identity_sdk_client.find_project.return_value = self.projects[0] + + self.compute_sdk_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 + self.cmd = quota.DeleteQuota(self.app, None) def test_delete(self): @@ -1210,11 +1179,13 @@ def test_delete(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.projects_mock.get.assert_called_once_with(self.projects[0].id) - self.compute_quotas_mock.delete.assert_called_once_with( + 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.projects[0].id, ) - self.volume_quotas_mock.delete.assert_called_once_with( + self.volume_sdk_client.revert_quota_set.assert_called_once_with( self.projects[0].id, ) self.network_client.delete_quota.assert_called_once_with( @@ -1237,11 +1208,13 @@ def test_delete__compute(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.projects_mock.get.assert_called_once_with(self.projects[0].id) - self.compute_quotas_mock.delete.assert_called_once_with( + 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.projects[0].id, ) - self.volume_quotas_mock.delete.assert_not_called() + self.volume_sdk_client.revert_quota_set.assert_not_called() self.network_client.delete_quota.assert_not_called() def test_delete__volume(self): @@ -1260,9 +1233,11 @@ def test_delete__volume(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.projects_mock.get.assert_called_once_with(self.projects[0].id) - self.compute_quotas_mock.delete.assert_not_called() - self.volume_quotas_mock.delete.assert_called_once_with( + 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.volume_sdk_client.revert_quota_set.assert_called_once_with( self.projects[0].id, ) self.network_client.delete_quota.assert_not_called() @@ -1283,9 +1258,11 @@ def test_delete__network(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - self.projects_mock.get.assert_called_once_with(self.projects[0].id) - self.compute_quotas_mock.delete.assert_not_called() - self.volume_quotas_mock.delete.assert_not_called() + 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.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 5a19ad56a..1e1bc4075 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -46,42 +46,6 @@ from openstackclient.tests.unit import utils from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes -floating_ip_num = 100 -fix_ip_num = 100 -injected_file_num = 100 -injected_file_size_num = 10240 -injected_path_size_num = 255 -key_pair_num = 100 -core_num = 20 -ram_num = 51200 -instance_num = 10 -property_num = 128 -secgroup_rule_num = 20 -secgroup_num = 10 -servgroup_num = 10 -servgroup_members_num = 10 -project_name = 'project_test' -QUOTA = { - 'project': project_name, - 'floating-ips': floating_ip_num, - 'fix-ips': fix_ip_num, - 'injected-files': injected_file_num, - 'injected-file-size': injected_file_size_num, - 'injected-path-size': injected_path_size_num, - 'key-pairs': key_pair_num, - 'cores': core_num, - 'ram': ram_num, - 'instances': instance_num, - 'properties': property_num, - 'secgroup_rules': secgroup_rule_num, - 'secgroups': secgroup_num, - 'server-groups': servgroup_num, - 'server-group-members': servgroup_members_num, -} - -QUOTA_columns = tuple(sorted(QUOTA)) -QUOTA_data = tuple(QUOTA[x] for x in sorted(QUOTA)) - class FakeComputev2Client: def __init__(self, **kwargs): @@ -105,12 +69,6 @@ def __init__(self, **kwargs): self.flavor_access = mock.Mock() self.flavor_access.resource_class = fakes.FakeResource(None, {}) - self.quotas = mock.Mock() - self.quotas.resource_class = fakes.FakeResource(None, {}) - - self.quota_classes = mock.Mock() - self.quota_classes.resource_class = fakes.FakeResource(None, {}) - self.usage = mock.Mock() self.usage.resource_class = fakes.FakeResource(None, {}) @@ -1077,98 +1035,6 @@ def create_usages(attrs=None, count=2): return usages -def create_one_comp_quota(attrs=None): - """Create one quota""" - - attrs = attrs or {} - - quota_attrs = { - 'id': 'project-id-' + uuid.uuid4().hex, - 'cores': 20, - 'fixed_ips': 30, - 'injected_files': 100, - 'injected_file_content_bytes': 10240, - 'injected_file_path_bytes': 255, - 'instances': 50, - 'key_pairs': 20, - 'metadata_items': 10, - 'ram': 51200, - 'server_groups': 10, - 'server_group_members': 10, - } - - quota_attrs.update(attrs) - quota = fakes.FakeResource(info=copy.deepcopy(quota_attrs), loaded=True) - - quota.project_id = quota_attrs['id'] - - return quota - - -def create_one_default_comp_quota(attrs=None): - """Create one quota""" - - attrs = attrs or {} - - quota_attrs = { - 'id': 'project-id-' + uuid.uuid4().hex, - 'cores': 10, - 'fixed_ips': 10, - 'injected_files': 100, - 'injected_file_content_bytes': 10240, - 'injected_file_path_bytes': 255, - 'instances': 20, - 'key_pairs': 20, - 'metadata_items': 10, - 'ram': 51200, - 'server_groups': 10, - 'server_group_members': 10, - } - - quota_attrs.update(attrs) - quota = fakes.FakeResource(info=copy.deepcopy(quota_attrs), loaded=True) - - quota.project_id = quota_attrs['id'] - - return quota - - -def create_one_comp_detailed_quota(attrs=None): - """Create one quota""" - - attrs = attrs or {} - - quota_attrs = { - 'id': 'project-id-' + uuid.uuid4().hex, - 'cores': {'reserved': 0, 'in_use': 0, 'limit': 20}, - 'fixed_ips': {'reserved': 0, 'in_use': 0, 'limit': 30}, - 'injected_files': {'reserved': 0, 'in_use': 0, 'limit': 100}, - 'injected_file_content_bytes': { - 'reserved': 0, - 'in_use': 0, - 'limit': 10240, - }, - 'injected_file_path_bytes': { - 'reserved': 0, - 'in_use': 0, - 'limit': 255, - }, - 'instances': {'reserved': 0, 'in_use': 0, 'limit': 50}, - 'key_pairs': {'reserved': 0, 'in_use': 0, 'limit': 20}, - 'metadata_items': {'reserved': 0, 'in_use': 0, 'limit': 10}, - 'ram': {'reserved': 0, 'in_use': 0, 'limit': 51200}, - 'server_groups': {'reserved': 0, 'in_use': 0, 'limit': 10}, - 'server_group_members': {'reserved': 0, 'in_use': 0, 'limit': 10}, - } - - quota_attrs.update(attrs) - quota = fakes.FakeResource(info=copy.deepcopy(quota_attrs), loaded=True) - - quota.project_id = quota_attrs['id'] - - return quota - - def create_limits(attrs=None): """Create a fake limits object.""" attrs = attrs or {} diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index 28af24287..999e1027c 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -42,22 +42,6 @@ from openstackclient.tests.unit import utils -QUOTA = { - "subnet": 10, - "network": 10, - "floatingip": 50, - "subnetpool": -1, - "security_group_rule": 100, - "security_group": 10, - "router": 10, - "rbac_policy": -1, - "port": 50, - "vip": 10, - "member": 10, - "healthmonitor": 10, - "l7policy": 5, -} - RULE_TYPE_BANDWIDTH_LIMIT = 'bandwidth-limit' RULE_TYPE_DSCP_MARKING = 'dscp-marking' RULE_TYPE_MINIMUM_BANDWIDTH = 'minimum-bandwidth' @@ -990,82 +974,6 @@ def create_network_service_providers(attrs=None, count=2): return service_providers -class FakeQuota: - """Fake quota""" - - @staticmethod - def create_one_net_quota(attrs=None): - """Create one quota""" - attrs = attrs or {} - - quota_attrs = { - 'floating_ips': 20, - 'networks': 25, - 'ports': 11, - 'rbac_policies': 15, - 'routers': 40, - 'security_groups': 10, - 'security_group_rules': 100, - 'subnets': 20, - 'subnet_pools': 30, - } - - quota_attrs.update(attrs) - - quota = fakes.FakeResource( - info=copy.deepcopy(quota_attrs), loaded=True - ) - return quota - - @staticmethod - def create_one_default_net_quota(attrs=None): - """Create one quota""" - attrs = attrs or {} - - quota_attrs = { - 'floatingip': 30, - 'network': 20, - 'port': 10, - 'rbac_policy': 25, - 'router': 30, - 'security_group': 30, - 'security_group_rule': 200, - 'subnet': 10, - 'subnetpool': 20, - } - - quota_attrs.update(attrs) - - quota = fakes.FakeResource( - info=copy.deepcopy(quota_attrs), loaded=True - ) - return quota - - @staticmethod - def create_one_net_detailed_quota(attrs=None): - """Create one quota""" - attrs = attrs or {} - - quota_attrs = { - 'floating_ips': {'used': 0, 'reserved': 0, 'limit': 20}, - 'networks': {'used': 0, 'reserved': 0, 'limit': 25}, - 'ports': {'used': 0, 'reserved': 0, 'limit': 11}, - 'rbac_policies': {'used': 0, 'reserved': 0, 'limit': 15}, - 'routers': {'used': 0, 'reserved': 0, 'limit': 40}, - 'security_groups': {'used': 0, 'reserved': 0, 'limit': 10}, - 'security_group_rules': {'used': 0, 'reserved': 0, 'limit': 100}, - 'subnets': {'used': 0, 'reserved': 0, 'limit': 20}, - 'subnet_pools': {'used': 0, 'reserved': 0, 'limit': 30}, - } - - quota_attrs.update(attrs) - - quota = fakes.FakeResource( - info=copy.deepcopy(quota_attrs), loaded=True - ) - return quota - - class FakeFloatingIPPortForwarding: """Fake one or more Port forwarding""" diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index 389b10b0f..1c183728c 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -33,19 +33,6 @@ from openstackclient.tests.unit import utils -QUOTA = { - "gigabytes": 1000, - "volumes": 11, - "snapshots": 10, - "backups": 10, - "backup_gigabytes": 1000, - "per_volume_gigabytes": -1, - "gigabytes_volume_type_backend": -1, - "volumes_volume_type_backend": -1, - "snapshots_volume_type_backend": -1, -} - - class FakeVolumeClient: def __init__(self, **kwargs): self.auth_token = kwargs['token'] @@ -68,10 +55,6 @@ def __init__(self, **kwargs): self.pools.resource_class = fakes.FakeResource(None, {}) self.qos_specs = mock.Mock() self.qos_specs.resource_class = fakes.FakeResource(None, {}) - self.quota_classes = mock.Mock() - self.quota_classes.resource_class = fakes.FakeResource(None, {}) - self.quotas = mock.Mock() - self.quotas.resource_class = fakes.FakeResource(None, {}) self.restores = mock.Mock() self.restores.resource_class = fakes.FakeResource(None, {}) self.services = mock.Mock() @@ -991,74 +974,3 @@ def create_one_encryption_volume_type(attrs=None): info=copy.deepcopy(encryption_info), loaded=True ) return encryption_type - - -def create_one_vol_quota(attrs=None): - """Create one quota""" - attrs = attrs or {} - - quota_attrs = { - 'id': 'project-id-' + uuid.uuid4().hex, - 'backups': 100, - 'backup_gigabytes': 100, - 'gigabytes': 10, - 'per_volume_gigabytes': 10, - 'snapshots': 0, - 'volumes': 10, - } - - quota_attrs.update(attrs) - - quota = fakes.FakeResource(info=copy.deepcopy(quota_attrs), loaded=True) - quota.project_id = quota_attrs['id'] - - return quota - - -def create_one_default_vol_quota(attrs=None): - """Create one quota""" - attrs = attrs or {} - - quota_attrs = { - 'id': 'project-id-' + uuid.uuid4().hex, - 'backups': 100, - 'backup_gigabytes': 100, - 'gigabytes': 100, - 'per_volume_gigabytes': 100, - 'snapshots': 100, - 'volumes': 100, - } - - quota_attrs.update(attrs) - - quota = fakes.FakeResource(info=copy.deepcopy(quota_attrs), loaded=True) - quota.project_id = quota_attrs['id'] - - return quota - - -def create_one_detailed_quota(attrs=None): - """Create one quota""" - attrs = attrs or {} - - quota_attrs = { - 'volumes': {'limit': 3, 'in_use': 1, 'reserved': 0}, - 'per_volume_gigabytes': {'limit': -1, 'in_use': 0, 'reserved': 0}, - 'snapshots': {'limit': 10, 'in_use': 0, 'reserved': 0}, - 'gigabytes': {'limit': 1000, 'in_use': 5, 'reserved': 0}, - 'backups': {'limit': 10, 'in_use': 0, 'reserved': 0}, - 'backup_gigabytes': {'limit': 1000, 'in_use': 0, 'reserved': 0}, - 'volumes_lvmdriver-1': {'limit': -1, 'in_use': 1, 'reserved': 0}, - 'gigabytes_lvmdriver-1': {'limit': -1, 'in_use': 5, 'reserved': 0}, - 'snapshots_lvmdriver-1': {'limit': -1, 'in_use': 0, 'reserved': 0}, - 'volumes___DEFAULT__': {'limit': -1, 'in_use': 0, 'reserved': 0}, - 'gigabytes___DEFAULT__': {'limit': -1, 'in_use': 0, 'reserved': 0}, - 'snapshots___DEFAULT__': {'limit': -1, 'in_use': 0, 'reserved': 0}, - 'groups': {'limit': 10, 'in_use': 0, 'reserved': 0}, - 'id': uuid.uuid4().hex, - } - quota_attrs.update(attrs) - - quota = fakes.FakeResource(info=copy.deepcopy(quota_attrs), loaded=True) - - return quota diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index e79282704..208cd7622 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -58,10 +58,6 @@ def __init__(self, **kwargs): self.group_types.resource_class = fakes.FakeResource(None, {}) self.messages = mock.Mock() self.messages.resource_class = fakes.FakeResource(None, {}) - self.quota_classes = mock.Mock() - self.quota_classes.resource_class = fakes.FakeResource(None, {}) - self.quotas = mock.Mock() - self.quotas.resource_class = fakes.FakeResource(None, {}) self.resource_filters = mock.Mock() self.resource_filters.resource_class = fakes.FakeResource(None, {}) self.restores = mock.Mock() diff --git a/requirements.txt b/requirements.txt index a20ce69f8..341525ea7 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.2.0 # Apache-2.0 +openstacksdk>=3.3.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 ece30e8f703f918e391e934ecd2b201f211bfbfe Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 18 Jul 2024 11:48:43 +0100 Subject: [PATCH 163/403] 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 438e40db3683307e35944b5a41b2e9895093135d Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Thu, 9 May 2024 17:51:02 +0100 Subject: [PATCH 164/403] Remove python-novaclient There are a few remnants left here but this is trivial to clean up now. Change-Id: I517d906796338e64a31afa08b9ee6909b08e0115 Signed-off-by: Stephen Finucane --- openstackclient/compute/client.py | 103 ++---------------- openstackclient/compute/v2/hypervisor.py | 7 +- openstackclient/compute/v2/service.py | 2 +- openstackclient/image/client.py | 8 +- .../tests/functional/common/test_module.py | 2 +- .../tests/unit/compute/v2/fakes.py | 9 -- .../tests/unit/compute/v2/test_hypervisor.py | 4 +- .../tests/unit/compute/v2/test_server.py | 3 - requirements.txt | 1 - 9 files changed, 19 insertions(+), 120 deletions(-) diff --git a/openstackclient/compute/client.py b/openstackclient/compute/client.py index 157e0dddc..23979ec7f 100644 --- a/openstackclient/compute/client.py +++ b/openstackclient/compute/client.py @@ -15,76 +15,28 @@ import logging -from osc_lib import exceptions from osc_lib import utils from openstackclient.i18n import _ - LOG = logging.getLogger(__name__) DEFAULT_API_VERSION = '2.1' API_VERSION_OPTION = 'os_compute_api_version' API_NAME = 'compute' API_VERSIONS = { - "2": "novaclient.client", - "2.1": "novaclient.client", -} - -COMPUTE_API_VERSIONS = { - '2': 'openstackclient.api.compute_v2.APIv2', + '2': 'openstack.connection.Connection', + '2.1': 'openstack.connection.Connection', } -# Save the microversion if in use -_compute_api_version = None - def make_client(instance): """Returns a compute service client.""" - - # Defer client import until we actually need them - from novaclient import client as nova_client - - if _compute_api_version is not None: - version = _compute_api_version - else: - version = instance._api_version[API_NAME] - from novaclient import api_versions - - # convert to APIVersion object - version = api_versions.get_api_version(version) - - if version.is_latest(): - import novaclient - - # NOTE(RuiChen): executing version discovery make sense, but that need - # an initialized REST client, it's not available now, - # fallback to use the max version of novaclient side. - version = novaclient.API_MAX_VERSION - - # Set client http_log_debug to True if verbosity level is high enough - http_log_debug = utils.get_effective_log_level() <= logging.DEBUG - - extensions = [ - ext - for ext in nova_client.discover_extensions(version) - if ext.name == "list_extensions" - ] - - # Remember interface only if it is set - kwargs = utils.build_kwargs_dict('endpoint_type', instance.interface) - - client = nova_client.Client( - version, - session=instance.session, - extensions=extensions, - http_log_debug=http_log_debug, - timings=instance.timing, - region_name=instance.region_name, - **kwargs + LOG.debug( + 'Compute client initialized using OpenStack SDK: %s', + instance.sdk_connection.compute, ) - - return client + return instance.sdk_connection.compute def build_option_parser(parser): @@ -93,48 +45,7 @@ def build_option_parser(parser): '--os-compute-api-version', metavar='', default=utils.env('OS_COMPUTE_API_VERSION'), - help=_( - "Compute API version, default=%s " "(Env: OS_COMPUTE_API_VERSION)" - ) + help=_("Compute API version, default=%s (Env: OS_COMPUTE_API_VERSION)") % DEFAULT_API_VERSION, ) return parser - - -def check_api_version(check_version): - """Validate version supplied by user - - Returns: - - * True if version is OK - * False if the version has not been checked and the previous plugin - check should be performed - * throws an exception if the version is no good - - TODO(dtroyer): make the exception thrown a version-related one - """ - - # Defer client imports until we actually need them - import novaclient - from novaclient import api_versions - - global _compute_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 2.0 or any valid version supplied by the user. - _compute_api_version = api_versions.get_api_version(check_version) - - # Bypass X.latest format microversion - if not _compute_api_version.is_latest(): - if _compute_api_version > api_versions.APIVersion("2.0"): - if not _compute_api_version.matches( - novaclient.API_MIN_VERSION, - novaclient.API_MAX_VERSION, - ): - msg = _("versions supported by client: %(min)s - %(max)s") % { - "min": novaclient.API_MIN_VERSION.get_string(), - "max": novaclient.API_MAX_VERSION.get_string(), - } - raise exceptions.CommandError(msg) - return True diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py index a118913b6..fffeb3521 100644 --- a/openstackclient/compute/v2/hypervisor.py +++ b/openstackclient/compute/v2/hypervisor.py @@ -18,7 +18,7 @@ import json import re -from novaclient import exceptions as nova_exceptions +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 @@ -223,8 +223,9 @@ def take_action(self, parsed_args): hypervisor['uptime'] = m.group(2) hypervisor['users'] = m.group(3) hypervisor['load_average'] = m.group(4) - except nova_exceptions.HTTPNotImplemented: - pass + except sdk_exceptions.HttpException as exc: + if exc.status_code != 501: + raise hypervisor['service_id'] = service_details['id'] hypervisor['service_host'] = service_details['host'] diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index d6e522d03..b96386e03 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -198,7 +198,7 @@ def _find_service_by_host_and_binary(compute_client, host, binary): :param host: the name of the compute service host :param binary: the compute service binary, e.g. nova-compute - :returns: novaclient.v2.services.Service dict-like object + :returns: The service. :raises: CommandError if no or multiple results were found """ services = list(compute_client.services(host=host, binary=binary)) diff --git a/openstackclient/image/client.py b/openstackclient/image/client.py index a3da099ad..d5ee52b35 100644 --- a/openstackclient/image/client.py +++ b/openstackclient/image/client.py @@ -19,19 +19,19 @@ from openstackclient.i18n import _ - LOG = logging.getLogger(__name__) DEFAULT_API_VERSION = '2' API_VERSION_OPTION = 'os_image_api_version' -API_NAME = "image" +API_NAME = 'image' API_VERSIONS = { - "1": "openstack.connection.Connection", - "2": "openstack.connection.Connection", + '1': 'openstack.connection.Connection', + '2': 'openstack.connection.Connection', } def make_client(instance): + """Returns an image service client.""" LOG.debug( 'Image client initialized using OpenStack SDK: %s', instance.sdk_connection.image, diff --git a/openstackclient/tests/functional/common/test_module.py b/openstackclient/tests/functional/common/test_module.py index 0cb373918..150668349 100644 --- a/openstackclient/tests/functional/common/test_module.py +++ b/openstackclient/tests/functional/common/test_module.py @@ -18,7 +18,7 @@ class ModuleTest(base.TestCase): """Functional tests for openstackclient module list output.""" - CLIENTS = ['openstackclient', 'keystoneclient', 'novaclient', 'openstack'] + CLIENTS = ['openstackclient', 'keystoneclient', 'openstack'] LIBS = ['osc_lib', 'keystoneauth1'] diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 1e1bc4075..d021c5d76 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -20,7 +20,6 @@ import uuid from keystoneauth1 import discover -from novaclient import api_versions from openstack.compute.v2 import _proxy from openstack.compute.v2 import aggregate as _aggregate from openstack.compute.v2 import availability_zone as _availability_zone @@ -105,12 +104,6 @@ class FakeClientMixin: def setUp(self): super().setUp() - self.app.client_manager.compute = FakeComputev2Client( - endpoint=fakes.AUTH_URL, - token=fakes.AUTH_TOKEN, - ) - self.compute_client = self.app.client_manager.compute - # TODO(stephenfin): Rename to 'compute_client' once all commands are # migrated to SDK self.app.client_manager.sdk_connection.compute = mock.Mock( @@ -130,8 +123,6 @@ def set_compute_api_version(self, version: str = '2.1'): """ assert re.match(r'2.\d+', version) - self.compute_client.api_version = api_versions.APIVersion(version) - self.compute_sdk_client.default_microversion = version self.compute_sdk_client.get_endpoint_data.return_value = ( discover.EndpointData( diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor.py b/openstackclient/tests/unit/compute/v2/test_hypervisor.py index ba3d58e8d..2c226f004 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor.py @@ -14,7 +14,7 @@ import json -from novaclient import exceptions as nova_exceptions +from openstack import exceptions as sdk_exceptions from osc_lib.cli import format_columns from osc_lib import exceptions @@ -484,7 +484,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 = ( - nova_exceptions.HTTPNotImplemented(501) + sdk_exceptions.HttpException(http_status=501) ) # In base command class ShowOne in cliff, abstract method take_action() diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index c96e0fada..a116ff3c3 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -5452,9 +5452,6 @@ def test_server_list_v269_with_partial_constructs(self): {"href": "http://fake/v2.1/", "rel": "self"}, {"href": "http://fake", "rel": "bookmark"}, ], - # We need to pass networks as {} because its defined as a property - # of the novaclient Server class which gives {} by default. If not - # it will fail at formatting the networks info later on. "networks": {}, } fake_server = compute_fakes.fakes.FakeResource( diff --git a/requirements.txt b/requirements.txt index 341525ea7..3c494d981 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,6 @@ openstacksdk>=3.3.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 -python-novaclient>=18.1.0 # Apache-2.0 python-cinderclient>=3.3.0 # Apache-2.0 requests>=2.14.2 # Apache-2.0 stevedore>=2.0.1 # Apache-2.0 From 0d89f01448ee2d77d82a9e86e18a0fd909ceb9fe Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 10 May 2024 14:26:39 +0100 Subject: [PATCH 165/403] 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 166/403] 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 167/403] 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 168/403] 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 169/403] 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 170/403] 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 171/403] 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 172/403] 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 173/403] 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 174/403] 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 175/403] 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 176/403] 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 177/403] 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 178/403] 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 179/403] 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 180/403] 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 181/403] 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 182/403] 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 183/403] 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 184/403] 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 185/403] 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 186/403] 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 187/403] 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 188/403] 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 189/403] 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 190/403] 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 191/403] 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 192/403] 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 193/403] 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 194/403] 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 195/403] 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 196/403] 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 197/403] 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 198/403] 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 199/403] 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 200/403] 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 201/403] 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 202/403] 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 203/403] 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 204/403] 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 205/403] 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 206/403] 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 207/403] 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 208/403] 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 209/403] 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 210/403] 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 211/403] 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 212/403] 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 213/403] 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 214/403] 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 215/403] 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 216/403] 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 217/403] 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 218/403] 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 219/403] 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 220/403] 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 221/403] 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 222/403] 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 223/403] 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 224/403] 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 225/403] 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 226/403] 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 227/403] 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 228/403] 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 229/403] 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 230/403] 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 231/403] 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 232/403] 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 233/403] 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 234/403] 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 235/403] 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 236/403] 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 237/403] [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 238/403] 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 239/403] [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 240/403] [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 241/403] 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 242/403] 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 243/403] 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 244/403] 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 245/403] 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 246/403] 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 247/403] 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 248/403] 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 249/403] 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 250/403] 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 251/403] 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 252/403] 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 253/403] 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 254/403] 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 255/403] [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 256/403] 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 257/403] 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 258/403] 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 259/403] 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 260/403] 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 261/403] 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 262/403] 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 263/403] 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 264/403] 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 265/403] 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 266/403] 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 267/403] 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 268/403] 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 269/403] 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 270/403] 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 271/403] 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 272/403] 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 273/403] 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 274/403] 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 275/403] 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 276/403] 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 277/403] 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 278/403] 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 279/403] 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 280/403] 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 281/403] 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 282/403] 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 283/403] 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 284/403] 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 285/403] 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 286/403] 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 287/403] 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 288/403] 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 289/403] 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 290/403] 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 291/403] 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 292/403] 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 293/403] 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 294/403] 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 295/403] 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 296/403] 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 297/403] 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 298/403] 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 299/403] 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 300/403] 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 301/403] 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 302/403] 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 303/403] 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 304/403] 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 305/403] 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 306/403] 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 307/403] 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 308/403] 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 309/403] 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 310/403] 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 311/403] 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 312/403] 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 313/403] 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 314/403] 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 315/403] 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 316/403] 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 317/403] 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 318/403] 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 319/403] 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 320/403] 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 321/403] 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 322/403] 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 323/403] 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 324/403] 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 325/403] 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 326/403] 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 327/403] 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 328/403] 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 329/403] 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 330/403] 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 331/403] 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 332/403] 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 333/403] 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 334/403] 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 335/403] 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 336/403] 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 337/403] 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 338/403] 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 339/403] 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 340/403] 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 341/403] 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 342/403] 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 343/403] 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 344/403] 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 345/403] 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 346/403] 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 347/403] 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 348/403] 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 349/403] 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 350/403] 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 351/403] 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 352/403] 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 353/403] 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 354/403] 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 355/403] 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 356/403] 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 357/403] 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 358/403] 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 359/403] 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 360/403] 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 361/403] 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 362/403] 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 363/403] 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 364/403] 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 365/403] 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 366/403] 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 367/403] 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 368/403] 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 369/403] 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 370/403] 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 371/403] 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 372/403] 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 373/403] 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 374/403] 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 375/403] 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 376/403] 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 377/403] 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 378/403] 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 379/403] 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 380/403] 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 381/403] 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 382/403] 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 383/403] 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 384/403] 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 385/403] 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 386/403] 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 387/403] 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 388/403] 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 389/403] 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 390/403] 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 391/403] 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 392/403] 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 393/403] 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 394/403] 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 395/403] 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 396/403] 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 397/403] 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 398/403] 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 399/403] 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 400/403] 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 401/403] 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 402/403] 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 403/403] 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