Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
767316125d | |||
995592a555 | |||
f1abb8cb37 | |||
6544f2acc8 | |||
bebccfab99 | |||
ff000ed5b1 | |||
f4a49b5843 | |||
9e1b4bb9e6 | |||
afc4b987a7 | |||
35d665a31a | |||
05a5824225 | |||
45463d4e54 | |||
d430e3ffbb |
24
README.md
24
README.md
@ -12,9 +12,21 @@ Ansible libraries for managing Gandi resources.
|
|||||||
gather_facts: false
|
gather_facts: false
|
||||||
connection: local
|
connection: local
|
||||||
tasks:
|
tasks:
|
||||||
|
- name: Prepare Python dependencies
|
||||||
|
become: true
|
||||||
|
apt:
|
||||||
|
name: python3-pip
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Install dns-lexicon system wide
|
||||||
|
become: true
|
||||||
|
pip:
|
||||||
|
name: dns-lexicon==3.3.19
|
||||||
|
executable: /usr/bin/pip3
|
||||||
|
|
||||||
- name: Create foobar.autonomic.zone
|
- name: Create foobar.autonomic.zone
|
||||||
gandi_dns:
|
gandi_dns:
|
||||||
rest_api_key: "{{ lookup('env', 'LEXICON_GANDI_AUTH_TOKEN') }}"
|
gandi_rest_token: abc...
|
||||||
domain: foobar.autonomic.zone
|
domain: foobar.autonomic.zone
|
||||||
ipv4: 94.130.105.60
|
ipv4: 94.130.105.60
|
||||||
state: present
|
state: present
|
||||||
@ -32,7 +44,7 @@ Ansible libraries for managing Gandi resources.
|
|||||||
- [dns-lexicon >= 3.3.19](https://pypi.org/project/dns-lexicon/) (if using `gandi_dns` module)
|
- [dns-lexicon >= 3.3.19](https://pypi.org/project/dns-lexicon/) (if using `gandi_dns` module)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ pip install ansible==2.6.9 "dns-lexicon[gandi]==3.3.19"
|
$ pip install ansible==2.6.9 dns-lexicon==3.3.19
|
||||||
```
|
```
|
||||||
|
|
||||||
These should be present on the localhost where you run Ansible.
|
These should be present on the localhost where you run Ansible.
|
||||||
@ -53,13 +65,17 @@ You can test that everything is working by running the following.
|
|||||||
$ lexicon gandi list autonomic.zone A
|
$ lexicon gandi list autonomic.zone A
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `gandi_dns` module will provide the `PROVIDER` and
|
||||||
|
`LEXICON_GANDI_API_PROTOCOL` environment variables internally so you only need
|
||||||
|
to pass `LEXICON_GANDI_AUTH_TOKEN` as the `gandi_rest_token` argument to the
|
||||||
|
task or expose it in the environment and it will be picked up.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Include an entry in your `requirements.yml` like so.
|
Include an entry in your `requirements.yml` like so.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- src: https://git.autonomic.zone/autonomic-cooperative/autonomic.gandi
|
- src: https://git.autonomic.zone/autonomic-cooperative/autonomic.gandi/archive/0.0.5.tar.gz
|
||||||
version: 0.0.1
|
|
||||||
name: autonomic.gandi
|
name: autonomic.gandi
|
||||||
```
|
```
|
||||||
|
|
||||||
|
1
defaults/main.yml
Normal file
1
defaults/main.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
---
|
@ -1,14 +1,11 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import traceback
|
import traceback
|
||||||
from subprocess import CalledProcessError, check_output
|
from subprocess import CalledProcessError, check_output
|
||||||
|
|
||||||
from ansible.module_utils.basic import (
|
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||||
AnsibleModule,
|
|
||||||
env_fallback,
|
|
||||||
missing_required_lib,
|
|
||||||
)
|
|
||||||
|
|
||||||
DOCUMENTATION = """
|
DOCUMENTATION = """
|
||||||
---
|
---
|
||||||
@ -28,13 +25,13 @@ options:
|
|||||||
description: The IP v4 address that the domain refers to.
|
description: The IP v4 address that the domain refers to.
|
||||||
type: str
|
type: str
|
||||||
required: true
|
required: true
|
||||||
rest_api_key:
|
gandi_rest_token:
|
||||||
description:
|
description:
|
||||||
- The Gandi REST API key. It may also be specified by the C(LEXICON_GANDI_AUTH_TOKEN)
|
- The Gandi REST API key. It may also be specified by the C(LEXICON_GANDI_AUTH_TOKEN)
|
||||||
environment variable. See U(https://github.com/AnalogJ/lexicon/blob/ce168132880558415c8c755e65f8e2f9b46cff62/lexicon/providers/gandi.py).
|
environment variable. See U(https://github.com/AnalogJ/lexicon/blob/ce168132880558415c8c755e65f8e2f9b46cff62/lexicon/providers/gandi.py).
|
||||||
for more.
|
for more.
|
||||||
type: str
|
type: str
|
||||||
required: true
|
required: false
|
||||||
state:
|
state:
|
||||||
description:
|
description:
|
||||||
- The desired instance state.
|
- The desired instance state.
|
||||||
@ -48,7 +45,6 @@ options:
|
|||||||
EXAMPLES = """
|
EXAMPLES = """
|
||||||
- name: Create a new Gandi DNS entry
|
- name: Create a new Gandi DNS entry
|
||||||
gandi_dns:
|
gandi_dns:
|
||||||
rest_api_key: "{{ lookup('env', 'LEXICON_GANDI_AUTH_TOKEN') }}"
|
|
||||||
domain: foobar.autonomic.zone
|
domain: foobar.autonomic.zone
|
||||||
ipv4: 192.168.1.2
|
ipv4: 192.168.1.2
|
||||||
state: present
|
state: present
|
||||||
@ -64,30 +60,47 @@ except ImportError:
|
|||||||
HAS_DNS_LEXICON_DEPENDENCY = False
|
HAS_DNS_LEXICON_DEPENDENCY = False
|
||||||
|
|
||||||
|
|
||||||
|
def error_msg(domain):
|
||||||
|
"""Better error message since check_output output is not helpful."""
|
||||||
|
return (
|
||||||
|
"Unable to retrieve domain info. Did you export "
|
||||||
|
"the LEXICON_GANDI_AUTH_TOKEN environment variable? "
|
||||||
|
"Does running the following command work? lexicon "
|
||||||
|
"gandi list {domain} A"
|
||||||
|
).format(domain=domain)
|
||||||
|
|
||||||
|
|
||||||
|
def get_env(module):
|
||||||
|
"""Build environment for running command-line commands."""
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["PROVIDER"] = "gandi"
|
||||||
|
env["LEXICON_GANDI_API_PROTOCOL"] = "rest"
|
||||||
|
env["LEXICON_GANDI_AUTH_TOKEN"] = module.params["gandi_rest_token"]
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
def retrieve_domain_info(module):
|
def retrieve_domain_info(module):
|
||||||
"""Retrieve all information about a specific domain."""
|
"""Retrieve all information about a specific domain."""
|
||||||
|
domain = module.params["domain"]
|
||||||
|
env = get_env(module)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return json.loads(
|
return json.loads(
|
||||||
check_output(
|
check_output(
|
||||||
[
|
["lexicon", "gandi", "list", domain, "A", "--output", "JSON"],
|
||||||
"lexicon",
|
env=env,
|
||||||
"gandi",
|
|
||||||
"list",
|
|
||||||
module.params["domain"],
|
|
||||||
"A",
|
|
||||||
"--output",
|
|
||||||
"JSON",
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except CalledProcessError as exception:
|
except CalledProcessError:
|
||||||
module.fail_json(
|
module.fail_json(msg=error_msg(domain))
|
||||||
msg="Unable to retrieve domain info. Saw: %s" % str(exception)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def create_domain(module):
|
def create_domain(module):
|
||||||
"""Create a new DNS entry."""
|
"""Create a new DNS entry."""
|
||||||
|
domain = module.params["domain"]
|
||||||
|
ipv4 = module.params["ipv4"]
|
||||||
|
env = get_env(module)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return json.loads(
|
return json.loads(
|
||||||
check_output(
|
check_output(
|
||||||
@ -95,25 +108,28 @@ def create_domain(module):
|
|||||||
"lexicon",
|
"lexicon",
|
||||||
"gandi",
|
"gandi",
|
||||||
"create",
|
"create",
|
||||||
module.params["domain"],
|
domain,
|
||||||
"A",
|
"A",
|
||||||
"--name",
|
"--name",
|
||||||
module.params["domain"],
|
domain,
|
||||||
"--content",
|
"--content",
|
||||||
module.params["ipv4"],
|
ipv4,
|
||||||
"--output",
|
"--output",
|
||||||
"JSON",
|
"JSON",
|
||||||
]
|
],
|
||||||
|
env=env,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except Exception as exception:
|
except Exception:
|
||||||
module.fail_json(
|
module.fail_json(msg=error_msg(domain))
|
||||||
msg="Unable to create domain entry. Saw: %s" % str(exception)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def delete_domain(module):
|
def delete_domain(module):
|
||||||
"""Delete an existing DNS entry."""
|
"""Delete an existing DNS entry."""
|
||||||
|
domain = module.params["domain"]
|
||||||
|
ipv4 = module.params["ipv4"]
|
||||||
|
env = get_env(module)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return json.loads(
|
return json.loads(
|
||||||
check_output(
|
check_output(
|
||||||
@ -121,59 +137,62 @@ def delete_domain(module):
|
|||||||
"lexicon",
|
"lexicon",
|
||||||
"gandi",
|
"gandi",
|
||||||
"delete",
|
"delete",
|
||||||
module.params["domain"],
|
domain,
|
||||||
"A",
|
"A",
|
||||||
"--name",
|
"--name",
|
||||||
module.params["domain"],
|
domain,
|
||||||
"--content",
|
"--content",
|
||||||
module.params["ipv4"],
|
ipv4,
|
||||||
"--output",
|
"--output",
|
||||||
"JSON",
|
"JSON",
|
||||||
]
|
],
|
||||||
|
env=env,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except Exception as exception:
|
except Exception:
|
||||||
module.fail_json(
|
module.fail_json(msg=error_msg(domain))
|
||||||
msg="Unable to delete domain entry. Saw: %s" % str(exception)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Module entrypoint."""
|
"""Module entrypoint."""
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=dict(
|
argument_spec=dict(
|
||||||
domain=dict(type='str', required=True),
|
domain=dict(type="str", required=True),
|
||||||
ipv4=dict(type='str', required=True),
|
ipv4=dict(type="str", required=True),
|
||||||
state=dict(
|
state=dict(
|
||||||
type='str', required=True, choices=['present', 'absent']
|
type="str", required=True, choices=["present", "absent"]
|
||||||
),
|
),
|
||||||
rest_api_key=dict(
|
gandi_rest_token=dict(
|
||||||
type='str',
|
type="str",
|
||||||
required=True,
|
required=False,
|
||||||
no_log=True,
|
no_log=True,
|
||||||
fallback=(env_fallback, ['LEXICON_GANDI_AUTH_TOKEN']),
|
fallback=(env_fallback, ["LEXICON_GANDI_AUTH_TOKEN"]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
supports_check_mode=False,
|
supports_check_mode=False,
|
||||||
required_together=(['domain', 'ipv4']),
|
required_together=(["domain", "ipv4"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
if not HAS_DNS_LEXICON_DEPENDENCY:
|
if not HAS_DNS_LEXICON_DEPENDENCY:
|
||||||
msg = missing_required_lib('lexicon')
|
msg = (
|
||||||
|
"Missing dns-lexicon, please run apt "
|
||||||
|
"install -y python3-lexicon or install it "
|
||||||
|
" using the Ansible `apt` module."
|
||||||
|
)
|
||||||
module.fail_json(msg=msg, exception=DNS_LEXICON_IMP_ERR)
|
module.fail_json(msg=msg, exception=DNS_LEXICON_IMP_ERR)
|
||||||
|
|
||||||
domains = retrieve_domain_info(module)
|
domains = retrieve_domain_info(module)
|
||||||
existing_domain = module.params['domain'] in [
|
existing_domain = module.params["domain"] in [
|
||||||
domain['name'] for domain in domains
|
domain["name"] for domain in domains
|
||||||
]
|
]
|
||||||
|
|
||||||
if module.params['state'] == 'present':
|
if module.params["state"] == "present":
|
||||||
if existing_domain:
|
if existing_domain:
|
||||||
module.exit_json(changed=False)
|
module.exit_json(changed=False)
|
||||||
create_domain(module)
|
create_domain(module)
|
||||||
module.exit_json(changed=True)
|
module.exit_json(changed=True)
|
||||||
|
|
||||||
if module.params['state'] == 'absent':
|
if module.params["state"] == "absent":
|
||||||
if not existing_domain:
|
if not existing_domain:
|
||||||
module.exit_json(changed=False)
|
module.exit_json(changed=False)
|
||||||
delete_domain(module)
|
delete_domain(module)
|
||||||
|
13
meta/main.yml
Normal file
13
meta/main.yml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
dependencies: []
|
||||||
|
|
||||||
|
galaxy_info:
|
||||||
|
author: autonomic
|
||||||
|
description: Ansible libraries for managing Gandi resources.
|
||||||
|
company: Autonomic
|
||||||
|
license: GPLv
|
||||||
|
min_ansible_version: 2.9.6
|
||||||
|
platforms:
|
||||||
|
- name: Debian
|
||||||
|
versions:
|
||||||
|
- buster
|
10
play.yml
Normal file
10
play.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
- hosts: localhost
|
||||||
|
gather_facts: false
|
||||||
|
connection: local
|
||||||
|
tasks:
|
||||||
|
- name: Create foobar.autonomic.zone
|
||||||
|
gandi_dns:
|
||||||
|
domain: foobar.autonomic.zone
|
||||||
|
ipv4: 94.130.105.60
|
||||||
|
state: absent
|
@ -9,5 +9,4 @@ build-backend = "setuptools.build_meta"
|
|||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 80
|
line-length = 80
|
||||||
target-version = ["py38"]
|
target-version = ["py38"]
|
||||||
skip-string-normalization = true
|
|
||||||
include = '\.pyi?$'
|
include = '\.pyi?$'
|
||||||
|
@ -13,7 +13,7 @@ include_trailing_comma = True
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = autonomic.gandi
|
name = autonomic.gandi
|
||||||
author = decentral1se
|
author = decentral1se
|
||||||
version = 0.0.1
|
version = 0.0.5
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
packages = find:
|
packages = find:
|
||||||
|
1
tasks/main.yml
Normal file
1
tasks/main.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
---
|
@ -5,5 +5,5 @@ from ansible.module_utils._text import to_bytes
|
|||||||
|
|
||||||
|
|
||||||
def set_module_args(args):
|
def set_module_args(args):
|
||||||
args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
|
args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
|
||||||
basic._ANSIBLE_ARGS = to_bytes(args)
|
basic._ANSIBLE_ARGS = to_bytes(args)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
gandi_cli = pytest.importorskip('lexicon')
|
gandi_cli = pytest.importorskip("lexicon")
|
||||||
|
|
||||||
|
|
||||||
def test_TODO():
|
def test_TODO():
|
||||||
|
Reference in New Issue
Block a user