first commit

This commit is contained in:
yohan 2024-06-15 07:17:31 +02:00
commit 4bf73689b4
5 changed files with 464 additions and 0 deletions

38
README.md Normal file
View File

@ -0,0 +1,38 @@
Role Name
=========
A brief description of the role goes here.
Requirements
------------
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
Role Variables
--------------
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
Dependencies
------------
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
Example Playbook
----------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
- hosts: servers
roles:
- { role: username.rolename, x: 42 }
License
-------
BSD
Author Information
------------------
An optional section for the role authors to include contact information, or a website (HTML is not allowed).

352
library/ovh_dns.py Normal file
View File

@ -0,0 +1,352 @@
# -*- coding: utf-8 -*-
# ovh_dns, an Ansible module for managing OVH DNS records
# 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_dns
author: Carlos Izquierdo
short_description: Manage OVH DNS records
description:
- Manage OVH (French European hosting provider) DNS records
requirements: [ "ovh" ]
options:
create:
required: false
description:
- If 'state' == 'present' and 'replace' is not empty then create the record
domain:
required: true
description:
- Name of the domain zone
name:
required: true
description:
- Name of the DNS record
value:
required: true if present/append
description:
- Value of the DNS record (i.e. what it points to)
- If None with 'present' then deletes ALL records at 'name'
removes:
required: false
description:
- specifies a regex pattern to match for bulk deletion
replace:
required: true if present and multi records found
- Old value of the DNS record (i.e. what it points to now)
- Accept regex
ttl:
required: false
description:
- value of record TTL value in seconds (defaults to 3600)
type:
required: true if present/append
choices: ['A', 'AAAA', 'CAA', 'CNAME', 'DKIM', 'LOC', 'MX', 'NAPTR', 'NS', 'PTR', 'SPF', 'SRV', 'SSHFP', 'TLSA', 'TXT']
description:
- Type of DNS record (A, AAAA, PTR, CNAME, etc.)
state:
required: false
default: present
choices: ['present', 'absent', 'append']
description:
- Determines wether the record is to be created/modified or deleted
'''
EXAMPLES = '''
# Create a typical A record
- ovh_dns: state=present domain=mydomain.com name=db1 value=10.10.10.10
# Create a CNAME record
- ovh_dns: state=present domain=mydomain.com name=dbprod type=cname value=db1
# Delete an existing record, must specify all parameters
- ovh_dns: state=absent domain=mydomain.com name=dbprod type=cname value=db1
# Delete all TXT records matching '^_acme-challenge.*$' regex
- ovh_dns: state=absent domain=mydomain.com name='' type=TXT removes='^_acme-challenge.*'
'''
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_domain_records(client, domain, fieldtype=None, subDomain=None):
"""Obtain all records for a specific domain"""
records = {}
params = {}
# List all ids and then get info for each one
if subDomain is not None:
params['subDomain'] = subDomain
if fieldtype is not None:
params['fieldType'] = fieldtype
record_ids = client.get('/domain/zone/{}/record'.format(domain),
**params)
for record_id in record_ids:
info = client.get('/domain/zone/{}/record/{}'.format(domain, record_id))
records[record_id] = info
return records
def count_type(records, fieldtype=['A', 'AAAA']):
i = 0
for id in records:
if records[id]['fieldType'] in fieldtype:
i+=1
return i
def main():
module = AnsibleModule(
argument_spec=dict(
domain=dict(required=True),
name=dict(required=True),
state=dict(default='present', choices=['present', 'absent', 'append']),
type=dict(default=None, choices=['A', 'AAAA', 'CNAME', 'CAA', 'DKIM', 'LOC', 'MX', 'NAPTR', 'NS', 'PTR', 'SPF', 'SRV', 'SSHFP', 'TXT', 'TLSA']),
removes=dict(default=None),
replace=dict(default=None),
value=dict(default=None),
create=dict(default=False, type='bool'),
ttl=dict(default=3600, type='int'),
),
supports_check_mode=True
)
results = dict(
changed=False,
msg='',
records='',
response='',
original_message=module.params['name'],
diff={}
)
response = []
# Get parameters
domain = module.params.get('domain')
name = module.params.get('name')
state = module.params.get('state')
fieldtype = module.params.get('type')
targetval = module.params.get('value')
removes = module.params.get('removes')
ttlval = module.params.get('ttl')
oldtargetval = module.params.get('replace')
create = module.params.get('create')
# Connect to OVH API
client = ovh.Client()
# Check that the domain exists
domains = client.get('/domain/zone')
if domain not in domains:
module.fail_json(msg='Domain {} does not exist'.format(domain))
# Obtain all domain records to check status against what is demanded
records = get_domain_records(client, domain, fieldtype, name)
# Remove a record(s)
if state == 'absent':
if len(name) == 0 and not removes:
module.fail_json(msg='wildcard delete not allowed')
if not records:
module.exit_json(changed=False)
# Delete same target
rn = None
rv = None
if removes:
rn = re.compile(removes, re.IGNORECASE)
else:
rn = re.compile("^{}$".format(name), re.IGNORECASE)
if targetval:
rv = re.compile(targetval, re.IGNORECASE)
else:
rv = re.compile(r'.*')
tmprecords = records.copy()
for id in records:
if not rn.match(records[id]['subDomain']) or not rv.match(records[id]['target']):
tmprecords.pop(id)
records = tmprecords
results['delete'] = records
if records:
before_records=[]
# Remove the ALL record
for id in records:
before_records.append(dict(
domain=domain,
fieldType=records[id]['fieldType'],
subDomain=records[id]['subDomain'],
target=records[id]['target'],
ttl=records[id]['ttl'],
))
if not module.check_mode:
client.delete('/domain/zone/{}/record/{}'.format(domain, id))
if not module.check_mode:
client.post('/domain/zone/{}/refresh'.format(domain))
results['changed'] = True
results['diff']['before'] = yaml.dump(before_records)
results['diff']['after'] = ''
module.exit_json(**results)
# Add / modify a record
elif state in ['present', 'append']:
# Since we are inserting a record, we need a target
if targetval is None:
module.fail_json(msg='Did not specify a value')
if fieldtype is None:
module.fail_json(msg='Did not specify a type')
# Does the record exist already? Yes
if records:
for id in records:
if records[id]['target'].lower() == targetval.lower() and records[id]['ttl'] == ttlval:
# The record is already as requested, no need to change anything
module.exit_json(changed=False)
# list records modify in end
oldrecords = {}
if state == 'present':
if oldtargetval:
r = re.compile(oldtargetval, re.IGNORECASE)
for id in records:
# update target
if oldtargetval:
if re.match(r, records[id]['target']):
oldrecords.update({id: records[id]})
# uniq update
else:
oldrecords.update({id: records[id]})
if oldtargetval and not oldrecords and not create:
module.fail_json(msg='Old record not match, use append ?')
if oldrecords:
before_records = []
# FIXME: check if all records as same fieldType not A/AAAA and CNAME
# if fieldtype in ['A', 'AAAA', 'CNAME']:
# oldA = count_type(records)
# oldC = count_type(records, 'CNAME')
# newA = count_type(oldrecords)
# newC = count_type(oldrecords, 'CNAME')
# check = True
# if oldA > 0 and newC > 0 and oldA != newC:
# check = False
# if oldC > 0 and newA > 0 and oldC != newA:
# check = False
# if not check:
# module.fail_json(msg='The subdomain already uses a DNS record. You can not register a {} field because of an incompatibility.'.format(fieldType))
# Delete all records and re-create the record
newrecord = dict(
fieldType=fieldtype,
subDomain=name,
target=targetval,
ttl=ttlval
)
for id in oldrecords:
before_records.append(dict(
domain=domain,
fieldType=oldrecords[id]['fieldType'],
subDomain=oldrecords[id]['subDomain'],
target=oldrecords[id]['target'],
ttl=oldrecords[id]['ttl'],
))
if not module.check_mode:
client.delete('/domain/zone/{}/record/{}'.format(domain, id))
if not module.check_mode:
response.append({'delete': oldrecords})
res = client.post('/domain/zone/{}/record'.format(domain), **newrecord)
response.append(res)
# Refresh the zone and exit
client.post('/domain/zone/{}/refresh'.format(domain))
results['response'] = response
results['diff']['before'] = yaml.dump(before_records)
after = [newrecord]
after[0]['domain'] = domain
results['diff']['after'] = yaml.dump(after)
results['changed'] = True
module.exit_json(**results)
# end records exist
# Add record
if state == 'append' or not records:
newrecord = dict(
fieldType=fieldtype,
subDomain=name,
target=targetval,
ttl=ttlval
)
if not module.check_mode:
# Add the record
res = client.post('/domain/zone/{}/record'.format(domain), **newrecord)
response.append(res)
client.post('/domain/zone/{}/refresh'.format(domain))
results['diff']['before'] = ''
after = dict(newrecord)
after['domain'] = domain
results['diff']['after'] = yaml.dump(after)
results['changed'] = True
results['response'] = response
module.exit_json(**results)
# end state == 'present'
# We should never reach here
results['msg'] = 'Internal ovh_dns module error'
module.fail_json(**results)
# import module snippets
from ansible.module_utils.basic import *
main()

52
meta/main.yml Normal file
View File

@ -0,0 +1,52 @@
galaxy_info:
author: your name
description: your role description
company: your company (optional)
# If the issue tracker for your role is not on github, uncomment the
# next line and provide a value
# issue_tracker_url: http://example.com/issue/tracker
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
# - MIT
# - GPL-2.0-or-later
# - GPL-3.0-only
# - Apache-2.0
# - CC-BY-4.0
license: license (GPL-2.0-or-later, MIT, etc)
min_ansible_version: 2.1
# If this a Container Enabled role, provide the minimum Ansible Container version.
# min_ansible_container_version:
#
# Provide a list of supported platforms, and for each platform a list of versions.
# If you don't wish to enumerate all versions for a particular platform, use 'all'.
# To view available platforms and versions (or releases), visit:
# https://galaxy.ansible.com/api/v1/platforms/
#
# platforms:
# - name: Fedora
# versions:
# - all
# - 25
# - name: SomePlatform
# versions:
# - all
# - 1.0
# - 7
# - 99.99
galaxy_tags: []
# List tags for your role here, one per line. A tag is a keyword that describes
# and categorizes the role. Users find roles by searching for tags. Be sure to
# remove the '[]' above, if you add tags to this list.
#
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
# Maximum 20 tags per role.
dependencies: []
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.

20
tasks/main.yml Normal file
View File

@ -0,0 +1,20 @@
---
# tasks file for role_add_ovh_dns_record
- name: install ovh python module
ansible.builtin.pip:
name:
- ovh
- name: Create A server record
ovh_dns:
state: present
domain: "{{ DOMAIN }}"
name: "{{ NAME }}"
type: "{{ TYPE }}"
value: "{{ VALUE }}"
environment:
OVH_ENDPOINT: ovh-eu
OVH_APPLICATION_KEY: "{{ OVH_APPLICATION_KEY }}"
OVH_APPLICATION_SECRET: "{{ OVH_APPLICATION_SECRET }}"
OVH_CONSUMER_KEY: "{{ OVH_DNS_CONSUMER_KEY }}"

2
vars/main.yml Normal file
View File

@ -0,0 +1,2 @@
---
# vars file for role_add_ovh_dns_record