From 46bd6ef91f297aa84fb761b0e7fd983668da56de Mon Sep 17 00:00:00 2001 From: Pierre Riteau Date: Tue, 9 Mar 2021 14:49:38 +0100 Subject: [PATCH 001/706] Update volume create documentation Change I94aa7a9824e44f9585ffb45e5e7637b9588539b4 removed these options. Change-Id: I43d84b5532ae6570e1486867c03b8ebec81e38e4 --- doc/source/cli/command-objects/volume.rst | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/doc/source/cli/command-objects/volume.rst b/doc/source/cli/command-objects/volume.rst index fc6188c0a8..ac414110a0 100644 --- a/doc/source/cli/command-objects/volume.rst +++ b/doc/source/cli/command-objects/volume.rst @@ -17,13 +17,10 @@ Create new volume [--type ] [--image | --snapshot | --source ] [--description ] - [--user ] - [--project ] [--availability-zone ] [--consistency-group ] [--property [...] ] [--hint [...] ] - [--multi-attach] [--bootable | --non-bootable] [--read-only | --read-write] @@ -58,14 +55,6 @@ Create new volume Volume description -.. option:: --user - - Specify an alternate user (name or ID) - -.. option:: --project - - Specify an alternate project (name or ID) - .. option:: --availability-zone Create volume in ```` @@ -83,10 +72,6 @@ Create new volume Arbitrary scheduler hint key-value pairs to help boot an instance (repeat option to set multiple hints) -.. option:: --multi-attach - - Allow volume to be attached more than once (default to False) - .. option:: --bootable Mark volume as bootable @@ -108,10 +93,6 @@ Create new volume Volume name -The :option:`--project` and :option:`--user` options are typically only -useful for admin users, but may be allowed for other users depending on -the policy of the cloud and the roles granted to the user. - volume delete ------------- From e4e9fb594d003ea6c3ec29aab0bccf72ffab6781 Mon Sep 17 00:00:00 2001 From: Brian Haley Date: Wed, 3 Mar 2021 12:46:02 -0500 Subject: [PATCH 002/706] Add --subnet-pool to subnet list The neutron API supports filtering subnets by subnet pool id, but the CLI was missing support for it. Change-Id: Ic230c2c5cda8255d8f2c422880aeac81670b2df3 --- openstackclient/network/v2/subnet.py | 10 +++++ .../tests/unit/network/v2/test_subnet.py | 42 +++++++++++++++++++ ...st-subnet-by-pool-id-a642efc13d04fa08.yaml | 5 +++ 3 files changed, 57 insertions(+) create mode 100644 releasenotes/notes/list-subnet-by-pool-id-a642efc13d04fa08.yaml diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index f87f7abe1b..92a9e750c5 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -488,6 +488,12 @@ def get_parser(self, prog_name): "(in CIDR notation) in output " "e.g.: --subnet-range 10.10.0.0/16") ) + parser.add_argument( + '--subnet-pool', + metavar='', + help=_("List only subnets which belong to a given subnet pool " + "in output (Name or ID)") + ) _tag.add_tag_filtering_option_to_parser(parser, _('subnets')) return parser @@ -523,6 +529,10 @@ def take_action(self, parsed_args): filters['name'] = parsed_args.name if parsed_args.subnet_range: filters['cidr'] = parsed_args.subnet_range + if parsed_args.subnet_pool: + subnetpool_id = network_client.find_subnet_pool( + parsed_args.subnet_pool, ignore_missing=False).id + filters['subnetpool_id'] = subnetpool_id _tag.get_tag_filtering_args(parsed_args, filters) data = network_client.subnets(**filters) diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py index 1b4bfdad2f..06096f4b63 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet.py +++ b/openstackclient/tests/unit/network/v2/test_subnet.py @@ -899,6 +899,48 @@ def test_subnet_list_subnet_range(self): self.assertEqual(self.columns, columns) self.assertItemsEqual(self.data, list(data)) + def test_subnet_list_subnetpool_by_name(self): + subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool() + subnet = network_fakes.FakeSubnet.create_one_subnet( + {'subnetpool_id': subnet_pool.id}) + self.network.find_network = mock.Mock(return_value=subnet) + self.network.find_subnet_pool = mock.Mock(return_value=subnet_pool) + arglist = [ + '--subnet-pool', subnet_pool.name, + ] + verifylist = [ + ('subnet_pool', subnet_pool.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'subnetpool_id': subnet_pool.id} + + self.network.subnets.assert_called_once_with(**filters) + self.assertEqual(self.columns, columns) + self.assertItemsEqual(self.data, list(data)) + + def test_subnet_list_subnetpool_by_id(self): + subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool() + subnet = network_fakes.FakeSubnet.create_one_subnet( + {'subnetpool_id': subnet_pool.id}) + self.network.find_network = mock.Mock(return_value=subnet) + self.network.find_subnet_pool = mock.Mock(return_value=subnet_pool) + arglist = [ + '--subnet-pool', subnet_pool.id, + ] + verifylist = [ + ('subnet_pool', subnet_pool.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = {'subnetpool_id': subnet_pool.id} + + self.network.subnets.assert_called_once_with(**filters) + self.assertEqual(self.columns, columns) + self.assertItemsEqual(self.data, list(data)) + def test_list_with_tag_options(self): arglist = [ '--tags', 'red,blue', diff --git a/releasenotes/notes/list-subnet-by-pool-id-a642efc13d04fa08.yaml b/releasenotes/notes/list-subnet-by-pool-id-a642efc13d04fa08.yaml new file mode 100644 index 0000000000..d784a9aadb --- /dev/null +++ b/releasenotes/notes/list-subnet-by-pool-id-a642efc13d04fa08.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``--subnet-pool`` option to ``subnet list`` to filter + by subnets by subnet pool. From e82a05864f482acc485d1bd35a4db23452f8b2ac Mon Sep 17 00:00:00 2001 From: Dirk Mueller Date: Mon, 3 May 2021 22:07:39 +0200 Subject: [PATCH 003/706] Replace assertItemsEqual with assertCountEqual assertItemsEqual was removed from Python's unittest.TestCase in Python 3.3 [1][2]. We have been able to use them since then, because testtools required unittest2, which still included it. With testtools removing Python 2.7 support [3][4], we will lose support for assertItemsEqual, so we should switch to use assertCountEqual. [1] - https://bugs.python.org/issue17866 [2] - https://hg.python.org/cpython/rev/d9921cb6e3cd [3] - testing-cabal/testtools#286 [4] - testing-cabal/testtools#277 Change-Id: I0bbffbec8889b8b3067cfe17d258f5cb16624f38 --- .../tests/unit/compute/v2/test_aggregate.py | 16 ++--- .../tests/unit/compute/v2/test_flavor.py | 14 ++-- .../tests/unit/compute/v2/test_hypervisor.py | 6 +- .../unit/compute/v2/test_server_backup.py | 6 +- .../unit/compute/v2/test_server_image.py | 6 +- .../tests/unit/identity/v2_0/test_catalog.py | 6 +- .../tests/unit/identity/v2_0/test_project.py | 2 +- .../tests/unit/identity/v2_0/test_user.py | 8 +-- .../tests/unit/identity/v3/test_catalog.py | 4 +- .../identity/v3/test_identity_provider.py | 24 +++---- .../tests/unit/image/v1/test_image.py | 10 +-- .../tests/unit/image/v2/test_image.py | 24 +++---- .../unit/network/v2/test_address_group.py | 14 ++-- .../unit/network/v2/test_ip_availability.py | 8 +-- .../tests/unit/network/v2/test_network.py | 46 ++++++------- .../unit/network/v2/test_network_agent.py | 14 ++-- .../tests/unit/network/v2/test_port.py | 68 +++++++++---------- .../tests/unit/network/v2/test_router.py | 32 ++++----- .../network/v2/test_security_group_compute.py | 10 +-- .../network/v2/test_security_group_network.py | 16 ++--- .../tests/unit/network/v2/test_subnet.py | 42 ++++++------ .../tests/unit/network/v2/test_subnet_pool.py | 40 +++++------ .../tests/unit/volume/v1/test_qos_specs.py | 12 ++-- .../tests/unit/volume/v1/test_type.py | 14 ++-- .../tests/unit/volume/v1/test_volume.py | 36 +++++----- .../unit/volume/v1/test_volume_backup.py | 10 +-- .../unit/volume/v2/test_consistency_group.py | 14 ++-- .../tests/unit/volume/v2/test_qos_specs.py | 12 ++-- .../tests/unit/volume/v2/test_type.py | 24 +++---- .../tests/unit/volume/v2/test_volume.py | 40 +++++------ .../unit/volume/v2/test_volume_backup.py | 4 +- .../unit/volume/v2/test_volume_snapshot.py | 2 +- 32 files changed, 292 insertions(+), 292 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index 8563f98811..7c4fe5cbd3 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -88,7 +88,7 @@ def test_aggregate_add_host(self): self.sdk_client.add_host_to_aggregate.assert_called_once_with( self.fake_ag.id, parsed_args.host) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestAggregateCreate(TestAggregate): @@ -112,7 +112,7 @@ def test_aggregate_create(self): self.sdk_client.create_aggregate.assert_called_once_with( name=parsed_args.name) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_aggregate_create_with_zone(self): arglist = [ @@ -129,7 +129,7 @@ def test_aggregate_create_with_zone(self): self.sdk_client.create_aggregate.assert_called_once_with( name=parsed_args.name, availability_zone=parsed_args.zone) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_aggregate_create_with_property(self): arglist = [ @@ -148,7 +148,7 @@ def test_aggregate_create_with_property(self): self.sdk_client.set_aggregate_metadata.assert_called_once_with( self.fake_ag.id, parsed_args.properties) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestAggregateDelete(TestAggregate): @@ -265,7 +265,7 @@ def test_aggregate_list(self): columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.list_columns, columns) - self.assertItemsEqual(self.list_data, tuple(data)) + self.assertCountEqual(self.list_data, tuple(data)) def test_aggregate_list_with_long(self): arglist = [ @@ -278,7 +278,7 @@ def test_aggregate_list_with_long(self): columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.list_columns_long, columns) - self.assertItemsEqual(self.list_data_long, tuple(data)) + self.assertCountEqual(self.list_data_long, tuple(data)) class TestAggregateRemoveHost(TestAggregate): @@ -306,7 +306,7 @@ def test_aggregate_remove_host(self): self.sdk_client.remove_host_from_aggregate.assert_called_once_with( self.fake_ag.id, parsed_args.host) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestAggregateSet(TestAggregate): @@ -492,7 +492,7 @@ def test_aggregate_show(self): parsed_args.aggregate, ignore_missing=False) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, tuple(data)) + self.assertCountEqual(self.data, tuple(data)) class TestAggregateUnset(TestAggregate): diff --git a/openstackclient/tests/unit/compute/v2/test_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py index ee4479b009..14dd3df20a 100644 --- a/openstackclient/tests/unit/compute/v2/test_flavor.py +++ b/openstackclient/tests/unit/compute/v2/test_flavor.py @@ -133,7 +133,7 @@ def test_flavor_create_default_options(self): self.sdk_client.create_flavor.assert_called_once_with(**default_args) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_flavor_create_all_options(self): @@ -202,7 +202,7 @@ def test_flavor_create_all_options(self): self.sdk_client.get_flavor_access.assert_not_called() self.assertEqual(self.columns, columns) - self.assertItemsEqual(tuple(cmp_data), data) + self.assertCountEqual(tuple(cmp_data), data) def test_flavor_create_other_options(self): @@ -277,7 +277,7 @@ def test_flavor_create_other_options(self): self.sdk_client.create_flavor_extra_specs.assert_called_with( create_flavor, props) self.assertEqual(self.columns, columns) - self.assertItemsEqual(cmp_data, data) + self.assertCountEqual(cmp_data, data) def test_public_flavor_create_with_project(self): arglist = [ @@ -350,7 +350,7 @@ def test_flavor_create_with_description_api_newer(self): self.sdk_client.create_flavor.assert_called_once_with(**args) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data_private, data) + self.assertCountEqual(self.data_private, data) def test_flavor_create_with_description_api_older(self): arglist = [ @@ -633,7 +633,7 @@ def test_flavor_list_long(self): ) self.assertEqual(self.columns_long, columns) - self.assertItemsEqual(self.data_long, tuple(data)) + self.assertCountEqual(self.data_long, tuple(data)) def test_flavor_list_min_disk_min_ram(self): arglist = [ @@ -951,7 +951,7 @@ def test_public_flavor_show(self): columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_private_flavor_show(self): private_flavor = compute_fakes.FakeFlavor.create_one_flavor( @@ -991,7 +991,7 @@ def test_private_flavor_show(self): self.sdk_client.get_flavor_access.assert_called_with( flavor=private_flavor.id) self.assertEqual(self.columns, columns) - self.assertItemsEqual(data_with_project, data) + self.assertCountEqual(data_with_project, data) class TestFlavorUnset(TestFlavor): diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor.py b/openstackclient/tests/unit/compute/v2/test_hypervisor.py index 3220a76450..7dbd6e1983 100644 --- a/openstackclient/tests/unit/compute/v2/test_hypervisor.py +++ b/openstackclient/tests/unit/compute/v2/test_hypervisor.py @@ -394,7 +394,7 @@ def test_hypervisor_show(self): columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_hypervisor_show_pre_v228(self): self.app.client_manager.compute.api_version = \ @@ -420,7 +420,7 @@ def test_hypervisor_show_pre_v228(self): columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_hypervisor_show_uptime_not_implemented(self): self.app.client_manager.compute.api_version = \ @@ -492,4 +492,4 @@ def test_hypervisor_show_uptime_not_implemented(self): ) self.assertEqual(expected_columns, columns) - self.assertItemsEqual(expected_data, data) + self.assertCountEqual(expected_data, data) diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py index 753db9cd70..0012d70062 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_backup.py +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -139,7 +139,7 @@ def test_server_backup_defaults(self): ) self.assertEqual(self.image_columns(images[0]), columns) - self.assertItemsEqual(self.image_data(images[0]), data) + self.assertCountEqual(self.image_data(images[0]), data) def test_server_backup_create_options(self): servers = self.setup_servers_mock(count=1) @@ -173,7 +173,7 @@ def test_server_backup_create_options(self): ) self.assertEqual(self.image_columns(images[0]), columns) - self.assertItemsEqual(self.image_data(images[0]), data) + self.assertCountEqual(self.image_data(images[0]), data) @mock.patch.object(common_utils, 'wait_for_status', return_value=False) def test_server_backup_wait_fail(self, mock_wait_for_status): @@ -269,4 +269,4 @@ def test_server_backup_wait_ok(self, mock_wait_for_status): ) self.assertEqual(self.image_columns(images[0]), columns) - self.assertItemsEqual(self.image_data(images[0]), data) + self.assertCountEqual(self.image_data(images[0]), data) diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py index 66452a8bb8..9b14428a27 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_image.py +++ b/openstackclient/tests/unit/compute/v2/test_server_image.py @@ -134,7 +134,7 @@ def test_server_image_create_defaults(self): ) self.assertEqual(self.image_columns(images[0]), columns) - self.assertItemsEqual(self.image_data(images[0]), data) + self.assertCountEqual(self.image_data(images[0]), data) def test_server_image_create_options(self): servers = self.setup_servers_mock(count=1) @@ -165,7 +165,7 @@ def test_server_image_create_options(self): ) self.assertEqual(self.image_columns(images[0]), columns) - self.assertItemsEqual(self.image_data(images[0]), data) + self.assertCountEqual(self.image_data(images[0]), data) @mock.patch.object(common_utils, 'wait_for_status', return_value=False) def test_server_create_image_wait_fail(self, mock_wait_for_status): @@ -235,4 +235,4 @@ def test_server_create_image_wait_ok(self, mock_wait_for_status): ) self.assertEqual(self.image_columns(images[0]), columns) - self.assertItemsEqual(self.image_data(images[0]), data) + self.assertCountEqual(self.image_data(images[0]), data) diff --git a/openstackclient/tests/unit/identity/v2_0/test_catalog.py b/openstackclient/tests/unit/identity/v2_0/test_catalog.py index e2c56ba1ec..bfb28f6962 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_catalog.py +++ b/openstackclient/tests/unit/identity/v2_0/test_catalog.py @@ -74,7 +74,7 @@ def test_catalog_list(self): catalog.EndpointsColumn( auth_ref.service_catalog.catalog[0]['endpoints']), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_catalog_list_with_endpoint_url(self): attr = { @@ -117,7 +117,7 @@ def test_catalog_list_with_endpoint_url(self): catalog.EndpointsColumn( auth_ref.service_catalog.catalog[0]['endpoints']), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) class TestCatalogShow(TestCatalog): @@ -158,7 +158,7 @@ def test_catalog_show(self): 'supernova', 'compute', ) - self.assertItemsEqual(datalist, data) + self.assertCountEqual(datalist, data) class TestFormatColumns(TestCatalog): diff --git a/openstackclient/tests/unit/identity/v2_0/test_project.py b/openstackclient/tests/unit/identity/v2_0/test_project.py index 766d5dab55..496214aaee 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_project.py +++ b/openstackclient/tests/unit/identity/v2_0/test_project.py @@ -643,7 +643,7 @@ def test_project_show(self): self.fake_proj_show.name, format_columns.DictColumn({}), ) - self.assertItemsEqual(datalist, data) + self.assertCountEqual(datalist, data) class TestProjectUnset(TestProject): diff --git a/openstackclient/tests/unit/identity/v2_0/test_user.py b/openstackclient/tests/unit/identity/v2_0/test_user.py index dd30047814..c3f5f1d7a1 100644 --- a/openstackclient/tests/unit/identity/v2_0/test_user.py +++ b/openstackclient/tests/unit/identity/v2_0/test_user.py @@ -482,7 +482,7 @@ def test_user_list_no_options(self): self.users_mock.list.assert_called_with(tenant_id=None) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) def test_user_list_project(self): arglist = [ @@ -502,7 +502,7 @@ def test_user_list_project(self): self.users_mock.list.assert_called_with(tenant_id=project_id) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) def test_user_list_long(self): arglist = [ @@ -531,7 +531,7 @@ def test_user_list_long(self): self.fake_user_l.email, True, ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) class TestUserSet(TestUser): @@ -819,4 +819,4 @@ def test_user_show(self): self.fake_user.name, self.fake_project.id, ) - self.assertItemsEqual(datalist, data) + self.assertCountEqual(datalist, data) diff --git a/openstackclient/tests/unit/identity/v3/test_catalog.py b/openstackclient/tests/unit/identity/v3/test_catalog.py index 97ce48f6a5..802a9017e2 100644 --- a/openstackclient/tests/unit/identity/v3/test_catalog.py +++ b/openstackclient/tests/unit/identity/v3/test_catalog.py @@ -94,7 +94,7 @@ def test_catalog_list(self): catalog.EndpointsColumn( auth_ref.service_catalog.catalog[0]['endpoints']), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) class TestCatalogShow(TestCatalog): @@ -135,7 +135,7 @@ def test_catalog_show(self): 'supernova', 'compute', ) - self.assertItemsEqual(datalist, data) + self.assertCountEqual(datalist, data) class TestFormatColumns(TestCatalog): diff --git a/openstackclient/tests/unit/identity/v3/test_identity_provider.py b/openstackclient/tests/unit/identity/v3/test_identity_provider.py index 5aff2b1be2..1a9a799123 100644 --- a/openstackclient/tests/unit/identity/v3/test_identity_provider.py +++ b/openstackclient/tests/unit/identity/v3/test_identity_provider.py @@ -89,7 +89,7 @@ def test_create_identity_provider_no_options(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_create_identity_provider_description(self): arglist = [ @@ -117,7 +117,7 @@ def test_create_identity_provider_description(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_create_identity_provider_remote_id(self): arglist = [ @@ -145,7 +145,7 @@ def test_create_identity_provider_remote_id(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_create_identity_provider_remote_ids_multiple(self): arglist = [ @@ -174,7 +174,7 @@ def test_create_identity_provider_remote_ids_multiple(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_create_identity_provider_remote_ids_file(self): arglist = [ @@ -207,7 +207,7 @@ def test_create_identity_provider_remote_ids_file(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_create_identity_provider_disabled(self): @@ -250,7 +250,7 @@ def test_create_identity_provider_disabled(self): identity_fakes.idp_id, identity_fakes.formatted_idp_remote_ids ) - self.assertItemsEqual(datalist, data) + self.assertCountEqual(datalist, data) def test_create_identity_provider_domain_name(self): arglist = [ @@ -278,7 +278,7 @@ def test_create_identity_provider_domain_name(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_create_identity_provider_domain_id(self): arglist = [ @@ -306,7 +306,7 @@ def test_create_identity_provider_domain_id(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) class TestIdentityProviderDelete(TestIdentityProvider): @@ -382,7 +382,7 @@ def test_identity_provider_list_no_options(self): identity_fakes.domain_id, identity_fakes.idp_description, ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_identity_provider_list_ID_option(self): arglist = ['--id', @@ -410,7 +410,7 @@ def test_identity_provider_list_ID_option(self): identity_fakes.domain_id, identity_fakes.idp_description, ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_identity_provider_list_enabled_option(self): arglist = ['--enabled'] @@ -437,7 +437,7 @@ def test_identity_provider_list_enabled_option(self): identity_fakes.domain_id, identity_fakes.idp_description, ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) class TestIdentityProviderSet(TestIdentityProvider): @@ -722,4 +722,4 @@ def test_identity_provider_show(self): identity_fakes.idp_id, identity_fakes.formatted_idp_remote_ids ) - self.assertItemsEqual(datalist, data) + self.assertCountEqual(datalist, data) diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py index db64983c97..5c69bf0f87 100644 --- a/openstackclient/tests/unit/image/v1/test_image.py +++ b/openstackclient/tests/unit/image/v1/test_image.py @@ -100,7 +100,7 @@ def test_image_reserve_no_options(self, raw_input): self.assertEqual(self.client.update_image.call_args_list, []) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) @mock.patch('sys.stdin', side_effect=[None]) def test_image_reserve_options(self, raw_input): @@ -149,7 +149,7 @@ def test_image_reserve_options(self, raw_input): self.assertEqual(self.client.update_image.call_args_list, []) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) @mock.patch('openstackclient.image.v1.image.io.open', name='Open') def test_image_create_file(self, mock_open): @@ -205,7 +205,7 @@ def test_image_create_file(self, mock_open): self.assertEqual(self.client.update_image.call_args_list, []) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestImageDelete(TestImage): @@ -386,7 +386,7 @@ def test_image_list_long_option(self): format_columns.DictColumn( {'Alpha': 'a', 'Beta': 'b', 'Gamma': 'g'}), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) @mock.patch('osc_lib.api.utils.simple_filter') def test_image_list_property_option(self, sf_mock): @@ -737,7 +737,7 @@ def test_image_show(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_image_show_human_readable(self): arglist = [ diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index c44c767b70..35af6799f6 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -111,7 +111,7 @@ def test_image_reserve_no_options(self, raw_input): self.assertEqual( self.expected_columns, columns) - self.assertItemsEqual( + self.assertCountEqual( self.expected_data, data) @@ -166,7 +166,7 @@ def test_image_reserve_options(self, raw_input): self.assertEqual( self.expected_columns, columns) - self.assertItemsEqual( + self.assertCountEqual( self.expected_data, data) @@ -255,7 +255,7 @@ def test_image_create_file(self): self.assertEqual( self.expected_columns, columns) - self.assertItemsEqual( + self.assertCountEqual( self.expected_data, data) @@ -513,7 +513,7 @@ def test_image_list_no_options(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) def test_image_list_public_option(self): arglist = [ @@ -537,7 +537,7 @@ def test_image_list_public_option(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) def test_image_list_private_option(self): arglist = [ @@ -561,7 +561,7 @@ def test_image_list_private_option(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) def test_image_list_community_option(self): arglist = [ @@ -609,7 +609,7 @@ def test_image_list_shared_option(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) def test_image_list_shared_member_status_option(self): arglist = [ @@ -697,7 +697,7 @@ def test_image_list_long_option(self): self._image.owner_id, format_columns.ListColumn(self._image.tags), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) @mock.patch('osc_lib.api.utils.simple_filter') def test_image_list_property_option(self, sf_mock): @@ -725,7 +725,7 @@ def test_image_list_property_option(self, sf_mock): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) @mock.patch('osc_lib.utils.sort_items') def test_image_list_sort_option(self, si_mock): @@ -747,7 +747,7 @@ def test_image_list_sort_option(self, si_mock): str, ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) def test_image_list_limit_option(self): ret_limit = 1 @@ -782,7 +782,7 @@ def test_image_list_project_option(self): columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) @mock.patch('osc_lib.utils.find_resource') def test_image_list_marker_option(self, fr_mock): @@ -1555,7 +1555,7 @@ def test_image_show(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_image_show_human_readable(self): self.client.find_image.return_value = self.new_image diff --git a/openstackclient/tests/unit/network/v2/test_address_group.py b/openstackclient/tests/unit/network/v2/test_address_group.py index e4fa8ab3dc..a5ee83cb56 100644 --- a/openstackclient/tests/unit/network/v2/test_address_group.py +++ b/openstackclient/tests/unit/network/v2/test_address_group.py @@ -99,7 +99,7 @@ def test_create_default_options(self): 'addresses': [], }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_all_options(self): arglist = [ @@ -127,7 +127,7 @@ def test_create_all_options(self): 'description': self.new_address_group.description, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestDeleteAddressGroup(TestAddressGroup): @@ -252,7 +252,7 @@ def test_address_group_list(self): self.network.address_groups.assert_called_once_with(**{}) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_address_group_list_name(self): arglist = [ @@ -267,7 +267,7 @@ def test_address_group_list_name(self): self.network.address_groups.assert_called_once_with( **{'name': self.address_groups[0].name}) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_address_group_list_project(self): project = identity_fakes_v3.FakeProject.create_one_project() @@ -284,7 +284,7 @@ def test_address_group_list_project(self): self.network.address_groups.assert_called_once_with( project_id=project.id) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_address_group_project_domain(self): project = identity_fakes_v3.FakeProject.create_one_project() @@ -302,7 +302,7 @@ def test_address_group_project_domain(self): self.network.address_groups.assert_called_once_with( project_id=project.id) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) class TestSetAddressGroup(TestAddressGroup): @@ -438,7 +438,7 @@ def test_show_all_options(self): self.network.find_address_group.assert_called_once_with( self._address_group.name, ignore_missing=False) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) class TestUnsetAddressGroup(TestAddressGroup): diff --git a/openstackclient/tests/unit/network/v2/test_ip_availability.py b/openstackclient/tests/unit/network/v2/test_ip_availability.py index ade5783700..a722a02391 100644 --- a/openstackclient/tests/unit/network/v2/test_ip_availability.py +++ b/openstackclient/tests/unit/network/v2/test_ip_availability.py @@ -75,7 +75,7 @@ def test_list_no_options(self): self.network.network_ip_availabilities.assert_called_once_with( **filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_list_ip_version(self): arglist = [ @@ -93,7 +93,7 @@ def test_list_ip_version(self): self.network.network_ip_availabilities.assert_called_once_with( **filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_list_project(self): arglist = [ @@ -113,7 +113,7 @@ def test_list_project(self): self.network.network_ip_availabilities.assert_called_once_with( **filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) class TestShowIPAvailability(TestIPAvailability): @@ -176,4 +176,4 @@ def test_show_all_options(self): self._ip_availability.network_name, ignore_missing=False) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index e29b72c769..127d82b015 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -146,7 +146,7 @@ def test_create_default_options(self): }) self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_all_options(self): arglist = [ @@ -211,7 +211,7 @@ def test_create_all_options(self): 'dns_domain': 'example.org.', }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_other_options(self): arglist = [ @@ -238,7 +238,7 @@ def test_create_other_options(self): 'port_security_enabled': False, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def _test_create_with_tag(self, add_tags=True): arglist = [self._network.name] @@ -270,7 +270,7 @@ def _test_create_with_tag(self, add_tags=True): else: self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_tags(self): self._test_create_with_tag(add_tags=True) @@ -385,7 +385,7 @@ def test_create_with_project_identityv2(self): }) self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_domain_identityv2(self): arglist = [ @@ -577,7 +577,7 @@ def test_network_list_no_options(self): self.network.networks.assert_called_once_with() self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_list_external(self): arglist = [ @@ -598,7 +598,7 @@ def test_list_external(self): **{'router:external': True, 'is_router_external': True} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_list_internal(self): arglist = [ @@ -615,7 +615,7 @@ def test_list_internal(self): **{'router:external': False, 'is_router_external': False} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_list_long(self): arglist = [ @@ -634,7 +634,7 @@ def test_network_list_long(self): self.network.networks.assert_called_once_with() self.assertEqual(self.columns_long, columns) - self.assertItemsEqual(self.data_long, list(data)) + self.assertCountEqual(self.data_long, list(data)) def test_list_name(self): test_name = "fakename" @@ -653,7 +653,7 @@ def test_list_name(self): **{'name': test_name} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_list_enable(self): arglist = [ @@ -671,7 +671,7 @@ def test_network_list_enable(self): **{'admin_state_up': True, 'is_admin_state_up': True} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_list_disable(self): arglist = [ @@ -689,7 +689,7 @@ def test_network_list_disable(self): **{'admin_state_up': False, 'is_admin_state_up': False} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_list_project(self): project = identity_fakes_v3.FakeProject.create_one_project() @@ -708,7 +708,7 @@ def test_network_list_project(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_list_project_domain(self): project = identity_fakes_v3.FakeProject.create_one_project() @@ -727,7 +727,7 @@ def test_network_list_project_domain(self): self.network.networks.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_list_share(self): arglist = [ @@ -744,7 +744,7 @@ def test_network_list_share(self): **{'shared': True, 'is_shared': True} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_list_no_share(self): arglist = [ @@ -761,7 +761,7 @@ def test_network_list_no_share(self): **{'shared': False, 'is_shared': False} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_list_status(self): choices = ['ACTIVE', 'BUILD', 'DOWN', 'ERROR'] @@ -780,7 +780,7 @@ def test_network_list_status(self): **{'status': test_status} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_list_provider_network_type(self): network_type = self._network[0].provider_network_type @@ -798,7 +798,7 @@ def test_network_list_provider_network_type(self): 'provider_network_type': network_type} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_list_provider_physical_network(self): physical_network = self._network[0].provider_physical_network @@ -816,7 +816,7 @@ def test_network_list_provider_physical_network(self): 'provider_physical_network': physical_network} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_list_provider_segment(self): segmentation_id = self._network[0].provider_segmentation_id @@ -834,7 +834,7 @@ def test_network_list_provider_segment(self): 'provider_segmentation_id': segmentation_id} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_list_dhcp_agent(self): arglist = [ @@ -853,7 +853,7 @@ def test_network_list_dhcp_agent(self): *attrs) self.assertEqual(self.columns, columns) - self.assertItemsEqual(list(data), list(self.data)) + self.assertCountEqual(list(data), list(self.data)) def test_list_with_tag_options(self): arglist = [ @@ -878,7 +878,7 @@ def test_list_with_tag_options(self): 'not_any_tags': 'black,white'} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) class TestSetNetwork(TestNetwork): @@ -1111,7 +1111,7 @@ def test_show_all_options(self): self._network.name, ignore_missing=False) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestUnsetNetwork(TestNetwork): diff --git a/openstackclient/tests/unit/network/v2/test_network_agent.py b/openstackclient/tests/unit/network/v2/test_network_agent.py index fceac68ea0..734a36ee94 100644 --- a/openstackclient/tests/unit/network/v2/test_network_agent.py +++ b/openstackclient/tests/unit/network/v2/test_network_agent.py @@ -246,7 +246,7 @@ def test_network_agents_list(self): self.network.agents.assert_called_once_with(**{}) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_agents_list_agent_type(self): arglist = [ @@ -263,7 +263,7 @@ def test_network_agents_list_agent_type(self): 'agent_type': 'DHCP agent', }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_agents_list_host(self): arglist = [ @@ -280,7 +280,7 @@ def test_network_agents_list_host(self): 'host': self.network_agents[0].host, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_agents_list_networks(self): arglist = [ @@ -298,7 +298,7 @@ def test_network_agents_list_networks(self): self.network.network_hosting_dhcp_agents.assert_called_once_with( *attrs) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_agents_list_routers(self): arglist = [ @@ -318,7 +318,7 @@ def test_network_agents_list_routers(self): *attrs) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_network_agents_list_routers_with_long_option(self): arglist = [ @@ -343,7 +343,7 @@ def test_network_agents_list_routers_with_long_option(self): router_agent_data = [d + ('',) for d in self.data] self.assertEqual(router_agent_columns, columns) - self.assertItemsEqual(router_agent_data, list(data)) + self.assertCountEqual(router_agent_data, list(data)) class TestRemoveNetworkFromAgent(TestNetworkAgent): @@ -571,4 +571,4 @@ def test_show_all_options(self): self.network.get_agent.assert_called_once_with( self._network_agent.id) self.assertEqual(self.columns, columns) - self.assertItemsEqual(list(self.data), list(data)) + self.assertCountEqual(list(self.data), list(data)) diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 8c5158d7c8..5f2a12836a 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -153,7 +153,7 @@ def test_create_default_options(self): self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_full_options(self): arglist = [ @@ -211,7 +211,7 @@ def test_create_full_options(self): }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_invalid_json_binding_profile(self): arglist = [ @@ -262,7 +262,7 @@ def test_create_json_binding_profile(self): }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_security_group(self): secgroup = network_fakes.FakeSecurityGroup.create_one_security_group() @@ -291,7 +291,7 @@ def test_create_with_security_group(self): }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_port_with_dns_name(self): arglist = [ @@ -317,7 +317,7 @@ def test_create_port_with_dns_name(self): }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_security_groups(self): sg_1 = network_fakes.FakeSecurityGroup.create_one_security_group() @@ -347,7 +347,7 @@ def test_create_with_security_groups(self): }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_no_security_groups(self): arglist = [ @@ -373,7 +373,7 @@ def test_create_with_no_security_groups(self): }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_no_fixed_ips(self): arglist = [ @@ -399,7 +399,7 @@ def test_create_with_no_fixed_ips(self): }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_port_with_allowed_address_pair_ipaddr(self): pairs = [{'ip_address': '192.168.1.123'}, @@ -429,7 +429,7 @@ def test_create_port_with_allowed_address_pair_ipaddr(self): }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_port_with_allowed_address_pair(self): pairs = [{'ip_address': '192.168.1.123', @@ -465,7 +465,7 @@ def test_create_port_with_allowed_address_pair(self): }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_port_with_qos(self): qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() @@ -493,7 +493,7 @@ def test_create_port_with_qos(self): }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_port_security_enabled(self): arglist = [ @@ -602,7 +602,7 @@ def _test_create_with_tag(self, add_tags=True, add_tags_in_post=True): self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_tags(self): self._test_create_with_tag(add_tags=True, add_tags_in_post=True) @@ -645,7 +645,7 @@ def _test_create_with_uplink_status_propagation(self, enable=True): }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_uplink_status_propagation_enabled(self): self._test_create_with_uplink_status_propagation(enable=True) @@ -725,7 +725,7 @@ def _test_create_with_numa_affinity_policy(self, policy=None): self.network.create_port.assert_called_once_with(**create_args) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_numa_affinity_policy_required(self): self._test_create_with_numa_affinity_policy(policy='required') @@ -764,7 +764,7 @@ def test_create_with_device_profile(self): } self.network.create_port.assert_called_once_with(**create_args) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestDeletePort(TestPort): @@ -919,7 +919,7 @@ def test_port_list_no_options(self): self.network.ports.assert_called_once_with( fields=LIST_FIELDS_TO_RETRIEVE) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_port_list_router_opt(self): arglist = [ @@ -939,7 +939,7 @@ def test_port_list_router_opt(self): 'fields': LIST_FIELDS_TO_RETRIEVE, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) @mock.patch.object(utils, 'find_resource') def test_port_list_with_server_option(self, mock_find): @@ -960,7 +960,7 @@ def test_port_list_with_server_option(self, mock_find): fields=LIST_FIELDS_TO_RETRIEVE) mock_find.assert_called_once_with(mock.ANY, 'fake-server-name') self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_port_list_device_id_opt(self): arglist = [ @@ -980,7 +980,7 @@ def test_port_list_device_id_opt(self): 'fields': LIST_FIELDS_TO_RETRIEVE, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_port_list_device_owner_opt(self): arglist = [ @@ -1000,7 +1000,7 @@ def test_port_list_device_owner_opt(self): 'fields': LIST_FIELDS_TO_RETRIEVE, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_port_list_all_opt(self): arglist = [ @@ -1029,7 +1029,7 @@ def test_port_list_all_opt(self): 'fields': LIST_FIELDS_TO_RETRIEVE, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_port_list_mac_address_opt(self): arglist = [ @@ -1049,7 +1049,7 @@ def test_port_list_mac_address_opt(self): 'fields': LIST_FIELDS_TO_RETRIEVE, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_port_list_fixed_ip_opt_ip_address(self): ip_address = self._ports[0].fixed_ips[0]['ip_address'] @@ -1069,7 +1069,7 @@ def test_port_list_fixed_ip_opt_ip_address(self): 'fields': LIST_FIELDS_TO_RETRIEVE, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_port_list_fixed_ip_opt_ip_address_substr(self): ip_address_ss = self._ports[0].fixed_ips[0]['ip_address'][:-1] @@ -1089,7 +1089,7 @@ def test_port_list_fixed_ip_opt_ip_address_substr(self): 'fields': LIST_FIELDS_TO_RETRIEVE, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_port_list_fixed_ip_opt_subnet_id(self): subnet_id = self._ports[0].fixed_ips[0]['subnet_id'] @@ -1111,7 +1111,7 @@ def test_port_list_fixed_ip_opt_subnet_id(self): 'fields': LIST_FIELDS_TO_RETRIEVE, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_port_list_fixed_ip_opts(self): subnet_id = self._ports[0].fixed_ips[0]['subnet_id'] @@ -1137,7 +1137,7 @@ def test_port_list_fixed_ip_opts(self): 'fields': LIST_FIELDS_TO_RETRIEVE, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_port_list_fixed_ips(self): subnet_id = self._ports[0].fixed_ips[0]['subnet_id'] @@ -1165,7 +1165,7 @@ def test_port_list_fixed_ips(self): 'fields': LIST_FIELDS_TO_RETRIEVE, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_list_port_with_long(self): arglist = [ @@ -1183,7 +1183,7 @@ def test_list_port_with_long(self): self.network.ports.assert_called_once_with( fields=LIST_FIELDS_TO_RETRIEVE + LIST_FIELDS_TO_RETRIEVE_LONG) self.assertEqual(self.columns_long, columns) - self.assertItemsEqual(self.data_long, list(data)) + self.assertCountEqual(self.data_long, list(data)) def test_port_list_host(self): arglist = [ @@ -1202,7 +1202,7 @@ def test_port_list_host(self): self.network.ports.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_port_list_project(self): project = identity_fakes.FakeProject.create_one_project() @@ -1224,7 +1224,7 @@ def test_port_list_project(self): self.network.ports.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_port_list_project_domain(self): project = identity_fakes.FakeProject.create_one_project() @@ -1248,7 +1248,7 @@ def test_port_list_project_domain(self): self.network.ports.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_port_list_name(self): test_name = "fakename" @@ -1268,7 +1268,7 @@ def test_port_list_name(self): self.network.ports.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_list_with_tag_options(self): arglist = [ @@ -1294,7 +1294,7 @@ def test_list_with_tag_options(self): 'fields': LIST_FIELDS_TO_RETRIEVE} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) class TestSetPort(TestPort): @@ -1894,7 +1894,7 @@ def test_show_all_options(self): self._port.name, ignore_missing=False) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestUnsetPort(TestPort): diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 323c919828..0324674817 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -184,7 +184,7 @@ def test_create_default_options(self): }) self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def _test_create_with_ha_options(self, option, ha): arglist = [ @@ -208,7 +208,7 @@ def _test_create_with_ha_options(self, option, ha): 'ha': ha, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_ha_option(self): self._test_create_with_ha_options('--ha', True) @@ -237,7 +237,7 @@ def _test_create_with_distributed_options(self, option, distributed): 'distributed': distributed, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_distributed_option(self): self._test_create_with_distributed_options('--distributed', True) @@ -268,7 +268,7 @@ def test_create_with_AZ_hints(self): }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def _test_create_with_tag(self, add_tags=True): arglist = [self.new_router.name] @@ -301,7 +301,7 @@ def _test_create_with_tag(self, add_tags=True): else: self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_tags(self): self._test_create_with_tag(add_tags=True) @@ -494,7 +494,7 @@ def test_router_list_no_options(self): self.network.routers.assert_called_once_with() self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_router_list_no_ha_no_distributed(self): _routers = network_fakes.FakeRouter.create_routers({ @@ -531,7 +531,7 @@ def test_router_list_long(self): self.network.routers.assert_called_once_with() self.assertEqual(self.columns_long, columns) - self.assertItemsEqual(self.data_long, list(data)) + self.assertCountEqual(self.data_long, list(data)) def test_router_list_long_no_az(self): arglist = [ @@ -552,7 +552,7 @@ def test_router_list_long_no_az(self): self.network.routers.assert_called_once_with() self.assertEqual(self.columns_long_no_az, columns) - self.assertItemsEqual(self.data_long_no_az, list(data)) + self.assertCountEqual(self.data_long_no_az, list(data)) def test_list_name(self): test_name = "fakename" @@ -570,7 +570,7 @@ def test_list_name(self): **{'name': test_name} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_router_list_enable(self): arglist = [ @@ -587,7 +587,7 @@ def test_router_list_enable(self): **{'admin_state_up': True, 'is_admin_state_up': True} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_router_list_disable(self): arglist = [ @@ -605,7 +605,7 @@ def test_router_list_disable(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_router_list_project(self): project = identity_fakes_v3.FakeProject.create_one_project() @@ -623,7 +623,7 @@ def test_router_list_project(self): self.network.routers.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_router_list_project_domain(self): project = identity_fakes_v3.FakeProject.create_one_project() @@ -643,7 +643,7 @@ def test_router_list_project_domain(self): self.network.routers.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_router_list_agents_no_args(self): arglist = [ @@ -671,7 +671,7 @@ def test_router_list_agents(self): self.network.agent_hosted_routers( *attrs) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_list_with_tag_options(self): arglist = [ @@ -696,7 +696,7 @@ def test_list_with_tag_options(self): 'not_any_tags': 'black,white'} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) class TestRemovePortFromRouter(TestRouter): @@ -1403,7 +1403,7 @@ def test_show_all_options(self): 'device_id': self._router.id }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_show_no_ha_no_distributed(self): _router = network_fakes.FakeRouter.create_one_router({ 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 837c9b21af..4f1ddce590 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_compute.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_compute.py @@ -88,7 +88,7 @@ def test_security_group_create_min_options(self, sg_mock): self._security_group['name'], ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_security_group_create_all_options(self, sg_mock): sg_mock.return_value = self._security_group @@ -109,7 +109,7 @@ def test_security_group_create_all_options(self, sg_mock): self._security_group['description'], ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) @mock.patch( @@ -255,7 +255,7 @@ def test_security_group_list_no_options(self, sg_mock): kwargs = {'search_opts': {'all_tenants': False}} sg_mock.assert_called_once_with(**kwargs) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_security_group_list_all_projects(self, sg_mock): sg_mock.return_value = self._security_groups @@ -272,7 +272,7 @@ def test_security_group_list_all_projects(self, sg_mock): kwargs = {'search_opts': {'all_tenants': True}} sg_mock.assert_called_once_with(**kwargs) self.assertEqual(self.columns_all_projects, columns) - self.assertItemsEqual(self.data_all_projects, list(data)) + self.assertCountEqual(self.data_all_projects, list(data)) @mock.patch( @@ -401,4 +401,4 @@ def test_security_group_show_all_options(self, sg_mock): sg_mock.assert_called_once_with(self._security_group['id']) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) 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 fe37778598..569c0cd5f0 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_network.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_network.py @@ -96,7 +96,7 @@ def test_create_min_options(self): 'name': self._security_group.name, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_all_options(self): arglist = [ @@ -124,7 +124,7 @@ def test_create_all_options(self): 'tenant_id': self.project.id, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def _test_create_with_tag(self, add_tags=True): arglist = [self._security_group.name] @@ -155,7 +155,7 @@ def _test_create_with_tag(self, add_tags=True): else: self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_tags(self): self._test_create_with_tag(add_tags=True) @@ -293,7 +293,7 @@ def test_security_group_list_no_options(self): self.network.security_groups.assert_called_once_with( fields=security_group.ListSecurityGroup.FIELDS_TO_RETRIEVE) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_security_group_list_all_projects(self): arglist = [ @@ -309,7 +309,7 @@ def test_security_group_list_all_projects(self): self.network.security_groups.assert_called_once_with( fields=security_group.ListSecurityGroup.FIELDS_TO_RETRIEVE) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_security_group_list_project(self): project = identity_fakes.FakeProject.create_one_project() @@ -329,7 +329,7 @@ def test_security_group_list_project(self): self.network.security_groups.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_security_group_list_project_domain(self): project = identity_fakes.FakeProject.create_one_project() @@ -351,7 +351,7 @@ def test_security_group_list_project_domain(self): self.network.security_groups.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_list_with_tag_options(self): arglist = [ @@ -539,7 +539,7 @@ def test_show_all_options(self): self.network.find_security_group.assert_called_once_with( self._security_group.id, ignore_missing=False) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestUnsetSecurityGroupNetwork(TestSecurityGroupNetwork): diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py index 6085cda8a1..5147b64d2a 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet.py +++ b/openstackclient/tests/unit/network/v2/test_subnet.py @@ -255,7 +255,7 @@ def test_create_default_options(self): }) self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_from_subnet_pool_options(self): # Mock SDK calls for this test. @@ -317,7 +317,7 @@ def test_create_from_subnet_pool_options(self): 'service_types': self._subnet_from_pool.service_types, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data_subnet_pool, data) + self.assertCountEqual(self.data_subnet_pool, data) def test_create_options_subnet_range_ipv6(self): # Mock SDK calls for this test. @@ -390,7 +390,7 @@ def test_create_options_subnet_range_ipv6(self): }) self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data_ipv6, data) + self.assertCountEqual(self.data_ipv6, data) def test_create_with_network_segment(self): # Mock SDK calls for this test. @@ -424,7 +424,7 @@ def test_create_with_network_segment(self): }) self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_description(self): # Mock SDK calls for this test. @@ -458,7 +458,7 @@ def test_create_with_description(self): }) self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def _test_create_with_dns(self, publish_dns=True): arglist = [ @@ -490,7 +490,7 @@ def _test_create_with_dns(self, publish_dns=True): dns_publish_fixed_ip=publish_dns, ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_dns(self): self._test_create_with_dns(publish_dns=True) @@ -535,7 +535,7 @@ def _test_create_with_tag(self, add_tags=True): else: self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_tags(self): self._test_create_with_tag(add_tags=True) @@ -691,7 +691,7 @@ def test_subnet_list_no_options(self): self.network.subnets.assert_called_once_with() self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_list_long(self): arglist = [ @@ -706,7 +706,7 @@ def test_subnet_list_long(self): self.network.subnets.assert_called_once_with() self.assertEqual(self.columns_long, columns) - self.assertItemsEqual(self.data_long, list(data)) + self.assertCountEqual(self.data_long, list(data)) def test_subnet_list_ip_version(self): arglist = [ @@ -722,7 +722,7 @@ def test_subnet_list_ip_version(self): self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_list_dhcp(self): arglist = [ @@ -738,7 +738,7 @@ def test_subnet_list_dhcp(self): self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_list_no_dhcp(self): arglist = [ @@ -754,7 +754,7 @@ def test_subnet_list_no_dhcp(self): self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_list_service_type(self): arglist = [ @@ -769,7 +769,7 @@ def test_subnet_list_service_type(self): self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_list_project(self): project = identity_fakes_v3.FakeProject.create_one_project() @@ -787,7 +787,7 @@ def test_subnet_list_project(self): self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_list_service_type_multiple(self): arglist = [ @@ -805,7 +805,7 @@ def test_subnet_list_service_type_multiple(self): 'network:floatingip_agent_gateway']} self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_list_project_domain(self): project = identity_fakes_v3.FakeProject.create_one_project() @@ -825,7 +825,7 @@ def test_subnet_list_project_domain(self): self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_list_network(self): network = network_fakes.FakeNetwork.create_one_network() @@ -843,7 +843,7 @@ def test_subnet_list_network(self): self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_list_gateway(self): subnet = network_fakes.FakeSubnet.create_one_subnet() @@ -861,7 +861,7 @@ def test_subnet_list_gateway(self): self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_list_name(self): subnet = network_fakes.FakeSubnet.create_one_subnet() @@ -879,7 +879,7 @@ def test_subnet_list_name(self): self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_list_subnet_range(self): subnet = network_fakes.FakeSubnet.create_one_subnet() @@ -897,7 +897,7 @@ def test_subnet_list_subnet_range(self): self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_list_with_tag_options(self): arglist = [ @@ -1244,7 +1244,7 @@ def test_show_all_options(self): self._subnet.name, ignore_missing=False) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestUnsetSubnet(TestSubnet): diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py index 243fc76df4..4d18dc99b4 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py @@ -133,7 +133,7 @@ def test_create_default_options(self): }) self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_prefixlen_options(self): arglist = [ @@ -163,7 +163,7 @@ def test_create_prefixlen_options(self): 'name': self._subnet_pool.name, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_len_negative(self): arglist = [ @@ -201,7 +201,7 @@ def test_create_project_domain(self): 'name': self._subnet_pool.name, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_address_scope_option(self): arglist = [ @@ -224,7 +224,7 @@ def test_create_address_scope_option(self): 'name': self._subnet_pool.name, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_default_and_shared_options(self): arglist = [ @@ -250,7 +250,7 @@ def test_create_default_and_shared_options(self): 'shared': True, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_description(self): arglist = [ @@ -273,7 +273,7 @@ def test_create_with_description(self): 'description': self._subnet_pool.description, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_default_quota(self): arglist = [ @@ -294,7 +294,7 @@ def test_create_with_default_quota(self): 'default_quota': 10, }) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def _test_create_with_tag(self, add_tags=True): arglist = [ @@ -328,7 +328,7 @@ def _test_create_with_tag(self, add_tags=True): else: self.assertFalse(self.network.set_tags.called) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_create_with_tags(self): self._test_create_with_tag(add_tags=True) @@ -476,7 +476,7 @@ def test_subnet_pool_list_no_option(self): self.network.subnet_pools.assert_called_once_with() self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_pool_list_long(self): arglist = [ @@ -491,7 +491,7 @@ def test_subnet_pool_list_long(self): self.network.subnet_pools.assert_called_once_with() self.assertEqual(self.columns_long, columns) - self.assertItemsEqual(self.data_long, list(data)) + self.assertCountEqual(self.data_long, list(data)) def test_subnet_pool_list_no_share(self): arglist = [ @@ -507,7 +507,7 @@ def test_subnet_pool_list_no_share(self): self.network.subnet_pools.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_pool_list_share(self): arglist = [ @@ -523,7 +523,7 @@ def test_subnet_pool_list_share(self): self.network.subnet_pools.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_pool_list_no_default(self): arglist = [ @@ -539,7 +539,7 @@ def test_subnet_pool_list_no_default(self): self.network.subnet_pools.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_pool_list_default(self): arglist = [ @@ -555,7 +555,7 @@ def test_subnet_pool_list_default(self): self.network.subnet_pools.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_pool_list_project(self): project = identity_fakes_v3.FakeProject.create_one_project() @@ -573,7 +573,7 @@ def test_subnet_pool_list_project(self): self.network.subnet_pools.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_pool_list_project_domain(self): project = identity_fakes_v3.FakeProject.create_one_project() @@ -593,7 +593,7 @@ def test_subnet_pool_list_project_domain(self): self.network.subnet_pools.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_pool_list_name(self): subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool() @@ -611,7 +611,7 @@ def test_subnet_pool_list_name(self): self.network.subnet_pools.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_subnet_pool_list_address_scope(self): addr_scope = network_fakes.FakeAddressScope.create_one_address_scope() @@ -629,7 +629,7 @@ def test_subnet_pool_list_address_scope(self): self.network.subnet_pools.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_list_with_tag_options(self): arglist = [ @@ -654,7 +654,7 @@ def test_list_with_tag_options(self): 'not_any_tags': 'black,white'} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) class TestSetSubnetPool(TestSubnetPool): @@ -1008,7 +1008,7 @@ def test_show_all_options(self): ignore_missing=False ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestUnsetSubnetPool(TestSubnetPool): diff --git a/openstackclient/tests/unit/volume/v1/test_qos_specs.py b/openstackclient/tests/unit/volume/v1/test_qos_specs.py index 5500438bd6..15c20561e2 100644 --- a/openstackclient/tests/unit/volume/v1/test_qos_specs.py +++ b/openstackclient/tests/unit/volume/v1/test_qos_specs.py @@ -109,7 +109,7 @@ def test_qos_create_without_properties(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_qos_create_with_consumer(self): arglist = [ @@ -129,7 +129,7 @@ def test_qos_create_with_consumer(self): {'consumer': self.new_qos_spec.consumer} ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_qos_create_with_properties(self): arglist = [ @@ -155,7 +155,7 @@ def test_qos_create_with_properties(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) class TestQosDelete(TestQos): @@ -350,7 +350,7 @@ def test_qos_list(self): self.qos_mock.list.assert_called_with() self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_qos_list_no_association(self): self.qos_mock.reset_mock() @@ -377,7 +377,7 @@ def test_qos_list_no_association(self): format_columns.ListColumn(None), format_columns.DictColumn(self.qos_specs[1].specs), ) - self.assertItemsEqual(ex_data, list(data)) + self.assertCountEqual(ex_data, list(data)) class TestQosSet(TestQos): @@ -454,7 +454,7 @@ def test_qos_show(self): self.qos_spec.name, format_columns.DictColumn(self.qos_spec.specs), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) class TestQosUnset(TestQos): diff --git a/openstackclient/tests/unit/volume/v1/test_type.py b/openstackclient/tests/unit/volume/v1/test_type.py index f1d469140b..be47f5db59 100644 --- a/openstackclient/tests/unit/volume/v1/test_type.py +++ b/openstackclient/tests/unit/volume/v1/test_type.py @@ -78,7 +78,7 @@ def test_type_create(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_type_create_with_encryption(self): encryption_info = { @@ -139,7 +139,7 @@ def test_type_create_with_encryption(self): body, ) self.assertEqual(encryption_columns, columns) - self.assertItemsEqual(encryption_data, data) + self.assertCountEqual(encryption_data, data) class TestTypeDelete(TestType): @@ -270,7 +270,7 @@ def test_type_list_without_options(self): columns, data = self.cmd.take_action(parsed_args) self.types_mock.list.assert_called_once_with() self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_type_list_with_options(self): arglist = [ @@ -284,7 +284,7 @@ def test_type_list_with_options(self): columns, data = self.cmd.take_action(parsed_args) self.types_mock.list.assert_called_once_with() self.assertEqual(self.columns_long, columns) - self.assertItemsEqual(self.data_long, list(data)) + self.assertCountEqual(self.data_long, list(data)) def test_type_list_with_encryption(self): encryption_type = volume_fakes.FakeType.create_one_encryption_type( @@ -328,7 +328,7 @@ def test_type_list_with_encryption(self): self.encryption_types_mock.list.assert_called_once_with() self.types_mock.list.assert_called_once_with() self.assertEqual(encryption_columns, columns) - self.assertItemsEqual(encryption_data, list(data)) + self.assertCountEqual(encryption_data, list(data)) class TestTypeSet(TestType): @@ -469,7 +469,7 @@ def test_type_show(self): self.types_mock.get.assert_called_with(self.volume_type.id) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_type_show_with_encryption(self): encryption_type = volume_fakes.FakeType.create_one_encryption_type() @@ -513,7 +513,7 @@ def test_type_show_with_encryption(self): 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.assertItemsEqual(encryption_data, data) + self.assertCountEqual(encryption_data, data) class TestTypeUnset(TestType): diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py index 704a66da71..de0c99c291 100644 --- a/openstackclient/tests/unit/volume/v1/test_volume.py +++ b/openstackclient/tests/unit/volume/v1/test_volume.py @@ -135,7 +135,7 @@ def test_volume_create_min_options(self): None, ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_options(self): arglist = [ @@ -179,7 +179,7 @@ def test_volume_create_options(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_user_project_id(self): # Return a project @@ -226,7 +226,7 @@ def test_volume_create_user_project_id(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_user_project_name(self): # Return a project @@ -273,7 +273,7 @@ def test_volume_create_user_project_name(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_properties(self): arglist = [ @@ -314,7 +314,7 @@ def test_volume_create_properties(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_image_id(self): image = image_fakes.FakeImage.create_one_image() @@ -357,7 +357,7 @@ def test_volume_create_image_id(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_image_name(self): image = image_fakes.FakeImage.create_one_image() @@ -400,7 +400,7 @@ def test_volume_create_image_name(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_with_source(self): self.volumes_mock.get.return_value = self.new_volume @@ -430,7 +430,7 @@ def test_volume_create_with_source(self): None, ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_with_bootable_and_readonly(self): arglist = [ @@ -468,7 +468,7 @@ def test_volume_create_with_bootable_and_readonly(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + 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( @@ -510,7 +510,7 @@ def test_volume_create_with_nonbootable_and_readwrite(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + 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( @@ -562,7 +562,7 @@ def test_volume_create_with_bootable_and_readonly_fail( self.assertEqual(2, mock_error.call_count) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + 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( @@ -765,7 +765,7 @@ def test_volume_list_no_options(self): columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) def test_volume_list_name(self): arglist = [ @@ -782,7 +782,7 @@ def test_volume_list_name(self): columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.columns, tuple(columns)) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) def test_volume_list_status(self): arglist = [ @@ -799,7 +799,7 @@ def test_volume_list_status(self): columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.columns, tuple(columns)) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) def test_volume_list_all_projects(self): arglist = [ @@ -816,7 +816,7 @@ def test_volume_list_all_projects(self): columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.columns, tuple(columns)) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) def test_volume_list_long(self): arglist = [ @@ -856,7 +856,7 @@ def test_volume_list_long(self): volume.AttachmentsColumn(self._volume.attachments), format_columns.DictColumn(self._volume.metadata), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_volume_list_with_limit(self): arglist = [ @@ -881,7 +881,7 @@ def test_volume_list_with_limit(self): 'all_tenants': False, } ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, tuple(data)) + self.assertCountEqual(self.datalist, tuple(data)) def test_volume_list_negative_limit(self): arglist = [ @@ -1272,7 +1272,7 @@ def test_volume_show(self): self.volumes_mock.get.assert_called_with(self._volume.id) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_show_backward_compatibility(self): arglist = [ diff --git a/openstackclient/tests/unit/volume/v1/test_volume_backup.py b/openstackclient/tests/unit/volume/v1/test_volume_backup.py index a713155092..f25a5ffa6d 100644 --- a/openstackclient/tests/unit/volume/v1/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v1/test_volume_backup.py @@ -100,7 +100,7 @@ def test_backup_create(self): self.new_backup.description, ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_backup_create_without_name(self): arglist = [ @@ -124,7 +124,7 @@ def test_backup_create_without_name(self): self.new_backup.description, ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestBackupDelete(TestBackup): @@ -277,7 +277,7 @@ def test_backup_list_without_options(self): search_opts=search_opts, ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_backup_list_with_options(self): arglist = [ @@ -309,7 +309,7 @@ def test_backup_list_with_options(self): search_opts=search_opts, ) self.assertEqual(self.columns_long, columns) - self.assertItemsEqual(self.data_long, list(data)) + self.assertCountEqual(self.data_long, list(data)) class TestBackupRestore(TestBackup): @@ -391,4 +391,4 @@ def test_backup_show(self): self.backups_mock.get.assert_called_with(self.backup.id) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v2/test_consistency_group.py b/openstackclient/tests/unit/volume/v2/test_consistency_group.py index 6bb6c02978..7fd5187170 100644 --- a/openstackclient/tests/unit/volume/v2/test_consistency_group.py +++ b/openstackclient/tests/unit/volume/v2/test_consistency_group.py @@ -251,7 +251,7 @@ def test_consistency_group_create_without_name(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_consistency_group_create_from_source(self): arglist = [ @@ -279,7 +279,7 @@ def test_consistency_group_create_from_source(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_consistency_group_create_from_snapshot(self): arglist = [ @@ -307,7 +307,7 @@ def test_consistency_group_create_from_snapshot(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestConsistencyGroupDelete(TestConsistencyGroup): @@ -463,7 +463,7 @@ def test_consistency_group_list_without_options(self): self.consistencygroups_mock.list.assert_called_once_with( detailed=True, search_opts={'all_tenants': False}) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_consistency_group_list_with_all_project(self): arglist = [ @@ -480,7 +480,7 @@ def test_consistency_group_list_with_all_project(self): self.consistencygroups_mock.list.assert_called_once_with( detailed=True, search_opts={'all_tenants': True}) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_consistency_group_list_with_long(self): arglist = [ @@ -497,7 +497,7 @@ def test_consistency_group_list_with_long(self): self.consistencygroups_mock.list.assert_called_once_with( detailed=True, search_opts={'all_tenants': False}) self.assertEqual(self.columns_long, columns) - self.assertItemsEqual(self.data_long, list(data)) + self.assertCountEqual(self.data_long, list(data)) class TestConsistencyGroupRemoveVolume(TestConsistencyGroup): @@ -705,4 +705,4 @@ def test_consistency_group_show(self): self.consistencygroups_mock.get.assert_called_once_with( self.consistency_group.id) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v2/test_qos_specs.py b/openstackclient/tests/unit/volume/v2/test_qos_specs.py index bc4cee8b40..8f8d26c864 100644 --- a/openstackclient/tests/unit/volume/v2/test_qos_specs.py +++ b/openstackclient/tests/unit/volume/v2/test_qos_specs.py @@ -112,7 +112,7 @@ def test_qos_create_without_properties(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_qos_create_with_consumer(self): arglist = [ @@ -133,7 +133,7 @@ def test_qos_create_with_consumer(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_qos_create_with_properties(self): arglist = [ @@ -159,7 +159,7 @@ def test_qos_create_with_properties(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestQosDelete(TestQos): @@ -342,7 +342,7 @@ def test_qos_list(self): self.qos_mock.list.assert_called_with() self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_qos_list_no_association(self): self.qos_mock.reset_mock() @@ -369,7 +369,7 @@ def test_qos_list_no_association(self): format_columns.ListColumn(None), format_columns.DictColumn(self.qos_specs[1].specs), ) - self.assertItemsEqual(ex_data, list(data)) + self.assertCountEqual(ex_data, list(data)) class TestQosSet(TestQos): @@ -449,7 +449,7 @@ def test_qos_show(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, tuple(data)) + self.assertCountEqual(self.data, tuple(data)) class TestQosUnset(TestQos): diff --git a/openstackclient/tests/unit/volume/v2/test_type.py b/openstackclient/tests/unit/volume/v2/test_type.py index 000464c5d8..f718f4c4b8 100644 --- a/openstackclient/tests/unit/volume/v2/test_type.py +++ b/openstackclient/tests/unit/volume/v2/test_type.py @@ -93,7 +93,7 @@ def test_type_create_public(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_type_create_private(self): arglist = [ @@ -119,7 +119,7 @@ def test_type_create_private(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_public_type_create_with_project(self): arglist = [ @@ -196,7 +196,7 @@ def test_type_create_with_encryption(self): body, ) self.assertEqual(encryption_columns, columns) - self.assertItemsEqual(encryption_data, data) + self.assertCountEqual(encryption_data, data) class TestTypeDelete(TestType): @@ -330,7 +330,7 @@ def test_type_list_without_options(self): columns, data = self.cmd.take_action(parsed_args) self.types_mock.list.assert_called_once_with(is_public=None) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_type_list_with_options(self): arglist = [ @@ -348,7 +348,7 @@ def test_type_list_with_options(self): columns, data = self.cmd.take_action(parsed_args) self.types_mock.list.assert_called_once_with(is_public=True) self.assertEqual(self.columns_long, columns) - self.assertItemsEqual(self.data_long, list(data)) + self.assertCountEqual(self.data_long, list(data)) def test_type_list_with_private_option(self): arglist = [ @@ -365,7 +365,7 @@ def test_type_list_with_private_option(self): columns, data = self.cmd.take_action(parsed_args) self.types_mock.list.assert_called_once_with(is_public=False) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_type_list_with_default_option(self): arglist = [ @@ -383,7 +383,7 @@ def test_type_list_with_default_option(self): columns, data = self.cmd.take_action(parsed_args) self.types_mock.default.assert_called_once_with() self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data_with_default_type, list(data)) + self.assertCountEqual(self.data_with_default_type, list(data)) def test_type_list_with_encryption(self): encryption_type = volume_fakes.FakeType.create_one_encryption_type( @@ -427,7 +427,7 @@ def test_type_list_with_encryption(self): self.encryption_types_mock.list.assert_called_once_with() self.types_mock.list.assert_called_once_with(is_public=None) self.assertEqual(encryption_columns, columns) - self.assertItemsEqual(encryption_data, list(data)) + self.assertCountEqual(encryption_data, list(data)) class TestTypeSet(TestType): @@ -713,7 +713,7 @@ def test_type_show(self): self.types_mock.get.assert_called_with(self.volume_type.id) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) def test_type_show_with_access(self): arglist = [ @@ -746,7 +746,7 @@ def test_type_show_with_access(self): private_type.name, format_columns.DictColumn(private_type.extra_specs) ) - self.assertItemsEqual(private_type_data, data) + self.assertCountEqual(private_type_data, data) def test_type_show_with_list_access_exec(self): arglist = [ @@ -778,7 +778,7 @@ def test_type_show_with_list_access_exec(self): private_type.name, format_columns.DictColumn(private_type.extra_specs) ) - self.assertItemsEqual(private_type_data, data) + self.assertCountEqual(private_type_data, data) def test_type_show_with_encryption(self): encryption_type = volume_fakes.FakeType.create_one_encryption_type() @@ -824,7 +824,7 @@ def test_type_show_with_encryption(self): 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.assertItemsEqual(encryption_data, data) + self.assertCountEqual(encryption_data, data) class TestTypeUnset(TestType): diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index b9fe4e834c..31ba6036c4 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -136,7 +136,7 @@ def test_volume_create_min_options(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_options(self): consistency_group = ( @@ -182,7 +182,7 @@ def test_volume_create_options(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_properties(self): arglist = [ @@ -218,7 +218,7 @@ def test_volume_create_properties(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_image_id(self): image = image_fakes.FakeImage.create_one_image() @@ -256,7 +256,7 @@ def test_volume_create_image_id(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_image_name(self): image = image_fakes.FakeImage.create_one_image() @@ -294,7 +294,7 @@ def test_volume_create_image_name(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_with_snapshot(self): snapshot = volume_fakes.FakeSnapshot.create_one_snapshot() @@ -331,7 +331,7 @@ def test_volume_create_with_snapshot(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + self.assertCountEqual(self.datalist, data) def test_volume_create_with_bootable_and_readonly(self): arglist = [ @@ -369,7 +369,7 @@ def test_volume_create_with_bootable_and_readonly(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + 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( @@ -411,7 +411,7 @@ def test_volume_create_with_nonbootable_and_readwrite(self): ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + 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( @@ -463,7 +463,7 @@ def test_volume_create_with_bootable_and_readonly_fail( self.assertEqual(2, mock_error.call_count) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.datalist, data) + 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( @@ -680,7 +680,7 @@ def test_volume_list_no_options(self): self.mock_volume.size, volume.AttachmentsColumn(self.mock_volume.attachments), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_volume_list_project(self): arglist = [ @@ -720,7 +720,7 @@ def test_volume_list_project(self): self.mock_volume.size, volume.AttachmentsColumn(self.mock_volume.attachments), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_volume_list_project_domain(self): arglist = [ @@ -762,7 +762,7 @@ def test_volume_list_project_domain(self): self.mock_volume.size, volume.AttachmentsColumn(self.mock_volume.attachments), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_volume_list_user(self): arglist = [ @@ -801,7 +801,7 @@ def test_volume_list_user(self): self.mock_volume.size, volume.AttachmentsColumn(self.mock_volume.attachments), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_volume_list_user_domain(self): arglist = [ @@ -843,7 +843,7 @@ def test_volume_list_user_domain(self): self.mock_volume.size, volume.AttachmentsColumn(self.mock_volume.attachments), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_volume_list_name(self): arglist = [ @@ -883,7 +883,7 @@ def test_volume_list_name(self): self.mock_volume.size, volume.AttachmentsColumn(self.mock_volume.attachments), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_volume_list_status(self): arglist = [ @@ -923,7 +923,7 @@ def test_volume_list_status(self): self.mock_volume.size, volume.AttachmentsColumn(self.mock_volume.attachments), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_volume_list_all_projects(self): arglist = [ @@ -963,7 +963,7 @@ def test_volume_list_all_projects(self): self.mock_volume.size, volume.AttachmentsColumn(self.mock_volume.attachments), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_volume_list_long(self): arglist = [ @@ -1017,7 +1017,7 @@ def test_volume_list_long(self): volume.AttachmentsColumn(self.mock_volume.attachments), format_columns.DictColumn(self.mock_volume.metadata), ), ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_volume_list_with_marker_and_limit(self): arglist = [ @@ -1056,7 +1056,7 @@ def test_volume_list_with_marker_and_limit(self): 'name': None, 'all_tenants': False, } ) - self.assertItemsEqual(datalist, tuple(data)) + self.assertCountEqual(datalist, tuple(data)) def test_volume_list_negative_limit(self): arglist = [ @@ -1450,7 +1450,7 @@ def test_volume_show(self): volume_fakes.FakeVolume.get_volume_columns(self._volume), columns) - self.assertItemsEqual( + self.assertCountEqual( volume_fakes.FakeVolume.get_volume_data(self._volume), data) diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backup.py b/openstackclient/tests/unit/volume/v2/test_volume_backup.py index 13513ed8ad..97f64ce7ad 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backup.py @@ -314,7 +314,7 @@ def test_backup_list_without_options(self): limit=None, ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_backup_list_with_options(self): arglist = [ @@ -353,7 +353,7 @@ def test_backup_list_with_options(self): limit=3, ) self.assertEqual(self.columns_long, columns) - self.assertItemsEqual(self.data_long, list(data)) + self.assertCountEqual(self.data_long, list(data)) class TestBackupRestore(TestBackup): diff --git a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py index 3830f458d6..33a5a98a35 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_snapshot.py @@ -707,7 +707,7 @@ def test_snapshot_show(self): self.snapshots_mock.get.assert_called_with(self.snapshot.id) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, data) + self.assertCountEqual(self.data, data) class TestVolumeSnapshotUnset(TestVolumeSnapshot): From 3918622968073738e8fa17eec8bf5512ed609af9 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Wed, 5 May 2021 01:26:51 +0200 Subject: [PATCH 004/706] openstack image create: honor protection/visibility flags The --protected, --unprotected, --public, --shared, --community, --private flags were ignored when using --volume. Change-Id: Id5c05ef7d7bb0a04b9d7a9d821e544e1ff7b3d28 Story: 2008882 --- openstackclient/image/v2/image.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 644fbbb4f7..c1f46d2d99 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -490,6 +490,8 @@ def take_action(self, parsed_args): parsed_args.name, parsed_args.container_format, parsed_args.disk_format, + visibility=kwargs.get('visibility', 'private'), + protected=True if parsed_args.protected else False ) info = body['os-volume_upload_image'] try: From 1f0fcbcd1d480be8c097a5a663dd97b0bdcac2bc Mon Sep 17 00:00:00 2001 From: Jens Harbott Date: Tue, 11 May 2021 11:22:59 +0000 Subject: [PATCH 005/706] Fix the functional-tips tox environment The egg for the keystoneauth project is actually called keystonauth1. Seems newer pip actually complains about the difference and fails. Change-Id: I1602832d33cd467745a03b36c9b1545cd069ba1d --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index ebcdc2d5d9..5f9bf73b20 100644 --- a/tox.ini +++ b/tox.ini @@ -84,7 +84,7 @@ setenv = OS_TEST_PATH=./openstackclient/tests/functional 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=keystoneauth" + 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 freeze From 1169a114e7388e4ee622dca98b9fb8e2a06c8ec3 Mon Sep 17 00:00:00 2001 From: "wu.shiming" Date: Thu, 3 Jun 2021 14:49:23 +0800 Subject: [PATCH 006/706] Changed minversion in tox to 3.18.0 The patch bumps min version of tox to 3.18.0 in order to replace tox's whitelist_externals by allowlist_externals option: https://github.com/tox-dev/tox/blob/master/docs/changelog.rst#v3180-2020-07-23 Change-Id: Ibb77fa2afad3f09e95f0dba243d3a096daedd787 --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index ebcdc2d5d9..5e0be38422 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -minversion = 3.2.0 +minversion = 3.18.0 envlist = py38,pep8 skipdist = True # Automatic envs (pyXX) will only use the python version appropriate to that @@ -18,7 +18,7 @@ deps = -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt commands = stestr run {posargs} -whitelist_externals = stestr +allowlist_externals = stestr [testenv:fast8] # Use same environment directory as pep8 env to save space and install time @@ -71,7 +71,7 @@ commands = pythom -m pip install -q -e "git+file://{toxinidir}/../openstacksdk#egg=openstacksdk" python -m pip freeze stestr run {posargs} -whitelist_externals = stestr +allowlist_externals = stestr [testenv:functional] setenv = OS_TEST_PATH=./openstackclient/tests/functional From eca1fcd65f22ead444ec39fe20f48c83f8d7c1cf Mon Sep 17 00:00:00 2001 From: David Caro Date: Wed, 2 Jun 2021 16:33:53 +0200 Subject: [PATCH 007/706] Include hosts in aggregate list --long This makes it easier to get the total list of aggregates and the hosts belonging to each of them (specially for scripting purposes). Change-Id: I94833c15075ae655bc11e7c0fc47c0abad5846fc Signed-off-by: David Caro --- openstackclient/compute/v2/aggregate.py | 2 ++ openstackclient/tests/unit/compute/v2/test_aggregate.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index e39eb2d272..37522a788a 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -193,12 +193,14 @@ def take_action(self, parsed_args): "Name", "Availability Zone", "Properties", + "Hosts", ) columns = ( "ID", "Name", "Availability Zone", "Metadata", + "Hosts", ) else: column_headers = columns = ( diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index 8563f98811..92ff607f94 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -234,6 +234,7 @@ class TestAggregateList(TestAggregate): "Name", "Availability Zone", "Properties", + "Hosts", ) list_data = (( @@ -251,6 +252,7 @@ class TestAggregateList(TestAggregate): for key, value in TestAggregate.fake_ag.metadata.items() if key != 'availability_zone' }), + format_columns.ListColumn(TestAggregate.fake_ag.hosts), ), ) def setUp(self): From 02d6fe9be629c28da2568d1ad51a5334064709bd Mon Sep 17 00:00:00 2001 From: Akihiro Motoki Date: Tue, 8 Jun 2021 15:28:06 +0900 Subject: [PATCH 008/706] L3 conntrack helper: Use singular name consistently We use singular form for delete command argument in all places. This commit replaces conntrack-helper-ids with a singular form. The only visible change is a fix for the help message below. openstack network l3 conntrack helper delete [ ...] Change-Id: I50bbd9f6199071bb86cbb2f37c45ebda1de58433 --- openstackclient/network/v2/l3_conntrack_helper.py | 8 ++++---- .../tests/unit/network/v2/test_l3_conntrack_helper.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openstackclient/network/v2/l3_conntrack_helper.py b/openstackclient/network/v2/l3_conntrack_helper.py index dae259273f..94788823ab 100644 --- a/openstackclient/network/v2/l3_conntrack_helper.py +++ b/openstackclient/network/v2/l3_conntrack_helper.py @@ -99,8 +99,8 @@ def get_parser(self, prog_name): help=_('Router that the conntrack helper belong to') ) parser.add_argument( - 'conntrack_helper_ids', - metavar='', + 'conntrack_helper_id', + metavar='', nargs='+', help=_('The ID of the conntrack helper(s) to delete') ) @@ -112,7 +112,7 @@ def take_action(self, parsed_args): result = 0 router = client.find_router(parsed_args.router, ignore_missing=False) - for ct_helper in parsed_args.conntrack_helper_ids: + for ct_helper in parsed_args.conntrack_helper_id: try: client.delete_conntrack_helper( ct_helper, router.id, ignore_missing=False) @@ -123,7 +123,7 @@ def take_action(self, parsed_args): {'ct_helper': ct_helper, 'e': e}) if result > 0: - total = len(parsed_args.conntrack_helper_ids) + total = len(parsed_args.conntrack_helper_id) msg = (_("%(result)s of %(total)s L3 conntrack helpers failed " "to delete.") % {'result': result, 'total': total}) raise exceptions.CommandError(msg) 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 1676c9ffad..b3d026a7ef 100644 --- a/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py +++ b/openstackclient/tests/unit/network/v2/test_l3_conntrack_helper.py @@ -127,7 +127,7 @@ def test_delete(self): self.ct_helper.id ] verifylist = [ - ('conntrack_helper_ids', [self.ct_helper.id]), + ('conntrack_helper_id', [self.ct_helper.id]), ('router', self.ct_helper.router_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -143,7 +143,7 @@ def test_delete_error(self): self.ct_helper.id ] verifylist = [ - ('conntrack_helper_ids', [self.ct_helper.id]), + ('conntrack_helper_id', [self.ct_helper.id]), ('router', self.router.id), ] self.network.delete_conntrack_helper.side_effect = Exception( From 280b14abcddf3a308a819771117dd6b72d802642 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 16 Jun 2021 16:19:08 +0100 Subject: [PATCH 009/706] compute: Note that '--password' is deployment-specific Password injection requires either hypervisor-support or an agent running in the guest that will talk to the metadata service. It can be disabled for a deployment using the '[api] enable_instance_password' nova config option. Indicate this, albeit briefly. Change-Id: Ief94ea07fc7ab6a487af972e8759ca6704d8f085 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 468c6b1b3e..272233917a 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1012,7 +1012,10 @@ def get_parser(self, prog_name): parser.add_argument( '--password', metavar='', - help=_("Set the password to this server"), + help=_( + 'Set the password to this server. ' + 'This option requires cloud support.' + ), ) parser.add_argument( '--security-group', @@ -3142,7 +3145,10 @@ def get_parser(self, prog_name): parser.add_argument( '--password', metavar='', - help=_('Set the password on the rebuilt server'), + help=_( + 'Set the password on the rebuilt server. ' + 'This option requires cloud support.' + ), ) parser.add_argument( '--property', @@ -3435,7 +3441,8 @@ def get_parser(self, prog_name): '--password', metavar='', default=None, help=_( 'Set the password on the evacuated instance. This option is ' - 'mutually exclusive with the --shared-storage option' + 'mutually exclusive with the --shared-storage option. ' + 'This option requires cloud support.' ), ) shared_storage_group.add_argument( @@ -3725,7 +3732,10 @@ def get_parser(self, prog_name): parser.add_argument( '--password', metavar='', - help=_("Set the password on the rescued instance"), + help=_( + 'Set the password on the rescued instance. ' + 'This option requires cloud support.' + ), ) return parser @@ -3992,7 +4002,10 @@ def get_parser(self, prog_name): password_group = parser.add_mutually_exclusive_group() password_group.add_argument( '--password', - help=_('Set the server password'), + help=_( + 'Set the server password. ' + 'This option requires cloud support.' + ), ) password_group.add_argument( '--no-password', From a821d6b7c57c7684a990ee39b6b93d5085f25a70 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 13 Jul 2021 20:29:43 +0100 Subject: [PATCH 010/706] volume: Add 'volume transfer request create --(no-)snapshots' option This closes a gap with cinderclient's 'transfer-create' command. Change-Id: I7386a7be15c0e3ee87abbcfc2275ba8524c10ff8 Signed-off-by: Stephen Finucane Story: 2009054 Task: 42831 --- ...est.py => test_volume_transfer_request.py} | 46 +++++++++++++++++++ .../volume/v2/volume_transfer_request.py | 36 +++++++++++++++ ...reate-snapshots-opts-1361416d37021e89.yaml | 6 +++ 3 files changed, 88 insertions(+) rename openstackclient/tests/unit/volume/v2/{test_transfer_request.py => test_volume_transfer_request.py} (89%) create mode 100644 releasenotes/notes/add-volume-transfer-request-create-snapshots-opts-1361416d37021e89.yaml diff --git a/openstackclient/tests/unit/volume/v2/test_transfer_request.py b/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py similarity index 89% rename from openstackclient/tests/unit/volume/v2/test_transfer_request.py rename to openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py index c9dce3cab0..1a1f220ff6 100644 --- a/openstackclient/tests/unit/volume/v2/test_transfer_request.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_transfer_request.py @@ -15,6 +15,7 @@ from unittest import mock from unittest.mock import call +from cinderclient import api_versions from osc_lib import exceptions from osc_lib import utils @@ -172,6 +173,51 @@ 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.app.client_manager.volume.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.app.client_manager.volume.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): diff --git a/openstackclient/volume/v2/volume_transfer_request.py b/openstackclient/volume/v2/volume_transfer_request.py index 2a1ace1f42..8919933609 100644 --- a/openstackclient/volume/v2/volume_transfer_request.py +++ b/openstackclient/volume/v2/volume_transfer_request.py @@ -16,6 +16,7 @@ import logging +from cinderclient import api_versions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -76,6 +77,25 @@ 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="", @@ -85,6 +105,21 @@ def get_parser(self, prog_name): 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, @@ -92,6 +127,7 @@ 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/releasenotes/notes/add-volume-transfer-request-create-snapshots-opts-1361416d37021e89.yaml b/releasenotes/notes/add-volume-transfer-request-create-snapshots-opts-1361416d37021e89.yaml new file mode 100644 index 0000000000..f915f87c16 --- /dev/null +++ b/releasenotes/notes/add-volume-transfer-request-create-snapshots-opts-1361416d37021e89.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + The ``volume transfer request create`` command now accepts the + ``--snapshots`` / ``--no-snapshots`` option to configure whether to + create a transfer request for a volume without snapshots or not. From c1209601b4f4b81690a186e51aa819c783367fae Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 23 Jul 2021 12:48:23 +0100 Subject: [PATCH 011/706] tests: Handle removal of block-storage v2 API Cinder recently removed their v2 API [1] which is causing the functional tests to fail. Improve our 'is_service_enabled' test helper to use the 'versions show' command, which queries the service catalog and can give us information about the service version as well as answer the more general "is this service available" question. We also resolve a long-standing TODO in the process. [1] https://review.opendev.org/c/openstack/cinder/+/792299 Change-Id: I381069357aa008344e15327adf3a863c0c2e1f04 Signed-off-by: Stephen Finucane --- openstackclient/tests/functional/base.py | 29 ++++++++++++------- .../tests/functional/volume/v1/common.py | 23 ++++----------- .../tests/functional/volume/v2/common.py | 11 ++++++- .../tests/functional/volume/v3/common.py | 11 ++++++- 4 files changed, 44 insertions(+), 30 deletions(-) diff --git a/openstackclient/tests/functional/base.py b/openstackclient/tests/functional/base.py index 3542a82756..0ed7dff8c4 100644 --- a/openstackclient/tests/functional/base.py +++ b/openstackclient/tests/functional/base.py @@ -68,17 +68,24 @@ def openstack(cls, cmd, cloud=ADMIN_CLOUD, fail_ok=False): ) @classmethod - def is_service_enabled(cls, service): - """Ask client cloud if service is available""" - cmd = ('service show -f value -c enabled {service}' - .format(service=service)) - try: - return "True" in cls.openstack(cmd) - except exceptions.CommandFailed as e: - if "No service with a type, name or ID of" in str(e): - return False - else: - raise # Unable to determine if service is enabled + def is_service_enabled(cls, service, version=None): + """Ask client cloud if service is available + + :param service: The service name or type. This should be either an + exact match to what is in the catalog or a known official value or + alias from service-types-authority + :param version: Optional version. This should be a major version, e.g. + '2.0' + :returns: True if the service is enabled and optionally provides the + specified API version, else False + """ + ret = cls.openstack( + f'versions show --service {service} -f value -c Version' + ).splitlines() + if version: + return version in ret + + return bool(ret) @classmethod def is_extension_enabled(cls, alias): diff --git a/openstackclient/tests/functional/volume/v1/common.py b/openstackclient/tests/functional/volume/v1/common.py index 04eb1f48ae..755874785d 100644 --- a/openstackclient/tests/functional/volume/v1/common.py +++ b/openstackclient/tests/functional/volume/v1/common.py @@ -20,25 +20,14 @@ class BaseVolumeTests(volume_base.BaseVolumeTests): @classmethod def setUpClass(cls): - super(BaseVolumeTests, cls).setUpClass() - # TODO(dtroyer): This needs to be updated to specifically check for - # Volume v1 rather than just 'volume', but for now - # that is enough until we get proper version negotiation - cls.haz_volume_v1 = cls.is_service_enabled('volume') + super().setUpClass() + cls.haz_volume_v1 = cls.is_service_enabled('block-storage', '1.0') def setUp(self): - super(BaseVolumeTests, self).setUp() - - # This class requires Volume v1 - # if not self.haz_volume_v1: - # self.skipTest("No Volume v1 service present") - - # TODO(dtroyer): We really want the above to work but right now - # (12Sep2017) DevStack still creates a 'volume' - # service type even though there is no service behind - # it. Until that is fixed we need to just skip the - # volume v1 functional tests in master. - self.skipTest("No Volume v1 service present") + super().setUp() + + if not self.haz_volume_v1: + self.skipTest("No Volume v1 service present") ver_fixture = fixtures.EnvironmentVariable( 'OS_VOLUME_API_VERSION', '1' diff --git a/openstackclient/tests/functional/volume/v2/common.py b/openstackclient/tests/functional/volume/v2/common.py index 3817671425..7e3a80845a 100644 --- a/openstackclient/tests/functional/volume/v2/common.py +++ b/openstackclient/tests/functional/volume/v2/common.py @@ -18,8 +18,17 @@ class BaseVolumeTests(base.BaseVolumeTests): """Base class for Volume functional tests. """ + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.haz_volume_v2 = cls.is_service_enabled('block-storage', '2.0') + def setUp(self): - super(BaseVolumeTests, self).setUp() + super().setUp() + + if not self.haz_volume_v2: + self.skipTest("No Volume v2 service present") + ver_fixture = fixtures.EnvironmentVariable( 'OS_VOLUME_API_VERSION', '2' ) diff --git a/openstackclient/tests/functional/volume/v3/common.py b/openstackclient/tests/functional/volume/v3/common.py index a710a6835c..29f769b640 100644 --- a/openstackclient/tests/functional/volume/v3/common.py +++ b/openstackclient/tests/functional/volume/v3/common.py @@ -18,8 +18,17 @@ class BaseVolumeTests(base.BaseVolumeTests): """Base class for Volume functional tests. """ + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.haz_volume_v3 = cls.is_service_enabled('block-storage', '3.0') + def setUp(self): - super(BaseVolumeTests, self).setUp() + super().setUp() + + if not self.haz_volume_v3: + self.skipTest("No Volume v3 service present") + ver_fixture = fixtures.EnvironmentVariable( 'OS_VOLUME_API_VERSION', '3' ) From 4f6fe1c0fd0ee5be3cc78961fd334aac0bacd57b Mon Sep 17 00:00:00 2001 From: melanie witt Date: Tue, 27 Jul 2021 02:10:20 +0000 Subject: [PATCH 012/706] Fix TestListMigrationV223 test class MIGRATION_COLUMNS Currently only the test_server_migration_list adds the 'Id' and 'Type' columns to the expected output, so if the test_server_migration_list_no_options test is run by itself, it fails as the actual response contains 'Id' and 'Type' but the reference does not. This example run fails: tox -epy38 test_server_migration_list_no_options The reason the tests pass in the gate is because test_server_migration_list (which adds the 'Id' and 'Type' columns to self.MIGRATION_COLUMNS) appears to always run before test_server_migration_list_no_options, so the latter test gets the benefit of the former test's column additions. This changes the test class to just include the 'Id' and 'Type' columns all the time as they are always returned in microversion 2.23 anyway. Story: 2009079 Task: 42891 Change-Id: I2c97e9f64790b5e978e4d04230d45b8e343b53d4 --- openstackclient/tests/unit/compute/v2/test_server.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index c6dff5a8a2..42c8816bcf 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4977,9 +4977,9 @@ class TestListMigrationV223(TestListMigration): """Test fetch all migrations. """ MIGRATION_COLUMNS = [ - 'Source Node', 'Dest Node', 'Source Compute', - 'Dest Compute', 'Dest Host', 'Status', 'Server UUID', - 'Old Flavor', 'New Flavor', 'Created At', 'Updated At' + 'Id', 'Source Node', 'Dest Node', 'Source Compute', 'Dest Compute', + 'Dest Host', 'Status', 'Server UUID', 'Old Flavor', 'New Flavor', + 'Type', 'Created At', 'Updated At' ] def setUp(self): @@ -5006,9 +5006,6 @@ def test_server_migration_list(self): self.migrations_mock.list.assert_called_with(**kwargs) - self.MIGRATION_COLUMNS.insert(0, "Id") - self.MIGRATION_COLUMNS.insert( - len(self.MIGRATION_COLUMNS) - 2, 'Type') self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) From e0dc31f32eb6720059439e791713e2c61f81bf70 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 27 Jul 2021 11:08:35 +0100 Subject: [PATCH 013/706] volume: Add missing 'volume list --offset' parameter Looking at the code for the ancient v1 cinder API, we see that this supported offset-style pagination [1][2][3]. Add this parameter, simplifying a future patch to standardize pagination across OSC. [1] https://github.com/openstack/cinder/blob/juno-eol/cinder/api/v1/volumes.py#L259 [2] https://github.com/openstack/cinder/blob/juno-eol/cinder/api/v1/volumes.py#L292 [3] https://github.com/openstack/cinder/blob/juno-eol/cinder/api/common.py#L120 Change-Id: Ifec208ea9ed7afb4bebced6132abb96a3af034b5 Signed-off-by: Stephen Finucane --- openstackclient/tests/unit/volume/v1/test_volume.py | 8 ++++++-- openstackclient/volume/v1/volume.py | 10 ++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py index 704a66da71..b8002d6341 100644 --- a/openstackclient/tests/unit/volume/v1/test_volume.py +++ b/openstackclient/tests/unit/volume/v1/test_volume.py @@ -858,9 +858,10 @@ def test_volume_list_long(self): ), ) self.assertItemsEqual(datalist, tuple(data)) - def test_volume_list_with_limit(self): + def test_volume_list_with_limit_and_offset(self): arglist = [ '--limit', '2', + '--offset', '5', ] verifylist = [ ('long', False), @@ -868,6 +869,7 @@ def test_volume_list_with_limit(self): ('name', None), ('status', None), ('limit', 2), + ('offset', 5), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -876,9 +878,11 @@ def test_volume_list_with_limit(self): self.volumes_mock.list.assert_called_once_with( limit=2, search_opts={ + 'offset': 5, 'status': None, 'display_name': None, - 'all_tenants': False, } + 'all_tenants': False, + }, ) self.assertEqual(self.columns, columns) self.assertItemsEqual(self.datalist, tuple(data)) diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index 460bd85a8b..dfbb0c545f 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -327,6 +327,13 @@ 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, @@ -395,6 +402,9 @@ def take_action(self, parsed_args): '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, From ed87f7949ef1ef580ed71b9820e16823c0466472 Mon Sep 17 00:00:00 2001 From: melanie witt Date: Mon, 26 Jul 2021 22:13:55 +0000 Subject: [PATCH 014/706] Correct REST API response fields for /os-migrations API The compute APIs are unfortunately inconsistent with regard to the response parameters for migrations. * GET /servers/{server_id}/migrations returns server_uuid * GET /os-migrations returns instance_uuid Because the 'Server UUID' column is being specified for parsing the response from GET /os-migrations, it is always showing as an empty string to users. There are a few other mismatches between the column names and the REST API response fields [1]: * 'Old Flavor' vs 'old_instance_type_id' * 'New Flavor' vs 'new_instance_type_id' * 'Type' vs 'migration_type' This adds a new list containing the REST API response field names to pass to utils.get_item_properties so that the responses are correctly parsed and the client output contains the response data instead of empty strings. Story: 2009078 Task: 42890 [1] https://docs.openstack.org/api-ref/compute/?expanded=list-migrations-detail#list-migrations Change-Id: I8aab60619e0225047f6a1c31e44917ca8fcc799e --- openstackclient/compute/v2/server.py | 27 +++++++--- .../tests/unit/compute/v2/fakes.py | 8 +-- .../tests/unit/compute/v2/test_server.py | 54 ++++++++++++++++++- 3 files changed, 77 insertions(+), 12 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 24df46d8ce..a09fd44d0a 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2781,28 +2781,41 @@ def get_parser(self, prog_name): return parser def print_migrations(self, parsed_args, compute_client, migrations): - columns = [ + column_headers = [ 'Source Node', 'Dest Node', 'Source Compute', 'Dest Compute', 'Dest Host', 'Status', 'Server UUID', 'Old Flavor', 'New Flavor', 'Created At', 'Updated At', ] + # Response fields coming back from the REST API are not always exactly + # the same as the column header names. + columns = [ + 'source_node', 'dest_node', 'source_compute', 'dest_compute', + 'dest_host', 'status', 'instance_uuid', 'old_instance_type_id', + 'new_instance_type_id', 'created_at', 'updated_at', + ] + # Insert migrations UUID after ID if compute_client.api_version >= api_versions.APIVersion("2.59"): - columns.insert(0, "UUID") + column_headers.insert(0, "UUID") + columns.insert(0, "uuid") if compute_client.api_version >= api_versions.APIVersion("2.23"): - columns.insert(0, "Id") - columns.insert(len(columns) - 2, "Type") + column_headers.insert(0, "Id") + columns.insert(0, "id") + column_headers.insert(len(column_headers) - 2, "Type") + columns.insert(len(columns) - 2, "migration_type") if compute_client.api_version >= api_versions.APIVersion("2.80"): if parsed_args.project: - columns.insert(len(columns) - 2, "Project") + column_headers.insert(len(column_headers) - 2, "Project") + columns.insert(len(columns) - 2, "project_id") if parsed_args.user: - columns.insert(len(columns) - 2, "User") + column_headers.insert(len(column_headers) - 2, "User") + columns.insert(len(columns) - 2, "user_id") return ( - columns, + column_headers, (utils.get_item_properties(mig, columns) for mig in migrations), ) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 4a2a44de12..47457acbc6 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -1587,20 +1587,20 @@ def create_one_migration(attrs=None, methods=None): migration_info = { "dest_host": "10.0.2.15", "status": "migrating", - "type": "migration", + "migration_type": "migration", "updated_at": "2017-01-31T08:03:25.000000", "created_at": "2017-01-31T08:03:21.000000", "dest_compute": "compute-" + uuid.uuid4().hex, "id": random.randint(1, 999), "source_node": "node-" + uuid.uuid4().hex, - "server": uuid.uuid4().hex, + "instance_uuid": uuid.uuid4().hex, "dest_node": "node-" + uuid.uuid4().hex, "source_compute": "compute-" + uuid.uuid4().hex, "uuid": uuid.uuid4().hex, "old_instance_type_id": uuid.uuid4().hex, "new_instance_type_id": uuid.uuid4().hex, - "project": uuid.uuid4().hex, - "user": uuid.uuid4().hex + "project_id": uuid.uuid4().hex, + "user_id": uuid.uuid4().hex } # Overwrite default attributes. diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 42c8816bcf..57840cb076 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4909,6 +4909,13 @@ class TestListMigration(TestServer): 'Old Flavor', 'New Flavor', 'Created At', 'Updated At' ] + # These are the fields that come back in the response from the REST API. + MIGRATION_FIELDS = [ + 'source_node', 'dest_node', 'source_compute', 'dest_compute', + 'dest_host', 'status', 'instance_uuid', 'old_instance_type_id', + 'new_instance_type_id', 'created_at', 'updated_at' + ] + def setUp(self): super(TestListMigration, self).setUp() @@ -4920,7 +4927,7 @@ def setUp(self): self.migrations_mock.list.return_value = self.migrations self.data = (common_utils.get_item_properties( - s, self.MIGRATION_COLUMNS) for s in self.migrations) + s, self.MIGRATION_FIELDS) for s in self.migrations) # Get the command object to test self.cmd = server.ListMigration(self.app, None) @@ -4982,6 +4989,13 @@ class TestListMigrationV223(TestListMigration): 'Type', 'Created At', 'Updated At' ] + # These are the fields that come back in the response from the REST API. + MIGRATION_FIELDS = [ + 'id', 'source_node', 'dest_node', 'source_compute', 'dest_compute', + 'dest_host', 'status', 'instance_uuid', 'old_instance_type_id', + 'new_instance_type_id', 'migration_type', 'created_at', 'updated_at' + ] + def setUp(self): super(TestListMigrationV223, self).setUp() @@ -5019,6 +5033,14 @@ class TestListMigrationV259(TestListMigration): 'Old Flavor', 'New Flavor', 'Type', 'Created At', 'Updated At' ] + # These are the fields that come back in the response from the REST API. + MIGRATION_FIELDS = [ + 'id', 'uuid', 'source_node', 'dest_node', 'source_compute', + 'dest_compute', 'dest_host', 'status', 'instance_uuid', + 'old_instance_type_id', 'new_instance_type_id', 'migration_type', + 'created_at', 'updated_at' + ] + def setUp(self): super(TestListMigrationV259, self).setUp() @@ -5125,6 +5147,14 @@ class TestListMigrationV266(TestListMigration): 'Old Flavor', 'New Flavor', 'Type', 'Created At', 'Updated At' ] + # These are the fields that come back in the response from the REST API. + MIGRATION_FIELDS = [ + 'id', 'uuid', 'source_node', 'dest_node', 'source_compute', + 'dest_compute', 'dest_host', 'status', 'instance_uuid', + 'old_instance_type_id', 'new_instance_type_id', 'migration_type', + 'created_at', 'updated_at' + ] + def setUp(self): super(TestListMigrationV266, self).setUp() @@ -5194,6 +5224,14 @@ class TestListMigrationV280(TestListMigration): 'Old Flavor', 'New Flavor', 'Type', 'Created At', 'Updated At' ] + # These are the fields that come back in the response from the REST API. + MIGRATION_FIELDS = [ + 'id', 'uuid', 'source_node', 'dest_node', 'source_compute', + 'dest_compute', 'dest_host', 'status', 'instance_uuid', + 'old_instance_type_id', 'new_instance_type_id', 'migration_type', + 'created_at', 'updated_at' + ] + project = identity_fakes.FakeProject.create_one_project() user = identity_fakes.FakeUser.create_one_user() @@ -5247,10 +5285,14 @@ def test_server_migration_list_with_project(self): self.MIGRATION_COLUMNS.insert( len(self.MIGRATION_COLUMNS) - 2, "Project") + self.MIGRATION_FIELDS.insert( + len(self.MIGRATION_FIELDS) - 2, "project_id") self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) # Clean up global variables MIGRATION_COLUMNS self.MIGRATION_COLUMNS.remove('Project') + # Clean up global variables MIGRATION_FIELDS + self.MIGRATION_FIELDS.remove('project_id') def test_get_migrations_with_project_pre_v280(self): self.app.client_manager.compute.api_version = api_versions.APIVersion( @@ -5309,10 +5351,14 @@ def test_server_migration_list_with_user(self): self.MIGRATION_COLUMNS.insert( len(self.MIGRATION_COLUMNS) - 2, "User") + self.MIGRATION_FIELDS.insert( + len(self.MIGRATION_FIELDS) - 2, "user_id") self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) # Clean up global variables MIGRATION_COLUMNS self.MIGRATION_COLUMNS.remove('User') + # Clean up global variables MIGRATION_FIELDS + self.MIGRATION_FIELDS.remove('user_id') def test_get_migrations_with_user_pre_v280(self): self.app.client_manager.compute.api_version = api_versions.APIVersion( @@ -5371,13 +5417,19 @@ def test_server_migration_list_with_project_and_user(self): self.MIGRATION_COLUMNS.insert( len(self.MIGRATION_COLUMNS) - 2, "Project") + self.MIGRATION_FIELDS.insert( + len(self.MIGRATION_FIELDS) - 2, "project_id") self.MIGRATION_COLUMNS.insert( len(self.MIGRATION_COLUMNS) - 2, "User") + self.MIGRATION_FIELDS.insert( + len(self.MIGRATION_FIELDS) - 2, "user_id") self.assertEqual(self.MIGRATION_COLUMNS, columns) self.assertEqual(tuple(self.data), tuple(data)) # Clean up global variables MIGRATION_COLUMNS self.MIGRATION_COLUMNS.remove('Project') + self.MIGRATION_FIELDS.remove('project_id') self.MIGRATION_COLUMNS.remove('User') + self.MIGRATION_FIELDS.remove('user_id') def test_get_migrations_with_project_and_user_pre_v280(self): self.app.client_manager.compute.api_version = api_versions.APIVersion( From 12c93c6d5ff420f6a4a8833d33bad6ee7222e2f7 Mon Sep 17 00:00:00 2001 From: melanie witt Date: Thu, 12 Aug 2021 22:06:36 +0000 Subject: [PATCH 015/706] Show "Forced Down" compute service status with --long Currently, the unified client does not have the ability to show the "Forced Down" field of a GET /os-services response in microversion 2.11 even though the legacy client can. This adds a "Forced Down" column to the 'openstack compute service list --long' command output when microversion 2.11 is used. Story: 2009115 Task: 43011 Change-Id: I10bc2fedbf0e867a990227962b2b6e60f5681f69 --- openstackclient/compute/v2/service.py | 4 +++ .../tests/unit/compute/v2/fakes.py | 2 ++ .../tests/unit/compute/v2/test_service.py | 32 +++++++++++++++++++ ...ice-list-forced-down-2b16d1cb44f71a08.yaml | 5 +++ 4 files changed, 43 insertions(+) create mode 100644 releasenotes/notes/compute-service-list-forced-down-2b16d1cb44f71a08.yaml diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index 625c0fad3a..6427e548be 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -103,6 +103,10 @@ def take_action(self, parsed_args): "Updated At", "Disabled Reason" ) + has_forced_down = ( + compute_client.api_version >= api_versions.APIVersion('2.11')) + if has_forced_down: + columns += ("Forced Down",) else: columns = ( "ID", diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 47457acbc6..05a14e16a7 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -722,6 +722,8 @@ def create_one_service(attrs=None): 'state': 'state-' + uuid.uuid4().hex, 'updated_at': 'time-' + uuid.uuid4().hex, 'disabled_reason': 'earthquake', + # Introduced in API microversion 2.11 + 'forced_down': False, } # Overwrite default attributes. diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py index 87e54747f1..c547c3a687 100644 --- a/openstackclient/tests/unit/compute/v2/test_service.py +++ b/openstackclient/tests/unit/compute/v2/test_service.py @@ -190,6 +190,38 @@ def test_service_list_with_long_option(self): self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, list(data)) + def test_service_list_with_long_option_2_11(self): + arglist = [ + '--host', self.service.host, + '--service', self.service.binary, + '--long' + ] + verifylist = [ + ('host', self.service.host), + ('service', self.service.binary), + ('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.service_mock.list.assert_called_with( + self.service.host, + self.service.binary, + ) + + # 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.forced_down,)] + + self.assertEqual(columns_long, columns) + self.assertEqual(data_long, list(data)) + class TestServiceSet(TestService): diff --git a/releasenotes/notes/compute-service-list-forced-down-2b16d1cb44f71a08.yaml b/releasenotes/notes/compute-service-list-forced-down-2b16d1cb44f71a08.yaml new file mode 100644 index 0000000000..ebdc41556f --- /dev/null +++ b/releasenotes/notes/compute-service-list-forced-down-2b16d1cb44f71a08.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add column ``Forced Down`` to the output of ``compute service list + --long``. Only available starting with ``--os-compute-api-version 2.11``. From 4aad7dd77953e09b4973df0b37d1cb23d8b0afbf Mon Sep 17 00:00:00 2001 From: LEE JAE YONG Date: Sat, 28 Aug 2021 07:17:04 +0000 Subject: [PATCH 016/706] Fix typo error in listing server's column name openstack server list -c "Created At" command doesn't work because the wrong variable was used here. When we receive resp data, Created At data is saved with the name "created". But in "server.py", we append columns as created_at. So it seems to print an empty table. Story: 2009149 Task: 43112 Change-Id: I06de6903d5cc427a8b0fdcd168fec47192f4365b --- openstackclient/compute/v2/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index a09fd44d0a..6996405faa 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2341,7 +2341,7 @@ def take_action(self, parsed_args): columns.append('user_id') column_headers.append('User ID') if c in ('Created At', 'created_at'): - columns.append('created_at') + columns.append('created') column_headers.append('Created At') # convert back to tuple From 6ce7da8aeb7e5d1347940433e087036e8e43eaa6 Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Mon, 30 Aug 2021 12:06:10 -0500 Subject: [PATCH 017/706] [community goal] Update contributor documentation This patch updates/adds the contributor documentation to follow the guidelines of the Ussuri cycle community goal[1]. [1] https://governance.openstack.org/tc/goals/selected/ussuri/project-ptl-and-contrib-docs.html Story: #2007236 Task: #38547 Change-Id: I0afa1796d488a96160f4a7fd615920d05fe1771c --- CONTRIBUTING.rst | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 69ecd79cad..f8732b7211 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,16 +1,27 @@ -If you would like to contribute to the development of OpenStack, -you must follow the steps documented at: +The source repository for this project can be found at: - https://docs.openstack.org/infra/manual/developers.html + https://opendev.org/openstack/python-openstackclient -Once those steps have been completed, changes to OpenStack -should be submitted for review via the Gerrit tool, following -the workflow documented at: +Pull requests submitted through GitHub are not monitored. - https://docs.openstack.org/infra/manual/developers.html#development-workflow +To start contributing to OpenStack, follow the steps in the contribution guide +to set up and use Gerrit: -Pull requests submitted through GitHub will be ignored. + https://docs.openstack.org/contributors/code-and-documentation/quick-start.html -Bugs should be filed on Storyboard, not GitHub or Launchpad: +Bugs should be filed on StoryBoard: https://storyboard.openstack.org/#!/project/openstack/python-openstackclient + +Developers should also join the discussion on the mailing list, at: + + http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss + +or join the IRC channel on + + #openstack-sdks on OFTC (irc.oftc.net) + +For more specific information about contributing to this repository, see the +openstacksdk contributor guide: + + https://docs.openstack.org/openstacksdk/latest/contributor/index.html From 8e833a3ed26467a1190ba69d8ba716a7cd1cccb3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 1 Sep 2021 13:04:51 +0100 Subject: [PATCH 018/706] compute: Add support for microversion 2.90 Allow configuring hostname when creating a new server or updating or rebuilding an existing server. Change-Id: Ibe603eab78bbbec43605f56de62a20493b6aa93d Signed-off-by: Stephen Finucane Depends-On: https://review.opendev.org/c/openstack/python-novaclient/+/806917 --- openstackclient/compute/v2/server.py | 76 +++++++- .../tests/unit/compute/v2/test_server.py | 171 ++++++++++++++++-- ...server-hostname-opts-3cb4fd90b5bf47ca.yaml | 8 + 3 files changed, 235 insertions(+), 20 deletions(-) create mode 100644 releasenotes/notes/add-server-hostname-opts-3cb4fd90b5bf47ca.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index a09fd44d0a..4750583812 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1154,6 +1154,18 @@ def get_parser(self, prog_name): '(supported by --os-compute-api-version 2.52 or above)' ), ) + parser.add_argument( + '--hostname', + metavar='', + help=_( + 'Hostname configured for the server in the metadata service. ' + 'If unset, a hostname will be automatically generated from ' + 'the server name. ' + 'A utility such as cloud-init is required to propagate the ' + 'hostname in the metadata service to the guest OS itself. ' + '(supported by --os-compute-api-version 2.90 or above)' + ), + ) parser.add_argument( '--wait', action='store_true', @@ -1618,6 +1630,16 @@ def _match_image(image_api, wanted_properties): boot_kwargs['hypervisor_hostname'] = ( parsed_args.hypervisor_hostname) + if parsed_args.hostname: + 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' + ) + raise exceptions.CommandError(msg) + + boot_kwargs['hostname'] = parsed_args.hostname + LOG.debug('boot_args: %s', boot_args) LOG.debug('boot_kwargs: %s', boot_kwargs) @@ -3273,6 +3295,16 @@ def get_parser(self, prog_name): '(supported by --os-compute-api-version 2.63 or above)' ), ) + parser.add_argument( + '--hostname', + metavar='', + help=_( + 'Hostname configured for the server in the metadata service. ' + 'A separate utility running in the guest is required to ' + 'propagate changes to this value to the guest OS itself. ' + '(supported by --os-compute-api-version 2.90 or above)' + ), + ) parser.add_argument( '--wait', action='store_true', @@ -3390,6 +3422,16 @@ def _show_progress(progress): kwargs['trusted_image_certificates'] = None + if parsed_args.hostname: + 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' + ) + raise exceptions.CommandError(msg) + + kwargs['hostname'] = parsed_args.hostname + try: server = server.rebuild(image, parsed_args.password, **kwargs) finally: @@ -4076,6 +4118,16 @@ def get_parser(self, prog_name): '(supported by --os-compute-api-version 2.26 or above)' ), ) + parser.add_argument( + '--hostname', + metavar='', + help=_( + 'Hostname configured for the server in the metadata service. ' + 'A separate utility running in the guest is required to ' + 'propagate changes to this value to the guest OS itself. ' + '(supported by --os-compute-api-version 2.90 or above)' + ), + ) return parser def take_action(self, parsed_args): @@ -4102,8 +4154,27 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) + if parsed_args.hostname: + if server.api_version < api_versions.APIVersion('2.90'): + msg = _( + '--os-compute-api-version 2.90 or greater is required to ' + 'support the --hostname option' + ) + raise exceptions.CommandError(msg) + + update_kwargs = {} + if parsed_args.name: - server.update(name=parsed_args.name) + update_kwargs['name'] = parsed_args.name + + if parsed_args.description: + update_kwargs['description'] = parsed_args.description + + if parsed_args.hostname: + update_kwargs['hostname'] = parsed_args.hostname + + if update_kwargs: + server.update(**update_kwargs) if parsed_args.properties: compute_client.servers.set_meta(server, parsed_args.properties) @@ -4124,9 +4195,6 @@ def take_action(self, parsed_args): elif parsed_args.no_password: server.clear_password() - if parsed_args.description: - server.update(description=parsed_args.description) - if parsed_args.tags: for tag in parsed_args.tags: server.add_tag(tag=tag) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 57840cb076..cab9efd0c0 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -3554,6 +3554,76 @@ def test_server_create_with_host_and_hypervisor_hostname_v274(self): self.assertFalse(self.images_mock.called) 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') + + arglist = [ + '--image', 'image1', + '--flavor', 'flavor1', + '--hostname', 'hostname', + self.new_server.name, + ] + verifylist = [ + ('image', 'image1'), + ('flavor', 'flavor1'), + ('hostname', 'hostname'), + ('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) + + # 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, + 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, + hostname='hostname', + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) + self.assertFalse(self.images_mock.called) + 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') + + arglist = [ + '--image', 'image1', + '--flavor', 'flavor1', + '--hostname', 'hostname', + self.new_server.name, + ] + verifylist = [ + ('image', 'image1'), + ('flavor', 'flavor1'), + ('hostname', 'hostname'), + ('config_drive', False), + ('server_name', self.new_server.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, + parsed_args) + class TestServerDelete(TestServer): @@ -6235,6 +6305,46 @@ def test_rebuild_with_no_trusted_image_cert_pre_257(self): self.cmd.take_action, parsed_args) + def test_rebuild_with_hostname(self): + self.app.client_manager.compute.api_version = \ + api_versions.APIVersion('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) + + self.cmd.take_action(parsed_args) + + self.servers_mock.get.assert_called_with(self.server.id) + self.get_image_mock.assert_called_with(self.image.id) + self.server.rebuild.assert_called_with( + self.image, None, hostname='new-hostname') + + def test_rebuild_with_hostname_pre_v290(self): + self.app.client_manager.compute.api_version = \ + api_versions.APIVersion('2.89') + + 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.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args) + class TestEvacuateServer(TestServer): @@ -7340,10 +7450,10 @@ def test_server_set_with_root_password(self, mock_getpass): mock.sentinel.fake_pass) self.assertIsNone(result) - def test_server_set_with_description_api_newer(self): + def test_server_set_with_description(self): # Description is supported for nova api version 2.19 or above - self.fake_servers[0].api_version = 2.19 + self.fake_servers[0].api_version = api_versions.APIVersion('2.19') arglist = [ '--description', 'foo_description', @@ -7354,18 +7464,15 @@ def test_server_set_with_description_api_newer(self): ('server', 'foo_vm'), ] 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) - self.fake_servers[0].update.assert_called_once_with( - description='foo_description') - self.assertIsNone(result) + result = self.cmd.take_action(parsed_args) + self.fake_servers[0].update.assert_called_once_with( + description='foo_description') + self.assertIsNone(result) - def test_server_set_with_description_api_older(self): + 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 = 2.18 + self.fake_servers[0].api_version = api_versions.APIVersion('2.18') arglist = [ '--description', 'foo_description', @@ -7376,11 +7483,8 @@ def test_server_set_with_description_api_older(self): ('server', 'foo_vm'), ] 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_set_with_tag(self): self.fake_servers[0].api_version = api_versions.APIVersion('2.26') @@ -7426,6 +7530,41 @@ def test_server_set_with_tag_pre_v226(self): '--os-compute-api-version 2.26 or greater is required', str(ex)) + def test_server_set_with_hostname(self): + + self.fake_servers[0].api_version = api_versions.APIVersion('2.90') + + arglist = [ + '--hostname', 'foo-hostname', + 'foo_vm', + ] + verifylist = [ + ('hostname', 'foo-hostname'), + ('server', 'foo_vm'), + ] + 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.assertIsNone(result) + + def test_server_set_with_hostname_pre_v290(self): + + self.fake_servers[0].api_version = api_versions.APIVersion('2.89') + + arglist = [ + '--hostname', 'foo-hostname', + 'foo_vm', + ] + verifylist = [ + ('hostname', 'foo-hostname'), + ('server', 'foo_vm'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, self.cmd.take_action, + parsed_args) + class TestServerShelve(TestServer): diff --git a/releasenotes/notes/add-server-hostname-opts-3cb4fd90b5bf47ca.yaml b/releasenotes/notes/add-server-hostname-opts-3cb4fd90b5bf47ca.yaml new file mode 100644 index 0000000000..458d1529e8 --- /dev/null +++ b/releasenotes/notes/add-server-hostname-opts-3cb4fd90b5bf47ca.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + The ``server create``, ``server set`` and ``server rebuild`` commands now + accept an optional ``--hostname HOSTNAME`` option. This can be used to + configure the hostname stored in the metadata service and/or config drive. + Utilities such as ``cloud-init`` can then consume this information to set + the hostname within the guest OS. From 51ee17a94dccd297101725593b223f01c4f9b906 Mon Sep 17 00:00:00 2001 From: Lee Yarwood Date: Thu, 12 Aug 2021 11:27:17 +0100 Subject: [PATCH 019/706] compute: Add support for microversion 2.89 This microversion drops the duplicate ``id`` field while adding ``attachment_id`` and ``bdm_uuid`` to the output of the os-volume_attachments API reflected within osc by the ``openstack server volume list $server``command. Depends-On: https://review.opendev.org/c/openstack/nova/+/804275 Change-Id: I8a7002d8d65d7795e106b768df868198ab8b8143 --- openstackclient/compute/v2/server_volume.py | 18 +++++-- .../tests/unit/compute/v2/fakes.py | 3 ++ .../unit/compute/v2/test_server_volume.py | 49 +++++++++++++++++++ ...to_volume_attachment-cea605585db29e14.yaml | 11 +++++ 4 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/add_attachment_id_to_volume_attachment-cea605585db29e14.yaml diff --git a/openstackclient/compute/v2/server_volume.py b/openstackclient/compute/v2/server_volume.py index b53c92fe5e..d53cec931d 100644 --- a/openstackclient/compute/v2/server_volume.py +++ b/openstackclient/compute/v2/server_volume.py @@ -44,18 +44,24 @@ def take_action(self, parsed_args): volumes = compute_client.volumes.get_server_volumes(server.id) - columns = ( - 'id', + columns = () + column_headers = () + + if compute_client.api_version < api_versions.APIVersion('2.89'): + columns += ('id',) + column_headers += ('ID',) + + columns += ( 'device', 'serverId', 'volumeId', ) - column_headers = ( - 'ID', + column_headers += ( 'Device', 'Server ID', 'Volume ID', ) + if compute_client.api_version >= api_versions.APIVersion('2.70'): columns += ('tag',) column_headers += ('Tag',) @@ -64,6 +70,10 @@ def take_action(self, parsed_args): columns += ('delete_on_termination',) column_headers += ('Delete On Termination?',) + if compute_client.api_version >= api_versions.APIVersion('2.89'): + columns += ('attachment_id', 'bdm_uuid') + column_headers += ('Attachment ID', 'BlockDeviceMapping UUID') + return ( column_headers, ( diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 05a14e16a7..3142a24489 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -1715,6 +1715,9 @@ def create_one_volume_attachment(attrs=None, methods=None): "tag": "foo", # introduced in API microversion 2.79 "delete_on_termination": True, + # introduced in API microversion 2.89 + "attachment_id": uuid.uuid4().hex, + "bdm_uuid": uuid.uuid4().hex } # Overwrite default attributes. diff --git a/openstackclient/tests/unit/compute/v2/test_server_volume.py b/openstackclient/tests/unit/compute/v2/test_server_volume.py index 4d4916b7ec..02d378f82a 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_volume.py +++ b/openstackclient/tests/unit/compute/v2/test_server_volume.py @@ -167,6 +167,55 @@ def test_server_volume_list_with_delete_on_attachment(self): self.servers_volumes_mock.get_server_volumes.assert_called_once_with( self.server.id) + def test_server_volume_list_with_attachment_ids(self): + self.app.client_manager.compute.api_version = \ + api_versions.APIVersion('2.89') + + arglist = [ + self.server.id, + ] + verifylist = [ + ('server', self.server.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual( + ( + 'Device', 'Server ID', 'Volume ID', 'Tag', + 'Delete On Termination?', 'Attachment ID', + 'BlockDeviceMapping UUID', + ), + columns, + ) + self.assertEqual( + ( + ( + self.volume_attachments[0].device, + self.volume_attachments[0].serverId, + self.volume_attachments[0].volumeId, + self.volume_attachments[0].tag, + self.volume_attachments[0].delete_on_termination, + self.volume_attachments[0].attachment_id, + self.volume_attachments[0].bdm_uuid + + ), + ( + self.volume_attachments[1].device, + self.volume_attachments[1].serverId, + self.volume_attachments[1].volumeId, + self.volume_attachments[1].tag, + self.volume_attachments[1].delete_on_termination, + self.volume_attachments[1].attachment_id, + self.volume_attachments[1].bdm_uuid + ), + ), + tuple(data), + ) + self.servers_volumes_mock.get_server_volumes.assert_called_once_with( + self.server.id) + class TestServerVolumeUpdate(TestServerVolume): diff --git a/releasenotes/notes/add_attachment_id_to_volume_attachment-cea605585db29e14.yaml b/releasenotes/notes/add_attachment_id_to_volume_attachment-cea605585db29e14.yaml new file mode 100644 index 0000000000..9a43989683 --- /dev/null +++ b/releasenotes/notes/add_attachment_id_to_volume_attachment-cea605585db29e14.yaml @@ -0,0 +1,11 @@ +--- +features: + - | + Added support for `microversion 2.89`_. This microversion removes the + ``id`` field while adding the ``attachment_id`` and ``bdm_uuid`` fields to + the responses of ``GET /servers/{server_id}/os-volume_attachments`` and + ``GET /servers/{server_id}/os-volume_attachments/{volume_id}`` with these + changes reflected in novaclient under the ``openstack server volume list`` + command. + + .. _microversion 2.89: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#microversion-2-89 From ed5d2a37c51dfc621b0d8ac79510e21c9ee3dd0f Mon Sep 17 00:00:00 2001 From: Alfredo Moralejo Date: Thu, 9 Sep 2021 15:49:04 +0200 Subject: [PATCH 020/706] Replace assertItemsEqual with assertCountEqual Follow-up of [1]. After this patch was sent, two more assertItemsEqual were added in [2]. This patch is fixing it. [1] https://review.opendev.org/c/openstack/python-openstackclient/+/789410 [2] https://review.opendev.org/c/openstack/python-openstackclient/+/781637 Change-Id: Ic2276bd0ff0f5df76505f37d8994b3384d40e9a7 --- openstackclient/tests/unit/volume/v3/test_volume_message.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstackclient/tests/unit/volume/v3/test_volume_message.py b/openstackclient/tests/unit/volume/v3/test_volume_message.py index 68becf4418..8cabc0c3ad 100644 --- a/openstackclient/tests/unit/volume/v3/test_volume_message.py +++ b/openstackclient/tests/unit/volume/v3/test_volume_message.py @@ -198,7 +198,7 @@ def test_message_list(self): limit=None, ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_message_list_with_options(self): self.app.client_manager.volume.api_version = \ @@ -227,7 +227,7 @@ def test_message_list_with_options(self): limit=3, ) self.assertEqual(self.columns, columns) - self.assertItemsEqual(self.data, list(data)) + self.assertCountEqual(self.data, list(data)) def test_message_list_pre_v33(self): self.app.client_manager.volume.api_version = \ From 8ef9280af93b46e44b489592d3ae640a01c98190 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Wed, 22 Sep 2021 10:42:16 +0000 Subject: [PATCH 021/706] Update master for stable/xena Add file to the reno documentation build to show release notes for stable/xena. Use pbr instruction to increment the minor version number automatically so that master versions are higher than the versions on stable/xena. Sem-Ver: feature Change-Id: Iedf2c908bf5a9d87effa02717eb604ee8d15ef3b --- releasenotes/source/index.rst | 1 + releasenotes/source/xena.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 releasenotes/source/xena.rst diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index cdbb595ce8..0ad18a2b9f 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ OpenStackClient Release Notes :maxdepth: 1 unreleased + xena wallaby victoria ussuri diff --git a/releasenotes/source/xena.rst b/releasenotes/source/xena.rst new file mode 100644 index 0000000000..1be85be3eb --- /dev/null +++ b/releasenotes/source/xena.rst @@ -0,0 +1,6 @@ +========================= +Xena Series Release Notes +========================= + +.. release-notes:: + :branch: stable/xena From ff372ffdfbfe036993f84be20cd18262599b37de Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Wed, 22 Sep 2021 10:42:18 +0000 Subject: [PATCH 022/706] Add Python3 yoga unit tests This is an automatically generated patch to ensure unit testing is in place for all the of the tested runtimes for yoga. See also the PTI in governance [1]. [1]: https://governance.openstack.org/tc/reference/project-testing-interface.html Change-Id: I89cff43c0eb97c63deaba320e0fc63bd8ba31a2a --- .zuul.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.zuul.yaml b/.zuul.yaml index e1c1f970af..9ed506c676 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -240,7 +240,7 @@ - osc-tox-unit-tips - openstack-cover-jobs - openstack-lower-constraints-jobs - - openstack-python3-xena-jobs + - openstack-python3-yoga-jobs - publish-openstack-docs-pti - check-requirements - release-notes-jobs-python3 From c0a0f0f3d86cacbff386a5ea7b8e846dd595e197 Mon Sep 17 00:00:00 2001 From: ryanKor Date: Sat, 25 Sep 2021 18:21:03 +0900 Subject: [PATCH 023/706] Fix that the path of functional test before change: $ tox -e functional -- --regex functional.tests.compute.v2.test_server after change: $ tox -e functional -- --regex tests.functional.compute.v2.test_server the test unit path document should be change the above line. (fixed wrong letter) Change-Id: I49674fb0d56ee65c1f6328b9d960b16876173e2d --- doc/source/contributor/developing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/contributor/developing.rst b/doc/source/contributor/developing.rst index a70b33268a..c6573b9ba6 100644 --- a/doc/source/contributor/developing.rst +++ b/doc/source/contributor/developing.rst @@ -88,7 +88,7 @@ To run a specific functional test: .. code-block:: bash - $ tox -e functional -- --regex functional.tests.compute.v2.test_server + $ tox -e functional -- --regex tests.functional.compute.v2.test_server Running with PDB ~~~~~~~~~~~~~~~~ From 28a376bfb0a330470b028b6d5244ee4c8e1fe864 Mon Sep 17 00:00:00 2001 From: Pavlo Shchelokovskyy Date: Thu, 30 Sep 2021 17:14:19 +0300 Subject: [PATCH 024/706] Add --trusted-image-cert option for server create this already exists for server rebuild, but was missing for server create. This option is supported from Compute API version >= 2.63, and is only available for servers booted directly from images (not from volumes, not from snapshots, and not from images first converted to volumes). Additionally, this patch removes mentions of OS_TRUSTED_IMAGE_CERTIFICATE_IDS env var from similar option help string in server rebuild command as it is not actually implemented yet. Change-Id: I4e9faea05c499bd91034d1d284c44fdcc8e18db5 --- openstackclient/compute/v2/server.py | 32 +++- .../tests/unit/compute/v2/test_server.py | 150 ++++++++++++++++++ ...option-server-create-a660488407300f22.yaml | 7 + 3 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-trusted-certs-option-server-create-a660488407300f22.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 4750583812..291397769f 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1171,6 +1171,19 @@ def get_parser(self, prog_name): action='store_true', help=_('Wait for build to complete'), ) + parser.add_argument( + '--trusted-image-cert', + metavar='', + action='append', + dest='trusted_image_certs', + help=_( + 'Trusted image certificate IDs used to validate certificates ' + 'during the image signature verification process. ' + 'May be specified multiple times to pass multiple trusted ' + 'image certificate IDs. ' + '(supported by --os-compute-api-version 2.63 or above)' + ), + ) return parser def take_action(self, parsed_args): @@ -1640,6 +1653,24 @@ def _match_image(image_api, wanted_properties): boot_kwargs['hostname'] = parsed_args.hostname + # TODO(stephenfin): Handle OS_TRUSTED_IMAGE_CERTIFICATE_IDS + if parsed_args.trusted_image_certs: + if not (image and not parsed_args.boot_from_volume): + msg = _( + '--trusted-image-cert option is only supported for ' + 'servers booted directly from images' + ) + raise exceptions.CommandError(msg) + if compute_client.api_version < api_versions.APIVersion('2.63'): + msg = _( + '--os-compute-api-version 2.63 or greater is required to ' + 'support the --trusted-image-cert option' + ) + raise exceptions.CommandError(msg) + + certs = parsed_args.trusted_image_certs + boot_kwargs['trusted_image_certificates'] = certs + LOG.debug('boot_args: %s', boot_args) LOG.debug('boot_kwargs: %s', boot_kwargs) @@ -3277,7 +3308,6 @@ def get_parser(self, prog_name): help=_( 'Trusted image certificate IDs used to validate certificates ' 'during the image signature verification process. ' - 'Defaults to env[OS_TRUSTED_IMAGE_CERTIFICATE_IDS]. ' 'May be specified multiple times to pass multiple trusted ' 'image certificate IDs. ' 'Cannot be specified with the --no-trusted-certs option. ' diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index cab9efd0c0..13431e005d 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -3624,6 +3624,156 @@ def test_server_create_with_hostname_pre_v290(self): exceptions.CommandError, self.cmd.take_action, parsed_args) + def test_server_create_with_trusted_image_cert(self): + self.app.client_manager.compute.api_version = \ + api_versions.APIVersion('2.63') + + arglist = [ + '--image', 'image1', + '--flavor', 'flavor1', + '--trusted-image-cert', 'foo', + '--trusted-image-cert', 'bar', + self.new_server.name, + ] + verifylist = [ + ('image', 'image1'), + ('flavor', 'flavor1'), + ('config_drive', False), + ('trusted_image_certs', ['foo', 'bar']), + ('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, + 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 + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) + self.assertFalse(self.images_mock.called) + 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') + + arglist = [ + '--image', 'image1', + '--flavor', 'flavor1', + '--trusted-image-cert', 'foo', + '--trusted-image-cert', 'bar', + self.new_server.name, + ] + verifylist = [ + ('image', 'image1'), + ('flavor', 'flavor1'), + ('config_drive', False), + ('trusted_image_certs', ['foo', 'bar']), + ('server_name', self.new_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_trusted_image_cert_from_volume(self): + self.app.client_manager.compute.api_version = \ + api_versions.APIVersion('2.63') + arglist = [ + '--volume', 'volume1', + '--flavor', 'flavor1', + '--trusted-image-cert', 'foo', + '--trusted-image-cert', 'bar', + self.new_server.name, + ] + verifylist = [ + ('volume', 'volume1'), + ('flavor', 'flavor1'), + ('config_drive', False), + ('trusted_image_certs', ['foo', 'bar']), + ('server_name', self.new_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_trusted_image_cert_from_snapshot(self): + self.app.client_manager.compute.api_version = \ + api_versions.APIVersion('2.63') + arglist = [ + '--snapshot', 'snapshot1', + '--flavor', 'flavor1', + '--trusted-image-cert', 'foo', + '--trusted-image-cert', 'bar', + self.new_server.name, + ] + verifylist = [ + ('snapshot', 'snapshot1'), + ('flavor', 'flavor1'), + ('config_drive', False), + ('trusted_image_certs', ['foo', 'bar']), + ('server_name', self.new_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_trusted_image_cert_boot_from_volume(self): + self.app.client_manager.compute.api_version = \ + api_versions.APIVersion('2.63') + arglist = [ + '--image', 'image1', + '--flavor', 'flavor1', + '--boot-from-volume', '1', + '--trusted-image-cert', 'foo', + '--trusted-image-cert', 'bar', + self.new_server.name, + ] + verifylist = [ + ('image', 'image1'), + ('flavor', 'flavor1'), + ('boot_from_volume', 1), + ('config_drive', False), + ('trusted_image_certs', ['foo', 'bar']), + ('server_name', self.new_server.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args) + class TestServerDelete(TestServer): diff --git a/releasenotes/notes/add-trusted-certs-option-server-create-a660488407300f22.yaml b/releasenotes/notes/add-trusted-certs-option-server-create-a660488407300f22.yaml new file mode 100644 index 0000000000..8814a63ae6 --- /dev/null +++ b/releasenotes/notes/add-trusted-certs-option-server-create-a660488407300f22.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Added ``--trusted-image-cert`` option for server create. It is available + only when directly booting server from image (not from volume, not from + snapshot and not via image converted to volume first). + This option is supported for Compute API version >=2.63 From abed9f20f5d9c1af3345456ec82d726e49db9f68 Mon Sep 17 00:00:00 2001 From: lsmman Date: Wed, 6 Oct 2021 19:21:51 +0900 Subject: [PATCH 025/706] Remove non-working code after method return. Delete duplicate return code. While adding return of a new Member type, the existing return code part is not deleted. Note the code in fakes.py in the below commit where these codes were added. - Project: python-openstackclient - The commit: 60e7c51df4cf061ebbb435a959ad63c7d3a296bf Change-Id: Iae44770a784732991962cd38472095f76ab2543f --- openstackclient/tests/unit/image/v2/fakes.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py index 516d563001..0d83f98b95 100644 --- a/openstackclient/tests/unit/image/v2/fakes.py +++ b/openstackclient/tests/unit/image/v2/fakes.py @@ -308,8 +308,3 @@ def create_one_image_member(attrs=None): image_member_info.update(attrs) return member.Member(**image_member_info) - - image_member = fakes.FakeModel( - copy.deepcopy(image_member_info)) - - return image_member From 70fed75c852d5a0518adbde14dad41b7579b04a5 Mon Sep 17 00:00:00 2001 From: choidoa-git Date: Wed, 6 Oct 2021 15:32:29 +0000 Subject: [PATCH 026/706] Update the Nova CLI decoder document In this patch, Update missing command in Mapping Guide. List of updated commands (Nova CLI / OSC) - server-migration-list / server migration list - server-migration-show / server migration show - live-migration-abort / server migration abort - live-migration-force-complete / server migration force complete - migration-list / server migration list - evacuate / server evacuate - flavor-access-add / flavor set --project - flavor-access-list / flavor show - flavor-access-remove / flavor unset - server-tag-add / server set --tag - server-tag-delete / server unset --tag - server-tag-delete-all / server unset --tag - server-tag-list / server list --tag - server-tag-set / server set --tag - quota-class-show / quota show --class Change-Id: Id1b4980fbc0f6e8e58bfae6f393f9336c6a7e3b1 --- doc/source/cli/data/nova.csv | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/source/cli/data/nova.csv b/doc/source/cli/data/nova.csv index 83911f1237..cc42fe5475 100644 --- a/doc/source/cli/data/nova.csv +++ b/doc/source/cli/data/nova.csv @@ -19,10 +19,10 @@ clear-password,server set --root-password,Clear the admin password for a server console-log,console log show,Get console log output of a server. delete,server delete,Immediately shut down and delete specified server(s). diagnostics,openstack server show --diagnostics,Retrieve server diagnostics. -evacuate,,Evacuate server from failed host. -flavor-access-add,,Add flavor access for the given tenant. -flavor-access-list,,Print access information about the given flavor. -flavor-access-remove,,Remove flavor access for the given tenant. +evacuate,server evacuate,Evacuate server from failed host. +flavor-access-add,flavor set --project,Add flavor access for the given tenant. +flavor-access-list,flavor show,Print access information about the given flavor. +flavor-access-remove,flavor unset,Remove flavor access for the given tenant. flavor-create,flavor create,Create a new flavor. flavor-delete,flavor delete,Delete a specific flavor flavor-key,flavor set / unset,Set or unset extra_spec for a flavor. @@ -59,15 +59,15 @@ keypair-show,keypair show,Show details about the given keypair. limits,limits show,Print rate and absolute limits. list,server list,List active servers. list-secgroup,security group list,List Security Group(s) of a server. -live-migration,,Migrate running server to a new machine. -live-migration-abort,,Abort an on-going live migration. -live-migration-force-comp,,Force on-going live migration to complete. +live-migration,server migration list,Migrate running server to a new machine. +live-migration-abort,server migration abort,Abort an on-going live migration. +live-migration-force-comp,server migration force complete,Force on-going live migration to complete. lock,server lock,Lock a server. meta,server set --property / unset,Set or delete metadata on a server. migrate,server migrate,Migrate a server. The new host will be selected by the scheduler. migration-list,,Print a list of migrations. pause,server pause,Pause a server. -quota-class-show,,List the quotas for a quota class. +quota-class-show,quota show --class,List the quotas for a quota class. quota-class-update,quota set --class,Update the quotas for a quota class. quota-defaults,quota list,List the default quotas for a tenant. quota-delete,quota set,Delete quota for a tenant/user so their quota will Revert back to default. @@ -89,13 +89,13 @@ server-group-create,server group create,Create a new server group with the speci server-group-delete,server group delete,Delete specific server group(s). server-group-get,server group show,Get a specific server group. server-group-list,server group list,Print a list of all server groups. -server-migration-list,,Get the migrations list of specified server. -server-migration-show,,Get the migration of specified server. -server-tag-add,,Add one or more tags to a server. -server-tag-delete,,Delete one or more tags from a server. -server-tag-delete-all,,Delete all tags from a server. -server-tag-list,,Get list of tags from a server. -server-tag-set,,Set list of tags to a server. +server-migration-list,server migration list,Get the migrations list of specified server. +server-migration-show,server migration show,Get the migration of specified server. +server-tag-add,server set --tag,Add one or more tags to a server. +server-tag-delete,server unset --tag,Delete one or more tags from a server. +server-tag-delete-all,server unset --tag,Delete all tags from a server. +server-tag-list,server list --tag,Get list of tags from a server. +server-tag-set,server set --tag,Set list of tags to a server. server-topology,openstack server show --topology,Retrieve server topology. (Supported by API versions '2.78' - '2.latest') [hint: use '-- os-compute-api-version' flag to show help message for proper version] service-delete,compute service delete,Delete the service. service-disable,compute service set --disable,Disable the service. From e06a4f1c20496c3acc4cdd0027d31d1cfe3485ea Mon Sep 17 00:00:00 2001 From: JIHOJU Date: Tue, 5 Oct 2021 09:13:57 +0900 Subject: [PATCH 027/706] Update the Nova CLI docoder document There are several update in CLI decoder document. - Change flavor set/unset to flavor set/unset --property - Update the mapping with flavor-update, interface-attach, and interface-detach Change-Id: I1db50188b3643d3fe28689dc73b3f63806defd29 --- doc/source/cli/data/nova.csv | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/source/cli/data/nova.csv b/doc/source/cli/data/nova.csv index 83911f1237..5141e86ea6 100644 --- a/doc/source/cli/data/nova.csv +++ b/doc/source/cli/data/nova.csv @@ -25,10 +25,10 @@ flavor-access-list,,Print access information about the given flavor. flavor-access-remove,,Remove flavor access for the given tenant. flavor-create,flavor create,Create a new flavor. flavor-delete,flavor delete,Delete a specific flavor -flavor-key,flavor set / unset,Set or unset extra_spec for a flavor. +flavor-key,flavor set / unset --property,Set or unset extra_spec for a flavor. flavor-list,flavor list,Print a list of available 'flavors' flavor-show,flavor show,Show details about the given flavor. -flavor-update,,Update the description of an existing flavor. (Supported by API versions '2.55' - '2.latest') [hint: use '--os-compute-api- version' flag to show help message for proper version] +flavor-update,flavor set --description,Update the description of an existing flavor. (Supported by API versions '2.55' - '2.latest') [hint: use '--os-compute-api-version' flag to show help message for proper version] force-delete,server delete,Force delete a server. get-mks-console,console url show --mks,Get an MKS console to a server. (Supported by API versions '2.8' - '2.latest') [hint: use ' --os-compute-api-version' flag to show help message for proper version] get-password,WONTFIX,Get the admin password for a server. This operation calls the metadata service to query metadata information and does not read password information from the server itself. @@ -49,8 +49,8 @@ image-create,server image create,Create a new image by taking a snapshot of a ru instance-action,,Show an action. instance-action-list,,List actions on a server. instance-usage-audit-log,,List/Get server usage audits. -interface-attach,,Attach a network interface to a server. -interface-detach,,Detach a network interface from a server. +interface-attach,server add port / server add floating ip / server add fixed ip,Attach a network interface to a server. +interface-detach,server remove port,Detach a network interface from a server. interface-list,port list --server,List interfaces attached to a server. keypair-add,keypair create,Create a new key pair for use with servers. keypair-delete,keypair delete,Delete keypair given by its name. From 53debe7fe1978f661768a27430f646a288948ecc Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 13 Oct 2021 10:18:53 +0100 Subject: [PATCH 028/706] compute: Fix filtering servers by tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nova API expects the 'tags' and 'not-tags' filters of the 'GET /servers' (list servers) API to be a CSV string [1]: tags (Optional) A list of tags to filter the server list by. Servers that match all tags in this list will be returned. Boolean expression in this case is 't1 AND t2'. Tags in query must be separated by comma. New in version 2.26 not-tags (Optional) A list of tags to filter the server list by. Servers that don’t match all tags in this list will be returned. Boolean expression in this case is 'NOT (t1 AND t2)'. Tags in query must be separated by comma. New in version 2.26 We were instead providing a Python list, which was simply being URL encoded. Correct this. [1] https://docs.openstack.org/api-ref/compute/?expanded=list-servers-detail#list-servers Change-Id: Ie0251a0dccdf3385089e5bbaedf646a5e928cc48 Signed-off-by: Stephen Finucane Closes-Bug: #1946816 --- openstackclient/compute/v2/server.py | 4 ++-- openstackclient/tests/unit/compute/v2/test_server.py | 4 ++-- releasenotes/notes/bug-1946816-7665858605453578.yaml | 6 ++++++ 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/bug-1946816-7665858605453578.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 08345243e9..c11f4b5781 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2257,7 +2257,7 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - search_opts['tags'] = parsed_args.tags + search_opts['tags'] = ','.join(parsed_args.tags) if parsed_args.not_tags: if compute_client.api_version < api_versions.APIVersion('2.26'): @@ -2267,7 +2267,7 @@ def take_action(self, parsed_args): ) raise exceptions.CommandError(msg) - search_opts['not-tags'] = parsed_args.not_tags + search_opts['not-tags'] = ','.join(parsed_args.not_tags) if parsed_args.locked: if compute_client.api_version < api_versions.APIVersion('2.73'): diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 13431e005d..3d8c17fdeb 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4489,7 +4489,7 @@ def test_server_list_with_tag(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.search_opts['tags'] = ['tag1', 'tag2'] + self.search_opts['tags'] = 'tag1,tag2' self.servers_mock.list.assert_called_with(**self.kwargs) @@ -4532,7 +4532,7 @@ def test_server_list_with_not_tag(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.search_opts['not-tags'] = ['tag1', 'tag2'] + self.search_opts['not-tags'] = 'tag1,tag2' self.servers_mock.list.assert_called_with(**self.kwargs) diff --git a/releasenotes/notes/bug-1946816-7665858605453578.yaml b/releasenotes/notes/bug-1946816-7665858605453578.yaml new file mode 100644 index 0000000000..f9e8406a9e --- /dev/null +++ b/releasenotes/notes/bug-1946816-7665858605453578.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Filtering servers by tags (``server list --tag``, + ``server list --not-tag``) now works correctly. + [Bug `1946816 `_] From a797c9d2a351fd87d2f8075bbf7180fc6b202d92 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 13 Oct 2021 12:14:30 +0100 Subject: [PATCH 029/706] tox: Ignore virtualenvs for pep8 environment Change-Id: I473d1b6c1287325566a5f5f5aadaea802c6af6f4 Signed-off-by: Stephen Finucane --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 8a386267da..6eefc26ece 100644 --- a/tox.ini +++ b/tox.ini @@ -133,7 +133,7 @@ commands = show-source = True # H203: Use assertIs(Not)None to check for None enable-extensions = H203 -exclude = .git,.tox,dist,doc,*lib/python*,*egg,build,tools +exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,releasenotes # W504 is disabled since you must choose between this or W503 ignore = W504 import-order-style = pep8 From 30612bf62295720a21d39c4f589cfcefb7a86a2f Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 8 Oct 2021 18:06:11 +0100 Subject: [PATCH 030/706] Remove 'get_osc_show_columns_for_sdk_resource' duplicates There were a number of 'get_osc_show_columns_for_sdk_resource' defined in-tree. However, osc-lib has provided this method for some time (since 2.2.0, June 2020 [1] - our minimum version is currently 2.3.0) so there's no need to provide our own copies. Remove them. [1] https://github.com/openstack/osc-lib/commit/29a0c5a5 Change-Id: I25695f4f9a379dd691b7eaa1e3247164668ae77e Signed-off-by: Stephen Finucane --- openstackclient/common/sdk_utils.py | 58 ----------------- openstackclient/image/v1/image.py | 13 ++-- openstackclient/image/v2/image.py | 7 +-- openstackclient/network/sdk_utils.py | 63 ------------------- openstackclient/network/v2/address_group.py | 4 +- openstackclient/network/v2/address_scope.py | 4 +- openstackclient/network/v2/floating_ip.py | 3 +- .../network/v2/floating_ip_port_forwarding.py | 5 +- openstackclient/network/v2/ip_availability.py | 3 +- .../network/v2/l3_conntrack_helper.py | 4 +- openstackclient/network/v2/network.py | 5 +- openstackclient/network/v2/network_agent.py | 4 +- .../v2/network_auto_allocated_topology.py | 3 +- openstackclient/network/v2/network_flavor.py | 4 +- .../network/v2/network_flavor_profile.py | 4 +- openstackclient/network/v2/network_meter.py | 3 +- .../network/v2/network_meter_rule.py | 3 +- .../network/v2/network_qos_policy.py | 4 +- .../network/v2/network_qos_rule.py | 4 +- .../network/v2/network_qos_rule_type.py | 3 +- openstackclient/network/v2/network_rbac.py | 4 +- openstackclient/network/v2/network_segment.py | 4 +- .../network/v2/network_segment_range.py | 3 +- openstackclient/network/v2/port.py | 4 +- openstackclient/network/v2/router.py | 4 +- openstackclient/network/v2/security_group.py | 3 +- .../network/v2/security_group_rule.py | 4 +- openstackclient/network/v2/subnet.py | 4 +- openstackclient/network/v2/subnet_pool.py | 3 +- .../tests/unit/network/test_sdk_utils.py | 59 ----------------- 30 files changed, 37 insertions(+), 256 deletions(-) delete mode 100644 openstackclient/common/sdk_utils.py delete mode 100644 openstackclient/network/sdk_utils.py delete mode 100644 openstackclient/tests/unit/network/test_sdk_utils.py diff --git a/openstackclient/common/sdk_utils.py b/openstackclient/common/sdk_utils.py deleted file mode 100644 index af9c74f944..0000000000 --- a/openstackclient/common/sdk_utils.py +++ /dev/null @@ -1,58 +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. - - -def get_osc_show_columns_for_sdk_resource( - sdk_resource, - osc_column_map, - invisible_columns=None -): - """Get and filter the display and attribute columns for an SDK resource. - - Common utility function for preparing the output of an OSC show command. - Some of the columns may need to get renamed, others made invisible. - - :param sdk_resource: An SDK resource - :param osc_column_map: A hash of mappings for display column names - :param invisible_columns: A list of invisible column names - - :returns: Two tuples containing the names of the display and attribute - columns - """ - - if getattr(sdk_resource, 'allow_get', None) is not None: - resource_dict = sdk_resource.to_dict( - body=True, headers=False, ignore_none=False) - else: - resource_dict = sdk_resource - - # Build the OSC column names to display for the SDK resource. - attr_map = {} - display_columns = list(resource_dict.keys()) - invisible_columns = [] if invisible_columns is None else invisible_columns - for col_name in invisible_columns: - if col_name in display_columns: - display_columns.remove(col_name) - for sdk_attr, osc_attr in osc_column_map.items(): - if sdk_attr in display_columns: - attr_map[osc_attr] = sdk_attr - display_columns.remove(sdk_attr) - if osc_attr not in display_columns: - display_columns.append(osc_attr) - sorted_display_columns = sorted(display_columns) - - # Build the SDK attribute names for the OSC column names. - attr_columns = [] - for column in sorted_display_columns: - new_column = attr_map[column] if column in attr_map else column - attr_columns.append(new_column) - return tuple(sorted_display_columns), tuple(attr_columns) diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 64aa3fcdab..43ccf5d212 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -28,7 +28,6 @@ from osc_lib.command import command from osc_lib import utils -from openstackclient.common import sdk_utils from openstackclient.i18n import _ if os.name == "nt": @@ -48,15 +47,17 @@ def _get_columns(item): - # Trick sdk_utils to return URI attribute column_map = { 'is_protected': 'protected', 'owner_id': 'owner' } - hidden_columns = ['location', 'checksum', - 'copy_from', 'created_at', 'status', 'updated_at'] - return sdk_utils.get_osc_show_columns_for_sdk_resource( - item.to_dict(), column_map, hidden_columns) + hidden_columns = [ + 'location', 'checksum', 'copy_from', 'created_at', 'status', + 'updated_at', + ] + return utils.get_osc_show_columns_for_sdk_resource( + item.to_dict(), column_map, hidden_columns, + ) _formatters = { diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index c1f46d2d99..becb54f456 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -31,7 +31,6 @@ from osc_lib import utils from openstackclient.common import progressbar -from openstackclient.common import sdk_utils from openstackclient.i18n import _ from openstackclient.identity import common @@ -99,13 +98,13 @@ def _format_image(image, human_readable=False): def _get_member_columns(item): - # Trick sdk_utils to return URI attribute column_map = { 'image_id': 'image_id' } hidden_columns = ['id', 'location', 'name'] - return sdk_utils.get_osc_show_columns_for_sdk_resource( - item.to_dict(), column_map, hidden_columns) + return utils.get_osc_show_columns_for_sdk_resource( + item.to_dict(), column_map, hidden_columns, + ) def get_data_file(args): diff --git a/openstackclient/network/sdk_utils.py b/openstackclient/network/sdk_utils.py deleted file mode 100644 index cff3071354..0000000000 --- a/openstackclient/network/sdk_utils.py +++ /dev/null @@ -1,63 +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 munch - - -def get_osc_show_columns_for_sdk_resource( - sdk_resource, - osc_column_map, - invisible_columns=None -): - """Get and filter the display and attribute columns for an SDK resource. - - Common utility function for preparing the output of an OSC show command. - Some of the columns may need to get renamed, others made invisible. - - :param sdk_resource: An SDK resource - :param osc_column_map: A hash of mappings for display column names - :param invisible_columns: A list of invisible column names - - :returns: Two tuples containing the names of the display and attribute - columns - """ - - if getattr(sdk_resource, 'allow_get', None) is not None: - resource_dict = sdk_resource.to_dict( - body=True, headers=False, ignore_none=False) - else: - resource_dict = sdk_resource - - # Build the OSC column names to display for the SDK resource. - attr_map = {} - display_columns = list(resource_dict.keys()) - for col_name in display_columns: - if isinstance(resource_dict[col_name], munch.Munch): - display_columns.remove(col_name) - invisible_columns = [] if invisible_columns is None else invisible_columns - for col_name in invisible_columns: - if col_name in display_columns: - display_columns.remove(col_name) - for sdk_attr, osc_attr in osc_column_map.items(): - if sdk_attr in display_columns: - attr_map[osc_attr] = sdk_attr - display_columns.remove(sdk_attr) - if osc_attr not in display_columns: - display_columns.append(osc_attr) - sorted_display_columns = sorted(display_columns) - - # Build the SDK attribute names for the OSC column names. - attr_columns = [] - for column in sorted_display_columns: - new_column = attr_map[column] if column in attr_map else column - attr_columns.append(new_column) - return tuple(sorted_display_columns), tuple(attr_columns) diff --git a/openstackclient/network/v2/address_group.py b/openstackclient/network/v2/address_group.py index fc83470053..9017047fac 100644 --- a/openstackclient/network/v2/address_group.py +++ b/openstackclient/network/v2/address_group.py @@ -23,8 +23,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils - LOG = logging.getLogger(__name__) @@ -33,7 +31,7 @@ def _get_columns(item): column_map = { 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _format_addresses(addresses): diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py index cd27678ee9..5748793a57 100644 --- a/openstackclient/network/v2/address_scope.py +++ b/openstackclient/network/v2/address_scope.py @@ -22,8 +22,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils - LOG = logging.getLogger(__name__) @@ -33,7 +31,7 @@ def _get_columns(item): 'is_shared': 'shared', 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_attrs(client_manager, parsed_args): diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index 25b2a1baf6..0951565cb6 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -19,7 +19,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils _formatters = { @@ -31,7 +30,7 @@ def _get_network_columns(item): column_map = { 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_columns(item): diff --git a/openstackclient/network/v2/floating_ip_port_forwarding.py b/openstackclient/network/v2/floating_ip_port_forwarding.py index 71b0b7da63..f137174cf1 100644 --- a/openstackclient/network/v2/floating_ip_port_forwarding.py +++ b/openstackclient/network/v2/floating_ip_port_forwarding.py @@ -12,6 +12,7 @@ # """Floating IP Port Forwarding action implementations""" + import logging from osc_lib.command import command @@ -20,8 +21,6 @@ from openstackclient.i18n import _ from openstackclient.network import common -from openstackclient.network import sdk_utils - LOG = logging.getLogger(__name__) @@ -30,7 +29,7 @@ def _get_columns(item): column_map = { 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) class CreateFloatingIPPortForwarding(command.ShowOne, diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py index ddc88e557e..6a3c67e21b 100644 --- a/openstackclient/network/v2/ip_availability.py +++ b/openstackclient/network/v2/ip_availability.py @@ -19,7 +19,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common -from openstackclient.network import sdk_utils _formatters = { 'subnet_ip_availability': format_columns.ListDictColumn, @@ -30,7 +29,7 @@ def _get_columns(item): column_map = { 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) # TODO(ankur-gupta-f): Use the SDK resource mapped attribute names once diff --git a/openstackclient/network/v2/l3_conntrack_helper.py b/openstackclient/network/v2/l3_conntrack_helper.py index 94788823ab..9fc33d8f15 100644 --- a/openstackclient/network/v2/l3_conntrack_helper.py +++ b/openstackclient/network/v2/l3_conntrack_helper.py @@ -20,15 +20,13 @@ from osc_lib import utils from openstackclient.i18n import _ -from openstackclient.network import sdk_utils - LOG = logging.getLogger(__name__) def _get_columns(item): column_map = {} - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_attrs(client, parsed_args): diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index b8eb9f014b..191e4aa8e2 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -21,7 +21,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils class AdminStateColumn(cliff_columns.FormattableColumn): @@ -62,14 +61,14 @@ def _get_columns_network(item): 'tenant_id': 'project_id', 'tags': 'tags', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_columns_compute(item): column_map = { 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_attrs_network(client_manager, parsed_args): diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py index 1678485434..c995e36cb8 100644 --- a/openstackclient/network/v2/network_agent.py +++ b/openstackclient/network/v2/network_agent.py @@ -22,8 +22,6 @@ from osc_lib import utils from openstackclient.i18n import _ -from openstackclient.network import sdk_utils - LOG = logging.getLogger(__name__) @@ -52,7 +50,7 @@ def _get_network_columns(item): 'is_admin_state_up': 'admin_state_up', 'is_alive': 'alive', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) class AddNetworkToAgent(command.Command): diff --git a/openstackclient/network/v2/network_auto_allocated_topology.py b/openstackclient/network/v2/network_auto_allocated_topology.py index 36f392006e..7b7df4d75c 100644 --- a/openstackclient/network/v2/network_auto_allocated_topology.py +++ b/openstackclient/network/v2/network_auto_allocated_topology.py @@ -20,7 +20,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common -from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) @@ -29,7 +28,7 @@ def _get_columns(item): column_map = { 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _format_check_resource_columns(): diff --git a/openstackclient/network/v2/network_flavor.py b/openstackclient/network/v2/network_flavor.py index 9e758ae29c..6e3a5a0432 100644 --- a/openstackclient/network/v2/network_flavor.py +++ b/openstackclient/network/v2/network_flavor.py @@ -22,8 +22,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils - LOG = logging.getLogger(__name__) @@ -34,7 +32,7 @@ def _get_columns(item): 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_attrs(client_manager, parsed_args): diff --git a/openstackclient/network/v2/network_flavor_profile.py b/openstackclient/network/v2/network_flavor_profile.py index 0212e0d9ba..df7cfb7430 100644 --- a/openstackclient/network/v2/network_flavor_profile.py +++ b/openstackclient/network/v2/network_flavor_profile.py @@ -20,8 +20,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils - LOG = logging.getLogger(__name__) @@ -32,7 +30,7 @@ def _get_columns(item): 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_attrs(client_manager, parsed_args): diff --git a/openstackclient/network/v2/network_meter.py b/openstackclient/network/v2/network_meter.py index f8f188a806..8b63de2c7a 100644 --- a/openstackclient/network/v2/network_meter.py +++ b/openstackclient/network/v2/network_meter.py @@ -22,7 +22,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) @@ -32,7 +31,7 @@ def _get_columns(item): 'is_shared': 'shared', 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_attrs(client_manager, parsed_args): diff --git a/openstackclient/network/v2/network_meter_rule.py b/openstackclient/network/v2/network_meter_rule.py index 06362fa14c..4117d04342 100644 --- a/openstackclient/network/v2/network_meter_rule.py +++ b/openstackclient/network/v2/network_meter_rule.py @@ -22,7 +22,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) @@ -31,7 +30,7 @@ def _get_columns(item): column_map = { 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_attrs(client_manager, parsed_args): diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py index 7300a5c0ac..8d4312484b 100644 --- a/openstackclient/network/v2/network_qos_policy.py +++ b/openstackclient/network/v2/network_qos_policy.py @@ -22,8 +22,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils - LOG = logging.getLogger(__name__) @@ -33,7 +31,7 @@ def _get_columns(item): 'is_shared': 'shared', 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_attrs(client_manager, parsed_args): diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py index f30a5aeb1f..4bf72d269a 100644 --- a/openstackclient/network/v2/network_qos_rule.py +++ b/openstackclient/network/v2/network_qos_rule.py @@ -21,8 +21,6 @@ from openstackclient.i18n import _ from openstackclient.network import common -from openstackclient.network import sdk_utils - RULE_TYPE_BANDWIDTH_LIMIT = 'bandwidth-limit' RULE_TYPE_DSCP_MARKING = 'dscp-marking' @@ -51,7 +49,7 @@ def _get_columns(item): column_map = { 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _check_type_parameters(attrs, type, is_create): diff --git a/openstackclient/network/v2/network_qos_rule_type.py b/openstackclient/network/v2/network_qos_rule_type.py index 7b92c8ad4b..036b682fae 100644 --- a/openstackclient/network/v2/network_qos_rule_type.py +++ b/openstackclient/network/v2/network_qos_rule_type.py @@ -17,7 +17,6 @@ from osc_lib import utils from openstackclient.i18n import _ -from openstackclient.network import sdk_utils def _get_columns(item): @@ -26,7 +25,7 @@ def _get_columns(item): "drivers": "drivers", } invisible_columns = ["id", "name"] - return sdk_utils.get_osc_show_columns_for_sdk_resource( + return utils.get_osc_show_columns_for_sdk_resource( item, column_map, invisible_columns) diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py index 692a43857d..edca872cf2 100644 --- a/openstackclient/network/v2/network_rbac.py +++ b/openstackclient/network/v2/network_rbac.py @@ -22,8 +22,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils - LOG = logging.getLogger(__name__) @@ -33,7 +31,7 @@ def _get_columns(item): 'target_tenant': 'target_project_id', 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_attrs(client_manager, parsed_args): diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py index 14a8edabe5..e18ac47529 100644 --- a/openstackclient/network/v2/network_segment.py +++ b/openstackclient/network/v2/network_segment.py @@ -21,14 +21,12 @@ from openstackclient.i18n import _ from openstackclient.network import common -from openstackclient.network import sdk_utils - LOG = logging.getLogger(__name__) def _get_columns(item): - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {}) + return utils.get_osc_show_columns_for_sdk_resource(item, {}) class CreateNetworkSegment(command.ShowOne, diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py index ee414407ee..e105111dd0 100644 --- a/openstackclient/network/v2/network_segment_range.py +++ b/openstackclient/network/v2/network_segment_range.py @@ -26,14 +26,13 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) def _get_columns(item): - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {}) + return utils.get_osc_show_columns_for_sdk_resource(item, {}) def _get_ranges(item): diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index ecb2382a85..132c384a0e 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -29,8 +29,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils - LOG = logging.getLogger(__name__) @@ -67,7 +65,7 @@ def _get_columns(item): 'is_port_security_enabled': 'port_security_enabled', 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) class JSONKeyValueAction(argparse.Action): diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index d15300a0b7..dde4eda99f 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -28,8 +28,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils - LOG = logging.getLogger(__name__) @@ -83,7 +81,7 @@ def _get_columns(item): if item.is_distributed is None: invisible_columns.append('is_distributed') column_map.pop('is_distributed') - return sdk_utils.get_osc_show_columns_for_sdk_resource( + return utils.get_osc_show_columns_for_sdk_resource( item, column_map, invisible_columns) diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index 49dc14e403..37d2dc5be0 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -23,7 +23,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils from openstackclient.network import utils as network_utils @@ -90,7 +89,7 @@ def _get_columns(item): 'security_group_rules': 'rules', 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) # TODO(abhiraut): Use the SDK resource mapped attribute names once the diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index e273ded3d6..252dcb05a8 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -23,10 +23,8 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils from openstackclient.network import utils as network_utils - LOG = logging.getLogger(__name__) @@ -76,7 +74,7 @@ def _get_columns(item): column_map = { 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _convert_to_lowercase(string): diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 09fd7c7c39..c07fab4170 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -27,8 +27,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils - LOG = logging.getLogger(__name__) @@ -143,7 +141,7 @@ def _get_columns(item): } # Do not show this column when displaying a subnet invisible_columns = ['use_default_subnet_pool', 'prefix_length'] - return sdk_utils.get_osc_show_columns_for_sdk_resource( + return utils.get_osc_show_columns_for_sdk_resource( item, column_map, invisible_columns=invisible_columns diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index bdf7aba805..6b88888c0e 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -25,7 +25,6 @@ from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common -from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) @@ -39,7 +38,7 @@ def _get_columns(item): 'minimum_prefix_length': 'min_prefixlen', 'tenant_id': 'project_id', } - return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) + return utils.get_osc_show_columns_for_sdk_resource(item, column_map) _formatters = { diff --git a/openstackclient/tests/unit/network/test_sdk_utils.py b/openstackclient/tests/unit/network/test_sdk_utils.py deleted file mode 100644 index d1efa7e40a..0000000000 --- a/openstackclient/tests/unit/network/test_sdk_utils.py +++ /dev/null @@ -1,59 +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.network import sdk_utils -from openstackclient.tests.unit import utils as tests_utils - - -class TestSDKUtils(tests_utils.TestCase): - - def setUp(self): - super(TestSDKUtils, self).setUp() - - def _test_get_osc_show_columns_for_sdk_resource( - self, sdk_resource, column_map, - expected_display_columns, expected_attr_columns): - display_columns, attr_columns = \ - sdk_utils.get_osc_show_columns_for_sdk_resource( - sdk_resource, column_map) - self.assertEqual(expected_display_columns, display_columns) - self.assertEqual(expected_attr_columns, attr_columns) - - def test_get_osc_show_columns_for_sdk_resource_empty(self): - self._test_get_osc_show_columns_for_sdk_resource( - {}, {}, tuple(), tuple()) - - def test_get_osc_show_columns_for_sdk_resource_empty_map(self): - self._test_get_osc_show_columns_for_sdk_resource( - {'foo': 'foo1'}, {}, - ('foo',), ('foo',)) - - def test_get_osc_show_columns_for_sdk_resource_empty_data(self): - self._test_get_osc_show_columns_for_sdk_resource( - {}, {'foo': 'foo_map'}, - ('foo_map',), ('foo_map',)) - - def test_get_osc_show_columns_for_sdk_resource_map(self): - self._test_get_osc_show_columns_for_sdk_resource( - {'foo': 'foo1'}, {'foo': 'foo_map'}, - ('foo_map',), ('foo',)) - - def test_get_osc_show_columns_for_sdk_resource_map_dup(self): - self._test_get_osc_show_columns_for_sdk_resource( - {'foo': 'foo1', 'foo_map': 'foo1'}, {'foo': 'foo_map'}, - ('foo_map',), ('foo',)) - - def test_get_osc_show_columns_for_sdk_resource_map_full(self): - self._test_get_osc_show_columns_for_sdk_resource( - {'foo': 'foo1', 'bar': 'bar1'}, - {'foo': 'foo_map', 'new': 'bar'}, - ('bar', 'foo_map'), ('bar', 'foo')) From 728401bbd76afc4d31b4f22e44bf98d1de40ef46 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 8 Oct 2021 18:20:31 +0100 Subject: [PATCH 031/706] Remove remnants of 'six' Just one entry left. Remove it. Change-Id: Ia12173ecb7f3fed4a1195a46ebf9b096d917b3b6 Signed-off-by: Stephen Finucane --- lower-constraints.txt | 1 - openstackclient/tests/unit/common/test_progressbar.py | 11 +++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lower-constraints.txt b/lower-constraints.txt index 861749b738..b98b8432a9 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -81,7 +81,6 @@ requestsexceptions==1.2.0 rfc3986==0.3.1 Routes==2.3.1 simplejson==3.5.1 -six==1.15.0 statsd==3.2.1 stestr==1.0.0 stevedore==2.0.1 diff --git a/openstackclient/tests/unit/common/test_progressbar.py b/openstackclient/tests/unit/common/test_progressbar.py index 7bc0b6baf4..a624fc438a 100644 --- a/openstackclient/tests/unit/common/test_progressbar.py +++ b/openstackclient/tests/unit/common/test_progressbar.py @@ -11,10 +11,9 @@ # under the License. # +import io import sys -import six - from openstackclient.common import progressbar from openstackclient.tests.unit import utils @@ -23,7 +22,7 @@ class TestProgressBarWrapper(utils.TestCase): def test_iter_file_display_progress_bar(self): size = 98304 - file_obj = six.StringIO('X' * size) + file_obj = io.StringIO('X' * size) saved_stdout = sys.stdout try: sys.stdout = output = FakeTTYStdout() @@ -41,7 +40,7 @@ def test_iter_file_display_progress_bar(self): def test_iter_file_no_tty(self): size = 98304 - file_obj = six.StringIO('X' * size) + file_obj = io.StringIO('X' * size) saved_stdout = sys.stdout try: sys.stdout = output = FakeNoTTYStdout() @@ -56,7 +55,7 @@ def test_iter_file_no_tty(self): sys.stdout = saved_stdout -class FakeTTYStdout(six.StringIO): +class FakeTTYStdout(io.StringIO): """A Fake stdout that try to emulate a TTY device as much as possible.""" def isatty(self): @@ -67,7 +66,7 @@ def write(self, data): if data.startswith('\r'): self.seek(0) data = data[1:] - return six.StringIO.write(self, data) + return io.StringIO.write(self, data) class FakeNoTTYStdout(FakeTTYStdout): From 43639e1118a757fd1a00d9ea999db43133c40763 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Tue, 26 Oct 2021 15:53:35 +0200 Subject: [PATCH 032/706] Fix typos Change-Id: Idd502c8df21da79ff3b9339870f38378f5337879 --- openstackclient/common/progressbar.py | 2 +- openstackclient/common/quota.py | 4 ++-- openstackclient/compute/v2/server.py | 4 ++-- openstackclient/identity/v3/endpoint_group.py | 2 +- openstackclient/tests/functional/base.py | 4 ++-- .../tests/functional/volume/v1/test_volume_type.py | 4 ++-- .../tests/functional/volume/v2/test_volume_type.py | 4 ++-- .../tests/functional/volume/v3/test_volume_type.py | 4 ++-- openstackclient/tests/unit/compute/v2/fakes.py | 4 ++-- openstackclient/volume/v1/volume_type.py | 2 +- openstackclient/volume/v2/volume_snapshot.py | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/openstackclient/common/progressbar.py b/openstackclient/common/progressbar.py index ef767a9cf8..7678aceba0 100644 --- a/openstackclient/common/progressbar.py +++ b/openstackclient/common/progressbar.py @@ -17,7 +17,7 @@ class _ProgressBarBase(object): - """A progress bar provider for a wrapped obect. + """A progress bar provider for a wrapped object. Base abstract class used by specific class wrapper to show a progress bar when the wrapped object are consumed. diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 643cb4e474..00bbc51459 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -205,7 +205,7 @@ def get_network_quota(self, parsed_args): class ListQuota(command.Lister, BaseQuota): _description = _( "List quotas for all projects with non-default quota values or " - "list detailed quota informations for requested project") + "list detailed quota information for requested project") def _get_detailed_quotas(self, parsed_args): columns = ( @@ -230,7 +230,7 @@ def _get_detailed_quotas(self, parsed_args): result = [] for resource, values in quotas.items(): # NOTE(slaweq): there is no detailed quotas info for some resources - # and it should't be displayed here + # and it shouldn't be displayed here if type(values) is dict: result.append({ 'resource': resource, diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index c11f4b5781..b0de65a7c3 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2466,7 +2466,7 @@ def take_action(self, parsed_args): # and 'image' missing in the server response during # infrastructure failure situations. # For those servers with partial constructs we just skip the - # processing of the image and flavor informations. + # processing of the image and flavor information. if not hasattr(s, 'image') or not hasattr(s, 'flavor'): continue if 'id' in s.image: @@ -4292,7 +4292,7 @@ def _show_progress(progress): server_obj.shelve() - # if we don't hav to wait, either because it was requested explicitly + # if we don't have to wait, either because it was requested explicitly # or is required implicitly, then our job is done if not parsed_args.wait and not parsed_args.offload: return diff --git a/openstackclient/identity/v3/endpoint_group.py b/openstackclient/identity/v3/endpoint_group.py index cbe27edb4b..9bb026a9b8 100644 --- a/openstackclient/identity/v3/endpoint_group.py +++ b/openstackclient/identity/v3/endpoint_group.py @@ -268,7 +268,7 @@ def get_parser(self, prog_name): parser.add_argument( '--name', metavar='', - help=_('New enpoint group name'), + help=_('New endpoint group name'), ) parser.add_argument( '--filters', diff --git a/openstackclient/tests/functional/base.py b/openstackclient/tests/functional/base.py index 0ed7dff8c4..3a4c13944e 100644 --- a/openstackclient/tests/functional/base.py +++ b/openstackclient/tests/functional/base.py @@ -42,7 +42,7 @@ class TestCase(testtools.TestCase): def openstack(cls, cmd, cloud=ADMIN_CLOUD, fail_ok=False): """Executes openstackclient command for the given action - NOTE(dtroyer): There is a subtle distinction between pasing + NOTE(dtroyer): There is a subtle distinction between passing cloud=None and cloud='': for compatibility reasons passing cloud=None continues to include the option '--os-auth-type none' in the command while passing cloud='' omits the '--os-auth-type' @@ -61,7 +61,7 @@ def openstack(cls, cmd, cloud=ADMIN_CLOUD, fail_ok=False): fail_ok=fail_ok ) else: - # Execure command with an explicit cloud specified + # Execute command with an explicit cloud specified return execute( 'openstack --os-cloud=' + cloud + ' ' + cmd, fail_ok=fail_ok diff --git a/openstackclient/tests/functional/volume/v1/test_volume_type.py b/openstackclient/tests/functional/volume/v1/test_volume_type.py index fb8dabdb04..7434b5b36b 100644 --- a/openstackclient/tests/functional/volume/v1/test_volume_type.py +++ b/openstackclient/tests/functional/volume/v1/test_volume_type.py @@ -118,10 +118,10 @@ def test_multi_delete(self): raw_output = self.openstack(cmd) self.assertOutput('', raw_output) - # NOTE: Add some basic funtional tests with the old format to + # 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 tye commands. + # volume type commands. def test_encryption_type(self): encryption_type = uuid.uuid4().hex # test create new encryption type diff --git a/openstackclient/tests/functional/volume/v2/test_volume_type.py b/openstackclient/tests/functional/volume/v2/test_volume_type.py index 3f1a6ea8a5..861c393d80 100644 --- a/openstackclient/tests/functional/volume/v2/test_volume_type.py +++ b/openstackclient/tests/functional/volume/v2/test_volume_type.py @@ -139,10 +139,10 @@ def test_multi_delete(self): raw_output = self.openstack(cmd) self.assertOutput('', raw_output) - # NOTE: Add some basic funtional tests with the old format to + # 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 tye commands. + # volume type commands. def test_encryption_type(self): name = uuid.uuid4().hex encryption_type = uuid.uuid4().hex diff --git a/openstackclient/tests/functional/volume/v3/test_volume_type.py b/openstackclient/tests/functional/volume/v3/test_volume_type.py index 79d4096998..165c625c20 100644 --- a/openstackclient/tests/functional/volume/v3/test_volume_type.py +++ b/openstackclient/tests/functional/volume/v3/test_volume_type.py @@ -139,10 +139,10 @@ def test_multi_delete(self): raw_output = self.openstack(cmd) self.assertOutput('', raw_output) - # NOTE: Add some basic funtional tests with the old format to + # 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 tye commands. + # volume type commands. def test_encryption_type(self): name = uuid.uuid4().hex encryption_type = uuid.uuid4().hex diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 3142a24489..fd0a570969 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -1285,7 +1285,7 @@ def create_one_server_group(attrs=None): class FakeServerGroupV264(object): - """Fake one server group fo API >= 2.64""" + """Fake one server group for API >= 2.64""" @staticmethod def create_one_server_group(attrs=None): @@ -1400,7 +1400,7 @@ def create_one_comp_quota(attrs=None): @staticmethod def create_one_default_comp_quota(attrs=None): - """Crate one quota""" + """Create one quota""" attrs = attrs or {} diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py index 4f015d13f6..c584943e10 100644 --- a/openstackclient/volume/v1/volume_type.py +++ b/openstackclient/volume/v1/volume_type.py @@ -411,7 +411,7 @@ def get_parser(self, prog_name): "--encryption-type", action="store_true", help=_("Remove the encryption type for this volume type " - "(admin oly)"), + "(admin only)"), ) return parser diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py index 656f59d4ac..53d8d27fed 100644 --- a/openstackclient/volume/v2/volume_snapshot.py +++ b/openstackclient/volume/v2/volume_snapshot.py @@ -98,7 +98,7 @@ def get_parser(self, prog_name): "--remote-source", metavar="", action=parseractions.KeyValueAction, - help=_("The attribute(s) of the exsiting remote volume snapshot " + 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'"), From 57aad01886fe9d98210496a92d517aa067c049a1 Mon Sep 17 00:00:00 2001 From: Diwei Zhu Date: Tue, 26 Oct 2021 14:47:46 +0000 Subject: [PATCH 033/706] Switch server backup to sdk. Switch this command from novaclient to SDK. As this is the first command related to server that we are migrating, we need to extend our test fakes to support fake Server resources. The extended fakes will replace the old ones once all commands related to server are switched. Change-Id: If476fb1614a64320ed071bbda35e941bf3290a2e --- openstackclient/compute/v2/server_backup.py | 9 +- .../tests/unit/compute/v2/fakes.py | 150 ++++++++++++------ .../unit/compute/v2/test_server_backup.py | 23 ++- ...server-backup-to-sdk-0f170baf38e98b40.yaml | 4 + 4 files changed, 119 insertions(+), 67 deletions(-) create mode 100644 releasenotes/notes/migrate-server-backup-to-sdk-0f170baf38e98b40.yaml diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py index b1b821b24e..53891991b4 100644 --- a/openstackclient/compute/v2/server_backup.py +++ b/openstackclient/compute/v2/server_backup.py @@ -72,12 +72,9 @@ def _show_progress(progress): self.app.stderr.write('\rProgress: %s' % progress) self.app.stderr.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) # Set sane defaults as this API wants all mouths to be fed if parsed_args.name is None: @@ -93,7 +90,7 @@ def _show_progress(progress): else: backup_rotation = parsed_args.rotate - compute_client.servers.backup( + compute_client.backup_server( server.id, backup_name, backup_type, diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 3142a24489..23468ebc4d 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -20,6 +20,7 @@ from novaclient import api_versions from openstack.compute.v2 import flavor as _flavor +from openstack.compute.v2 import server from openstackclient.api import compute_v2 from openstackclient.tests.unit import fakes @@ -73,7 +74,7 @@ class FakeAggregate(object): def create_one_aggregate(attrs=None): """Create a fake aggregate. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with id and other attributes @@ -104,7 +105,7 @@ def create_one_aggregate(attrs=None): def create_aggregates(attrs=None, count=2): """Create multiple fake aggregates. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of aggregates to fake @@ -255,7 +256,7 @@ class FakeAgent(object): def create_one_agent(attrs=None): """Create a fake agent. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with agent_id, os, and so on @@ -285,7 +286,7 @@ def create_one_agent(attrs=None): def create_agents(attrs=None, count=2): """Create multiple fake agents. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of agents to fake @@ -306,7 +307,7 @@ class FakeExtension(object): def create_one_extension(attrs=None): """Create a fake extension. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object with name, namespace, etc. @@ -342,7 +343,7 @@ class FakeHypervisor(object): def create_one_hypervisor(attrs=None): """Create a fake hypervisor. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with id, hypervisor_hostname, and so on @@ -390,7 +391,7 @@ def create_one_hypervisor(attrs=None): def create_hypervisors(attrs=None, count=2): """Create multiple fake hypervisors. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of hypervisors to fake @@ -411,7 +412,7 @@ class FakeHypervisorStats(object): def create_one_hypervisor_stats(attrs=None): """Create a fake hypervisor stats. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with count, current_workload, and so on @@ -450,7 +451,7 @@ def create_one_hypervisor_stats(attrs=None): def create_hypervisors_stats(attrs=None, count=2): """Create multiple fake hypervisors stats. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of hypervisors to fake @@ -472,7 +473,7 @@ class FakeSecurityGroup(object): def create_one_security_group(attrs=None): """Create a fake security group. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with id, name, etc. @@ -496,7 +497,7 @@ def create_one_security_group(attrs=None): def create_security_groups(attrs=None, count=2): """Create multiple fake security groups. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of security groups to fake @@ -537,7 +538,7 @@ class FakeSecurityGroupRule(object): def create_one_security_group_rule(attrs=None): """Create a fake security group rule. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with id, etc. @@ -564,7 +565,7 @@ def create_one_security_group_rule(attrs=None): def create_security_group_rules(attrs=None, count=2): """Create multiple fake security group rules. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of security group rules to fake @@ -586,9 +587,9 @@ class FakeServer(object): def create_one_server(attrs=None, methods=None): """Create a fake server. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes - :param Dictionary methods: + :param dict methods: A dictionary with all methods :return: A FakeResource object, with id, name, metadata, and so on @@ -622,9 +623,9 @@ def create_one_server(attrs=None, methods=None): def create_servers(attrs=None, methods=None, count=2): """Create multiple fake servers. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes - :param Dictionary methods: + :param dict methods: A dictionary with all methods :param int count: The number of servers to fake @@ -637,6 +638,59 @@ def create_servers(attrs=None, methods=None, count=2): return servers + @staticmethod + def create_one_sdk_server(attrs=None, methods=None): + """Create a fake server for testing migration to sdk + + :param dict attrs: + A dictionary with all attributes + :param dict methods: + A dictionary with all methods + :return: + A openstack.compute.v2.server.Server 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) + return server.Server(**server_info) + + @staticmethod + def create_sdk_servers(attrs=None, methods=None, count=2): + """Create multiple fake servers for testing migration to sdk + + :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 openstack.compute.v2.server.Server objects + faking the servers + """ + servers = [] + for i in range(0, count): + servers.append(FakeServer.create_one_sdk_server(attrs, methods)) + + return servers + @staticmethod def get_servers(servers=None, count=2): """Get an iterable MagicMock object with a list of faked servers. @@ -705,7 +759,7 @@ class FakeService(object): def create_one_service(attrs=None): """Create a fake service. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with id, host, binary, and so on @@ -738,7 +792,7 @@ def create_one_service(attrs=None): def create_services(attrs=None, count=2): """Create multiple fake services. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of services to fake @@ -759,7 +813,7 @@ class FakeFlavor(object): def create_one_flavor(attrs=None): """Create a fake flavor. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with id, name, ram, vcpus, and so on @@ -793,7 +847,7 @@ def create_one_flavor(attrs=None): def create_flavors(attrs=None, count=2): """Create multiple fake flavors. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of flavors to fake @@ -833,7 +887,7 @@ class FakeFlavorAccess(object): def create_one_flavor_access(attrs=None): """Create a fake flavor access. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with flavor_id, tenat_id @@ -862,7 +916,7 @@ class FakeKeypair(object): def create_one_keypair(attrs=None, no_pri=False): """Create a fake keypair - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, name, fingerprint, and so on @@ -892,7 +946,7 @@ def create_one_keypair(attrs=None, no_pri=False): def create_keypairs(attrs=None, count=2): """Create multiple fake keypairs. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of keypairs to fake @@ -933,7 +987,7 @@ class FakeAvailabilityZone(object): def create_one_availability_zone(attrs=None): """Create a fake AZ. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object with zoneName, zoneState, etc. @@ -966,7 +1020,7 @@ def create_one_availability_zone(attrs=None): def create_availability_zones(attrs=None, count=2): """Create multiple fake AZs. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of AZs to fake @@ -989,7 +1043,7 @@ class FakeFloatingIP(object): def create_one_floating_ip(attrs=None): """Create a fake floating ip. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with id, ip, and so on @@ -1014,7 +1068,7 @@ def create_one_floating_ip(attrs=None): def create_floating_ips(attrs=None, count=2): """Create multiple fake floating ips. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of floating ips to fake @@ -1053,7 +1107,7 @@ class FakeFloatingIPPool(object): def create_one_floating_ip_pool(attrs=None): """Create a fake floating ip pool. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with name, etc @@ -1075,7 +1129,7 @@ def create_one_floating_ip_pool(attrs=None): def create_floating_ip_pools(attrs=None, count=2): """Create multiple fake floating ip pools. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of floating ip pools to fake @@ -1097,7 +1151,7 @@ class FakeNetwork(object): def create_one_network(attrs=None): """Create a fake network. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with id, label, cidr and so on @@ -1149,7 +1203,7 @@ def create_one_network(attrs=None): def create_networks(attrs=None, count=2): """Create multiple fake networks. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of networks to fake @@ -1189,7 +1243,7 @@ class FakeHost(object): def create_one_host(attrs=None): """Create a fake host. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with uuid and other attributes @@ -1243,7 +1297,7 @@ class FakeServerGroup(object): def _create_one_server_group(attrs=None): """Create a fake server group - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with id and other attributes @@ -1273,7 +1327,7 @@ def _create_one_server_group(attrs=None): def create_one_server_group(attrs=None): """Create a fake server group - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with id and other attributes @@ -1291,7 +1345,7 @@ class FakeServerGroupV264(object): def create_one_server_group(attrs=None): """Create a fake server group - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with id and other attributes @@ -1309,7 +1363,7 @@ class FakeUsage(object): def create_one_usage(attrs=None): """Create a fake usage. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with tenant_id and other attributes @@ -1351,7 +1405,7 @@ def create_one_usage(attrs=None): def create_usages(attrs=None, count=2): """Create multiple fake services. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of services to fake @@ -1575,9 +1629,9 @@ class FakeMigration(object): def create_one_migration(attrs=None, methods=None): """Create a fake migration. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes - :param Dictionary methods: + :param dict methods: A dictionary with all methods :return: A FakeResource object, with id, type, and so on @@ -1617,9 +1671,9 @@ def create_one_migration(attrs=None, methods=None): def create_migrations(attrs=None, methods=None, count=2): """Create multiple fake migrations. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes - :param Dictionary methods: + :param dict methods: A dictionary with all methods :param int count: The number of migrations to fake @@ -1642,9 +1696,9 @@ class FakeServerMigration(object): def create_one_server_migration(attrs=None, methods=None): """Create a fake server migration. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes - :param Dictionary methods: + :param dict methods: A dictionary with all methods :return: A FakeResource object, with id, type, and so on @@ -1695,9 +1749,9 @@ class FakeVolumeAttachment(object): def create_one_volume_attachment(attrs=None, methods=None): """Create a fake volume attachment. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes - :param Dictionary methods: + :param dict methods: A dictionary with all methods :return: A FakeResource object, with id, device, and so on @@ -1733,9 +1787,9 @@ def create_one_volume_attachment(attrs=None, methods=None): def create_volume_attachments(attrs=None, methods=None, count=2): """Create multiple fake volume attachments (BDMs). - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes - :param Dictionary methods: + :param dict methods: A dictionary with all methods :param int count: The number of volume attachments to fake diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py index 0012d70062..1644baae00 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_backup.py +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -28,8 +28,9 @@ def setUp(self): super(TestServerBackup, self).setUp() # Get a shortcut to the compute client ServerManager Mock - 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 # Get a shortcut to the image client ImageManager Mock self.images_mock = self.app.client_manager.image @@ -42,14 +43,14 @@ def setUp(self): self.methods = {} def setup_servers_mock(self, count): - servers = compute_fakes.FakeServer.create_servers( + servers = compute_fakes.FakeServer.create_sdk_servers( attrs=self.attrs, methods=self.methods, count=count, ) - # This is the return value for utils.find_resource() - self.servers_mock.get = compute_fakes.FakeServer.get_servers( + # This is the return value for compute_client.find_server() + self.sdk_client.find_server = compute_fakes.FakeServer.get_servers( servers, 0, ) @@ -130,8 +131,7 @@ def test_server_backup_defaults(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # ServerManager.backup(server, backup_name, backup_type, rotation) - self.servers_mock.backup.assert_called_with( + self.sdk_client.backup_server.assert_called_with( servers[0].id, servers[0].name, '', @@ -164,8 +164,7 @@ def test_server_backup_create_options(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # ServerManager.backup(server, backup_name, backup_type, rotation) - self.servers_mock.backup.assert_called_with( + self.sdk_client.backup_server.assert_called_with( servers[0].id, 'image', 'daily', @@ -212,8 +211,7 @@ def test_server_backup_wait_fail(self, mock_wait_for_status): parsed_args, ) - # ServerManager.backup(server, backup_name, backup_type, rotation) - self.servers_mock.backup.assert_called_with( + self.sdk_client.backup_server.assert_called_with( servers[0].id, 'image', 'daily', @@ -254,8 +252,7 @@ def test_server_backup_wait_ok(self, mock_wait_for_status): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # ServerManager.backup(server, backup_name, backup_type, rotation) - self.servers_mock.backup.assert_called_with( + self.sdk_client.backup_server.assert_called_with( servers[0].id, 'image', 'daily', diff --git a/releasenotes/notes/migrate-server-backup-to-sdk-0f170baf38e98b40.yaml b/releasenotes/notes/migrate-server-backup-to-sdk-0f170baf38e98b40.yaml new file mode 100644 index 0000000000..4f5cef27f1 --- /dev/null +++ b/releasenotes/notes/migrate-server-backup-to-sdk-0f170baf38e98b40.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Migrate openstack server backup from novaclient to sdk. From 8cb0a28607e7f8b9eb4bb71a95b39230d43a969c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 2 Nov 2021 09:59:27 +0000 Subject: [PATCH 034/706] compute: Don't warn if disk overcommit params unset Due to a small logic error, we were emitting a warning about a deprecated option when the user tried to live migrate an instance using microversion 2.25 even though the user hadn't actually set that option. Correct this. Change-Id: Ib61e817bd4ced9b5533e7c7f9d8f0b45fe81c211 Signed-off-by: Stephen Finucane Story: 2009657 Task: 43836 --- openstackclient/compute/v2/server.py | 8 ++++++-- .../tests/unit/compute/v2/test_server.py | 20 +++++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index c11f4b5781..d0edaf0769 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2621,7 +2621,7 @@ def get_parser(self, prog_name): disk_group.add_argument( '--disk-overcommit', action='store_true', - default=False, + default=None, help=_( 'Allow disk over-commit on the destination host' '(supported with --os-compute-api-version 2.24 or below)' @@ -2631,7 +2631,6 @@ def get_parser(self, prog_name): '--no-disk-overcommit', dest='disk_overcommit', action='store_false', - default=False, help=_( 'Do not over-commit disk on the destination host (default)' '(supported with --os-compute-api-version 2.24 or below)' @@ -2693,6 +2692,11 @@ def _show_progress(progress): if compute_client.api_version < api_versions.APIVersion('2.25'): kwargs['disk_over_commit'] = 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 elif parsed_args.disk_overcommit is not None: # TODO(stephenfin): Raise an error here in OSC 7.0 msg = _( diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 3d8c17fdeb..285b2b411e 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4812,7 +4812,7 @@ def test_server_migrate_no_options(self): verifylist = [ ('live_migration', False), ('block_migration', None), - ('disk_overcommit', False), + ('disk_overcommit', None), ('wait', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -4834,7 +4834,7 @@ def test_server_migrate_with_host_2_56(self): ('live_migration', False), ('host', 'fakehost'), ('block_migration', None), - ('disk_overcommit', False), + ('disk_overcommit', None), ('wait', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -4856,7 +4856,7 @@ def test_server_migrate_with_block_migration(self): verifylist = [ ('live_migration', False), ('block_migration', True), - ('disk_overcommit', False), + ('disk_overcommit', None), ('wait', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -4897,7 +4897,7 @@ def test_server_migrate_with_host_pre_2_56(self): ('live_migration', False), ('host', 'fakehost'), ('block_migration', None), - ('disk_overcommit', False), + ('disk_overcommit', None), ('wait', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -4923,7 +4923,7 @@ def test_server_live_migrate(self): ('live_migration', True), ('host', None), ('block_migration', None), - ('disk_overcommit', False), + ('disk_overcommit', None), ('wait', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -4947,7 +4947,7 @@ def test_server_live_migrate_with_host(self): ('live_migration', True), ('host', 'fakehost'), ('block_migration', None), - ('disk_overcommit', False), + ('disk_overcommit', None), ('wait', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -4975,7 +4975,7 @@ def test_server_live_migrate_with_host_pre_v230(self): ('live_migration', True), ('host', 'fakehost'), ('block_migration', None), - ('disk_overcommit', False), + ('disk_overcommit', None), ('wait', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -5002,7 +5002,7 @@ def test_server_block_live_migrate(self): verifylist = [ ('live_migration', True), ('block_migration', True), - ('disk_overcommit', False), + ('disk_overcommit', None), ('wait', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -5088,7 +5088,7 @@ def test_server_migrate_with_wait(self, mock_wait_for_status): verifylist = [ ('live_migration', False), ('block_migration', None), - ('disk_overcommit', False), + ('disk_overcommit', None), ('wait', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -5108,7 +5108,7 @@ def test_server_migrate_with_wait_fails(self, mock_wait_for_status): verifylist = [ ('live_migration', False), ('block_migration', None), - ('disk_overcommit', False), + ('disk_overcommit', None), ('wait', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) From 442838ed158cd8fb4168877744a60de5cfca29cc Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 2 Nov 2021 09:34:32 +0000 Subject: [PATCH 035/706] compute: Use correct command class for 'show migration' We should be inheriting from 'ShowOne'. Failure to do so results in a tuple being dumped to the screen. Not what we intended. While we're here, we update the docstring of this command to clarify the command's intent. Nova does not provide an API to retrieve an individual migration record for a cold migration or completed live migration. As such, the 'server migration show' command only works for in-progress live-migrations. Change-Id: I2e2fe3da7d642b9e8e3d930603dcde178cd68cde Signed-off-by: Stephen Finucane Story: 2009658 Task: 43837 --- openstackclient/compute/v2/server.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index d0edaf0769..1dc56fc7e6 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2967,8 +2967,13 @@ def take_action(self, parsed_args): return self.print_migrations(parsed_args, compute_client, migrations) -class ShowMigration(command.Command): - """Show a migration for a given server.""" +class ShowMigration(command.ShowOne): + """Show an in-progress live migration for a given server. + + Note that it is not possible to show cold migrations or completed + live-migrations. Use 'openstack server migration list' to get details for + these. + """ def get_parser(self, prog_name): parser = super().get_parser(prog_name) From 163cb01e46fc3f906154a7045fdbe9342cd446c7 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 3 Nov 2021 11:31:04 +0000 Subject: [PATCH 036/706] compute: Return details of attached volumes The API behind the 'server add volume' command returns details of the created volume attachment, however, we were dropping these results rather than displaying them to the user. Correct this. Change-Id: I3f7e121220d29422ccf4e6940de2f28bb8496c83 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 22 ++++- .../tests/unit/compute/v2/test_server.py | 91 +++++++++++++++++-- ...or-server-add-volume-f75277ad58e31024.yaml | 5 + 3 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 releasenotes/notes/show-result-for-server-add-volume-f75277ad58e31024.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index c11f4b5781..80bdc61224 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -496,7 +496,7 @@ def take_action(self, parsed_args): server.add_security_group(security_group['id']) -class AddServerVolume(command.Command): +class AddServerVolume(command.ShowOne): _description = _( "Add volume to server. " "Specify ``--os-compute-api-version 2.20`` or higher to add a volume " @@ -595,12 +595,30 @@ def take_action(self, parsed_args): kwargs['delete_on_termination'] = False - compute_client.volumes.create_server_volume( + volume_attachment = compute_client.volumes.create_server_volume( server.id, volume.id, **kwargs ) + columns = ('id', 'serverId', 'volumeId', 'device') + column_headers = ('ID', 'Server ID', 'Volume ID', 'Device') + if compute_client.api_version >= api_versions.APIVersion('2.49'): + columns += ('tag',) + column_headers += ('Tag',) + if compute_client.api_version >= api_versions.APIVersion('2.79'): + columns += ('delete_on_termination',) + column_headers += ('Delete On Termination',) + + return ( + column_headers, + utils.get_item_properties( + volume_attachment, + columns, + mixed_case_fields=('serverId', 'volumeId'), + ) + ) + # TODO(stephenfin): Replace with 'MultiKeyValueAction' when we no longer # support '--nic=auto' and '--nic=none' diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 3d8c17fdeb..585f49de84 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -670,6 +670,11 @@ def setUp(self): def test_server_add_volume(self): servers = self.setup_servers_mock(count=1) + volume_attachment = \ + compute_fakes.FakeVolumeAttachment.create_one_volume_attachment() + self.servers_volumes_mock.create_server_volume.return_value = \ + volume_attachment + arglist = [ '--device', '/dev/sdb', servers[0].id, @@ -683,11 +688,20 @@ def test_server_add_volume(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + expected_columns = ('ID', 'Server ID', 'Volume ID', 'Device') + expected_data = ( + volume_attachment.id, + volume_attachment.serverId, + volume_attachment.volumeId, + volume_attachment.device, + ) + + columns, data = self.cmd.take_action(parsed_args) self.servers_volumes_mock.create_server_volume.assert_called_once_with( servers[0].id, self.volume.id, device='/dev/sdb') - self.assertIsNone(result) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) def test_server_add_volume_with_tag(self): # requires API 2.49 or later @@ -695,6 +709,11 @@ def test_server_add_volume_with_tag(self): '2.49') servers = self.setup_servers_mock(count=1) + volume_attachment = \ + compute_fakes.FakeVolumeAttachment.create_one_volume_attachment() + self.servers_volumes_mock.create_server_volume.return_value = \ + volume_attachment + arglist = [ '--device', '/dev/sdb', '--tag', 'foo', @@ -710,11 +729,21 @@ def test_server_add_volume_with_tag(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + expected_columns = ('ID', 'Server ID', 'Volume ID', 'Device', 'Tag') + expected_data = ( + volume_attachment.id, + volume_attachment.serverId, + volume_attachment.volumeId, + volume_attachment.device, + volume_attachment.tag, + ) + + columns, data = self.cmd.take_action(parsed_args) self.servers_volumes_mock.create_server_volume.assert_called_once_with( servers[0].id, self.volume.id, device='/dev/sdb', tag='foo') - self.assertIsNone(result) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) def test_server_add_volume_with_tag_pre_v249(self): self.app.client_manager.compute.api_version = api_versions.APIVersion( @@ -746,6 +775,11 @@ def test_server_add_volume_with_enable_delete_on_termination(self): '2.79') servers = self.setup_servers_mock(count=1) + volume_attachment = \ + compute_fakes.FakeVolumeAttachment.create_one_volume_attachment() + self.servers_volumes_mock.create_server_volume.return_value = \ + volume_attachment + arglist = [ '--enable-delete-on-termination', '--device', '/dev/sdb', @@ -761,18 +795,41 @@ def test_server_add_volume_with_enable_delete_on_termination(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + expected_columns = ( + 'ID', + 'Server ID', + 'Volume ID', + 'Device', + 'Tag', + 'Delete On Termination', + ) + expected_data = ( + volume_attachment.id, + volume_attachment.serverId, + volume_attachment.volumeId, + volume_attachment.device, + volume_attachment.tag, + volume_attachment.delete_on_termination, + ) + + columns, data = self.cmd.take_action(parsed_args) self.servers_volumes_mock.create_server_volume.assert_called_once_with( servers[0].id, self.volume.id, device='/dev/sdb', delete_on_termination=True) - self.assertIsNone(result) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) def test_server_add_volume_with_disable_delete_on_termination(self): self.app.client_manager.compute.api_version = api_versions.APIVersion( '2.79') servers = self.setup_servers_mock(count=1) + volume_attachment = \ + compute_fakes.FakeVolumeAttachment.create_one_volume_attachment() + self.servers_volumes_mock.create_server_volume.return_value = \ + volume_attachment + arglist = [ '--disable-delete-on-termination', '--device', '/dev/sdb', @@ -788,12 +845,30 @@ def test_server_add_volume_with_disable_delete_on_termination(self): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + expected_columns = ( + 'ID', + 'Server ID', + 'Volume ID', + 'Device', + 'Tag', + 'Delete On Termination', + ) + expected_data = ( + volume_attachment.id, + volume_attachment.serverId, + volume_attachment.volumeId, + volume_attachment.device, + volume_attachment.tag, + volume_attachment.delete_on_termination, + ) + + columns, data = self.cmd.take_action(parsed_args) self.servers_volumes_mock.create_server_volume.assert_called_once_with( servers[0].id, self.volume.id, device='/dev/sdb', delete_on_termination=False) - self.assertIsNone(result) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) def test_server_add_volume_with_enable_delete_on_termination_pre_v279( self, diff --git a/releasenotes/notes/show-result-for-server-add-volume-f75277ad58e31024.yaml b/releasenotes/notes/show-result-for-server-add-volume-f75277ad58e31024.yaml new file mode 100644 index 0000000000..7e062d6d5f --- /dev/null +++ b/releasenotes/notes/show-result-for-server-add-volume-f75277ad58e31024.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + The ``server add volume`` command will now return details of the created + volume attachment upon successful attachment. From 2183a611475090347863917f6c90f0f38cd80893 Mon Sep 17 00:00:00 2001 From: Diwei Zhu Date: Thu, 28 Oct 2021 02:16:23 +0000 Subject: [PATCH 037/706] Switch openstack server add port/network to using sdk. The old novaclient.v2.server.Server.interface_attach() method is replaced with proxy.create_server_interface(). In swargs, 'net_id' and 'port_id' are mutual-exclusive, if one of them is given with value, the other one cannot be None, as the API would responde with 400 (None is not string). In unit test, temporary method 'setup_sdk_servers_mock' is added, because other tests are still using the old 'setup_servers_mock'. Functional tests are added. Releasenote is generated. Change-Id: I9899f0509febc5143560a1859ae6344d0a6d1427 --- openstackclient/compute/v2/server.py | 23 ++++---- .../functional/compute/v2/test_server.py | 48 +++++++++++++++ .../tests/unit/compute/v2/test_server.py | 58 +++++++++++++------ ...work-add-port-to-sdk-7d81b25f59cfbec9.yaml | 4 ++ 4 files changed, 104 insertions(+), 29 deletions(-) create mode 100644 releasenotes/notes/migrate-server-add-network-add-port-to-sdk-7d81b25f59cfbec9.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index c11f4b5781..fcd7d69cdc 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -27,6 +27,7 @@ from novaclient import api_versions from novaclient.v2 import servers 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 from osc_lib.command import command @@ -378,10 +379,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) if self.app.client_manager.is_network_endpoint_enabled(): network_client = self.app.client_manager.network @@ -392,12 +393,11 @@ def take_action(self, parsed_args): kwargs = { 'port_id': port_id, - 'net_id': None, 'fixed_ip': None, } if parsed_args.tag: - if compute_client.api_version < api_versions.APIVersion("2.49"): + if not sdk_utils.supports_microversion(compute_client, '2.49'): msg = _( '--os-compute-api-version 2.49 or greater is required to ' 'support the --tag option' @@ -405,7 +405,7 @@ def take_action(self, parsed_args): raise exceptions.CommandError(msg) kwargs['tag'] = parsed_args.tag - server.interface_attach(**kwargs) + compute_client.create_server_interface(server, **kwargs) class AddNetwork(command.Command): @@ -434,10 +434,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) if self.app.client_manager.is_network_endpoint_enabled(): network_client = self.app.client_manager.network @@ -447,13 +447,12 @@ def take_action(self, parsed_args): net_id = parsed_args.network kwargs = { - 'port_id': None, 'net_id': net_id, 'fixed_ip': None, } if parsed_args.tag: - if compute_client.api_version < api_versions.APIVersion('2.49'): + if not sdk_utils.supports_microversion(compute_client, '2.49'): msg = _( '--os-compute-api-version 2.49 or greater is required to ' 'support the --tag option' @@ -462,7 +461,7 @@ def take_action(self, parsed_args): kwargs['tag'] = parsed_args.tag - server.interface_attach(**kwargs) + compute_client.create_server_interface(server, **kwargs) class AddServerSecurityGroup(command.Command): diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py index 9cf2fc7f0f..59b1fad5f9 100644 --- a/openstackclient/tests/functional/compute/v2/test_server.py +++ b/openstackclient/tests/functional/compute/v2/test_server.py @@ -1071,3 +1071,51 @@ def test_server_create_with_empty_network_option_latest(self): # networks and the test didn't specify a specific network. self.assertNotIn('nics are required after microversion 2.36', e.stderr) + + def test_server_add_remove_network_port(self): + name = uuid.uuid4().hex + cmd_output = json.loads(self.openstack( + 'server create -f json ' + + '--network private ' + + '--flavor ' + self.flavor_name + ' ' + + '--image ' + self.image_name + ' ' + + '--wait ' + + name + )) + + self.assertIsNotNone(cmd_output['id']) + self.assertEqual(name, cmd_output['name']) + + self.openstack( + 'server add network ' + name + ' public') + + cmd_output = json.loads(self.openstack( + 'server show -f json ' + name + )) + + addresses = cmd_output['addresses'] + self.assertIn('public', addresses) + + port_name = 'test-port' + + cmd_output = json.loads(self.openstack( + 'port list -f json' + )) + self.assertNotIn(port_name, cmd_output) + + cmd_output = json.loads(self.openstack( + 'port create -f json ' + + '--network private ' + port_name + )) + self.assertIsNotNone(cmd_output['id']) + + self.openstack('server add port ' + name + ' ' + port_name) + + cmd_output = json.loads(self.openstack( + 'server show -f json ' + name + )) + + # TODO(diwei): test remove network/port after the commands are switched + + self.openstack('server delete ' + name) + self.openstack('port delete ' + port_name) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 3d8c17fdeb..705a96b79c 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -24,6 +24,7 @@ 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 @@ -69,6 +70,10 @@ 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 + # Get a shortcut to the compute client ServerMigrationsManager Mock self.server_migrations_mock = \ self.app.client_manager.compute.server_migrations @@ -133,6 +138,21 @@ def setup_servers_mock(self, count): 0) return servers + def setup_sdk_servers_mock(self, count): + servers = compute_fakes.FakeServer.create_sdk_servers( + attrs=self.attrs, + methods=self.methods, + count=count, + ) + + # This is the return value for compute_client.find_server() + self.sdk_client.find_server = compute_fakes.FakeServer.get_servers( + servers, + 0, + ) + + return servers + def run_method_with_servers(self, method_name, server_count): servers = self.setup_servers_mock(server_count) @@ -570,7 +590,7 @@ def setUp(self): self.app.client_manager.network.find_port = self.find_port def _test_server_add_port(self, port_id): - servers = self.setup_servers_mock(count=1) + servers = self.setup_sdk_servers_mock(count=1) port = 'fake-port' arglist = [ @@ -585,8 +605,8 @@ def _test_server_add_port(self, port_id): result = self.cmd.take_action(parsed_args) - servers[0].interface_attach.assert_called_once_with( - port_id=port_id, net_id=None, fixed_ip=None) + self.sdk_client.create_server_interface.assert_called_once_with( + servers[0], port_id=port_id, fixed_ip=None) self.assertIsNone(result) def test_server_add_port(self): @@ -599,11 +619,12 @@ def test_server_add_port_no_neutron(self): self._test_server_add_port('fake-port') self.find_port.assert_not_called() - def test_server_add_port_with_tag(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_servers_mock(count=1) + servers = self.setup_sdk_servers_mock(count=1) self.find_port.return_value.id = 'fake-port' arglist = [ servers[0].id, @@ -620,13 +641,14 @@ def test_server_add_port_with_tag(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - servers[0].interface_attach.assert_called_once_with( + self.sdk_client.create_server_interface.assert_called_once_with( + servers[0], port_id='fake-port', - net_id=None, fixed_ip=None, tag='tag1') - def test_server_add_port_with_tag_pre_v249(self): + @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') @@ -891,7 +913,7 @@ def setUp(self): self.app.client_manager.network.find_network = self.find_network def _test_server_add_network(self, net_id): - servers = self.setup_servers_mock(count=1) + servers = self.setup_sdk_servers_mock(count=1) network = 'fake-network' arglist = [ @@ -906,8 +928,8 @@ def _test_server_add_network(self, net_id): result = self.cmd.take_action(parsed_args) - servers[0].interface_attach.assert_called_once_with( - port_id=None, net_id=net_id, fixed_ip=None) + self.sdk_client.create_server_interface.assert_called_once_with( + servers[0], net_id=net_id, fixed_ip=None) self.assertIsNone(result) def test_server_add_network(self): @@ -920,11 +942,12 @@ def test_server_add_network_no_neutron(self): self._test_server_add_network('fake-network') self.find_network.assert_not_called() - def test_server_add_network_with_tag(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_servers_mock(count=1) + servers = self.setup_sdk_servers_mock(count=1) self.find_network.return_value.id = 'fake-network' arglist = [ @@ -942,18 +965,19 @@ def test_server_add_network_with_tag(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - servers[0].interface_attach.assert_called_once_with( - port_id=None, + self.sdk_client.create_server_interface.assert_called_once_with( + servers[0], net_id='fake-network', fixed_ip=None, tag='tag1' ) - def test_server_add_network_with_tag_pre_v249(self): + @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_servers_mock(count=1) + servers = self.setup_sdk_servers_mock(count=1) self.find_network.return_value.id = 'fake-network' arglist = [ diff --git a/releasenotes/notes/migrate-server-add-network-add-port-to-sdk-7d81b25f59cfbec9.yaml b/releasenotes/notes/migrate-server-add-network-add-port-to-sdk-7d81b25f59cfbec9.yaml new file mode 100644 index 0000000000..930d6bc597 --- /dev/null +++ b/releasenotes/notes/migrate-server-add-network-add-port-to-sdk-7d81b25f59cfbec9.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Migrate server add network/port from novaclient to openstacksdk. From 9acbd3e1052d533c1395eb59de4274170baed67b Mon Sep 17 00:00:00 2001 From: Thrivikram Mudunuri Date: Thu, 28 Oct 2021 18:05:03 -0400 Subject: [PATCH 038/706] Switch server image create to SDK Switch the server image create command from novaclient to SDK. Use the SDK versions of test fakes to support fake Server resources. Also, fetch updated image *after* waiting. If a user requests that we wait (--wait) for a server image to become active before returning, then we should probably return the final image. If we don't then the image can appear to be in a non-active state when it fact it's active. Correct this by fetching the image after the wait call. Change-Id: I83a403c035add9ab041ed6d59b5b29e42267f143 --- openstackclient/compute/v2/server_image.py | 18 ++++++------- .../unit/compute/v2/test_server_image.py | 27 +++++++++---------- ...-server-image-to-sdk-e3d8077ffe05bb3d.yaml | 4 +++ 3 files changed, 25 insertions(+), 24 deletions(-) create mode 100644 releasenotes/notes/migrate-create-server-image-to-sdk-e3d8077ffe05bb3d.yaml diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py index 6c0e3b22cf..2021fae7c0 100644 --- a/openstackclient/compute/v2/server_image.py +++ b/openstackclient/compute/v2/server_image.py @@ -73,25 +73,23 @@ 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.name: image_name = parsed_args.name else: image_name = server.name - image_id = compute_client.servers.create_image( + image_id = compute_client.create_server_image( server.id, image_name, parsed_args.properties, - ) - - image_client = self.app.client_manager.image - image = image_client.find_image(image_id) + ).id if parsed_args.wait: if utils.wait_for_status( @@ -105,6 +103,8 @@ def _show_progress(progress): _('Error creating server image: %s'), parsed_args.server) raise exceptions.CommandError + image = image_client.find_image(image_id, ignore_missing=False) + if self.app.client_manager._api_version['image'] == '1': info = {} info.update(image._info) diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py index 9b14428a27..b73cc763cb 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_image.py +++ b/openstackclient/tests/unit/compute/v2/test_server_image.py @@ -27,8 +27,9 @@ def setUp(self): super(TestServerImage, self).setUp() # Get a shortcut to the compute client ServerManager Mock - 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 # Get a shortcut to the image client ImageManager Mock self.images_mock = self.app.client_manager.image @@ -41,14 +42,14 @@ def setUp(self): self.methods = {} def setup_servers_mock(self, count): - servers = compute_fakes.FakeServer.create_servers( + servers = compute_fakes.FakeServer.create_sdk_servers( attrs=self.attrs, methods=self.methods, count=count, ) - # This is the return value for utils.find_resource() - self.servers_mock.get = compute_fakes.FakeServer.get_servers( + # This is the return value for compute_client.find_server() + self.sdk_client.find_server = compute_fakes.FakeServer.get_servers( servers, 0, ) @@ -104,8 +105,8 @@ def setup_images_mock(self, count, servers=None): ) self.images_mock.find_image = mock.Mock(side_effect=images) - self.servers_mock.create_image = mock.Mock( - return_value=images[0].id, + self.sdk_client.create_server_image = mock.Mock( + return_value=images[0], ) return images @@ -126,8 +127,7 @@ def test_server_image_create_defaults(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # ServerManager.create_image(server, image_name, metadata=) - self.servers_mock.create_image.assert_called_with( + self.sdk_client.create_server_image.assert_called_with( servers[0].id, servers[0].name, None, @@ -157,8 +157,7 @@ def test_server_image_create_options(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # ServerManager.create_image(server, image_name, metadata=) - self.servers_mock.create_image.assert_called_with( + self.sdk_client.create_server_image.assert_called_with( servers[0].id, 'img-nam', {'key': 'value'}, @@ -188,8 +187,7 @@ def test_server_create_image_wait_fail(self, mock_wait_for_status): parsed_args, ) - # ServerManager.create_image(server, image_name, metadata=) - self.servers_mock.create_image.assert_called_with( + self.sdk_client.create_server_image.assert_called_with( servers[0].id, servers[0].name, None, @@ -221,8 +219,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) - # ServerManager.create_image(server, image_name, metadata=) - self.servers_mock.create_image.assert_called_with( + self.sdk_client.create_server_image.assert_called_with( servers[0].id, servers[0].name, None, diff --git a/releasenotes/notes/migrate-create-server-image-to-sdk-e3d8077ffe05bb3d.yaml b/releasenotes/notes/migrate-create-server-image-to-sdk-e3d8077ffe05bb3d.yaml new file mode 100644 index 0000000000..20f8d55018 --- /dev/null +++ b/releasenotes/notes/migrate-create-server-image-to-sdk-e3d8077ffe05bb3d.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Migrate openstack server image create from novaclient to sdk. From 690e9a13a232f522162adc109d32c8eee864814e Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 17 Nov 2021 10:28:40 +0000 Subject: [PATCH 039/706] image: Remove dead test helper methods These haven't been used since we switched the image commands from glanceclient to openstacksdk. There's more cleanup to be done here but that can be done later. Change-Id: I3de1f24323886b122b3a30660fb3de18eb7014e9 Signed-off-by: Stephen Finucane --- openstackclient/tests/unit/image/v1/fakes.py | 31 ---- openstackclient/tests/unit/image/v2/fakes.py | 173 ------------------- 2 files changed, 204 deletions(-) diff --git a/openstackclient/tests/unit/image/v1/fakes.py b/openstackclient/tests/unit/image/v1/fakes.py index add3978d1d..59ae5f7a2d 100644 --- a/openstackclient/tests/unit/image/v1/fakes.py +++ b/openstackclient/tests/unit/image/v1/fakes.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 import uuid @@ -25,36 +24,6 @@ image_id = 'im1' image_name = 'graven' -image_owner = 'baal' -image_protected = False -image_public = True -image_properties = { - 'Alpha': 'a', - 'Beta': 'b', - 'Gamma': 'g', -} -image_properties_str = "Alpha='a', Beta='b', Gamma='g'" -image_data = 'line 1\nline 2\n' -image_size = 0 - -IMAGE = { - 'id': image_id, - 'name': image_name, - 'container_format': '', - 'disk_format': '', - 'owner': image_owner, - 'min_disk': 0, - 'min_ram': 0, - 'is_public': image_public, - 'protected': image_protected, - 'properties': image_properties, - 'size': image_size, -} - -IMAGE_columns = tuple(sorted(IMAGE)) -IMAGE_output = dict(IMAGE) -IMAGE_output['properties'] = image_properties_str -IMAGE_data = tuple((IMAGE_output[x] for x in sorted(IMAGE_output))) class FakeImagev1Client(object): diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py index 0d83f98b95..49ce400d92 100644 --- a/openstackclient/tests/unit/image/v2/fakes.py +++ b/openstackclient/tests/unit/image/v2/fakes.py @@ -11,16 +11,13 @@ # 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.v2 import image from openstack.image.v2 import member -from osc_lib.cli import format_columns from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes @@ -28,121 +25,6 @@ image_id = '0f41529e-7c12-4de8-be2d-181abb825b3c' image_name = 'graven' -image_owner = 'baal' -image_protected = False -image_visibility = 'public' -image_tags = [] -image_size = 0 - -IMAGE = { - 'id': image_id, - 'name': image_name, - 'owner': image_owner, - 'protected': image_protected, - 'visibility': image_visibility, - 'tags': image_tags, - 'size': image_size -} - -IMAGE_columns = tuple(sorted(IMAGE)) -IMAGE_data = tuple((IMAGE[x] for x in sorted(IMAGE))) - -IMAGE_SHOW = copy.copy(IMAGE) -IMAGE_SHOW['tags'] = format_columns.ListColumn(IMAGE_SHOW['tags']) -IMAGE_SHOW_data = tuple((IMAGE_SHOW[x] for x in sorted(IMAGE_SHOW))) - -# Just enough v2 schema to do some testing -IMAGE_schema = { - "additionalProperties": { - "type": "string" - }, - "name": "image", - "links": [ - { - "href": "{self}", - "rel": "self" - }, - { - "href": "{file}", - "rel": "enclosure" - }, - { - "href": "{schema}", - "rel": "describedby" - } - ], - "properties": { - "id": { - "pattern": "^([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}$", # noqa - "type": "string", - "description": "An identifier for the image" - }, - "name": { - "type": [ - "null", - "string" - ], - "description": "Descriptive name for the image", - "maxLength": 255 - }, - "owner": { - "type": [ - "null", - "string" - ], - "description": "Owner of the image", - "maxLength": 255 - }, - "protected": { - "type": "boolean", - "description": "If true, image will not be deletable." - }, - "self": { - "type": "string", - "description": "(READ-ONLY)" - }, - "schema": { - "type": "string", - "description": "(READ-ONLY)" - }, - "size": { - "type": [ - "null", - "integer", - "string" - ], - "description": "Size of image file in bytes (READ-ONLY)" - }, - "status": { - "enum": [ - "queued", - "saving", - "active", - "killed", - "deleted", - "pending_delete" - ], - "type": "string", - "description": "Status of the image (READ-ONLY)" - }, - "tags": { - "items": { - "type": "string", - "maxLength": 255 - }, - "type": "array", - "description": "List of strings related to the image" - }, - "visibility": { - "enum": [ - "public", - "private" - ], - "type": "string", - "description": "Scope of image accessibility" - }, - } -} class FakeImagev2Client(object): @@ -231,61 +113,6 @@ def create_images(attrs=None, count=2): return images - @staticmethod - def get_images(images=None, count=2): - """Get an iterable MagicMock object with a list of faked images. - - If images list is provided, then initialize the Mock object with the - list. Otherwise create one. - - :param List images: - A list of FakeResource objects faking images - :param Integer count: - The number of images to be faked - :return: - An iterable Mock object with side_effect set to a list of faked - images - """ - if images is None: - images = FakeImage.create_images(count) - - return mock.Mock(side_effect=images) - - @staticmethod - def get_image_columns(image=None): - """Get the image columns from a faked image object. - - :param image: - A FakeResource objects faking image - :return: - A tuple which may include the following keys: - ('id', 'name', 'owner', 'protected', 'visibility', 'tags') - """ - if image is not None: - return tuple(sorted(image)) - return IMAGE_columns - - @staticmethod - def get_image_data(image=None): - """Get the image data from a faked image object. - - :param image: - A FakeResource objects faking image - :return: - A tuple which may include the following values: - ('image-123', 'image-foo', 'admin', False, 'public', 'bar, baz') - """ - data_list = [] - if image is not None: - for x in sorted(image.keys()): - if x == 'tags': - # The 'tags' should be format_list - data_list.append( - format_columns.ListColumn(getattr(image, x))) - else: - data_list.append(getattr(image, x)) - return tuple(data_list) - @staticmethod def create_one_image_member(attrs=None): """Create a fake image member. From 2135a9ea05c79a11185ca87f6bb5ade3b71501bb Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 17 Nov 2021 10:35:51 +0000 Subject: [PATCH 040/706] image: Remove FakeImage test helper We're no longer creating fake versions of glanceclient's 'Resource' object but rather openstacksdk objects. As such, there's no point nesting things under a fake resource class. Change-Id: I39cd5302622f4542db9eebcccfad0cb90d077441 Signed-off-by: Stephen Finucane --- .../tests/unit/common/test_project_purge.py | 2 +- .../tests/unit/compute/v2/test_aggregate.py | 2 +- .../tests/unit/compute/v2/test_server.py | 28 ++-- .../unit/compute/v2/test_server_backup.py | 13 +- .../unit/compute/v2/test_server_image.py | 4 +- openstackclient/tests/unit/image/v1/fakes.py | 74 +++++----- .../tests/unit/image/v1/test_image.py | 11 +- openstackclient/tests/unit/image/v2/fakes.py | 133 ++++++++--------- .../tests/unit/image/v2/test_image.py | 134 +++++++++--------- .../tests/unit/volume/v1/test_volume.py | 4 +- .../tests/unit/volume/v2/test_volume.py | 4 +- 11 files changed, 187 insertions(+), 222 deletions(-) diff --git a/openstackclient/tests/unit/common/test_project_purge.py b/openstackclient/tests/unit/common/test_project_purge.py index adc48ce26f..5199093ce3 100644 --- a/openstackclient/tests/unit/common/test_project_purge.py +++ b/openstackclient/tests/unit/common/test_project_purge.py @@ -70,7 +70,7 @@ class TestProjectPurge(TestProjectPurgeInit): project = identity_fakes.FakeProject.create_one_project() server = compute_fakes.FakeServer.create_one_server() - image = image_fakes.FakeImage.create_one_image() + image = image_fakes.create_one_image() volume = volume_fakes.FakeVolume.create_one_volume() backup = volume_fakes.FakeBackup.create_one_backup() snapshot = volume_fakes.FakeSnapshot.create_one_snapshot() diff --git a/openstackclient/tests/unit/compute/v2/test_aggregate.py b/openstackclient/tests/unit/compute/v2/test_aggregate.py index 7c4fe5cbd3..071f2a3027 100644 --- a/openstackclient/tests/unit/compute/v2/test_aggregate.py +++ b/openstackclient/tests/unit/compute/v2/test_aggregate.py @@ -552,7 +552,7 @@ def test_aggregate_unset_no_option(self): class TestAggregateCacheImage(TestAggregate): - images = image_fakes.FakeImage.create_images(count=2) + images = image_fakes.create_images(count=2) def setUp(self): super(TestAggregateCacheImage, self).setUp() diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 9623cb0abf..f7ed4b16a4 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -1168,7 +1168,7 @@ def setUp(self): self.servers_mock.create.return_value = self.new_server - self.image = image_fakes.FakeImage.create_one_image() + self.image = image_fakes.create_one_image() self.find_image_mock.return_value = self.image self.get_image_mock.return_value = self.image @@ -2918,7 +2918,7 @@ def test_server_create_image_property(self): 'hypervisor_type': 'qemu', } - _image = image_fakes.FakeImage.create_one_image(image_info) + _image = image_fakes.create_one_image(image_info) self.images_mock.return_value = [_image] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2974,7 +2974,7 @@ def test_server_create_image_property_multi(self): 'hypervisor_type': 'qemu', 'hw_disk_bus': 'ide', } - _image = image_fakes.FakeImage.create_one_image(image_info) + _image = image_fakes.create_one_image(image_info) self.images_mock.return_value = [_image] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -3031,7 +3031,7 @@ def test_server_create_image_property_missed(self): 'hw_disk_bus': 'ide', } - _image = image_fakes.FakeImage.create_one_image(image_info) + _image = image_fakes.create_one_image(image_info) self.images_mock.return_value = [_image] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -3063,8 +3063,8 @@ def test_server_create_image_property_with_image_list(self): } } - target_image = image_fakes.FakeImage.create_one_image(image_info) - another_image = image_fakes.FakeImage.create_one_image({}) + target_image = image_fakes.create_one_image(image_info) + another_image = image_fakes.create_one_image({}) self.images_mock.return_value = [target_image, another_image] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -4102,7 +4102,7 @@ def setUp(self): self.servers = self.setup_servers_mock(3) self.servers_mock.list.return_value = self.servers - self.image = image_fakes.FakeImage.create_one_image() + self.image = image_fakes.create_one_image() # self.images_mock.return_value = [self.image] self.find_image_mock.return_value = self.image @@ -6021,7 +6021,7 @@ def setUp(self): super(TestServerRebuild, self).setUp() # Return value for utils.find_resource for image - self.image = image_fakes.FakeImage.create_one_image() + self.image = image_fakes.create_one_image() self.get_image_mock.return_value = self.image # Fake the rebuilt new server. @@ -6051,7 +6051,7 @@ def setUp(self): def test_rebuild_with_image_name(self): image_name = 'my-custom-image' - user_image = image_fakes.FakeImage.create_one_image( + user_image = image_fakes.create_one_image( attrs={'name': image_name}) self.find_image_mock.return_value = user_image @@ -6600,7 +6600,7 @@ class TestEvacuateServer(TestServer): def setUp(self): super(TestEvacuateServer, self).setUp() # Return value for utils.find_resource for image - self.image = image_fakes.FakeImage.create_one_image() + self.image = image_fakes.create_one_image() self.images_mock.get.return_value = self.image # Fake the rebuilt new server. @@ -6794,7 +6794,7 @@ def setUp(self): super(TestServerRescue, self).setUp() # Return value for utils.find_resource for image - self.image = image_fakes.FakeImage.create_one_image() + self.image = image_fakes.create_one_image() self.get_image_mock.return_value = self.image new_server = compute_fakes.FakeServer.create_one_server() @@ -6835,7 +6835,7 @@ def test_rescue_with_current_image(self): self.server.rescue.assert_called_with(image=None, password=None) def test_rescue_with_new_image(self): - new_image = image_fakes.FakeImage.create_one_image() + new_image = image_fakes.create_one_image() self.find_image_mock.return_value = new_image arglist = [ '--image', new_image.id, @@ -7950,7 +7950,7 @@ class TestServerShow(TestServer): def setUp(self): super(TestServerShow, self).setUp() - self.image = image_fakes.FakeImage.create_one_image() + self.image = image_fakes.create_one_image() self.flavor = compute_fakes.FakeFlavor.create_one_flavor() self.topology = { 'nodes': [{'vcpu_set': [0, 1]}, {'vcpu_set': [2, 3]}], @@ -8540,7 +8540,7 @@ def test_prep_server_detail(self, find_resource): # - The first time, return server info. # - The second time, return image info. # - The third time, return flavor info. - _image = image_fakes.FakeImage.create_one_image() + _image = image_fakes.create_one_image() _flavor = compute_fakes.FakeFlavor.create_one_flavor() server_info = { 'image': {u'id': _image.id}, diff --git a/openstackclient/tests/unit/compute/v2/test_server_backup.py b/openstackclient/tests/unit/compute/v2/test_server_backup.py index 1644baae00..1a5e0a1225 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_backup.py +++ b/openstackclient/tests/unit/compute/v2/test_server_backup.py @@ -91,7 +91,7 @@ def setUp(self): def setup_images_mock(self, count, servers=None): if servers: - images = image_fakes.FakeImage.create_images( + images = image_fakes.create_images( attrs={ 'name': servers[0].name, 'status': 'active', @@ -99,7 +99,7 @@ def setup_images_mock(self, count, servers=None): count=count, ) else: - images = image_fakes.FakeImage.create_images( + images = image_fakes.create_images( attrs={ 'status': 'active', }, @@ -178,15 +178,6 @@ def test_server_backup_create_options(self): 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) -# images = image_fakes.FakeImage.create_images( -# attrs={ -# 'name': servers[0].name, -# 'status': 'active', -# }, -# count=1, -# ) -# -# self.images_mock.find_image.return_value = images[0] self.images_mock.get_image = mock.Mock( side_effect=images[0], ) diff --git a/openstackclient/tests/unit/compute/v2/test_server_image.py b/openstackclient/tests/unit/compute/v2/test_server_image.py index 9b14428a27..e17401691a 100644 --- a/openstackclient/tests/unit/compute/v2/test_server_image.py +++ b/openstackclient/tests/unit/compute/v2/test_server_image.py @@ -88,7 +88,7 @@ def setUp(self): def setup_images_mock(self, count, servers=None): if servers: - images = image_fakes.FakeImage.create_images( + images = image_fakes.create_images( attrs={ 'name': servers[0].name, 'status': 'active', @@ -96,7 +96,7 @@ def setup_images_mock(self, count, servers=None): count=count, ) else: - images = image_fakes.FakeImage.create_images( + images = image_fakes.create_images( attrs={ 'status': 'active', }, diff --git a/openstackclient/tests/unit/image/v1/fakes.py b/openstackclient/tests/unit/image/v1/fakes.py index 59ae5f7a2d..3097a42f59 100644 --- a/openstackclient/tests/unit/image/v1/fakes.py +++ b/openstackclient/tests/unit/image/v1/fakes.py @@ -22,10 +22,6 @@ from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -image_id = 'im1' -image_name = 'graven' - - class FakeImagev1Client(object): def __init__(self, **kwargs): @@ -51,40 +47,36 @@ def setUp(self): ) -class FakeImage(object): - """Fake one or more images.""" - - @staticmethod - def create_one_image(attrs=None): - """Create a fake image. - - :param Dictionary attrs: - A dictionary with all attrbutes of image - :return: - A FakeResource object with id, name, owner, protected, - visibility and tags attrs - """ - attrs = attrs or {} - - # Set default attribute - image_info = { - 'id': str(uuid.uuid4()), - 'name': 'image-name' + uuid.uuid4().hex, - 'owner': 'image-owner' + uuid.uuid4().hex, - 'container_format': '', - 'disk_format': '', - 'min_disk': 0, - 'min_ram': 0, - 'is_public': True, - 'protected': False, - 'properties': { - 'Alpha': 'a', - 'Beta': 'b', - 'Gamma': 'g'}, - 'status': 'status' + uuid.uuid4().hex - } - - # Overwrite default attributes if there are some attributes set - image_info.update(attrs) - - return image.Image(**image_info) +def create_one_image(attrs=None): + """Create a fake image. + + :param Dictionary attrs: + A dictionary with all attrbutes of image + :return: + A FakeResource object with id, name, owner, protected, + visibility and tags attrs + """ + attrs = attrs or {} + + # Set default attribute + image_info = { + 'id': str(uuid.uuid4()), + 'name': 'image-name' + uuid.uuid4().hex, + 'owner': 'image-owner' + uuid.uuid4().hex, + 'container_format': '', + 'disk_format': '', + 'min_disk': 0, + 'min_ram': 0, + 'is_public': True, + 'protected': False, + 'properties': { + 'Alpha': 'a', + 'Beta': 'b', + 'Gamma': 'g'}, + 'status': 'status' + uuid.uuid4().hex + } + + # Overwrite default attributes if there are some attributes set + image_info.update(attrs) + + return image.Image(**image_info) diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py index 5c69bf0f87..06519800ce 100644 --- a/openstackclient/tests/unit/image/v1/test_image.py +++ b/openstackclient/tests/unit/image/v1/test_image.py @@ -34,7 +34,7 @@ def setUp(self): class TestImageCreate(TestImage): - new_image = image_fakes.FakeImage.create_one_image() + new_image = image_fakes.create_one_image() columns = ( 'container_format', 'disk_format', @@ -210,7 +210,7 @@ def test_image_create_file(self, mock_open): class TestImageDelete(TestImage): - _image = image_fakes.FakeImage.create_one_image() + _image = image_fakes.create_one_image() def setUp(self): super(TestImageDelete, self).setUp() @@ -239,7 +239,7 @@ def test_image_delete_no_options(self): class TestImageList(TestImage): - _image = image_fakes.FakeImage.create_one_image() + _image = image_fakes.create_one_image() columns = ( 'ID', @@ -443,7 +443,7 @@ def test_image_list_sort_option(self, si_mock): class TestImageSet(TestImage): - _image = image_fakes.FakeImage.create_one_image() + _image = image_fakes.create_one_image() def setUp(self): super(TestImageSet, self).setUp() @@ -682,8 +682,7 @@ def test_image_set_numeric_options_to_zero(self): class TestImageShow(TestImage): - _image = image_fakes.FakeImage.create_one_image( - attrs={'size': 2000}) + _image = image_fakes.create_one_image(attrs={'size': 2000}) columns = ( 'container_format', 'disk_format', diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py index 49ce400d92..910bd726ff 100644 --- a/openstackclient/tests/unit/image/v2/fakes.py +++ b/openstackclient/tests/unit/image/v2/fakes.py @@ -23,9 +23,6 @@ from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit import utils -image_id = '0f41529e-7c12-4de8-be2d-181abb825b3c' -image_name = 'graven' - class FakeImagev2Client(object): @@ -63,75 +60,67 @@ def setUp(self): ) -class FakeImage(object): - """Fake one or more images. +def create_one_image(attrs=None): + """Create a fake image. + + :param attrs: A dictionary with all attributes of image + :type attrs: dict + :return: A fake Image object. + :rtype: `openstack.image.v2.image.Image` + """ + attrs = attrs or {} + + # Set default attribute + image_info = { + 'id': str(uuid.uuid4()), + 'name': 'image-name' + uuid.uuid4().hex, + 'owner_id': 'image-owner' + uuid.uuid4().hex, + 'is_protected': bool(random.choice([0, 1])), + 'visibility': random.choice(['public', 'private']), + 'tags': [uuid.uuid4().hex for r in range(2)], + } + + # Overwrite default attributes if there are some attributes set + image_info.update(attrs) + + return image.Image(**image_info) + + +def create_images(attrs=None, count=2): + """Create multiple fake images. - TODO(xiexs): Currently, only image API v2 is supported by this class. + :param attrs: A dictionary with all attributes of image + :type attrs: dict + :param count: The number of images to be faked + :type count: int + :return: A list of fake Image objects + :rtype: list """ + images = [] + for n in range(0, count): + images.append(create_one_image(attrs)) + + return images + + +def create_one_image_member(attrs=None): + """Create a fake image member. + + :param attrs: A dictionary with all attributes of image member + :type attrs: dict + :return: A fake Member object. + :rtype: `openstack.image.v2.member.Member` + """ + attrs = attrs or {} + + # Set default attribute + image_member_info = { + 'member_id': 'member-id-' + uuid.uuid4().hex, + 'image_id': 'image-id-' + uuid.uuid4().hex, + 'status': 'pending', + } + + # Overwrite default attributes if there are some attributes set + image_member_info.update(attrs) - @staticmethod - def create_one_image(attrs=None): - """Create a fake image. - - :param Dictionary attrs: - A dictionary with all attrbutes of image - :return: - A FakeResource object with id, name, owner, protected, - visibility, tags and size attrs - """ - attrs = attrs or {} - - # Set default attribute - image_info = { - 'id': str(uuid.uuid4()), - 'name': 'image-name' + uuid.uuid4().hex, - 'owner_id': 'image-owner' + uuid.uuid4().hex, - 'is_protected': bool(random.choice([0, 1])), - 'visibility': random.choice(['public', 'private']), - 'tags': [uuid.uuid4().hex for r in range(2)], - } - - # Overwrite default attributes if there are some attributes set - image_info.update(attrs) - - return image.Image(**image_info) - - @staticmethod - def create_images(attrs=None, count=2): - """Create multiple fake images. - - :param Dictionary attrs: - A dictionary with all attributes of image - :param Integer count: - The number of images to be faked - :return: - A list of FakeResource objects - """ - images = [] - for n in range(0, count): - images.append(FakeImage.create_one_image(attrs)) - - return images - - @staticmethod - def create_one_image_member(attrs=None): - """Create a fake image member. - - :param Dictionary attrs: - A dictionary with all attributes of image member - :return: - A FakeResource object with member_id, image_id and so on - """ - attrs = attrs or {} - - # Set default attribute - image_member_info = { - 'member_id': 'member-id-' + uuid.uuid4().hex, - 'image_id': 'image-id-' + uuid.uuid4().hex, - 'status': 'pending', - } - - # Overwrite default attributes if there are some attributes set - image_member_info.update(attrs) - - return member.Member(**image_member_info) + return member.Member(**image_member_info) diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index 35af6799f6..f15463738a 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.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 copy import io @@ -52,7 +51,7 @@ def setUp(self): self.domain_mock.reset_mock() def setup_images_mock(self, count): - images = image_fakes.FakeImage.create_images(count=count) + images = image_fakes.create_images(count=count) return images @@ -65,7 +64,7 @@ class TestImageCreate(TestImage): def setUp(self): super(TestImageCreate, self).setUp() - self.new_image = image_fakes.FakeImage.create_one_image() + self.new_image = image_fakes.create_one_image() self.client.create_image.return_value = self.new_image self.project_mock.get.return_value = self.project @@ -182,7 +181,7 @@ def test_image_create_with_unexist_project(self): '--protected', '--private', '--project', 'unexist_owner', - image_fakes.image_name, + 'graven', ] verifylist = [ ('container_format', 'ovf'), @@ -194,7 +193,7 @@ def test_image_create_with_unexist_project(self): ('public', False), ('private', True), ('project', 'unexist_owner'), - ('name', image_fakes.image_name), + ('name', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -302,8 +301,8 @@ class TestAddProjectToImage(TestImage): project = identity_fakes.FakeProject.create_one_project() domain = identity_fakes.FakeDomain.create_one_domain() - _image = image_fakes.FakeImage.create_one_image() - new_member = image_fakes.FakeImage.create_one_image_member( + _image = image_fakes.create_one_image() + new_member = image_fakes.create_one_image_member( attrs={'image_id': _image.id, 'member_id': project.id} ) @@ -435,7 +434,7 @@ def test_image_delete_multi_images(self): def test_image_delete_multi_images_exception(self): - images = image_fakes.FakeImage.create_images(count=2) + images = image_fakes.create_images(count=2) arglist = [ images[0].id, images[1].id, @@ -467,7 +466,7 @@ def test_image_delete_multi_images_exception(self): class TestImageList(TestImage): - _image = image_fakes.FakeImage.create_one_image() + _image = image_fakes.create_one_image() columns = ( 'ID', @@ -786,18 +785,13 @@ def test_image_list_project_option(self): @mock.patch('osc_lib.utils.find_resource') def test_image_list_marker_option(self, fr_mock): - # tangchen: Since image_fakes.IMAGE is a dict, it cannot offer a .id - # operation. Will fix this by using FakeImage class instead - # of IMAGE dict. self.client.find_image = mock.Mock(return_value=self._image) -# fr_mock.return_value = mock.Mock() -# fr_mock.return_value.id = image_fakes.image_id arglist = [ - '--marker', image_fakes.image_name, + '--marker', 'graven', ] verifylist = [ - ('marker', image_fakes.image_name), + ('marker', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -806,7 +800,7 @@ def test_image_list_marker_option(self, fr_mock): marker=self._image.id, ) - self.client.find_image.assert_called_with(image_fakes.image_name) + self.client.find_image.assert_called_with('graven') def test_image_list_name_option(self): arglist = [ @@ -869,8 +863,8 @@ def test_image_list_tag_option(self): class TestListImageProjects(TestImage): project = identity_fakes.FakeProject.create_one_project() - _image = image_fakes.FakeImage.create_one_image() - member = image_fakes.FakeImage.create_one_image_member( + _image = image_fakes.create_one_image() + member = image_fakes.create_one_image_member( attrs={'image_id': _image.id, 'member_id': project.id} ) @@ -920,7 +914,7 @@ class TestRemoveProjectImage(TestImage): def setUp(self): super(TestRemoveProjectImage, self).setUp() - self._image = image_fakes.FakeImage.create_one_image() + self._image = image_fakes.create_one_image() # This is the return value for utils.find_resource() self.client.find_image.return_value = self._image @@ -979,7 +973,7 @@ class TestImageSet(TestImage): project = identity_fakes.FakeProject.create_one_project() domain = identity_fakes.FakeDomain.create_one_domain() - _image = image_fakes.FakeImage.create_one_image({'tags': []}) + _image = image_fakes.create_one_image({'tags': []}) def setUp(self): super(TestImageSet, self).setUp() @@ -999,10 +993,10 @@ def setUp(self): def test_image_set_no_options(self): arglist = [ - image_fakes.image_id, + '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [ - ('image', image_fakes.image_id) + ('image', '0f41529e-7c12-4de8-be2d-181abb825b3c') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1013,8 +1007,8 @@ def test_image_set_no_options(self): self.image_members_mock.update.assert_not_called() def test_image_set_membership_option_accept(self): - membership = image_fakes.FakeImage.create_one_image_member( - attrs={'image_id': image_fakes.image_id, + membership = image_fakes.create_one_image_member( + attrs={'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', 'member_id': self.project.id} ) self.client.update_member.return_value = membership @@ -1044,21 +1038,21 @@ def test_image_set_membership_option_accept(self): self.client.update_image.assert_called_with(self._image.id) def test_image_set_membership_option_reject(self): - membership = image_fakes.FakeImage.create_one_image_member( - attrs={'image_id': image_fakes.image_id, + membership = image_fakes.create_one_image_member( + attrs={'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', 'member_id': self.project.id} ) self.client.update_member.return_value = membership arglist = [ '--reject', - image_fakes.image_id, + '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [ ('accept', False), ('reject', True), ('pending', False), - ('image', image_fakes.image_id) + ('image', '0f41529e-7c12-4de8-be2d-181abb825b3c') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1075,21 +1069,21 @@ def test_image_set_membership_option_reject(self): self.client.update_image.assert_called_with(self._image.id) def test_image_set_membership_option_pending(self): - membership = image_fakes.FakeImage.create_one_image_member( - attrs={'image_id': image_fakes.image_id, + membership = image_fakes.create_one_image_member( + attrs={'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', 'member_id': self.project.id} ) self.client.update_member.return_value = membership arglist = [ '--pending', - image_fakes.image_id, + '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [ ('accept', False), ('reject', False), ('pending', True), - ('image', image_fakes.image_id) + ('image', '0f41529e-7c12-4de8-be2d-181abb825b3c') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1149,11 +1143,11 @@ def test_image_set_with_unexist_project(self): arglist = [ '--project', 'unexist_owner', - image_fakes.image_id, + '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [ ('project', 'unexist_owner'), - ('image', image_fakes.image_id), + ('image', '0f41529e-7c12-4de8-be2d-181abb825b3c'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1165,14 +1159,14 @@ def test_image_set_bools1(self): arglist = [ '--protected', '--private', - image_fakes.image_name, + 'graven', ] verifylist = [ ('protected', True), ('unprotected', False), ('public', False), ('private', True), - ('image', image_fakes.image_name), + ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1193,14 +1187,14 @@ def test_image_set_bools2(self): arglist = [ '--unprotected', '--public', - image_fakes.image_name, + 'graven', ] verifylist = [ ('protected', False), ('unprotected', True), ('public', True), ('private', False), - ('image', image_fakes.image_name), + ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1221,11 +1215,11 @@ def test_image_set_properties(self): arglist = [ '--property', 'Alpha=1', '--property', 'Beta=2', - image_fakes.image_name, + 'graven', ] verifylist = [ ('properties', {'Alpha': '1', 'Beta': '2'}), - ('image', image_fakes.image_name), + ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1250,7 +1244,7 @@ def test_image_set_fake_properties(self): '--os-distro', 'cpm', '--os-version', '2.2H', '--ramdisk-id', 'xyzpdq', - image_fakes.image_name, + 'graven', ] verifylist = [ ('architecture', 'z80'), @@ -1259,7 +1253,7 @@ def test_image_set_fake_properties(self): ('os_distro', 'cpm'), ('os_version', '2.2H'), ('ramdisk_id', 'xyzpdq'), - ('image', image_fakes.image_name), + ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1283,11 +1277,11 @@ def test_image_set_fake_properties(self): def test_image_set_tag(self): arglist = [ '--tag', 'test-tag', - image_fakes.image_name, + 'graven', ] verifylist = [ ('tags', ['test-tag']), - ('image', image_fakes.image_name), + ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1307,11 +1301,11 @@ def test_image_set_activate(self): arglist = [ '--tag', 'test-tag', '--activate', - image_fakes.image_name, + 'graven', ] verifylist = [ ('tags', ['test-tag']), - ('image', image_fakes.image_name), + ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1335,11 +1329,11 @@ def test_image_set_deactivate(self): arglist = [ '--tag', 'test-tag', '--deactivate', - image_fakes.image_name, + 'graven', ] verifylist = [ ('tags', ['test-tag']), - ('image', image_fakes.image_name), + ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1365,11 +1359,11 @@ def test_image_set_tag_merge(self): self.client.find_image.return_value = old_image arglist = [ '--tag', 'test-tag', - image_fakes.image_name, + 'graven', ] verifylist = [ ('tags', ['test-tag']), - ('image', image_fakes.image_name), + ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1391,11 +1385,11 @@ def test_image_set_tag_merge_dupe(self): self.client.find_image.return_value = old_image arglist = [ '--tag', 'old1', - image_fakes.image_name, + 'graven', ] verifylist = [ ('tags', ['old1']), - ('image', image_fakes.image_name), + ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1415,11 +1409,11 @@ def test_image_set_dead_options(self): arglist = [ '--visibility', '1-mile', - image_fakes.image_name, + 'graven', ] verifylist = [ ('visibility', '1-mile'), - ('image', image_fakes.image_name), + ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1431,12 +1425,12 @@ def test_image_set_numeric_options_to_zero(self): arglist = [ '--min-disk', '0', '--min-ram', '0', - image_fakes.image_name, + 'graven', ] verifylist = [ ('min_disk', 0), ('min_ram', 0), - ('image', image_fakes.image_name), + ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1457,13 +1451,13 @@ def test_image_set_hidden(self): arglist = [ '--hidden', '--public', - image_fakes.image_name, + 'graven', ] verifylist = [ ('hidden', True), ('public', True), ('private', False), - ('image', image_fakes.image_name), + ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1484,13 +1478,13 @@ def test_image_set_unhidden(self): arglist = [ '--unhidden', '--public', - image_fakes.image_name, + 'graven', ] verifylist = [ ('hidden', False), ('public', True), ('private', False), - ('image', image_fakes.image_name), + ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1510,10 +1504,10 @@ def test_image_set_unhidden(self): class TestImageShow(TestImage): - new_image = image_fakes.FakeImage.create_one_image( + new_image = image_fakes.create_one_image( attrs={'size': 1000}) - _data = image_fakes.FakeImage.create_one_image() + _data = image_fakes.create_one_image() columns = ( 'id', 'name', 'owner', 'protected', 'tags', 'visibility' @@ -1538,10 +1532,10 @@ def setUp(self): def test_image_show(self): arglist = [ - image_fakes.image_id, + '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [ - ('image', image_fakes.image_id), + ('image', '0f41529e-7c12-4de8-be2d-181abb825b3c'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1550,7 +1544,7 @@ def test_image_show(self): # data to be shown. columns, data = self.cmd.take_action(parsed_args) self.client.find_image.assert_called_with( - image_fakes.image_id, + '0f41529e-7c12-4de8-be2d-181abb825b3c', ignore_missing=False ) @@ -1592,7 +1586,7 @@ def setUp(self): attrs['hw_rng_model'] = 'virtio' attrs['prop'] = 'test' attrs['prop2'] = 'fake' - self.image = image_fakes.FakeImage.create_one_image(attrs) + self.image = image_fakes.create_one_image(attrs) self.client.find_image.return_value = self.image self.client.remove_tag.return_value = self.image @@ -1603,10 +1597,10 @@ def setUp(self): def test_image_unset_no_options(self): arglist = [ - image_fakes.image_id, + '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [ - ('image', image_fakes.image_id) + ('image', '0f41529e-7c12-4de8-be2d-181abb825b3c') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1681,7 +1675,7 @@ def test_image_unset_mixed_option(self): class TestImageSave(TestImage): - image = image_fakes.FakeImage.create_one_image({}) + image = image_fakes.create_one_image({}) def setUp(self): super(TestImageSave, self).setUp() diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py index 702f79ed83..584eca2a7b 100644 --- a/openstackclient/tests/unit/volume/v1/test_volume.py +++ b/openstackclient/tests/unit/volume/v1/test_volume.py @@ -317,7 +317,7 @@ def test_volume_create_properties(self): self.assertCountEqual(self.datalist, data) def test_volume_create_image_id(self): - image = image_fakes.FakeImage.create_one_image() + image = image_fakes.create_one_image() self.images_mock.get.return_value = image arglist = [ @@ -360,7 +360,7 @@ def test_volume_create_image_id(self): self.assertCountEqual(self.datalist, data) def test_volume_create_image_name(self): - image = image_fakes.FakeImage.create_one_image() + image = image_fakes.create_one_image() self.images_mock.get.return_value = image arglist = [ diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index 4aa6f906cc..ec82e6746b 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -221,7 +221,7 @@ def test_volume_create_properties(self): self.assertCountEqual(self.datalist, data) def test_volume_create_image_id(self): - image = image_fakes.FakeImage.create_one_image() + image = image_fakes.create_one_image() self.find_image_mock.return_value = image arglist = [ @@ -259,7 +259,7 @@ def test_volume_create_image_id(self): self.assertCountEqual(self.datalist, data) def test_volume_create_image_name(self): - image = image_fakes.FakeImage.create_one_image() + image = image_fakes.create_one_image() self.find_image_mock.return_value = image arglist = [ From 1feb676469f7ccd6a022027bf2e1ecee9cf6d548 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 17 Nov 2021 10:55:33 +0000 Subject: [PATCH 041/706] tests: Update fake image client in tests These clients are intended to fake out the old glanceclient client which we no longer use. They were only "working" because we weren't actually using any of the glancelclient-based stuff and were instead overriding everything within the tests. Move these overrides back to the main fake client and remove the crud. Change-Id: I92ee74a1df72a6dd23f9d2dc04342aab0cbd3210 Signed-off-by: Stephen Finucane --- openstackclient/tests/unit/image/v1/fakes.py | 8 +++--- .../tests/unit/image/v1/test_image.py | 6 +---- openstackclient/tests/unit/image/v2/fakes.py | 27 +++++++++++-------- .../tests/unit/image/v2/test_image.py | 23 +++++----------- 4 files changed, 28 insertions(+), 36 deletions(-) diff --git a/openstackclient/tests/unit/image/v1/fakes.py b/openstackclient/tests/unit/image/v1/fakes.py index 3097a42f59..164050c00e 100644 --- a/openstackclient/tests/unit/image/v1/fakes.py +++ b/openstackclient/tests/unit/image/v1/fakes.py @@ -22,11 +22,11 @@ from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes -class FakeImagev1Client(object): +class FakeImagev1Client: def __init__(self, **kwargs): self.images = mock.Mock() - self.images.resource_class = fakes.FakeResource(None, {}) + self.auth_token = kwargs['token'] self.management_url = kwargs['endpoint'] self.version = 1.0 @@ -35,7 +35,7 @@ def __init__(self, **kwargs): class TestImagev1(utils.TestCommand): def setUp(self): - super(TestImagev1, self).setUp() + super().setUp() self.app.client_manager.image = FakeImagev1Client( endpoint=fakes.AUTH_URL, @@ -46,6 +46,8 @@ def setUp(self): token=fakes.AUTH_TOKEN, ) + self.client = self.app.client_manager.image + def create_one_image(attrs=None): """Create a fake image. diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py index 06519800ce..6c65f9a395 100644 --- a/openstackclient/tests/unit/image/v1/test_image.py +++ b/openstackclient/tests/unit/image/v1/test_image.py @@ -25,11 +25,7 @@ class TestImage(image_fakes.TestImagev1): - def setUp(self): - super(TestImage, self).setUp() - - self.app.client_manager.image = mock.Mock() - self.client = self.app.client_manager.image + pass class TestImageCreate(TestImage): diff --git a/openstackclient/tests/unit/image/v2/fakes.py b/openstackclient/tests/unit/image/v2/fakes.py index 910bd726ff..a0eda6d23a 100644 --- a/openstackclient/tests/unit/image/v2/fakes.py +++ b/openstackclient/tests/unit/image/v2/fakes.py @@ -24,21 +24,26 @@ from openstackclient.tests.unit import utils -class FakeImagev2Client(object): +class FakeImagev2Client: def __init__(self, **kwargs): self.images = mock.Mock() - self.images.resource_class = fakes.FakeResource(None, {}) - self.image_members = mock.Mock() - self.image_members.resource_class = fakes.FakeResource(None, {}) - self.image_tags = mock.Mock() - self.image_tags.resource_class = fakes.FakeResource(None, {}) - + self.create_image = mock.Mock() + self.delete_image = mock.Mock() + self.update_image = mock.Mock() self.find_image = mock.Mock() - self.find_image.resource_class = fakes.FakeResource(None, {}) - self.get_image = mock.Mock() - self.get_image.resource_class = fakes.FakeResource(None, {}) + self.download_image = mock.Mock() + self.reactivate_image = mock.Mock() + self.deactivate_image = mock.Mock() + + self.members = mock.Mock() + self.add_member = mock.Mock() + self.remove_member = mock.Mock() + self.update_member = mock.Mock() + + self.remove_tag = mock.Mock() + self.auth_token = kwargs['token'] self.management_url = kwargs['endpoint'] self.version = 2.0 @@ -47,7 +52,7 @@ def __init__(self, **kwargs): class TestImagev2(utils.TestCommand): def setUp(self): - super(TestImagev2, self).setUp() + super().setUp() self.app.client_manager.image = FakeImagev2Client( endpoint=fakes.AUTH_URL, diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index f15463738a..510976f7e8 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -32,18 +32,9 @@ class TestImage(image_fakes.TestImagev2): def setUp(self): super(TestImage, self).setUp() - # Get shortcuts to the Mocks in image client - # SDK proxy mock - self.app.client_manager.image = mock.Mock() + # Get shortcuts to mocked image client self.client = self.app.client_manager.image - self.client.remove_member = mock.Mock() - - self.client.create_image = mock.Mock() - self.client.update_image = mock.Mock() - self.image_members_mock = self.app.client_manager.image.image_members - self.image_tags_mock = self.app.client_manager.image.image_tags - # Get shortcut to the Mocks in identity client self.project_mock = self.app.client_manager.identity.projects self.project_mock.reset_mock() @@ -483,11 +474,7 @@ class TestImageList(TestImage): def setUp(self): super(TestImageList, self).setUp() - self.api_mock = mock.Mock() - self.api_mock.side_effect = [ - [self._image], [], - ] - self.client.images = self.api_mock + self.client.images.side_effect = [[self._image], []] # Get the command object to test self.cmd = image.ListImage(self.app, None) @@ -1003,8 +990,10 @@ def test_image_set_no_options(self): result = self.cmd.take_action(parsed_args) self.assertIsNone(result) - - self.image_members_mock.update.assert_not_called() + # we'll have called this but not set anything + self.app.client_manager.image.update_image.called_once_with( + self._image.id, + ) def test_image_set_membership_option_accept(self): membership = image_fakes.create_one_image_member( From 61fac5b79e2e4a4120046b2f830e4745bb383fb3 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 17 Nov 2021 11:31:55 +0000 Subject: [PATCH 042/706] image: Sanity check the 'SetImage' command This was a very difficult command to grok, due to the layering on of additional features over the years. Make this a little easier to follow by grouping related logic and making use of argparse features. Change-Id: I4e1a0aed09ea5d6a8c26ec3e888c9c7b6cefc25a Signed-off-by: Stephen Finucane --- openstackclient/image/v2/image.py | 90 ++++++++++--------- .../tests/unit/image/v2/test_image.py | 12 +-- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index becb54f456..407c129294 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -1012,17 +1012,26 @@ def get_parser(self, prog_name): membership_group = parser.add_mutually_exclusive_group() membership_group.add_argument( "--accept", - action="store_true", + action="store_const", + const="accepted", + dest="membership", + default=None, help=_("Accept the image membership"), ) membership_group.add_argument( "--reject", - action="store_true", + action="store_const", + const="rejected", + dest="membership", + default=None, help=_("Reject the image membership"), ) membership_group.add_argument( "--pending", - action="store_true", + action="store_const", + const="pending", + dest="membership", + default=None, help=_("Reset the image membership to 'pending'"), ) @@ -1053,6 +1062,43 @@ def take_action(self, parsed_args): _("ERROR: --%s was given, which is an Image v1 option" " that is no longer supported in Image v2") % deadopt) + image = image_client.find_image( + parsed_args.image, ignore_missing=False, + ) + project_id = None + if parsed_args.project: + project_id = common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + + # handle activation status changes + + activation_status = None + if parsed_args.deactivate or parsed_args.activate: + if parsed_args.deactivate: + image_client.deactivate_image(image.id) + activation_status = "deactivated" + if parsed_args.activate: + image_client.reactivate_image(image.id) + activation_status = "activated" + + # handle membership changes + + if parsed_args.membership: + # If a specific project is not passed, assume we want to update + # our own membership + if not project_id: + project_id = self.app.client_manager.auth_ref.project_id + image_client.update_member( + image=image.id, + member=project_id, + status=parsed_args.membership, + ) + + # handle everything else + kwargs = {} copy_attrs = ('architecture', 'container_format', 'disk_format', 'file', 'instance_id', 'kernel_id', 'locations', @@ -1089,48 +1135,12 @@ def take_action(self, parsed_args): kwargs['visibility'] = 'community' if parsed_args.shared: kwargs['visibility'] = 'shared' - project_id = None if parsed_args.project: - project_id = common.find_project( - identity_client, - parsed_args.project, - parsed_args.project_domain, - ).id + # We already did the project lookup above kwargs['owner_id'] = project_id - - image = image_client.find_image(parsed_args.image, - ignore_missing=False) - - # image = utils.find_resource( - # image_client.images, parsed_args.image) - - activation_status = None - if parsed_args.deactivate: - image_client.deactivate_image(image.id) - activation_status = "deactivated" - if parsed_args.activate: - image_client.reactivate_image(image.id) - activation_status = "activated" - - membership_group_args = ('accept', 'reject', 'pending') - membership_status = [status for status in membership_group_args - if getattr(parsed_args, status)] - if membership_status: - # If a specific project is not passed, assume we want to update - # our own membership - if not project_id: - project_id = self.app.client_manager.auth_ref.project_id - # The mutually exclusive group of the arg parser ensure we have at - # most one item in the membership_status list. - if membership_status[0] != 'pending': - membership_status[0] += 'ed' # Glance expects the past form - image_client.update_member( - image=image.id, member=project_id, status=membership_status[0]) - if parsed_args.tags: # Tags should be extended, but duplicates removed kwargs['tags'] = list(set(image.tags).union(set(parsed_args.tags))) - if parsed_args.hidden is not None: kwargs['is_hidden'] = parsed_args.hidden diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index 510976f7e8..7ccc9f0fb4 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -1007,9 +1007,7 @@ def test_image_set_membership_option_accept(self): self._image.id, ] verifylist = [ - ('accept', True), - ('reject', False), - ('pending', False), + ('membership', 'accepted'), ('image', self._image.id) ] @@ -1038,9 +1036,7 @@ def test_image_set_membership_option_reject(self): '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [ - ('accept', False), - ('reject', True), - ('pending', False), + ('membership', 'rejected'), ('image', '0f41529e-7c12-4de8-be2d-181abb825b3c') ] @@ -1069,9 +1065,7 @@ def test_image_set_membership_option_pending(self): '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [ - ('accept', False), - ('reject', False), - ('pending', True), + ('membership', 'pending'), ('image', '0f41529e-7c12-4de8-be2d-181abb825b3c') ] From b3d09ffc37f6b577ce479a5203c6c882062fee74 Mon Sep 17 00:00:00 2001 From: JieonLee Date: Sun, 21 Nov 2021 05:05:25 +0000 Subject: [PATCH 043/706] Add missing command mapping in nova nova command: instance-action openstack command: server event show Change-Id: I8e5dad90cfd28b1f0d65be688651918869f679e4 --- doc/source/cli/data/nova.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/cli/data/nova.csv b/doc/source/cli/data/nova.csv index dd8992f68c..0ab2b2fee4 100644 --- a/doc/source/cli/data/nova.csv +++ b/doc/source/cli/data/nova.csv @@ -46,7 +46,7 @@ hypervisor-show,hypervisor show,Display the details of the specified hypervisor. hypervisor-stats,hypervisor stats show,Get hypervisor statistics over all compute nodes. hypervisor-uptime,,Display the uptime of the specified hypervisor. image-create,server image create,Create a new image by taking a snapshot of a running server. -instance-action,,Show an action. +instance-action,server event show,Show an action. instance-action-list,,List actions on a server. instance-usage-audit-log,,List/Get server usage audits. interface-attach,server add port / server add floating ip / server add fixed ip,Attach a network interface to a server. From 3078a0a121743c387d83d7f27ce3d8fd8fbb4ccf Mon Sep 17 00:00:00 2001 From: Diwei Zhu Date: Thu, 28 Oct 2021 23:25:52 +0000 Subject: [PATCH 044/706] Switch command server add volume to sdk. File tests.unit.volume.v2.fakes is modified to provide sdk volume fakes. File tests.unit.compute.v2.fakes is modified to provide sdk volume attachment fakes. For test, setup_sdk_volumes_mock() method is created so that volumes are created in similar way as servers are created. Change-Id: I290ba83b6ba27a1377ab73fd0ae06ecced25efd1 --- openstackclient/compute/v2/server.py | 38 ++- .../tests/unit/compute/v2/fakes.py | 56 ++++ .../tests/unit/compute/v2/test_server.py | 258 +++++++++--------- openstackclient/tests/unit/volume/v2/fakes.py | 110 ++++++-- ...er-add-volume-to-sdk-685e036a88839651.yaml | 4 + 5 files changed, 296 insertions(+), 170 deletions(-) create mode 100644 releasenotes/notes/migrate-server-add-volume-to-sdk-685e036a88839651.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 08627f9bc5..18c1197cc9 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -548,24 +548,25 @@ def get_parser(self, prog_name): 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 - server = utils.find_resource( - compute_client.servers, + server = compute_client.find_server( parsed_args.server, + ignore_missing=False, ) - volume = utils.find_resource( - volume_client.volumes, + volume = volume_client.find_volume( parsed_args.volume, + ignore_missing=False, ) kwargs = { + "volumeId": volume.id, "device": parsed_args.device } if parsed_args.tag: - if compute_client.api_version < api_versions.APIVersion('2.49'): + if not sdk_utils.supports_microversion(compute_client, '2.49'): msg = _( '--os-compute-api-version 2.49 or greater is required to ' 'support the --tag option' @@ -575,7 +576,7 @@ def take_action(self, parsed_args): kwargs['tag'] = parsed_args.tag if parsed_args.enable_delete_on_termination: - if compute_client.api_version < api_versions.APIVersion('2.79'): + if not sdk_utils.supports_microversion(compute_client, '2.79'): msg = _( '--os-compute-api-version 2.79 or greater is required to ' 'support the --enable-delete-on-termination option.' @@ -585,7 +586,7 @@ def take_action(self, parsed_args): kwargs['delete_on_termination'] = True if parsed_args.disable_delete_on_termination: - if compute_client.api_version < api_versions.APIVersion('2.79'): + if not sdk_utils.supports_microversion(compute_client, '2.79'): msg = _( '--os-compute-api-version 2.79 or greater is required to ' 'support the --disable-delete-on-termination option.' @@ -594,28 +595,23 @@ def take_action(self, parsed_args): kwargs['delete_on_termination'] = False - volume_attachment = compute_client.volumes.create_server_volume( - server.id, - volume.id, - **kwargs + volume_attachment = compute_client.create_volume_attachment( + server, + **kwargs, ) - columns = ('id', 'serverId', 'volumeId', 'device') + columns = ('id', 'server id', 'volume id', 'device') column_headers = ('ID', 'Server ID', 'Volume ID', 'Device') - if compute_client.api_version >= api_versions.APIVersion('2.49'): + if sdk_utils.supports_microversion(compute_client, '2.49'): columns += ('tag',) column_headers += ('Tag',) - if compute_client.api_version >= api_versions.APIVersion('2.79'): + if sdk_utils.supports_microversion(compute_client, '2.79'): columns += ('delete_on_termination',) column_headers += ('Delete On Termination',) return ( column_headers, - utils.get_item_properties( - volume_attachment, - columns, - mixed_case_fields=('serverId', 'volumeId'), - ) + utils.get_item_properties(volume_attachment, columns,) ) diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 23468ebc4d..7618c229c5 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -21,6 +21,7 @@ from novaclient import api_versions from openstack.compute.v2 import flavor as _flavor from openstack.compute.v2 import server +from openstack.compute.v2 import volume_attachment from openstackclient.api import compute_v2 from openstackclient.tests.unit import fakes @@ -1803,3 +1804,58 @@ def create_volume_attachments(attrs=None, methods=None, count=2): attrs, methods)) return volume_attachments + + @staticmethod + def create_one_sdk_volume_attachment(attrs=None, methods=None): + """Create a fake sdk VolumeAttachment. + + :param dict attrs: + A dictionary with all attributes + :param dict methods: + A dictionary with all methods + :return: + A fake VolumeAttachment object, with id, device, and so on + """ + attrs = attrs or {} + methods = methods or {} + + # Set default attributes. + volume_attachment_info = { + "id": uuid.uuid4().hex, + "device": "/dev/sdb", + "server_id": uuid.uuid4().hex, + "volume_id": uuid.uuid4().hex, + # introduced in API microversion 2.70 + "tag": "foo", + # introduced in API microversion 2.79 + "delete_on_termination": True, + # introduced in API microversion 2.89 + "attachment_id": uuid.uuid4().hex, + "bdm_uuid": uuid.uuid4().hex + } + + # Overwrite default attributes. + volume_attachment_info.update(attrs) + + return volume_attachment.VolumeAttachment(**volume_attachment_info) + + @staticmethod + def create_sdk_volume_attachments(attrs=None, methods=None, count=2): + """Create multiple fake VolumeAttachment objects (BDMs). + + :param dict attrs: + A dictionary with all attributes + :param dict methods: + A dictionary with all methods + :param int count: + The number of volume attachments to fake + :return: + A list of VolumeAttachment objects faking the volume attachments. + """ + volume_attachments = [] + for i in range(0, count): + volume_attachments.append( + FakeVolumeAttachment.create_one_sdk_volume_attachment( + attrs, methods)) + + return volume_attachments diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 9623cb0abf..203e47ebb3 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -105,6 +105,9 @@ def setUp(self): self.volumes_mock = self.app.client_manager.volume.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 + # Get a shortcut to the volume client VolumeManager Mock self.snapshots_mock = self.app.client_manager.volume.volume_snapshots self.snapshots_mock.reset_mock() @@ -146,13 +149,18 @@ def setup_sdk_servers_mock(self, count): ) # This is the return value for compute_client.find_server() - self.sdk_client.find_server = compute_fakes.FakeServer.get_servers( - servers, - 0, - ) + self.sdk_client.find_server.side_effect = servers return servers + def setup_sdk_volumes_mock(self, count): + volumes = volume_fakes.FakeVolume.create_sdk_volumes(count=count) + + # This is the return value for volume_client.find_volume() + self.sdk_volume_client.find_volume.side_effect = volumes + + return volumes + def run_method_with_servers(self, method_name, server_count): servers = self.setup_servers_mock(server_count) @@ -680,31 +688,38 @@ class TestServerVolume(TestServer): def setUp(self): super(TestServerVolume, self).setUp() - self.volume = volume_fakes.FakeVolume.create_one_volume() - self.volumes_mock.get.return_value = self.volume - self.methods = { - 'create_server_volume': None, + 'create_volume_attachment': None, } # Get the command object to test self.cmd = server.AddServerVolume(self.app, None) - def test_server_add_volume(self): - servers = self.setup_servers_mock(count=1) - volume_attachment = \ - compute_fakes.FakeVolumeAttachment.create_one_volume_attachment() - self.servers_volumes_mock.create_server_volume.return_value = \ - volume_attachment + self.servers = self.setup_sdk_servers_mock(count=1) + self.volumes = self.setup_sdk_volumes_mock(count=1) + + attrs = { + 'server_id': self.servers[0].id, + 'volume_id': self.volumes[0].id, + } + self.volume_attachment = \ + compute_fakes.FakeVolumeAttachment.\ + create_one_sdk_volume_attachment(attrs=attrs) + + self.sdk_client.create_volume_attachment.return_value = \ + self.volume_attachment + + @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) + def test_server_add_volume(self, sm_mock): arglist = [ '--device', '/dev/sdb', - servers[0].id, - self.volume.id, + self.servers[0].id, + self.volumes[0].id, ] verifylist = [ - ('server', servers[0].id), - ('volume', self.volume.id), + ('server', self.servers[0].id), + ('volume', self.volumes[0].id), ('device', '/dev/sdb'), ] @@ -712,39 +727,36 @@ def test_server_add_volume(self): expected_columns = ('ID', 'Server ID', 'Volume ID', 'Device') expected_data = ( - volume_attachment.id, - volume_attachment.serverId, - volume_attachment.volumeId, - volume_attachment.device, + self.volume_attachment.id, + self.volume_attachment.server_id, + self.volume_attachment.volume_id, + '/dev/sdb', ) columns, data = self.cmd.take_action(parsed_args) - self.servers_volumes_mock.create_server_volume.assert_called_once_with( - servers[0].id, self.volume.id, device='/dev/sdb') self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) + self.sdk_client.create_volume_attachment.assert_called_once_with( + self.servers[0], volumeId=self.volumes[0].id, device='/dev/sdb') - def test_server_add_volume_with_tag(self): - # requires API 2.49 or later - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.49') - - servers = self.setup_servers_mock(count=1) - volume_attachment = \ - compute_fakes.FakeVolumeAttachment.create_one_volume_attachment() - self.servers_volumes_mock.create_server_volume.return_value = \ - volume_attachment + @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 arglist = [ '--device', '/dev/sdb', '--tag', 'foo', - servers[0].id, - self.volume.id, + self.servers[0].id, + self.volumes[0].id, ] verifylist = [ - ('server', servers[0].id), - ('volume', self.volume.id), + ('server', self.servers[0].id), + ('volume', self.volumes[0].id), ('device', '/dev/sdb'), ('tag', 'foo'), ] @@ -753,33 +765,33 @@ def test_server_add_volume_with_tag(self): expected_columns = ('ID', 'Server ID', 'Volume ID', 'Device', 'Tag') expected_data = ( - volume_attachment.id, - volume_attachment.serverId, - volume_attachment.volumeId, - volume_attachment.device, - volume_attachment.tag, + self.volume_attachment.id, + self.volume_attachment.server_id, + self.volume_attachment.volume_id, + self.volume_attachment.device, + self.volume_attachment.tag, ) columns, data = self.cmd.take_action(parsed_args) - self.servers_volumes_mock.create_server_volume.assert_called_once_with( - servers[0].id, self.volume.id, device='/dev/sdb', tag='foo') self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) + self.sdk_client.create_volume_attachment.assert_called_once_with( + self.servers[0], + volumeId=self.volumes[0].id, + device='/dev/sdb', + tag='foo') - def test_server_add_volume_with_tag_pre_v249(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.48') - - servers = self.setup_servers_mock(count=1) + @mock.patch.object(sdk_utils, 'supports_microversion', return_value=False) + def test_server_add_volume_with_tag_pre_v249(self, sm_mock): arglist = [ - servers[0].id, - self.volume.id, + self.servers[0].id, + self.volumes[0].id, '--tag', 'foo', ] verifylist = [ - ('server', servers[0].id), - ('volume', self.volume.id), + ('server', self.servers[0].id), + ('volume', self.volumes[0].id), ('tag', 'foo'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -792,26 +804,22 @@ def test_server_add_volume_with_tag_pre_v249(self): '--os-compute-api-version 2.49 or greater is required', str(ex)) - def test_server_add_volume_with_enable_delete_on_termination(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.79') - - servers = self.setup_servers_mock(count=1) - volume_attachment = \ - compute_fakes.FakeVolumeAttachment.create_one_volume_attachment() - self.servers_volumes_mock.create_server_volume.return_value = \ - volume_attachment - + @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) + def test_server_add_volume_with_enable_delete_on_termination( + self, + sm_mock, + ): + self.volume_attachment.delete_on_termination = True arglist = [ '--enable-delete-on-termination', '--device', '/dev/sdb', - servers[0].id, - self.volume.id, + self.servers[0].id, + self.volumes[0].id, ] verifylist = [ - ('server', servers[0].id), - ('volume', self.volume.id), + ('server', self.servers[0].id), + ('volume', self.volumes[0].id), ('device', '/dev/sdb'), ('enable_delete_on_termination', True), ] @@ -826,42 +834,40 @@ def test_server_add_volume_with_enable_delete_on_termination(self): 'Delete On Termination', ) expected_data = ( - volume_attachment.id, - volume_attachment.serverId, - volume_attachment.volumeId, - volume_attachment.device, - volume_attachment.tag, - volume_attachment.delete_on_termination, + self.volume_attachment.id, + self.volume_attachment.server_id, + self.volume_attachment.volume_id, + self.volume_attachment.device, + self.volume_attachment.tag, + self.volume_attachment.delete_on_termination, ) columns, data = self.cmd.take_action(parsed_args) - - self.servers_volumes_mock.create_server_volume.assert_called_once_with( - servers[0].id, self.volume.id, - device='/dev/sdb', delete_on_termination=True) self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) + self.sdk_client.create_volume_attachment.assert_called_once_with( + self.servers[0], + volumeId=self.volumes[0].id, + device='/dev/sdb', + delete_on_termination=True) - def test_server_add_volume_with_disable_delete_on_termination(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.79') - - servers = self.setup_servers_mock(count=1) - volume_attachment = \ - compute_fakes.FakeVolumeAttachment.create_one_volume_attachment() - self.servers_volumes_mock.create_server_volume.return_value = \ - volume_attachment + @mock.patch.object(sdk_utils, 'supports_microversion', return_value=True) + def test_server_add_volume_with_disable_delete_on_termination( + self, + sm_mock, + ): + self.volume_attachment.delete_on_termination = False arglist = [ '--disable-delete-on-termination', '--device', '/dev/sdb', - servers[0].id, - self.volume.id, + self.servers[0].id, + self.volumes[0].id, ] verifylist = [ - ('server', servers[0].id), - ('volume', self.volume.id), + ('server', self.servers[0].id), + ('volume', self.volumes[0].id), ('device', '/dev/sdb'), ('disable_delete_on_termination', True), ] @@ -876,37 +882,43 @@ def test_server_add_volume_with_disable_delete_on_termination(self): 'Delete On Termination', ) expected_data = ( - volume_attachment.id, - volume_attachment.serverId, - volume_attachment.volumeId, - volume_attachment.device, - volume_attachment.tag, - volume_attachment.delete_on_termination, + self.volume_attachment.id, + self.volume_attachment.server_id, + self.volume_attachment.volume_id, + self.volume_attachment.device, + self.volume_attachment.tag, + self.volume_attachment.delete_on_termination, ) columns, data = self.cmd.take_action(parsed_args) - self.servers_volumes_mock.create_server_volume.assert_called_once_with( - servers[0].id, self.volume.id, - device='/dev/sdb', delete_on_termination=False) self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) + self.sdk_client.create_volume_attachment.assert_called_once_with( + self.servers[0], + volumeId=self.volumes[0].id, + device='/dev/sdb', + 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, ): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.78') + def side_effect(compute_client, version): + if version == '2.79': + return False + return True + sm_mock.side_effect = side_effect - servers = self.setup_servers_mock(count=1) arglist = [ - servers[0].id, - self.volume.id, + self.servers[0].id, + self.volumes[0].id, '--enable-delete-on-termination', ] verifylist = [ - ('server', servers[0].id), - ('volume', self.volume.id), + ('server', self.servers[0].id), + ('volume', self.volumes[0].id), ('enable_delete_on_termination', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -917,21 +929,25 @@ def test_server_add_volume_with_enable_delete_on_termination_pre_v279( self.assertIn('--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, ): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.78') + def side_effect(compute_client, version): + if version == '2.79': + return False + return True + sm_mock.side_effect = side_effect - servers = self.setup_servers_mock(count=1) arglist = [ - servers[0].id, - self.volume.id, + self.servers[0].id, + self.volumes[0].id, '--disable-delete-on-termination', ] verifylist = [ - ('server', servers[0].id), - ('volume', self.volume.id), + ('server', self.servers[0].id), + ('volume', self.volumes[0].id), ('disable_delete_on_termination', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -942,24 +958,22 @@ def test_server_add_volume_with_disable_delete_on_termination_pre_v279( self.assertIn('--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.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.79') - - servers = self.setup_servers_mock(count=1) arglist = [ '--enable-delete-on-termination', '--disable-delete-on-termination', '--device', '/dev/sdb', - servers[0].id, - self.volume.id, + self.servers[0].id, + self.volumes[0].id, ] verifylist = [ - ('server', servers[0].id), - ('volume', self.volume.id), + ('server', self.servers[0].id), + ('volume', self.volumes[0].id), ('device', '/dev/sdb'), ('enable_delete_on_termination', True), ('disable_delete_on_termination', True), diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index b5f66d4b1d..96e381d3cc 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -18,6 +18,7 @@ import uuid from cinderclient import api_versions +from openstack.block_storage.v3 import volume from osc_lib.cli import format_columns from openstackclient.tests.unit import fakes @@ -46,7 +47,7 @@ class FakeTransfer(object): def create_one_transfer(attrs=None): """Create a fake transfer. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes of Transfer Request :return: A FakeResource object with volume_id, name, id. @@ -75,7 +76,7 @@ def create_one_transfer(attrs=None): def create_transfers(attrs=None, count=2): """Create multiple fake transfers. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes of transfer :param Integer count: The number of transfers to be faked @@ -116,7 +117,7 @@ class FakeTypeAccess(object): def create_one_type_access(attrs=None): """Create a fake volume type access for project. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object, with Volume_type_ID and Project_ID. @@ -148,7 +149,7 @@ class FakeService(object): def create_one_service(attrs=None): """Create a fake service. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes of service :return: A FakeResource object with host, status, etc. @@ -180,7 +181,7 @@ def create_one_service(attrs=None): def create_services(attrs=None, count=2): """Create multiple fake services. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes of service :param Integer count: The number of services to be faked @@ -201,7 +202,7 @@ class FakeCapability(object): def create_one_capability(attrs=None): """Create a fake volume backend capability. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes of the Capabilities. :return: A FakeResource object with capability name and attrs. @@ -260,7 +261,7 @@ class FakePool(object): def create_one_pool(attrs=None): """Create a fake pool. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes of the pool :return: A FakeResource object with pool name and attrs. @@ -362,7 +363,7 @@ class FakeVolume(object): def create_one_volume(attrs=None): """Create a fake volume. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes of volume :return: A FakeResource object with id, name, status, etc. @@ -405,7 +406,7 @@ def create_one_volume(attrs=None): def create_volumes(attrs=None, count=2): """Create multiple fake volumes. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes of volume :param Integer count: The number of volumes to be faked @@ -418,6 +419,61 @@ def create_volumes(attrs=None, count=2): return volumes + @staticmethod + 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) + + @staticmethod + 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(FakeVolume.create_one_sdk_volume(attrs)) + + return volumes + @staticmethod def get_volumes(volumes=None, count=2): """Get an iterable MagicMock object with a list of faked volumes. @@ -484,7 +540,7 @@ class FakeAvailabilityZone(object): def create_one_availability_zone(attrs=None): """Create a fake AZ. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object with zoneName, zoneState, etc. @@ -509,7 +565,7 @@ def create_one_availability_zone(attrs=None): def create_availability_zones(attrs=None, count=2): """Create multiple fake AZs. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of AZs to fake @@ -532,7 +588,7 @@ class FakeBackup(object): def create_one_backup(attrs=None): """Create a fake backup. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object with id, name, volume_id, etc. @@ -565,7 +621,7 @@ def create_one_backup(attrs=None): def create_backups(attrs=None, count=2): """Create multiple fake backups. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of backups to fake @@ -636,7 +692,7 @@ class FakeConsistencyGroup(object): def create_one_consistency_group(attrs=None): """Create a fake consistency group. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object with id, name, description, etc. @@ -666,7 +722,7 @@ def create_one_consistency_group(attrs=None): def create_consistency_groups(attrs=None, count=2): """Create multiple fake consistency groups. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of consistency groups to fake @@ -713,7 +769,7 @@ class FakeConsistencyGroupSnapshot(object): def create_one_consistency_group_snapshot(attrs=None): """Create a fake consistency group snapshot. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object with id, name, description, etc. @@ -742,7 +798,7 @@ def create_one_consistency_group_snapshot(attrs=None): def create_consistency_group_snapshots(attrs=None, count=2): """Create multiple fake consistency group snapshots. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of consistency group snapshots to fake @@ -789,7 +845,7 @@ class FakeExtension(object): def create_one_extension(attrs=None): """Create a fake extension. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object with name, namespace, etc. @@ -825,7 +881,7 @@ class FakeQos(object): def create_one_qos(attrs=None): """Create a fake Qos specification. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object with id, name, consumer, etc. @@ -852,7 +908,7 @@ def create_one_qos(attrs=None): def create_one_qos_association(attrs=None): """Create a fake Qos specification association. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object with id, name, association_type, etc. @@ -878,7 +934,7 @@ def create_one_qos_association(attrs=None): def create_qoses(attrs=None, count=2): """Create multiple fake Qos specifications. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of Qos specifications to fake @@ -920,7 +976,7 @@ class FakeSnapshot(object): def create_one_snapshot(attrs=None): """Create a fake snapshot. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object with id, name, description, etc. @@ -951,7 +1007,7 @@ def create_one_snapshot(attrs=None): def create_snapshots(attrs=None, count=2): """Create multiple fake snapshots. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of snapshots to fake @@ -993,9 +1049,9 @@ class FakeVolumeType(object): def create_one_volume_type(attrs=None, methods=None): """Create a fake volume type. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes - :param Dictionary methods: + :param dict methods: A dictionary with all methods :return: A FakeResource object with id, name, description, etc. @@ -1025,7 +1081,7 @@ def create_one_volume_type(attrs=None, methods=None): def create_volume_types(attrs=None, count=2): """Create multiple fake volume_types. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :param int count: The number of types to fake @@ -1063,7 +1119,7 @@ def get_volume_types(volume_types=None, count=2): def create_one_encryption_volume_type(attrs=None): """Create a fake encryption volume type. - :param Dictionary attrs: + :param dict attrs: A dictionary with all attributes :return: A FakeResource object with volume_type_id etc. diff --git a/releasenotes/notes/migrate-server-add-volume-to-sdk-685e036a88839651.yaml b/releasenotes/notes/migrate-server-add-volume-to-sdk-685e036a88839651.yaml new file mode 100644 index 0000000000..54abdacbbc --- /dev/null +++ b/releasenotes/notes/migrate-server-add-volume-to-sdk-685e036a88839651.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Migrate openstack server add volume to using sdk. From 860d6360474b2f215097d1aa4018a57070e44924 Mon Sep 17 00:00:00 2001 From: "Dr. Jens Harbott" Date: Wed, 24 Nov 2021 06:46:22 +0100 Subject: [PATCH 045/706] Temporarily drop aodhclient from doc build Building plugin documentation is failing for aodhclient when running with latest pyparsing. Drop the plugin from docs for now until the issue can be fixed. Needed-By: https://review.opendev.org/c/openstack/requirements/+/818614/ Signed-off-by: Dr. Jens Harbott Change-Id: I1cc1efd9ff2004dd711ed9da0b1d9e8be31175f4 --- doc/source/cli/plugin-commands/aodh.rst | 4 ---- doc/source/cli/plugin-commands/index.rst | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) delete 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 deleted file mode 100644 index 5d8b4332cf..0000000000 --- a/doc/source/cli/plugin-commands/aodh.rst +++ /dev/null @@ -1,4 +0,0 @@ -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 4e1ce54b13..638dcbe560 100644 --- a/doc/source/cli/plugin-commands/index.rst +++ b/doc/source/cli/plugin-commands/index.rst @@ -7,7 +7,6 @@ Plugin Commands .. toctree:: :maxdepth: 1 - aodh barbican designate gnocchi @@ -29,6 +28,10 @@ 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 28cd5763de8706fb997bdd53deaf94aca0de5c52 Mon Sep 17 00:00:00 2001 From: Diwei Zhu Date: Fri, 26 Nov 2021 14:11:02 +0000 Subject: [PATCH 046/706] Add functional test for server add/remove volume. Change-Id: I86a76f32790cafcff1d94364fb72f8890a8cb025 --- .../functional/compute/v2/test_server.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py index 59b1fad5f9..0d07950e97 100644 --- a/openstackclient/tests/functional/compute/v2/test_server.py +++ b/openstackclient/tests/functional/compute/v2/test_server.py @@ -1119,3 +1119,62 @@ def test_server_add_remove_network_port(self): self.openstack('server delete ' + name) self.openstack('port delete ' + port_name) + + def test_server_add_remove_volume(self): + volume_wait_for = volume_common.BaseVolumeTests.wait_for_status + + name = uuid.uuid4().hex + cmd_output = json.loads(self.openstack( + 'server create -f json ' + + '--network private ' + + '--flavor ' + self.flavor_name + ' ' + + '--image ' + self.image_name + ' ' + + '--wait ' + + name + )) + + self.assertIsNotNone(cmd_output['id']) + self.assertEqual(name, cmd_output['name']) + self.addCleanup(self.openstack, 'server delete --wait ' + name) + server_id = cmd_output['id'] + + volume_name = uuid.uuid4().hex + cmd_output = json.loads(self.openstack( + 'volume create -f json ' + + '--size 1 ' + + volume_name + )) + + self.assertIsNotNone(cmd_output['id']) + self.assertEqual(volume_name, cmd_output['name']) + volume_wait_for('volume', volume_name, 'available') + self.addCleanup(self.openstack, 'volume delete ' + volume_name) + volume_id = cmd_output['id'] + + cmd_output = json.loads(self.openstack( + 'server add volume -f json ' + + name + ' ' + + volume_name + ' ' + + '--tag bar' + )) + + self.assertIsNotNone(cmd_output['ID']) + self.assertEqual(server_id, cmd_output['Server ID']) + self.assertEqual(volume_id, cmd_output['Volume ID']) + volume_attachment_id = cmd_output['ID'] + + cmd_output = json.loads(self.openstack( + 'server volume list -f json ' + + name + )) + + self.assertEqual(volume_attachment_id, cmd_output[0]['ID']) + self.assertEqual(server_id, cmd_output[0]['Server ID']) + self.assertEqual(volume_id, cmd_output[0]['Volume ID']) + + volume_wait_for('volume', volume_name, 'in-use') + self.openstack('server remove volume ' + name + ' ' + volume_name) + volume_wait_for('volume', volume_name, 'available') + + raw_output = self.openstack('server volume list ' + name) + self.assertEqual('\n', raw_output) From fae293dd5218cf4ea03d0a4c44d17b97987dea12 Mon Sep 17 00:00:00 2001 From: Diwei Zhu Date: Tue, 16 Nov 2021 19:08:58 +0000 Subject: [PATCH 047/706] Switch command server remove volume to sdk Change-Id: If6f6cf93b55a67e767c54de8ce21f25252cf99ca --- openstackclient/compute/v2/server.py | 27 ++++++----- .../tests/unit/compute/v2/test_server.py | 45 +++++++++++++++++-- ...remove-volume-to-sdk-47e9befd2672dcdf.yaml | 4 ++ 3 files changed, 63 insertions(+), 13 deletions(-) create mode 100644 releasenotes/notes/switch-server-remove-volume-to-sdk-47e9befd2672dcdf.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 18c1197cc9..121a7b82e8 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -3793,22 +3793,29 @@ def get_parser(self, prog_name): 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 - server = utils.find_resource( - compute_client.servers, + server = compute_client.find_server( parsed_args.server, + ignore_missing=False, ) - volume = utils.find_resource( - volume_client.volumes, + volume = volume_client.find_volume( parsed_args.volume, + ignore_missing=False, ) - compute_client.volumes.delete_server_volume( - server.id, - volume.id, - ) + volume_attachments = compute_client.volume_attachments(server) + for volume_attachment in volume_attachments: + if volume_attachment.volume_id == volume.id: + compute_client.delete_volume_attachment( + volume_attachment, + server, + ) + break + else: + msg = _('Target volume attachment not found.') + raise exceptions.CommandError(msg) class RescueServer(command.Command): diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 203e47ebb3..10ea07adb3 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -692,9 +692,6 @@ def setUp(self): 'create_volume_attachment': None, } - # Get the command object to test - self.cmd = server.AddServerVolume(self.app, None) - self.servers = self.setup_sdk_servers_mock(count=1) self.volumes = self.setup_sdk_volumes_mock(count=1) @@ -709,6 +706,15 @@ def setUp(self): self.sdk_client.create_volume_attachment.return_value = \ self.volume_attachment + +class TestServerAddVolume(TestServerVolume): + + def setUp(self): + super(TestServerAddVolume, self).setUp() + + # 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): @@ -985,6 +991,39 @@ def test_server_add_volume_with_disable_and_enable_delete_on_termination( 'with argument --enable-delete-on-termination', str(ex)) +class TestServerRemoveVolume(TestServerVolume): + + def setUp(self): + super(TestServerRemoveVolume, self).setUp() + + # Get the command object to test + self.cmd = server.RemoveServerVolume(self.app, None) + + def test_server_remove_volume(self): + self.sdk_client.volume_attachments.return_value = [ + self.volume_attachment + ] + + arglist = [ + self.servers[0].id, + self.volumes[0].id, + ] + + verifylist = [ + ('server', self.servers[0].id), + ('volume', self.volumes[0].id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertIsNone(result) + self.sdk_client.delete_volume_attachment.assert_called_once_with( + self.volume_attachment, + self.servers[0]) + + class TestServerAddNetwork(TestServer): def setUp(self): diff --git a/releasenotes/notes/switch-server-remove-volume-to-sdk-47e9befd2672dcdf.yaml b/releasenotes/notes/switch-server-remove-volume-to-sdk-47e9befd2672dcdf.yaml new file mode 100644 index 0000000000..3e0397d789 --- /dev/null +++ b/releasenotes/notes/switch-server-remove-volume-to-sdk-47e9befd2672dcdf.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Switch command server remove volume to using sdk. From f4629331134c40599f9baf908a391460b74d2767 Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Tue, 23 Nov 2021 21:56:56 +0100 Subject: [PATCH 048/706] Allow unset port's host_id It is supported by Neutron and needs to be done like that when e.g. admin wants to unbound port from the host. Task: #44043 Story: #2009705 Change-Id: I08f1bb40f4dc72cfa7c62feeb5f513455de0ca45 --- openstackclient/network/v2/port.py | 8 ++++++++ openstackclient/tests/unit/network/v2/test_port.py | 5 ++++- .../add-option-to-unset-port-host-c76de9b1d2addf9a.yaml | 5 +++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-option-to-unset-port-host-c76de9b1d2addf9a.yaml diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 132c384a0e..8f79b80b0b 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -969,6 +969,12 @@ def get_parser(self, prog_name): action='store_true', help=_("Clear existing NUMA affinity policy") ) + parser.add_argument( + '--host', + action='store_true', + default=False, + help=_("Clear host binding for the port.") + ) _tag.add_tag_option_to_parser_for_unset(parser, _('port')) @@ -1026,6 +1032,8 @@ def take_action(self, parsed_args): attrs['data_plane_status'] = None if parsed_args.numa_policy: attrs['numa_affinity_policy'] = None + if parsed_args.host: + attrs['binding:host_id'] = None 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 5f2a12836a..c8540ba057 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -1923,6 +1923,7 @@ def test_unset_port_parameters(self): 'subnet=042eb10a-3a18-4658-ab-cf47c8d03152,ip-address=1.0.0.0', '--binding-profile', 'Superman', '--qos-policy', + '--host', self._testport.name, ] verifylist = [ @@ -1931,6 +1932,7 @@ def test_unset_port_parameters(self): 'ip-address': '1.0.0.0'}]), ('binding_profile', ['Superman']), ('qos_policy', True), + ('host', True) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -1941,7 +1943,8 @@ def test_unset_port_parameters(self): 'subnet_id': '042eb10a-3a18-4658-ab-cf47c8d03152', 'ip_address': '0.0.0.1'}], 'binding:profile': {'batman': 'Joker'}, - 'qos_policy_id': None + 'qos_policy_id': None, + 'binding:host_id': None } self.network.update_port.assert_called_once_with( self._testport, **attrs) diff --git a/releasenotes/notes/add-option-to-unset-port-host-c76de9b1d2addf9a.yaml b/releasenotes/notes/add-option-to-unset-port-host-c76de9b1d2addf9a.yaml new file mode 100644 index 0000000000..0aa0476050 --- /dev/null +++ b/releasenotes/notes/add-option-to-unset-port-host-c76de9b1d2addf9a.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add possibility to unbind Neutron's port from the host by unsetting its + host_id. From f82afc7f379daebd1994d9133eff801f790c0d32 Mon Sep 17 00:00:00 2001 From: Diwei Zhu Date: Sun, 14 Nov 2021 22:52:14 +0000 Subject: [PATCH 049/706] Switch openstack server remove port/network to using sdk Change-Id: I1540c1f52e9a107dba20eeea9dc323c5510fe2b1 --- openstackclient/compute/v2/server.py | 25 +++-- .../functional/compute/v2/test_server.py | 94 ++++++++++++++++--- .../tests/unit/compute/v2/test_server.py | 19 ++-- ...-network-port-to-sdk-829ba711e0e198d5.yaml | 4 + 4 files changed, 114 insertions(+), 28 deletions(-) create mode 100644 releasenotes/notes/switch-server-remove-network-port-to-sdk-829ba711e0e198d5.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 18c1197cc9..dfb4dba8fe 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -3690,10 +3690,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) if self.app.client_manager.is_network_endpoint_enabled(): network_client = self.app.client_manager.network @@ -3702,7 +3702,11 @@ def take_action(self, parsed_args): else: port_id = parsed_args.port - server.interface_detach(port_id) + compute_client.delete_server_interface( + port_id, + server=server, + ignore_missing=False, + ) class RemoveNetwork(command.Command): @@ -3723,10 +3727,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) if self.app.client_manager.is_network_endpoint_enabled(): network_client = self.app.client_manager.network @@ -3735,9 +3739,12 @@ def take_action(self, parsed_args): else: net_id = parsed_args.network - for inf in server.interface_list(): + for inf in compute_client.server_interfaces(server): if inf.net_id == net_id: - server.interface_detach(inf.port_id) + compute_client.delete_server_interface( + inf.port_id, + server=server, + ) class RemoveServerSecurityGroup(command.Command): diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py index 0d07950e97..cf4bcbc2a9 100644 --- a/openstackclient/tests/functional/compute/v2/test_server.py +++ b/openstackclient/tests/functional/compute/v2/test_server.py @@ -1072,7 +1072,7 @@ def test_server_create_with_empty_network_option_latest(self): self.assertNotIn('nics are required after microversion 2.36', e.stderr) - def test_server_add_remove_network_port(self): + def test_server_add_remove_network(self): name = uuid.uuid4().hex cmd_output = json.loads(self.openstack( 'server create -f json ' + @@ -1085,18 +1085,63 @@ def test_server_add_remove_network_port(self): self.assertIsNotNone(cmd_output['id']) self.assertEqual(name, cmd_output['name']) + self.addCleanup(self.openstack, 'server delete --wait ' + name) + # add network and check 'public' is in server show self.openstack( 'server add network ' + name + ' public') + wait_time = 0 + while wait_time < 60: + cmd_output = json.loads(self.openstack( + 'server show -f json ' + name + )) + if 'public' not in cmd_output['addresses']: + # Hang out for a bit and try again + print('retrying add network check') + wait_time += 10 + time.sleep(10) + else: + break + addresses = cmd_output['addresses'] + self.assertIn('public', addresses) + + # remove network and check 'public' is not in server show + self.openstack('server remove network ' + name + ' public') + + wait_time = 0 + while wait_time < 60: + cmd_output = json.loads(self.openstack( + 'server show -f json ' + name + )) + if 'public' in cmd_output['addresses']: + # Hang out for a bit and try again + print('retrying remove network check') + wait_time += 10 + time.sleep(10) + else: + break + + addresses = cmd_output['addresses'] + self.assertNotIn('public', addresses) + + def test_server_add_remove_port(self): + name = uuid.uuid4().hex cmd_output = json.loads(self.openstack( - 'server show -f json ' + name + 'server create -f json ' + + '--network private ' + + '--flavor ' + self.flavor_name + ' ' + + '--image ' + self.image_name + ' ' + + '--wait ' + + name )) - addresses = cmd_output['addresses'] - self.assertIn('public', addresses) + self.assertIsNotNone(cmd_output['id']) + self.assertEqual(name, cmd_output['name']) + self.addCleanup(self.openstack, 'server delete --wait ' + name) - port_name = 'test-port' + # create port, record one of its ip address + port_name = uuid.uuid4().hex cmd_output = json.loads(self.openstack( 'port list -f json' @@ -1108,17 +1153,44 @@ def test_server_add_remove_network_port(self): '--network private ' + port_name )) self.assertIsNotNone(cmd_output['id']) + ip_address = cmd_output['fixed_ips'][0]['ip_address'] + self.addCleanup(self.openstack, 'port delete ' + port_name) + # add port to server, assert the ip address of the port appears self.openstack('server add port ' + name + ' ' + port_name) - cmd_output = json.loads(self.openstack( - 'server show -f json ' + name - )) + wait_time = 0 + while wait_time < 60: + cmd_output = json.loads(self.openstack( + 'server show -f json ' + name + )) + if ip_address not in cmd_output['addresses']['private']: + # Hang out for a bit and try again + print('retrying add port check') + wait_time += 10 + time.sleep(10) + else: + break + addresses = cmd_output['addresses']['private'] + self.assertIn(ip_address, addresses) - # TODO(diwei): test remove network/port after the commands are switched + # remove port, assert the ip address of the port doesn't appear + self.openstack('server remove port ' + name + ' ' + port_name) - self.openstack('server delete ' + name) - self.openstack('port delete ' + port_name) + wait_time = 0 + while wait_time < 60: + cmd_output = json.loads(self.openstack( + 'server show -f json ' + name + )) + if ip_address in cmd_output['addresses']['private']: + # Hang out for a bit and try again + print('retrying add port check') + wait_time += 10 + time.sleep(10) + else: + break + addresses = cmd_output['addresses']['private'] + self.assertNotIn(ip_address, addresses) def test_server_add_remove_volume(self): volume_wait_for = volume_common.BaseVolumeTests.wait_for_status diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 203e47ebb3..bb0a2c6768 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -6973,14 +6973,14 @@ def setUp(self): # Set method to be tested. self.methods = { - 'interface_detach': None, + 'delete_server_interface': 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_servers_mock(count=1) + servers = self.setup_sdk_servers_mock(count=1) port = 'fake-port' arglist = [ @@ -6995,7 +6995,8 @@ def _test_server_remove_port(self, port_id): result = self.cmd.take_action(parsed_args) - servers[0].interface_detach.assert_called_once_with(port_id) + self.sdk_client.delete_server_interface.assert_called_with( + port_id, server=servers[0], ignore_missing=False) self.assertIsNone(result) def test_server_remove_port(self): @@ -7020,17 +7021,18 @@ def setUp(self): # Set method to be tested. self.fake_inf = mock.Mock() self.methods = { - 'interface_list': [self.fake_inf], - 'interface_detach': None, + 'server_interfaces': [self.fake_inf], + 'delete_server_interface': None, } 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] def _test_server_remove_network(self, network_id): self.fake_inf.net_id = network_id self.fake_inf.port_id = 'fake-port' - servers = self.setup_servers_mock(count=1) + servers = self.setup_sdk_servers_mock(count=1) network = 'fake-network' arglist = [ @@ -7045,8 +7047,9 @@ def _test_server_remove_network(self, network_id): result = self.cmd.take_action(parsed_args) - servers[0].interface_list.assert_called_once_with() - servers[0].interface_detach.assert_called_once_with('fake-port') + self.sdk_client.server_interfaces.assert_called_once_with(servers[0]) + self.sdk_client.delete_server_interface.assert_called_once_with( + 'fake-port', server=servers[0]) self.assertIsNone(result) def test_server_remove_network(self): diff --git a/releasenotes/notes/switch-server-remove-network-port-to-sdk-829ba711e0e198d5.yaml b/releasenotes/notes/switch-server-remove-network-port-to-sdk-829ba711e0e198d5.yaml new file mode 100644 index 0000000000..6b47b1b35c --- /dev/null +++ b/releasenotes/notes/switch-server-remove-network-port-to-sdk-829ba711e0e198d5.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Switch server remove volume/port to using sdk. From b515fe61b27408e78639da8abb3acaa485ebca4e Mon Sep 17 00:00:00 2001 From: Thrivikram Mudunuri Date: Sat, 13 Nov 2021 02:37:44 -0500 Subject: [PATCH 050/706] Switch server pause and server unpause to SDK Switch the server pause and server unpause commands from novaclient to SDK. Use the SDK versions of test fakes to support fake Server resources. Change-Id: Id626f06f3d7edd44b306b7fc7b9b00d04af09621 --- openstackclient/compute/v2/server.py | 22 ++++++++--------- .../tests/unit/compute/v2/test_server.py | 24 +++++++++++++++---- ...pause-unpause-to-sdk-d74ec8536b764af6.yaml | 5 ++++ 3 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 releasenotes/notes/migrate-server-pause-unpause-to-sdk-d74ec8536b764af6.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 121a7b82e8..09954c49d2 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -3130,12 +3130,13 @@ 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 for server in parsed_args.server: - utils.find_resource( - compute_client.servers, - server - ).pause() + server_id = compute_client.find_server( + server, + ignore_missing=False, + ).id + compute_client.pause_server(server_id) class RebootServer(command.Command): @@ -4674,7 +4675,6 @@ def get_parser(self, prog_name): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute for server in parsed_args.server: utils.find_resource( @@ -4697,13 +4697,13 @@ 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 for server in parsed_args.server: - utils.find_resource( - compute_client.servers, + server_id = compute_client.find_server( server, - ).unpause() + ignore_missing=False, + ).id + compute_client.unpause_server(server_id) class UnrescueServer(command.Command): diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 10ea07adb3..435ddb4778 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -192,6 +192,22 @@ def run_method_with_servers(self, method_name, server_count): method.assert_called_with() self.assertIsNone(result) + 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 = [call(s.id) for s in servers] + method = getattr(self.sdk_client, method_name) + method.assert_has_calls(calls) + self.assertIsNone(result) + class TestServerAddFixedIP(TestServer): @@ -6062,10 +6078,10 @@ def setUp(self): } def test_server_pause_one_server(self): - self.run_method_with_servers('pause', 1) + self.run_method_with_sdk_servers('pause_server', 1) def test_server_pause_multi_servers(self): - self.run_method_with_servers('pause', 3) + self.run_method_with_sdk_servers('pause_server', 3) class TestServerRebuild(TestServer): @@ -8308,10 +8324,10 @@ def setUp(self): } def test_server_unpause_one_server(self): - self.run_method_with_servers('unpause', 1) + self.run_method_with_sdk_servers('unpause_server', 1) def test_server_unpause_multi_servers(self): - self.run_method_with_servers('unpause', 3) + self.run_method_with_sdk_servers('unpause_server', 3) class TestServerUnset(TestServer): diff --git a/releasenotes/notes/migrate-server-pause-unpause-to-sdk-d74ec8536b764af6.yaml b/releasenotes/notes/migrate-server-pause-unpause-to-sdk-d74ec8536b764af6.yaml new file mode 100644 index 0000000000..e2d4003489 --- /dev/null +++ b/releasenotes/notes/migrate-server-pause-unpause-to-sdk-d74ec8536b764af6.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Migrate ``server pause`` and ``server unpause`` commands from novaclient + to sdk. From ff96fea0120ab43968a10230ce7899a3c6504e75 Mon Sep 17 00:00:00 2001 From: Thrivikram Mudunuri Date: Sat, 13 Nov 2021 17:39:41 -0500 Subject: [PATCH 051/706] Switch server suspend and server resume to SDK Switch the server suspend and server resume commands from novaclient to SDK. Use the SDK versions of test fakes to support fake Server resources. Change-Id: Idd0b4f13fab0f238e42844a7d759538bbda24f68 --- openstackclient/compute/v2/server.py | 20 +++++++++---------- .../tests/unit/compute/v2/test_server.py | 8 ++++---- ...uspend-resume-to-sdk-fd1709336607b496.yaml | 5 +++++ 3 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 releasenotes/notes/migrate-server-suspend-resume-to-sdk-fd1709336607b496.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 09954c49d2..a7479bb41a 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -4081,13 +4081,13 @@ 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 for server in parsed_args.server: - utils.find_resource( - compute_client.servers, + server_id = compute_client.find_server( server, - ).resume() + ignore_missing=False, + ).id + compute_client.resume_server(server_id) class SetServer(command.Command): @@ -4652,13 +4652,13 @@ 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 for server in parsed_args.server: - utils.find_resource( - compute_client.servers, + server_id = compute_client.find_server( server, - ).suspend() + ignore_missing=False, + ).id + compute_client.suspend_server(server_id) class UnlockServer(command.Command): diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 435ddb4778..27ea12637c 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -7617,10 +7617,10 @@ def setUp(self): } def test_server_resume_one_server(self): - self.run_method_with_servers('resume', 1) + self.run_method_with_sdk_servers('resume_server', 1) def test_server_resume_multi_servers(self): - self.run_method_with_servers('resume', 3) + self.run_method_with_sdk_servers('resume_server', 3) class TestServerSet(TestServer): @@ -8284,10 +8284,10 @@ def setUp(self): } def test_server_suspend_one_server(self): - self.run_method_with_servers('suspend', 1) + self.run_method_with_sdk_servers('suspend_server', 1) def test_server_suspend_multi_servers(self): - self.run_method_with_servers('suspend', 3) + self.run_method_with_sdk_servers('suspend_server', 3) class TestServerUnlock(TestServer): diff --git a/releasenotes/notes/migrate-server-suspend-resume-to-sdk-fd1709336607b496.yaml b/releasenotes/notes/migrate-server-suspend-resume-to-sdk-fd1709336607b496.yaml new file mode 100644 index 0000000000..7d3781bbf4 --- /dev/null +++ b/releasenotes/notes/migrate-server-suspend-resume-to-sdk-fd1709336607b496.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Migrate ``server suspend`` and ``server resume`` commands from novaclient + to sdk. From 4c3de28e83babb0672950320a20492dc61803b4a Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 26 Jan 2021 16:55:05 +0000 Subject: [PATCH 052/706] compute: Reorder building of columns for 'server list' This has no impact on the end result, but it should make fixing issues introduced by API microversion 2.69 a little easier. Change-Id: I7d70eac8aa1a6197ed05a49f071e6899ec219c03 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 192 +++++++++++++++------------ 1 file changed, 105 insertions(+), 87 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 121a7b82e8..be14074b7d 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2204,15 +2204,19 @@ def take_action(self, parsed_args): # flavor name is given, map it to ID. flavor_id = None if parsed_args.flavor: - flavor_id = utils.find_resource(compute_client.flavors, - parsed_args.flavor).id + flavor_id = utils.find_resource( + compute_client.flavors, + parsed_args.flavor, + ).id # Nova only supports list servers searching by image ID. So if a # image name is given, map it to ID. image_id = None if parsed_args.image: - image_id = image_client.find_image(parsed_args.image, - ignore_missing=False).id + image_id = image_client.find_image( + parsed_args.image, + ignore_missing=False, + ).id search_opts = { 'reservation_id': parsed_args.reservation_id, @@ -2320,95 +2324,93 @@ def take_action(self, parsed_args): try: iso8601.parse_date(search_opts['changes-since']) except (TypeError, iso8601.ParseError): + msg = _('Invalid changes-since value: %s') raise exceptions.CommandError( - _('Invalid changes-since value: %s') % - search_opts['changes-since'] + msg % search_opts['changes-since'] ) + columns = ( + 'id', + 'name', + 'status', + ) + column_headers = ( + 'ID', + 'Name', + 'Status', + ) + if parsed_args.long: - columns = ( - 'ID', - 'Name', - 'Status', + columns += ( 'OS-EXT-STS:task_state', 'OS-EXT-STS:power_state', - 'Networks', - 'Image Name', - 'Image ID', - 'Flavor Name', - 'Flavor ID', - 'OS-EXT-AZ:availability_zone', - 'OS-EXT-SRV-ATTR:host', - 'Metadata', ) - column_headers = ( - 'ID', - 'Name', - 'Status', + column_headers += ( 'Task State', 'Power State', - 'Networks', + ) + + columns += ('networks',) + column_headers += ('Networks',) + + if parsed_args.long: + columns += ( + 'image_name', + 'image_id', + ) + column_headers += ( 'Image Name', 'Image ID', + ) + else: + if parsed_args.no_name_lookup: + columns += ('image_id',) + else: + columns += ('image_name',) + column_headers += ('Image',) + + if parsed_args.long: + columns += ( + 'flavor_name', + 'flavor_id', + ) + column_headers += ( 'Flavor Name', 'Flavor ID', - 'Availability Zone', - 'Host', - 'Properties', ) - mixed_case_fields = [ - 'OS-EXT-STS:task_state', - 'OS-EXT-STS:power_state', - 'OS-EXT-AZ:availability_zone', - 'OS-EXT-SRV-ATTR:host', - ] else: if parsed_args.no_name_lookup: - columns = ( - 'ID', - 'Name', - 'Status', - 'Networks', - 'Image ID', - 'Flavor ID', - ) + columns += ('flavor_id',) else: - columns = ( - 'ID', - 'Name', - 'Status', - 'Networks', - 'Image Name', - 'Flavor Name', - ) - column_headers = ( - 'ID', - 'Name', - 'Status', - 'Networks', - 'Image', - 'Flavor', + columns += ('flavor_name',) + column_headers += ('Flavor',) + + if parsed_args.long: + columns += ( + 'OS-EXT-AZ:availability_zone', + 'OS-EXT-SRV-ATTR:host', + 'metadata', + ) + column_headers += ( + 'Availability Zone', + 'Host', + 'Properties', ) - mixed_case_fields = [] marker_id = None # support for additional columns if parsed_args.columns: - # convert tuple to list to edit them - column_headers = list(column_headers) - columns = list(columns) - for c in parsed_args.columns: if c in ('Project ID', 'project_id'): - columns.append('tenant_id') - column_headers.append('Project ID') + columns += ('tenant_id',) + column_headers += ('Project ID',) if c in ('User ID', 'user_id'): - columns.append('user_id') - column_headers.append('User ID') + columns += ('user_id',) + column_headers += ('User ID',) if c in ('Created At', 'created_at'): - columns.append('created') - column_headers.append('Created At') + columns += ('created',) + column_headers += ('Created At',) # convert back to tuple column_headers = tuple(column_headers) @@ -2422,25 +2424,29 @@ def take_action(self, parsed_args): if parsed_args.deleted: marker_id = parsed_args.marker else: - marker_id = utils.find_resource(compute_client.servers, - parsed_args.marker).id + marker_id = utils.find_resource( + compute_client.servers, + parsed_args.marker, + ).id - data = compute_client.servers.list(search_opts=search_opts, - marker=marker_id, - limit=parsed_args.limit) + data = compute_client.servers.list( + search_opts=search_opts, + marker=marker_id, + limit=parsed_args.limit) images = {} flavors = {} if data and not parsed_args.no_name_lookup: - # Create a dict that maps image_id to image object. - # Needed so that we can display the "Image Name" column. - # "Image Name" is not crucial, so we swallow any exceptions. - # The 'image' attribute can be an empty string if the server was - # booted from a volume. + # create a dict that maps image_id to image object, which is used + # to display the "Image Name" column. Note that 'image.id' can be + # empty for BFV instances and 'image' can be missing entirely if + # there are infra failures if parsed_args.name_lookup_one_by_one or image_id: - for i_id in set(filter(lambda x: x is not None, - (s.image.get('id') for s in data - if s.image))): + for i_id in set( + s.image['id'] for s in data + if s.image and s.image.get('id') + ): + # "Image Name" is not crucial, so we swallow any exceptions try: images[i_id] = image_client.get_image(i_id) except Exception: @@ -2453,12 +2459,17 @@ def take_action(self, parsed_args): except Exception: pass - # Create a dict that maps flavor_id to flavor object. - # Needed so that we can display the "Flavor Name" column. - # "Flavor Name" is not crucial, so we swallow any exceptions. + # 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 + # 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(filter(lambda x: x is not None, - (s.flavor.get('id') for s in data))): + for f_id in set( + s.flavor['id'] 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.flavors.get(f_id) except Exception: @@ -2482,6 +2493,7 @@ def take_action(self, parsed_args): # processing of the image and flavor informations. if not hasattr(s, 'image') or not hasattr(s, 'flavor'): continue + if 'id' in s.image: image = images.get(s.image['id']) if image: @@ -2494,6 +2506,7 @@ def take_action(self, parsed_args): # able to grep for boot-from-volume servers when using the CLI. s.image_name = IMAGE_STRING_FOR_BFV s.image_id = IMAGE_STRING_FOR_BFV + if 'id' in s.flavor: flavor = flavors.get(s.flavor['id']) if flavor: @@ -2512,11 +2525,16 @@ def take_action(self, parsed_args): ( utils.get_item_properties( s, columns, - mixed_case_fields=mixed_case_fields, + mixed_case_fields=( + 'OS-EXT-STS:task_state', + 'OS-EXT-STS:power_state', + 'OS-EXT-AZ:availability_zone', + 'OS-EXT-SRV-ATTR:host', + ), formatters={ 'OS-EXT-STS:power_state': PowerStateColumn, - 'Networks': format_columns.DictListColumn, - 'Metadata': format_columns.DictColumn, + 'networks': format_columns.DictListColumn, + 'metadata': format_columns.DictColumn, }, ) for s in data ), From 8e362402dee07744668bcf7f6774af4fbe9a07e3 Mon Sep 17 00:00:00 2001 From: Khomesh Thakre Date: Fri, 6 Nov 2020 22:45:03 +0530 Subject: [PATCH 053/706] compute: Show flavor in 'server list' with API >= 2.47 Fix the issue where the flavor name was empty in server list output. This requires somewhat invasive unit test changes to reflect the changed API response from the server, but this has the upside of meaning we don't need new tests since what we have validates things. Also drop the flavor ID column as it is removed from the compute API. Change-Id: Ica3320242a38901c1180b2b29109c9474366fde0 Signed-off-by: Khomesh Thakre Story: 2008257 Task: 41113 --- openstackclient/compute/v2/server.py | 42 +- .../tests/unit/compute/v2/test_server.py | 558 ++++++++++-------- ...st-microversion-2.47-af200e9bb4747e2d.yaml | 8 + 3 files changed, 348 insertions(+), 260 deletions(-) create mode 100644 releasenotes/notes/fix-flavor-in-server-list-microversion-2.47-af200e9bb4747e2d.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index be14074b7d..a0b27242d4 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -2369,21 +2369,28 @@ def take_action(self, parsed_args): columns += ('image_name',) column_headers += ('Image',) - if parsed_args.long: - columns += ( - 'flavor_name', - 'flavor_id', - ) - column_headers += ( - 'Flavor Name', - 'Flavor ID', - ) + # microversion 2.47 puts the embedded flavor into the server response + # body but omits the id, so if not present we just expose the original + # flavor name in the output + if compute_client.api_version >= api_versions.APIVersion('2.47'): + columns += ('flavor_name',) + column_headers += ('Flavor',) else: - if parsed_args.no_name_lookup: - columns += ('flavor_id',) + if parsed_args.long: + columns += ( + 'flavor_name', + 'flavor_id', + ) + column_headers += ( + 'Flavor Name', + 'Flavor ID', + ) else: - columns += ('flavor_name',) - column_headers += ('Flavor',) + if parsed_args.no_name_lookup: + columns += ('flavor_id',) + else: + columns += ('flavor_name',) + column_headers += ('Flavor',) if parsed_args.long: columns += ( @@ -2507,18 +2514,13 @@ def take_action(self, parsed_args): s.image_name = IMAGE_STRING_FOR_BFV s.image_id = IMAGE_STRING_FOR_BFV - if 'id' in s.flavor: + if compute_client.api_version < api_versions.APIVersion('2.47'): flavor = flavors.get(s.flavor['id']) if flavor: s.flavor_name = flavor.name s.flavor_id = s.flavor['id'] else: - # TODO(mriedem): Fix this for microversion >= 2.47 where the - # flavor is embedded in the server response without the id. - # We likely need to drop the Flavor ID column in that case if - # --long is specified. - s.flavor_name = '' - s.flavor_id = '' + s.flavor_name = s.flavor['original_name'] table = ( column_headers, diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 10ea07adb3..17762d72bc 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -4081,7 +4081,7 @@ def test_server_dump_multi_servers(self): self.run_method_with_servers('trigger_crash_dump', 3) -class TestServerList(TestServer): +class _TestServerList(TestServer): # Columns to be listed up. columns = ( @@ -4109,7 +4109,7 @@ class TestServerList(TestServer): ) def setUp(self): - super(TestServerList, self).setUp() + super(_TestServerList, self).setUp() self.search_opts = { 'reservation_id': None, @@ -4148,7 +4148,7 @@ def setUp(self): }, 'OS-EXT-AZ:availability_zone': 'availability-zone-xxx', 'OS-EXT-SRV-ATTR:host': 'host-name-xxx', - 'Metadata': '', + 'Metadata': format_columns.DictColumn({}), } # The servers to be listed. @@ -4167,10 +4167,11 @@ def setUp(self): # Get the command object to test self.cmd = server.ListServer(self.app, None) - # Prepare data returned by fake Nova API. - self.data = [] - self.data_long = [] - self.data_no_name_lookup = [] + +class TestServerList(_TestServerList): + + def setUp(self): + super(TestServerList, self).setUp() Image = collections.namedtuple('Image', 'id name') self.images_mock.return_value = [ @@ -4185,8 +4186,8 @@ def setUp(self): for s in self.servers ] - for s in self.servers: - self.data.append(( + self.data = tuple( + ( s.id, s.name, s.status, @@ -4194,34 +4195,8 @@ def setUp(self): # 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, - )) - self.data_long.append(( - s.id, - s.name, - s.status, - getattr(s, 'OS-EXT-STS:task_state'), - server.PowerStateColumn( - getattr(s, 'OS-EXT-STS:power_state') - ), - format_columns.DictListColumn(s.networks), - # 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, - s.flavor['id'], - getattr(s, 'OS-EXT-AZ:availability_zone'), - getattr(s, 'OS-EXT-SRV-ATTR:host'), - format_columns.DictColumn({}), - )) - self.data_no_name_lookup.append(( - s.id, - s.name, - s.status, - format_columns.DictListColumn(s.networks), - # Image will be an empty string if boot-from-volume - s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV, - s.flavor['id'] - )) + ) for s in self.servers + ) def test_server_list_no_option(self): arglist = [] @@ -4242,7 +4217,7 @@ def test_server_list_no_option(self): self.assertFalse(self.flavors_mock.get.call_count) self.assertFalse(self.get_image_mock.call_count) self.assertEqual(self.columns, columns) - self.assertEqual(tuple(self.data), tuple(data)) + self.assertEqual(self.data, tuple(data)) def test_server_list_no_servers(self): arglist = [] @@ -4261,9 +4236,28 @@ def test_server_list_no_servers(self): self.assertEqual(0, self.images_mock.list.call_count) self.assertEqual(0, self.flavors_mock.list.call_count) self.assertEqual(self.columns, columns) - self.assertEqual(tuple(self.data), tuple(data)) + self.assertEqual(self.data, tuple(data)) def test_server_list_long_option(self): + self.data = tuple( + ( + s.id, + s.name, + s.status, + getattr(s, 'OS-EXT-STS:task_state'), + server.PowerStateColumn( + getattr(s, 'OS-EXT-STS:power_state') + ), + format_columns.DictListColumn(s.networks), + # 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, + s.flavor['id'], + getattr(s, 'OS-EXT-AZ:availability_zone'), + getattr(s, 'OS-EXT-SRV-ATTR:host'), + s.Metadata, + ) for s in self.servers) arglist = [ '--long', ] @@ -4274,10 +4268,9 @@ 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.servers_mock.list.assert_called_with(**self.kwargs) self.assertEqual(self.columns_long, columns) - self.assertCountEqual(tuple(self.data_long), tuple(data)) + self.assertEqual(self.data, tuple(data)) def test_server_list_column_option(self): arglist = [ @@ -4299,6 +4292,18 @@ def test_server_list_column_option(self): self.assertIn('Created At', columns) def test_server_list_no_name_lookup_option(self): + self.data = tuple( + ( + s.id, + s.name, + s.status, + format_columns.DictListColumn(s.networks), + # Image will be an empty string if boot-from-volume + s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV, + s.flavor['id'] + ) for s in self.servers + ) + arglist = [ '--no-name-lookup', ] @@ -4312,9 +4317,21 @@ def test_server_list_no_name_lookup_option(self): self.servers_mock.list.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(tuple(self.data_no_name_lookup), tuple(data)) + self.assertEqual(self.data, tuple(data)) def test_server_list_n_option(self): + self.data = tuple( + ( + s.id, + s.name, + s.status, + format_columns.DictListColumn(s.networks), + # Image will be an empty string if boot-from-volume + s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV, + s.flavor['id'] + ) for s in self.servers + ) + arglist = [ '-n', ] @@ -4328,7 +4345,7 @@ def test_server_list_n_option(self): self.servers_mock.list.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(tuple(self.data_no_name_lookup), tuple(data)) + self.assertEqual(self.data, tuple(data)) def test_server_list_name_lookup_one_by_one(self): arglist = [ @@ -4350,7 +4367,7 @@ def test_server_list_name_lookup_one_by_one(self): self.flavors_mock.get.assert_called() self.assertEqual(self.columns, columns) - self.assertEqual(tuple(self.data), tuple(data)) + self.assertEqual(self.data, tuple(data)) def test_server_list_with_image(self): @@ -4371,81 +4388,7 @@ def test_server_list_with_image(self): self.servers_mock.list.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(tuple(self.data), tuple(data)) - - def test_server_list_with_locked_pre_v273(self): - - arglist = [ - '--locked' - ] - verifylist = [ - ('locked', True) - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - ex = self.assertRaises(exceptions.CommandError, - self.cmd.take_action, - parsed_args) - self.assertIn( - '--os-compute-api-version 2.73 or greater is required', str(ex)) - - def test_server_list_with_locked_v273(self): - - self.app.client_manager.compute.api_version = \ - api_versions.APIVersion('2.73') - arglist = [ - '--locked' - ] - verifylist = [ - ('locked', True) - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - - self.search_opts['locked'] = True - self.servers_mock.list.assert_called_with(**self.kwargs) - - self.assertEqual(self.columns, columns) - self.assertEqual(tuple(self.data), tuple(data)) - - def test_server_list_with_unlocked_v273(self): - - self.app.client_manager.compute.api_version = \ - api_versions.APIVersion('2.73') - arglist = [ - '--unlocked' - ] - verifylist = [ - ('unlocked', True) - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - - self.search_opts['locked'] = False - self.servers_mock.list.assert_called_with(**self.kwargs) - - self.assertEqual(self.columns, columns) - self.assertEqual(tuple(self.data), tuple(data)) - - def test_server_list_with_locked_and_unlocked_v273(self): - - self.app.client_manager.compute.api_version = \ - api_versions.APIVersion('2.73') - arglist = [ - '--locked', - '--unlocked' - ] - verifylist = [ - ('locked', True), - ('unlocked', True) - ] - - ex = self.assertRaises( - utils.ParserException, - self.check_parser, self.cmd, arglist, verifylist) - self.assertIn('Argument parse failed', str(ex)) + self.assertEqual(self.data, tuple(data)) def test_server_list_with_flavor(self): @@ -4465,7 +4408,7 @@ def test_server_list_with_flavor(self): self.servers_mock.list.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(tuple(self.data), tuple(data)) + self.assertEqual(self.data, tuple(data)) def test_server_list_with_changes_since(self): @@ -4486,7 +4429,7 @@ def test_server_list_with_changes_since(self): self.servers_mock.list.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(tuple(self.data), tuple(data)) + self.assertEqual(self.data, tuple(data)) @mock.patch.object(iso8601, 'parse_date', side_effect=iso8601.ParseError) def test_server_list_with_invalid_changes_since(self, mock_parse_isotime): @@ -4509,123 +4452,6 @@ def test_server_list_with_invalid_changes_since(self, mock_parse_isotime): 'Invalid time value' ) - def test_server_list_v266_with_changes_before(self): - self.app.client_manager.compute.api_version = ( - api_versions.APIVersion('2.66')) - arglist = [ - '--changes-before', '2016-03-05T06:27:59Z', - '--deleted' - ] - verifylist = [ - ('changes_before', '2016-03-05T06:27:59Z'), - ('deleted', True), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - - self.search_opts['changes-before'] = '2016-03-05T06:27:59Z' - self.search_opts['deleted'] = True - - self.servers_mock.list.assert_called_with(**self.kwargs) - - self.assertEqual(self.columns, columns) - self.assertEqual(tuple(self.data), tuple(data)) - - @mock.patch.object(iso8601, 'parse_date', side_effect=iso8601.ParseError) - def test_server_list_v266_with_invalid_changes_before( - self, mock_parse_isotime): - self.app.client_manager.compute.api_version = ( - api_versions.APIVersion('2.66')) - - arglist = [ - '--changes-before', 'Invalid time value', - ] - verifylist = [ - ('changes_before', 'Invalid time value'), - ] - - 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('Invalid changes-before value: Invalid time ' - 'value', str(e)) - mock_parse_isotime.assert_called_once_with( - 'Invalid time value' - ) - - def test_server_with_changes_before_pre_v266(self): - self.app.client_manager.compute.api_version = ( - api_versions.APIVersion('2.65')) - - arglist = [ - '--changes-before', '2016-03-05T06:27:59Z', - '--deleted' - ] - verifylist = [ - ('changes_before', '2016-03-05T06:27:59Z'), - ('deleted', True), - ] - - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises(exceptions.CommandError, - self.cmd.take_action, - parsed_args) - - def test_server_list_v269_with_partial_constructs(self): - self.app.client_manager.compute.api_version = \ - api_versions.APIVersion('2.69') - - arglist = [] - verifylist = [] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # include "partial results" from non-responsive part of - # infrastructure. - server_dict = { - "id": "server-id-95a56bfc4xxxxxx28d7e418bfd97813a", - "status": "UNKNOWN", - "tenant_id": "6f70656e737461636b20342065766572", - "created": "2018-12-03T21:06:18Z", - "links": [ - { - "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": {} - } - _server = compute_fakes.fakes.FakeResource( - info=server_dict, - ) - self.servers.append(_server) - columns, data = self.cmd.take_action(parsed_args) - # get the first three servers out since our interest is in the partial - # server. - next(data) - next(data) - next(data) - partial_server = next(data) - expected_row = ( - 'server-id-95a56bfc4xxxxxx28d7e418bfd97813a', - '', - 'UNKNOWN', - format_columns.DictListColumn({}), - '', - '', - ) - self.assertEqual(expected_row, partial_server) - def test_server_list_with_tag(self): self.app.client_manager.compute.api_version = api_versions.APIVersion( '2.26') @@ -4646,7 +4472,7 @@ def test_server_list_with_tag(self): self.servers_mock.list.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(tuple(self.data), tuple(data)) + self.assertEqual(self.data, tuple(data)) def test_server_list_with_tag_pre_v225(self): self.app.client_manager.compute.api_version = api_versions.APIVersion( @@ -4689,7 +4515,7 @@ def test_server_list_with_not_tag(self): self.servers_mock.list.assert_called_with(**self.kwargs) self.assertEqual(self.columns, columns) - self.assertEqual(tuple(self.data), tuple(data)) + self.assertEqual(self.data, tuple(data)) def test_server_list_with_not_tag_pre_v226(self): self.app.client_manager.compute.api_version = api_versions.APIVersion( @@ -4850,6 +4676,258 @@ def test_server_list_with_power_state(self): self.assertEqual(tuple(self.data), tuple(data)) +class TestServerListV273(_TestServerList): + + # Columns to be listed up. + 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', + ) + + def setUp(self): + super(TestServerListV273, self).setUp() + + # The fake servers' attributes. Use the original attributes names in + # nova, not the ones printed by "server list" command. + self.attrs['flavor'] = { + 'vcpus': self.flavor.vcpus, + 'ram': self.flavor.ram, + 'disk': self.flavor.disk, + 'ephemeral': self.flavor.ephemeral, + 'swap': self.flavor.swap, + 'original_name': self.flavor.name, + 'extra_specs': self.flavor.extra_specs, + } + + # The servers to be listed. + self.servers = self.setup_servers_mock(3) + self.servers_mock.list.return_value = self.servers + + Image = collections.namedtuple('Image', 'id name') + self.images_mock.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 + ] + + # The flavor information is embedded, so now reason for this to be + # called + self.flavors_mock.list = mock.NonCallableMock() + + self.data = tuple( + ( + s.id, + s.name, + s.status, + format_columns.DictListColumn(s.networks), + # 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_with_locked_pre_v273(self): + + arglist = [ + '--locked' + ] + verifylist = [ + ('locked', True) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + ex = self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) + self.assertIn( + '--os-compute-api-version 2.73 or greater is required', str(ex)) + + def test_server_list_with_locked(self): + + self.app.client_manager.compute.api_version = \ + api_versions.APIVersion('2.73') + arglist = [ + '--locked' + ] + verifylist = [ + ('locked', True) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.search_opts['locked'] = True + self.servers_mock.list.assert_called_with(**self.kwargs) + + self.assertItemsEqual(self.columns, columns) + self.assertItemsEqual(self.data, tuple(data)) + + def test_server_list_with_unlocked_v273(self): + + self.app.client_manager.compute.api_version = \ + api_versions.APIVersion('2.73') + arglist = [ + '--unlocked' + ] + verifylist = [ + ('unlocked', True) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.search_opts['locked'] = False + self.servers_mock.list.assert_called_with(**self.kwargs) + + self.assertItemsEqual(self.columns, columns) + self.assertItemsEqual(self.data, tuple(data)) + + def test_server_list_with_locked_and_unlocked(self): + + self.app.client_manager.compute.api_version = \ + api_versions.APIVersion('2.73') + arglist = [ + '--locked', + '--unlocked' + ] + verifylist = [ + ('locked', True), + ('unlocked', True) + ] + + ex = self.assertRaises( + utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + self.assertIn('Argument parse failed', str(ex)) + + def test_server_list_with_changes_before(self): + self.app.client_manager.compute.api_version = ( + api_versions.APIVersion('2.66')) + arglist = [ + '--changes-before', '2016-03-05T06:27:59Z', + '--deleted' + ] + verifylist = [ + ('changes_before', '2016-03-05T06:27:59Z'), + ('deleted', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.search_opts['changes-before'] = '2016-03-05T06:27:59Z' + self.search_opts['deleted'] = True + + self.servers_mock.list.assert_called_with(**self.kwargs) + + self.assertItemsEqual(self.columns, columns) + self.assertItemsEqual(self.data, tuple(data)) + + @mock.patch.object(iso8601, 'parse_date', side_effect=iso8601.ParseError) + def test_server_list_with_invalid_changes_before( + self, mock_parse_isotime): + self.app.client_manager.compute.api_version = ( + api_versions.APIVersion('2.66')) + + arglist = [ + '--changes-before', 'Invalid time value', + ] + verifylist = [ + ('changes_before', 'Invalid time value'), + ] + + 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('Invalid changes-before value: Invalid time ' + 'value', str(e)) + mock_parse_isotime.assert_called_once_with( + 'Invalid time value' + ) + + def test_server_with_changes_before_pre_v266(self): + self.app.client_manager.compute.api_version = ( + api_versions.APIVersion('2.65')) + + arglist = [ + '--changes-before', '2016-03-05T06:27:59Z', + '--deleted' + ] + verifylist = [ + ('changes_before', '2016-03-05T06:27:59Z'), + ('deleted', True), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) + + def test_server_list_v269_with_partial_constructs(self): + self.app.client_manager.compute.api_version = \ + api_versions.APIVersion('2.69') + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + # include "partial results" from non-responsive part of + # infrastructure. + server_dict = { + "id": "server-id-95a56bfc4xxxxxx28d7e418bfd97813a", + "status": "UNKNOWN", + "tenant_id": "6f70656e737461636b20342065766572", + "created": "2018-12-03T21:06:18Z", + "links": [ + { + "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": {} + } + server = compute_fakes.fakes.FakeResource( + info=server_dict, + ) + self.servers.append(server) + columns, data = self.cmd.take_action(parsed_args) + # get the first three servers out since our interest is in the partial + # server. + next(data) + next(data) + next(data) + partial_server = next(data) + expected_row = ( + 'server-id-95a56bfc4xxxxxx28d7e418bfd97813a', '', + 'UNKNOWN', format_columns.DictListColumn({}), '', '') + self.assertEqual(expected_row, partial_server) + + class TestServerLock(TestServer): def setUp(self): diff --git a/releasenotes/notes/fix-flavor-in-server-list-microversion-2.47-af200e9bb4747e2d.yaml b/releasenotes/notes/fix-flavor-in-server-list-microversion-2.47-af200e9bb4747e2d.yaml new file mode 100644 index 0000000000..fdb37bbbc9 --- /dev/null +++ b/releasenotes/notes/fix-flavor-in-server-list-microversion-2.47-af200e9bb4747e2d.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Add support for compute API microversion 2.47, which changes how flavor + details are included in server detail responses. In 2.46 and below, + only the flavor ID was shown in the server detail response. Starting in + 2.47, flavor information is embedded in the server response. The newer + behavior is now supported. From c8c4f76498de3380c7cbf80c5dc800a588bed649 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Tue, 26 Oct 2021 13:03:22 +0000 Subject: [PATCH 054/706] Add --security-group to port list The neutron API supports filtering ports by security group. Closes-Bug: #1405057 Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/804979 Change-Id: I0f626882716c21ac200c1b929ea04664d21874d8 --- openstackclient/network/v2/port.py | 9 +++++++++ .../tests/unit/network/v2/test_port.py | 20 +++++++++++++++++++ ...-list-security-group-4af5d2e789174ff9.yaml | 5 +++++ 3 files changed, 34 insertions(+) create mode 100644 releasenotes/notes/port-list-security-group-4af5d2e789174ff9.yaml diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 132c384a0e..887c531808 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -610,6 +610,13 @@ def get_parser(self, prog_name): metavar='', help=_("List ports according to their name") ) + parser.add_argument( + '--security-group', + action='append', + dest='security_groups', + metavar='', + help=_("List only ports associated with this security group") + ) identity_common.add_project_domain_option_to_parser(parser) parser.add_argument( '--fixed-ip', @@ -682,6 +689,8 @@ def take_action(self, parsed_args): if parsed_args.fixed_ip: 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 _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 5f2a12836a..96edb74df8 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -1296,6 +1296,26 @@ def test_list_with_tag_options(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) + def test_port_list_security_group(self): + arglist = [ + '--security-group', 'sg-id1', + '--security-group', 'sg-id2', + ] + verifylist = [ + ('security_groups', ['sg-id1', 'sg-id2']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + filters = { + 'security_groups': ['sg-id1', 'sg-id2'], + 'fields': LIST_FIELDS_TO_RETRIEVE, + } + + self.network.ports.assert_called_once_with(**filters) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, list(data)) + class TestSetPort(TestPort): diff --git a/releasenotes/notes/port-list-security-group-4af5d2e789174ff9.yaml b/releasenotes/notes/port-list-security-group-4af5d2e789174ff9.yaml new file mode 100644 index 0000000000..c68eeafbce --- /dev/null +++ b/releasenotes/notes/port-list-security-group-4af5d2e789174ff9.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Added ``--security-group`` option to the ``os port list`` command. This + option is appendable and multiple security group IDs can be provided. From bef70397a3e1240cc593b3fb34049f2ff6601e68 Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Tue, 27 Jul 2021 16:45:24 +0000 Subject: [PATCH 055/706] Add network update quota "limit_check" parameter This new parameter commands the Neutron server to first check the resource usage before setting the new quota limit. If the resource usage is below the new limit, the Neutron server will raise an exception. Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/806254 Depends-On: https://review.opendev.org/c/openstack/neutron/+/801470 Partial-Bug: #1936408 Change-Id: Idc1b99492d609eb699d0a6bef6cd760458a774f6 --- openstackclient/common/quota.py | 9 ++++ .../tests/functional/common/test_quota.py | 25 +++++++++++ .../tests/unit/common/test_quota.py | 43 +++++++++++++++++++ .../check-limit-quota-cc7f291dd1b537c1.yaml | 5 +++ 4 files changed, 82 insertions(+) create mode 100644 releasenotes/notes/check-limit-quota-cc7f291dd1b537c1.yaml diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 643cb4e474..677cba038e 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -535,6 +535,12 @@ def get_parser(self, prog_name): action='store_true', help=_('Force quota update (only supported by compute)') ) + parser.add_argument( + '--check-limit', + action='store_true', + help=_('Check quota limit when updating (only supported by ' + 'network)') + ) return parser def take_action(self, parsed_args): @@ -561,6 +567,9 @@ def take_action(self, parsed_args): volume_kwargs[k] = value network_kwargs = {} + if parsed_args.check_limit: + network_kwargs['check_limit'] = True + if self.app.client_manager.is_network_endpoint_enabled(): for k, v in NETWORK_QUOTAS.items(): value = getattr(parsed_args, k, None) diff --git a/openstackclient/tests/functional/common/test_quota.py b/openstackclient/tests/functional/common/test_quota.py index 9c05746077..bf67101a32 100644 --- a/openstackclient/tests/functional/common/test_quota.py +++ b/openstackclient/tests/functional/common/test_quota.py @@ -11,6 +11,9 @@ # under the License. import json +import uuid + +from tempest.lib import exceptions from openstackclient.tests.functional import base @@ -165,3 +168,25 @@ def test_quota_set_class(self): # returned attributes self.assertTrue(cmd_output["key-pairs"] >= 0) self.assertTrue(cmd_output["snapshots"] >= 0) + + def test_quota_network_set_with_check_limit(self): + if not self.haz_network: + self.skipTest('No Network service present') + if not self.is_extension_enabled('quota-check-limit'): + self.skipTest('No "quota-check-limit" extension present') + + self.openstack('quota set --networks 40 ' + self.PROJECT_NAME) + cmd_output = json.loads(self.openstack( + 'quota list -f json --network' + )) + self.assertIsNotNone(cmd_output) + self.assertEqual(40, cmd_output[0]['Networks']) + + # 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)) + + self.assertRaises(exceptions.CommandFailed, self.openstack, + 'quota set --networks 1 --check-limit ' + + self.PROJECT_NAME) diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index 8771359cb5..896a63a7bc 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -950,6 +950,49 @@ def test_quota_set_with_force(self): ) self.assertIsNone(result) + def test_quota_set_with_check_limit(self): + arglist = [ + '--subnets', str(network_fakes.QUOTA['subnet']), + '--volumes', str(volume_fakes.QUOTA['volumes']), + '--cores', str(compute_fakes.core_num), + '--check-limit', + self.projects[0].name, + ] + verifylist = [ + ('subnet', network_fakes.QUOTA['subnet']), + ('volumes', volume_fakes.QUOTA['volumes']), + ('cores', compute_fakes.core_num), + ('check_limit', True), + ('project', self.projects[0].name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + kwargs_compute = { + 'cores': compute_fakes.core_num, + } + kwargs_volume = { + 'volumes': volume_fakes.QUOTA['volumes'], + } + kwargs_network = { + 'subnet': network_fakes.QUOTA['subnet'], + 'check_limit': True, + } + self.compute_quotas_mock.update.assert_called_once_with( + self.projects[0].id, + **kwargs_compute + ) + self.volume_quotas_mock.update.assert_called_once_with( + self.projects[0].id, + **kwargs_volume + ) + self.network_mock.update_quota.assert_called_once_with( + self.projects[0].id, + **kwargs_network + ) + self.assertIsNone(result) + class TestQuotaShow(TestQuota): diff --git a/releasenotes/notes/check-limit-quota-cc7f291dd1b537c1.yaml b/releasenotes/notes/check-limit-quota-cc7f291dd1b537c1.yaml new file mode 100644 index 0000000000..171b4a5ae0 --- /dev/null +++ b/releasenotes/notes/check-limit-quota-cc7f291dd1b537c1.yaml @@ -0,0 +1,5 @@ +--- +features: + - Add ``--check-limit`` option to the ``openstack quota set`` command (only + for network commands). The network quota engine will check the resource + usage before setting the new quota limit. From 32e18253faa742aae5a4c9708a8a505c85ebb317 Mon Sep 17 00:00:00 2001 From: "Dr. Jens Harbott" Date: Tue, 7 Dec 2021 18:33:54 +0100 Subject: [PATCH 056/706] Fix RemoveServerVolume The nova API we're using to delete a server volume attachment needs to be handed a volume, not a volume attachment. Also make sure that we create an error if the volume isn't actually attached to the server. Signed-off-by: Dr. Jens Harbott Co-authored-by: Stephen Finucane Change-Id: I12abd3787ea47acb4da282d00fdc1989405a0564 --- openstackclient/compute/v2/server.py | 16 +++++----------- .../tests/unit/compute/v2/test_server.py | 10 ++++------ 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index fb13d30572..a18ce81012 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -3833,17 +3833,11 @@ def take_action(self, parsed_args): ignore_missing=False, ) - volume_attachments = compute_client.volume_attachments(server) - for volume_attachment in volume_attachments: - if volume_attachment.volume_id == volume.id: - compute_client.delete_volume_attachment( - volume_attachment, - server, - ) - break - else: - msg = _('Target volume attachment not found.') - raise exceptions.CommandError(msg) + compute_client.delete_volume_attachment( + volume, + server, + ignore_missing=False, + ) class RescueServer(command.Command): diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 00733e4a76..15be07fc6c 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -1016,10 +1016,6 @@ def setUp(self): self.cmd = server.RemoveServerVolume(self.app, None) def test_server_remove_volume(self): - self.sdk_client.volume_attachments.return_value = [ - self.volume_attachment - ] - arglist = [ self.servers[0].id, self.volumes[0].id, @@ -1036,8 +1032,10 @@ def test_server_remove_volume(self): self.assertIsNone(result) self.sdk_client.delete_volume_attachment.assert_called_once_with( - self.volume_attachment, - self.servers[0]) + self.volumes[0], + self.servers[0], + ignore_missing=False, + ) class TestServerAddNetwork(TestServer): From 4e9b9298429f5db505987853f98d2388b6745b13 Mon Sep 17 00:00:00 2001 From: "Dr. Jens Harbott" Date: Tue, 30 Nov 2021 09:21:56 +0100 Subject: [PATCH 057/706] Allow setting gateway when creating a router These options are not only valid when modifying a router, but also when one is created initially. Signed-off-by: Dr. Jens Harbott Change-Id: I3e12901f37cbd1639ac9dc9cc49b04114b80474c --- openstackclient/network/v2/router.py | 79 +++++++++++++------ .../tests/unit/network/v2/test_router.py | 37 +++++++++ ...ptions-create-router-97910a882b604652.yaml | 8 ++ 3 files changed, 101 insertions(+), 23 deletions(-) create mode 100644 releasenotes/notes/options-create-router-97910a882b604652.yaml diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index dde4eda99f..aeeec93175 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -111,6 +111,30 @@ def _get_attrs(client_manager, parsed_args): parsed_args.project_domain, ).id attrs['tenant_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 return attrs @@ -320,6 +344,32 @@ def get_parser(self, prog_name): "repeat option to set multiple availability zones)") ) _tag.add_tag_option_to_parser_for_create(parser, _('router')) + parser.add_argument( + '--external-gateway', + metavar="", + help=_("External Network used as router's gateway (name or ID)") + ) + parser.add_argument( + '--fixed-ip', + metavar='subnet=,ip-address=', + action=parseractions.MultiKeyValueAction, + optional_keys=['subnet', 'ip-address'], + help=_("Desired IP and/or subnet (name or ID) " + "on external gateway: " + "subnet=,ip-address= " + "(repeat option to set multiple fixed IP addresses)") + ) + snat_group = parser.add_mutually_exclusive_group() + snat_group.add_argument( + '--enable-snat', + action='store_true', + help=_("Enable Source NAT on external gateway") + ) + snat_group.add_argument( + '--disable-snat', + action='store_true', + help=_("Disable Source NAT on external gateway") + ) return parser @@ -338,6 +388,12 @@ def take_action(self, parsed_args): # tags cannot be set when created, so tags need to be set later. _tag.update_tags_for_set(client, obj, parsed_args) + if (parsed_args.disable_snat or parsed_args.enable_snat or + parsed_args.fixed_ip) and not parsed_args.external_gateway: + msg = (_("You must specify '--external-gateway' in order " + "to specify SNAT or fixed-ip values")) + raise exceptions.CommandError(msg) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters=_formatters) @@ -725,29 +781,6 @@ def take_action(self, parsed_args): msg = (_("You must specify '--external-gateway' in order " "to update the SNAT or fixed-ip values")) raise exceptions.CommandError(msg) - if parsed_args.external_gateway: - gateway_info = {} - network = 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 = 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 if ((parsed_args.qos_policy or parsed_args.no_qos_policy) and not parsed_args.external_gateway): diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 0324674817..04d9fe4836 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -186,6 +186,43 @@ def test_create_default_options(self): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) + def test_create_with_gateway(self): + _network = network_fakes.FakeNetwork.create_one_network() + _subnet = network_fakes.FakeSubnet.create_one_subnet() + self.network.find_network = mock.Mock(return_value=_network) + self.network.find_subnet = mock.Mock(return_value=_subnet) + arglist = [ + self.new_router.name, + '--external-gateway', _network.name, + '--enable-snat', + '--fixed-ip', 'ip-address=2001:db8::1' + ] + verifylist = [ + ('name', self.new_router.name), + ('enable', True), + ('distributed', False), + ('ha', False), + ('external_gateway', _network.name), + ('enable_snat', True), + ('fixed_ip', [{'ip-address': '2001:db8::1'}]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_router.assert_called_once_with(**{ + 'admin_state_up': True, + 'name': self.new_router.name, + 'external_gateway_info': { + 'network_id': _network.id, + 'enable_snat': True, + 'external_fixed_ips': [{'ip_address': '2001:db8::1'}], + }, + }) + self.assertFalse(self.network.set_tags.called) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + def _test_create_with_ha_options(self, option, ha): arglist = [ option, diff --git a/releasenotes/notes/options-create-router-97910a882b604652.yaml b/releasenotes/notes/options-create-router-97910a882b604652.yaml new file mode 100644 index 0000000000..f7d90b75c2 --- /dev/null +++ b/releasenotes/notes/options-create-router-97910a882b604652.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + It is now possible to add an external gateway to a router + immediately on creation. Previously it could only be done by + modifying the router after it had been created. This includes + the options to en- or disable SNAT and to specify a fixed-ip + on the external network. From b3cb85f1123b15c1ec4fafac9dcedc9381072a8b Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Mon, 6 Dec 2021 10:24:15 +0000 Subject: [PATCH 058/706] tests: Improve logging for executed commands We're seeing failures in a recently added tests, 'ServerTests.test_server_add_remove_volume' from 'openstackclient/tests/functional/compute/v2/test_server.py'. These failures are likely the result of slow CI nodes, but we don't have enough information in the CI logs to debug them. Starting logging the various commands executed in tests so that we can see these logs if and when tests fail. Change-Id: I4584dc5e6343fe8c8544431a527d8c3c7e7b3c5b Signed-off-by: Stephen Finucane --- openstackclient/tests/functional/base.py | 21 ++++++++---- .../functional/compute/v2/test_server.py | 33 ++++++++++++++----- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/openstackclient/tests/functional/base.py b/openstackclient/tests/functional/base.py index 0ed7dff8c4..e89c5b9737 100644 --- a/openstackclient/tests/functional/base.py +++ b/openstackclient/tests/functional/base.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +import logging import os import shlex import subprocess @@ -18,22 +19,30 @@ from tempest.lib import exceptions import testtools - ADMIN_CLOUD = os.environ.get('OS_ADMIN_CLOUD', 'devstack-admin') +LOG = logging.getLogger(__name__) def execute(cmd, fail_ok=False, merge_stderr=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 + proc = subprocess.Popen(cmdlist, stdout=stdout, stderr=stderr) - result, result_err = proc.communicate() - result = result.decode('utf-8') + + result_out, result_err = proc.communicate() + result_out = result_out.decode('utf-8') + LOG.debug('stdout: %s', result_out) + LOG.debug('stderr: %s', result_err) + if not fail_ok and proc.returncode != 0: - raise exceptions.CommandFailed(proc.returncode, cmd, result, - result_err) - return result + raise exceptions.CommandFailed( + proc.returncode, cmd, result_out, result_err, + ) + + return result_out class TestCase(testtools.TestCase): diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py index cf4bcbc2a9..0558ef6213 100644 --- a/openstackclient/tests/functional/compute/v2/test_server.py +++ b/openstackclient/tests/functional/compute/v2/test_server.py @@ -1195,19 +1195,19 @@ def test_server_add_remove_port(self): def test_server_add_remove_volume(self): volume_wait_for = volume_common.BaseVolumeTests.wait_for_status - name = uuid.uuid4().hex + server_name = uuid.uuid4().hex cmd_output = json.loads(self.openstack( 'server create -f json ' + '--network private ' + '--flavor ' + self.flavor_name + ' ' + '--image ' + self.image_name + ' ' + '--wait ' + - name + server_name )) self.assertIsNotNone(cmd_output['id']) - self.assertEqual(name, cmd_output['name']) - self.addCleanup(self.openstack, 'server delete --wait ' + name) + self.assertEqual(server_name, cmd_output['name']) + self.addCleanup(self.openstack, 'server delete --wait ' + server_name) server_id = cmd_output['id'] volume_name = uuid.uuid4().hex @@ -1225,7 +1225,7 @@ def test_server_add_remove_volume(self): cmd_output = json.loads(self.openstack( 'server add volume -f json ' + - name + ' ' + + server_name + ' ' + volume_name + ' ' + '--tag bar' )) @@ -1237,7 +1237,7 @@ def test_server_add_remove_volume(self): cmd_output = json.loads(self.openstack( 'server volume list -f json ' + - name + server_name )) self.assertEqual(volume_attachment_id, cmd_output[0]['ID']) @@ -1245,8 +1245,25 @@ def test_server_add_remove_volume(self): self.assertEqual(volume_id, cmd_output[0]['Volume ID']) volume_wait_for('volume', volume_name, 'in-use') - self.openstack('server remove volume ' + name + ' ' + volume_name) + + cmd_output = json.loads(self.openstack( + 'server event list -f json ' + + server_name + )) + self.assertEqual(2, len(cmd_output)) + self.assertIn('attach_volume', {x['Action'] for x in cmd_output}) + + self.openstack( + 'server remove volume ' + server_name + ' ' + volume_name + ) volume_wait_for('volume', volume_name, 'available') - raw_output = self.openstack('server volume list ' + name) + cmd_output = json.loads(self.openstack( + 'server event list -f json ' + + server_name + )) + self.assertEqual(3, len(cmd_output)) + self.assertIn('detach_volume', {x['Action'] for x in cmd_output}) + + raw_output = self.openstack('server volume list ' + server_name) self.assertEqual('\n', raw_output) From 9971d7253e9c3abd2e3940bf549ef8532ef929f9 Mon Sep 17 00:00:00 2001 From: Ritvik Vinodkumar Date: Wed, 1 Dec 2021 16:54:53 +0000 Subject: [PATCH 059/706] Switch add fixed IP to SDK Switch the add fixed IP command from novaclient to SDK. Change-Id: I4752ea7b4bfc17e04b8f46dbe9a68d938501a89e --- openstackclient/compute/v2/server.py | 44 ++-- .../tests/unit/compute/v2/test_server.py | 224 ++++++++++++++---- ...-add-fixed-ip-to-sdk-3d932d77633bc765.yaml | 3 + 3 files changed, 211 insertions(+), 60 deletions(-) create mode 100644 releasenotes/notes/migrate-add-fixed-ip-to-sdk-3d932d77633bc765.yaml diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index a18ce81012..0931a6ca01 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -237,30 +237,44 @@ 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) - - network = compute_client.api.network_find(parsed_args.network) - - kwargs = { - 'port_id': None, - 'net_id': network['id'], - 'fixed_ip': parsed_args.fixed_ip_address, - } + compute_client = self.app.client_manager.sdk_connection.compute + server = compute_client.find_server( + parsed_args.server, + ignore_missing=False + ) if parsed_args.tag: - if compute_client.api_version < api_versions.APIVersion('2.49'): + if not sdk_utils.supports_microversion(compute_client, '2.49'): msg = _( '--os-compute-api-version 2.49 or greater is required to ' 'support the --tag option' ) raise exceptions.CommandError(msg) - kwargs['tag'] = parsed_args.tag + if self.app.client_manager.is_network_endpoint_enabled(): + network_client = self.app.client_manager.network + net_id = network_client.find_network( + parsed_args.network, + ignore_missing=False + ).id + else: + net_id = parsed_args.network + + if not sdk_utils.supports_microversion(compute_client, '2.44'): + compute_client.add_fixed_ip_to_server( + server.id, + net_id + ) + return + + kwargs = { + 'net_id': net_id, + 'fixed_ip': parsed_args.fixed_ip_address, + } - server.interface_attach(**kwargs) + if parsed_args.tag: + kwargs['tag'] = parsed_args.tag + compute_client.create_server_interface(server.id, **kwargs) class AddFloatingIP(network_common.NetworkAndComputeCommand): diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 15be07fc6c..5d47b68784 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -217,104 +217,104 @@ 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 + # Set add_fixed_ip method to be tested. self.methods = { 'interface_attach': None, } - def _test_server_add_fixed_ip(self, extralist, fixed_ip_address): - servers = self.setup_servers_mock(count=1) + @mock.patch.object(sdk_utils, 'supports_microversion') + def test_server_add_fixed_ip_pre_2_44(self, sm_mock): + sm_mock.return_value = False + + servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.FakeNetwork.create_one_network() - with mock.patch( - 'openstackclient.api.compute_v2.APIv2.network_find' - ) as net_mock: - net_mock.return_value = network + with mock.patch.object( + self.app.client_manager, + 'is_network_endpoint_enabled', + return_value=False + ): arglist = [ servers[0].id, network['id'], - ] + extralist + ] verifylist = [ ('server', servers[0].id), ('network', network['id']), - ('fixed_ip_address', fixed_ip_address), + ('fixed_ip_address', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - servers[0].interface_attach.assert_called_once_with( - port_id=None, - net_id=network['id'], - fixed_ip=fixed_ip_address, + self.sdk_client.add_fixed_ip_to_server.assert_called_once_with( + servers[0].id, + network['id'] ) self.assertIsNone(result) - def test_server_add_fixed_ip(self): - self._test_server_add_fixed_ip([], None) - - def test_server_add_specific_fixed_ip(self): - extralist = ['--fixed-ip-address', '5.6.7.8'] - self._test_server_add_fixed_ip(extralist, '5.6.7.8') - - def test_server_add_fixed_ip_with_tag(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.49') + @mock.patch.object(sdk_utils, 'supports_microversion') + def test_server_add_fixed_ip_pre_2_44_with_fixed_ip(self, sm_mock): + sm_mock.return_value = False - servers = self.setup_servers_mock(count=1) + servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.FakeNetwork.create_one_network() - with mock.patch( - 'openstackclient.api.compute_v2.APIv2.network_find' - ) as net_mock: - net_mock.return_value = network + with mock.patch.object( + self.app.client_manager, + 'is_network_endpoint_enabled', + return_value=False + ): arglist = [ servers[0].id, network['id'], - '--fixed-ip-address', '5.6.7.8', - '--tag', 'tag1', + '--fixed-ip-address', '5.6.7.8' ] verifylist = [ ('server', servers[0].id), ('network', network['id']), ('fixed_ip_address', '5.6.7.8'), - ('tag', 'tag1'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) - servers[0].interface_attach.assert_called_once_with( - port_id=None, - net_id=network['id'], - fixed_ip='5.6.7.8', - tag='tag1' + self.sdk_client.add_fixed_ip_to_server.assert_called_once_with( + servers[0].id, + network['id'] ) self.assertIsNone(result) - def test_server_add_fixed_ip_with_tag_pre_v249(self): - self.app.client_manager.compute.api_version = api_versions.APIVersion( - '2.48') + @mock.patch.object(sdk_utils, 'supports_microversion') + def test_server_add_fixed_ip_pre_2_44_with_tag(self, sm_mock): + sm_mock.return_value = False - servers = self.setup_servers_mock(count=1) + servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.FakeNetwork.create_one_network() - with mock.patch( - 'openstackclient.api.compute_v2.APIv2.network_find' - ) as net_mock: - net_mock.return_value = network + with mock.patch.object( + self.app.client_manager, + 'is_network_endpoint_enabled', + return_value=False + ): arglist = [ servers[0].id, network['id'], '--fixed-ip-address', '5.6.7.8', - '--tag', 'tag1', + '--tag', 'tag1' ] verifylist = [ ('server', servers[0].id), ('network', network['id']), ('fixed_ip_address', '5.6.7.8'), - ('tag', 'tag1'), + ('tag', 'tag1') ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) + ex = self.assertRaises( exceptions.CommandError, self.cmd.take_action, @@ -323,6 +323,140 @@ def test_server_add_fixed_ip_with_tag_pre_v249(self): '--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_pre_2_49_with_tag(self, sm_mock): + sm_mock.side_effect = [False, True] + + servers = self.setup_sdk_servers_mock(count=1) + network = compute_fakes.FakeNetwork.create_one_network() + + with mock.patch.object( + self.app.client_manager, + 'is_network_endpoint_enabled', + return_value=False + ): + arglist = [ + servers[0].id, + network['id'], + '--fixed-ip-address', '5.6.7.8', + '--tag', 'tag1' + ] + verifylist = [ + ('server', servers[0].id), + ('network', network['id']), + ('fixed_ip_address', '5.6.7.8'), + ('tag', 'tag1') + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + ex = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args) + self.assertIn( + '--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_post_2_49(self, sm_mock): + sm_mock.side_effect = [True, True] + + servers = self.setup_sdk_servers_mock(count=1) + network = compute_fakes.FakeNetwork.create_one_network() + + with mock.patch.object( + self.app.client_manager, + 'is_network_endpoint_enabled', + return_value=False + ): + arglist = [ + servers[0].id, + network['id'] + ] + verifylist = [ + ('server', servers[0].id), + ('network', network['id']) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.sdk_client.create_server_interface.assert_called_once_with( + servers[0].id, + net_id=network['id'], + fixed_ip=None + ) + self.assertIsNone(result) + + @mock.patch.object(sdk_utils, 'supports_microversion') + def test_server_add_fixed_ip_post_2_49_with_fixedip(self, sm_mock): + sm_mock.side_effect = [True, True] + + servers = self.setup_sdk_servers_mock(count=1) + network = compute_fakes.FakeNetwork.create_one_network() + + with mock.patch.object( + self.app.client_manager, + 'is_network_endpoint_enabled', + return_value=False + ): + arglist = [ + servers[0].id, + network['id'], + '--fixed-ip-address', '5.6.7.8' + ] + verifylist = [ + ('server', servers[0].id), + ('network', network['id']), + ('fixed_ip_address', '5.6.7.8') + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.sdk_client.create_server_interface.assert_called_once_with( + servers[0].id, + net_id=network['id'], + fixed_ip='5.6.7.8' + ) + self.assertIsNone(result) + + @mock.patch.object(sdk_utils, 'supports_microversion') + def test_server_add_fixed_ip_post_2_49_with_tag(self, sm_mock): + sm_mock.side_effect = [True, True] + + servers = self.setup_sdk_servers_mock(count=1) + network = compute_fakes.FakeNetwork.create_one_network() + + with mock.patch.object( + self.app.client_manager, + 'is_network_endpoint_enabled', + return_value=False + ): + arglist = [ + servers[0].id, + network['id'], + '--fixed-ip-address', '5.6.7.8', + '--tag', 'tag1' + ] + verifylist = [ + ('server', servers[0].id), + ('network', network['id']), + ('fixed_ip_address', '5.6.7.8'), + ('tag', 'tag1') + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.sdk_client.create_server_interface.assert_called_once_with( + servers[0].id, + net_id=network['id'], + fixed_ip='5.6.7.8', + tag='tag1' + ) + self.assertIsNone(result) + @mock.patch( 'openstackclient.api.compute_v2.APIv2.floating_ip_add' diff --git a/releasenotes/notes/migrate-add-fixed-ip-to-sdk-3d932d77633bc765.yaml b/releasenotes/notes/migrate-add-fixed-ip-to-sdk-3d932d77633bc765.yaml new file mode 100644 index 0000000000..79899b3e92 --- /dev/null +++ b/releasenotes/notes/migrate-add-fixed-ip-to-sdk-3d932d77633bc765.yaml @@ -0,0 +1,3 @@ +features: + - | + Switch the add fixed IP command from novaclient to SDK. From 0cde82dcd86205f9e190b1a4f02136e1d83797b9 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 14 Dec 2021 15:14:49 +0000 Subject: [PATCH 060/706] compute: Return information about fixed IP The compute API provides this information to us. We might as well use it. Change-Id: I5608fa80745975ce49712718452cfe296c0f64d2 Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 33 ++++- .../tests/unit/compute/v2/fakes.py | 28 +++++ .../tests/unit/compute/v2/test_server.py | 117 +++++++++++++----- 3 files changed, 145 insertions(+), 33 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 0931a6ca01..2c1e42cc79 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -204,7 +204,7 @@ def boolenv(*vars, default=False): return default -class AddFixedIP(command.Command): +class AddFixedIP(command.ShowOne): _description = _("Add fixed IP address to server") def get_parser(self, prog_name): @@ -231,7 +231,7 @@ def get_parser(self, prog_name): metavar='', help=_( 'Tag for the attached interface. ' - '(supported by --os-compute-api-version 2.52 or above)' + '(supported by --os-compute-api-version 2.49 or above)' ) ) return parser @@ -265,16 +265,39 @@ def take_action(self, parsed_args): server.id, net_id ) - return + return ((), ()) kwargs = { 'net_id': net_id, 'fixed_ip': parsed_args.fixed_ip_address, } - if parsed_args.tag: kwargs['tag'] = parsed_args.tag - compute_client.create_server_interface(server.id, **kwargs) + + interface = compute_client.create_server_interface(server.id, **kwargs) + + columns = ( + 'port_id', 'server_id', 'net_id', 'mac_addr', 'port_state', + 'fixed_ips', + ) + column_headers = ( + 'Port ID', 'Server ID', 'Network ID', 'MAC Address', 'Port State', + 'Fixed IPs', + ) + if sdk_utils.supports_microversion(compute_client, '2.49'): + columns += ('tag',) + column_headers += ('Tag',) + + return ( + column_headers, + utils.get_item_properties( + interface, + columns, + formatters={ + 'fixed_ips': format_columns.ListDictColumn, + }, + ), + ) class AddFloatingIP(network_common.NetworkAndComputeCommand): diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py index 7618c229c5..2a53164735 100644 --- a/openstackclient/tests/unit/compute/v2/fakes.py +++ b/openstackclient/tests/unit/compute/v2/fakes.py @@ -21,6 +21,7 @@ from novaclient import api_versions from openstack.compute.v2 import flavor as _flavor from openstack.compute.v2 import server +from openstack.compute.v2 import server_interface as _server_interface from openstack.compute.v2 import volume_attachment from openstackclient.api import compute_v2 @@ -1859,3 +1860,30 @@ def create_sdk_volume_attachments(attrs=None, methods=None, count=2): attrs, methods)) return volume_attachments + + +def create_one_server_interface(attrs=None): + """Create a fake SDK ServerInterface. + + :param dict attrs: A dictionary with all attributes + :param dict methods: A dictionary with all methods + :return: A fake ServerInterface object with various attributes set + """ + attrs = attrs or {} + + # Set default attributes. + server_interface_info = { + "fixed_ips": uuid.uuid4().hex, + "mac_addr": "aa:aa:aa:aa:aa:aa", + "net_id": uuid.uuid4().hex, + "port_id": uuid.uuid4().hex, + "port_state": "ACTIVE", + "server_id": uuid.uuid4().hex, + # introduced in API microversion 2.70 + "tag": "foo", + } + + # Overwrite default attributes. + server_interface_info.update(attrs) + + return _server_interface.ServerInterface(**server_interface_info) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 5d47b68784..6553f90465 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -212,7 +212,7 @@ def run_method_with_sdk_servers(self, method_name, server_count): class TestServerAddFixedIP(TestServer): def setUp(self): - super(TestServerAddFixedIP, self).setUp() + super().setUp() # Get the command object to test self.cmd = server.AddFixedIP(self.app, None) @@ -221,13 +221,8 @@ def setUp(self): self.find_network = mock.Mock() self.app.client_manager.network.find_network = self.find_network - # Set add_fixed_ip method to be tested. - self.methods = { - 'interface_attach': None, - } - @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_add_fixed_ip_pre_2_44(self, sm_mock): + def test_server_add_fixed_ip_pre_v244(self, sm_mock): sm_mock.return_value = False servers = self.setup_sdk_servers_mock(count=1) @@ -255,10 +250,11 @@ def test_server_add_fixed_ip_pre_2_44(self, sm_mock): servers[0].id, network['id'] ) - self.assertIsNone(result) + # the legacy API operates asynchronously + self.assertEqual(((), ()), result) @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_add_fixed_ip_pre_2_44_with_fixed_ip(self, sm_mock): + def test_server_add_fixed_ip_pre_v244_with_fixed_ip(self, sm_mock): sm_mock.return_value = False servers = self.setup_sdk_servers_mock(count=1) @@ -287,10 +283,11 @@ def test_server_add_fixed_ip_pre_2_44_with_fixed_ip(self, sm_mock): servers[0].id, network['id'] ) - self.assertIsNone(result) + # the legacy API operates asynchronously + self.assertEqual(((), ()), result) @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_add_fixed_ip_pre_2_44_with_tag(self, sm_mock): + def test_server_add_fixed_ip_pre_v244_with_tag(self, sm_mock): sm_mock.return_value = False servers = self.setup_sdk_servers_mock(count=1) @@ -324,7 +321,7 @@ def test_server_add_fixed_ip_pre_2_44_with_tag(self, sm_mock): str(ex)) @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_add_fixed_ip_pre_2_49_with_tag(self, sm_mock): + def test_server_add_fixed_ip_pre_v249_with_tag(self, sm_mock): sm_mock.side_effect = [False, True] servers = self.setup_sdk_servers_mock(count=1) @@ -358,11 +355,13 @@ def test_server_add_fixed_ip_pre_2_49_with_tag(self, sm_mock): str(ex)) @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_add_fixed_ip_post_2_49(self, sm_mock): - sm_mock.side_effect = [True, True] + def test_server_add_fixed_ip(self, sm_mock): + sm_mock.side_effect = [True, False] servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.FakeNetwork.create_one_network() + interface = compute_fakes.create_one_server_interface() + self.sdk_client.create_server_interface.return_value = interface with mock.patch.object( self.app.client_manager, @@ -379,21 +378,41 @@ def test_server_add_fixed_ip_post_2_49(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + expected_columns = ( + 'Port ID', + 'Server ID', + 'Network ID', + 'MAC Address', + 'Port State', + 'Fixed IPs', + ) + expected_data = ( + interface.port_id, + interface.server_id, + interface.net_id, + interface.mac_addr, + interface.port_state, + format_columns.ListDictColumn(interface.fixed_ips), + ) + + columns, data = self.cmd.take_action(parsed_args) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, tuple(data)) self.sdk_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'], fixed_ip=None ) - self.assertIsNone(result) @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_add_fixed_ip_post_2_49_with_fixedip(self, sm_mock): + def test_server_add_fixed_ip_with_fixed_ip(self, sm_mock): sm_mock.side_effect = [True, True] servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.FakeNetwork.create_one_network() + interface = compute_fakes.create_one_server_interface() + self.sdk_client.create_server_interface.return_value = interface with mock.patch.object( self.app.client_manager, @@ -412,21 +431,43 @@ def test_server_add_fixed_ip_post_2_49_with_fixedip(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + expected_columns = ( + 'Port ID', + 'Server ID', + 'Network ID', + 'MAC Address', + 'Port State', + 'Fixed IPs', + 'Tag', + ) + expected_data = ( + interface.port_id, + interface.server_id, + interface.net_id, + interface.mac_addr, + interface.port_state, + format_columns.ListDictColumn(interface.fixed_ips), + interface.tag, + ) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, tuple(data)) self.sdk_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'], fixed_ip='5.6.7.8' ) - self.assertIsNone(result) @mock.patch.object(sdk_utils, 'supports_microversion') - def test_server_add_fixed_ip_post_2_49_with_tag(self, sm_mock): - sm_mock.side_effect = [True, True] + def test_server_add_fixed_ip_with_tag(self, sm_mock): + sm_mock.side_effect = [True, True, True] servers = self.setup_sdk_servers_mock(count=1) network = compute_fakes.FakeNetwork.create_one_network() + interface = compute_fakes.create_one_server_interface() + self.sdk_client.create_server_interface.return_value = interface with mock.patch.object( self.app.client_manager, @@ -447,15 +488,35 @@ def test_server_add_fixed_ip_post_2_49_with_tag(self, sm_mock): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) + expected_columns = ( + 'Port ID', + 'Server ID', + 'Network ID', + 'MAC Address', + 'Port State', + 'Fixed IPs', + 'Tag', + ) + expected_data = ( + interface.port_id, + interface.server_id, + interface.net_id, + interface.mac_addr, + interface.port_state, + format_columns.ListDictColumn(interface.fixed_ips), + interface.tag, + ) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, tuple(data)) self.sdk_client.create_server_interface.assert_called_once_with( servers[0].id, net_id=network['id'], fixed_ip='5.6.7.8', - tag='tag1' + tag='tag1', ) - self.assertIsNone(result) @mock.patch( @@ -5265,7 +5326,7 @@ def test_server_migrate_with_disk_overcommit(self): self.assertNotCalled(self.servers_mock.live_migrate) self.assertNotCalled(self.servers_mock.migrate) - def test_server_migrate_with_host_pre_2_56(self): + 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). arglist = [ @@ -6682,7 +6743,7 @@ def test_rebuild_with_user_data(self, mock_open): self.image, None, userdata=mock_file,) - def test_rebuild_with_user_data_pre_257(self): + def test_rebuild_with_user_data_pre_v257(self): self.app.client_manager.compute.api_version = \ api_versions.APIVersion('2.56') @@ -6722,7 +6783,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_254(self): + def test_rebuild_with_no_user_data_pre_v254(self): self.app.client_manager.compute.api_version = \ api_versions.APIVersion('2.53') @@ -6814,7 +6875,7 @@ def test_rebuild_with_no_trusted_image_cert(self): self.server.rebuild.assert_called_with( self.image, None, trusted_image_certificates=None) - def test_rebuild_with_no_trusted_image_cert_pre_257(self): + def test_rebuild_with_no_trusted_image_cert_pre_v263(self): self.app.client_manager.compute.api_version = \ api_versions.APIVersion('2.62') From ba69870d86b5840dec06c6c30c8ddf50398bdb44 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 15 Dec 2021 17:32:10 +0000 Subject: [PATCH 061/706] compute: Fix weird option definition for 'server ssh' argparse allows you to specify multiple options for a given argument when declaring the argument. For some reason, we weren't doing this for the 'server ssh' command. There's no apparent reason for doing things this way and it's been that way since the beginning (2013) so let's not do that. We also add unit tests since they were missing and should exist. Change-Id: I67a9e6516d7057266210cd4083e9ddeb1cfaa5de Signed-off-by: Stephen Finucane --- openstackclient/compute/v2/server.py | 33 +------- .../tests/unit/compute/v2/test_server.py | 77 +++++++++++++++++++ 2 files changed, 81 insertions(+), 29 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index a18ce81012..5c603d04da 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -4462,51 +4462,26 @@ def get_parser(self, prog_name): help=_('Server (name or ID)'), ) parser.add_argument( - '--login', + '--login', '-l', metavar='', help=_('Login name (ssh -l option)'), ) parser.add_argument( - '-l', - dest='login', - metavar='', - help=argparse.SUPPRESS, - ) - parser.add_argument( - '--port', + '--port', '-p', metavar='', type=int, help=_('Destination port (ssh -p option)'), ) parser.add_argument( - '-p', - metavar='', - dest='port', - type=int, - help=argparse.SUPPRESS, - ) - parser.add_argument( - '--identity', + '--identity', '-i', metavar='', help=_('Private key file (ssh -i option)'), ) parser.add_argument( - '-i', - metavar='', - dest='identity', - help=argparse.SUPPRESS, - ) - parser.add_argument( - '--option', + '--option', '-o', metavar='', help=_('Options in ssh_config(5) format (ssh -o option)'), ) - parser.add_argument( - '-o', - metavar='