From a7f7327f3ae18218b29d70124f86d54d166cc414 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Tue, 3 Mar 2026 09:03:48 +0000 Subject: [PATCH 1/4] Update .gitreview for stable/2026.1 Change-Id: I300afc0992bcc39a9c4d9fe78bad1d59948f625d Signed-off-by: OpenStack Release Bot Generated-By: openstack/project-config:roles/copy-release-tools-scripts/files/release-tools/functions --- .gitreview | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitreview b/.gitreview index 4eee726db6..ae16227da3 100644 --- a/.gitreview +++ b/.gitreview @@ -2,3 +2,4 @@ host=review.opendev.org port=29418 project=openstack/python-openstackclient.git +defaultbranch=stable/2026.1 From bf0e8e26743f3effa69e88c8414cf80f02b75179 Mon Sep 17 00:00:00 2001 From: OpenStack Release Bot Date: Tue, 3 Mar 2026 09:03:50 +0000 Subject: [PATCH 2/4] Update TOX_CONSTRAINTS_FILE for stable/2026.1 Update the URL to the upper-constraints file to point to the redirect rule on releases.openstack.org so that anyone working on this branch will switch to the correct upper-constraints list automatically when the requirements repository branches. Until the requirements repository has as stable/2026.1 branch, tests will continue to use the upper-constraints list on master. Change-Id: If6329ff2acc43369f5594e96682f4abac2f56013 Signed-off-by: OpenStack Release Bot Generated-By: openstack/project-config:roles/copy-release-tools-scripts/files/release-tools/functions --- tox.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tox.ini b/tox.ini index 6ce9e96c60..1b7c7cc48b 100644 --- a/tox.ini +++ b/tox.ini @@ -11,7 +11,7 @@ setenv = OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 deps = - -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2026.1} -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt commands = @@ -65,7 +65,7 @@ commands = description = Run specified command in a virtual environment with all dependencies installed. deps = - -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2026.1} -r{toxinidir}/requirements.txt -r{toxinidir}/doc/requirements.txt commands = @@ -95,7 +95,7 @@ commands = description = Build documentation in HTML format. deps = - -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2026.1} -r{toxinidir}/doc/requirements.txt commands = sphinx-build -a -E -W -d doc/build/doctrees -b html doc/source doc/build/html @@ -107,7 +107,7 @@ commands = description = Build release note documentation in HTML format. deps = - -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} + -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2026.1} -r{toxinidir}/doc/requirements.txt commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html From f3e89d4df07dd535bee8be69a4d12a7b4a795842 Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Tue, 3 Mar 2026 09:38:25 +0000 Subject: [PATCH 3/4] typing: Add class variables to testcases testtools was recently bumped to a version that is typed, which means we now see type hints for that library. As a result, we now see issues with variables defined in tests via setUpClass. Note that the issue lies with mypy and not testtools, however: mypy doesn't adding class variables via assignment [1]. [1] https://github.com/python/mypy/issues/8723 Change-Id: I8b09846ff8fc6ee51ba03531f5b41039282ee1c0 Signed-off-by: Stephen Finucane (cherry picked from commit aeb0c6828b764e47a90f9f719a33a61bcb438ee7) --- .../tests/functional/common/test_extension.py | 4 ++ .../tests/functional/common/test_quota.py | 4 +- .../functional/compute/v2/test_flavor.py | 4 +- .../functional/compute/v2/test_server.py | 3 ++ .../tests/functional/identity/v2/common.py | 4 ++ .../tests/functional/identity/v3/common.py | 6 +++ .../tests/functional/image/base.py | 6 +++ .../tests/functional/network/v2/common.py | 5 ++- .../functional/network/v2/test_floating_ip.py | 6 +++ .../network/v2/test_ip_availability.py | 4 ++ .../network/v2/test_network_meter_rule.py | 5 ++- .../network/v2/test_network_segment.py | 5 +++ .../functional/network/v2/test_subnet.py | 22 ++++++----- .../tests/functional/object/v1/common.py | 4 ++ .../tests/functional/volume/v2/common.py | 4 ++ .../volume/v2/test_volume_snapshot.py | 38 +++++++++++++------ .../tests/functional/volume/v3/common.py | 4 ++ .../volume/v3/test_volume_snapshot.py | 18 +++++---- 18 files changed, 114 insertions(+), 32 deletions(-) diff --git a/openstackclient/tests/functional/common/test_extension.py b/openstackclient/tests/functional/common/test_extension.py index c65f52db51..1a8af9bc89 100644 --- a/openstackclient/tests/functional/common/test_extension.py +++ b/openstackclient/tests/functional/common/test_extension.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +from typing import ClassVar + from tempest.lib import exceptions as tempest_exc from openstackclient.tests.functional import base @@ -21,6 +23,8 @@ class ExtensionTests(base.TestCase): """Functional tests for extension""" + haz_network: ClassVar[bool] + @classmethod def setUpClass(cls): super().setUpClass() diff --git a/openstackclient/tests/functional/common/test_quota.py b/openstackclient/tests/functional/common/test_quota.py index 373b178c15..a9bcaec742 100644 --- a/openstackclient/tests/functional/common/test_quota.py +++ b/openstackclient/tests/functional/common/test_quota.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from typing import ClassVar import uuid from tempest.lib.common.utils import data_utils @@ -25,7 +26,8 @@ class QuotaTests(base.TestCase): test runs as these may run in parallel and otherwise step on each other. """ - PROJECT_NAME: str + haz_network: ClassVar[bool] + PROJECT_NAME: ClassVar[str] @classmethod def setUpClass(cls): diff --git a/openstackclient/tests/functional/compute/v2/test_flavor.py b/openstackclient/tests/functional/compute/v2/test_flavor.py index 4a0ff4883c..7f4cc43e71 100644 --- a/openstackclient/tests/functional/compute/v2/test_flavor.py +++ b/openstackclient/tests/functional/compute/v2/test_flavor.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from typing import ClassVar import uuid from openstackclient.tests.functional import base @@ -19,6 +20,7 @@ class FlavorTests(base.TestCase): """Functional tests for flavor.""" PROJECT_NAME = uuid.uuid4().hex + PROJECT_ID: ClassVar[str] @classmethod def setUpClass(cls): @@ -28,7 +30,7 @@ def setUpClass(cls): "project create --enable " + cls.PROJECT_NAME, parse_output=True, ) - cls.project_id = cmd_output["id"] + cls.PROJECT_ID = cmd_output["id"] @classmethod def tearDownClass(cls): diff --git a/openstackclient/tests/functional/compute/v2/test_server.py b/openstackclient/tests/functional/compute/v2/test_server.py index 6afa2c7c0e..c00d9dc555 100644 --- a/openstackclient/tests/functional/compute/v2/test_server.py +++ b/openstackclient/tests/functional/compute/v2/test_server.py @@ -13,6 +13,7 @@ import itertools import json import time +from typing import ClassVar import uuid from tempest.lib import exceptions @@ -25,6 +26,8 @@ class ServerTests(common.ComputeTestCase): """Functional tests for openstack server commands""" + haz_network: ClassVar[bool] + @classmethod def setUpClass(cls): super().setUpClass() diff --git a/openstackclient/tests/functional/identity/v2/common.py b/openstackclient/tests/functional/identity/v2/common.py index dd2e271933..34962e6fea 100644 --- a/openstackclient/tests/functional/identity/v2/common.py +++ b/openstackclient/tests/functional/identity/v2/common.py @@ -11,6 +11,7 @@ # under the License. import os +from typing import ClassVar import unittest import fixtures @@ -57,6 +58,9 @@ class IdentityTests(base.TestCase): CATALOG_LIST_HEADERS = ['Name', 'Type', 'Endpoints'] ENDPOINT_LIST_HEADERS = ['ID', 'Region', 'Service Name', 'Service Type'] + project_name: ClassVar[str] + project_description: ClassVar[str] + @classmethod def setUpClass(cls): super().setUpClass() diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py index 9f21374ff5..8089e1d046 100644 --- a/openstackclient/tests/functional/identity/v3/common.py +++ b/openstackclient/tests/functional/identity/v3/common.py @@ -11,6 +11,7 @@ # under the License. import os +from typing import ClassVar import fixtures from tempest.lib.common.utils import data_utils @@ -147,6 +148,11 @@ class IdentityTests(base.TestCase): 'Region ID', ] + domain_name: ClassVar[str] + domain_description: ClassVar[str] + project_name: ClassVar[str] + project_description: ClassVar[str] + @classmethod def setUpClass(cls): super().setUpClass() diff --git a/openstackclient/tests/functional/image/base.py b/openstackclient/tests/functional/image/base.py index d948f81551..e11093f386 100644 --- a/openstackclient/tests/functional/image/base.py +++ b/openstackclient/tests/functional/image/base.py @@ -10,12 +10,18 @@ # License for the specific language governing permissions and limitations # under the License. +from typing import ClassVar + from openstackclient.tests.functional import base class BaseImageTests(base.TestCase): """Functional tests for Image commands""" + # TODO(stephenfin): Nothing sets this to true any more. We should remove it + # along with any dependent tests. + haz_v1_api: ClassVar[bool] + @classmethod def setUpClass(cls): super().setUpClass() diff --git a/openstackclient/tests/functional/network/v2/common.py b/openstackclient/tests/functional/network/v2/common.py index 248758a843..6d7272493b 100644 --- a/openstackclient/tests/functional/network/v2/common.py +++ b/openstackclient/tests/functional/network/v2/common.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from typing import ClassVar import uuid from openstackclient.tests.functional import base @@ -18,6 +19,8 @@ class NetworkTests(base.TestCase): """Functional tests for Network commands""" + haz_network: ClassVar[bool] + @classmethod def setUpClass(cls): super().setUpClass() @@ -33,7 +36,7 @@ def setUp(self): class NetworkTagTests(NetworkTests): """Functional tests with tag operation""" - base_command: str + base_command: ClassVar[str] def test_tag_operation(self): # Get project IDs diff --git a/openstackclient/tests/functional/network/v2/test_floating_ip.py b/openstackclient/tests/functional/network/v2/test_floating_ip.py index a1b11a44a3..e34fe552c2 100644 --- a/openstackclient/tests/functional/network/v2/test_floating_ip.py +++ b/openstackclient/tests/functional/network/v2/test_floating_ip.py @@ -11,6 +11,7 @@ # under the License. import random +from typing import ClassVar import uuid from openstackclient.tests.functional.network.v2 import common @@ -19,6 +20,11 @@ class FloatingIpTests(common.NetworkTests): """Functional tests for floating ip""" + EXTERNAL_NETWORK_NAME: ClassVar[str] + PRIVATE_NETWORK_NAME: ClassVar[str] + external_network_id: ClassVar[str] + private_network_id: ClassVar[str] + @classmethod def setUpClass(cls): super().setUpClass() diff --git a/openstackclient/tests/functional/network/v2/test_ip_availability.py b/openstackclient/tests/functional/network/v2/test_ip_availability.py index 1cdbd487a5..be1df9108a 100644 --- a/openstackclient/tests/functional/network/v2/test_ip_availability.py +++ b/openstackclient/tests/functional/network/v2/test_ip_availability.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from typing import ClassVar import uuid from openstackclient.tests.functional.network.v2 import common @@ -18,6 +19,9 @@ class IPAvailabilityTests(common.NetworkTests): """Functional tests for IP availability""" + NAME: ClassVar[str] + NETWORK_NAME: ClassVar[str] + @classmethod def setUpClass(cls): super().setUpClass() diff --git a/openstackclient/tests/functional/network/v2/test_network_meter_rule.py b/openstackclient/tests/functional/network/v2/test_network_meter_rule.py index c80643e31d..268794298c 100644 --- a/openstackclient/tests/functional/network/v2/test_network_meter_rule.py +++ b/openstackclient/tests/functional/network/v2/test_network_meter_rule.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from typing import ClassVar import unittest import uuid @@ -22,8 +23,8 @@ class TestMeterRule(common.NetworkTests): """Functional tests for meter rule""" - METER_ID: str - METER_RULE_ID: str + METER_ID: ClassVar[str] + METER_NAME: ClassVar[str] @classmethod def setUpClass(cls): diff --git a/openstackclient/tests/functional/network/v2/test_network_segment.py b/openstackclient/tests/functional/network/v2/test_network_segment.py index 03f5daf743..df26bcc8d7 100644 --- a/openstackclient/tests/functional/network/v2/test_network_segment.py +++ b/openstackclient/tests/functional/network/v2/test_network_segment.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from typing import ClassVar import uuid from openstackclient.tests.functional.network.v2 import common @@ -18,6 +19,10 @@ class NetworkSegmentTests(common.NetworkTests): """Functional tests for network segment""" + NETWORK_NAME: ClassVar[str] + NETWORK_ID: ClassVar[str] + PHYSICAL_NETWORK_NAME: ClassVar[str] + @classmethod def setUpClass(cls): super().setUpClass() diff --git a/openstackclient/tests/functional/network/v2/test_subnet.py b/openstackclient/tests/functional/network/v2/test_subnet.py index 2ec987e9b8..6af7c94541 100644 --- a/openstackclient/tests/functional/network/v2/test_subnet.py +++ b/openstackclient/tests/functional/network/v2/test_subnet.py @@ -11,6 +11,7 @@ # under the License. import random +from typing import ClassVar import uuid from openstackclient.tests.functional.network.v2 import common @@ -21,19 +22,22 @@ class SubnetTests(common.NetworkTagTests): base_command = 'subnet' + NETWORK_NAME: ClassVar[str] + NETWORK_ID: ClassVar[str] + @classmethod def setUpClass(cls): super().setUpClass() - if cls.haz_network: - cls.NETWORK_NAME = uuid.uuid4().hex - # Create a network for the all subnet tests - cmd_output = cls.openstack( - 'network create ' + cls.NETWORK_NAME, - parse_output=True, - ) - # Get network_id for assertEqual - cls.NETWORK_ID = cmd_output["id"] + cls.NETWORK_NAME = uuid.uuid4().hex + + # Create a network for the all subnet tests + cmd_output = cls.openstack( + 'network create ' + cls.NETWORK_NAME, + parse_output=True, + ) + # Get network_id for assertEqual + cls.NETWORK_ID = cmd_output["id"] @classmethod def tearDownClass(cls): diff --git a/openstackclient/tests/functional/object/v1/common.py b/openstackclient/tests/functional/object/v1/common.py index 036731da50..f3cc9aea93 100644 --- a/openstackclient/tests/functional/object/v1/common.py +++ b/openstackclient/tests/functional/object/v1/common.py @@ -10,12 +10,16 @@ # License for the specific language governing permissions and limitations # under the License. +from typing import ClassVar + from openstackclient.tests.functional import base class ObjectStoreTests(base.TestCase): """Functional tests for Object Store commands""" + haz_object_store: ClassVar[bool] + @classmethod def setUpClass(cls): super().setUpClass() diff --git a/openstackclient/tests/functional/volume/v2/common.py b/openstackclient/tests/functional/volume/v2/common.py index f15d4d961f..ed55030d27 100644 --- a/openstackclient/tests/functional/volume/v2/common.py +++ b/openstackclient/tests/functional/volume/v2/common.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +from typing import ClassVar + import fixtures from openstackclient.tests.functional.volume import base @@ -18,6 +20,8 @@ class BaseVolumeTests(base.BaseVolumeTests): """Base class for Volume functional tests.""" + haz_volume_v2: ClassVar[bool] + @classmethod def setUpClass(cls): super().setUpClass() diff --git a/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py b/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py index e5daded1bd..a5302033b6 100644 --- a/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py +++ b/openstackclient/tests/functional/volume/v2/test_volume_snapshot.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from typing import ClassVar import uuid from openstackclient.tests.functional.volume.v2 import common @@ -18,24 +19,27 @@ class VolumeSnapshotTests(common.BaseVolumeTests): """Functional tests for volume snapshot.""" - VOLLY = uuid.uuid4().hex + VOLUME_NAME = uuid.uuid4().hex + VOLUME_ID: ClassVar[str] @classmethod def setUpClass(cls): super().setUpClass() # create a volume for all tests to create snapshot cmd_output = cls.openstack( - 'volume create ' + '--size 1 ' + cls.VOLLY, + 'volume create ' + '--size 1 ' + cls.VOLUME_NAME, parse_output=True, ) - cls.wait_for_status('volume', cls.VOLLY, 'available') + cls.wait_for_status('volume', cls.VOLUME_NAME, 'available') cls.VOLUME_ID = cmd_output['id'] @classmethod def tearDownClass(cls): try: - cls.wait_for_status('volume', cls.VOLLY, 'available') - raw_output = cls.openstack('volume delete --force ' + cls.VOLLY) + cls.wait_for_status('volume', cls.VOLUME_NAME, 'available') + raw_output = cls.openstack( + 'volume delete --force ' + cls.VOLUME_NAME + ) cls.assertOutput('', raw_output) finally: super().tearDownClass() @@ -44,7 +48,10 @@ def test_volume_snapshot_delete(self): """Test create, delete multiple""" name1 = uuid.uuid4().hex cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, + 'volume snapshot create ' + + name1 + + ' --volume ' + + self.VOLUME_NAME, parse_output=True, ) self.assertEqual( @@ -54,7 +61,10 @@ def test_volume_snapshot_delete(self): name2 = uuid.uuid4().hex cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, + 'volume snapshot create ' + + name2 + + ' --volume ' + + self.VOLUME_NAME, parse_output=True, ) self.assertEqual( @@ -76,7 +86,10 @@ def test_volume_snapshot_list(self): """Test create, list filter""" name1 = uuid.uuid4().hex cmd_output = self.openstack( - 'volume snapshot create ' + name1 + ' --volume ' + self.VOLLY, + 'volume snapshot create ' + + name1 + + ' --volume ' + + self.VOLUME_NAME, parse_output=True, ) self.addCleanup(self.wait_for_delete, 'volume snapshot', name1) @@ -97,7 +110,10 @@ def test_volume_snapshot_list(self): name2 = uuid.uuid4().hex cmd_output = self.openstack( - 'volume snapshot create ' + name2 + ' --volume ' + self.VOLLY, + 'volume snapshot create ' + + name2 + + ' --volume ' + + self.VOLUME_NAME, parse_output=True, ) self.addCleanup(self.wait_for_delete, 'volume snapshot', name2) @@ -146,7 +162,7 @@ def test_volume_snapshot_list(self): # Test list --volume cmd_output = self.openstack( - 'volume snapshot list ' + '--volume ' + self.VOLLY, + 'volume snapshot list ' + '--volume ' + self.VOLUME_NAME, parse_output=True, ) names = [x["Name"] for x in cmd_output] @@ -169,7 +185,7 @@ def test_volume_snapshot_set(self): cmd_output = self.openstack( 'volume snapshot create ' + '--volume ' - + self.VOLLY + + self.VOLUME_NAME + ' --description aaaa ' + '--property Alpha=a ' + name, diff --git a/openstackclient/tests/functional/volume/v3/common.py b/openstackclient/tests/functional/volume/v3/common.py index cbab39275c..038991313a 100644 --- a/openstackclient/tests/functional/volume/v3/common.py +++ b/openstackclient/tests/functional/volume/v3/common.py @@ -10,6 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. +from typing import ClassVar + import fixtures from openstackclient.tests.functional.volume import base @@ -18,6 +20,8 @@ class BaseVolumeTests(base.BaseVolumeTests): """Base class for Volume functional tests.""" + haz_volume_v3: ClassVar[bool] + @classmethod def setUpClass(cls): super().setUpClass() diff --git a/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py b/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py index b84bb0368b..6921c997dd 100644 --- a/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py +++ b/openstackclient/tests/functional/volume/v3/test_volume_snapshot.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +from typing import ClassVar import uuid from openstackclient.tests.functional.volume.v3 import common @@ -18,24 +19,27 @@ class VolumeSnapshotTests(common.BaseVolumeTests): """Functional tests for volume snapshot.""" - VOLLY = uuid.uuid4().hex + VOLUME_NAME = uuid.uuid4().hex + VOLUME_ID: ClassVar[str] @classmethod def setUpClass(cls): super().setUpClass() # create a test volume used by all snapshot tests cmd_output = cls.openstack( - 'volume create ' + '--size 1 ' + cls.VOLLY, + 'volume create ' + '--size 1 ' + cls.VOLUME_NAME, parse_output=True, ) - cls.wait_for_status('volume', cls.VOLLY, 'available') + cls.wait_for_status('volume', cls.VOLUME_NAME, 'available') cls.VOLUME_ID = cmd_output['id'] @classmethod def tearDownClass(cls): try: - cls.wait_for_status('volume', cls.VOLLY, 'available') - raw_output = cls.openstack('volume delete --force ' + cls.VOLLY) + cls.wait_for_status('volume', cls.VOLUME_NAME, 'available') + raw_output = cls.openstack( + 'volume delete --force ' + cls.VOLUME_NAME + ) cls.assertOutput('', raw_output) finally: super().tearDownClass() @@ -47,7 +51,7 @@ def test_volume_snapshot(self): cmd_output = self.openstack( 'volume snapshot create ' + '--volume ' - + self.VOLLY + + self.VOLUME_NAME + ' --description aaaa ' + '--property Alpha=a ' + name, @@ -83,7 +87,7 @@ def test_volume_snapshot(self): # list volume snapshot --volume cmd_output = self.openstack( - 'volume snapshot list ' + '--volume ' + self.VOLLY, + 'volume snapshot list ' + '--volume ' + self.VOLUME_NAME, parse_output=True, ) names = [x["Name"] for x in cmd_output] From 3cb4491de5efcf4b6b9f8399426e77670ab62ffb Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Wed, 4 Mar 2026 12:16:16 +0000 Subject: [PATCH 4/4] identity: Fix project list SDK returns generators, not lists, and we do not see errors until they are iterated. Force early iteration by wrapping then in a list to ensure we actually see the HTTP 403 errors we were expecting. Change-Id: I0ab72e587bf4e16ae877db7a81023a226124e4d5 Signed-off-by: Stephen Finucane (cherry picked from commit 02b5630a9f9bf8bbc62d17542f61e769c2cb7fd1) --- openstackclient/identity/v3/project.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 99242391fe..f3d7fea237 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -342,21 +342,21 @@ def take_action(self, parsed_args): user = self.app.client_manager.auth_ref.user_id if user: - data = identity_client.user_projects(user, **kwargs) + data = list(identity_client.user_projects(user, **kwargs)) else: try: - data = identity_client.projects(**kwargs) + data = list(identity_client.projects(**kwargs)) except sdk_exc.ForbiddenException: # NOTE(adriant): if no filters, assume a forbidden is non-admin # wanting their own project list. if not kwargs: user = self.app.client_manager.auth_ref.user_id - data = identity_client.user_projects(user) + data = list(identity_client.user_projects(user)) else: raise if parsed_args.sort: - data = utils.sort_items(data, parsed_args.sort) + data = list(utils.sort_items(data, parsed_args.sort)) return ( column_headers,