diff --git a/meta/runtime.yml b/meta/runtime.yml index 4bbbe84d35..998ad61ee4 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -5,6 +5,7 @@ action_groups: - architecture - auth_source_ldap - bookmark + - build_pxe_default - compute_attribute - compute_profile - compute_resource diff --git a/plugins/modules/build_pxe_default.py b/plugins/modules/build_pxe_default.py new file mode 100644 index 0000000000..859a136129 --- /dev/null +++ b/plugins/modules/build_pxe_default.py @@ -0,0 +1,81 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: build_pxe_default +version_added: 5.5.0 +short_description: Build PXE default configuration +description: + - Update the default PXE menu on all configured TFTP servers +author: + - "Dirk Goetz (@dgoetz)" +options: + organization: + description: + - Name of related organization + required: false + type: str + location: + description: + - Name of related location + required: false + type: str +extends_documentation_fragment: + - theforeman.foreman.foreman +... +''' + +EXAMPLES = ''' +- name: "Build PXE default configuration" + theforeman.foreman.build_pxe_default: + username: "admin" + password: "changeme" + server_url: "https://foreman.example.com" + organization: "Default Organization" + location: "Default Location" +''' + +RETURN = ''' # ''' + +from ansible_collections.theforeman.foreman.plugins.module_utils.foreman_helper import ForemanAnsibleModule + + +def main(): + module = ForemanAnsibleModule( + foreman_spec=dict( + organization=dict(type='entity'), + location=dict(type='entity'), + ), + ) + + with module.api_connection(): + organization = module.lookup_entity('organization') + location = module.lookup_entity('location') + params = {} + if organization is not None: + params.update({'organization_id': organization['id']}) + if location is not None: + params.update({'location_id': location['id']}) + module.resource_action('provisioning_templates', 'build_pxe_default', params=params) + + +if __name__ == '__main__': + main() diff --git a/tests/fixtures/apidoc/build_pxe_default.json b/tests/fixtures/apidoc/build_pxe_default.json new file mode 120000 index 0000000000..f9e401512c --- /dev/null +++ b/tests/fixtures/apidoc/build_pxe_default.json @@ -0,0 +1 @@ +foreman.json \ No newline at end of file diff --git a/tests/test_playbooks/build_pxe_default.yml b/tests/test_playbooks/build_pxe_default.yml new file mode 100644 index 0000000000..c263e46e49 --- /dev/null +++ b/tests/test_playbooks/build_pxe_default.yml @@ -0,0 +1,39 @@ +--- +- hosts: localhost + collections: + - theforeman.foreman + gather_facts: false + vars_files: + - vars/server.yml + tasks: + - include_tasks: tasks/organization.yml + vars: + organization_state: present + - include_tasks: tasks/location.yml + vars: + location_state: present + +- hosts: tests + collections: + - theforeman.foreman + gather_facts: false + vars_files: + - vars/server.yml + tasks: + - name: Build PXE default + include_tasks: tasks/build_pxe_default.yml + +- hosts: localhost + collections: + - theforeman.foreman + gather_facts: false + vars_files: + - vars/server.yml + tasks: + - include_tasks: tasks/location.yml + vars: + location_state: absent + - include_tasks: tasks/organization.yml + vars: + organization_state: absent +... diff --git a/tests/test_playbooks/fixtures/build_pxe_default-0.yml b/tests/test_playbooks/fixtures/build_pxe_default-0.yml new file mode 100644 index 0000000000..0233dec37a --- /dev/null +++ b/tests/test_playbooks/fixtures/build_pxe_default-0.yml @@ -0,0 +1,240 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json;version=2 + Accept-Encoding: + - gzip, deflate, br + Connection: + - keep-alive + User-Agent: + - apypie (https://github.com/Apipie/apypie) + method: GET + uri: https://foreman.example.org/api/status + response: + body: + string: '{"result":"ok","status":200,"version":"3.15.0","api_version":2}' + headers: + Cache-Control: + - max-age=0, private, must-revalidate + Connection: + - Keep-Alive + Content-Length: + - '63' + Content-Type: + - application/json; charset=utf-8 + Foreman_api_version: + - '2' + Foreman_current_location: + - ; ANY + Foreman_current_organization: + - ; ANY + Foreman_version: + - 3.15.0 + Keep-Alive: + - timeout=15, max=100 + Referrer-Policy: + - strict-origin-when-cross-origin + content-security-policy: + - 'default-src ''self''; child-src ''self''; connect-src ''self'' ws: wss:; + img-src ''self'' data:; script-src ''unsafe-eval'' ''unsafe-inline'' ''self''; + style-src ''unsafe-inline'' ''self''' + strict-transport-security: + - max-age=631139040; includeSubdomains + x-content-type-options: + - nosniff + x-download-options: + - noopen + x-frame-options: + - sameorigin + x-permitted-cross-domain-policies: + - none + x-xss-protection: + - '0' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;version=2 + Accept-Encoding: + - gzip, deflate, br + Connection: + - keep-alive + User-Agent: + - apypie (https://github.com/Apipie/apypie) + method: GET + uri: https://foreman.example.org/api/organizations?search=name%3D%22Test+Organization%22&per_page=4294967296 + response: + body: + string: "{\n \"total\": 2,\n \"subtotal\": 1,\n \"page\": 1,\n \"per_page\": + 4294967296,\n \"search\": \"name=\\\"Test Organization\\\"\",\n \"sort\": + {\n \"by\": null,\n \"order\": null\n },\n \"results\": [{\"ancestry\":null,\"parent_id\":null,\"parent_name\":null,\"created_at\":\"2025-07-08 + 13:30:33 UTC\",\"updated_at\":\"2025-07-08 13:30:33 UTC\",\"id\":3,\"name\":\"Test + Organization\",\"title\":\"Test Organization\",\"description\":\"A test organization\"}]\n}\n" + headers: + Cache-Control: + - max-age=0, private, must-revalidate + Connection: + - Keep-Alive + Content-Length: + - '412' + Content-Type: + - application/json; charset=utf-8 + Foreman_api_version: + - '2' + Foreman_current_location: + - ; ANY + Foreman_current_organization: + - ; ANY + Foreman_version: + - 3.15.0 + Keep-Alive: + - timeout=15, max=99 + Referrer-Policy: + - strict-origin-when-cross-origin + content-security-policy: + - 'default-src ''self''; child-src ''self''; connect-src ''self'' ws: wss:; + img-src ''self'' data:; script-src ''unsafe-eval'' ''unsafe-inline'' ''self''; + style-src ''unsafe-inline'' ''self''' + strict-transport-security: + - max-age=631139040; includeSubdomains + x-content-type-options: + - nosniff + x-download-options: + - noopen + x-frame-options: + - sameorigin + x-permitted-cross-domain-policies: + - none + x-xss-protection: + - '0' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;version=2 + Accept-Encoding: + - gzip, deflate, br + Connection: + - keep-alive + User-Agent: + - apypie (https://github.com/Apipie/apypie) + method: GET + uri: https://foreman.example.org/api/locations?search=title%3D%22Test+Location%22&per_page=4294967296 + response: + body: + string: "{\n \"total\": 2,\n \"subtotal\": 1,\n \"page\": 1,\n \"per_page\": + 4294967296,\n \"search\": \"title=\\\"Test Location\\\"\",\n \"sort\": {\n + \ \"by\": null,\n \"order\": null\n },\n \"results\": [{\"ancestry\":null,\"parent_id\":null,\"parent_name\":null,\"created_at\":\"2025-07-08 + 13:30:34 UTC\",\"updated_at\":\"2025-07-08 13:30:34 UTC\",\"id\":4,\"name\":\"Test + Location\",\"title\":\"Test Location\",\"description\":null}]\n}\n" + headers: + Cache-Control: + - max-age=0, private, must-revalidate + Connection: + - Keep-Alive + Content-Length: + - '384' + Content-Type: + - application/json; charset=utf-8 + Foreman_api_version: + - '2' + Foreman_current_location: + - ; ANY + Foreman_current_organization: + - ; ANY + Foreman_version: + - 3.15.0 + Keep-Alive: + - timeout=15, max=98 + Referrer-Policy: + - strict-origin-when-cross-origin + content-security-policy: + - 'default-src ''self''; child-src ''self''; connect-src ''self'' ws: wss:; + img-src ''self'' data:; script-src ''unsafe-eval'' ''unsafe-inline'' ''self''; + style-src ''unsafe-inline'' ''self''' + strict-transport-security: + - max-age=631139040; includeSubdomains + x-content-type-options: + - nosniff + x-download-options: + - noopen + x-frame-options: + - sameorigin + x-permitted-cross-domain-policies: + - none + x-xss-protection: + - '0' + status: + code: 200 + message: OK +- request: + body: '{"location_id": 4, "organization_id": 3}' + headers: + Accept: + - application/json;version=2 + Accept-Encoding: + - gzip, deflate, br + Connection: + - keep-alive + Content-Length: + - '40' + Content-Type: + - application/json + User-Agent: + - apypie (https://github.com/Apipie/apypie) + method: POST + uri: https://foreman.example.org/api/provisioning_templates/build_pxe_default + response: + body: + string: '{"message":"PXE files for templates PXEGrub2 global default, PXELinux + global default, and PXEGrub global default have been deployed to all Smart + Proxies"}' + headers: + Cache-Control: + - max-age=0, private, must-revalidate + Connection: + - Keep-Alive + Content-Length: + - '154' + Content-Type: + - application/json; charset=utf-8 + Foreman_api_version: + - '2' + Foreman_current_location: + - 4; Test Location + Foreman_current_organization: + - 3; Test Organization + Foreman_version: + - 3.15.0 + Keep-Alive: + - timeout=15, max=97 + Referrer-Policy: + - strict-origin-when-cross-origin + content-security-policy: + - 'default-src ''self''; child-src ''self''; connect-src ''self'' ws: wss:; + img-src ''self'' data:; script-src ''unsafe-eval'' ''unsafe-inline'' ''self''; + style-src ''unsafe-inline'' ''self''' + strict-transport-security: + - max-age=631139040; includeSubdomains + x-content-type-options: + - nosniff + x-download-options: + - noopen + x-frame-options: + - sameorigin + x-permitted-cross-domain-policies: + - none + x-xss-protection: + - '0' + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_playbooks/tasks/build_pxe_default.yml b/tests/test_playbooks/tasks/build_pxe_default.yml new file mode 100644 index 0000000000..2a4024f36f --- /dev/null +++ b/tests/test_playbooks/tasks/build_pxe_default.yml @@ -0,0 +1,20 @@ +--- +- name: "Build PXE default" + vars: + organization_name: "Test Organization" + location_name: "Test Location" + build_pxe_default: + username: "{{ foreman_username }}" + password: "{{ foreman_password }}" + server_url: "{{ foreman_server_url }}" + validate_certs: "{{ foreman_validate_certs }}" + organization: "{{ organization_name }}" + location: "{{ location_name }}" + register: result + ignore_errors: "{{ expected_fail | default(false) }}" +- assert: + fail_msg: "Building PXE default failed! (expected_change: True)" + that: + - result.changed + when: + - expected_fail is not defined or not expected_fail