diff --git a/.gitreview b/.gitreview index 4eee726db..a5f028781 100644 --- a/.gitreview +++ b/.gitreview @@ -2,3 +2,4 @@ host=review.opendev.org port=29418 project=openstack/python-openstackclient.git +defaultbranch=stable/2025.1 diff --git a/.zuul.yaml b/.zuul.yaml index e688bd26a..a320f6d02 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -137,22 +137,6 @@ tox_envlist: functional tox_install_siblings: true -- secret: - name: osc-dockerhub - data: - username: osclientzuul - password: !encrypted/pkcs1-oaep - - LbIZjJiVstRVXMpoLQ3+/JcNB6lKVUWJXXo5+Outf+PKAaO7mNnv8XLiFMKnJ6ftopLyu - hWbX9rA+NddvplLQkf1xxkh7QBBU8PToLr58quI2SENUclt4tpjxbZfZu451kFSNJvNvR - E58cHHpfJZpyRnS2htXmN/Qy24gbV2w7CQxSZD2YhlcrerD8uQ8rWEnlY1wcJEaEGomtS - ZTGxsdK2TsZC2cd4b7TG7+xbl2i+hjADzwSQAgUzlLlwuG71667+IWk4SOZ7OycJTv9NN - ZTak8+CGfiMKdmsxZ1Z8uD7DC+RIklDjMWyly6zuhWzfhOmsmU0CesR50moodRUvbK79p - NZM8u0hBex5cl2EpUEwJL/FSPJXUhDMPoMoTZT/SAuXf25R9eZ9JGrKsIAlmVhpl8ifoE - 8TpPyvIHGS3YelTQjhqOX0wGb9T4ZauQCcI5Ajzy9NuCTyD9xxme9OX1zz7gMACRnVHvz - q7U7Ue90MnmGH6E2SgKjIZhyzy9Efwb7JUvH1Zb3hlrjCjEhwi9MV5FnABTEeXyYwE10s - 3o/KZg2zvdWkVG6x0dEkjpoQaNuaB7T2Na7Sm421n/z3LCzhiQGuTUjENnL6cMEtuA6Pp - BfI5+Qlg7HMwkBXNB73EPfWHzbCR3VNrzGYTy9FvhGud0/cXsuBXgps4WH63ic= - - job: name: osc-build-image parent: opendev-build-docker-image @@ -162,38 +146,10 @@ - python-builder-3.11-bookworm-container-image - python-base-3.11-bookworm-container-image provides: osc-container-image - vars: &osc_image_vars + vars: docker_images: - context: . - repository: osclient/python-openstackclient - -- job: - name: osc-upload-image - parent: opendev-upload-docker-image - description: Build Docker images and upload to Docker Hub. - allowed-projects: openstack/python-openstackclient - requires: - - python-builder-3.11-bookworm-container-image - - python-base-3.11-bookworm-container-image - provides: osc-container-image - secrets: - - name: docker_credentials - secret: osc-dockerhub - pass-to-parent: true - vars: *osc_image_vars - -- job: - name: osc-promote-image - parent: opendev-promote-docker-image - allowed-projects: openstack/python-openstackclient - description: Promote previously uploaded Docker images. - secrets: - - name: docker_credentials - secret: osc-dockerhub - pass-to-parent: true - nodeset: - nodes: [] - vars: *osc_image_vars + tags: [] - project-template: name: osc-tox-unit-tips @@ -217,7 +173,8 @@ - release-notes-jobs-python3 check: jobs: - - osc-build-image + - osc-build-image: + voting: false - osc-functional-devstack - osc-functional-devstack-tips: # The functional-tips job only tests the latest and shouldn't be run @@ -225,8 +182,4 @@ branches: ^master$ gate: jobs: - - osc-upload-image - osc-functional-devstack - promote: - jobs: - - osc-promote-image diff --git a/bindep.txt b/bindep.txt index 4c90a026f..83ebf2d43 100644 --- a/bindep.txt +++ b/bindep.txt @@ -8,3 +8,4 @@ libffi-dev [compile test platform:dpkg] libssl-dev [compile test platform:dpkg] python3-dev [compile test platform:dpkg] python3-devel [compile test platform:rpm] +libpcre3-dev [test platform:dpkg] diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 638d2e1ce..329dc5f96 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -733,21 +733,32 @@ def take_action(self, parsed_args): # values if the project or class does not exist. This is expected # behavior. However, we have already checked for the presence of the # project above so it shouldn't be an issue. - if parsed_args.service in {'all', 'compute'}: + if parsed_args.service == 'compute' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_compute_endpoint_enabled() + ): compute_quota_info = get_compute_quotas( self.app, project, detail=parsed_args.usage, default=parsed_args.default, ) - if parsed_args.service in {'all', 'volume'}: + + if parsed_args.service == 'volume' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_volume_endpoint_enabled() + ): volume_quota_info = get_volume_quotas( self.app, project, detail=parsed_args.usage, default=parsed_args.default, ) - if parsed_args.service in {'all', 'network'}: + + if parsed_args.service == 'network' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_network_endpoint_enabled() + ): network_quota_info = get_network_quotas( self.app, project, @@ -880,12 +891,18 @@ def take_action(self, parsed_args): ) # compute quotas - if parsed_args.service in {'all', 'compute'}: + if parsed_args.service == 'compute' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_compute_endpoint_enabled() + ): compute_client = self.app.client_manager.sdk_connection.compute compute_client.revert_quota_set(project.id) # volume quotas - if parsed_args.service in {'all', 'volume'}: + if parsed_args.service == 'volume' or ( + parsed_args.service == 'all' + and self.app.client_manager.is_volume_endpoint_enabled() + ): volume_client = self.app.client_manager.sdk_connection.volume volume_client.revert_quota_set(project.id) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index dcadca1b6..a26628d3a 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -183,8 +183,12 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): 'updated_at': 'updated', 'user_data': 'OS-EXT-SRV-ATTR:user_data', 'vm_state': 'OS-EXT-STS:vm_state', - 'pinned_availability_zone': 'pinned_availability_zone', } + # NOTE(ratailor): microversion 2.96 introduces + # pinned_availability_zone support + if sdk_utils.supports_microversion(compute_client, '2.96'): + column_map['pinned_availability_zone'] = 'pinned_availability_zone' + # Some columns returned by openstacksdk should not be shown because they're # either irrelevant or duplicates ignored_columns = { @@ -235,6 +239,11 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True): info = data + # NOTE(ratailor): microversion 2.96 introduces + # pinned_availability_zone support + if not sdk_utils.supports_microversion(compute_client, '2.96'): + info.pop('pinned_availability_zone', None) + # Convert the image blob to a name image_info = info.get('image', {}) if image_info and any(image_info.values()): @@ -2870,16 +2879,17 @@ def take_action(self, parsed_args): if parsed_args.long: columns += ( 'availability_zone', - 'pinned_availability_zone', 'hypervisor_hostname', 'metadata', ) column_headers += ( 'Availability Zone', - 'Pinned Availability Zone', 'Host', 'Properties', ) + if sdk_utils.supports_microversion(compute_client, '2.96'): + columns += ('pinned_availability_zone',) + column_headers += ('Pinned Availability Zone',) # support for additional columns if parsed_args.columns: @@ -2913,10 +2923,11 @@ def take_action(self, parsed_args): column_headers += ('Availability Zone',) if c in ( 'pinned_availability_zone', - "Pinned Availability Zone", + 'Pinned Availability Zone', ): - columns += ('Pinned Availability Zone',) - column_headers += ('Pinned Availability Zone',) + if sdk_utils.supports_microversion(compute_client, '2.96'): + columns += ('pinned_availability_zone',) + column_headers += ('Pinned Availability Zone',) if c in ('Host', "host"): columns += ('hypervisor_hostname',) column_headers += ('Host',) diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py index 447a57f2b..41d01fa7b 100644 --- a/openstackclient/identity/v3/trust.py +++ b/openstackclient/identity/v3/trust.py @@ -123,37 +123,67 @@ def take_action(self, parsed_args): # pointless, and trusts are immutable, so let's enforce it at the # client level. try: - trustor_id = identity_client.find_user( - parsed_args.trustor, parsed_args.trustor_domain - ).id - kwargs['trustor_id'] = trustor_id + if parsed_args.trustor_domain: + trustor_domain_id = identity_client.find_domain( + parsed_args.trustor_domain, ignore_missing=False + ).id + trustor_id = identity_client.find_user( + parsed_args.trustor, + ignore_missing=False, + domain_id=trustor_domain_id, + ).id + else: + trustor_id = identity_client.find_user( + parsed_args.trustor, ignore_missing=False + ).id + kwargs['trustor_user_id'] = trustor_id except sdk_exceptions.ForbiddenException: - kwargs['trustor_id'] = parsed_args.trustor + kwargs['trustor_user_id'] = parsed_args.trustor try: - trustee_id = identity_client.find_user( - parsed_args.trustee, parsed_args.trustee_domain - ).id - kwargs['trustee_id'] = trustee_id + if parsed_args.trustee_domain: + trustee_domain_id = identity_client.find_domain( + parsed_args.trustee_domain, ignore_missing=False + ).id + trustee_id = identity_client.find_user( + parsed_args.trustee, + ignore_missing=False, + domain_id=trustee_domain_id, + ).id + else: + trustee_id = identity_client.find_user( + parsed_args.trustee, ignore_missing=False + ).id + kwargs['trustee_user_id'] = trustee_id except sdk_exceptions.ForbiddenException: - kwargs['trustee_id'] = parsed_args.trustee + kwargs['trustee_user_id'] = parsed_args.trustee try: - project_id = identity_client.find_project( - parsed_args.project, parsed_args.project_domain - ).id + if parsed_args.project_domain: + project_domain_id = identity_client.find_domain( + parsed_args.project_domain, ignore_missing=False + ).id + project_id = identity_client.find_project( + parsed_args.project, + ignore_missing=False, + domain_id=project_domain_id, + ).id + else: + project_id = identity_client.find_project( + parsed_args.project, ignore_missing=False + ).id kwargs['project_id'] = project_id except sdk_exceptions.ForbiddenException: kwargs['project_id'] = parsed_args.project - role_ids = [] + roles = [] for role in parsed_args.roles: try: role_id = identity_client.find_role(role).id except sdk_exceptions.ForbiddenException: role_id = role - role_ids.append(role_id) - kwargs['roles'] = role_ids + roles.append({"id": role_id}) + kwargs['roles'] = roles if parsed_args.expiration: expires_at = datetime.datetime.strptime( @@ -161,8 +191,7 @@ def take_action(self, parsed_args): ) kwargs['expires_at'] = expires_at - if parsed_args.is_impersonation: - kwargs['is_impersonation'] = parsed_args.is_impersonation + kwargs['impersonation'] = bool(parsed_args.is_impersonation) trust = identity_client.create_trust(**kwargs) @@ -289,9 +318,19 @@ def take_action(self, parsed_args): trustor = None if parsed_args.trustor: try: - trustor_id = identity_client.find_user( - parsed_args.trustor, parsed_args.trustor_domain - ).id + if parsed_args.trustor_domain: + trustor_domain_id = identity_client.find_domain( + parsed_args.trustor_domain, ignore_missing=False + ).id + trustor_id = identity_client.find_user( + parsed_args.trustor, + ignore_missing=False, + domain_id=trustor_domain_id, + ).id + else: + trustor_id = identity_client.find_user( + parsed_args.trustor, ignore_missing=False + ).id trustor = trustor_id except sdk_exceptions.ForbiddenException: trustor = parsed_args.trustor @@ -299,9 +338,19 @@ def take_action(self, parsed_args): trustee = None if parsed_args.trustee: try: - trustee_id = identity_client.find_user( - parsed_args.trustee, parsed_args.trustee_domain - ).id + if parsed_args.trustee_domain: + trustee_domain_id = identity_client.find_domain( + parsed_args.trustee_domain, ignore_missing=False + ).id + trustee_id = identity_client.find_user( + parsed_args.trustee, + ignore_missing=False, + domain_id=trustee_domain_id, + ).id + else: + trustee_id = identity_client.find_user( + parsed_args.trustee, ignore_missing=False + ).id trustee = trustee_id except sdk_exceptions.ForbiddenException: trustee = parsed_args.trustee diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 545b1cda7..1a15bfa48 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -443,15 +443,13 @@ def take_action(self, parsed_args): ignore_missing=False, ).id - assignments = identity_client.role_assignments_filter( - project=project - ) - # NOTE(stevemar): If a user has more than one role on a project # then they will have two entries in the returned data. Since we # are looking for any role, let's just track unique user IDs. user_ids = set() - for assignment in assignments: + for assignment in identity_client.role_assignments( + scope_project_id=project + ): if assignment.user: user_ids.add(assignment.user['id']) @@ -612,10 +610,12 @@ def take_action(self, parsed_args): if parsed_args.description: kwargs['description'] = parsed_args.description if parsed_args.project: - project_domain_id = identity_client.find_domain( - name_or_id=parsed_args.project_domain, - ignore_missing=False, - ).id + project_domain_id = None + if parsed_args.project_domain: + project_domain_id = identity_client.find_domain( + name_or_id=parsed_args.project_domain, + ignore_missing=False, + ).id project_id = identity_client.find_project( name_or_id=parsed_args.project, ignore_missing=False, diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py index e18161d4e..d05aa7697 100644 --- a/openstackclient/tests/unit/common/test_quota.py +++ b/openstackclient/tests/unit/common/test_quota.py @@ -1018,6 +1018,26 @@ def test_quota_show(self): ) self.assertNotCalled(self.network_client.get_quota_default) + def test_quota_show__missing_services(self): + self.app.client_manager.compute_endpoint_enabled = False + self.app.client_manager.volume_endpoint_enabled = False + self.app.client_manager.network_endpoint_enabled = False + + arglist = [ + self.projects[0].name, + ] + verifylist = [ + ('service', 'all'), + ('project', self.projects[0].name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.compute_sdk_client.get_quota_set.assert_not_called() + self.volume_sdk_client.get_quota_set.assert_not_called() + self.network_client.get_quota.assert_not_called() + def test_quota_show__with_compute(self): arglist = [ '--compute', diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 789daadf4..d149ec4f4 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -1227,7 +1227,6 @@ class TestServerCreate(TestServer): 'locked', 'locked_reason', 'name', - 'pinned_availability_zone', 'progress', 'project_id', 'properties', @@ -1276,7 +1275,6 @@ def datalist(self): None, # locked None, # locked_reason self.server.name, - None, # pinned_availability_zone None, # progress None, # project_id format_columns.DictColumn({}), # properties @@ -4600,7 +4598,6 @@ class _TestServerList(TestServer): 'Flavor Name', 'Flavor ID', 'Availability Zone', - 'Pinned Availability Zone', 'Host', 'Properties', ) @@ -4739,7 +4736,6 @@ def test_server_list_long_option(self): self.flavor.name, s.flavor['id'], getattr(s, 'availability_zone'), - getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), ) @@ -4785,8 +4781,6 @@ def test_server_list_column_option(self): '-c', 'Availability Zone', '-c', - 'Pinned Availability Zone', - '-c', 'Host', '-c', 'Properties', @@ -4809,7 +4803,6 @@ def test_server_list_column_option(self): self.assertIn('Image ID', columns) self.assertIn('Flavor ID', columns) self.assertIn('Availability Zone', columns) - self.assertIn('Pinned Availability Zone', columns) self.assertIn('Host', columns) self.assertIn('Properties', columns) self.assertCountEqual(columns, set(columns)) @@ -5222,7 +5215,6 @@ def test_server_list_long_with_host_status_v216(self): self.flavor.name, s.flavor['id'], getattr(s, 'availability_zone'), - getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), ) @@ -5277,7 +5269,6 @@ def test_server_list_long_with_host_status_v216(self): self.flavor.name, s.flavor['id'], getattr(s, 'availability_zone'), - getattr(s, 'pinned_availability_zone', ''), server.HostColumn(getattr(s, 'hypervisor_hostname')), format_columns.DictColumn(s.metadata), s.host_status, @@ -5314,7 +5305,6 @@ class TestServerListV273(_TestServerList): 'Image ID', 'Flavor', 'Availability Zone', - 'Pinned Availability Zone', 'Host', 'Properties', ) @@ -5513,6 +5503,152 @@ def test_server_list_v269_with_partial_constructs(self): self.assertEqual(expected_row, partial_server) +class TestServerListV296(_TestServerList): + columns = ( + 'ID', + 'Name', + 'Status', + 'Networks', + 'Image', + 'Flavor', + ) + columns_long = ( + 'ID', + 'Name', + 'Status', + 'Task State', + 'Power State', + 'Networks', + 'Image Name', + 'Image ID', + 'Flavor', + 'Availability Zone', + 'Host', + 'Properties', + 'Pinned Availability Zone', + ) + + def setUp(self): + super().setUp() + self.set_compute_api_version('2.96') + + Image = collections.namedtuple('Image', 'id name') + self.image_client.images.return_value = [ + Image(id=s.image['id'], name=self.image.name) + # Image will be an empty string if boot-from-volume + for s in self.servers + if s.image + ] + + Flavor = collections.namedtuple('Flavor', 'id name') + self.compute_sdk_client.flavors.return_value = [ + Flavor(id=s.flavor['id'], name=self.flavor.name) + for s in self.servers + ] + + self.data = tuple( + ( + s.id, + s.name, + s.status, + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + ) + for s in self.servers + ) + + def test_server_list_long_option(self): + self.data = tuple( + ( + s.id, + s.name, + s.status, + getattr(s, 'task_state'), + server.PowerStateColumn(getattr(s, 'power_state')), + server.AddressesColumn(s.addresses), + # Image will be an empty string if boot-from-volume + self.image.name if s.image else server.IMAGE_STRING_FOR_BFV, + s.image['id'] if s.image else server.IMAGE_STRING_FOR_BFV, + self.flavor.name, + getattr(s, 'availability_zone'), + server.HostColumn(getattr(s, 'hypervisor_hostname')), + format_columns.DictColumn(s.metadata), + getattr(s, 'pinned_availability_zone', ''), + ) + for s in self.servers + ) + arglist = [ + '--long', + ] + verifylist = [ + ('all_projects', False), + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + image_ids = {s.image['id'] for s in self.servers if s.image} + self.image_client.images.assert_called_once_with( + id=f'in:{",".join(image_ids)}', + ) + self.compute_sdk_client.flavors.assert_called_once_with(is_public=None) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data, tuple(data)) + + def test_server_list_column_option(self): + arglist = [ + '-c', + 'Project ID', + '-c', + 'User ID', + '-c', + 'Created At', + '-c', + 'Security Groups', + '-c', + 'Task State', + '-c', + 'Power State', + '-c', + 'Image ID', + '-c', + 'Flavor ID', + '-c', + 'Availability Zone', + '-c', + 'Host', + '-c', + 'Properties', + '-c', + 'Pinned Availability Zone', + '--long', + ] + verifylist = [ + ('long', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute_sdk_client.servers.assert_called_with(**self.kwargs) + self.assertIn('Project ID', columns) + self.assertIn('User ID', columns) + self.assertIn('Created At', columns) + self.assertIn('Security Groups', columns) + self.assertIn('Task State', columns) + self.assertIn('Power State', columns) + self.assertIn('Image ID', columns) + self.assertIn('Flavor ID', columns) + self.assertIn('Availability Zone', columns) + self.assertIn('Pinned Availability Zone', columns) + self.assertIn('Host', columns) + self.assertIn('Properties', columns) + self.assertCountEqual(columns, set(columns)) + + class TestServerAction(compute_fakes.TestComputev2): def run_method_with_sdk_servers(self, method_name, server_count): servers = compute_fakes.create_sdk_servers(count=server_count) @@ -8443,7 +8579,6 @@ def setUp(self): 'locked', 'locked_reason', 'name', - 'pinned_availability_zone', 'progress', 'project_id', 'properties', @@ -8493,7 +8628,6 @@ def setUp(self): None, # locked None, # locked_reason self.server.name, - None, # pinned_availability_zone None, # progress 'tenant-id-xxx', # project_id format_columns.DictColumn({}), # properties @@ -9444,7 +9578,6 @@ def test_prep_server_detail(self): 'locked': None, 'locked_reason': None, 'name': _server.name, - 'pinned_availability_zone': None, 'progress': None, 'project_id': 'tenant-id-xxx', 'properties': format_columns.DictColumn({}), @@ -9531,7 +9664,6 @@ def test_prep_server_detail_v247(self): 'locked': None, 'locked_reason': None, 'name': _server.name, - 'pinned_availability_zone': None, 'progress': None, 'project_id': 'tenant-id-xxx', 'properties': format_columns.DictColumn({}), diff --git a/openstackclient/tests/unit/identity/v3/test_trust.py b/openstackclient/tests/unit/identity/v3/test_trust.py index 07776fa45..5c14b7ad9 100644 --- a/openstackclient/tests/unit/identity/v3/test_trust.py +++ b/openstackclient/tests/unit/identity/v3/test_trust.py @@ -70,12 +70,15 @@ def test_trust_create_basic(self): # Set expected values kwargs = { 'project_id': self.project.id, - 'roles': [self.role.id], + 'roles': [{'id': self.role.id}], + 'impersonation': False, } # TrustManager.create(trustee_id, trustor_id, impersonation=, # project=, role_names=, expires_at=) self.identity_sdk_client.create_trust.assert_called_with( - trustor_id=self.user.id, trustee_id=self.user.id, **kwargs + trustor_user_id=self.user.id, + trustee_user_id=self.user.id, + **kwargs, ) collist = ( diff --git a/openstackclient/tests/unit/identity/v3/test_user.py b/openstackclient/tests/unit/identity/v3/test_user.py index 7f4c2497e..d0e596a33 100644 --- a/openstackclient/tests/unit/identity/v3/test_user.py +++ b/openstackclient/tests/unit/identity/v3/test_user.py @@ -840,7 +840,7 @@ def setUp(self): self.identity_sdk_client.find_domain.return_value = self.domain self.identity_sdk_client.find_group.return_value = self.group self.identity_sdk_client.find_project.return_value = self.project - self.identity_sdk_client.role_assignments_filter.return_value = [ + self.identity_sdk_client.role_assignments.return_value = [ self.role_assignment ] @@ -978,12 +978,10 @@ def test_user_list_project(self): columns, data = self.cmd.take_action(parsed_args) kwargs = { - 'project': self.project.id, + 'scope_project_id': self.project.id, } - self.identity_sdk_client.role_assignments_filter.assert_called_with( - **kwargs - ) + self.identity_sdk_client.role_assignments.assert_called_with(**kwargs) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) @@ -1206,6 +1204,17 @@ def test_user_set_project(self): self.identity_sdk_client.update_user.assert_called_with( user=self.user, **kwargs ) + self.identity_sdk_client.find_domain.assert_not_called() + + # Set expected values + kwargs = { + 'ignore_missing': False, + 'domain_id': None, + } + self.identity_sdk_client.find_project.assert_called_once_with( + name_or_id=self.project.id, **kwargs + ) + self.assertIsNone(result) def test_user_set_project_domain(self): @@ -1238,6 +1247,11 @@ def test_user_set_project_domain(self): self.identity_sdk_client.update_user.assert_called_with( user=self.user, **kwargs ) + + self.identity_sdk_client.find_domain.assert_called_once_with( + name_or_id=self.project.domain_id, ignore_missing=False + ) + self.assertIsNone(result) def test_user_set_enable(self): diff --git a/tox.ini b/tox.ini index 7b3d951d2..380f64fdc 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/2025.1} -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt commands = @@ -63,7 +63,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/2025.1} -r{toxinidir}/requirements.txt -r{toxinidir}/doc/requirements.txt commands = @@ -93,7 +93,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/2025.1} -r{toxinidir}/doc/requirements.txt commands = sphinx-build -a -E -W -d doc/build/doctrees -b html doc/source doc/build/html @@ -105,7 +105,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/2025.1} -r{toxinidir}/doc/requirements.txt commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html