From 7efa6844c67c53436d7a3e9cd539bec5237eb015 Mon Sep 17 00:00:00 2001 From: Leos Stejskal Date: Tue, 7 Sep 2021 15:41:08 +0200 Subject: [PATCH] Module for generating registration commands --- plugins/module_utils/foreman_helper.py | 2 +- plugins/modules/registration_command.py | 89 ++++++++ .../fixtures/apidoc/registration_command.json | 1 + .../fixtures/registration_command-0.yml | 209 ++++++++++++++++++ tests/test_playbooks/registration_command.yml | 28 +++ .../tasks/registration_command.yml | 21 ++ 6 files changed, 349 insertions(+), 1 deletion(-) create mode 100644 plugins/modules/registration_command.py create mode 120000 tests/fixtures/apidoc/registration_command.json create mode 100644 tests/test_playbooks/fixtures/registration_command-0.yml create mode 100644 tests/test_playbooks/registration_command.yml create mode 100644 tests/test_playbooks/tasks/registration_command.yml diff --git a/plugins/module_utils/foreman_helper.py b/plugins/module_utils/foreman_helper.py index a0959ffeab..b493dba39d 100644 --- a/plugins/module_utils/foreman_helper.py +++ b/plugins/module_utils/foreman_helper.py @@ -237,7 +237,7 @@ def __init__(self, **kwargs): def run(self, **kwargs): new_entity = super(NestedParametersMixin, self).run(**kwargs) - if new_entity: + if new_entity and 'id' in new_entity: scope = {'{0}_id'.format(self.entity_name): new_entity['id']} self.ensure_scoped_parameters(scope) return new_entity diff --git a/plugins/modules/registration_command.py b/plugins/modules/registration_command.py new file mode 100644 index 0000000000..d81072b387 --- /dev/null +++ b/plugins/modules/registration_command.py @@ -0,0 +1,89 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# (c) 2021 Stejskal Leos, lstejska@redhat.com +# +# 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: registration_command +version_added: 1.0.0 +short_description: Generate registration command +description: + - Generate registration command +author: + - "Stejskal Leos (@lstejska)" + +''' + +EXAMPLES = ''' +- name: "Generate registration command" + theforeman.foreman.registration_command: + username: "admin" + password: "changeme" + server_url: "https://foreman.example.com" +''' + +RETURN = ''' +entity: + description: Final state of the affected entities grouped by their type. + returned: success + type: dict + contains: + registration_commands: + description: List of registration commands + type: list + elements: dict +''' + +from ansible_collections.theforeman.foreman.plugins.module_utils.foreman_helper import ForemanEntityAnsibleModule, NestedParametersMixin + + +class ForemanRegistrationCommandModule(NestedParametersMixin, ForemanEntityAnsibleModule): + pass + + +def main(): + module = ForemanRegistrationCommandModule( + foreman_spec=dict( + organization=dict(type='entity'), + location=dict(type='entity'), + hostgroup=dict(type='entity'), + operatingsystem=dict(type='entity'), + smart_proxy=dict(type='entity'), + insecure=dict(type='bool'), + setup_remote_execution=dict(type='bool'), + setup_insights=dict(type='bool'), + packages=dict(type='str'), + update_packages=dict(type='bool'), + repo=dict(type='str'), + repo_gpg_key_url=dict(type='str'), + jwt_expiration=dict(type='int', default=4), + remote_execution_interface=dict(type='str'), + lifecycle_environment=dict(type='entity', flat_name='lifecycle_environment_id'), + ignore_subman_errors=dict(type='bool'), + force=dict(type='bool'), + activation_keys=dict(type='list', elements='str'), + ), + ) + + with module.api_connection(): + module.run() + + +if __name__ == '__main__': + main() diff --git a/tests/fixtures/apidoc/registration_command.json b/tests/fixtures/apidoc/registration_command.json new file mode 120000 index 0000000000..f9e401512c --- /dev/null +++ b/tests/fixtures/apidoc/registration_command.json @@ -0,0 +1 @@ +foreman.json \ No newline at end of file diff --git a/tests/test_playbooks/fixtures/registration_command-0.yml b/tests/test_playbooks/fixtures/registration_command-0.yml new file mode 100644 index 0000000000..bc16a6a318 --- /dev/null +++ b/tests/test_playbooks/fixtures/registration_command-0.yml @@ -0,0 +1,209 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json;version=2 + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - apypie (https://github.com/Apipie/apypie) + method: GET + uri: http://foreman.example.org/api/status + response: + body: + string: '{"result":"ok","status":200,"version":"3.1.0-develop","api_version":2}' + headers: + Cache-Control: + - max-age=0, private, must-revalidate + 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''' + Content-Type: + - application/json; charset=utf-8 + Foreman_api_version: + - '2' + Foreman_current_location: + - ; ANY + Foreman_current_organization: + - ; ANY + Foreman_version: + - 3.1.0-develop + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - sameorigin + X-Permitted-Cross-Domain-Policies: + - none + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;version=2 + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - apypie (https://github.com/Apipie/apypie) + method: GET + uri: http://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\":\"2021-09-09 + 15:12:38 UTC\",\"updated_at\":\"2021-09-09 15:12:38 UTC\",\"id\":4,\"name\":\"Test + Organization\",\"title\":\"Test Organization\",\"description\":\"A test organization\"}]\n}\n" + headers: + Cache-Control: + - max-age=0, private, must-revalidate + 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''' + Content-Type: + - application/json; charset=utf-8 + Foreman_api_version: + - '2' + Foreman_current_location: + - ; ANY + Foreman_current_organization: + - ; ANY + Foreman_version: + - 3.1.0-develop + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - sameorigin + X-Permitted-Cross-Domain-Policies: + - none + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json;version=2 + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - apypie (https://github.com/Apipie/apypie) + method: GET + uri: http://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\":\"2021-09-09 + 15:12:37 UTC\",\"updated_at\":\"2021-09-09 15:12:37 UTC\",\"id\":3,\"name\":\"Test + Location\",\"title\":\"Test Location\",\"description\":null}]\n}\n" + headers: + Cache-Control: + - max-age=0, private, must-revalidate + 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''' + Content-Type: + - application/json; charset=utf-8 + Foreman_api_version: + - '2' + Foreman_current_location: + - ; ANY + Foreman_current_organization: + - ; ANY + Foreman_version: + - 3.1.0-develop + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - sameorigin + X-Permitted-Cross-Domain-Policies: + - none + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +- request: + body: '{"location_id": 3, "organization_id": 4, "registration_command": {"organization_id": + 4, "location_id": 3, "jwt_expiration": 4}}' + headers: + Accept: + - application/json;version=2 + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '127' + Content-Type: + - application/json + User-Agent: + - apypie (https://github.com/Apipie/apypie) + method: POST + uri: http://foreman.example.org/api/registration_commands + response: + body: + string: '{"registration_command":"curl -sS ''http://localhost:3000/register?location_id=3\u0026organization_id=4'' + -H ''Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo0LCJpYXQiOjE2MzEyMDA3NjcsImp0aSI6ImE1NWE2MDZmZDI3ZGY5NGRiMjFkMmZkYzI5NWVmODIxZDI2YmRiZmVmMGZjNzcyZTVlZTkxYmM1MTlmMzVmNWIiLCJleHAiOjE2MzEyMTUxNjcsInNjb3BlIjoicmVnaXN0cmF0aW9uI2dsb2JhbCByZWdpc3RyYXRpb24jaG9zdCJ9.3BzVTEVe5qS_cYO_HMO_BKrtFty6c-ROOBIqfKmfSO4'' + | bash"}' + headers: + Cache-Control: + - max-age=0, private, must-revalidate + 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''' + Content-Type: + - application/json; charset=utf-8 + Foreman_api_version: + - '2' + Foreman_current_location: + - 3; Test Location + Foreman_current_organization: + - 4; Test Organization + Foreman_version: + - 3.1.0-develop + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + X-Download-Options: + - noopen + X-Frame-Options: + - sameorigin + X-Permitted-Cross-Domain-Policies: + - none + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_playbooks/registration_command.yml b/tests/test_playbooks/registration_command.yml new file mode 100644 index 0000000000..86fbf0eb2d --- /dev/null +++ b/tests/test_playbooks/registration_command.yml @@ -0,0 +1,28 @@ +--- +- hosts: localhost + collections: + - theforeman.foreman + tags: + - setup + gather_facts: false + vars_files: + - vars/server.yml + tasks: + - include_tasks: tasks/location.yml + vars: + location_state: "present" + - include_tasks: tasks/organization.yml + vars: + organization_state: "present" + +- hosts: tests + collections: + - theforeman.foreman + tags: + - test + gather_facts: false + vars_files: + - vars/server.yml + tasks: + # generate registration command + - include: tasks/registration_command.yml diff --git a/tests/test_playbooks/tasks/registration_command.yml b/tests/test_playbooks/tasks/registration_command.yml new file mode 100644 index 0000000000..e48fb4e11c --- /dev/null +++ b/tests/test_playbooks/tasks/registration_command.yml @@ -0,0 +1,21 @@ +--- +- name: "Ensure registration command is {{ rc_state }}" + vars: + rc_organization: Test Organization + rc_location: Test Location + rc_state: present + registration_command: + username: "{{ foreman_username }}" + password: "{{ foreman_password }}" + server_url: "{{ foreman_server_url }}" + validate_certs: "{{ foreman_validate_certs }}" + organization: "{{ rc_organization }}" + location: "{{ rc_location }}" + state: "{{ rc_state }}" + register: result +- assert: + fail_msg: "Ensuring registering command is {{ rc_state }} failed! (expected_change: {{ expected_change | default('unknown') }})" + that: + - result.changed == expected_change + when: expected_change is defined +...