215 lines
7.7 KiB
Python
215 lines
7.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
# ovh_reverse, an Ansible module for managing OVH DNS reverse
|
|
# Copyright (C) 2014, Carlos Izquierdo <gheesh@gheesh.org>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
from __future__ import print_function
|
|
|
|
DOCUMENTATION = '''
|
|
---
|
|
module: ovh_reverse
|
|
author: Laurent Almeras
|
|
short_description: Manage OVH DNS reverse
|
|
description:
|
|
- Manage OVH (French European hosting provider) DNS reverse
|
|
requirements: [ "ovh" ]
|
|
options:
|
|
ip:
|
|
required: true
|
|
description:
|
|
- IP we want to manage
|
|
reverse:
|
|
required: false
|
|
description:
|
|
- reverse name to associate the IP with; not used if state: absent.
|
|
For state: present and reverse is empty, only checks if a reverse
|
|
exists else triggers a failure
|
|
state:
|
|
required: false
|
|
default: present
|
|
choices: ['present', 'absent']
|
|
description:
|
|
- present or absent: present checks current reverse and update it as
|
|
needed, absent delete reverse record if present.
|
|
'''
|
|
|
|
EXAMPLES = '''
|
|
# Create a reverse
|
|
- ovh_reverse: ip=10.10.10.10 state=present reverse=myhost.mydomain.tld.
|
|
|
|
# Check a reverse exists, else triggers a failure
|
|
- ovh_reverse: ip=10.10.10.10 state=present
|
|
|
|
# Delete a reverse
|
|
- ovh_reverse: ip=10.10.10.10 state=absent
|
|
'''
|
|
|
|
|
|
import sys
|
|
import re
|
|
import yaml
|
|
|
|
try:
|
|
import ovh
|
|
except ImportError:
|
|
print("failed=True msg='ovh required for this module'")
|
|
sys.exit(1)
|
|
|
|
|
|
# TODO: Try to automate this in case the supplied credentials are not valid
|
|
def get_credentials():
|
|
"""This function is used to obtain an authentication token.
|
|
It should only be called once."""
|
|
client = ovh.Client()
|
|
access_rules = [
|
|
{'method': 'GET', 'path': '/domain/*'},
|
|
{'method': 'PUT', 'path': '/domain/*'},
|
|
{'method': 'POST', 'path': '/domain/*'},
|
|
{'method': 'DELETE', 'path': '/domain/*'},
|
|
]
|
|
validation = client.request_consumerkey(access_rules)
|
|
# print("Your consumer key is {}".format(validation['consumerKey']))
|
|
# print("Please visit {} to validate".format(validation['validationUrl']))
|
|
return validation['consumerKey']
|
|
|
|
|
|
def get_reverse(client, ip):
|
|
"""Obtain a reverse"""
|
|
# this url works both with /32 blocks or ip as first parameter
|
|
# may throw an APIError
|
|
|
|
# first check ip management is accessible, throw an ApiError if not
|
|
ip_reverses = client.get('/ip/{}%2F32/reverse'.format(ip))
|
|
if not ip_reverses:
|
|
# if list if empty, ip is manageable but there is no reverse
|
|
return None
|
|
else:
|
|
# if ip is manageable, get reverse information
|
|
# result is a list; only one reverse is expected
|
|
return client.get('/ip/{}%2F32/reverse/{}'.format(ip, ip_reverses[0]))
|
|
|
|
def exc_str(ovh_exception):
|
|
"""__str__ is overloaded in ovh APIError and does not provide any insight.
|
|
Alternative implementation to retrieve first exception parameter.
|
|
"""
|
|
args = getattr(ovh_exception, 'args', None)
|
|
if args:
|
|
return args[0]
|
|
else:
|
|
return str(ovh_exception)
|
|
|
|
|
|
def update_reverse(check_mode, client, ip, original_reverse, reverse, results):
|
|
"""Update a reverse"""
|
|
if original_reverse is None or original_reverse['reverse'] != reverse:
|
|
if original_reverse:
|
|
results['diff']['before'] = original_reverse['reverse'] + "\n"
|
|
updated_reverse = None
|
|
results['diff']['before'] = original_reverse['reverse'] + "\n" if original_reverse else "\n"
|
|
if not check_mode:
|
|
client.post('/ip/{}%2F32/reverse'.format(ip), ipReverse=ip, reverse=reverse)
|
|
updated_reverse = get_reverse(client, ip)
|
|
results['reverse'] = updated_reverse
|
|
results['msg'] = 'IP reverse for {} updated from {} to {}'.format(ip, original_reverse['reverse'] if original_reverse else '<none>', updated_reverse['reverse'])
|
|
results['diff']['after'] = updated_reverse['reverse'] + "\n"
|
|
else:
|
|
results['reverse'] = None
|
|
results['msg'] = 'IP reverse for {} needs to be updated from {} to {}'.format(ip, original_reverse['reverse'] if original_reverse else '<none>', reverse)
|
|
results['diff']['after'] = reverse + "\n"
|
|
results['changed'] = True
|
|
else:
|
|
results['msg'] = 'IP reverse for {} already set to {}'.format(ip, reverse)
|
|
|
|
|
|
def main():
|
|
module = AnsibleModule(
|
|
argument_spec=dict(
|
|
ip=dict(required=True),
|
|
reverse=dict(required=False),
|
|
state=dict(default='present', choices=['present', 'absent'])
|
|
),
|
|
supports_check_mode=True
|
|
)
|
|
results = dict(
|
|
changed=False,
|
|
msg='',
|
|
original_reverse=None,
|
|
reverse=None,
|
|
diff={},
|
|
failed=False
|
|
)
|
|
response = []
|
|
|
|
# Get parameters
|
|
ip = module.params.get('ip')
|
|
reverse = module.params.get('reverse')
|
|
state = module.params.get('state')
|
|
|
|
# Connect to OVH API
|
|
client = ovh.Client()
|
|
|
|
# Check that the domain exists
|
|
original_reverse = None
|
|
try:
|
|
original_reverse = get_reverse(client, ip)
|
|
results['original_reverse'] = original_reverse
|
|
results['reverse'] = original_reverse
|
|
except Exception as e:
|
|
module.fail_json(msg='IP reverse for {} does not seem to be manageable: {}.'.format(ip, exc_str(e)))
|
|
|
|
try:
|
|
if state == 'present':
|
|
if reverse:
|
|
# we have a value to check and set
|
|
update_reverse(module.check_mode, client, ip, original_reverse, reverse, results)
|
|
else:
|
|
# we only check if reverse is set (whatever value it is)
|
|
if original_reverse is None:
|
|
# no reverse to set and no reverse, failure
|
|
results['msg'] = 'No IP reverse for {} and not reverse provided. Failure.'.format(ip)
|
|
results['failed'] = True
|
|
else:
|
|
results['msg'] = 'IP reverse record for {} is set to {}.'.format(ip, original_reverse['reverse'])
|
|
elif state == 'absent' and original_reverse is None:
|
|
results['msg'] = 'IP reverse record for {} is absent.'.format(ip)
|
|
elif state == 'absent' and not original_reverse is None:
|
|
if not module.check_mode:
|
|
client.delete('/ip/{}%2F32/reverse/{}'.format(ip, original_reverse['ipReverse']))
|
|
results['msg'] = 'IP reverse record for {} deleted.'.format(ip)
|
|
else:
|
|
results['msg'] = 'IP reverse record for {} needs to be deleted.'.format(ip)
|
|
results['diff']['before'] = original_reverse['reverse'] + "\n"
|
|
results['diff']['after'] = "\n"
|
|
results['changed'] = True
|
|
results['reverse'] = None
|
|
|
|
failed = results['failed']
|
|
results.pop('failed')
|
|
if failed:
|
|
module.fail_json(**results)
|
|
else:
|
|
module.exit_json(**results)
|
|
except Exception as e:
|
|
module.fail_json(msg='IP reverse for {} fails during update: {}.'.format(ip, exc_str(e)))
|
|
|
|
|
|
|
|
# import module snippets
|
|
from ansible.module_utils.basic import *
|
|
|
|
main()
|