-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #406 from linode/dev
Release v0.19.0
- Loading branch information
Showing
7 changed files
with
316 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# ip_share | ||
|
||
Manage the Linode shared IPs. | ||
|
||
- [Examples](#examples) | ||
- [Parameters](#parameters) | ||
- [Return Values](#return-values) | ||
|
||
## Examples | ||
|
||
```yaml | ||
- name: Configure the Linode shared IPs. | ||
linode.cloud.ip_share: | ||
linode_id: 12345 | ||
ips: ["192.0.2.1", "2001:db8:3c4d:15::"] | ||
``` | ||
## Parameters | ||
| Field | Type | Required | Description | | ||
|-----------|------|----------|------------------------------------------------------------------------------| | ||
| `ips` | <center>`list`</center> | <center>**Required**</center> | A list of secondary Linode IPs to share with the primary Linode. | | ||
| `linode_id` | <center>`int`</center> | <center>**Required**</center> | The ID of the primary Linode that the addresses will be shared with. | | ||
|
||
## Return Values | ||
|
||
- `ip_share_stats` - The Linode IP share info in JSON serialized form | ||
|
||
- Sample Response: | ||
```json | ||
[ | ||
{ | ||
"linode_id": 12345, | ||
"ips": ["192.0.2.1", "2001:db8:3c4d:15::"], | ||
} | ||
] | ||
``` | ||
- See the [Linode API response documentation](https://www.linode.com/docs/api/networking/#ip-addresses-share__response-samples) for a list of returned fields | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
"""Documentation fragments for the ip_share module""" | ||
specdoc_examples = [''' | ||
- name: Configure the Linode shared IPs. | ||
linode.cloud.ip_share: | ||
linode_id: 12345 | ||
ips: ["192.0.2.1", "2001:db8:3c4d:15::"]'''] | ||
|
||
result_ip_share_stats_samples = ['''[ | ||
{ | ||
"linode_id": 12345, | ||
"ips": ["192.0.2.1", "2001:db8:3c4d:15::"], | ||
} | ||
]'''] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
#!/usr/bin/python | ||
# -*- coding: utf-8 -*- | ||
|
||
"""This module contains all of the functionality for Linode IP Share.""" | ||
|
||
from __future__ import absolute_import, division, print_function | ||
|
||
from typing import Any, List, Optional | ||
|
||
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.ip_share as ip_share_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.objects import Instance | ||
|
||
ip_share_spec = { | ||
# Disable the default values | ||
"label": SpecField(type=FieldType.string, required=False, doc_hide=True), | ||
"state": SpecField(type=FieldType.string, required=False, doc_hide=True), | ||
"ips": SpecField( | ||
type=FieldType.list, | ||
required=True, | ||
description=[ | ||
"A list of secondary Linode IPs to share with the primary Linode." | ||
], | ||
), | ||
"linode_id": SpecField( | ||
type=FieldType.integer, | ||
required=True, | ||
description=[ | ||
"The ID of the primary Linode that the addresses will be shared with." | ||
], | ||
), | ||
} | ||
|
||
SPECDOC_META = SpecDocMeta( | ||
description=["Manage the Linode shared IPs."], | ||
requirements=global_requirements, | ||
author=global_authors, | ||
options=ip_share_spec, | ||
examples=ip_share_docs.specdoc_examples, | ||
return_values={ | ||
"ip_share_stats": SpecReturnValue( | ||
description="The Linode IP share info in JSON serialized form", | ||
docs_url="https://www.linode.com/docs/api/networking/" | ||
+ "#ip-addresses-share__response-samples", | ||
type=FieldType.dict, | ||
sample=ip_share_docs.result_ip_share_stats_samples, | ||
) | ||
}, | ||
) | ||
|
||
|
||
class IPShareModule(LinodeModuleBase): | ||
"""Module for configuring Linode shared IPs.""" | ||
|
||
def __init__(self) -> None: | ||
self.module_arg_spec = SPECDOC_META.ansible_spec | ||
self.results = { | ||
"changed": False, | ||
"actions": [], | ||
"linode_id": None, | ||
"ips": None, | ||
} | ||
|
||
super().__init__( | ||
module_arg_spec=self.module_arg_spec, | ||
) | ||
|
||
def _share_ip_addresses(self, ips: List[str], linode_id: str) -> None: | ||
""" | ||
Configure shared IPs. | ||
""" | ||
try: | ||
self.client.networking.ip_addresses_share( | ||
ips=ips, | ||
linode=linode_id, | ||
) | ||
except Exception as exception: | ||
self.fail( | ||
msg="failed to configure shared ips for linode {0}: {1}".format( | ||
linode_id, exception | ||
) | ||
) | ||
|
||
# check if the IPs have been shared with the Linode instance | ||
def _check_shared_ip_addresses( | ||
self, ips: List[str], linode: Instance | ||
) -> bool: | ||
current_ips = {i.address for i in linode.ips.ipv4.shared} | ||
|
||
# ensure that IPv6 ranges are only shared by checking if is_bgp is True | ||
for ipv6 in linode.ips.ipv6.ranges: | ||
# We need to make a manual GET request | ||
# because is_bgp is only available in the GET | ||
# response body. | ||
ipv6._api_get() | ||
|
||
if ipv6.is_bgp: | ||
current_ips.add(ipv6.range) | ||
|
||
return set(ips) == current_ips | ||
|
||
def _handle_present(self) -> None: | ||
linode_id = self.module.params.get("linode_id") | ||
ips = self.module.params.get("ips") | ||
|
||
linode = Instance(self.client, linode_id) | ||
|
||
if not self._check_shared_ip_addresses(ips, linode): | ||
self._share_ip_addresses(ips, linode_id) | ||
self.register_action("Shared IPs with Linode {0}".format(linode_id)) | ||
|
||
linode = Instance(self.client, linode_id) | ||
self.results["linode_id"] = linode.id | ||
self.results["ips"] = [ | ||
i.address for i in linode.ips.ipv4.shared | ||
] + [i.range for i in linode.ips.ipv6.ranges] | ||
|
||
def _handle_absent(self) -> None: | ||
linode_id = self.module.params.get("linode_id") | ||
|
||
# Send an empty array to remove all shared IP addresses. | ||
self._share_ip_addresses([], linode_id) | ||
self.register_action( | ||
"Removed shared ips from Linode {0}".format(linode_id) | ||
) | ||
|
||
linode = Instance(self.client, linode_id) | ||
self.results["linode_id"] = linode.id | ||
self.results["ips"] = [i.address for i in linode.ips.ipv4.shared] + [ | ||
i.range for i in linode.ips.ipv6.ranges | ||
] | ||
|
||
def exec_module(self, **kwargs: Any) -> Optional[dict]: | ||
"""Entrypoint for configuring shared IPs for a Linode.""" | ||
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 IP Share module""" | ||
IPShareModule() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
linode-api4>=5.7.0 | ||
linode-api4>=5.7.2 | ||
polling>=0.3.2 | ||
types-requests==2.31.0.2 | ||
ansible-specdoc>=0.0.14 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
|
||
- name: ip_share | ||
block: | ||
- set_fact: | ||
r1: "{{ 1000000000 | random }}" | ||
r2: "{{ 1000000000 | random }}" | ||
|
||
- name: Create an instance to get IPs. | ||
linode.cloud.instance: | ||
label: 'ansible-test-{{ r1 }}' | ||
region: us-east | ||
type: g6-standard-1 | ||
image: linode/alpine3.16 | ||
wait: false | ||
state: present | ||
register: instance_create | ||
|
||
- name: Create an instance to be shared with IPs. | ||
linode.cloud.instance: | ||
label: 'ansible-test-{{ r2 }}' | ||
region: us-east | ||
type: g6-standard-1 | ||
image: linode/alpine3.16 | ||
wait: false | ||
state: present | ||
register: instance_create_shared | ||
|
||
- name: Create an IPv6 range | ||
linode.cloud.api_request: | ||
method: POST | ||
path: networking/ipv6/ranges | ||
body_json: > | ||
{ | ||
"linode_id": {{ instance_create.instance.id }}, | ||
"prefix_length": 64 | ||
} | ||
register: create_ipv6 | ||
|
||
- set_fact: | ||
ipv6_range: '{{ (create_ipv6.body.range | split("/"))[0] }}' | ||
|
||
# IPv6 ranges must be shared with their assigned Linode before they can be shared to other Linodes | ||
- name: Share the IPv6 range to the assigned Linode. | ||
linode.cloud.ip_share: | ||
api_version: v4beta | ||
ips: ['{{ ipv6_range }}'] | ||
linode_id: '{{ instance_create.instance.id }}' | ||
state: present | ||
register: ipv6_shared | ||
|
||
- name: Configure a Linode shared IPs. | ||
linode.cloud.ip_share: | ||
api_version: v4beta | ||
ips: ['{{ instance_create.instance.ipv4[0]}}', '{{ ipv6_range }}'] | ||
linode_id: '{{ instance_create_shared.instance.id }}' | ||
state: present | ||
register: ip_shared | ||
|
||
- name: Configure a Linode with already shared IPs. | ||
linode.cloud.ip_share: | ||
api_version: v4beta | ||
ips: ['{{ instance_create.instance.ipv4[0]}}', '{{ ipv6_range }}'] | ||
linode_id: '{{ instance_create_shared.instance.id }}' | ||
state: present | ||
register: ip_already_shared | ||
|
||
- name: Remove the shared IPs from a Linode. | ||
linode.cloud.ip_share: | ||
api_version: v4beta | ||
state: absent | ||
ips: [] | ||
linode_id: '{{ instance_create_shared.instance.id }}' | ||
register: ip_unshared | ||
|
||
- name: Assert shared IPs configured. | ||
assert: | ||
that: | ||
- ip_shared.ips[0] == '{{ instance_create.instance.ipv4[0]}}' | ||
- ip_already_shared.changed == false | ||
- ip_unshared.ips == [] | ||
|
||
always: | ||
- ignore_errors: true | ||
block: | ||
- linode.cloud.instance: | ||
label: '{{ instance_create.instance.label }}' | ||
state: absent | ||
- linode.cloud.instance: | ||
label: '{{ instance_create_shared.instance.label }}' | ||
state: absent | ||
|
||
environment: | ||
LINODE_UA_PREFIX: '{{ ua_prefix }}' | ||
LINODE_API_TOKEN: '{{ api_token }}' | ||
LINODE_API_VERSION: '{{ api_version }}' |