#!/usr/bin/python # Copyright Ansible Project # 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 from __future__ import annotations DOCUMENTATION = r""" module: ovh_ip_loadbalancing_backend short_description: Manage OVH IP LoadBalancing backends description: - Manage OVH (French European hosting provider) LoadBalancing IP backends. author: Pascal Heraud (@pascalheraud) notes: - Uses the Python OVH API U(https://github.com/ovh/python-ovh). You have to create an application (a key and secret) with a consumer key as described into U(https://docs.ovh.com/gb/en/customer/first-steps-with-ovh-api/). requirements: - ovh > 0.3.5 extends_documentation_fragment: - community.general.attributes attributes: check_mode: support: none diff_mode: support: none options: name: required: true description: - Name of the LoadBalancing internal name (V(ip-X.X.X.X)). type: str backend: required: true description: - The IP address of the backend to update / modify / delete. type: str state: default: present choices: ['present', 'absent'] description: - Determines whether the backend is to be created/modified or deleted. type: str probe: default: 'none' choices: ['none', 'http', 'icmp', 'oco'] description: - Determines the type of probe to use for this backend. type: str weight: default: 8 description: - Determines the weight for this backend. type: int endpoint: required: true description: - The endpoint to use (for instance V(ovh-eu)). type: str application_key: required: true description: - The applicationKey to use. type: str application_secret: required: true description: - The application secret to use. type: str consumer_key: required: true description: - The consumer key to use. type: str timeout: default: 120 description: - The timeout in seconds used to wait for a task to be completed. type: int """ EXAMPLES = r""" - name: Adds or modify the backend '212.1.1.1' to a loadbalancing 'ip-1.1.1.1' ovh_ip_loadbalancing: name: ip-1.1.1.1 backend: 212.1.1.1 state: present probe: none weight: 8 endpoint: ovh-eu application_key: yourkey application_secret: yoursecret consumer_key: yourconsumerkey - name: Removes a backend '212.1.1.1' from a loadbalancing 'ip-1.1.1.1' ovh_ip_loadbalancing: name: ip-1.1.1.1 backend: 212.1.1.1 state: absent endpoint: ovh-eu application_key: yourkey application_secret: yoursecret consumer_key: yourconsumerkey """ import time try: import ovh import ovh.exceptions from ovh.exceptions import APIError HAS_OVH = True except ImportError: HAS_OVH = False from ansible.module_utils.basic import AnsibleModule def getOvhClient(ansibleModule): endpoint = ansibleModule.params.get("endpoint") application_key = ansibleModule.params.get("application_key") application_secret = ansibleModule.params.get("application_secret") consumer_key = ansibleModule.params.get("consumer_key") return ovh.Client( endpoint=endpoint, application_key=application_key, application_secret=application_secret, consumer_key=consumer_key, ) def waitForNoTask(client, name, timeout): currentTimeout = timeout while len(client.get(f"/ip/loadBalancing/{name}/task")) > 0: time.sleep(1) # Delay for 1 sec currentTimeout -= 1 if currentTimeout < 0: return False return True def main(): module = AnsibleModule( argument_spec=dict( name=dict(required=True), backend=dict(required=True), weight=dict(default=8, type="int"), probe=dict(default="none", choices=["none", "http", "icmp", "oco"]), state=dict(default="present", choices=["present", "absent"]), endpoint=dict(required=True), application_key=dict(required=True, no_log=True), application_secret=dict(required=True, no_log=True), consumer_key=dict(required=True, no_log=True), timeout=dict(default=120, type="int"), ) ) if not HAS_OVH: module.fail_json(msg="ovh-api python module is required to run this module") # Get parameters name = module.params.get("name") state = module.params.get("state") backend = module.params.get("backend") weight = module.params.get("weight") probe = module.params.get("probe") timeout = module.params.get("timeout") # Connect to OVH API client = getOvhClient(module) # Check that the load balancing exists try: loadBalancings = client.get("/ip/loadBalancing") except APIError as apiError: module.fail_json( msg=f"Unable to call OVH API for getting the list of loadBalancing, check application key, secret, consumerkey and parameters. " f"Error returned by OVH API was : {apiError}" ) if name not in loadBalancings: module.fail_json(msg=f"IP LoadBalancing {name} does not exist") # Check that no task is pending before going on try: if not waitForNoTask(client, name, timeout): module.fail_json( msg=f"Timeout of {timeout} seconds while waiting for no pending tasks before executing the module " ) except APIError as apiError: module.fail_json( msg=f"Unable to call OVH API for getting the list of pending tasks of the loadBalancing, check application key, secret, consumerkey and " f"parameters. Error returned by OVH API was : {apiError}" ) try: backends = client.get(f"/ip/loadBalancing/{name}/backend") except APIError as apiError: module.fail_json( msg=( "Unable to call OVH API for getting the list of backends " "of the loadBalancing, check application key, secret, consumerkey " f"and parameters. Error returned by OVH API was : {apiError}" ) ) backendExists = backend in backends moduleChanged = False if state == "absent": if backendExists: # Remove backend try: client.delete(f"/ip/loadBalancing/{name}/backend/{backend}") if not waitForNoTask(client, name, timeout): module.fail_json( msg=f"Timeout of {timeout} seconds while waiting for completion of removing backend task" ) except APIError as apiError: module.fail_json( msg=f"Unable to call OVH API for deleting the backend, check application key, secret, consumerkey and parameters. " f"Error returned by OVH API was : {apiError}" ) moduleChanged = True else: if backendExists: # Get properties try: backendProperties = client.get(f"/ip/loadBalancing/{name}/backend/{backend}") except APIError as apiError: module.fail_json( msg=f"Unable to call OVH API for getting the backend properties, check application key, secret, consumerkey and parameters. " f"Error returned by OVH API was : {apiError}" ) if backendProperties["weight"] != weight: # Change weight try: client.post(f"/ip/loadBalancing/{name}/backend/{backend}/setWeight", weight=weight) if not waitForNoTask(client, name, timeout): module.fail_json( msg=f"Timeout of {timeout} seconds while waiting for completion of setWeight to backend task" ) except APIError as apiError: module.fail_json( msg=f"Unable to call OVH API for updating the weight of the backend, check application key, secret, consumerkey and parameters. " f"Error returned by OVH API was : {apiError}" ) moduleChanged = True if backendProperties["probe"] != probe: # Change probe backendProperties["probe"] = probe try: client.put(f"/ip/loadBalancing/{name}/backend/{backend}", probe=probe) if not waitForNoTask(client, name, timeout): module.fail_json( msg=f"Timeout of {timeout} seconds while waiting for completion of setProbe to backend task" ) except APIError as apiError: module.fail_json( msg=f"Unable to call OVH API for updating the probe of the backend, check application key, secret, consumerkey and parameters. " f"Error returned by OVH API was : {apiError}" ) moduleChanged = True else: # Creates backend try: try: client.post(f"/ip/loadBalancing/{name}/backend", ipBackend=backend, probe=probe, weight=weight) except APIError as apiError: module.fail_json( msg=f"Unable to call OVH API for creating the backend, check application key, secret, consumerkey and parameters. " f"Error returned by OVH API was : {apiError}" ) if not waitForNoTask(client, name, timeout): module.fail_json( msg=f"Timeout of {timeout} seconds while waiting for completion of backend creation task" ) except APIError as apiError: module.fail_json( msg=f"Unable to call OVH API for creating the backend, check application key, secret, consumerkey and parameters. " f"Error returned by OVH API was : {apiError}" ) moduleChanged = True module.exit_json(changed=moduleChanged) if __name__ == "__main__": main()