Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reverse DNS Module #302

Merged
merged 9 commits into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Name | Description |
[linode.cloud.firewall_device](./docs/modules/firewall_device.md)|Manage Linode Firewall Devices.|
[linode.cloud.image](./docs/modules/image.md)|Manage a Linode Image.|
[linode.cloud.instance](./docs/modules/instance.md)|Manage Linode Instances, Configs, and Disks.|
[linode.cloud.ip_rdns](./docs/modules/ip_rdns.md)|Manage a Linode IP address's rDNS.|
[linode.cloud.lke_cluster](./docs/modules/lke_cluster.md)|Manage Linode LKE clusters.|
[linode.cloud.lke_node_pool](./docs/modules/lke_node_pool.md)|Manage Linode LKE cluster node pools.|
[linode.cloud.nodebalancer](./docs/modules/nodebalancer.md)|Manage a Linode NodeBalancer.|
Expand Down
57 changes: 57 additions & 0 deletions docs/modules/ip_rdns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# ip_rdns

Manage a Linode IP address's rDNS.

- [Examples](#examples)
- [Parameters](#parameters)
- [Return Values](#return-values)

## Examples

```yaml
- name: Update reverse DNS
linode.cloud.ip_rdns:
state: present
address: 97.107.143.141
rdns: 97.107.143.141.nip.io

```

```yaml
- name: Remove the reverse DNS
linode.cloud.ip_rdns:
state: absent
address: 97.107.143.141

```


## Parameters

| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
| `address` | <center>`str`</center> | <center>**Required**</center> | The IP address. |
| `state` | <center>`str`</center> | <center>Optional</center> | The state of this rDNS of the IP address. **(Choices: `present`, `absent`)** |
| `rdns` | <center>`str`</center> | <center>Optional</center> | The desired rDNS value. **(Updatable)** |

## Return Values

- `ip` - The updated IP address with the new reverse DNS in JSON serialized form.

- Sample Response:
```json
{
"address": "97.107.143.141",
"gateway": "97.107.143.1",
"linode_id": 123,
"prefix": 24,
"public": true,
"rdns": "test.example.org",
"region": "us-east",
"subnet_mask": "255.255.255.0",
"type": "ipv4"
}
```
- See the [Linode API response documentation](https://www.linode.com/docs/api/profile/#ip-address-rdns-update) for a list of returned fields


16 changes: 16 additions & 0 deletions plugins/module_utils/doc_fragments/ip_rdns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Documentation fragments for the ip_rdns module"""

specdoc_examples = ['''
- name: Update reverse DNS
linode.cloud.ip_rdns:
state: present
address: 97.107.143.141
rdns: 97.107.143.141.nip.io
''',
'''
- name: Remove the reverse DNS
linode.cloud.ip_rdns:
state: absent
address: 97.107.143.141
'''
]
7 changes: 3 additions & 4 deletions plugins/module_utils/linode_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
missing_required_lib)

try:
from linode_api4 import (ApiError, Base, Image, LinodeClient,
from linode_api4 import (ApiError, Base as LinodeAPIType, Image, LinodeClient,
MySQLDatabase, PersonalAccessToken,
PostgreSQLDatabase, SSHKey, StackScript)
PostgreSQLDatabase, SSHKey, StackScript, IPAddress)

HAS_LINODE = True
except ImportError:
Expand Down Expand Up @@ -69,15 +69,14 @@
description='The label to assign to this resource.'),
)

LinodeAPIType = Base

RESOURCE_NAMES = {
Image: "image",
MySQLDatabase: "MySQL database",
PersonalAccessToken: "personal access token",
PostgreSQLDatabase: "PostgreSQL database",
SSHKey: "SSH key",
StackScript: "stackscript",
IPAddress: "IP address",
} if HAS_LINODE else {}

class LinodeModuleBase:
Expand Down
122 changes: 122 additions & 0 deletions plugins/modules/ip_rdns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

"""This module contains all of the functionality for Linode rDNS."""

from __future__ import absolute_import, division, print_function

from typing import Any, Optional

import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.ip_info as ip_docs
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.ip_rdns as ip_rdns_docs
from ansible_collections.linode.cloud.plugins.module_utils.linode_common import (
LinodeModuleBase,
)
from ansible_collections.linode.cloud.plugins.module_utils.linode_docs import (
global_authors,
global_requirements,
)
from ansible_specdoc.objects import FieldType, SpecDocMeta, SpecField, SpecReturnValue
from linode_api4 import IPAddress

ip_rdns_spec = dict(
# Disable the default values
label=SpecField(type=FieldType.string, required=False, doc_hide=True),

state=SpecField(
type=FieldType.string,
choices=["present", "absent"],
description=["The state of this rDNS of the IP address."],
),
address=SpecField(
type=FieldType.string,
description=["The IP address."],
required=True,
),
rdns=SpecField(
type=FieldType.string,
editable=True,
description=["The desired rDNS value."],
),
)

SPECDOC_META = SpecDocMeta(
description=["Manage a Linode IP address's rDNS."],
requirements=global_requirements,
author=global_authors,
options=ip_rdns_spec,
examples=ip_rdns_docs.specdoc_examples,
return_values=dict(
ip=SpecReturnValue(
description=(
"The updated IP address with the new "
"reverse DNS in JSON serialized form."
),
docs_url=(
"https://www.linode.com/docs/api/profile/#ip-address-rdns-update"
),
type=FieldType.dict,
sample=ip_docs.result_ip_samples,
)
),
)


class ReverseDNSModule(LinodeModuleBase):
"""Module for updating Linode IP address's reverse DNS value"""

def __init__(self) -> None:
self.module_arg_spec = SPECDOC_META.ansible_spec
self.required_one_of = ["state", "rdns"]
self.results = dict(
changed=False,
actions=[],
ip=None,
)
super().__init__(
module_arg_spec=self.module_arg_spec,
required_if=[["state", "present", ["rdns"]]],
)

def update_rdns(self, rdns: str) -> None:
"""
Update the reverse DNS of the IP address.
"""
ip_str = self.module.params.get("address")
ip_obj = self._get_resource_by_id(IPAddress, ip_str)
ip_obj.rdns = rdns
ip_obj.save()
ip_obj._api_get()
self.register_action(
f"Updated reverse DNS of the IP address {ip_str} to be {rdns}"
)
self.results["ip"] = ip_obj._raw_json

def _handle_present(self) -> None:
rdns = self.module.params.get("rdns")
if not rdns:
self.fail("`rdns` attribute is required to update the IP address")
self.update_rdns(rdns)

def _handle_absent(self) -> None:
self.update_rdns(None)

def exec_module(self, **kwargs: Any) -> Optional[dict]:
"""Entrypoint for reverse DNS module"""
state = kwargs.get("state", "present")

if state == "absent":
self._handle_absent()
else:
self._handle_present()

return self.results


def main() -> None:
"""Constructs and calls the reverse DNS module"""
ReverseDNSModule()


if __name__ == "__main__":
main()
4 changes: 2 additions & 2 deletions tests/integration/targets/ip_info/tasks/main.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- name: stackscript_basic
- name: ip_info_basic
block:
- set_fact:
r: "{{ 1000000000 | random }}"
Expand All @@ -23,7 +23,7 @@
- ip_info.ip.address == instance_create.instance.ipv4[0]

always:
- ignore_errors: yes
- ignore_errors: true
block:
- linode.cloud.instance:
label: '{{ instance_create.instance.label }}'
Expand Down
52 changes: 52 additions & 0 deletions tests/integration/targets/ip_rdns/tasks/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
- name: ip_rdns_basic
block:
- set_fact:
r: "{{ 1000000000 | random }}"

- name: Create an instance
linode.cloud.instance:
label: 'ansible-test-{{ r }}'
region: us-central
type: g6-standard-1
image: linode/ubuntu22.04
wait: no
state: present
register: instance_create

- name: Get info about the instance's primary IP
linode.cloud.ip_info:
address: '{{ instance_create.instance.ipv4[0] }}'
register: ip_info

- set_fact:
new_rdns: '{{ instance_create.instance.ipv4[0] }}.nip.io'

- name: Modify reverse DNS of the IP
linode.cloud.ip_rdns:
state: present
address: '{{ instance_create.instance.ipv4[0] }}'
rdns: '{{ new_rdns }}'
register: ip_rdns_modified

- name: Remove reverse DNS of the IP
linode.cloud.ip_rdns:
state: absent
address: '{{ instance_create.instance.ipv4[0] }}'
register: ip_rdns_removed

- assert:
that:
- ip_info.ip.address == instance_create.instance.ipv4[0]
- ip_rdns_modified.ip.rdns == new_rdns
- ip_rdns_removed.ip.rdns != new_rdns

always:
- ignore_errors: true
block:
- linode.cloud.instance:
label: '{{ instance_create.instance.label }}'
state: absent

environment:
LINODE_UA_PREFIX: '{{ ua_prefix }}'
LINODE_API_TOKEN: '{{ api_token }}'