Skip to content

Commit

Permalink
new: Support IP module to allocate a new IP (#628)
Browse files Browse the repository at this point in the history
* ip module

* simplify test

* address comments

* fix lint&doc

* handle absent
  • Loading branch information
yec-akamai authored Jan 7, 2025
1 parent 9974870 commit 4da84f0
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 0 deletions.
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](./docs/modules/ip.md)|Allocates a new IPv4 Address on your Account. The Linode must be configured to support additional addresses - please Open a support ticket requesting additional addresses before attempting allocation.|
[linode.cloud.ip_assign](./docs/modules/ip_assign.md)|Assign IPs to Linodes in a given Region.|
[linode.cloud.ip_rdns](./docs/modules/ip_rdns.md)|Manage a Linode IP address's rDNS.|
[linode.cloud.ip_share](./docs/modules/ip_share.md)|Manage the Linode shared IPs.|
Expand Down
38 changes: 38 additions & 0 deletions docs/modules/ip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# ip

Allocates a new IPv4 Address on your Account. The Linode must be configured to support additional addresses - please Open a support ticket requesting additional addresses before attempting allocation.

- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
- [Parameters](#parameters)
- [Return Values](#return-values)

## Minimum Required Fields
| Field | Type | Required | Description |
|-------------|-------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `api_token` | `str` | **Required** | The Linode account personal access token. It is necessary to run the module. <br/>It can be exposed by the environment variable `LINODE_API_TOKEN` instead. <br/>See details in [Usage](https://github.com/linode/ansible_linode?tab=readme-ov-file#usage). |

## Examples

```yaml
- name: Allocate IP to Linode
linode.cloud.ip:
linode_id: 123
public: true
type: ipv4
state: present
```
## Parameters
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
| `state` | <center>`str`</center> | <center>**Required**</center> | The state of this IP. **(Choices: `present`, `absent`)** |
| `linode_id` | <center>`int`</center> | <center>Optional</center> | The ID of a Linode you have access to that this address will be allocated to. |
| `public` | <center>`bool`</center> | <center>Optional</center> | Whether to create a public or private IPv4 address. |
| `type` | <center>`str`</center> | <center>Optional</center> | The type of address you are requesting. Only IPv4 addresses may be allocated through this operation. **(Choices: `ipv4`)** |
| `address` | <center>`str`</center> | <center>Optional</center> | The IP address to delete. **(Conflicts With: `linode_id`,`public`,`type`)** |

## Return Values

25 changes: 25 additions & 0 deletions plugins/module_utils/doc_fragments/ip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Documentation fragments for the ip module"""
specdoc_examples = ['''
- name: Allocate IP to Linode
linode.cloud.ip:
linode_id: 123
public: true
type: ipv4
state: present''']

result_ip_samples = ['''{
"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",
"vpc_nat_1_1": {
"vpc_id": 242,
"subnet_id": 194,
"address": "139.144.244.36"
}
}''']
137 changes: 137 additions & 0 deletions plugins/modules/ip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""This module allows users to allocate a new IPv4 Address on their accounts."""

from __future__ import absolute_import, division, print_function

from typing import Any, Optional

import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.ip as 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_collections.linode.cloud.plugins.module_utils.linode_helper import (
filter_null_values,
)
from ansible_specdoc.objects import FieldType, SpecDocMeta, SpecField

spec: dict = {
"linode_id": SpecField(
type=FieldType.integer,
description=[
"The ID of a Linode you have access to "
"that this address will be allocated to."
],
),
"public": SpecField(
type=FieldType.bool,
description=["Whether to create a public or private IPv4 address."],
),
"type": SpecField(
type=FieldType.string,
choices=["ipv4"],
description=[
"The type of address you are requesting. "
"Only IPv4 addresses may be allocated through this operation."
],
),
"address": SpecField(
type=FieldType.string,
description=["The IP address to delete."],
conflicts_with=["linode_id", "public", "type"],
),
"state": SpecField(
type=FieldType.string,
choices=["present", "absent"],
required=True,
description=["The state of this IP."],
),
}

SPECDOC_META = SpecDocMeta(
description=[
"Allocates a new IPv4 Address on your Account. "
"The Linode must be configured to support "
"additional addresses - "
"please Open a support ticket "
"requesting additional addresses before attempting allocation.",
],
requirements=global_requirements,
author=global_authors,
options=spec,
examples=docs.specdoc_examples,
return_values={},
)

DOCUMENTATION = r"""
"""
EXAMPLES = r"""
"""
RETURN = r"""
"""


class Module(LinodeModuleBase):
"""Module for allocating a new IP"""

def __init__(self) -> None:
self.module_arg_spec = SPECDOC_META.ansible_spec
self.results = {
"changed": False,
"actions": [],
"ip": None,
}
super().__init__(
module_arg_spec=self.module_arg_spec,
required_together=[
("linode_id", "public", "type"),
],
)

def _handle_present(self) -> None:
params = filter_null_values(self.module.params)
linode_id = params.get("linode_id")
public = params.get("public")

try:
ip = self.client.networking.ip_allocate(linode_id, public)
self.register_action(
f"IP allocation to Linode {linode_id} completed."
)
except Exception as exc:
self.fail(msg=f"failed to allocate IP to Linode {linode_id}: {exc}")

self.results["ip"] = ip._raw_json

def _handle_absent(self) -> None:
# TODO: Implement deleting IP once it's available in python-sdk.
# Raise an error for now when user reaches deleting IP.
self.fail(
msg="failed to delete IP: IP deleting is currently not supported."
)

def exec_module(self, **kwargs: Any) -> Optional[dict]:
"""Entrypoint for IP module"""

state = kwargs.get("state")

if state == "absent":
self._handle_absent()
return self.results

self._handle_present()

return self.results


def main() -> None:
"""Constructs and calls the module"""
Module()


if __name__ == "__main__":
main()
53 changes: 53 additions & 0 deletions tests/integration/targets/ip_basic/tasks/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
- name: ip
block:
- set_fact:
r: "{{ 1000000000 | random }}"

- name: Create a Linode Instance
linode.cloud.instance:
label: 'ansible-test-{{ r }}'
region: us-southeast
type: g6-standard-1
image: linode/alpine3.19
state: present
firewall_id: '{{ firewall_id }}'
register: create_instance

- name: Allocate a new IP to the Linode
linode.cloud.ip:
linode_id: '{{ create_instance.instance.id }}'
public: true
type: ipv4
state: present
register: allocate_ip

- name: Assert changes
assert:
that:
- allocate_ip.ip.linode_id == create_instance.instance.id
- allocate_ip.ip.type == 'ipv4'
- allocate_ip.ip.region == create_instance.instance.region

# - name: Delete an IP
# linode.cloud.ip:
# address: allocate_ip.ip.address
# state: absent
# register: delete_ip

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


environment:
LINODE_UA_PREFIX: '{{ ua_prefix }}'
LINODE_API_TOKEN: '{{ api_token }}'
LINODE_API_URL: '{{ api_url }}'
LINODE_API_VERSION: '{{ api_version }}'
LINODE_CA: '{{ ca_file or "" }}'


0 comments on commit 4da84f0

Please sign in to comment.