#!/usr/bin/python # # Copyright (c) 2021, Jyrki Gadinger # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later # Make coding more python3-ish from __future__ import annotations DOCUMENTATION = r""" module: one_template short_description: Manages OpenNebula templates version_added: 2.4.0 requirements: - pyone description: - Manages OpenNebula templates. attributes: check_mode: support: partial details: - Note that check mode always returns RV(ignore:changed=true) for existing templates, even if the template would not actually change. diff_mode: support: none options: id: description: - A O(id) of the template you would like to manage. If not set then a new template is created with the given O(name). type: int name: description: - A O(name) of the template you would like to manage. If a template with the given name does not exist it is created, otherwise it is managed by this module. type: str template: description: - A string containing the template contents. type: str state: description: - V(present) - state that is used to manage the template. - V(absent) - delete the template. choices: ["present", "absent"] default: present type: str filter: description: - V(user_primary_group) - Resources belonging to the user's primary group. - V(user) - Resources belonging to the user. - V(all) - All resources. - V(user_groups) - Resources belonging to the user and any of his groups. choices: [user_primary_group, user, all, user_groups] default: user type: str version_added: 10.3.0 extends_documentation_fragment: - community.general.opennebula - community.general.attributes author: - "Jyrki Gadinger (@nilsding)" """ EXAMPLES = r""" - name: Fetch the TEMPLATE by id community.general.one_template: id: 6459 register: result - name: Print the TEMPLATE properties ansible.builtin.debug: var: result - name: Fetch the TEMPLATE by name community.general.one_template: name: tf-prd-users-workerredis-p6379a register: result - name: Create a new or update an existing TEMPLATE community.general.one_template: name: generic-opensuse template: | CONTEXT = [ HOSTNAME = "generic-opensuse" ] CPU = "1" CUSTOM_ATTRIBUTE = "" DISK = [ CACHE = "writeback", DEV_PREFIX = "sd", DISCARD = "unmap", IMAGE = "opensuse-leap-15.2", IMAGE_UNAME = "oneadmin", IO = "threads", SIZE = "" ] MEMORY = "2048" NIC = [ MODEL = "virtio", NETWORK = "testnet", NETWORK_UNAME = "oneadmin" ] OS = [ ARCH = "x86_64", BOOT = "disk0" ] SCHED_REQUIREMENTS = "CLUSTER_ID=\\"100\\"" VCPU = "2" - name: Delete the TEMPLATE by id community.general.one_template: id: 6459 state: absent """ RETURN = r""" id: description: Template ID. type: int returned: when O(state=present) sample: 153 name: description: Template name. type: str returned: when O(state=present) sample: app1 template: description: The parsed template. type: dict returned: when O(state=present) group_id: description: Template's group ID. type: int returned: when O(state=present) sample: 1 group_name: description: Template's group name. type: str returned: when O(state=present) sample: one-users owner_id: description: Template's owner ID. type: int returned: when O(state=present) sample: 143 owner_name: description: Template's owner name. type: str returned: when O(state=present) sample: ansible-test """ from ansible_collections.community.general.plugins.module_utils.opennebula import OpenNebulaModule class TemplateModule(OpenNebulaModule): def __init__(self): argument_spec = dict( id=dict(type="int"), name=dict(type="str"), state=dict(type="str", choices=["present", "absent"], default="present"), template=dict(type="str"), filter=dict(type="str", choices=["user_primary_group", "user", "all", "user_groups"], default="user"), ) mutually_exclusive = [["id", "name"]] required_one_of = [("id", "name")] required_if = [["state", "present", ["template"]]] OpenNebulaModule.__init__( self, argument_spec, supports_check_mode=True, mutually_exclusive=mutually_exclusive, required_one_of=required_one_of, required_if=required_if, ) def run(self, one, module, result): params = module.params id = params.get("id") name = params.get("name") desired_state = params.get("state") template_data = params.get("template") filter = params.get("filter") self.result = {} template = self.get_template_instance(id, name, filter) needs_creation = False if not template and desired_state != "absent": if id: module.fail_json(msg=f"There is no template with id={id}") else: needs_creation = True if desired_state == "absent": self.result = self.delete_template(template) else: if needs_creation: self.result = self.create_template(name, template_data, filter) else: self.result = self.update_template(template, template_data, filter) self.exit() def get_template(self, predicate, filter): # filter was included, for discussions see: # Issue: https://github.com/ansible-collections/community.general/issues/9278 # PR: https://github.com/ansible-collections/community.general/pull/9547 # the other two parameters are used for pagination, -1 for both essentially means "return all" filter_values = {"user_primary_group": -4, "user": -3, "all": -2, "user_groups": -1} pool = self.one.templatepool.info(filter_values[filter], -1, -1) for template in pool.VMTEMPLATE: if predicate(template): return template return None def get_template_by_id(self, template_id, filter): return self.get_template(lambda template: template_id == template.ID, filter) def get_template_by_name(self, name, filter): return self.get_template(lambda template: name == template.NAME, filter) def get_template_instance(self, requested_id, requested_name, filter): if requested_id: return self.get_template_by_id(requested_id, filter) else: return self.get_template_by_name(requested_name, filter) def get_template_info(self, template): info = { "id": template.ID, "name": template.NAME, "template": template.TEMPLATE, "user_name": template.UNAME, "user_id": template.UID, "group_name": template.GNAME, "group_id": template.GID, } return info def create_template(self, name, template_data, filter): if not self.module.check_mode: self.one.template.allocate(f'NAME = "{name}"\n{template_data}') result = self.get_template_info(self.get_template_by_name(name, filter)) result["changed"] = True return result def update_template(self, template, template_data, filter): if not self.module.check_mode: # 0 = replace the whole template self.one.template.update(template.ID, template_data, 0) result = self.get_template_info(self.get_template_by_id(template.ID, filter)) if self.module.check_mode: # Unfortunately it is not easy to detect if the template would have changed, therefore always report a change here. result["changed"] = True else: # if the previous parsed template data is not equal to the updated one, this has changed result["changed"] = result["template"] != template.TEMPLATE return result def delete_template(self, template): if not template: return {"changed": False} if not self.module.check_mode: self.one.template.delete(template.ID) return {"changed": True} def main(): TemplateModule().run_module() if __name__ == "__main__": main()