From 9ab1a2342e60b945cbe3287a5e211b7ebb852d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Moser?= Date: Thu, 15 Dec 2022 20:12:23 +0100 Subject: [PATCH] new module instance_info (#48) --- plugins/module_utils/vultr_v2.py | 21 +- plugins/modules/instance_info.py | 271 ++++++++++++++++++ .../integration/targets/instance_info/aliases | 3 + .../targets/instance_info/defaults/main.yml | 23 ++ .../targets/instance_info/meta/main.yml | 3 + .../targets/instance_info/tasks/main.yml | 7 + .../targets/instance_info/tasks/tests.yml | 62 ++++ 7 files changed, 379 insertions(+), 11 deletions(-) create mode 100644 plugins/modules/instance_info.py create mode 100644 tests/integration/targets/instance_info/aliases create mode 100644 tests/integration/targets/instance_info/defaults/main.yml create mode 100644 tests/integration/targets/instance_info/meta/main.yml create mode 100644 tests/integration/targets/instance_info/tasks/main.yml create mode 100644 tests/integration/targets/instance_info/tasks/tests.yml diff --git a/plugins/module_utils/vultr_v2.py b/plugins/module_utils/vultr_v2.py index 02e378e..655730b 100644 --- a/plugins/module_utils/vultr_v2.py +++ b/plugins/module_utils/vultr_v2.py @@ -10,8 +10,9 @@ import time import urllib -from ansible.module_utils._text import to_native, to_text +from ansible.module_utils._text import to_text from ansible.module_utils.basic import env_fallback +from ansible.module_utils.six.moves.urllib.parse import quote from ansible.module_utils.urls import fetch_url VULTR_USER_AGENT = "Ansible Vultr v2" @@ -136,16 +137,14 @@ def transform_resource(self, resource): """ return resource - def api_query(self, path, method="GET", data=None): + def api_query(self, path, method="GET", data=None, query_params=None): + if query_params: + query = "?" + for k, v in query_params.items(): + query += "&%s=%s" % (to_text(k), quote(to_text(v))) + path += query - if method == "GET" and data: - data_encoded = data.copy() - try: - data = urllib.urlencode(data_encoded) - except AttributeError: - data = urllib.parse.urlencode(data_encoded) - else: - data = self.module.jsonify(data) + data = self.module.jsonify(data) retry_max_delay = self.module.params["api_retry_max_delay"] @@ -252,7 +251,7 @@ def query_list(self, path=None, result_key=None, query_params=None): path = path or self.resource_path result_key = result_key or self.ressource_result_key_plural - resources = self.api_query(path=path, data=query_params) + resources = self.api_query(path=path, query_params=query_params) return resources[result_key] if resources else [] def wait_for_state(self, resource, key, state, cmp="="): diff --git a/plugins/modules/instance_info.py b/plugins/modules/instance_info.py new file mode 100644 index 0000000..c79c6c6 --- /dev/null +++ b/plugins/modules/instance_info.py @@ -0,0 +1,271 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright (c) 2022, René Moser +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +DOCUMENTATION = """ +--- +module: instance_info +short_description: Get information about the Vultr instances +description: + - Get infos about available instances. +version_added: "1.5.0" +author: + - "René Moser (@resmo)" +options: + label: + description: + - Name of the instance. + aliases: [ name ] + type: str + region: + description: + - Filter instances by region. + type: str +extends_documentation_fragment: + - vultr.cloud.vultr_v2 +""" + +EXAMPLES = """ +- name: Get Vultr instance infos of region ams + vultr.cloud.instances_info: + region: ams + +- name: Get Vultr instance infos of a single host + vultr.cloud.instances_info: + label: myhost + +- name: Get all Vultr instance infos + vultr.cloud.instances_info: + register: results + +- name: Print the gathered infos + ansible.builtin.debug: + var: results.vultr_instance_info +""" + +RETURN = """ +--- +vultr_api: + description: Response from Vultr API with a few additions/modification. + returned: success + type: dict + contains: + api_timeout: + description: Timeout used for the API requests. + returned: success + type: int + sample: 60 + api_retries: + description: Amount of max retries for the API requests. + returned: success + type: int + sample: 5 + api_retry_max_delay: + description: Exponential backoff delay in seconds between retries up to this max delay value. + returned: success + type: int + sample: 12 + api_endpoint: + description: Endpoint used for the API requests. + returned: success + type: str + sample: "https://api.vultr.com/v2" +vultr_instance_info: + description: Response from Vultr API as list. + returned: available + type: list + contains: + id: + description: ID of the instance. + returned: success + type: str + sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60 + v6_main_ip: + description: IPv6 of the instance. + returned: success + type: str + sample: "" + v6_network: + description: IPv6 network of the instance. + returned: success + type: str + sample: "" + v6_network_size: + description: IPv6 network size of the instance. + returned: success + type: int + sample: 0 + main_ip: + description: IPv4 of the instance. + returned: success + type: str + sample: 95.179.189.95 + netmask_v4: + description: Netmask IPv4 of the instance. + returned: success + type: str + sample: 255.255.254.0 + hostname: + description: Hostname of the instance. + returned: success + type: str + sample: vultr.guest + internal_ip: + description: Internal IP of the instance. + returned: success + type: str + sample: "" + gateway_v4: + description: Gateway IPv4. + returned: success + type: str + sample: 95.179.188.1 + kvm: + description: KVM of the instance. + returned: success + type: str + sample: "https://my.vultr.com/subs/vps/novnc/api.php?data=..." + disk: + description: Disk size of the instance. + returned: success + type: int + sample: 25 + allowed_bandwidth: + description: Allowed bandwidth of the instance. + returned: success + type: int + sample: 1000 + vcpu_count: + description: vCPUs of the instance. + returned: success + type: int + sample: 1 + firewall_group_id: + description: Firewall group ID of the instance. + returned: success + type: str + sample: "" + plan: + description: Plan of the instance. + returned: success + type: str + sample: vc2-1c-1gb + image_id: + description: Image ID of the instance. + returned: success + type: str + sample: "" + os_id: + description: OS ID of the instance. + returned: success + type: int + sample: 186 + app_id: + description: App ID of the instance. + returned: success + type: int + sample: 37 + date_created: + description: Date when the instance was created. + returned: success + type: str + sample: "2020-10-10T01:56:20+00:00" + label: + description: Label of the instance. + returned: success + type: str + sample: my instance + region: + description: Region the instance was deployed into. + returned: success + type: str + sample: ews + status: + description: Status about the deployment of the instance. + returned: success + type: str + sample: active + server_status: + description: Server status of the instance. + returned: success + type: str + sample: installingbooting + power_status: + description: Power status of the instance. + returned: success + type: str + sample: running + ram: + description: RAM in MB of the instance. + returned: success + type: int + sample: 1024 + os: + description: OS of the instance. + returned: success + type: str + sample: Application + tags: + description: Tags of the instance. + returned: success + type: list + sample: [ my-tag ] + features: + description: Features of the instance. + returned: success + type: list + sample: [ ddos_protection, ipv6, auto_backups ] + user_data: + description: Base64 encoded user data (cloud init) of the instance. + returned: success + type: str + sample: I2Nsb3VkLWNvbmZpZwpwYWNrYWdlczoKICAtIGh0b3AK + +""" + +from ansible.module_utils.basic import AnsibleModule + +from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec + + +def main(): + argument_spec = vultr_argument_spec() + argument_spec.update( + dict( + region=dict(type="str", aliases=["name"]), + label=dict(type="str"), + ) # type: ignore + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + vultr = AnsibleVultr( + module=module, + namespace="vultr_instance_info", + resource_path="/instances", + ressource_result_key_singular="instance", + ressource_result_key_plural="instances", + ) + + query_params = dict() + if module.params["region"] is not None: # type: ignore + query_params.update({"region": module.params["region"]}) # type: ignore + + if module.params["label"] is not None: # type: ignore + query_params.update({"label": module.params["label"]}) # type: ignore + + vultr.get_result(vultr.query_list(query_params=query_params)) + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/instance_info/aliases b/tests/integration/targets/instance_info/aliases new file mode 100644 index 0000000..c749ce7 --- /dev/null +++ b/tests/integration/targets/instance_info/aliases @@ -0,0 +1,3 @@ +cloud/vultr +needs/target/common +needs/target/cleanup diff --git a/tests/integration/targets/instance_info/defaults/main.yml b/tests/integration/targets/instance_info/defaults/main.yml new file mode 100644 index 0000000..463a901 --- /dev/null +++ b/tests/integration/targets/instance_info/defaults/main.yml @@ -0,0 +1,23 @@ +# Copyright (c) 2022, René Moser +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +vultr_region1: ams +vultr_region2: cdg + +vultr_instances: + - label: "{{ vultr_resource_prefix }}_info1" + hostname: info1 + plan: vc2-1c-1gb + tags: + - one + - two + region: "{{ vultr_region1 }}" + os: Debian 11 x64 (bullseye) + - label: "{{ vultr_resource_prefix }}_info2" + hostname: info2 + plan: vc2-1c-1gb + tags: + - three + - four + region: "{{ vultr_region2 }}" + os: Debian 11 x64 (bullseye) diff --git a/tests/integration/targets/instance_info/meta/main.yml b/tests/integration/targets/instance_info/meta/main.yml new file mode 100644 index 0000000..2083f0e --- /dev/null +++ b/tests/integration/targets/instance_info/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - common diff --git a/tests/integration/targets/instance_info/tasks/main.yml b/tests/integration/targets/instance_info/tasks/main.yml new file mode 100644 index 0000000..352cfa2 --- /dev/null +++ b/tests/integration/targets/instance_info/tasks/main.yml @@ -0,0 +1,7 @@ +--- +- block: + - ansible.builtin.import_tasks: tests.yml + always: + - ansible.builtin.import_role: + name: cleanup + tasks_from: cleanup_instance diff --git a/tests/integration/targets/instance_info/tasks/tests.yml b/tests/integration/targets/instance_info/tasks/tests.yml new file mode 100644 index 0000000..204d071 --- /dev/null +++ b/tests/integration/targets/instance_info/tasks/tests.yml @@ -0,0 +1,62 @@ +# Copyright (c) 2022, René Moser +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +- name: setup + vultr.cloud.instance: + label: "{{ instance.label }}" + hostname: "{{ instance.hostname }}" + plan: "{{ instance.plan }}" + tags: "{{ instance.tags }}" + region: "{{ instance.region }}" + os: "{{ instance.os }}" + register: result + with_items: "{{ vultr_instances }}" + loop_control: + loop_var: instance + +- name: test gather vultr instance info in check mode + vultr.cloud.instance_info: + check_mode: true + register: result +- name: verify test gather vultr instance info in check mode + ansible.builtin.assert: + that: + - result.vultr_instance_info|selectattr('label','search','^{{ vultr_resource_prefix }}') | list | count == 2 + - result.vultr_instance_info|selectattr('label','equalto','{{ vultr_resource_prefix }}_info1') | list | count == 1 + +- name: test gather vultr instance info + vultr.cloud.instance_info: + register: result +- name: verify test gather vultr instance info + ansible.builtin.assert: + that: + - result.vultr_instance_info|selectattr('label','search','^{{ vultr_resource_prefix }}') | list | count == 2 + - result.vultr_instance_info|selectattr('label','equalto','{{ vultr_resource_prefix }}_info1') | list | count == 1 + +- name: test gather vultr instance info filter region + vultr.cloud.instance_info: + region: "{{ vultr_region1 }}" + register: result +- name: verify test gather vultr instance info filter region + ansible.builtin.assert: + that: + - result.vultr_instance_info|selectattr('label','search','^{{ vultr_resource_prefix }}') | list | count == 1 + +- name: test gather vultr instance info filter label + vultr.cloud.instance_info: + label: "{{ vultr_resource_prefix }}_info2" + register: result +- name: verify test gather vultr instance info + ansible.builtin.assert: + that: + - result.vultr_instance_info|selectattr('label','search','^{{ vultr_resource_prefix }}') | list | count == 1 + - result.vultr_instance_info|selectattr('label','equalto','{{ vultr_resource_prefix }}_info2') | list | count == 1 + +- name: cleanup + vultr.cloud.instance: + label: "{{ instance.label }}" + region: "{{ instance.region }}" + state: absent + with_items: "{{ vultr_instances }}" + loop_control: + loop_var: instance