2020-03-09 10:11:07 +01:00
|
|
|
#!/usr/bin/python
|
2021-08-08 10:40:22 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
2020-03-09 10:11:07 +01:00
|
|
|
|
2022-08-05 12:28:29 +02:00
|
|
|
# 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
|
2020-03-09 10:11:07 +01:00
|
|
|
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
__metaclass__ = type
|
|
|
|
|
|
|
|
|
|
|
|
DOCUMENTATION = '''
|
|
|
|
---
|
|
|
|
module: ovh_ip_failover
|
|
|
|
short_description: Manage OVH IP failover address
|
|
|
|
description:
|
|
|
|
- Manage OVH (French European hosting provider) IP Failover Address. For now, this module can only be used to move
|
|
|
|
an ip failover (or failover block) between services
|
|
|
|
author: "Pascal HERAUD (@pascalheraud)"
|
|
|
|
notes:
|
|
|
|
- Uses the python OVH Api U(https://github.com/ovh/python-ovh).
|
2023-10-25 23:01:32 +02:00
|
|
|
You have to create an application (a key and secret) with a consumer
|
2020-03-09 10:11:07 +01:00
|
|
|
key as described into U(https://docs.ovh.com/gb/en/customer/first-steps-with-ovh-api/)
|
|
|
|
requirements:
|
|
|
|
- ovh >= 0.4.8
|
2023-02-24 09:21:43 +01:00
|
|
|
extends_documentation_fragment:
|
|
|
|
- community.general.attributes
|
|
|
|
attributes:
|
|
|
|
check_mode:
|
|
|
|
support: full
|
|
|
|
diff_mode:
|
|
|
|
support: none
|
2020-03-09 10:11:07 +01:00
|
|
|
options:
|
|
|
|
name:
|
|
|
|
required: true
|
|
|
|
description:
|
|
|
|
- The IP address to manage (can be a single IP like 1.1.1.1
|
|
|
|
or a block like 1.1.1.1/28 )
|
2020-11-23 09:22:30 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
service:
|
|
|
|
required: true
|
|
|
|
description:
|
|
|
|
- The name of the OVH service this IP address should be routed
|
2020-11-23 09:22:30 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
endpoint:
|
|
|
|
required: true
|
|
|
|
description:
|
|
|
|
- The endpoint to use ( for instance ovh-eu)
|
2020-11-23 09:22:30 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
wait_completion:
|
|
|
|
required: false
|
|
|
|
default: true
|
|
|
|
type: bool
|
|
|
|
description:
|
|
|
|
- If true, the module will wait for the IP address to be moved.
|
|
|
|
If false, exit without waiting. The taskId will be returned
|
|
|
|
in module output
|
|
|
|
wait_task_completion:
|
|
|
|
required: false
|
|
|
|
default: 0
|
|
|
|
description:
|
|
|
|
- If not 0, the module will wait for this task id to be
|
|
|
|
completed. Use wait_task_completion if you want to wait for
|
|
|
|
completion of a previously executed task with
|
|
|
|
wait_completion=false. You can execute this module repeatedly on
|
|
|
|
a list of failover IPs using wait_completion=false (see examples)
|
2020-11-23 09:22:30 +01:00
|
|
|
type: int
|
2020-03-09 10:11:07 +01:00
|
|
|
application_key:
|
|
|
|
required: true
|
|
|
|
description:
|
|
|
|
- The applicationKey to use
|
2020-11-23 09:22:30 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
application_secret:
|
|
|
|
required: true
|
|
|
|
description:
|
|
|
|
- The application secret to use
|
2020-11-23 09:22:30 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
consumer_key:
|
|
|
|
required: true
|
|
|
|
description:
|
|
|
|
- The consumer key to use
|
2020-11-23 09:22:30 +01:00
|
|
|
type: str
|
2020-03-09 10:11:07 +01:00
|
|
|
timeout:
|
|
|
|
required: false
|
|
|
|
default: 120
|
|
|
|
description:
|
|
|
|
- The timeout in seconds used to wait for a task to be
|
|
|
|
completed. Default is 120 seconds.
|
2020-11-23 09:22:30 +01:00
|
|
|
type: int
|
2020-03-09 10:11:07 +01:00
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
EXAMPLES = '''
|
|
|
|
# Route an IP address 1.1.1.1 to the service ns666.ovh.net
|
2020-07-13 21:50:31 +02:00
|
|
|
- community.general.ovh_ip_failover:
|
2020-03-09 10:11:07 +01:00
|
|
|
name: 1.1.1.1
|
|
|
|
service: ns666.ovh.net
|
|
|
|
endpoint: ovh-eu
|
|
|
|
application_key: yourkey
|
|
|
|
application_secret: yoursecret
|
|
|
|
consumer_key: yourconsumerkey
|
2020-07-13 21:50:31 +02:00
|
|
|
- community.general.ovh_ip_failover:
|
2020-03-09 10:11:07 +01:00
|
|
|
name: 1.1.1.1
|
|
|
|
service: ns666.ovh.net
|
|
|
|
endpoint: ovh-eu
|
|
|
|
wait_completion: false
|
|
|
|
application_key: yourkey
|
|
|
|
application_secret: yoursecret
|
|
|
|
consumer_key: yourconsumerkey
|
|
|
|
register: moved
|
2020-07-13 21:50:31 +02:00
|
|
|
- community.general.ovh_ip_failover:
|
2020-03-09 10:11:07 +01:00
|
|
|
name: 1.1.1.1
|
|
|
|
service: ns666.ovh.net
|
|
|
|
endpoint: ovh-eu
|
|
|
|
wait_task_completion: "{{moved.taskId}}"
|
|
|
|
application_key: yourkey
|
|
|
|
application_secret: yoursecret
|
|
|
|
consumer_key: yourconsumerkey
|
|
|
|
'''
|
|
|
|
|
|
|
|
RETURN = '''
|
|
|
|
'''
|
|
|
|
|
|
|
|
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
|
|
|
|
from ansible.module_utils.six.moves.urllib.parse import quote_plus
|
|
|
|
|
|
|
|
|
|
|
|
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 client.get('/ip/{0}/task'.format(quote_plus(name)),
|
|
|
|
function='genericMoveFloatingIp',
|
|
|
|
status='todo'):
|
|
|
|
time.sleep(1) # Delay for 1 sec
|
|
|
|
currentTimeout -= 1
|
|
|
|
if currentTimeout < 0:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def waitForTaskDone(client, name, taskId, timeout):
|
|
|
|
currentTimeout = timeout
|
|
|
|
while True:
|
|
|
|
task = client.get('/ip/{0}/task/{1}'.format(quote_plus(name), taskId))
|
|
|
|
if task['status'] == 'done':
|
|
|
|
return True
|
|
|
|
time.sleep(5) # Delay for 5 sec because it's long to wait completion, do not harass the API
|
|
|
|
currentTimeout -= 5
|
|
|
|
if currentTimeout < 0:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
module = AnsibleModule(
|
|
|
|
argument_spec=dict(
|
|
|
|
name=dict(required=True),
|
|
|
|
service=dict(required=True),
|
|
|
|
endpoint=dict(required=True),
|
|
|
|
wait_completion=dict(default=True, type='bool'),
|
|
|
|
wait_task_completion=dict(default=0, type='int'),
|
|
|
|
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')
|
|
|
|
),
|
|
|
|
supports_check_mode=True
|
|
|
|
)
|
|
|
|
|
|
|
|
result = dict(
|
|
|
|
changed=False
|
|
|
|
)
|
|
|
|
|
|
|
|
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')
|
|
|
|
service = module.params.get('service')
|
|
|
|
timeout = module.params.get('timeout')
|
|
|
|
wait_completion = module.params.get('wait_completion')
|
|
|
|
wait_task_completion = module.params.get('wait_task_completion')
|
|
|
|
|
|
|
|
# Connect to OVH API
|
|
|
|
client = getOvhClient(module)
|
|
|
|
|
|
|
|
# Check that the load balancing exists
|
|
|
|
try:
|
|
|
|
ips = client.get('/ip', ip=name, type='failover')
|
|
|
|
except APIError as apiError:
|
|
|
|
module.fail_json(
|
|
|
|
msg='Unable to call OVH api for getting the list of ips, '
|
|
|
|
'check application key, secret, consumerkey and parameters. '
|
|
|
|
'Error returned by OVH api was : {0}'.format(apiError))
|
|
|
|
|
|
|
|
if name not in ips and '{0}/32'.format(name) not in ips:
|
|
|
|
module.fail_json(msg='IP {0} does not exist'.format(name))
|
|
|
|
|
|
|
|
# Check that no task is pending before going on
|
|
|
|
try:
|
|
|
|
if not waitForNoTask(client, name, timeout):
|
|
|
|
module.fail_json(
|
|
|
|
msg='Timeout of {0} seconds while waiting for no pending '
|
|
|
|
'tasks before executing the module '.format(timeout))
|
|
|
|
except APIError as apiError:
|
|
|
|
module.fail_json(
|
|
|
|
msg='Unable to call OVH api for getting the list of pending tasks '
|
|
|
|
'of the ip, check application key, secret, consumerkey '
|
|
|
|
'and parameters. Error returned by OVH api was : {0}'
|
|
|
|
.format(apiError))
|
|
|
|
|
|
|
|
try:
|
|
|
|
ipproperties = client.get('/ip/{0}'.format(quote_plus(name)))
|
|
|
|
except APIError as apiError:
|
|
|
|
module.fail_json(
|
|
|
|
msg='Unable to call OVH api for getting the properties '
|
|
|
|
'of the ip, check application key, secret, consumerkey '
|
|
|
|
'and parameters. Error returned by OVH api was : {0}'
|
|
|
|
.format(apiError))
|
|
|
|
|
|
|
|
if ipproperties['routedTo']['serviceName'] != service:
|
|
|
|
if not module.check_mode:
|
|
|
|
if wait_task_completion == 0:
|
|
|
|
# Move the IP and get the created taskId
|
|
|
|
task = client.post('/ip/{0}/move'.format(quote_plus(name)), to=service)
|
|
|
|
taskId = task['taskId']
|
|
|
|
result['moved'] = True
|
|
|
|
else:
|
|
|
|
# Just wait for the given taskId to be completed
|
|
|
|
taskId = wait_task_completion
|
|
|
|
result['moved'] = False
|
|
|
|
result['taskId'] = taskId
|
|
|
|
if wait_completion or wait_task_completion != 0:
|
|
|
|
if not waitForTaskDone(client, name, taskId, timeout):
|
|
|
|
module.fail_json(
|
|
|
|
msg='Timeout of {0} seconds while waiting for completion '
|
|
|
|
'of move ip to service'.format(timeout))
|
|
|
|
result['waited'] = True
|
|
|
|
else:
|
|
|
|
result['waited'] = False
|
|
|
|
result['changed'] = True
|
|
|
|
|
|
|
|
module.exit_json(**result)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|