From fec100d42d975c80fd5294c274dfc3f2051d87f3 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Fri, 22 Nov 2024 09:29:50 +0100 Subject: [PATCH 01/13] Bump version to 5.1.0-dev --- galaxy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/galaxy.yml b/galaxy.yml index 602c15f6..3734a74c 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -109,7 +109,7 @@ authors: - "russianguppie <46544650+russianguppie@users.noreply.github.com>" - "willtome " - "yuqo2450 <79540477+yuqo2450@users.noreply.github.com>" -version: "5.0.0" +version: "5.1.0-dev" license: - "GPL-3.0-or-later" tags: From c0f9dc364aafbe88b80a5bc5a1342acc396f7ac1 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Mon, 25 Nov 2024 09:39:55 +0100 Subject: [PATCH 02/13] configure tests to run with Python 3.6+ only this avoids sanity on Galaxy complain that it can't import the code on Python 2 (which we don't support anymore) Refs: #1799 --- tests/config.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/config.yml diff --git a/tests/config.yml b/tests/config.yml new file mode 100644 index 00000000..41f52926 --- /dev/null +++ b/tests/config.yml @@ -0,0 +1,3 @@ +--- +modules: + python_requires: ">=3.6" From 5dda6d0581f138fbfe9852cc329102b3e1223d71 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Mon, 25 Nov 2024 09:26:25 +0100 Subject: [PATCH 03/13] add role metadata to our roles Refs: #1799 --- roles/activation_keys/meta/main.yml | 4 ++++ roles/auth_sources_ldap/meta/main.yml | 4 ++++ roles/compute_profiles/meta/main.yml | 4 ++++ roles/compute_resources/meta/main.yml | 4 ++++ roles/content_credentials/meta/main.yml | 4 ++++ roles/content_rhel/meta/main.yml | 4 ++++ roles/content_view_publish/meta/main.yml | 4 ++++ roles/content_view_version_cleanup/meta/main.yml | 4 ++++ roles/content_views/meta/main.yml | 4 ++++ roles/convert2rhel/meta/main.yml | 4 ++++ roles/domains/meta/main.yml | 4 ++++ roles/hostgroups/meta/main.yml | 4 ++++ roles/lifecycle_environments/meta/main.yml | 4 ++++ roles/locations/meta/main.yml | 4 ++++ roles/manifest/meta/main.yml | 4 ++++ roles/operatingsystems/meta/main.yml | 4 ++++ roles/organizations/meta/main.yml | 4 ++++ roles/provisioning_templates/meta/main.yml | 4 ++++ roles/repositories/meta/main.yml | 4 ++++ roles/settings/meta/main.yml | 4 ++++ roles/subnets/meta/main.yml | 4 ++++ roles/sync_plans/meta/main.yml | 4 ++++ 22 files changed, 88 insertions(+) create mode 100644 roles/activation_keys/meta/main.yml create mode 100644 roles/auth_sources_ldap/meta/main.yml create mode 100644 roles/compute_profiles/meta/main.yml create mode 100644 roles/compute_resources/meta/main.yml create mode 100644 roles/content_credentials/meta/main.yml create mode 100644 roles/content_rhel/meta/main.yml create mode 100644 roles/content_view_publish/meta/main.yml create mode 100644 roles/content_view_version_cleanup/meta/main.yml create mode 100644 roles/content_views/meta/main.yml create mode 100644 roles/convert2rhel/meta/main.yml create mode 100644 roles/domains/meta/main.yml create mode 100644 roles/hostgroups/meta/main.yml create mode 100644 roles/lifecycle_environments/meta/main.yml create mode 100644 roles/locations/meta/main.yml create mode 100644 roles/manifest/meta/main.yml create mode 100644 roles/operatingsystems/meta/main.yml create mode 100644 roles/organizations/meta/main.yml create mode 100644 roles/provisioning_templates/meta/main.yml create mode 100644 roles/repositories/meta/main.yml create mode 100644 roles/settings/meta/main.yml create mode 100644 roles/subnets/meta/main.yml create mode 100644 roles/sync_plans/meta/main.yml diff --git a/roles/activation_keys/meta/main.yml b/roles/activation_keys/meta/main.yml new file mode 100644 index 00000000..4b606776 --- /dev/null +++ b/roles/activation_keys/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Activation Keys + standalone: false diff --git a/roles/auth_sources_ldap/meta/main.yml b/roles/auth_sources_ldap/meta/main.yml new file mode 100644 index 00000000..dd05c42d --- /dev/null +++ b/roles/auth_sources_ldap/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage LDAP authentication sources + standalone: false diff --git a/roles/compute_profiles/meta/main.yml b/roles/compute_profiles/meta/main.yml new file mode 100644 index 00000000..e03886ec --- /dev/null +++ b/roles/compute_profiles/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Compute Profiles + standalone: false diff --git a/roles/compute_resources/meta/main.yml b/roles/compute_resources/meta/main.yml new file mode 100644 index 00000000..274db5f7 --- /dev/null +++ b/roles/compute_resources/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Compute Resources + standalone: false diff --git a/roles/content_credentials/meta/main.yml b/roles/content_credentials/meta/main.yml new file mode 100644 index 00000000..4d654d58 --- /dev/null +++ b/roles/content_credentials/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Content Credentials + standalone: false diff --git a/roles/content_rhel/meta/main.yml b/roles/content_rhel/meta/main.yml new file mode 100644 index 00000000..af0ac536 --- /dev/null +++ b/roles/content_rhel/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Configuration for everything needed to register and patch existing RHEL clients + standalone: false diff --git a/roles/content_view_publish/meta/main.yml b/roles/content_view_publish/meta/main.yml new file mode 100644 index 00000000..84d4718b --- /dev/null +++ b/roles/content_view_publish/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Publish a list of Content Views + standalone: false diff --git a/roles/content_view_version_cleanup/meta/main.yml b/roles/content_view_version_cleanup/meta/main.yml new file mode 100644 index 00000000..cfb8ae88 --- /dev/null +++ b/roles/content_view_version_cleanup/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Clean up unused Content View Versions + standalone: false diff --git a/roles/content_views/meta/main.yml b/roles/content_views/meta/main.yml new file mode 100644 index 00000000..54fee639 --- /dev/null +++ b/roles/content_views/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Content Views + standalone: false diff --git a/roles/convert2rhel/meta/main.yml b/roles/convert2rhel/meta/main.yml new file mode 100644 index 00000000..9d428cd2 --- /dev/null +++ b/roles/convert2rhel/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Configure everything needed to register and convert CentOS clients to Red Hat Enterprise Linux + standalone: false diff --git a/roles/domains/meta/main.yml b/roles/domains/meta/main.yml new file mode 100644 index 00000000..cad3ead1 --- /dev/null +++ b/roles/domains/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Domains + standalone: false diff --git a/roles/hostgroups/meta/main.yml b/roles/hostgroups/meta/main.yml new file mode 100644 index 00000000..3727a9ad --- /dev/null +++ b/roles/hostgroups/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Hostgroups + standalone: false diff --git a/roles/lifecycle_environments/meta/main.yml b/roles/lifecycle_environments/meta/main.yml new file mode 100644 index 00000000..efd21e58 --- /dev/null +++ b/roles/lifecycle_environments/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Lifecycle Environments + standalone: false diff --git a/roles/locations/meta/main.yml b/roles/locations/meta/main.yml new file mode 100644 index 00000000..160192a1 --- /dev/null +++ b/roles/locations/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Locations + standalone: false diff --git a/roles/manifest/meta/main.yml b/roles/manifest/meta/main.yml new file mode 100644 index 00000000..3e4ad23b --- /dev/null +++ b/roles/manifest/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Fetch a Manifest from the Red Hat Portal and upload it to Foreman + standalone: false diff --git a/roles/operatingsystems/meta/main.yml b/roles/operatingsystems/meta/main.yml new file mode 100644 index 00000000..43dbd41f --- /dev/null +++ b/roles/operatingsystems/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Operating Systems + standalone: false diff --git a/roles/organizations/meta/main.yml b/roles/organizations/meta/main.yml new file mode 100644 index 00000000..17b1aa64 --- /dev/null +++ b/roles/organizations/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Organizations + standalone: false diff --git a/roles/provisioning_templates/meta/main.yml b/roles/provisioning_templates/meta/main.yml new file mode 100644 index 00000000..2342bf25 --- /dev/null +++ b/roles/provisioning_templates/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Provisioning Templates + standalone: false diff --git a/roles/repositories/meta/main.yml b/roles/repositories/meta/main.yml new file mode 100644 index 00000000..c05f3d2a --- /dev/null +++ b/roles/repositories/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Define Products and Custom Repositories and enables Red Hat Repositories + standalone: false diff --git a/roles/settings/meta/main.yml b/roles/settings/meta/main.yml new file mode 100644 index 00000000..51429f83 --- /dev/null +++ b/roles/settings/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Manage Settings + standalone: false diff --git a/roles/subnets/meta/main.yml b/roles/subnets/meta/main.yml new file mode 100644 index 00000000..81a65fb8 --- /dev/null +++ b/roles/subnets/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Subnets + standalone: false diff --git a/roles/sync_plans/meta/main.yml b/roles/sync_plans/meta/main.yml new file mode 100644 index 00000000..eebb27f4 --- /dev/null +++ b/roles/sync_plans/meta/main.yml @@ -0,0 +1,4 @@ +--- +galaxy_info: + description: Create and manage Sync Plans + standalone: false From 06c899350a006c35ad598039cbd4d9f1efdbc6c1 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Mon, 25 Nov 2024 09:49:08 +0100 Subject: [PATCH 04/13] use the official Ansible sanity action --- .github/workflows/main.yml | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d785a2a5..eb61b502 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,6 +12,30 @@ concurrency: cancel-in-progress: true jobs: + sanity: + name: Sanity (Ⓐ${{ matrix.ansible }}) + strategy: + matrix: + ansible: + - stable-2.15 + - stable-2.16 + - stable-2.17 + - stable-2.18 + - devel + + runs-on: ubuntu-latest + + steps: + # Run sanity tests inside a Docker container. + # The docker container has all the pinned dependencies that are + # required and all Python versions Ansible supports. + - name: Perform sanity testing + uses: ansible-community/ansible-test-gh-action@release/v1 + with: + ansible-core-version: ${{ matrix.ansible }} + testing-type: sanity + pull-request-change-detection: false + build: runs-on: ubuntu-20.04 container: ${{ matrix.container }} @@ -86,9 +110,6 @@ jobs: run: make test-other - name: Run dist tests run: make dist-test - - name: Run sanity tests - run: make SANITY_OPTS="--docker" sanity - if: matrix.ansible != 'v2.10.4' && matrix.ansible != 'stable-2.11' checkmode: runs-on: ubuntu-latest From be761b300ef8b5a42308eb34f576e8b229f70050 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Mon, 25 Nov 2024 09:54:25 +0100 Subject: [PATCH 05/13] rename .git-keep to .gitkeep to make lint pass --- changelogs/fragments/{.git-keep => .gitkeep} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelogs/fragments/{.git-keep => .gitkeep} (100%) diff --git a/changelogs/fragments/.git-keep b/changelogs/fragments/.gitkeep similarity index 100% rename from changelogs/fragments/.git-keep rename to changelogs/fragments/.gitkeep From a93420ab7fd4a9457efd07edc6bf6229de2a06a4 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Mon, 25 Nov 2024 10:14:21 +0100 Subject: [PATCH 06/13] don't check fixtures --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index eb61b502..f54f1a0f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,6 +35,7 @@ jobs: ansible-core-version: ${{ matrix.ansible }} testing-type: sanity pull-request-change-detection: false + pre-test-cmd: rm -rf tests/fixtures tests/test_playbooks/fixtures build: runs-on: ubuntu-20.04 From ba78995f90ae9ee378c6358849c957688e7f88d7 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Mon, 25 Nov 2024 09:59:59 +0100 Subject: [PATCH 07/13] make code pep8/pylint clean --- generate_action_groups.py | 2 +- tests/vcr_python_wrapper.py | 5 +++-- vendor.py | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/generate_action_groups.py b/generate_action_groups.py index 34fee937..f4dbd260 100644 --- a/generate_action_groups.py +++ b/generate_action_groups.py @@ -11,4 +11,4 @@ runtime['action_groups']['foreman'] = sorted(set(ALL_MODULES) - set(EXCLUDED_MODULES)) with open(META_RUNTIME, 'w') as runtime_file: - yaml.safe_dump(runtime, runtime_file, default_flow_style=False, explicit_start=True) + yaml.safe_dump(runtime, runtime_file, default_flow_style=False, explicit_start=True) diff --git a/tests/vcr_python_wrapper.py b/tests/vcr_python_wrapper.py index 76a35fda..136fc192 100755 --- a/tests/vcr_python_wrapper.py +++ b/tests/vcr_python_wrapper.py @@ -27,7 +27,8 @@ def body_json_l2_matcher(r1, r2): body1 = json.loads(r1.body.decode('utf8')) body2 = json.loads(r2.body.decode('utf8')) if 'common_parameter' in body1 and 'common_parameter' in body2: - if body1['common_parameter'].get('parameter_type') == body2['common_parameter'].get('parameter_type') in ['hash', 'json', 'yaml']: + if (body1['common_parameter'].get('parameter_type') == body2['common_parameter'].get('parameter_type') + and body1['common_parameter'].get('parameter_type') in ['hash', 'json', 'yaml']): body1['common_parameter']['value'] = json.loads(body1['common_parameter'].get('value')) body2['common_parameter']['value'] = json.loads(body2['common_parameter'].get('value')) assert body1 == body2, "{} != {}".format(body1, body2) @@ -39,7 +40,7 @@ def body_json_l2_matcher(r1, r2): body2 = sorted(r2.body.replace(b'~', b'%7E').split(b'&')) assert len(body1) == len(body2), "the body lengths don't match" for i, v in enumerate(body1): - assert body1[i] == body2[i], "body contents at position {} dont't match: '{}' vs '{}'".format(i, body1[i], body2[i]) + assert body1[i] == body2[i], "body contents at position {} dont't match: '{}' vs '{}'".format(i, body1[i], body2[i]) # pylint:disable=all else: assert r1.body == r2.body, "{} != {}".format(r1.body, r2.body) diff --git a/vendor.py b/vendor.py index e6128d8c..b0f19984 100644 --- a/vendor.py +++ b/vendor.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python3 - import fileinput import os.path @@ -53,8 +51,10 @@ elif line in ['try:', 'if TYPE_CHECKING:'] or buffer_lines: buffer_lines.append(line) if "from typing" in line: - typing_imports.update([element.strip(',') for element in line.split('#')[0].strip().split(' ')[3:] if not element.strip(',') == 'TYPE_CHECKING']) - if ('pass' in line or 'TYPE_CHECKING =' in line or ('from apypie' in line and 'if TYPE_CHECKING:' in buffer_lines)) and ('from typing' in buffer_lines[1] or 'from apypie' in buffer_lines[1]): + typing_imports.update([element.strip(',') for element in line.split('#')[0].strip().split(' ')[3:] + if not element.strip(',') == 'TYPE_CHECKING']) + if (('pass' in line or 'TYPE_CHECKING =' in line or ('from apypie' in line and 'if TYPE_CHECKING:' in buffer_lines)) and + ('from typing' in buffer_lines[1] or 'from apypie' in buffer_lines[1])): buffer_lines.clear() elif "from typing" in line: typing_imports.update([element.strip(',') for element in line.split('#')[0].strip().split(' ')[3:] if not element.strip(',') == 'TYPE_CHECKING']) From 879ec333daba264c704c121a463af854694300bf Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Mon, 28 Oct 2024 10:51:00 +0100 Subject: [PATCH 08/13] use ForemanApi from apypie --- plugins/module_utils/foreman_helper.py | 103 ++++--------------------- tests/test_recursive_dict_keys.py | 7 -- 2 files changed, 16 insertions(+), 94 deletions(-) delete mode 100644 tests/test_recursive_dict_keys.py diff --git a/plugins/module_utils/foreman_helper.py b/plugins/module_utils/foreman_helper.py index 6ba9485b..414a6d30 100644 --- a/plugins/module_utils/foreman_helper.py +++ b/plugins/module_utils/foreman_helper.py @@ -13,7 +13,6 @@ import os import operator import re -import time import traceback from contextlib import contextmanager @@ -397,7 +396,6 @@ def __init__(self, **kwargs): self.fail_json(msg="The server URL needs to be either HTTPS or HTTP!") self.task_timeout = 60 - self.task_poll = 4 self._thin_default = False self.state = 'undefined' @@ -608,12 +606,12 @@ def connect(self): that are required by the module. """ - self.foremanapi = apypie.Api( + self.foremanapi = apypie.ForemanApi( uri=self._foremanapi_server_url, username=to_bytes(self._foremanapi_username), password=to_bytes(self._foremanapi_password), - api_version=2, verify_ssl=self._foremanapi_validate_certs, + task_timeout=self.task_timeout, ) _status = self.status() @@ -651,18 +649,6 @@ def status(self): return self.foremanapi.resource('home').call('status') - def _resource(self, resource): - if resource not in self.foremanapi.resources: - raise Exception("The server doesn't know about {0}, is the right plugin installed?".format(resource)) - return self.foremanapi.resource(resource) - - def _resource_call(self, resource, *args, **kwargs): - return self._resource(resource).call(*args, **kwargs) - - def _resource_prepare_params(self, resource, action, params): - api_action = self._resource(resource).action(action) - return api_action.prepare_params(params) - @_exception2fail_json(msg='Failed to show resource: {0}') def show_resource(self, resource, resource_id, params=None): """ @@ -676,16 +662,7 @@ def show_resource(self, resource, resource_id, params=None): :type params: Union[dict,None], optional """ - if params is None: - params = {} - else: - params = params.copy() - - params['id'] = resource_id - - params = self._resource_prepare_params(resource, 'show', params) - - return self._resource_call(resource, 'show', params) + return self.foremanapi.show(resource, resource_id, params) @_exception2fail_json(msg='Failed to list resource: {0}') def list_resource(self, resource, search=None, params=None): @@ -700,18 +677,7 @@ def list_resource(self, resource, search=None, params=None): :type params: Union[dict,None], optional """ - if params is None: - params = {} - else: - params = params.copy() - - if search is not None: - params['search'] = search - params['per_page'] = PER_PAGE - - params = self._resource_prepare_params(resource, 'index', params) - - return self._resource_call(resource, 'index', params)['results'] + return self.foremanapi.list(resource, search, params) def find_resource(self, resource, search, params=None, failsafe=False, thin=None): list_params = {} @@ -1024,9 +990,7 @@ def _validate_supported_payload(self, resource, action, payload): :return: The payload as it can be submitted to the API :rtype: dict """ - filtered_payload = self._resource_prepare_params(resource, action, payload) - # On Python 2 dict.keys() is just a list, but we need a set here. - unsupported_parameters = set(payload.keys()) - set(_recursive_dict_keys(filtered_payload)) + filtered_payload, unsupported_parameters = self.foremanapi.validate_payload(resource, action, payload) if unsupported_parameters: warn_msg = "The following parameters are not supported by your server when performing {0} on {1}: {2}. They were ignored." self.warn(warn_msg.format(action, resource, unsupported_parameters)) @@ -1050,14 +1014,12 @@ def _create_entity(self, resource, desired_entity, params, foreman_spec): """ payload = _flatten_entity(desired_entity, foreman_spec) self._validate_supported_payload(resource, 'create', payload) + self.set_changed() if not self.check_mode: - if params: - payload.update(params) - return self.resource_action(resource, 'create', payload) + return self.foremanapi.create(resource, payload, params) else: fake_entity = desired_entity.copy() fake_entity['id'] = -1 - self.set_changed() return fake_entity def _update_entity(self, resource, desired_entity, current_entity, params, foreman_spec): @@ -1111,16 +1073,14 @@ def _update_entity(self, resource, desired_entity, current_entity, params, forem if new_value != old_value: payload[key] = value if self._validate_supported_payload(resource, 'update', payload): + self.set_changed() payload['id'] = current_flat_entity['id'] if not self.check_mode: - if params: - payload.update(params) - return self.resource_action(resource, 'update', payload) + return self.foremanapi.update(resource, payload, params) else: # In check_mode we emulate the server updating the entity fake_entity = current_flat_entity.copy() fake_entity.update(payload) - self.set_changed() return fake_entity else: # Nothing needs changing @@ -1183,29 +1143,18 @@ def _delete_entity(self, resource, current_entity, params): :return: The new current state of the entity :rtype: Union[dict,None] """ - payload = {'id': current_entity['id']} - if params: - payload.update(params) - entity = self.resource_action(resource, 'destroy', payload) - - # this is a workaround for https://projects.theforeman.org/issues/26937 - if entity and isinstance(entity, dict) and 'error' in entity and 'message' in entity['error']: - self.fail_json(msg=entity['error']['message']) - - return None + self.set_changed() + if not self.check_mode: + return self.foremanapi.delete(resource, current_entity, params) + else: + return None def resource_action(self, resource, action, params, options=None, data=None, files=None, ignore_check_mode=False, record_change=True, ignore_task_errors=False): - resource_payload = self._resource_prepare_params(resource, action, params) - if options is None: - options = {} try: result = None if ignore_check_mode or not self.check_mode: - result = self._resource_call(resource, action, resource_payload, options=options, data=data, files=files) - is_foreman_task = isinstance(result, dict) and 'action' in result and 'state' in result and 'started_at' in result - if is_foreman_task: - result = self.wait_for_task(result, ignore_errors=ignore_task_errors) + result = self.foremanapi.resource_action(resource, action, params, options, data, files, ignore_task_errors) except Exception as e: msg = 'Error while performing {0} on {1}: {2}'.format( action, resource, to_native(e)) @@ -1216,18 +1165,7 @@ def resource_action(self, resource, action, params, options=None, data=None, fil return result def wait_for_task(self, task, ignore_errors=False): - duration = self.task_timeout - while task['state'] not in ['paused', 'stopped']: - duration -= self.task_poll - if duration <= 0: - self.fail_json(msg="Timeout waiting for Task {0}".format(task['id'])) - time.sleep(self.task_poll) - - resource_payload = self._resource_prepare_params('foreman_tasks', 'show', {'id': task['id']}) - task = self._resource_call('foreman_tasks', 'show', resource_payload) - if not ignore_errors and task['result'] != 'success': - self.fail_json(msg='Task {0}({1}) did not succeed. Task information: {2}'.format(task['action'], task['id'], task['humanized']['errors'])) - return task + return self.foremanapi.wait_for_task(task, ignore_errors) def fail_from_exception(self, exc, msg): fail = {'msg': msg} @@ -1759,15 +1697,6 @@ def _flatten_entity(entity, foreman_spec): return result -def _recursive_dict_keys(a_dict): - """Find all keys of a nested dictionary""" - keys = set(a_dict.keys()) - for _k, v in a_dict.items(): - if isinstance(v, dict): - keys.update(_recursive_dict_keys(v)) - return keys - - def _recursive_dict_without_none(a_dict, exclude=None): """ Remove all entries with `None` value from a dict, recursively. diff --git a/tests/test_recursive_dict_keys.py b/tests/test_recursive_dict_keys.py deleted file mode 100644 index 0a34667b..00000000 --- a/tests/test_recursive_dict_keys.py +++ /dev/null @@ -1,7 +0,0 @@ -from plugins.module_utils.foreman_helper import _recursive_dict_keys - - -def test_recursive_dict_keys(): - a_dict = {'level1': 'has value', 'level2': {'real_level2': 'more value', 'level3': {'real_level3': 'nope'}}} - expected_keys = set(['level1', 'level2', 'level3', 'real_level2', 'real_level3']) - assert _recursive_dict_keys(a_dict) == expected_keys From a1ff8b14c6bd23fc6f7a974c31bfed74a0bdca61 Mon Sep 17 00:00:00 2001 From: adamlazik1 Date: Mon, 11 Nov 2024 21:53:51 +0000 Subject: [PATCH 09/13] Add proxy options to templates_import module relates to https://github.com/theforeman/foreman_templates/pull/191. --- plugins/modules/templates_import.py | 20 +++++++++++++++++++ .../test_playbooks/tasks/templates_import.yml | 2 ++ 2 files changed, 22 insertions(+) diff --git a/plugins/modules/templates_import.py b/plugins/modules/templates_import.py index 3d1141e1..31debc84 100644 --- a/plugins/modules/templates_import.py +++ b/plugins/modules/templates_import.py @@ -90,6 +90,23 @@ - The directory within Git repo containing the templates. required: false type: str + http_proxy_policy: + description: + - HTTP proxy policy for template sync. + - You can choose no HTTP proxy, global HTTP proxy, or a custom HTTP proxy (C(selected)) + - If you choose 'selected', provide the C(http_proxy) parameter. + required: false + type: str + choices: + - none + - global + - selected + http_proxy: + description: + - HTTP proxy to use for template sync. + - Use this parameter together with C(http_proxy_policy=selected) + required: false + type: str attributes: check_mode: support: none @@ -156,9 +173,12 @@ def main(): force=dict(type='bool'), lock=dict(type='bool'), negate=dict(type='bool'), + http_proxy_policy=dict(choices=['global', 'none', 'selected']), + http_proxy=dict(type='entity'), ), supports_check_mode=False, required_plugins=[('templates', ['*'])], + required_if=[('http_proxy_policy', 'selected', ['http_proxy'])], ) with module.api_connection(): diff --git a/tests/test_playbooks/tasks/templates_import.yml b/tests/test_playbooks/tasks/templates_import.yml index a930356b..83678e82 100644 --- a/tests/test_playbooks/tasks/templates_import.yml +++ b/tests/test_playbooks/tasks/templates_import.yml @@ -15,6 +15,8 @@ force: "{{ templates_import_force | default(omit) }}" lock: "{{ templates_import_lock | default(omit) }}" negate: "{{ templates_import_negate | default(omit) }}" + http_proxy_policy: "{{ templates_import_http_proxy_policy | default(omit) }}" + http_proxy: "{{ templates_import_http_proxy | default(omit) }}" locations: "{{ templates_import_locations | default(omit) }}" organizations: "{{ templates_import_organizations | default(omit) }}" register: result From dd8c903c3dad5895d08a1e64b18459187f3b1259 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Tue, 3 Dec 2024 13:29:44 +0100 Subject: [PATCH 10/13] allow setting an alternative password for auth sources this is required for recording/live in setups where the password is not "changeme" --- tests/test_playbooks/auth_source_ldap.yml | 3 ++- tests/test_playbooks/vars/server.yml.example | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_playbooks/auth_source_ldap.yml b/tests/test_playbooks/auth_source_ldap.yml index 45c59e43..9fdb3eed 100644 --- a/tests/test_playbooks/auth_source_ldap.yml +++ b/tests/test_playbooks/auth_source_ldap.yml @@ -31,7 +31,8 @@ - include_tasks: tasks/auth_source_ldap.yml vars: auth_source_ldap_state: present - auth_source_ldap_account_password: changeme + # we only set this once at creation, as otherwise idempotency tests would fail + auth_source_ldap_account_password: "{{ default_auth_source_ldap_account_password }}" expected_change: true - include_tasks: tasks/auth_source_ldap.yml vars: diff --git a/tests/test_playbooks/vars/server.yml.example b/tests/test_playbooks/vars/server.yml.example index 80817f38..cc36b49f 100644 --- a/tests/test_playbooks/vars/server.yml.example +++ b/tests/test_playbooks/vars/server.yml.example @@ -35,4 +35,5 @@ auth_source_ldap_base_dn: dc=example,dc=com auth_source_ldap_attr_login: uid auth_source_ldap_groups_base: cn=groups,cn=accounts,dc=example,dc=com external_usergroup_name: "admins" +default_auth_source_ldap_account_password: changeme ... From 1235433518d58f814cbb5b093efe3203d71ca4d3 Mon Sep 17 00:00:00 2001 From: Ewoud Kohl van Wijngaarden Date: Tue, 3 Dec 2024 16:51:13 +0100 Subject: [PATCH 11/13] Fix typo in user's default_organization parameter description Fixes: 9ccd95ceb47a ("Create foreman_user module (#296)") --- plugins/modules/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/user.py b/plugins/modules/user.py index 53e6228f..d6848a2a 100644 --- a/plugins/modules/user.py +++ b/plugins/modules/user.py @@ -75,7 +75,7 @@ type: str default_organization: description: - - The organizxation that the user uses by default + - The organization that the user uses by default required: false type: str auth_source: From 90bd31142c3f5960cb99fc7faf24b50545303b6b Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Thu, 5 Dec 2024 11:34:38 +0100 Subject: [PATCH 12/13] drop fallback to Host API when Reports API fails The results the Host API returns are different from the Reports API, so when the user asks for Reports API (via `use_reports_api`), they should get exactly that, as otherwise their Playbooks/Roles might miss-behave given different data is fed into them. --- changelogs/fragments/drop-host-api-fallback.yml | 2 ++ plugins/inventory/foreman.py | 11 ++++------- 2 files changed, 6 insertions(+), 7 deletions(-) create mode 100644 changelogs/fragments/drop-host-api-fallback.yml diff --git a/changelogs/fragments/drop-host-api-fallback.yml b/changelogs/fragments/drop-host-api-fallback.yml new file mode 100644 index 00000000..1624517c --- /dev/null +++ b/changelogs/fragments/drop-host-api-fallback.yml @@ -0,0 +1,2 @@ +bugfixes: + - inventory - Drop fallback to Host API when Reports API fails, as this leads to possibly wrong data being used diff --git a/plugins/inventory/foreman.py b/plugins/inventory/foreman.py index 076bf217..45919614 100644 --- a/plugins/inventory/foreman.py +++ b/plugins/inventory/foreman.py @@ -474,13 +474,10 @@ def _get_hostname(self, properties, hostnames, strict=False): def _populate_report_api(self): self.groups = dict() self.hosts = dict() - try: - # We need a deep copy of the data, as we modify it below and this would also modify the cache - host_data = copy.deepcopy(self._post_request()) - except Exception as exc: - self.display.warning("Failed to use Reports API, falling back to Hosts API: {0}".format(exc)) - self._populate_host_api() - return + + # We need a deep copy of the data, as we modify it below and this would also modify the cache + host_data = copy.deepcopy(self._post_request()) + self.group_prefix = self.get_option('group_prefix') hostnames = self.get_option('hostnames') From 4b7186f226711cb95a1054a26ddeca92720b7532 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Fri, 6 Dec 2024 15:23:44 +0100 Subject: [PATCH 13/13] Release 5.1.0 --- CHANGELOG.rst | 13 +++++++++++++ changelogs/changelog.yaml | 11 +++++++++++ changelogs/fragments/drop-host-api-fallback.yml | 2 -- galaxy.yml | 3 ++- 4 files changed, 26 insertions(+), 3 deletions(-) delete mode 100644 changelogs/fragments/drop-host-api-fallback.yml diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 66f8f8a9..0f58bded 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,19 @@ theforeman.foreman Release Notes This changelog describes changes after version 0.8.1. +v5.1.0 +====== + +Minor Changes +------------- + +- templates_import - Support configuring HTTP Proxy behaviour for template import + +Bugfixes +-------- + +- inventory - Drop fallback to Host API when Reports API fails, as this leads to possibly wrong data being used + v5.0.0 ====== diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 630d40cd..ae6ce82c 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -928,3 +928,14 @@ releases: - drop-ansible-29.yaml - drop-python-pre-36.yaml release_date: '2024-11-22' + 5.1.0: + changes: + bugfixes: + - inventory - Drop fallback to Host API when Reports API fails, as this leads + to possibly wrong data being used + minor_changes: + - templates_import - Support configuring HTTP Proxy behaviour for template + import + fragments: + - drop-host-api-fallback.yml + release_date: '2024-12-06' diff --git a/changelogs/fragments/drop-host-api-fallback.yml b/changelogs/fragments/drop-host-api-fallback.yml deleted file mode 100644 index 1624517c..00000000 --- a/changelogs/fragments/drop-host-api-fallback.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - inventory - Drop fallback to Host API when Reports API fails, as this leads to possibly wrong data being used diff --git a/galaxy.yml b/galaxy.yml index 3734a74c..bfc4ae77 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -94,6 +94,7 @@ authors: - "Yifat Makias " - "ab " - "achevalet " + - "adamlazik1 " - "alesc " - "bob <57952350+TheRedGreek@users.noreply.github.com>" - "calvingsmith <4283930+calvingsmith@users.noreply.github.com>" @@ -109,7 +110,7 @@ authors: - "russianguppie <46544650+russianguppie@users.noreply.github.com>" - "willtome " - "yuqo2450 <79540477+yuqo2450@users.noreply.github.com>" -version: "5.1.0-dev" +version: "5.1.0" license: - "GPL-3.0-or-later" tags: