1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

dedicated collection for exoscale.com related plugins (#519)

This commit is contained in:
René Moser 2020-06-16 19:19:06 +02:00 committed by GitHub
parent e5bb9dab9d
commit ffd107db4d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 0 additions and 744 deletions

2
.github/BOTMETA.yml vendored
View file

@ -713,8 +713,6 @@ files:
authors: drcapulet authors: drcapulet
$modules/net_tools/dnsmadeeasy.py: $modules/net_tools/dnsmadeeasy.py:
authors: briceburg authors: briceburg
$modules/net_tools/exoscale/:
authors: resmo
$modules/net_tools/haproxy.py: $modules/net_tools/haproxy.py:
authors: ravibhure authors: ravibhure
$modules/net_tools/: $modules/net_tools/:

View file

@ -1,56 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class ModuleDocFragment(object):
# Standard exoscale documentation fragment
DOCUMENTATION = r'''
options:
api_key:
description:
- API key of the Exoscale DNS API.
- Since 2.4, the ENV variable C(CLOUDSTACK_KEY) is used as default, when defined.
type: str
api_secret:
description:
- Secret key of the Exoscale DNS API.
- Since 2.4, the ENV variable C(CLOUDSTACK_SECRET) is used as default, when defined.
type: str
api_timeout:
description:
- HTTP timeout to Exoscale DNS API.
- Since 2.4, the ENV variable C(CLOUDSTACK_TIMEOUT) is used as default, when defined.
type: int
default: 10
api_region:
description:
- Name of the ini section in the C(cloustack.ini) file.
- Since 2.4, the ENV variable C(CLOUDSTACK_REGION) is used as default, when defined.
type: str
default: cloudstack
validate_certs:
description:
- Validate SSL certs of the Exoscale DNS API.
type: bool
default: yes
requirements:
- python >= 2.6
notes:
- As Exoscale DNS uses the same API key and secret for all services, we reuse the config used for Exscale Compute based on CloudStack.
The config is read from several locations, in the following order.
The C(CLOUDSTACK_KEY), C(CLOUDSTACK_SECRET) environment variables.
A C(CLOUDSTACK_CONFIG) environment variable pointing to an C(.ini) file,
A C(cloudstack.ini) file in the current working directory.
A C(.cloudstack.ini) file in the users home directory.
Optionally multiple credentials and endpoints can be specified using ini sections in C(cloudstack.ini).
Use the argument C(api_region) to select the section name, default section is C(cloudstack).
- This module does not support multiple A records and will complain properly if you try.
- More information Exoscale DNS can be found on https://community.exoscale.ch/documentation/dns/.
- This module supports check mode and diff.
'''

View file

@ -1,139 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2016, René Moser <mail@renemoser.net>
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os
from ansible.module_utils.six.moves import configparser
from ansible.module_utils.six import integer_types, string_types
from ansible.module_utils._text import to_native, to_text
from ansible.module_utils.urls import fetch_url
EXO_DNS_BASEURL = "https://api.exoscale.ch/dns/v1"
def exo_dns_argument_spec():
return dict(
api_key=dict(default=os.environ.get('CLOUDSTACK_KEY'), no_log=True),
api_secret=dict(default=os.environ.get('CLOUDSTACK_SECRET'), no_log=True),
api_timeout=dict(type='int', default=os.environ.get('CLOUDSTACK_TIMEOUT') or 10),
api_region=dict(default=os.environ.get('CLOUDSTACK_REGION') or 'cloudstack'),
validate_certs=dict(default=True, type='bool'),
)
def exo_dns_required_together():
return [['api_key', 'api_secret']]
class ExoDns(object):
def __init__(self, module):
self.module = module
self.api_key = self.module.params.get('api_key')
self.api_secret = self.module.params.get('api_secret')
if not (self.api_key and self.api_secret):
try:
region = self.module.params.get('api_region')
config = self.read_config(ini_group=region)
self.api_key = config['key']
self.api_secret = config['secret']
except Exception as e:
self.module.fail_json(msg="Error while processing config: %s" % to_native(e))
self.headers = {
'X-DNS-Token': "%s:%s" % (self.api_key, self.api_secret),
'Content-Type': 'application/json',
'Accept': 'application/json',
}
self.result = {
'changed': False,
'diff': {
'before': {},
'after': {},
}
}
def read_config(self, ini_group=None):
if not ini_group:
ini_group = os.environ.get('CLOUDSTACK_REGION', 'cloudstack')
keys = ['key', 'secret']
env_conf = {}
for key in keys:
if 'CLOUDSTACK_%s' % key.upper() not in os.environ:
break
else:
env_conf[key] = os.environ['CLOUDSTACK_%s' % key.upper()]
else:
return env_conf
# Config file: $PWD/cloudstack.ini or $HOME/.cloudstack.ini
# Last read wins in configparser
paths = (
os.path.join(os.path.expanduser('~'), '.cloudstack.ini'),
os.path.join(os.getcwd(), 'cloudstack.ini'),
)
# Look at CLOUDSTACK_CONFIG first if present
if 'CLOUDSTACK_CONFIG' in os.environ:
paths += (os.path.expanduser(os.environ['CLOUDSTACK_CONFIG']),)
if not any([os.path.exists(c) for c in paths]):
self.module.fail_json(msg="Config file not found. Tried : %s" % ", ".join(paths))
conf = configparser.ConfigParser()
conf.read(paths)
return dict(conf.items(ini_group))
def api_query(self, resource="/domains", method="GET", data=None):
url = EXO_DNS_BASEURL + resource
if data:
data = self.module.jsonify(data)
response, info = fetch_url(
module=self.module,
url=url,
data=data,
method=method,
headers=self.headers,
timeout=self.module.params.get('api_timeout'),
)
if info['status'] not in (200, 201, 204):
self.module.fail_json(msg="%s returned %s, with body: %s" % (url, info['status'], info['msg']))
try:
return self.module.from_json(to_text(response.read()))
except Exception as e:
self.module.fail_json(msg="Could not process response into json: %s" % to_native(e))
def has_changed(self, want_dict, current_dict, only_keys=None):
changed = False
for key, value in want_dict.items():
# Optionally limit by a list of keys
if only_keys and key not in only_keys:
continue
# Skip None values
if value is None:
continue
if key in current_dict:
if isinstance(current_dict[key], integer_types):
if value != current_dict[key]:
self.result['diff']['before'][key] = current_dict[key]
self.result['diff']['after'][key] = value
changed = True
elif isinstance(current_dict[key], string_types):
if value.lower() != current_dict[key].lower():
self.result['diff']['before'][key] = current_dict[key]
self.result['diff']['after'][key] = value
changed = True
else:
self.module.fail_json(msg="Unable to determine comparison for key %s" % key)
else:
self.result['diff']['after'][key] = value
changed = True
return changed

View file

@ -1 +0,0 @@
./net_tools/exoscale/exo_dns_domain.py

View file

@ -1 +0,0 @@
./net_tools/exoscale/exo_dns_record.py

View file

@ -1,205 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: exo_dns_domain
short_description: Manages domain records on Exoscale DNS API.
description:
- Create and remove domain records.
author: "René Moser (@resmo)"
options:
name:
description:
- Name of the record.
required: true
type: str
state:
description:
- State of the resource.
default: present
choices: [ present, absent ]
type: str
extends_documentation_fragment:
- community.general.exoscale
'''
EXAMPLES = '''
- name: Create a domain
exo_dns_domain:
name: example.com
- name: Remove a domain
exo_dns_domain:
name: example.com
state: absent
'''
RETURN = '''
---
exo_dns_domain:
description: API domain results
returned: success
type: complex
contains:
account_id:
description: Your account ID
returned: success
type: int
sample: 34569
auto_renew:
description: Whether domain is auto renewed or not
returned: success
type: bool
sample: false
created_at:
description: When the domain was created
returned: success
type: str
sample: "2016-08-12T15:24:23.989Z"
expires_on:
description: When the domain expires
returned: success
type: str
sample: "2016-08-12T15:24:23.989Z"
id:
description: ID of the domain
returned: success
type: int
sample: "2016-08-12T15:24:23.989Z"
lockable:
description: Whether the domain is lockable or not
returned: success
type: bool
sample: true
name:
description: Domain name
returned: success
type: str
sample: example.com
record_count:
description: Number of records related to this domain
returned: success
type: int
sample: 5
registrant_id:
description: ID of the registrant
returned: success
type: int
sample: null
service_count:
description: Number of services
returned: success
type: int
sample: 0
state:
description: State of the domain
returned: success
type: str
sample: "hosted"
token:
description: Token
returned: success
type: str
sample: "r4NzTRp6opIeFKfaFYvOd6MlhGyD07jl"
unicode_name:
description: Domain name as unicode
returned: success
type: str
sample: "example.com"
updated_at:
description: When the domain was updated last.
returned: success
type: str
sample: "2016-08-12T15:24:23.989Z"
user_id:
description: ID of the user
returned: success
type: int
sample: null
whois_protected:
description: Whether the whois is protected or not
returned: success
type: bool
sample: false
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.exoscale import ExoDns, exo_dns_argument_spec, exo_dns_required_together
class ExoDnsDomain(ExoDns):
def __init__(self, module):
super(ExoDnsDomain, self).__init__(module)
self.name = self.module.params.get('name').lower()
def get_domain(self):
domains = self.api_query("/domains", "GET")
for z in domains:
if z['domain']['name'].lower() == self.name:
return z
return None
def present_domain(self):
domain = self.get_domain()
data = {
'domain': {
'name': self.name,
}
}
if not domain:
self.result['diff']['after'] = data['domain']
self.result['changed'] = True
if not self.module.check_mode:
domain = self.api_query("/domains", "POST", data)
return domain
def absent_domain(self):
domain = self.get_domain()
if domain:
self.result['diff']['before'] = domain
self.result['changed'] = True
if not self.module.check_mode:
self.api_query("/domains/%s" % domain['domain']['name'], "DELETE")
return domain
def get_result(self, resource):
if resource:
self.result['exo_dns_domain'] = resource['domain']
return self.result
def main():
argument_spec = exo_dns_argument_spec()
argument_spec.update(dict(
name=dict(type='str', required=True),
state=dict(type='str', choices=['present', 'absent'], default='present'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=exo_dns_required_together(),
supports_check_mode=True
)
exo_dns_domain = ExoDnsDomain(module)
if module.params.get('state') == "present":
resource = exo_dns_domain.present_domain()
else:
resource = exo_dns_domain.absent_domain()
result = exo_dns_domain.get_result(resource)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,340 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
---
module: exo_dns_record
short_description: Manages DNS records on Exoscale DNS.
description:
- Create, update and delete records.
author: "René Moser (@resmo)"
options:
name:
description:
- Name of the record.
default: ""
type: str
domain:
description:
- Domain the record is related to.
required: true
type: str
record_type:
description:
- Type of the record.
default: A
choices: [ A, ALIAS, CNAME, MX, SPF, URL, TXT, NS, SRV, NAPTR, PTR, AAAA, SSHFP, HINFO, POOL ]
aliases: [ rtype, type ]
type: str
content:
description:
- Content of the record.
- Required if C(state=present) or C(multiple=yes).
aliases: [ value, address ]
type: str
ttl:
description:
- TTL of the record in seconds.
default: 3600
type: int
prio:
description:
- Priority of the record.
aliases: [ priority ]
type: int
multiple:
description:
- Whether there are more than one records with similar I(name) and I(record_type).
- Only allowed for a few record types, e.g. C(record_type=A), C(record_type=NS) or C(record_type=MX).
- I(content) will not be updated, instead it is used as a key to find existing records.
type: bool
default: no
state:
description:
- State of the record.
default: present
choices: [ present, absent ]
type: str
extends_documentation_fragment:
- community.general.exoscale
'''
EXAMPLES = '''
- name: Create or update an A record
exo_dns_record:
name: web-vm-1
domain: example.com
content: 1.2.3.4
- name: Update an existing A record with a new IP
exo_dns_record:
name: web-vm-1
domain: example.com
content: 1.2.3.5
- name: Create another A record with same name
exo_dns_record:
name: web-vm-1
domain: example.com
content: 1.2.3.6
multiple: yes
- name: Create or update a CNAME record
exo_dns_record:
name: www
domain: example.com
record_type: CNAME
content: web-vm-1
- name: Create another MX record
exo_dns_record:
domain: example.com
record_type: MX
content: mx1.example.com
prio: 10
multiple: yes
- name: Delete one MX record out of multiple
exo_dns_record:
domain: example.com
record_type: MX
content: mx1.example.com
multiple: yes
state: absent
- name: Remove a single A record
exo_dns_record:
name: www
domain: example.com
state: absent
'''
RETURN = '''
---
exo_dns_record:
description: API record results
returned: success
type: complex
contains:
content:
description: value of the record
returned: success
type: str
sample: 1.2.3.4
created_at:
description: When the record was created
returned: success
type: str
sample: "2016-08-12T15:24:23.989Z"
domain:
description: Name of the domain
returned: success
type: str
sample: example.com
domain_id:
description: ID of the domain
returned: success
type: int
sample: 254324
id:
description: ID of the record
returned: success
type: int
sample: 254324
name:
description: name of the record
returned: success
type: str
sample: www
parent_id:
description: ID of the parent
returned: success
type: int
sample: null
prio:
description: Priority of the record
returned: success
type: int
sample: 10
record_type:
description: Priority of the record
returned: success
type: str
sample: A
system_record:
description: Whether the record is a system record or not
returned: success
type: bool
sample: false
ttl:
description: Time to live of the record
returned: success
type: int
sample: 3600
updated_at:
description: When the record was updated
returned: success
type: str
sample: "2016-08-12T15:24:23.989Z"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.exoscale import ExoDns, exo_dns_argument_spec, exo_dns_required_together
EXO_RECORD_TYPES = [
'A',
'ALIAS',
'CNAME',
'MX',
'SPF',
'URL',
'TXT',
'NS',
'SRV',
'NAPTR',
'PTR',
'AAAA',
'SSHFP',
'HINFO',
'POOL'
]
class ExoDnsRecord(ExoDns):
def __init__(self, module):
super(ExoDnsRecord, self).__init__(module)
self.domain = self.module.params.get('domain').lower()
self.name = self.module.params.get('name').lower()
if self.name == self.domain:
self.name = ""
self.multiple = self.module.params.get('multiple')
self.record_type = self.module.params.get('record_type')
self.content = self.module.params.get('content')
def _create_record(self, record):
self.result['changed'] = True
data = {
'record': {
'name': self.name,
'record_type': self.record_type,
'content': self.content,
'ttl': self.module.params.get('ttl'),
'prio': self.module.params.get('prio'),
}
}
self.result['diff']['after'] = data['record']
if not self.module.check_mode:
record = self.api_query("/domains/%s/records" % self.domain, "POST", data)
return record
def _update_record(self, record):
data = {
'record': {
'name': self.name,
'content': self.content,
'ttl': self.module.params.get('ttl'),
'prio': self.module.params.get('prio'),
}
}
if self.has_changed(data['record'], record['record']):
self.result['changed'] = True
if not self.module.check_mode:
record = self.api_query("/domains/%s/records/%s" % (self.domain, record['record']['id']), "PUT", data)
return record
def get_record(self):
domain = self.module.params.get('domain')
records = self.api_query("/domains/%s/records" % domain, "GET")
result = {}
for r in records:
if r['record']['record_type'] != self.record_type:
continue
r_name = r['record']['name'].lower()
r_content = r['record']['content']
if r_name == self.name:
if not self.multiple:
if result:
self.module.fail_json(msg="More than one record with record_type=%s and name=%s params. "
"Use multiple=yes for more than one record." % (self.record_type, self.name))
else:
result = r
elif r_content == self.content:
return r
return result
def present_record(self):
record = self.get_record()
if not record:
record = self._create_record(record)
else:
record = self._update_record(record)
return record
def absent_record(self):
record = self.get_record()
if record:
self.result['diff']['before'] = record
self.result['changed'] = True
if not self.module.check_mode:
self.api_query("/domains/%s/records/%s" % (self.domain, record['record']['id']), "DELETE")
return record
def get_result(self, resource):
if resource:
self.result['exo_dns_record'] = resource['record']
self.result['exo_dns_record']['domain'] = self.domain
return self.result
def main():
argument_spec = exo_dns_argument_spec()
argument_spec.update(dict(
name=dict(type='str', default=''),
record_type=dict(type='str', choices=EXO_RECORD_TYPES, aliases=['rtype', 'type'], default='A'),
content=dict(type='str', aliases=['value', 'address']),
multiple=(dict(type='bool', default=False)),
ttl=dict(type='int', default=3600),
prio=dict(type='int', aliases=['priority']),
domain=dict(type='str', required=True),
state=dict(type='str', choices=['present', 'absent'], default='present'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=exo_dns_required_together(),
required_if=[
('state', 'present', ['content']),
('multiple', True, ['content']),
],
supports_check_mode=True,
)
exo_dns_record = ExoDnsRecord(module)
if module.params.get('state') == "present":
resource = exo_dns_record.present_record()
else:
resource = exo_dns_record.absent_record()
result = exo_dns_record.get_result(resource)
module.exit_json(**result)
if __name__ == '__main__':
main()