diff --git a/.ansible-lint b/.ansible-lint index 17644255b..2ada78bf9 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -53,10 +53,7 @@ verbosity: 1 mock_modules: - gcp_compute_location_info - lightsail_region_facts - - linode_stackscript_v4 - x25519_pubkey - - linode_v4 - scaleway_compute - - digital_ocean_floating_ip # vim: ft=yaml diff --git a/library/digital_ocean_floating_ip.py b/library/digital_ocean_floating_ip.py deleted file mode 100644 index 19cf54cbb..000000000 --- a/library/digital_ocean_floating_ip.py +++ /dev/null @@ -1,278 +0,0 @@ -#!/usr/bin/python -# -# (c) 2015, Patrick F. Marques -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -import json -import time - -from ansible.module_utils.basic import AnsibleModule, env_fallback -from ansible.module_utils.digital_ocean import DigitalOceanHelper - -ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"} - -DOCUMENTATION = """ ---- -module: digital_ocean_floating_ip -short_description: Manage DigitalOcean Floating IPs -description: - - Create/delete/assign a floating IP. -version_added: "2.4" -author: "Patrick Marques (@pmarques)" -options: - state: - description: - - Indicate desired state of the target. - default: present - choices: ['present', 'absent'] - ip: - description: - - Public IP address of the Floating IP. Used to remove an IP - region: - description: - - The region that the Floating IP is reserved to. - droplet_id: - description: - - The Droplet that the Floating IP has been assigned to. - oauth_token: - description: - - DigitalOcean OAuth token. - required: true -notes: - - Version 2 of DigitalOcean API is used. -requirements: - - "python >= 2.6" -""" - - -EXAMPLES = """ -- name: "Create a Floating IP in region lon1" - digital_ocean_floating_ip: - state: present - region: lon1 - -- name: "Create a Floating IP assigned to Droplet ID 123456" - digital_ocean_floating_ip: - state: present - droplet_id: 123456 - -- name: "Delete a Floating IP with ip 1.2.3.4" - digital_ocean_floating_ip: - state: absent - ip: "1.2.3.4" - -""" - - -RETURN = """ -# Digital Ocean API info https://developers.digitalocean.com/documentation/v2/#floating-ips -data: - description: a DigitalOcean Floating IP resource - returned: success and no resource constraint - type: dict - sample: { - "action": { - "id": 68212728, - "status": "in-progress", - "type": "assign_ip", - "started_at": "2015-10-15T17:45:44Z", - "completed_at": null, - "resource_id": 758603823, - "resource_type": "floating_ip", - "region": { - "name": "New York 3", - "slug": "nyc3", - "sizes": [ - "512mb", - "1gb", - "2gb", - "4gb", - "8gb", - "16gb", - "32gb", - "48gb", - "64gb" - ], - "features": [ - "private_networking", - "backups", - "ipv6", - "metadata" - ], - "available": true - }, - "region_slug": "nyc3" - } - } -""" - - -class Response: - def __init__(self, resp, info): - self.body = None - if resp: - self.body = resp.read() - self.info = info - - @property - def json(self): - if not self.body: - if "body" in self.info: - return json.loads(self.info["body"]) - return None - try: - return json.loads(self.body) - except ValueError: - return None - - @property - def status_code(self): - return self.info["status"] - - -def wait_action(module, rest, ip, action_id, timeout=10): - end_time = time.time() + 10 - while time.time() < end_time: - response = rest.get(f"floating_ips/{ip}/actions/{action_id}") - # status_code = response.status_code # TODO: check status_code == 200? - status = response.json["action"]["status"] - if status == "completed": - return True - elif status == "errored": - module.fail_json(msg=f"Floating ip action error [ip: {ip}: action: {action_id}]", data=json) - - module.fail_json(msg=f"Floating ip action timeout [ip: {ip}: action: {action_id}]", data=json) - - -def core(module): - # api_token = module.params['oauth_token'] # unused for now - state = module.params["state"] - ip = module.params["ip"] - droplet_id = module.params["droplet_id"] - - rest = DigitalOceanHelper(module) - - if state in ("present"): - if droplet_id is not None and module.params["ip"] is not None: - # Lets try to associate the ip to the specified droplet - associate_floating_ips(module, rest) - else: - create_floating_ips(module, rest) - - elif state in ("absent"): - response = rest.delete(f"floating_ips/{ip}") - status_code = response.status_code - json_data = response.json - if status_code == 204: - module.exit_json(changed=True) - elif status_code == 404: - module.exit_json(changed=False) - else: - module.exit_json(changed=False, data=json_data) - - -def get_floating_ip_details(module, rest): - ip = module.params["ip"] - - response = rest.get(f"floating_ips/{ip}") - status_code = response.status_code - json_data = response.json - if status_code == 200: - return json_data["floating_ip"] - else: - module.fail_json( - msg="Error assigning floating ip [{}: {}]".format(status_code, json_data["message"]), - region=module.params["region"], - ) - - -def assign_floating_id_to_droplet(module, rest): - ip = module.params["ip"] - - payload = { - "type": "assign", - "droplet_id": module.params["droplet_id"], - } - - response = rest.post(f"floating_ips/{ip}/actions", data=payload) - status_code = response.status_code - json_data = response.json - if status_code == 201: - wait_action(module, rest, ip, json_data["action"]["id"]) - - module.exit_json(changed=True, data=json_data) - else: - module.fail_json( - msg="Error creating floating ip [{}: {}]".format(status_code, json_data["message"]), - region=module.params["region"], - ) - - -def associate_floating_ips(module, rest): - floating_ip = get_floating_ip_details(module, rest) - droplet = floating_ip["droplet"] - - # TODO: If already assigned to a droplet verify if is one of the specified as valid - if droplet is not None and str(droplet["id"]) in [module.params["droplet_id"]]: - module.exit_json(changed=False) - else: - assign_floating_id_to_droplet(module, rest) - - -def create_floating_ips(module, rest): - payload = {} - floating_ip_data = None - - if module.params["region"] is not None: - payload["region"] = module.params["region"] - - if module.params["droplet_id"] is not None: - payload["droplet_id"] = module.params["droplet_id"] - - floating_ips = rest.get_paginated_data(base_url="floating_ips?", data_key_name="floating_ips") - - for floating_ip in floating_ips: - if floating_ip["droplet"] and floating_ip["droplet"]["id"] == module.params["droplet_id"]: - floating_ip_data = {"floating_ip": floating_ip} - - if floating_ip_data: - module.exit_json(changed=False, data=floating_ip_data) - else: - response = rest.post("floating_ips", data=payload) - status_code = response.status_code - json_data = response.json - - if status_code == 202: - module.exit_json(changed=True, data=json_data) - else: - module.fail_json( - msg="Error creating floating ip [{}: {}]".format(status_code, json_data["message"]), - region=module.params["region"], - ) - - -def main(): - module = AnsibleModule( - argument_spec={ - "state": {"choices": ["present", "absent"], "default": "present"}, - "ip": {"aliases": ["id"], "required": False}, - "region": {"required": False}, - "droplet_id": {"required": False, "type": "int"}, - "oauth_token": { - "no_log": True, - # Support environment variable for DigitalOcean OAuth Token - "fallback": (env_fallback, ["DO_API_TOKEN", "DO_API_KEY", "DO_OAUTH_TOKEN"]), - "required": True, - }, - "validate_certs": {"type": "bool", "default": True}, - "timeout": {"type": "int", "default": 30}, - }, - required_if=[("state", "delete", ["ip"])], - mutually_exclusive=[["region", "droplet_id"]], - ) - - core(module) - - -if __name__ == "__main__": - main() diff --git a/library/linode_stackscript_v4.py b/library/linode_stackscript_v4.py deleted file mode 100644 index 1298fc9c5..000000000 --- a/library/linode_stackscript_v4.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/python - - -import traceback - -from ansible.module_utils.basic import AnsibleModule, env_fallback, missing_required_lib -from ansible.module_utils.linode import get_user_agent - -LINODE_IMP_ERR = None -try: - from linode_api4 import LinodeClient, StackScript - - HAS_LINODE_DEPENDENCY = True -except ImportError: - LINODE_IMP_ERR = traceback.format_exc() - HAS_LINODE_DEPENDENCY = False - - -def create_stackscript(module, client, **kwargs): - """Creates a stackscript and handles return format.""" - try: - response = client.linode.stackscript_create(**kwargs) - return response._raw_json - except Exception as exception: - module.fail_json(msg="Unable to query the Linode API. Saw: %s" % exception) - - -def stackscript_available(module, client): - """Try to retrieve a stackscript.""" - try: - label = module.params["label"] - desc = module.params["description"] - - result = client.linode.stackscripts(StackScript.label == label, StackScript.description == desc, mine_only=True) - return result[0] - except IndexError: - return None - except Exception as exception: - module.fail_json(msg="Unable to query the Linode API. Saw: %s" % exception) - - -def initialise_module(): - """Initialise the module parameter specification.""" - return AnsibleModule( - argument_spec=dict( - label=dict(type="str", required=True), - state=dict(type="str", required=True, choices=["present", "absent"]), - access_token=dict( - type="str", - required=True, - no_log=True, - fallback=(env_fallback, ["LINODE_ACCESS_TOKEN"]), - ), - script=dict(type="str", required=True), - images=dict(type="list", required=True), - description=dict(type="str", required=False), - public=dict(type="bool", required=False, default=False), - ), - supports_check_mode=False, - ) - - -def build_client(module): - """Build a LinodeClient.""" - return LinodeClient(module.params["access_token"], user_agent=get_user_agent("linode_v4_module")) - - -def main(): - """Module entrypoint.""" - module = initialise_module() - - if not HAS_LINODE_DEPENDENCY: - module.fail_json(msg=missing_required_lib("linode-api4"), exception=LINODE_IMP_ERR) - - client = build_client(module) - stackscript = stackscript_available(module, client) - - if module.params["state"] == "present" and stackscript is not None: - module.exit_json(changed=False, stackscript=stackscript._raw_json) - - elif module.params["state"] == "present" and stackscript is None: - stackscript_json = create_stackscript( - module, - client, - label=module.params["label"], - script=module.params["script"], - images=module.params["images"], - desc=module.params["description"], - public=module.params["public"], - ) - module.exit_json(changed=True, stackscript=stackscript_json) - - elif module.params["state"] == "absent" and stackscript is not None: - stackscript.delete() - module.exit_json(changed=True, stackscript=stackscript._raw_json) - - elif module.params["state"] == "absent" and stackscript is None: - module.exit_json(changed=False, stackscript={}) - - -if __name__ == "__main__": - main() diff --git a/library/linode_v4.py b/library/linode_v4.py deleted file mode 100644 index cf93602a3..000000000 --- a/library/linode_v4.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/python - -# Copyright (c) 2017 Ansible Project -# GNU General Public License v3.0+ -# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -import traceback - -from ansible.module_utils.basic import AnsibleModule, env_fallback, missing_required_lib -from ansible.module_utils.linode import get_user_agent - -LINODE_IMP_ERR = None -try: - from linode_api4 import Instance, LinodeClient - - HAS_LINODE_DEPENDENCY = True -except ImportError: - LINODE_IMP_ERR = traceback.format_exc() - HAS_LINODE_DEPENDENCY = False - - -def create_linode(module, client, **kwargs): - """Creates a Linode instance and handles return format.""" - if kwargs["root_pass"] is None: - kwargs.pop("root_pass") - - try: - response = client.linode.instance_create(**kwargs) - except Exception as exception: - module.fail_json(msg="Unable to query the Linode API. Saw: %s" % exception) - - try: - if isinstance(response, tuple): - instance, root_pass = response - instance_json = instance._raw_json - instance_json.update({"root_pass": root_pass}) - return instance_json - else: - return response._raw_json - except TypeError: - module.fail_json( - msg="Unable to parse Linode instance creation" - " response. Please raise a bug against this" - " module on https://github.com/ansible/ansible/issues" - ) - - -def maybe_instance_from_label(module, client): - """Try to retrieve an instance based on a label.""" - try: - label = module.params["label"] - result = client.linode.instances(Instance.label == label) - return result[0] - except IndexError: - return None - except Exception as exception: - module.fail_json(msg="Unable to query the Linode API. Saw: %s" % exception) - - -def initialise_module(): - """Initialise the module parameter specification.""" - return AnsibleModule( - argument_spec=dict( - label=dict(type="str", required=True), - state=dict(type="str", required=True, choices=["present", "absent"]), - access_token=dict( - type="str", - required=True, - no_log=True, - fallback=(env_fallback, ["LINODE_ACCESS_TOKEN"]), - ), - authorized_keys=dict(type="list", required=False), - group=dict(type="str", required=False), - image=dict(type="str", required=False), - region=dict(type="str", required=False), - root_pass=dict(type="str", required=False, no_log=True), - tags=dict(type="list", required=False), - type=dict(type="str", required=False), - stackscript_id=dict(type="int", required=False), - ), - supports_check_mode=False, - required_one_of=(["state", "label"],), - required_together=(["region", "image", "type"],), - ) - - -def build_client(module): - """Build a LinodeClient.""" - return LinodeClient(module.params["access_token"], user_agent=get_user_agent("linode_v4_module")) - - -def main(): - """Module entrypoint.""" - module = initialise_module() - - if not HAS_LINODE_DEPENDENCY: - module.fail_json(msg=missing_required_lib("linode-api4"), exception=LINODE_IMP_ERR) - - client = build_client(module) - instance = maybe_instance_from_label(module, client) - - if module.params["state"] == "present" and instance is not None: - module.exit_json(changed=False, instance=instance._raw_json) - - elif module.params["state"] == "present" and instance is None: - instance_json = create_linode( - module, - client, - authorized_keys=module.params["authorized_keys"], - group=module.params["group"], - image=module.params["image"], - label=module.params["label"], - region=module.params["region"], - root_pass=module.params["root_pass"], - tags=module.params["tags"], - ltype=module.params["type"], - stackscript_id=module.params["stackscript_id"], - ) - module.exit_json(changed=True, instance=instance_json) - - elif module.params["state"] == "absent" and instance is not None: - instance.delete() - module.exit_json(changed=True, instance=instance._raw_json) - - elif module.params["state"] == "absent" and instance is None: - module.exit_json(changed=False, instance={}) - - -if __name__ == "__main__": - main() diff --git a/requirements.yml b/requirements.yml index f0594d3cb..af1d2240b 100644 --- a/requirements.yml +++ b/requirements.yml @@ -10,3 +10,7 @@ collections: version: "==3.0.3" - name: openstack.cloud version: "==2.4.1" + - name: linode.cloud + version: ">=0.41.0" + - name: community.digitalocean + version: ">=1.26.0" diff --git a/roles/cloud-digitalocean/tasks/main.yml b/roles/cloud-digitalocean/tasks/main.yml index 50c022053..845deeb0a 100644 --- a/roles/cloud-digitalocean/tasks/main.yml +++ b/roles/cloud-digitalocean/tasks/main.yml @@ -32,7 +32,7 @@ - block: - name: Create a Floating IP - digital_ocean_floating_ip: + community.digitalocean.digital_ocean_floating_ip: state: present oauth_token: "{{ algo_do_token }}" droplet_id: "{{ droplet.id }}" diff --git a/roles/cloud-linode/tasks/main.yml b/roles/cloud-linode/tasks/main.yml index 8241a8f53..5af5d04d9 100644 --- a/roles/cloud-linode/tasks/main.yml +++ b/roles/cloud-linode/tasks/main.yml @@ -10,8 +10,8 @@ touch /var/lib/cloud/data/result.json - name: Create a stackscript - linode_stackscript_v4: - access_token: "{{ algo_linode_token }}" + linode.cloud.stackscript: + api_token: "{{ algo_linode_token }}" label: "{{ algo_server_name }}" state: present description: Environment:Algo @@ -37,8 +37,8 @@ no_log: true - name: Creating an instance... - linode_v4: - access_token: "{{ algo_linode_token }}" + linode.cloud.instance: + api_token: "{{ algo_linode_token }}" label: "{{ algo_server_name }}" state: present region: "{{ algo_linode_region }}"