From d1753c0e3cd9293bc75f9e26442c7a7240e05a4c Mon Sep 17 00:00:00 2001
From: Ye Chen <127243817+yec-akamai@users.noreply.github.com>
Date: Fri, 26 Jul 2024 12:57:22 -0400
Subject: [PATCH 01/17] fix: Create SRV domain_record (#548)
* init
* fix srv
---
docs/modules/domain_record.md | 16 ++++++++-
.../doc_fragments/domain_record.py | 11 ++++++
plugins/modules/domain_record.py | 17 +++++----
.../targets/domain_record/tasks/main.yaml | 35 +++++++++++++++++--
4 files changed, 70 insertions(+), 9 deletions(-)
diff --git a/docs/modules/domain_record.md b/docs/modules/domain_record.md
index 5f08966e..770f92a5 100644
--- a/docs/modules/domain_record.md
+++ b/docs/modules/domain_record.md
@@ -26,6 +26,20 @@ NOTE: Domain records are identified by their name, target, and type.
state: present
```
+```yaml
+- name: Create an SRV domain record
+ linode.cloud.domain_record:
+ domain: my-domain.com
+ service: srv-service
+ protocol: tcp
+ type: 'SRV'
+ target: host.example.com
+ port: 443
+ priority: 0
+ weight: 1
+ state: present
+```
+
```yaml
- name: Delete a domain record
linode.cloud.domain_record:
@@ -53,7 +67,7 @@ NOTE: Domain records are identified by their name, target, and type.
| `domain_id` |
`int` | Optional | The ID of the parent Domain. |
| `domain` | `str` | Optional | The name of the parent Domain. |
| `record_id` | `int` | Optional | The id of the record to modify. **(Conflicts With: `name`)** |
-| `name` | `str` | Optional | The name of this Record. NOTE: If the name of the record ends with the domain, it will be dropped from the resulting record's name. **(Conflicts With: `record_id`)** |
+| `name` | `str` | Optional | The name of this Record. NOTE: If the name of the record ends with the domain, it will be dropped from the resulting record's name. Unused for SRV record. Use the service property to set the service name for this record. **(Conflicts With: `record_id`)** |
| `port` | `int` | Optional | The port this Record points to. Only valid and required for SRV record requests. **(Updatable)** |
| `priority` | `int` | Optional | The priority of the target host for this Record. Lower values are preferred. Only valid for MX and SRV record requests. Required for SRV record requests. **(Updatable)** |
| `protocol` | `str` | Optional | The protocol this Record’s service communicates with. An underscore (_) is prepended automatically to the submitted value for this property. **(Updatable)** |
diff --git a/plugins/module_utils/doc_fragments/domain_record.py b/plugins/module_utils/doc_fragments/domain_record.py
index 9b898be9..8dcb7d31 100644
--- a/plugins/module_utils/doc_fragments/domain_record.py
+++ b/plugins/module_utils/doc_fragments/domain_record.py
@@ -8,6 +8,17 @@
type: 'A'
target: '127.0.0.1'
state: present''', '''
+- name: Create an SRV domain record
+ linode.cloud.domain_record:
+ domain: my-domain.com
+ service: srv-service
+ protocol: tcp
+ type: 'SRV'
+ target: host.example.com
+ port: 443
+ priority: 0
+ weight: 1
+ state: present''', '''
- name: Delete a domain record
linode.cloud.domain_record:
domain: my-domain.com
diff --git a/plugins/modules/domain_record.py b/plugins/modules/domain_record.py
index 65be32d2..afa6a794 100644
--- a/plugins/modules/domain_record.py
+++ b/plugins/modules/domain_record.py
@@ -47,7 +47,9 @@
description=[
"The name of this Record.",
"NOTE: If the name of the record ends with the domain, "
- "it will be dropped from the resulting record's name.",
+ "it will be dropped from the resulting record's name. "
+ "Unused for SRV record. "
+ "Use the service property to set the service name for this record.",
],
),
"port": SpecField(
@@ -173,10 +175,9 @@ def __init__(self) -> None:
self.module_arg_spec = SPECDOC_META.ansible_spec
self.required_one_of: List[List[str]] = [
["domain", "domain_id"],
- ["name", "record_id"],
+ ["name", "record_id", "service"],
]
self.mutually_exclusive: List[List[str]] = [["name", "record_id"]]
- self.required_together: List[List[str]] = [["name", "type"]]
self.results = {"changed": False, "actions": [], "record": None}
self._domain: Optional[Domain] = None
@@ -186,7 +187,6 @@ def __init__(self) -> None:
module_arg_spec=self.module_arg_spec,
required_one_of=self.required_one_of,
mutually_exclusive=self.mutually_exclusive,
- required_together=self.required_together,
)
def _find_record(
@@ -267,10 +267,13 @@ def _create_record(self) -> Optional[DomainRecord]:
params = self.module.params
record_type = params.pop("type")
record_name = params.get("name")
+ record_service = params.get("service")
try:
self.register_action(
- "Created domain record {0}".format(record_name)
+ "Created domain record type {0}: name is {1}; service is {2}".format(
+ record_type, record_name, record_service
+ )
)
return self._domain.record_create(record_type, **params)
except Exception as exception:
@@ -306,7 +309,9 @@ def _handle_present(self) -> None:
"record with id {0} does not exist".format(record_id)
)
- if self._record is None and record_name is not None:
+ if self._record is None and (
+ record_name is not None or params.get("service") is not None
+ ):
self._record = self._create_record()
self._update_record()
diff --git a/tests/integration/targets/domain_record/tasks/main.yaml b/tests/integration/targets/domain_record/tasks/main.yaml
index fee9f8be..076af077 100644
--- a/tests/integration/targets/domain_record/tasks/main.yaml
+++ b/tests/integration/targets/domain_record/tasks/main.yaml
@@ -146,6 +146,24 @@
- record_dupe_update.record.ttl_sec == 300
- record_dupe_update.record.weight == 55
+ - name: Create an SRV domain record
+ linode.cloud.domain_record:
+ domain: '{{ domain_create.domain.domain }}'
+ service: carddavs
+ protocol: tcp
+ type: "SRV"
+ target: host.example.com
+ port: 443
+ priority: 0
+ weight: 1
+ state: present
+ register: srv_create
+
+ - name: Assert SRV record is created
+ assert:
+ that:
+ - srv_create.record.type == 'SRV'
+
- name: Get domain_info
linode.cloud.domain_info:
domain: '{{ domain_create.domain.domain }}'
@@ -154,7 +172,7 @@
- name: Assert domain_info response
assert:
that:
- - domain_info.records|length == 3
+ - domain_info.records|length == 4
- name: Create domain_record containing FQDN
linode.cloud.domain_record:
@@ -243,7 +261,20 @@
assert:
that:
- record_dupe_delete.changed
- - record_dupe_delete.record.id == record_dupe_delete.record.id
+ - record_dupe_delete.record.id == record_dupe_create.record.id
+
+ - name: Delete SRV domain_record
+ linode.cloud.domain_record:
+ domain: '{{ domain_create.domain.domain }}'
+ record_id: '{{ srv_create.record.id }}'
+ state: absent
+ register: srv_record_delete
+
+ - name: Assert SRV domain_record is deleted
+ assert:
+ that:
+ - srv_record_delete.changed
+ - srv_record_delete.record.id == srv_create.record.id
- name: Delete domain
linode.cloud.domain:
From 60488cba2ffdda410677dd349201eb73c3cb5ca9 Mon Sep 17 00:00:00 2001
From: Lena Garber <114949949+lgarber-akamai@users.noreply.github.com>
Date: Tue, 30 Jul 2024 15:43:14 -0400
Subject: [PATCH 02/17] Migrate user_info to base InfoModule; fix grants error
(#552)
---
docs/modules/user_info.md | 6 +-
plugins/modules/user_info.py | 118 ++++++------------
.../targets/user_info/tasks/main.yaml | 98 +++++++--------
3 files changed, 91 insertions(+), 131 deletions(-)
diff --git a/docs/modules/user_info.md b/docs/modules/user_info.md
index b4d1c1f2..2c57b4b3 100644
--- a/docs/modules/user_info.md
+++ b/docs/modules/user_info.md
@@ -25,11 +25,11 @@ Get info about a Linode User.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `username` | `str` | **Required** | The username of the user. |
+| `username` | `str` | **Required** | The Username of the User to resolve. |
## Return Values
-- `user` - The user info in JSON serialized form.
+- `user` - The returned User.
- Sample Response:
```json
@@ -48,7 +48,7 @@ Get info about a Linode User.
- See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-user) for a list of returned fields
-- `grants` - The grants info in JSON serialized form.
+- `grants` - The returned Grants.
- Sample Response:
```json
diff --git a/plugins/modules/user_info.py b/plugins/modules/user_info.py
index c918f006..6550c72f 100644
--- a/plugins/modules/user_info.py
+++ b/plugins/modules/user_info.py
@@ -5,57 +5,53 @@
from __future__ import absolute_import, division, print_function
-from typing import Any, List, Optional
-
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.user_info 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_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
+from ansible_collections.linode.cloud.plugins.module_utils.linode_common_info import (
+ InfoModule,
+ InfoModuleAttr,
+ InfoModuleResult,
)
+from ansible_specdoc.objects import FieldType
from linode_api4 import User
-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),
- "username": SpecField(
- type=FieldType.string,
- required=True,
- description=["The username of the user."],
+module = InfoModule(
+ primary_result=InfoModuleResult(
+ field_name="user",
+ field_type=FieldType.dict,
+ display_name="User",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-user",
+ samples=docs.result_user_samples,
),
-}
-
-SPECDOC_META = SpecDocMeta(
- description=["Get info about a Linode User."],
- requirements=global_requirements,
- author=global_authors,
- options=spec,
- examples=docs.specdoc_examples,
- return_values={
- "user": SpecReturnValue(
- description="The user info in JSON serialized form.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-user",
- type=FieldType.dict,
- sample=docs.result_user_samples,
- ),
- "grants": SpecReturnValue(
- description="The grants info in JSON serialized form.",
+ secondary_results=[
+ InfoModuleResult(
+ field_name="grants",
+ field_type=FieldType.dict,
+ display_name="Grants",
docs_url="https://techdocs.akamai.com/linode-api/reference/get-user-grants",
- type=FieldType.dict,
- sample=docs.result_grants_samples,
- ),
- },
+ samples=docs.result_grants_samples,
+ get=lambda client, user, params: client.get(
+ # We can't use the UserGrants type here because
+ # it does not serialize directly to JSON or store
+ # the API response JSON.
+ f"/account/users/{user['username']}/grants"
+ ),
+ )
+ ],
+ attributes=[
+ InfoModuleAttr(
+ name="username",
+ display_name="Username",
+ type=FieldType.string,
+ get=lambda client, params: client.load(
+ User, params.get("username")
+ )._raw_json,
+ )
+ ],
+ examples=docs.specdoc_examples,
)
+SPECDOC_META = module.spec
+
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
@@ -63,39 +59,5 @@
RETURN = r"""
"""
-
-class Module(LinodeModuleBase):
- """Module for getting info about a Linode user"""
-
- def __init__(self) -> None:
- self.required_one_of: List[str] = []
- self.results = {"user": None}
-
- self.module_arg_spec = SPECDOC_META.ansible_spec
-
- super().__init__(
- module_arg_spec=self.module_arg_spec,
- required_one_of=self.required_one_of,
- )
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for user info module"""
-
- user = self.client.account.users(
- User.username == self.module.params.get("username")
- )
- grants = user.grants
-
- self.results["user"] = user._raw_json
- self.results["grants"] = grants._raw_json
-
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the module"""
- Module()
-
-
if __name__ == "__main__":
- main()
+ module.run()
diff --git a/tests/integration/targets/user_info/tasks/main.yaml b/tests/integration/targets/user_info/tasks/main.yaml
index b87b3648..efa15d3e 100644
--- a/tests/integration/targets/user_info/tasks/main.yaml
+++ b/tests/integration/targets/user_info/tasks/main.yaml
@@ -1,52 +1,50 @@
- name: user_info
block:
- - debug:
- msg: Skipping for now...(https://jira.linode.com/browse/TPT-2939)
-# - set_fact:
-# r: "{{ 1000000000 | random }}"
-#
-# - name: Create Linode User
-# linode.cloud.user:
-# username: 'ansible-test-{{ r }}'
-# email: 'ansible-test-{{ r }}@linode.com'
-# state: present
-# register: create
-#
-# - name: Assert user created
-# assert:
-# that:
-# - create.user.email is not none
-# - create.user.restricted == true
-#
-# - name: Print username for the created user
-# debug:
-# msg: "The username is: {{ create.user.username }}"
-#
-# - name: Get info about a user
-# linode.cloud.user_info:
-# username: "{{ create.user.username }}"
-# register: user_info
-#
-# - name: Assert user_info for the created user
-# assert:
-# that:
-# - user_info_valid | default(false)
-# - user_info.user.email is defined
-# - user_info.user.email | length > 0
-# when: user_info_valid | default(false)
-#
-# always:
-# - ignore_errors: yes
-# block:
-# - name: Delete a Linode User
-# linode.cloud.user:
-# username: '{{ create.user.username }}'
-# 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 "" }}'
-#
+ - set_fact:
+ r: "{{ 1000000000 | random }}"
+
+ - name: Create Linode User
+ linode.cloud.user:
+ username: 'ansible-test-{{ r }}'
+ email: 'ansible-test-{{ r }}@linode.com'
+ state: present
+ register: create
+
+ - name: Assert user created
+ assert:
+ that:
+ - create.user.email is not none
+ - create.user.restricted == true
+
+ - name: Print username for the created user
+ debug:
+ msg: "The username is: {{ create.user.username }}"
+
+ - name: Get info about a user
+ linode.cloud.user_info:
+ username: "{{ create.user.username }}"
+ register: user_info
+
+ - name: Assert user_info for the created user
+ assert:
+ that:
+ - user_info_valid | default(false)
+ - user_info.user.email is defined
+ - user_info.user.email | length > 0
+ when: user_info_valid | default(false)
+
+ always:
+ - ignore_errors: yes
+ block:
+ - name: Delete a Linode User
+ linode.cloud.user:
+ username: '{{ create.user.username }}'
+ 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 "" }}'
+
From e8ffa5c01974c3af23efd954ba57a11eea0bf815 Mon Sep 17 00:00:00 2001
From: Lena Garber <114949949+lgarber-akamai@users.noreply.github.com>
Date: Thu, 1 Aug 2024 15:20:43 -0400
Subject: [PATCH 03/17] Replace all TODO docs with links (#553)
---
docs/modules/account_availability_info.md | 2 +-
docs/modules/account_availability_list.md | 4 ++--
docs/modules/child_account_info.md | 1 +
docs/modules/child_account_list.md | 3 ++-
docs/modules/placement_group.md | 2 +-
docs/modules/placement_group_assign.md | 1 -
docs/modules/placement_group_info.md | 2 +-
docs/modules/placement_group_list.md | 4 ++--
docs/modules/vpc.md | 2 +-
docs/modules/vpc_info.md | 1 +
docs/modules/vpc_ip_list.md | 3 ++-
docs/modules/vpc_list.md | 3 ++-
docs/modules/vpc_subnet.md | 2 +-
docs/modules/vpc_subnet_info.md | 1 +
docs/modules/vpc_subnet_list.md | 3 ++-
docs/modules/vpcs_ip_list.md | 3 ++-
plugins/modules/account_availability_info.py | 2 +-
plugins/modules/account_availability_list.py | 2 +-
plugins/modules/child_account_info.py | 2 +-
plugins/modules/child_account_list.py | 2 +-
plugins/modules/placement_group.py | 2 +-
plugins/modules/placement_group_assign.py | 3 ++-
plugins/modules/placement_group_info.py | 2 +-
plugins/modules/placement_group_list.py | 2 +-
plugins/modules/vpc.py | 2 +-
plugins/modules/vpc_info.py | 2 +-
plugins/modules/vpc_ip_list.py | 2 +-
plugins/modules/vpc_list.py | 2 +-
plugins/modules/vpc_subnet.py | 2 +-
plugins/modules/vpc_subnet_info.py | 2 +-
plugins/modules/vpc_subnet_list.py | 2 +-
plugins/modules/vpcs_ip_list.py | 2 +-
32 files changed, 39 insertions(+), 31 deletions(-)
diff --git a/docs/modules/account_availability_info.md b/docs/modules/account_availability_info.md
index 5b2fe5fe..7536aef9 100644
--- a/docs/modules/account_availability_info.md
+++ b/docs/modules/account_availability_info.md
@@ -45,6 +45,6 @@ Get info about a Linode Account Availability.
}
```
- - See the [Linode API response documentation](TBD) for a list of returned fields
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-account-availability) for a list of returned fields
diff --git a/docs/modules/account_availability_list.md b/docs/modules/account_availability_list.md
index 3b21b205..b72b278e 100644
--- a/docs/modules/account_availability_list.md
+++ b/docs/modules/account_availability_list.md
@@ -36,7 +36,7 @@ List and filter on Account Availabilities.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](TBD). |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-account-availability). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
@@ -58,6 +58,6 @@ List and filter on Account Availabilities.
}
]
```
- - See the [Linode API response documentation](TBD) for a list of returned fields
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-account-availability) for a list of returned fields
diff --git a/docs/modules/child_account_info.md b/docs/modules/child_account_info.md
index d96a1d82..de7e65aa 100644
--- a/docs/modules/child_account_info.md
+++ b/docs/modules/child_account_info.md
@@ -65,5 +65,6 @@ NOTE: Parent/Child related features may not be generally available.
"zip": "19102-1234"
}
```
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-child-account) for a list of returned fields
diff --git a/docs/modules/child_account_list.md b/docs/modules/child_account_list.md
index f26ea2a8..bd5f1f2e 100644
--- a/docs/modules/child_account_list.md
+++ b/docs/modules/child_account_list.md
@@ -35,7 +35,7 @@ NOTE: Parent/Child related features may not be generally available.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](). |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-child-accounts). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
@@ -74,5 +74,6 @@ NOTE: Parent/Child related features may not be generally available.
"zip": "19102-1234"
}
```
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-child-accounts) for a list of returned fields
diff --git a/docs/modules/placement_group.md b/docs/modules/placement_group.md
index 92fb846f..b13b551c 100644
--- a/docs/modules/placement_group.md
+++ b/docs/modules/placement_group.md
@@ -82,6 +82,6 @@ NOTE: Placement Groups may not currently be available to all users.
]
}
```
- - See the [Linode API response documentation](TBD) for a list of returned fields
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-placement-group) for a list of returned fields
diff --git a/docs/modules/placement_group_assign.md b/docs/modules/placement_group_assign.md
index 7c81dc16..fcdac1ed 100644
--- a/docs/modules/placement_group_assign.md
+++ b/docs/modules/placement_group_assign.md
@@ -40,7 +40,6 @@ NOTE: Placement Groups may not currently be available to all users.
|-----------|------|----------|------------------------------------------------------------------------------|
| `placement_group_id` | `int` | **Required** | The ID of the Placement Group for this assignment. |
| `linode_id` | `int` | **Required** | The Linode ID to assign or unassign to the Placement Group. |
-| `compliant_only` | `bool` | Optional | TODO |
## Return Values
diff --git a/docs/modules/placement_group_info.md b/docs/modules/placement_group_info.md
index 70f0b4c9..308568fa 100644
--- a/docs/modules/placement_group_info.md
+++ b/docs/modules/placement_group_info.md
@@ -54,6 +54,6 @@ Get info about a Linode Placement Group.
}
```
- - See the [Linode API response documentation](TBD) for a list of returned fields
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-placement-group) for a list of returned fields
diff --git a/docs/modules/placement_group_list.md b/docs/modules/placement_group_list.md
index 139334ac..26bcad57 100644
--- a/docs/modules/placement_group_list.md
+++ b/docs/modules/placement_group_list.md
@@ -36,7 +36,7 @@ List and filter on Placement Groups.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](TBD). |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-placement-groups). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
@@ -62,6 +62,6 @@ List and filter on Placement Groups.
}
]
```
- - See the [Linode API response documentation](TBD) for a list of returned fields
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-placement-groups) for a list of returned fields
diff --git a/docs/modules/vpc.md b/docs/modules/vpc.md
index e30c5228..9ec0a996 100644
--- a/docs/modules/vpc.md
+++ b/docs/modules/vpc.md
@@ -56,6 +56,6 @@ Create, read, and update a Linode VPC.
"updated": "2023-08-31T18:35:03"
}
```
- - See the [Linode API response documentation](TODO) for a list of returned fields
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-vpc) for a list of returned fields
diff --git a/docs/modules/vpc_info.md b/docs/modules/vpc_info.md
index b3caf931..985047fa 100644
--- a/docs/modules/vpc_info.md
+++ b/docs/modules/vpc_info.md
@@ -50,5 +50,6 @@ Get info about a Linode VPC.
"updated": "2023-08-31T18:35:03"
}
```
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-vpc) for a list of returned fields
diff --git a/docs/modules/vpc_ip_list.md b/docs/modules/vpc_ip_list.md
index 9ca51294..859fd01e 100644
--- a/docs/modules/vpc_ip_list.md
+++ b/docs/modules/vpc_ip_list.md
@@ -35,7 +35,7 @@ List and filter on VPC IP Addresses.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](). |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-vpc-ips). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
@@ -62,5 +62,6 @@ List and filter on VPC IP Addresses.
}
]
```
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-vpc-ips) for a list of returned fields
diff --git a/docs/modules/vpc_list.md b/docs/modules/vpc_list.md
index 4016b9a5..1c6357cc 100644
--- a/docs/modules/vpc_list.md
+++ b/docs/modules/vpc_list.md
@@ -41,7 +41,7 @@ List and filter on VPCs.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](). |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-vpcs). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
@@ -62,5 +62,6 @@ List and filter on VPCs.
}
]
```
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-vpcs) for a list of returned fields
diff --git a/docs/modules/vpc_subnet.md b/docs/modules/vpc_subnet.md
index a81133d8..1d36de5b 100644
--- a/docs/modules/vpc_subnet.md
+++ b/docs/modules/vpc_subnet.md
@@ -61,6 +61,6 @@ Create, read, and update a Linode VPC Subnet.
"updated": "2023-08-31T18:53:04"
}
```
- - See the [Linode API response documentation](TODO) for a list of returned fields
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-vpc-subnet) for a list of returned fields
diff --git a/docs/modules/vpc_subnet_info.md b/docs/modules/vpc_subnet_info.md
index d55a690d..30d17bfd 100644
--- a/docs/modules/vpc_subnet_info.md
+++ b/docs/modules/vpc_subnet_info.md
@@ -57,5 +57,6 @@ Get info about a Linode VPC Subnet.
"updated": "2023-08-31T18:53:04"
}
```
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-vpc-subnet) for a list of returned fields
diff --git a/docs/modules/vpc_subnet_list.md b/docs/modules/vpc_subnet_list.md
index 893db6d5..dc72a624 100644
--- a/docs/modules/vpc_subnet_list.md
+++ b/docs/modules/vpc_subnet_list.md
@@ -45,7 +45,7 @@ List and filter on VPC Subnets.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](). |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-vpc-subnets). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
@@ -70,5 +70,6 @@ List and filter on VPC Subnets.
}
]
```
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-vpc-subnets) for a list of returned fields
diff --git a/docs/modules/vpcs_ip_list.md b/docs/modules/vpcs_ip_list.md
index e994686d..af5cf5f1 100644
--- a/docs/modules/vpcs_ip_list.md
+++ b/docs/modules/vpcs_ip_list.md
@@ -33,7 +33,7 @@ List and filter on all VPC IP Addresses.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](). |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-vpcs-ips). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
@@ -61,5 +61,6 @@ List and filter on all VPC IP Addresses.
}
]
```
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-vpcs-ips) for a list of returned fields
diff --git a/plugins/modules/account_availability_info.py b/plugins/modules/account_availability_info.py
index 6fbbdb58..d8a20bfb 100644
--- a/plugins/modules/account_availability_info.py
+++ b/plugins/modules/account_availability_info.py
@@ -22,7 +22,7 @@
display_name="Account Availability",
field_name="account_availability",
field_type=FieldType.dict,
- docs_url="TBD",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-account-availability",
samples=docs.result_account_availability_samples,
),
attributes=[
diff --git a/plugins/modules/account_availability_list.py b/plugins/modules/account_availability_list.py
index 6f777348..68c5177e 100644
--- a/plugins/modules/account_availability_list.py
+++ b/plugins/modules/account_availability_list.py
@@ -16,7 +16,7 @@
result_display_name="Account Availabilities",
result_field_name="account_availabilities",
endpoint_template="/account/availability",
- result_docs_url="TBD",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-account-availability",
result_samples=docs.result_account_availabilities_samples,
examples=docs.specdoc_examples,
requires_beta=True,
diff --git a/plugins/modules/child_account_info.py b/plugins/modules/child_account_info.py
index 321ca2f4..e47bd506 100644
--- a/plugins/modules/child_account_info.py
+++ b/plugins/modules/child_account_info.py
@@ -21,7 +21,7 @@
field_name="child_account",
field_type=FieldType.dict,
display_name="Child Account",
- docs_url="",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-child-account",
samples=docs.result_child_account_samples,
),
attributes=[
diff --git a/plugins/modules/child_account_list.py b/plugins/modules/child_account_list.py
index cd1d49c8..f5851f96 100644
--- a/plugins/modules/child_account_list.py
+++ b/plugins/modules/child_account_list.py
@@ -18,7 +18,7 @@
result_display_name=RESULT_DISPLAY_NAME,
result_field_name="child_accounts",
endpoint_template="/account/child-accounts",
- result_docs_url="",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-child-accounts",
examples=docs.specdoc_examples,
result_samples=docs.result_child_accounts_samples,
description=[
diff --git a/plugins/modules/placement_group.py b/plugins/modules/placement_group.py
index 217a532d..1c42a1e8 100644
--- a/plugins/modules/placement_group.py
+++ b/plugins/modules/placement_group.py
@@ -69,7 +69,7 @@
return_values={
"placement_group": SpecReturnValue(
description="The Placement Group in JSON serialized form.",
- docs_url="TBD",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-placement-group",
type=FieldType.dict,
sample=docs.result_placement_group_samples,
)
diff --git a/plugins/modules/placement_group_assign.py b/plugins/modules/placement_group_assign.py
index 3eaad34f..7c66298f 100644
--- a/plugins/modules/placement_group_assign.py
+++ b/plugins/modules/placement_group_assign.py
@@ -38,7 +38,8 @@
),
"compliant_only": SpecField(
type=FieldType.bool,
- description=["TODO"],
+ description=[],
+ doc_hide=True,
),
}
diff --git a/plugins/modules/placement_group_info.py b/plugins/modules/placement_group_info.py
index dfb878b3..7d3ccc06 100644
--- a/plugins/modules/placement_group_info.py
+++ b/plugins/modules/placement_group_info.py
@@ -22,7 +22,7 @@
display_name="Placement Group",
field_name="placement_group",
field_type=FieldType.dict,
- docs_url="TBD",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-placement-group",
samples=docs.result_placement_group_samples,
),
attributes=[
diff --git a/plugins/modules/placement_group_list.py b/plugins/modules/placement_group_list.py
index b6fedff6..5a3cd478 100644
--- a/plugins/modules/placement_group_list.py
+++ b/plugins/modules/placement_group_list.py
@@ -16,7 +16,7 @@
result_display_name="Placement Groups",
result_field_name="placement_groups",
endpoint_template="/placement/groups",
- result_docs_url="TBD",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-placement-groups",
result_samples=docs.result_placement_groups_samples,
examples=docs.specdoc_examples,
requires_beta=True,
diff --git a/plugins/modules/vpc.py b/plugins/modules/vpc.py
index bbc90a04..bbf86791 100644
--- a/plugins/modules/vpc.py
+++ b/plugins/modules/vpc.py
@@ -62,7 +62,7 @@
return_values={
"vpc": SpecReturnValue(
description="The VPC in JSON serialized form.",
- docs_url="TODO",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-vpc",
type=FieldType.dict,
sample=docs.result_vpc_samples,
)
diff --git a/plugins/modules/vpc_info.py b/plugins/modules/vpc_info.py
index 8655fedd..9a539b03 100644
--- a/plugins/modules/vpc_info.py
+++ b/plugins/modules/vpc_info.py
@@ -23,7 +23,7 @@
field_name="vpc",
field_type=FieldType.dict,
display_name="VPC",
- docs_url="",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-vpc",
samples=docs_parent.result_vpc_samples,
),
attributes=[
diff --git a/plugins/modules/vpc_ip_list.py b/plugins/modules/vpc_ip_list.py
index 323bb433..12226fe9 100644
--- a/plugins/modules/vpc_ip_list.py
+++ b/plugins/modules/vpc_ip_list.py
@@ -16,7 +16,7 @@
result_display_name="VPC IP Addresses",
result_field_name="vpcs_ips",
endpoint_template="/vpcs/{vpc_id}/ips",
- result_docs_url="",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-vpc-ips",
examples=docs.specdoc_examples,
result_samples=docs.result_vpc_ip_view_samples,
params=[
diff --git a/plugins/modules/vpc_list.py b/plugins/modules/vpc_list.py
index c4f7d3c6..9e9498c0 100644
--- a/plugins/modules/vpc_list.py
+++ b/plugins/modules/vpc_list.py
@@ -14,7 +14,7 @@
result_display_name="VPCs",
result_field_name="vpcs",
endpoint_template="/vpcs",
- result_docs_url="",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-vpcs",
examples=docs.specdoc_examples,
result_samples=docs.result_vpc_samples,
)
diff --git a/plugins/modules/vpc_subnet.py b/plugins/modules/vpc_subnet.py
index 50b79cee..497082ce 100644
--- a/plugins/modules/vpc_subnet.py
+++ b/plugins/modules/vpc_subnet.py
@@ -63,7 +63,7 @@
return_values={
"subnet": SpecReturnValue(
description="The VPC in JSON serialized form.",
- docs_url="TODO",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-vpc-subnet",
type=FieldType.dict,
sample=docs.result_subnet_samples,
)
diff --git a/plugins/modules/vpc_subnet_info.py b/plugins/modules/vpc_subnet_info.py
index 181bcaa0..db69185c 100644
--- a/plugins/modules/vpc_subnet_info.py
+++ b/plugins/modules/vpc_subnet_info.py
@@ -42,7 +42,7 @@ def _subnet_by_label(
field_name="subnet",
field_type=FieldType.dict,
display_name="VPC Subnet",
- docs_url="",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-vpc-subnet",
samples=docs_parent.result_subnet_samples,
),
params=[
diff --git a/plugins/modules/vpc_subnet_list.py b/plugins/modules/vpc_subnet_list.py
index cbdd82af..d08b8b5d 100644
--- a/plugins/modules/vpc_subnet_list.py
+++ b/plugins/modules/vpc_subnet_list.py
@@ -16,7 +16,7 @@
result_display_name="VPC Subnets",
result_field_name="subnets",
endpoint_template="/vpcs/{vpc_id}/subnets",
- result_docs_url="",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-vpc-subnets",
result_samples=docs.result_vpc_samples,
examples=docs.specdoc_examples,
params=[
diff --git a/plugins/modules/vpcs_ip_list.py b/plugins/modules/vpcs_ip_list.py
index 97fc2456..33dca9b1 100644
--- a/plugins/modules/vpcs_ip_list.py
+++ b/plugins/modules/vpcs_ip_list.py
@@ -14,7 +14,7 @@
result_display_name="all VPC IP Addresses",
result_field_name="vpcs_ips",
endpoint_template="/vpcs/ips",
- result_docs_url="",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-vpcs-ips",
examples=docs.specdoc_examples,
result_samples=docs.result_vpc_samples,
)
From 20b6a7290a04f2fa6978d7b7fcaa3ce2e9d5d8e8 Mon Sep 17 00:00:00 2001
From: Ye Chen <127243817+yec-akamai@users.noreply.github.com>
Date: Fri, 2 Aug 2024 14:42:27 -0400
Subject: [PATCH 04/17] add swap_size (#554)
---
docs/modules/instance.md | 1 +
plugins/modules/instance.py | 7 +++++++
tests/integration/targets/instance_booted/tasks/main.yaml | 1 +
3 files changed, 9 insertions(+)
diff --git a/docs/modules/instance.md b/docs/modules/instance.md
index d7a6aa0e..581e93f2 100644
--- a/docs/modules/instance.md
+++ b/docs/modules/instance.md
@@ -149,6 +149,7 @@ Manage Linode Instances, Configs, and Disks.
| `auto_disk_resize` | `bool` | Optional | Whether implicitly created disks should be resized during a type change operation. **(Default: `False`)** |
| `tags` | `list` | Optional | An array of tags applied to this object. Tags are for organizational purposes only. **(Updatable)** |
| [`placement_group` (sub-options)](#placement_group) | `dict` | Optional | A Placement Group to create this Linode under. |
+| `swap_size` | `int` | Optional | When deploying from an Image, this field is optional, otherwise it is ignored. This is used to set the swap disk size for the newly-created Linode. |
### configs
diff --git a/plugins/modules/instance.py b/plugins/modules/instance.py
index 2d5bb19d..eeb38bc6 100644
--- a/plugins/modules/instance.py
+++ b/plugins/modules/instance.py
@@ -523,6 +523,13 @@
suboptions=linode_instance_placement_group_spec,
description=["A Placement Group to create this Linode under."],
),
+ "swap_size": SpecField(
+ type=FieldType.integer,
+ description=[
+ "When deploying from an Image, this field is optional, otherwise it is ignored. "
+ "This is used to set the swap disk size for the newly-created Linode."
+ ],
+ ),
}
SPECDOC_META = SpecDocMeta(
diff --git a/tests/integration/targets/instance_booted/tasks/main.yaml b/tests/integration/targets/instance_booted/tasks/main.yaml
index 00ed40f6..5b0cd0ce 100644
--- a/tests/integration/targets/instance_booted/tasks/main.yaml
+++ b/tests/integration/targets/instance_booted/tasks/main.yaml
@@ -12,6 +12,7 @@
root_pass: Fn$$oobar123
private_ip: true
booted: true
+ swap_size: 512
state: present
firewall_id: '{{ firewall_id }}'
register: create
From c5c99f5f17ce08e06e04e83db505afaca0b367a1 Mon Sep 17 00:00:00 2001
From: Ye Chen <127243817+yec-akamai@users.noreply.github.com>
Date: Tue, 6 Aug 2024 10:14:08 -0400
Subject: [PATCH 05/17] remove updatable (#556)
---
docs/modules/ssh_key.md | 2 +-
plugins/modules/ssh_key.py | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/docs/modules/ssh_key.md b/docs/modules/ssh_key.md
index 3d9755c1..a282e2b6 100644
--- a/docs/modules/ssh_key.md
+++ b/docs/modules/ssh_key.md
@@ -35,7 +35,7 @@ Manage a Linode SSH key.
|-----------|------|----------|------------------------------------------------------------------------------|
| `label` | `str` | **Required** | This SSH key's unique label. |
| `state` | `str` | **Required** | The state of this SSH key. **(Choices: `present`, `absent`)** |
-| `ssh_key` | `str` | Optional | The SSH public key value. **(Updatable)** |
+| `ssh_key` | `str` | Optional | The SSH public key value. |
## Return Values
diff --git a/plugins/modules/ssh_key.py b/plugins/modules/ssh_key.py
index 6d6c1030..ccd81bfb 100644
--- a/plugins/modules/ssh_key.py
+++ b/plugins/modules/ssh_key.py
@@ -40,7 +40,6 @@
),
"ssh_key": SpecField(
type=FieldType.string,
- editable=True,
description=["The SSH public key value."],
),
}
From c860c0aebe19bc50f4212c841b39b096ed7fd6b4 Mon Sep 17 00:00:00 2001
From: Youjung Kim <126618609+ykim-1@users.noreply.github.com>
Date: Fri, 9 Aug 2024 08:49:26 -0700
Subject: [PATCH 06/17] test: Improve E2E test firewall creation and teardown
(#551)
* improve e2e firewall creation/deletion error handling and clean up
* address PR comments
---
Makefile | 42 +++++++++++++++++++++++++++++-------------
1 file changed, 29 insertions(+), 13 deletions(-)
diff --git a/Makefile b/Makefile
index 1c5a50d0..6b0c5f10 100644
--- a/Makefile
+++ b/Makefile
@@ -62,26 +62,42 @@ gendocs:
# ansible-test integration $(TEST_ARGS) --tags never
integration-test: create-integration-config create-e2e-firewall
@echo "Running Integration Test(s)..."
- ansible-test integration $(TEST_ARGS)
-
-create-e2e-firewall:
+ { \
+ ansible-test integration $(TEST_ARGS); \
+ TEST_EXIT_CODE=$$?; \
+ make delete-e2e-firewall; \
+ exit $$TEST_EXIT_CODE; \
+ }
+
+create-e2e-firewall: update-test-submodules
@echo "Running create e2e firewall playbook..."
- @if ansible-playbook e2e_scripts/cloud_security_scripts/cloud_e2e_firewall/ansible_linode/create_e2e_cloud_firewall.yaml > /dev/null; then \
- echo "Successfully created e2e firewall"; \
- else \
- echo "Failed to create e2e firewall. Please update the cloud firewall scripts using `git submodule update --init` if yaml file doesn't exist"; \
+ @OUTPUT=$$(ansible-playbook e2e_scripts/cloud_security_scripts/cloud_e2e_firewall/ansible_linode/create_e2e_cloud_firewall.yaml 2>&1); \
+ FAILED_COUNT=$$(echo "$$OUTPUT" | grep "failed=" | awk -F 'failed=' '{print $$2}' | awk '{print $$1}'); \
+ if [ "$$FAILED_COUNT" -gt 0 ]; then \
+ echo "Playbook execution failed:"; \
+ echo "$$OUTPUT"; \
exit 1; \
+ else \
+ echo "E2E Cloud firewall created successfully."; \
fi
-delete-e2e-firewall:
+
+delete-e2e-firewall: update-test-submodules
@echo "Running delete e2e firewall playbook..."
- @if ansible-playbook e2e_scripts/cloud_security_scripts/cloud_e2e_firewall/ansible_linode/delete_e2e_cloud_firewall.yaml > /dev/null; then \
- echo "Successfully deleted e2e firewall"; \
+ @OUTPUT=$$(ansible-playbook e2e_scripts/cloud_security_scripts/cloud_e2e_firewall/ansible_linode/delete_e2e_cloud_firewall.yaml 2>&1); \
+ FAILED_COUNT=$$(echo "$$OUTPUT" | grep "failed=" | awk -F 'failed=' '{print $$2}' | awk '{print $$1}'); \
+ if [ "$$FAILED_COUNT" -gt 0 ]; then \
+ echo "Playbook execution failed:"; \
+ echo "$$OUTPUT"; \
+ exit 1; \
else \
- echo "Failed to delete e2e firewall"; \
- fi
+ echo "E2E Cloud firewall created successfully."; \
+ fi
+
+update-test-submodules:
+ @git submodule update --init
-test: integration-test delete-e2e-firewall
+test: integration-test
testall:
./scripts/test_all.sh
From 3b2467175ee5c174c1da8d8642ae2d84cd804cd8 Mon Sep 17 00:00:00 2001
From: Ye Chen <127243817+yec-akamai@users.noreply.github.com>
Date: Tue, 13 Aug 2024 14:39:51 -0400
Subject: [PATCH 07/17] Project: Image Services Gen2 (#560)
* new: Support tagging in image module; Migrate `image_info` to use InfoModule (#535)
* init
* lint
* new: Support replication in `image` module (#539)
* replication
* register action
* add LA note
* small fix
* lint
* rename
* oops
* restrict replica_regions; add wait_for_replications.
---
docs/modules/image.md | 41 +++++-
docs/modules/image_info.md | 26 +++-
docs/modules/image_list.md | 23 ++-
plugins/module_utils/doc_fragments/image.py | 35 ++++-
.../module_utils/doc_fragments/image_list.py | 23 ++-
plugins/modules/image.py | 86 ++++++++++-
plugins/modules/image_info.py | 139 +++++-------------
requirements.txt | 2 +-
.../targets/image_basic/tasks/main.yaml | 62 +++++++-
9 files changed, 302 insertions(+), 135 deletions(-)
diff --git a/docs/modules/image.md b/docs/modules/image.md
index 1b1d3b17..9b13dc08 100644
--- a/docs/modules/image.md
+++ b/docs/modules/image.md
@@ -20,6 +20,8 @@ Manage a Linode Image.
label: my-image
description: Created using Ansible!
disk_id: 12345
+ tags:
+ - test
state: present
```
@@ -29,6 +31,22 @@ Manage a Linode Image.
label: my-image
description: Created using Ansible!
source_file: myimage.img.gz
+ tags:
+ - test
+ state: present
+```
+
+```yaml
+- name: Replicate an image
+ linode.cloud.image:
+ label: my-image
+ description: Created using Ansible!
+ disk_id: 12345
+ tags:
+ - test
+ replica_regions:
+ - us-east
+ - us-central
state: present
```
@@ -54,6 +72,9 @@ Manage a Linode Image.
| `source_file` | `str` | Optional | An image file to create this image with. **(Conflicts With: `disk_id`)** |
| `wait` | `bool` | Optional | Wait for the image to have status `available` before returning. **(Default: `True`)** |
| `wait_timeout` | `int` | Optional | The amount of time, in seconds, to wait for an image to have status `available`. **(Default: `600`)** |
+| `tags` | `list` | Optional | A list of customized tags of this new Image. **(Updatable)** |
+| `replica_regions` | `list` | Optional | A list of regions that customer wants to replicate this image in. At least one available region must be provided and only core regions allowed. Existing images in the regions not passed will be removed. NOTE: Image replication may not currently be available to all users. **(Updatable)** |
+| `wait_for_replications` | `bool` | Optional | Wait for the all the replications `available` before returning. **(Default: `False`)** |
## Return Values
@@ -64,19 +85,31 @@ Manage a Linode Image.
{
"capabilities": [],
"created": "2021-08-14T22:44:02",
- "created_by": "linode",
+ "created_by": "my-account",
"deprecated": false,
"description": "Example Image description.",
"eol": "2026-07-01T04:00:00",
"expiry": null,
- "id": "linode/debian11",
+ "id": "private/123",
"is_public": true,
- "label": "Debian 11",
+ "label": "my-image",
"size": 2500,
"status": null,
"type": "manual",
"updated": "2021-08-14T22:44:02",
- "vendor": "Debian"
+ "vendor": "Debian",
+ "tags": ["test"],
+ "total_size": 5000,
+ "regions": [
+ {
+ "region": "us-east",
+ "status": "available"
+ },
+ {
+ "region": "us-central",
+ "status": "pending"
+ }
+ ]
}
```
- See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-image) for a list of returned fields
diff --git a/docs/modules/image_info.md b/docs/modules/image_info.md
index 5072433f..6ede4285 100644
--- a/docs/modules/image_info.md
+++ b/docs/modules/image_info.md
@@ -31,31 +31,43 @@ Get info about a Linode Image.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `id` | `str` | Optional | The ID of the image. **(Conflicts With: `label`)** |
-| `label` | `str` | Optional | The label of the image. **(Conflicts With: `id`)** |
+| `label` | `str` | Optional | The label of the Image to resolve. **(Conflicts With: `id`)** |
+| `id` | `str` | Optional | The ID of the Image to resolve. **(Conflicts With: `label`)** |
## Return Values
-- `image` - The image in JSON serialized form.
+- `image` - The returned Image.
- Sample Response:
```json
{
"capabilities": [],
"created": "2021-08-14T22:44:02",
- "created_by": "linode",
+ "created_by": "my-account",
"deprecated": false,
"description": "Example Image description.",
"eol": "2026-07-01T04:00:00",
"expiry": null,
- "id": "linode/debian11",
+ "id": "private/123",
"is_public": true,
- "label": "Debian 11",
+ "label": "my-image",
"size": 2500,
"status": null,
"type": "manual",
"updated": "2021-08-14T22:44:02",
- "vendor": "Debian"
+ "vendor": "Debian",
+ "tags": ["test"],
+ "total_size": 5000,
+ "regions": [
+ {
+ "region": "us-east",
+ "status": "available"
+ },
+ {
+ "region": "us-central",
+ "status": "pending"
+ }
+ ]
}
```
- See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-image) for a list of returned fields
diff --git a/docs/modules/image_list.md b/docs/modules/image_list.md
index ac8a37da..03345cf8 100644
--- a/docs/modules/image_list.md
+++ b/docs/modules/image_list.md
@@ -61,20 +61,31 @@ List and filter on Images.
[
{
"created":"2021-08-14T22:44:02",
- "created_by":"linode",
+ "created_by":"my-account",
"deprecated":false,
"description":"Example Image description.",
"eol":"2026-07-01T04:00:00",
"expiry":null,
- "id":"linode/debian11",
- "is_public":true,
- "label":"Debian 11",
+ "id":"private/123",
+ "is_public":false,
+ "label":"test",
"size":2500,
"status":null,
"type":"manual",
"updated":"2021-08-14T22:44:02",
- "vendor":"Debian"
- }
+ "vendor":"Debian",
+ "tags": ["test"],
+ "total_size": 5000,
+ "regions": [
+ {
+ "region": "us-east",
+ "status": "available"
+ },
+ {
+ "region": "us-central",
+ "status": "pending"
+ }]
+ }
]
```
- See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-images) for a list of returned fields
diff --git a/plugins/module_utils/doc_fragments/image.py b/plugins/module_utils/doc_fragments/image.py
index daf80342..a6e70cb2 100644
--- a/plugins/module_utils/doc_fragments/image.py
+++ b/plugins/module_utils/doc_fragments/image.py
@@ -6,13 +6,28 @@
label: my-image
description: Created using Ansible!
disk_id: 12345
+ tags:
+ - test
state: present''', '''
- name: Create a basic image from a file
linode.cloud.image:
label: my-image
description: Created using Ansible!
source_file: myimage.img.gz
+ tags:
+ - test
state: present''', '''
+- name: Replicate an image
+ linode.cloud.image:
+ label: my-image
+ description: Created using Ansible!
+ disk_id: 12345
+ tags:
+ - test
+ replica_regions:
+ - us-east
+ - us-central
+ state: present''', '''
- name: Delete an image
linode.cloud.image:
label: my-image
@@ -21,17 +36,29 @@
result_image_samples = ['''{
"capabilities": [],
"created": "2021-08-14T22:44:02",
- "created_by": "linode",
+ "created_by": "my-account",
"deprecated": false,
"description": "Example Image description.",
"eol": "2026-07-01T04:00:00",
"expiry": null,
- "id": "linode/debian11",
+ "id": "private/123",
"is_public": true,
- "label": "Debian 11",
+ "label": "my-image",
"size": 2500,
"status": null,
"type": "manual",
"updated": "2021-08-14T22:44:02",
- "vendor": "Debian"
+ "vendor": "Debian",
+ "tags": ["test"],
+ "total_size": 5000,
+ "regions": [
+ {
+ "region": "us-east",
+ "status": "available"
+ },
+ {
+ "region": "us-central",
+ "status": "pending"
+ }
+ ]
}''']
diff --git a/plugins/module_utils/doc_fragments/image_list.py b/plugins/module_utils/doc_fragments/image_list.py
index 9d9e2aa0..589d39c9 100644
--- a/plugins/module_utils/doc_fragments/image_list.py
+++ b/plugins/module_utils/doc_fragments/image_list.py
@@ -17,18 +17,29 @@
result_images_samples = ['''[
{
"created":"2021-08-14T22:44:02",
- "created_by":"linode",
+ "created_by":"my-account",
"deprecated":false,
"description":"Example Image description.",
"eol":"2026-07-01T04:00:00",
"expiry":null,
- "id":"linode/debian11",
- "is_public":true,
- "label":"Debian 11",
+ "id":"private/123",
+ "is_public":false,
+ "label":"test",
"size":2500,
"status":null,
"type":"manual",
"updated":"2021-08-14T22:44:02",
- "vendor":"Debian"
- }
+ "vendor":"Debian",
+ "tags": ["test"],
+ "total_size": 5000,
+ "regions": [
+ {
+ "region": "us-east",
+ "status": "available"
+ },
+ {
+ "region": "us-central",
+ "status": "pending"
+ }]
+ }
]''']
diff --git a/plugins/modules/image.py b/plugins/modules/image.py
index c59ea959..7a34c7c5 100644
--- a/plugins/modules/image.py
+++ b/plugins/modules/image.py
@@ -89,6 +89,31 @@
"have status `available`."
],
),
+ "tags": SpecField(
+ type=FieldType.list,
+ element_type=FieldType.string,
+ editable=True,
+ description=["A list of customized tags of this new Image."],
+ ),
+ # `regions` send to API for image replication
+ "replica_regions": SpecField(
+ type=FieldType.list,
+ element_type=FieldType.string,
+ editable=True,
+ description=[
+ "A list of regions that customer wants to replicate this image in. "
+ "At least one available region must be provided and only core regions allowed. "
+ "Existing images in the regions not passed will be removed. "
+ "NOTE: Image replication may not currently be available to all users.",
+ ],
+ ),
+ "wait_for_replications": SpecField(
+ type=FieldType.bool,
+ default=False,
+ description=[
+ "Wait for the all the replications `available` before returning."
+ ],
+ ),
}
SPECDOC_META = SpecDocMeta(
@@ -107,7 +132,7 @@
},
)
-MUTABLE_FIELDS = {"description"}
+MUTABLE_FIELDS = {"description", "tags"}
DOCUMENTATION = r"""
"""
@@ -165,11 +190,38 @@ def poll_func() -> bool:
except polling.TimeoutException:
self.fail("failed to wait for image status: timeout period expired")
+ def _wait_for_image_replication_status(
+ self, image: Image, status: Set[str]
+ ) -> None:
+ def poll_func() -> bool:
+ image._api_get()
+ for region in image.regions:
+ if region.status not in status:
+ return False
+
+ return True
+
+ # Initial attempt
+ if poll_func():
+ return
+
+ try:
+ polling.poll(
+ poll_func,
+ step=10,
+ timeout=self._timeout_ctx.seconds_remaining,
+ )
+ except polling.TimeoutException:
+ self.fail(
+ "failed to wait for image replication status: timeout period expired"
+ )
+
def _create_image_from_disk(self) -> Optional[Image]:
disk_id = self.module.params.get("disk_id")
label = self.module.params.get("label")
description = self.module.params.get("description")
cloud_init = self.module.params.get("cloud_init")
+ tags = self.module.params.get("tags")
try:
return self.client.images.create(
@@ -177,6 +229,7 @@ def _create_image_from_disk(self) -> Optional[Image]:
label=label,
description=description,
cloud_init=cloud_init,
+ tags=tags,
)
except Exception as exception:
return self.fail(
@@ -189,6 +242,7 @@ def _create_image_from_file(self) -> Optional[Image]:
region = self.module.params.get("region")
source_file = self.module.params.get("source_file")
cloud_init = self.module.params.get("cloud_init")
+ tags = self.module.params.get("tags")
if not os.path.exists(source_file):
return self.fail(
@@ -198,7 +252,11 @@ def _create_image_from_file(self) -> Optional[Image]:
# Create an image upload
try:
image, upload_to = self.client.images.create_upload(
- label, region, description=description, cloud_init=cloud_init
+ label,
+ region,
+ description=description,
+ cloud_init=cloud_init,
+ tags=tags,
)
except Exception as exception:
return self.fail(
@@ -260,6 +318,30 @@ def _handle_present(self) -> None:
self._update_image(image)
+ replica_regions = params.get("replica_regions")
+ new_regions: list = [] if replica_regions is None else replica_regions
+ old_regions = [r.region for r in image.regions]
+
+ # Replicate image in new regions
+ if replica_regions is not None and new_regions != old_regions:
+ if len(new_regions) == 0 or (
+ not set(new_regions) & set(old_regions)
+ ):
+ return self.fail(
+ msg="failed to replicate image {0}: replica_regions value {1} is invalid. "
+ "At least one available region must be provided.".format(
+ label, new_regions
+ )
+ )
+
+ image.replicate(new_regions)
+ self.register_action(
+ "Replicated image {0} in regions {1}".format(label, new_regions)
+ )
+
+ if params.get("wait_for_replications"):
+ self._wait_for_image_replication_status(image, {"available"})
+
# Force lazy-loading
image._api_get()
diff --git a/plugins/modules/image_info.py b/plugins/modules/image_info.py
index db2ab5a0..4902eb74 100644
--- a/plugins/modules/image_info.py
+++ b/plugins/modules/image_info.py
@@ -5,118 +5,51 @@
from __future__ import absolute_import, division, print_function
-from typing import Any, Optional
-
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.image as docs_parent
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.image_info 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_common_info import (
+ InfoModule,
+ InfoModuleAttr,
+ InfoModuleResult,
)
from ansible_collections.linode.cloud.plugins.module_utils.linode_helper import (
- filter_null_values,
-)
-from ansible_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
+ safe_find,
)
+from ansible_specdoc.objects import FieldType
from linode_api4 import Image
-spec = {
- # Disable the default values
- "state": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "id": SpecField(
- type=FieldType.string,
- description=["The ID of the image."],
- conflicts_with=["label"],
- ),
- "label": SpecField(
- type=FieldType.string,
- description=["The label of the image."],
- conflicts_with=["id"],
- ),
-}
-
-SPECDOC_META = SpecDocMeta(
- description=["Get info about a Linode Image."],
- requirements=global_requirements,
- author=global_authors,
- options=spec,
+module = InfoModule(
examples=docs.specdoc_examples,
- return_values={
- "image": SpecReturnValue(
- description="The image in JSON serialized form.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-image",
- type=FieldType.dict,
- sample=docs_parent.result_image_samples,
- )
- },
+ primary_result=InfoModuleResult(
+ display_name="Image",
+ field_name="image",
+ field_type=FieldType.dict,
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-image",
+ samples=docs_parent.result_image_samples,
+ ),
+ attributes=[
+ InfoModuleAttr(
+ name="id",
+ display_name="ID",
+ type=FieldType.string,
+ get=lambda client, params: client.load(
+ Image, params.get("id")
+ )._raw_json,
+ ),
+ InfoModuleAttr(
+ name="label",
+ display_name="label",
+ type=FieldType.string,
+ get=lambda client, params: safe_find(
+ client.images,
+ Image.label == params.get("label"),
+ raise_not_found=True,
+ )._raw_json,
+ ),
+ ],
)
-DOCUMENTATION = r"""
-"""
-EXAMPLES = r"""
-"""
-RETURN = r"""
-"""
-
-
-class Module(LinodeModuleBase):
- """Module for getting info about a Linode user"""
-
- def __init__(self) -> None:
- self.module_arg_spec = SPECDOC_META.ansible_spec
- self.results = {"image": None}
-
- super().__init__(
- module_arg_spec=self.module_arg_spec,
- required_one_of=[("id", "label")],
- mutually_exclusive=[("id", "label")],
- )
-
- def _get_image_by_label(self, label: str) -> Optional[Image]:
- try:
- return self.client.images(Image.label == label)[0]
- except IndexError:
- return self.fail(
- msg="failed to get image with label {0}: "
- "image does not exist".format(label)
- )
- except Exception as exception:
- return self.fail(
- msg="failed to get image {0}: {1}".format(label, exception)
- )
-
- def _get_image_by_id(self, image_id: int) -> Image:
- return self._get_resource_by_id(Image, image_id)
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for user info module"""
-
- params = filter_null_values(self.module.params)
-
- if "id" in params:
- self.results["image"] = self._get_image_by_id(
- params.get("id")
- )._raw_json
-
- if "label" in params:
- self.results["image"] = self._get_image_by_label(
- params.get("label")
- )._raw_json
-
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the module"""
- Module()
-
+SPECDOC_META = module.spec
if __name__ == "__main__":
- main()
+ module.run()
diff --git a/requirements.txt b/requirements.txt
index ba864887..856067fa 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-linode-api4>=5.19.0
+linode-api4>=5.20.0
polling>=0.3.2
types-requests==2.32.0.20240712
ansible-specdoc>=0.0.15
diff --git a/tests/integration/targets/image_basic/tasks/main.yaml b/tests/integration/targets/image_basic/tasks/main.yaml
index 0404cb01..f0b72f18 100644
--- a/tests/integration/targets/image_basic/tasks/main.yaml
+++ b/tests/integration/targets/image_basic/tasks/main.yaml
@@ -1,4 +1,4 @@
-- name: stackscript_basic
+- name: image_basic
block:
- set_fact:
r: "{{ 1000000000 | random }}"
@@ -6,7 +6,7 @@
- name: Create an instance to image
linode.cloud.instance:
label: 'ansible-test-{{ r }}'
- region: us-ord
+ region: us-east
type: g6-standard-1
image: linode/alpine3.19
state: present
@@ -19,6 +19,9 @@
disk_id: '{{ instance_create.disks.0.id }}'
description: 'cool'
cloud_init: true
+ tags:
+ - 'test-tag'
+ - 'image-gen2'
state: present
register: image_create
@@ -28,6 +31,8 @@
- image_create.image.status == 'available'
- image_create.image.description == 'cool'
- image_create.image.capabilities[0] == 'cloud-init'
+ - image_create.image.tags | length == 2
+ - image_create.image.size == image_create.image.total_size
- name: Get image_info by ID
linode.cloud.image_info:
@@ -58,6 +63,8 @@
label: 'ansible-test-{{ r }}'
disk_id: '{{ instance_create.disks.0.id }}'
description: 'cool2'
+ tags:
+ - 'test'
state: present
register: image_update
@@ -66,12 +73,62 @@
that:
- image_update.image.status == 'available'
- image_update.image.description == 'cool2'
+ - image_update.image.tags[0] == 'test'
+
+ - name: Replicate the image
+ linode.cloud.image:
+ label: 'ansible-test-{{ r }}'
+ disk_id: '{{ instance_create.disks.0.id }}'
+ description: 'cool2'
+ tags:
+ - 'test'
+ replica_regions:
+ - 'us-east'
+ - 'eu-west'
+ wait_for_replications: yes
+ wait_timeout: 1200
+ state: present
+ register: image_replicate
+
+ - name: Assert image is replicated in 2 regions
+ assert:
+ that:
+ - image_replicate.changed
+ - image_replicate.image.id == image_create.image.id
+ - image_replicate.image.regions | length == 2
+ - image_replicate.image.regions[0].region == 'us-east'
+ - image_replicate.image.regions[0].status == 'available'
+ - image_replicate.image.regions[1].status == 'available'
+
+ - name: Remove one of the image replicas
+ linode.cloud.image:
+ label: 'ansible-test-{{ r }}'
+ disk_id: '{{ instance_create.disks.0.id }}'
+ description: 'cool2'
+ tags:
+ - 'test'
+ replica_regions:
+ - 'eu-west'
+ wait_for_replications: yes
+ wait_timeout: 1200
+ state: present
+ register: image_replicate
+
+ - name: Assert image only has one replica left
+ assert:
+ that:
+ - image_replicate.changed
+ - image_replicate.image.regions | length == 1
+ - image_replicate.image.regions[0].region == 'eu-west'
+ - image_replicate.image.regions[0].status == 'available'
- name: Overwrite the image
linode.cloud.image:
label: 'ansible-test-{{ r }}'
disk_id: '{{ instance_create.disks.0.id }}'
description: 'yooo'
+ tags:
+ - 'new-tag'
recreate: yes
wait: no
state: present
@@ -83,6 +140,7 @@
- image_recreate.changed
- image_recreate.image.id != image_create.image.id
- image_recreate.image.description == 'yooo'
+ - image_recreate.image.tags[0] == 'new-tag'
always:
- ignore_errors: yes
From 149d3b3e4e8d3efdfd12c592024e9423ab770d93 Mon Sep 17 00:00:00 2001
From: Jacob Riddle <87780794+jriddle-linode@users.noreply.github.com>
Date: Wed, 14 Aug 2024 11:46:16 -0400
Subject: [PATCH 08/17] update global authors (#555)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## đź“ť Description
**What does this PR do and why is this change necessary?**
Update global authors to reflect current staff.
---
docs/modules/account_availability_info.md | 2 +-
docs/modules/account_availability_list.md | 2 +-
docs/modules/ip_share.md | 2 +-
docs/modules/placement_group_info.md | 2 +-
docs/modules/placement_group_list.md | 2 +-
docs/modules/vlan_info.md | 2 +-
docs/modules/vlan_list.md | 2 +-
plugins/module_utils/linode_docs.py | 9 +++++++--
8 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/docs/modules/account_availability_info.md b/docs/modules/account_availability_info.md
index 7536aef9..c94e2792 100644
--- a/docs/modules/account_availability_info.md
+++ b/docs/modules/account_availability_info.md
@@ -2,7 +2,7 @@
Get info about a Linode Account Availability.
-**:warning: This module makes use of beta endpoints and requires the `api_version` field be explicitly set to `v4beta`.**
+WARNING! This module makes use of beta endpoints and requires the C(api_version) field be explicitly set to C(v4beta).
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
diff --git a/docs/modules/account_availability_list.md b/docs/modules/account_availability_list.md
index b72b278e..5c04aecd 100644
--- a/docs/modules/account_availability_list.md
+++ b/docs/modules/account_availability_list.md
@@ -2,7 +2,7 @@
List and filter on Account Availabilities.
-**:warning: This module makes use of beta endpoints and requires the `api_version` field be explicitly set to `v4beta`.**
+WARNING! This module makes use of beta endpoints and requires the C(api_version) field be explicitly set to C(v4beta).
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
diff --git a/docs/modules/ip_share.md b/docs/modules/ip_share.md
index 96745f23..d69ff4c7 100644
--- a/docs/modules/ip_share.md
+++ b/docs/modules/ip_share.md
@@ -2,7 +2,7 @@
Manage the Linode shared IPs.
-**:warning: This module makes use of beta endpoints and requires the `api_version` field be explicitly set to `v4beta`.**
+WARNING! This module makes use of beta endpoints and requires the C(api_version) field be explicitly set to C(v4beta).
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
diff --git a/docs/modules/placement_group_info.md b/docs/modules/placement_group_info.md
index 308568fa..79799fa4 100644
--- a/docs/modules/placement_group_info.md
+++ b/docs/modules/placement_group_info.md
@@ -2,7 +2,7 @@
Get info about a Linode Placement Group.
-**:warning: This module makes use of beta endpoints and requires the `api_version` field be explicitly set to `v4beta`.**
+WARNING! This module makes use of beta endpoints and requires the C(api_version) field be explicitly set to C(v4beta).
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
diff --git a/docs/modules/placement_group_list.md b/docs/modules/placement_group_list.md
index 26bcad57..8b74e292 100644
--- a/docs/modules/placement_group_list.md
+++ b/docs/modules/placement_group_list.md
@@ -2,7 +2,7 @@
List and filter on Placement Groups.
-**:warning: This module makes use of beta endpoints and requires the `api_version` field be explicitly set to `v4beta`.**
+WARNING! This module makes use of beta endpoints and requires the C(api_version) field be explicitly set to C(v4beta).
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
diff --git a/docs/modules/vlan_info.md b/docs/modules/vlan_info.md
index e26ee87c..3d31800e 100644
--- a/docs/modules/vlan_info.md
+++ b/docs/modules/vlan_info.md
@@ -2,7 +2,7 @@
Get info about a Linode VLAN.
-**:warning: This module makes use of beta endpoints and requires the `api_version` field be explicitly set to `v4beta`.**
+WARNING! This module makes use of beta endpoints and requires the C(api_version) field be explicitly set to C(v4beta).
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
diff --git a/docs/modules/vlan_list.md b/docs/modules/vlan_list.md
index c2b676b3..893df5a1 100644
--- a/docs/modules/vlan_list.md
+++ b/docs/modules/vlan_list.md
@@ -2,7 +2,7 @@
List and filter on Linode VLANs.
-**:warning: This module makes use of beta endpoints and requires the `api_version` field be explicitly set to `v4beta`.**
+WARNING! This module makes use of beta endpoints and requires the C(api_version) field be explicitly set to C(v4beta).
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
diff --git a/plugins/module_utils/linode_docs.py b/plugins/module_utils/linode_docs.py
index bd8f3c5f..8e860f9e 100644
--- a/plugins/module_utils/linode_docs.py
+++ b/plugins/module_utils/linode_docs.py
@@ -6,11 +6,16 @@
"Phillip Campbell (@phillc)",
"Lena Garber (@lbgarber)",
"Jacob Riddle (@jriddle)",
+ "Zhiwei Liang (@zliang)",
+ "Ye Chen (@yechen)",
+ "Youjung Kim (@ykim)",
+ "Vinay Shanthegowda (@vshanthe)",
+ "Erik Zilber (@ezilber)",
]
global_requirements = ["python >= 3"]
BETA_DISCLAIMER = (
- "**:warning: This module makes use of beta endpoints and requires the `api_version` "
- "field be explicitly set to `v4beta`.**"
+ "WARNING! This module makes use of beta endpoints and requires the C(api_version) "
+ "field be explicitly set to C(v4beta)."
)
From 217b05fc19807b09c841e1a91a58004b343e13f0 Mon Sep 17 00:00:00 2001
From: Ye Chen <127243817+yec-akamai@users.noreply.github.com>
Date: Thu, 15 Aug 2024 12:29:44 -0400
Subject: [PATCH 09/17] profile_info (#558)
---
docs/modules/profile_info.md | 2 +-
plugins/module_utils/linode_common_info.py | 25 +++++--
plugins/modules/profile_info.py | 81 +++++-----------------
3 files changed, 38 insertions(+), 70 deletions(-)
diff --git a/docs/modules/profile_info.md b/docs/modules/profile_info.md
index 436d1299..ebdcef6c 100644
--- a/docs/modules/profile_info.md
+++ b/docs/modules/profile_info.md
@@ -22,7 +22,7 @@ Get info about a Linode Profile.
## Return Values
-- `profile` - The profile info in JSON serialized form.
+- `profile` - The returned Profile.
- Sample Response:
```json
diff --git a/plugins/module_utils/linode_common_info.py b/plugins/module_utils/linode_common_info.py
index 36f01efc..630a892f 100644
--- a/plugins/module_utils/linode_common_info.py
+++ b/plugins/module_utils/linode_common_info.py
@@ -72,7 +72,8 @@ class InfoModuleResult:
samples (Optional[List[str]]): A list of sample results for this field.
get (Optional[Callable]): A function to call out to the API and return the data
for this field.
- NOTE: This is only relevant for secondary results.
+ NOTE: This is only relevant for secondary results
+ or primary result without any attributes.
"""
field_name: str
@@ -82,7 +83,7 @@ class InfoModuleResult:
docs_url: Optional[str] = None
samples: Optional[List[str]] = None
get: Optional[
- Callable[[LinodeClient, Dict[str, Any], Dict[str, Any]], Any]
+ Callable[[LinodeClient, Dict[str, Any], Optional[Dict[str, Any]]], Any]
] = None
@@ -138,6 +139,16 @@ def exec_module(self, **kwargs: Any) -> Optional[dict]:
)
break
+ if primary_result is None:
+ # Get the primary result using the result get function
+ try:
+ primary_result = self.primary_result.get(self.client, kwargs)
+ except Exception as exception:
+ self.fail(
+ msg="Failed to get result for "
+ f"{self.primary_result.display_name}: {exception}"
+ )
+
if primary_result is None:
raise ValueError("Expected a result; got None")
@@ -220,10 +231,14 @@ def run(self) -> None:
"""
Initializes and runs the info module.
"""
- attribute_names = [v.name for v in self.attributes]
+ attribute_names = (
+ [[v.name for v in self.attributes]]
+ if len(self.attributes) > 0
+ else None
+ )
super().__init__(
module_arg_spec=self.module_arg_spec,
- required_one_of=[attribute_names],
- mutually_exclusive=[attribute_names],
+ required_one_of=attribute_names,
+ mutually_exclusive=attribute_names,
)
diff --git a/plugins/modules/profile_info.py b/plugins/modules/profile_info.py
index 0f9da33e..d81a2eec 100644
--- a/plugins/modules/profile_info.py
+++ b/plugins/modules/profile_info.py
@@ -1,50 +1,31 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-"""This module allows users to retrieve information about the current Linode profile."""
+"""This module contains all of the functionality for Linode Profile info."""
from __future__ import absolute_import, division, print_function
-from typing import Any, List, Optional
-
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.profile_info 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_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
+from ansible_collections.linode.cloud.plugins.module_utils.linode_common_info import (
+ InfoModule,
+ InfoModuleResult,
)
+from ansible_specdoc.objects import FieldType
-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),
-}
-
-
-SPECDOC_META = SpecDocMeta(
- description=["Get info about a Linode Profile."],
- requirements=global_requirements,
- author=global_authors,
- options=spec,
+module = InfoModule(
examples=docs.specdoc_examples,
- return_values={
- "profile": SpecReturnValue(
- description="The profile info in JSON serialized form.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-profile",
- type=FieldType.dict,
- sample=docs.result_profile_samples,
- )
- },
+ primary_result=InfoModuleResult(
+ display_name="Profile",
+ field_name="profile",
+ field_type=FieldType.dict,
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-profile",
+ samples=docs.result_profile_samples,
+ get=lambda client, params: client.profile()._raw_json,
+ ),
)
+SPECDOC_META = module.spec
+
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
@@ -52,33 +33,5 @@
RETURN = r"""
"""
-
-class Module(LinodeModuleBase):
- """Module for getting info about a Linode Profile"""
-
- def __init__(self) -> None:
- self.required_one_of: List[str] = []
- self.results = {"profile": None}
-
- self.module_arg_spec = SPECDOC_META.ansible_spec
-
- super().__init__(
- module_arg_spec=self.module_arg_spec,
- required_one_of=self.required_one_of,
- )
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for volume info module"""
-
- self.results["profile"] = self.client.profile()._raw_json
-
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the profile_info module"""
- Module()
-
-
if __name__ == "__main__":
- main()
+ module.run()
From 31729347df9cd33cd1bd0a62e65633111601cb87 Mon Sep 17 00:00:00 2001
From: Erik Zilber
Date: Fri, 16 Aug 2024 15:02:29 -0400
Subject: [PATCH 10/17] Migrate account_info (#562)
---
docs/modules/account_info.md | 2 +-
plugins/modules/account_info.py | 78 +++++++--------------------------
2 files changed, 17 insertions(+), 63 deletions(-)
diff --git a/docs/modules/account_info.md b/docs/modules/account_info.md
index a2ffd468..b52e9802 100644
--- a/docs/modules/account_info.md
+++ b/docs/modules/account_info.md
@@ -22,7 +22,7 @@ Get info about a Linode Account.
## Return Values
-- `account` - The account info in JSON serialized form.
+- `account` - The returned Account.
- Sample Response:
```json
diff --git a/plugins/modules/account_info.py b/plugins/modules/account_info.py
index e7add541..b48ac136 100644
--- a/plugins/modules/account_info.py
+++ b/plugins/modules/account_info.py
@@ -5,45 +5,27 @@
from __future__ import absolute_import, division, print_function
-from typing import Any, List, Optional
-
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.account_info 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_common_info import (
+ InfoModule,
+ InfoModuleResult,
)
-from ansible_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
-)
-
-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),
-}
+from ansible_specdoc.objects import FieldType
-SPECDOC_META = SpecDocMeta(
- description=["Get info about a Linode Account."],
- requirements=global_requirements,
- author=global_authors,
- options=spec,
+module = InfoModule(
examples=docs.specdoc_examples,
- return_values={
- "account": SpecReturnValue(
- description="The account info in JSON serialized form.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-account",
- type=FieldType.dict,
- sample=docs.result_account_samples,
- )
- },
+ primary_result=InfoModuleResult(
+ display_name="Account",
+ field_name="account",
+ field_type=FieldType.dict,
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-account",
+ samples=docs.result_account_samples,
+ get=lambda client, params: client.account()._raw_json,
+ ),
)
+SPECDOC_META = module.spec
+
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
@@ -51,33 +33,5 @@
RETURN = r"""
"""
-
-class Module(LinodeModuleBase):
- """Module for getting info about a Linode Account"""
-
- def __init__(self) -> None:
- self.required_one_of: List[str] = []
- self.results = {"account": None}
-
- self.module_arg_spec = SPECDOC_META.ansible_spec
-
- super().__init__(
- module_arg_spec=self.module_arg_spec,
- required_one_of=self.required_one_of,
- )
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for volume info module"""
-
- self.results["account"] = self.client.account()._raw_json
-
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the account_info module"""
- Module()
-
-
if __name__ == "__main__":
- main()
+ module.run()
From ce9f8aee1ba61e75cee9adc026f78fd9233a34e2 Mon Sep 17 00:00:00 2001
From: Erik Zilber
Date: Fri, 16 Aug 2024 16:50:29 -0400
Subject: [PATCH 11/17] Migrated instance related list modules (#565)
---
README.md | 6 +-
docs/modules/instance_list.md | 14 +--
docs/modules/instance_type_list.md | 14 +--
docs/modules/type_list.md | 14 +--
plugins/modules/instance_list.py | 123 +++---------------------
plugins/modules/instance_type_list.py | 132 ++++----------------------
plugins/modules/type_list.py | 126 +++---------------------
7 files changed, 64 insertions(+), 365 deletions(-)
diff --git a/README.md b/README.md
index 6932374b..bc2bcfe4 100644
--- a/README.md
+++ b/README.md
@@ -98,8 +98,8 @@ Name | Description |
[linode.cloud.event_list](./docs/modules/event_list.md)|List and filter on Linode events.|
[linode.cloud.firewall_list](./docs/modules/firewall_list.md)|List and filter on Firewalls.|
[linode.cloud.image_list](./docs/modules/image_list.md)|List and filter on Images.|
-[linode.cloud.instance_list](./docs/modules/instance_list.md)|List and filter on Linode Instances.|
-[linode.cloud.instance_type_list](./docs/modules/instance_type_list.md)|List and filter on Linode Instance Types.|
+[linode.cloud.instance_list](./docs/modules/instance_list.md)|List and filter on Instances.|
+[linode.cloud.instance_type_list](./docs/modules/instance_type_list.md)|**NOTE: This module has been deprecated in favor of `type_list`.|
[linode.cloud.lke_version_list](./docs/modules/lke_version_list.md)|List Kubernetes versions available for deployment to a Kubernetes cluster.|
[linode.cloud.nodebalancer_list](./docs/modules/nodebalancer_list.md)|List and filter on Nodebalancers.|
[linode.cloud.object_cluster_list](./docs/modules/object_cluster_list.md)|**NOTE: This module has been deprecated because it relies on deprecated API endpoints. Going forward, `region` will be the preferred way to designate where Object Storage resources should be created.**|
@@ -108,7 +108,7 @@ Name | Description |
[linode.cloud.ssh_key_list](./docs/modules/ssh_key_list.md)|List and filter on SSH keys in the Linode profile.|
[linode.cloud.stackscript_list](./docs/modules/stackscript_list.md)|List and filter on Linode stackscripts.|
[linode.cloud.token_list](./docs/modules/token_list.md)|List and filter on Linode Account tokens.|
-[linode.cloud.type_list](./docs/modules/type_list.md)|List and filter on Linode Instance Types.|
+[linode.cloud.type_list](./docs/modules/type_list.md)|List and filter on Types.|
[linode.cloud.user_list](./docs/modules/user_list.md)|List Users.|
[linode.cloud.vlan_list](./docs/modules/vlan_list.md)|List and filter on Linode VLANs.|
[linode.cloud.volume_list](./docs/modules/volume_list.md)|List and filter on Linode Volumes.|
diff --git a/docs/modules/instance_list.md b/docs/modules/instance_list.md
index 752d7066..e0b63c07 100644
--- a/docs/modules/instance_list.md
+++ b/docs/modules/instance_list.md
@@ -1,6 +1,6 @@
# instance_list
-List and filter on Linode Instances.
+List and filter on Instances.
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
@@ -32,21 +32,21 @@ List and filter on Linode Instances.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `order` | `str` | Optional | The order to list instances in. **(Choices: `desc`, `asc`; Default: `asc`)** |
-| `order_by` | `str` | Optional | The attribute to order instances by. |
-| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting instances. |
-| `count` | `int` | Optional | The number of results to return. If undefined, all results will be returned. |
+| `order` | `str` | Optional | The order to list Instances in. **(Choices: `desc`, `asc`; Default: `asc`)** |
+| `order_by` | `str` | Optional | The attribute to order Instances by. |
+| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting Instances. |
+| `count` | `int` | Optional | The number of Instances to return. If undefined, all results will be returned. |
### filters
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable attributes can be found here: https://techdocs.akamai.com/linode-api/reference/get-linode-instances |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-linode-instances). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
-- `instances` - The returned instances.
+- `instances` - The returned Instances.
- Sample Response:
```json
diff --git a/docs/modules/instance_type_list.md b/docs/modules/instance_type_list.md
index c9931d98..f3d18950 100644
--- a/docs/modules/instance_type_list.md
+++ b/docs/modules/instance_type_list.md
@@ -1,5 +1,7 @@
# instance_type_list
+**NOTE: This module has been deprecated in favor of `type_list`.
+
List and filter on Linode Instance Types.
- [Minimum Required Fields](#minimum-required-fields)
@@ -32,21 +34,21 @@ List and filter on Linode Instance Types.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `order` | `str` | Optional | The order to list instance types in. **(Choices: `desc`, `asc`; Default: `asc`)** |
-| `order_by` | `str` | Optional | The attribute to order instance types by. |
-| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting instance types. |
-| `count` | `int` | Optional | The number of results to return. If undefined, all results will be returned. |
+| `order` | `str` | Optional | The order to list Instance Types in. **(Choices: `desc`, `asc`; Default: `asc`)** |
+| `order_by` | `str` | Optional | The attribute to order Instance Types by. |
+| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting Instance Types. |
+| `count` | `int` | Optional | The number of Instance Types to return. If undefined, all results will be returned. |
### filters
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable attributes can be found here: https://techdocs.akamai.com/linode-api/reference/get-linode-types |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-linode-types). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
-- `instance_types` - The returned instance types.
+- `instance_types` - The returned Instance Types.
- Sample Response:
```json
diff --git a/docs/modules/type_list.md b/docs/modules/type_list.md
index 58f90407..e6bf7fac 100644
--- a/docs/modules/type_list.md
+++ b/docs/modules/type_list.md
@@ -1,6 +1,6 @@
# type_list
-List and filter on Linode Instance Types.
+List and filter on Types.
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
@@ -33,21 +33,21 @@ List and filter on Linode Instance Types.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `order` | `str` | Optional | The order to list Instance Types in. **(Choices: `desc`, `asc`; Default: `asc`)** |
-| `order_by` | `str` | Optional | The attribute to order Instance Types by. |
-| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting Instance Types. |
-| `count` | `int` | Optional | The number of results to return. If undefined, all results will be returned. |
+| `order` | `str` | Optional | The order to list Types in. **(Choices: `desc`, `asc`; Default: `asc`)** |
+| `order_by` | `str` | Optional | The attribute to order Types by. |
+| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting Types. |
+| `count` | `int` | Optional | The number of Types to return. If undefined, all results will be returned. |
### filters
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable attributes can be found here: https://techdocs.akamai.com/linode-api/reference/get-linode-types |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-linode-types). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
-- `types` - The returned Instance Types.
+- `types` - The returned Types.
- Sample Response:
```json
diff --git a/plugins/modules/instance_list.py b/plugins/modules/instance_list.py
index cf174d72..3577c29b 100644
--- a/plugins/modules/instance_list.py
+++ b/plugins/modules/instance_list.py
@@ -4,94 +4,22 @@
"""This module allows users to list Linode instances."""
from __future__ import absolute_import, division, print_function
-from typing import Any, Dict, Optional
-
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.instance_list 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 (
- construct_api_filter,
- get_all_paginated,
+from ansible_collections.linode.cloud.plugins.module_utils.linode_common_list import (
+ ListModule,
)
-from ansible_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
-)
-
-spec_filter = {
- "name": SpecField(
- type=FieldType.string,
- required=True,
- description=[
- "The name of the field to filter on.",
- "Valid filterable attributes can be found here: "
- "https://techdocs.akamai.com/linode-api/reference/get-linode-instances",
- ],
- ),
- "values": SpecField(
- type=FieldType.list,
- element_type=FieldType.string,
- required=True,
- description=[
- "A list of values to allow for this field.",
- "Fields will pass this filter if at least one of these values matches.",
- ],
- ),
-}
-spec = {
- # Disable the default values
- "state": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "label": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "order": SpecField(
- type=FieldType.string,
- description=["The order to list instances in."],
- default="asc",
- choices=["desc", "asc"],
- ),
- "order_by": SpecField(
- type=FieldType.string,
- description=["The attribute to order instances by."],
- ),
- "filters": SpecField(
- type=FieldType.list,
- element_type=FieldType.dict,
- suboptions=spec_filter,
- description=["A list of filters to apply to the resulting instances."],
- ),
- "count": SpecField(
- type=FieldType.integer,
- description=[
- "The number of results to return.",
- "If undefined, all results will be returned.",
- ],
- ),
-}
-
-SPECDOC_META = SpecDocMeta(
- description=["List and filter on Linode Instances."],
- requirements=global_requirements,
- author=global_authors,
- options=spec,
+module = ListModule(
+ result_display_name="Instances",
+ result_field_name="instances",
+ endpoint_template="/linode/instances",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-linode-instances",
examples=docs.specdoc_examples,
- return_values={
- "instances": SpecReturnValue(
- description="The returned instances.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-linode-instances",
- type=FieldType.list,
- elements=FieldType.dict,
- sample=docs.result_images_samples,
- )
- },
+ result_samples=docs.result_images_samples,
)
+SPECDOC_META = module.spec
+
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
@@ -99,34 +27,5 @@
RETURN = r"""
"""
-
-class Module(LinodeModuleBase):
- """Module for getting a list of Linode instances"""
-
- def __init__(self) -> None:
- self.module_arg_spec = SPECDOC_META.ansible_spec
- self.results: Dict[str, Any] = {"instances": []}
-
- super().__init__(module_arg_spec=self.module_arg_spec)
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for instance list module"""
-
- filter_dict = construct_api_filter(self.module.params)
-
- self.results["instances"] = get_all_paginated(
- self.client,
- "/linode/instances",
- filter_dict,
- num_results=self.module.params["count"],
- )
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the module"""
- Module()
-
-
if __name__ == "__main__":
- main()
+ module.run()
diff --git a/plugins/modules/instance_type_list.py b/plugins/modules/instance_type_list.py
index 63a096f0..fe83f01c 100644
--- a/plugins/modules/instance_type_list.py
+++ b/plugins/modules/instance_type_list.py
@@ -1,101 +1,32 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-"""This module allows users to list Linode instance types."""
+"""This module allows users to list Linode instance types. Deprecated in favor of type_list."""
from __future__ import absolute_import, division, print_function
-from typing import Any, Dict, Optional
-
from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import (
instance_type_list 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 (
- construct_api_filter,
- get_all_paginated,
+from ansible_collections.linode.cloud.plugins.module_utils.linode_common_list import (
+ ListModule,
)
-from ansible_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
-)
-
-spec_filter = {
- "name": SpecField(
- type=FieldType.string,
- required=True,
- description=[
- "The name of the field to filter on.",
- "Valid filterable attributes can be found here: "
- "https://techdocs.akamai.com/linode-api/reference/get-linode-types",
- ],
- ),
- "values": SpecField(
- type=FieldType.list,
- element_type=FieldType.string,
- required=True,
- description=[
- "A list of values to allow for this field.",
- "Fields will pass this filter if at least one of these values matches.",
- ],
- ),
-}
-spec = {
- # Disable the default values
- "state": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "label": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "order": SpecField(
- type=FieldType.string,
- description=["The order to list instance types in."],
- default="asc",
- choices=["desc", "asc"],
- ),
- "order_by": SpecField(
- type=FieldType.string,
- description=["The attribute to order instance types by."],
- ),
- "filters": SpecField(
- type=FieldType.list,
- element_type=FieldType.dict,
- suboptions=spec_filter,
- description=[
- "A list of filters to apply to the resulting instance types."
- ],
- ),
- "count": SpecField(
- type=FieldType.integer,
- description=[
- "The number of results to return.",
- "If undefined, all results will be returned.",
- ],
- ),
-}
-
-SPECDOC_META = SpecDocMeta(
- description=["List and filter on Linode Instance Types."],
- requirements=global_requirements,
- author=global_authors,
- options=spec,
+module = ListModule(
+ result_display_name="Instance Types",
+ result_field_name="instance_types",
+ endpoint_template="/linode/types",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-linode-types",
examples=docs.specdoc_examples,
- return_values={
- "instance_types": SpecReturnValue(
- description="The returned instance types.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-linode-types",
- type=FieldType.list,
- elements=FieldType.dict,
- sample=docs.result_instance_type_samples,
- )
- },
+ result_samples=docs.result_instance_type_samples,
)
+module.description = [
+ "**NOTE: This module has been deprecated in favor of `type_list`.",
+ "List and filter on Linode Instance Types.",
+]
+
+SPECDOC_META = module.spec
+
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
@@ -103,34 +34,5 @@
RETURN = r"""
"""
-
-class Module(LinodeModuleBase):
- """Module for getting a list of Linode instance types"""
-
- def __init__(self) -> None:
- self.module_arg_spec = SPECDOC_META.ansible_spec
- self.results: Dict[str, Any] = {"instance_types": []}
-
- super().__init__(module_arg_spec=self.module_arg_spec)
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for instance type list module"""
-
- filter_dict = construct_api_filter(self.module.params)
-
- self.results["instance_types"] = get_all_paginated(
- self.client,
- "/linode/types",
- filter_dict,
- num_results=self.module.params["count"],
- )
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the module"""
- Module()
-
-
if __name__ == "__main__":
- main()
+ module.run()
diff --git a/plugins/modules/type_list.py b/plugins/modules/type_list.py
index 23f13b8a..12b5b819 100644
--- a/plugins/modules/type_list.py
+++ b/plugins/modules/type_list.py
@@ -2,99 +2,24 @@
# -*- coding: utf-8 -*-
"""This module allows users to list Linode Instance Types."""
-
from __future__ import absolute_import, division, print_function
-from typing import Any, Dict, Optional
-
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.type_list 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 (
- construct_api_filter,
- get_all_paginated,
-)
-from ansible_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
+from ansible_collections.linode.cloud.plugins.module_utils.linode_common_list import (
+ ListModule,
)
-spec_filter = {
- "name": SpecField(
- type=FieldType.string,
- required=True,
- description=[
- "The name of the field to filter on.",
- "Valid filterable attributes can be found here: "
- "https://techdocs.akamai.com/linode-api/reference/get-linode-types",
- ],
- ),
- "values": SpecField(
- type=FieldType.list,
- element_type=FieldType.string,
- required=True,
- description=[
- "A list of values to allow for this field.",
- "Fields will pass this filter if at least one of these values matches.",
- ],
- ),
-}
-
-spec = {
- # Disable the default values
- "state": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "label": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "order": SpecField(
- type=FieldType.string,
- description=["The order to list Instance Types in."],
- default="asc",
- choices=["desc", "asc"],
- ),
- "order_by": SpecField(
- type=FieldType.string,
- description=["The attribute to order Instance Types by."],
- ),
- "filters": SpecField(
- type=FieldType.list,
- element_type=FieldType.dict,
- suboptions=spec_filter,
- description=[
- "A list of filters to apply to the resulting Instance Types."
- ],
- ),
- "count": SpecField(
- type=FieldType.integer,
- description=[
- "The number of results to return.",
- "If undefined, all results will be returned.",
- ],
- ),
-}
-
-SPECDOC_META = SpecDocMeta(
- description=["List and filter on Linode Instance Types."],
- requirements=global_requirements,
- author=global_authors,
- options=spec,
+module = ListModule(
+ result_display_name="Types",
+ result_field_name="types",
+ endpoint_template="/linode/types",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-linode-types",
examples=docs.specdoc_examples,
- return_values={
- "types": SpecReturnValue(
- description="The returned Instance Types.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-linode-types",
- type=FieldType.list,
- elements=FieldType.dict,
- sample=docs.result_type_samples,
- )
- },
+ result_samples=docs.result_type_samples,
)
+SPECDOC_META = module.spec
+
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
@@ -102,34 +27,5 @@
RETURN = r"""
"""
-
-class Module(LinodeModuleBase):
- """Module for getting info about a Linode Instance Types"""
-
- def __init__(self) -> None:
- self.module_arg_spec = SPECDOC_META.ansible_spec
- self.results: Dict[str, Any] = {"types": []}
-
- super().__init__(module_arg_spec=self.module_arg_spec)
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for Instance Types list module"""
-
- filter_dict = construct_api_filter(self.module.params)
-
- self.results["types"] = get_all_paginated(
- self.client,
- "/linode/types",
- filter_dict,
- num_results=self.module.params["count"],
- )
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the module"""
- Module()
-
-
if __name__ == "__main__":
- main()
+ module.run()
From d0329dfc37550d7b8299d95c3a19536bada07413 Mon Sep 17 00:00:00 2001
From: Lena Garber <114949949+lgarber-akamai@users.noreply.github.com>
Date: Tue, 20 Aug 2024 10:30:20 -0400
Subject: [PATCH 12/17] ref: Migrate domain_record_info to InfoModule base
class (#563)
* Migrate domain_record_info to InfoModule base class
* Add support for info module groups
* Re-render docs; fix None params case
* fix lint
* Oops
* Correct module docstring error
---
README.md | 2 +-
docs/modules/domain_record_info.md | 12 +-
plugins/module_utils/linode_common_info.py | 89 +++++--
plugins/modules/domain_record_info.py | 242 ++++++------------
.../targets/domain_record/tasks/main.yaml | 14 +
5 files changed, 168 insertions(+), 191 deletions(-)
diff --git a/README.md b/README.md
index bc2bcfe4..6d0c4585 100644
--- a/README.md
+++ b/README.md
@@ -62,7 +62,7 @@ Name | Description |
[linode.cloud.database_mysql_info](./docs/modules/database_mysql_info.md)|Get info about a Linode MySQL Managed Database.|
[linode.cloud.database_postgresql_info](./docs/modules/database_postgresql_info.md)|Get info about a Linode PostgreSQL Managed Database.|
[linode.cloud.domain_info](./docs/modules/domain_info.md)|Get info about a Linode Domain.|
-[linode.cloud.domain_record_info](./docs/modules/domain_record_info.md)|Get info about a Linode Domain Record.|
+[linode.cloud.domain_record_info](./docs/modules/domain_record_info.md)|Get info about a Linode Domain Records.|
[linode.cloud.firewall_info](./docs/modules/firewall_info.md)|Get info about a Linode Firewall.|
[linode.cloud.image_info](./docs/modules/image_info.md)|Get info about a Linode Image.|
[linode.cloud.instance_info](./docs/modules/instance_info.md)|Get info about a Linode Instance.|
diff --git a/docs/modules/domain_record_info.md b/docs/modules/domain_record_info.md
index e7aca469..88ee0fe4 100644
--- a/docs/modules/domain_record_info.md
+++ b/docs/modules/domain_record_info.md
@@ -1,6 +1,6 @@
# domain_record_info
-Get info about a Linode Domain Record.
+Get info about a Linode Domain Records.
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
@@ -35,14 +35,14 @@ Get info about a Linode Domain Record.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `domain_id` | `int` | Optional | The ID of the parent Domain. Optional if `domain` is defined. **(Conflicts With: `domain`)** |
-| `domain` | `str` | Optional | The name of the parent Domain. Optional if `domain_id` is defined. **(Conflicts With: `domain_id`)** |
-| `id` | `int` | Optional | The unique id of the subdomain. Optional if `name` is defined. **(Conflicts With: `name`)** |
-| `name` | `str` | Optional | The name of the domain record. Optional if `id` is defined. **(Conflicts With: `id`)** |
+| `domain_id` | `int` | Optional | The ID of the Domain ID for this resource. **(Conflicts With: `domain`)** |
+| `domain` | `str` | Optional | The ID of the Domain for this resource. **(Conflicts With: `domain_id`)** |
+| `id` | `int` | Optional | The ID of the Domain Records to resolve. **(Conflicts With: `name`)** |
+| `name` | `str` | Optional | The name of the Domain Records to resolve. **(Conflicts With: `id`)** |
## Return Values
-- `record` - View a single Record on this Domain.
+- `record` - The returned Domain Records.
- Sample Response:
```json
diff --git a/plugins/module_utils/linode_common_info.py b/plugins/module_utils/linode_common_info.py
index 630a892f..709b5144 100644
--- a/plugins/module_utils/linode_common_info.py
+++ b/plugins/module_utils/linode_common_info.py
@@ -6,7 +6,8 @@
from __future__ import absolute_import, division, print_function
from dataclasses import dataclass
-from typing import Any, Callable, Dict, List, Optional
+from enum import Enum
+from typing import Any, Callable, Dict, List, Optional, Union
from ansible_collections.linode.cloud.plugins.module_utils.linode_common import (
LinodeModuleBase,
@@ -41,6 +42,28 @@ class InfoModuleParam:
type: FieldType
+class InfoModuleParamGroupPolicy(Enum):
+ """
+ Defines the policies that can be set for a param group.
+ """
+
+ EXACTLY_ONE_OF = "exactly_one_of"
+
+
+class InfoModuleParamGroup:
+ """
+ A base class representing a group of InfoModuleParams.
+ """
+
+ def __init__(
+ self,
+ *param: InfoModuleParam,
+ policies: Optional[List[InfoModuleParamGroupPolicy]] = None,
+ ):
+ self.params = param
+ self.policies = policies or set()
+
+
@dataclass
class InfoModuleAttr:
"""
@@ -94,7 +117,9 @@ def __init__(
self,
primary_result: InfoModuleResult,
secondary_results: List[InfoModuleResult] = None,
- params: List[InfoModuleParam] = None,
+ params: Optional[
+ List[Union[InfoModuleParam, InfoModuleParamGroup]]
+ ] = None,
attributes: List[InfoModuleAttr] = None,
examples: List[str] = None,
description: List[str] = None,
@@ -102,7 +127,6 @@ def __init__(
) -> None:
self.primary_result = primary_result
self.secondary_results = secondary_results or []
- self.params = params or []
self.attributes = attributes or []
self.examples = examples or []
self.description = description or [
@@ -110,6 +134,16 @@ def __init__(
]
self.requires_beta = requires_beta
+ # Singular params should be translated into groups
+ self.param_groups = [
+ (
+ entry
+ if isinstance(entry, InfoModuleParamGroup)
+ else InfoModuleParamGroup(entry)
+ )
+ for entry in params or []
+ ]
+
self.module_arg_spec = self.spec.ansible_spec
self.results: Dict[str, Any] = {
k: None
@@ -183,12 +217,21 @@ def spec(self):
}
# Add params to spec
- for param in self.params:
- options[param.name] = SpecField(
- type=param.type,
- required=True,
- description=f"The ID of the {param.display_name} for this resource.",
- )
+ for group in self.param_groups:
+ param_names = {param.name for param in group.params}
+
+ for param in group.params:
+ param_spec = SpecField(
+ type=param.type,
+ required=True,
+ description=f"The ID of the {param.display_name} for this resource.",
+ )
+
+ if InfoModuleParamGroupPolicy.EXACTLY_ONE_OF in group.policies:
+ param_spec.conflicts_with = param_names ^ {param.name}
+ param_spec.required = False
+
+ options[param.name] = param_spec
# Add attrs to spec
for attr in self.attributes:
@@ -231,14 +274,22 @@ def run(self) -> None:
"""
Initializes and runs the info module.
"""
- attribute_names = (
- [[v.name for v in self.attributes]]
- if len(self.attributes) > 0
- else None
- )
+ base_module_args = {
+ "module_arg_spec": self.module_arg_spec,
+ "required_one_of": [],
+ "mutually_exclusive": [],
+ }
- super().__init__(
- module_arg_spec=self.module_arg_spec,
- required_one_of=attribute_names,
- mutually_exclusive=attribute_names,
- )
+ attribute_names = [v.name for v in self.attributes]
+
+ if len(attribute_names) > 0:
+ base_module_args["required_one_of"].append(attribute_names)
+ base_module_args["mutually_exclusive"].append(attribute_names)
+
+ for entry in self.param_groups:
+ if InfoModuleParamGroupPolicy.EXACTLY_ONE_OF in entry.policies:
+ param_names = [param.name for param in entry.params]
+ base_module_args["required_one_of"].append(param_names)
+ base_module_args["mutually_exclusive"].append(param_names)
+
+ super().__init__(**base_module_args)
diff --git a/plugins/modules/domain_record_info.py b/plugins/modules/domain_record_info.py
index 23bdfb7b..133236f4 100644
--- a/plugins/modules/domain_record_info.py
+++ b/plugins/modules/domain_record_info.py
@@ -5,7 +5,7 @@
from __future__ import absolute_import, division, print_function
-from typing import Any, Dict, List, Optional
+from typing import Any, Dict
from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import (
domain_record as docs_parent,
@@ -13,78 +13,87 @@
from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import (
domain_record_info as docs,
)
-from ansible_collections.linode.cloud.plugins.module_utils.linode_common import (
- LinodeModuleBase,
+from ansible_collections.linode.cloud.plugins.module_utils.linode_common_info import (
+ InfoModule,
+ InfoModuleAttr,
+ InfoModuleParam,
+ InfoModuleParamGroup,
+ InfoModuleParamGroupPolicy,
+ InfoModuleResult,
)
-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 (
- paginated_list_to_json,
-)
-from ansible_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
-)
-from linode_api4 import Domain, DomainRecord
+from ansible_specdoc.objects import FieldType
+from linode_api4 import Domain, DomainRecord, LinodeClient
-linode_domain_record_info_spec = {
- # We need to overwrite attributes to exclude them as requirements
- "state": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "label": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "domain_id": SpecField(
- type=FieldType.integer,
- conflicts_with=["domain"],
- description=[
- "The ID of the parent Domain.",
- "Optional if `domain` is defined.",
- ],
- ),
- "domain": SpecField(
- type=FieldType.string,
- conflicts_with=["domain_id"],
- description=[
- "The name of the parent Domain.",
- "Optional if `domain_id` is defined.",
- ],
- ),
- "id": SpecField(
- type=FieldType.integer,
- conflicts_with=["name"],
- description=[
- "The unique id of the subdomain.",
- "Optional if `name` is defined.",
- ],
- ),
- "name": SpecField(
- type=FieldType.string,
- conflicts_with=["id"],
- description=[
- "The name of the domain record.",
- "Optional if `id` is defined.",
- ],
- ),
-}
-SPECDOC_META = SpecDocMeta(
- description=["Get info about a Linode Domain Record."],
- requirements=global_requirements,
- author=global_authors,
- options=linode_domain_record_info_spec,
- examples=docs.specdoc_examples,
- return_values={
- "record": SpecReturnValue(
- description="View a single Record on this Domain.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-domain-record",
- type=FieldType.dict,
- sample=docs_parent.result_record_samples,
+def _domain_from_params(client: LinodeClient, params: Dict[str, Any]) -> Domain:
+ domain_id = params.get("domain_id", None)
+ domain = params.get("domain", None)
+
+ if domain_id is not None:
+ return Domain(client, domain_id)
+
+ if domain is not None:
+ target_domains = client.domains(Domain.domain == domain)
+ if len(target_domains) < 1:
+ raise ValueError(f"No domain with name {domain} found")
+
+ return target_domains[0]
+
+ raise ValueError("One of domain_id or domain must be specified")
+
+
+module = InfoModule(
+ primary_result=InfoModuleResult(
+ field_name="record",
+ field_type=FieldType.dict,
+ display_name="Domain Records",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-domain-record",
+ samples=docs_parent.result_record_samples,
+ ),
+ params=[
+ InfoModuleParamGroup(
+ InfoModuleParam(
+ display_name="Domain ID",
+ name="domain_id",
+ type=FieldType.integer,
+ ),
+ InfoModuleParam(
+ display_name="Domain",
+ name="domain",
+ type=FieldType.string,
+ ),
+ policies=[InfoModuleParamGroupPolicy.EXACTLY_ONE_OF],
)
- },
+ ],
+ attributes=[
+ InfoModuleAttr(
+ display_name="ID",
+ name="id",
+ type=FieldType.integer,
+ get=lambda client, params: [
+ client.load(
+ DomainRecord,
+ params.get("id"),
+ target_parent_id=_domain_from_params(client, params).id,
+ )._raw_json
+ ],
+ ),
+ InfoModuleAttr(
+ display_name="name",
+ name="name",
+ type=FieldType.string,
+ get=lambda client, params: [
+ record._raw_json
+ for record in _domain_from_params(client, params).records
+ if record.name == params.get("name")
+ ],
+ ),
+ ],
+ examples=docs.specdoc_examples,
)
+SPECDOC_META = module.spec
+
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
@@ -92,102 +101,5 @@
RETURN = r"""
"""
-
-class LinodeDomainRecordInfo(LinodeModuleBase):
- """Module for getting info about a Linode Domain record"""
-
- def __init__(self) -> None:
- self.module_arg_spec = SPECDOC_META.ansible_spec
- self.required_one_of: List[List[str]] = [
- ["domain_id", "domain"],
- ["id", "name"],
- ]
- self.results: Dict[Any, Any] = {"records": []}
-
- super().__init__(
- module_arg_spec=self.module_arg_spec,
- required_one_of=self.required_one_of,
- )
-
- def _get_domain_by_name(self, name: str) -> Optional[Domain]:
- try:
- domain = self.client.domains(Domain.domain == name)[0]
- return domain
- except IndexError:
- return None
- except Exception as exception:
- return self.fail(
- msg="failed to get domain {0}: {1}".format(name, exception)
- )
-
- def _get_domain_from_params(self) -> Optional[Domain]:
- domain_id = self.module.params.get("domain_id")
- domain = self.module.params.get("domain")
-
- if domain is not None:
- return self._get_domain_by_name(domain)
-
- if domain_id is not None:
- result = Domain(self.client, domain_id)
- result._api_get()
- return result
-
- return None
-
- def _get_records_by_name(
- self, domain: Domain, name: str
- ) -> Optional[List[DomainRecord]]:
- try:
- result = []
-
- for record in domain.records:
- if record.name == name:
- result.append(record)
-
- return result
- except IndexError:
- return []
- except Exception as exception:
- return self.fail(
- msg="failed to get domain record {0}: {1}".format(
- name, exception
- )
- )
-
- def _get_records_from_params(self, domain: Domain) -> List[DomainRecord]:
- record_id = self.module.params.get("id")
- record_name = self.module.params.get("name")
-
- if record_name is not None:
- return self._get_records_by_name(domain, record_name)
-
- if record_id is not None:
- result = DomainRecord(self.client, record_id, domain.id)
- result._api_get()
- return [result]
-
- return []
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for domain record info module"""
-
- domain = self._get_domain_from_params()
- if domain is None:
- return self.fail("failed to get domain")
-
- records = self._get_records_from_params(domain)
- if records is None:
- return self.fail("failed to get records")
-
- self.results["record"] = paginated_list_to_json(records)
-
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the Linode Domain Record info module"""
- LinodeDomainRecordInfo()
-
-
if __name__ == "__main__":
- main()
+ module.run()
diff --git a/tests/integration/targets/domain_record/tasks/main.yaml b/tests/integration/targets/domain_record/tasks/main.yaml
index 076af077..765515bd 100644
--- a/tests/integration/targets/domain_record/tasks/main.yaml
+++ b/tests/integration/targets/domain_record/tasks/main.yaml
@@ -87,6 +87,20 @@
- record_info_id.record[0].ttl_sec == 14400
- record_info_id.record[0].weight == 62
+ - name: Get domain_record_info with both domain_id and domain
+ linode.cloud.domain_record_info:
+ domain: '{{ domain_create.domain.domain }}'
+ domain_id: '{{ domain_create.domain.id }}'
+ id: '{{ record_create.record.id }}'
+ register: info_mutually_exclusive
+ failed_when: '"mutually exclusive" not in info_mutually_exclusive.msg'
+
+ - name: Get domain_record_info with neither domain_id nor domain
+ linode.cloud.domain_record_info:
+ id: '{{ record_create.record.id }}'
+ register: info_one_of
+ failed_when: '"one of the following" not in info_one_of.msg'
+
- name: Create domain_record with domain id
linode.cloud.domain_record:
domain_id: '{{ domain_create.domain.id }}'
From 77d59ba495467e1195cda1d67725f59562c85e9d Mon Sep 17 00:00:00 2001
From: Lena Garber <114949949+lgarber-akamai@users.noreply.github.com>
Date: Tue, 20 Aug 2024 10:30:43 -0400
Subject: [PATCH 13/17] ref: Migrate domain_info and domain_list to the shared
base modules (#559)
* Migrate domain_info and domain_list to the shared base modules
* Re-add zone file to info return values
* fix case
---
docs/modules/domain_info.md | 12 +--
docs/modules/domain_list.md | 12 +--
plugins/modules/domain_info.py | 180 +++++++++++----------------------
plugins/modules/domain_list.py | 127 +++--------------------
4 files changed, 83 insertions(+), 248 deletions(-)
diff --git a/docs/modules/domain_info.md b/docs/modules/domain_info.md
index 31f53287..fc43724a 100644
--- a/docs/modules/domain_info.md
+++ b/docs/modules/domain_info.md
@@ -31,12 +31,12 @@ Get info about a Linode Domain.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `id` | `int` | Optional | The unique domain name of the Domain. Optional if `domain` is defined. **(Conflicts With: `domain`)** |
-| `domain` | `str` | Optional | The unique id of the Domain. Optional if `id` is defined. **(Conflicts With: `id`)** |
+| `id` | `int` | Optional | The ID of the Domain to resolve. **(Conflicts With: `domain`)** |
+| `domain` | `str` | Optional | The domain of the Domain to resolve. **(Conflicts With: `id`)** |
## Return Values
-- `domain` - The domain in JSON serialized form.
+- `domain` - The returned Domain.
- Sample Response:
```json
@@ -63,7 +63,7 @@ Get info about a Linode Domain.
- See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-domain) for a list of returned fields
-- `records` - The domain record in JSON serialized form.
+- `records` - The returned records.
- Sample Response:
```json
@@ -85,10 +85,10 @@ Get info about a Linode Domain.
}
]
```
- - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-domain-record) for a list of returned fields
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-domain-records) for a list of returned fields
-- `zone_file` - The zone file for the last rendered zone for the specified domain.
+- `zone_file` - The returned zone file.
- Sample Response:
```json
diff --git a/docs/modules/domain_list.md b/docs/modules/domain_list.md
index 02f84006..754183c2 100644
--- a/docs/modules/domain_list.md
+++ b/docs/modules/domain_list.md
@@ -32,21 +32,21 @@ List and filter on Domains.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `order` | `str` | Optional | The order to list domains in. **(Choices: `desc`, `asc`; Default: `asc`)** |
-| `order_by` | `str` | Optional | The attribute to order domains by. |
-| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting domains. |
-| `count` | `int` | Optional | The number of results to return. If undefined, all results will be returned. |
+| `order` | `str` | Optional | The order to list Domains in. **(Choices: `desc`, `asc`; Default: `asc`)** |
+| `order_by` | `str` | Optional | The attribute to order Domains by. |
+| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting Domains. |
+| `count` | `int` | Optional | The number of Domains to return. If undefined, all results will be returned. |
### filters
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable attributes can be found here: https://techdocs.akamai.com/linode-api/reference/get-domains |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-domains). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
-- `domains` - The returned domains.
+- `domains` - The returned Domains.
- Sample Response:
```json
diff --git a/plugins/modules/domain_info.py b/plugins/modules/domain_info.py
index 919f5e23..4e3c07ae 100644
--- a/plugins/modules/domain_info.py
+++ b/plugins/modules/domain_info.py
@@ -1,86 +1,79 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-"""This module contains all of the functionality for Linode Domains."""
+"""This file contains the implementation of the domain_info module."""
from __future__ import absolute_import, division, print_function
-from typing import Any, List, Optional
-
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.domain as docs_parent
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.domain_info 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_common_info import (
+ InfoModule,
+ InfoModuleAttr,
+ InfoModuleResult,
)
from ansible_collections.linode.cloud.plugins.module_utils.linode_helper import (
- create_filter_and,
paginated_list_to_json,
+ safe_find,
)
-from ansible_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
-)
+from ansible_specdoc.objects import FieldType
from linode_api4 import Domain
-linode_domain_info_spec = {
- # We need to overwrite attributes to exclude them as requirements
- "state": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "label": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "id": SpecField(
- type=FieldType.integer,
- required=False,
- conflicts_with=["domain"],
- description=[
- "The unique domain name of the Domain.",
- "Optional if `domain` is defined.",
- ],
- ),
- "domain": SpecField(
- type=FieldType.string,
- required=False,
- conflicts_with=["id"],
- description=[
- "The unique id of the Domain.",
- "Optional if `id` is defined.",
- ],
+module = InfoModule(
+ primary_result=InfoModuleResult(
+ field_name="domain",
+ field_type=FieldType.dict,
+ display_name="Domain",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-domain",
+ samples=docs_parent.result_domain_samples,
),
-}
-
-SPECDOC_META = SpecDocMeta(
- description=["Get info about a Linode Domain."],
- requirements=global_requirements,
- author=global_authors,
- options=linode_domain_info_spec,
- examples=docs.specdoc_examples,
- return_values={
- "domain": SpecReturnValue(
- description="The domain in JSON serialized form.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-domain",
- type=FieldType.dict,
- sample=docs_parent.result_domain_samples,
- ),
- "records": SpecReturnValue(
- description="The domain record in JSON serialized form.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-domain-record",
- type=FieldType.list,
- sample=docs_parent.result_records_samples,
+ secondary_results=[
+ InfoModuleResult(
+ field_name="records",
+ field_type=FieldType.list,
+ display_name="records",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-domain-records",
+ samples=docs_parent.result_records_samples,
+ get=lambda client, domain, params: paginated_list_to_json(
+ Domain(client, domain["id"]).records
+ ),
),
- "zone_file": SpecReturnValue(
- description="The zone file for the last rendered zone for the specified domain.",
+ InfoModuleResult(
+ field_name="zone_file",
+ field_type=FieldType.list,
+ display_name="zone file",
docs_url="https://techdocs.akamai.com/linode-api/reference/get-domain-zone",
- type=FieldType.list,
- sample=docs_parent.result_zone_file_samples,
+ samples=docs_parent.result_zone_file_samples,
+ get=lambda client, domain, params: Domain(
+ client, domain["id"]
+ ).zone_file_view(),
),
- },
+ ],
+ attributes=[
+ InfoModuleAttr(
+ display_name="ID",
+ name="id",
+ type=FieldType.integer,
+ get=lambda client, params: client.load(
+ Domain,
+ params.get("id"),
+ )._raw_json,
+ ),
+ InfoModuleAttr(
+ display_name="domain",
+ name="domain",
+ type=FieldType.string,
+ get=lambda client, params: safe_find(
+ client.domains,
+ Domain.domain == params.get("domain"),
+ raise_not_found=True,
+ )._raw_json,
+ ),
+ ],
+ examples=docs.specdoc_examples,
)
-linode_domain_valid_filters = ["id", "domain"]
+SPECDOC_META = module.spec
DOCUMENTATION = r"""
"""
@@ -89,64 +82,5 @@
RETURN = r"""
"""
-
-class LinodeDomainInfo(LinodeModuleBase):
- """Module for getting info about a Linode Domain"""
-
- def __init__(self) -> None:
- self.module_arg_spec = SPECDOC_META.ansible_spec
- self.required_one_of: List[str] = []
- self.results = {"domain": None, "records": None, "zone_file": None}
-
- super().__init__(
- module_arg_spec=self.module_arg_spec,
- required_one_of=self.required_one_of,
- )
-
- def _get_matching_domain(self, spec_args: dict) -> Optional[Domain]:
- filter_items = {
- k: v
- for k, v in spec_args.items()
- if k in linode_domain_valid_filters and v is not None
- }
-
- filter_statement = create_filter_and(Domain, filter_items)
-
- try:
- # Special case because ID is not filterable
- if "id" in filter_items.keys():
- result = Domain(self.client, spec_args.get("id"))
- result._api_get() # Force lazy-loading
-
- return result
-
- return self.client.domains(filter_statement)[0]
- except IndexError:
- return None
- except Exception as exception:
- return self.fail(msg="failed to get domain {0}".format(exception))
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for domain info module"""
-
- domain = self._get_matching_domain(kwargs)
-
- if domain is None:
- self.fail("failed to get domain")
-
- self.results["domain"] = domain._raw_json
- self.results["records"] = paginated_list_to_json(domain.records)
- self.results["zone_file"] = self.client.get(
- "/domains/{}/zone-file".format(domain.id)
- )
-
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the Linode Domain info module"""
- LinodeDomainInfo()
-
-
if __name__ == "__main__":
- main()
+ module.run()
diff --git a/plugins/modules/domain_list.py b/plugins/modules/domain_list.py
index 2a18b73a..6f0ef311 100644
--- a/plugins/modules/domain_list.py
+++ b/plugins/modules/domain_list.py
@@ -1,96 +1,26 @@
+#!/usr/bin/python
# -*- coding: utf-8 -*-
-"""This module allows users to list Domains."""
-from __future__ import absolute_import, division, print_function
+"""This file contains the implementation of the domain_list module."""
-from typing import Any, Dict, Optional
+from __future__ import absolute_import, division, print_function
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.domain_list 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 (
- construct_api_filter,
- get_all_paginated,
-)
-from ansible_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
+from ansible_collections.linode.cloud.plugins.module_utils.linode_common_list import (
+ ListModule,
)
-spec_filter = {
- "name": SpecField(
- type=FieldType.string,
- required=True,
- description=[
- "The name of the field to filter on.",
- "Valid filterable attributes can be found here: "
- "https://techdocs.akamai.com/linode-api/reference/get-domains",
- ],
- ),
- "values": SpecField(
- type=FieldType.list,
- element_type=FieldType.string,
- required=True,
- description=[
- "A list of values to allow for this field.",
- "Fields will pass this filter if at least one of these values matches.",
- ],
- ),
-}
-
-spec = {
- # Disable the default values
- "state": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "label": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "order": SpecField(
- type=FieldType.string,
- description=["The order to list domains in."],
- default="asc",
- choices=["desc", "asc"],
- ),
- "order_by": SpecField(
- type=FieldType.string,
- description=["The attribute to order domains by."],
- ),
- "filters": SpecField(
- type=FieldType.list,
- element_type=FieldType.dict,
- suboptions=spec_filter,
- description=["A list of filters to apply to the resulting domains."],
- ),
- "count": SpecField(
- type=FieldType.integer,
- description=[
- "The number of results to return.",
- "If undefined, all results will be returned.",
- ],
- ),
-}
-
-SPECDOC_META = SpecDocMeta(
- description=["List and filter on Domains."],
- requirements=global_requirements,
- author=global_authors,
- options=spec,
+module = ListModule(
+ result_display_name="Domains",
+ result_field_name="domains",
+ endpoint_template="/domains",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-domains",
examples=docs.specdoc_examples,
- return_values={
- "domains": SpecReturnValue(
- description="The returned domains.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-domains",
- type=FieldType.list,
- elements=FieldType.dict,
- sample=docs.result_domains_samples,
- )
- },
+ result_samples=docs.result_domains_samples,
)
+SPECDOC_META = module.spec
+
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
@@ -98,34 +28,5 @@
RETURN = r"""
"""
-
-class Module(LinodeModuleBase):
- """Module for getting a list of domains"""
-
- def __init__(self) -> None:
- self.module_arg_spec = SPECDOC_META.ansible_spec
- self.results: Dict[str, Any] = {"domains": []}
-
- super().__init__(module_arg_spec=self.module_arg_spec)
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for domain list module"""
-
- filter_dict = construct_api_filter(self.module.params)
-
- self.results["domains"] = get_all_paginated(
- self.client,
- "/domains",
- filter_dict,
- num_results=self.module.params["count"],
- )
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the module"""
- Module()
-
-
if __name__ == "__main__":
- main()
+ module.run()
From 4eca745c22319a9700a9ab34a9bc2b8277c3512d Mon Sep 17 00:00:00 2001
From: Lena Garber <114949949+lgarber-akamai@users.noreply.github.com>
Date: Tue, 20 Aug 2024 10:31:01 -0400
Subject: [PATCH 14/17] ref: Migrate event_list module to ListModule base
(#561)
* Migrate event_list module to ListModule base
* Respect number of pages in get_all_paginated(...)
---
README.md | 2 +-
docs/modules/event_list.md | 14 +--
plugins/module_utils/linode_helper.py | 8 +-
plugins/modules/event_list.py | 124 +++-----------------------
4 files changed, 26 insertions(+), 122 deletions(-)
diff --git a/README.md b/README.md
index 6d0c4585..04a5e0ae 100644
--- a/README.md
+++ b/README.md
@@ -95,7 +95,7 @@ Name | Description |
[linode.cloud.database_engine_list](./docs/modules/database_engine_list.md)|List and filter on Managed Database engine types.|
[linode.cloud.database_list](./docs/modules/database_list.md)|List and filter on Linode Managed Databases.|
[linode.cloud.domain_list](./docs/modules/domain_list.md)|List and filter on Domains.|
-[linode.cloud.event_list](./docs/modules/event_list.md)|List and filter on Linode events.|
+[linode.cloud.event_list](./docs/modules/event_list.md)|List and filter on Events.|
[linode.cloud.firewall_list](./docs/modules/firewall_list.md)|List and filter on Firewalls.|
[linode.cloud.image_list](./docs/modules/image_list.md)|List and filter on Images.|
[linode.cloud.instance_list](./docs/modules/instance_list.md)|List and filter on Instances.|
diff --git a/docs/modules/event_list.md b/docs/modules/event_list.md
index 625e13c5..0b3e6ff3 100644
--- a/docs/modules/event_list.md
+++ b/docs/modules/event_list.md
@@ -1,6 +1,6 @@
# event_list
-List and filter on Linode events.
+List and filter on Events.
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
@@ -40,21 +40,21 @@ List and filter on Linode events.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `order` | `str` | Optional | The order to list events in. **(Choices: `desc`, `asc`; Default: `asc`)** |
-| `order_by` | `str` | Optional | The attribute to order events by. |
-| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting events. |
-| `count` | `int` | Optional | The number of results to return. If undefined, all results will be returned. |
+| `order` | `str` | Optional | The order to list Events in. **(Choices: `desc`, `asc`; Default: `asc`)** |
+| `order_by` | `str` | Optional | The attribute to order Events by. |
+| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting Events. |
+| `count` | `int` | Optional | The number of Events to return. If undefined, all results will be returned. |
### filters
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable attributes can be found here: https://techdocs.akamai.com/linode-api/reference/get-events |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-events). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
-- `events` - The returned events.
+- `events` - The returned Events.
- Sample Response:
```json
diff --git a/plugins/module_utils/linode_helper.py b/plugins/module_utils/linode_helper.py
index 69dd8941..43124162 100644
--- a/plugins/module_utils/linode_helper.py
+++ b/plugins/module_utils/linode_helper.py
@@ -283,13 +283,13 @@ def get_all_paginated(
result = []
current_page = 1
page_size = 100
- num_pages = 1
+ num_pages: Optional[int] = None
if num_results is not None and num_results < page_size:
# Clamp the page size
page_size = max(min(num_results, 100), 25)
- while current_page <= num_pages and (
+ while (num_pages is None or current_page <= num_pages) and (
num_results is None or len(result) < num_results
):
response = client.get(
@@ -300,6 +300,10 @@ def get_all_paginated(
if "data" not in response or "page" not in response:
raise Exception("Invalid list response")
+ # We only want to set num_pages once to avoid undefined behavior
+ # when the number of pages changes mid-aggregation
+ num_pages = num_pages or response["pages"]
+
result.extend(response["data"])
if num_results is not None and len(result) >= num_results:
diff --git a/plugins/modules/event_list.py b/plugins/modules/event_list.py
index fcd1e61f..f79c1552 100644
--- a/plugins/modules/event_list.py
+++ b/plugins/modules/event_list.py
@@ -1,97 +1,26 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-"""This module allows users to list Linode events."""
+"""This file contains the implementation of the event_list module."""
from __future__ import absolute_import, division, print_function
-from typing import Any, Dict, Optional
-
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.event_list 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 (
- construct_api_filter,
- get_all_paginated,
+from ansible_collections.linode.cloud.plugins.module_utils.linode_common_list import (
+ ListModule,
)
-from ansible_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
-)
-
-spec_filter = {
- "name": SpecField(
- type=FieldType.string,
- required=True,
- description=[
- "The name of the field to filter on.",
- "Valid filterable attributes can be found here: "
- "https://techdocs.akamai.com/linode-api/reference/get-events",
- ],
- ),
- "values": SpecField(
- type=FieldType.list,
- element_type=FieldType.string,
- required=True,
- description=[
- "A list of values to allow for this field.",
- "Fields will pass this filter if at least one of these values matches.",
- ],
- ),
-}
-spec = {
- # Disable the default values
- "state": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "label": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "order": SpecField(
- type=FieldType.string,
- description=["The order to list events in."],
- default="asc",
- choices=["desc", "asc"],
- ),
- "order_by": SpecField(
- type=FieldType.string, description=["The attribute to order events by."]
- ),
- "filters": SpecField(
- type=FieldType.list,
- element_type=FieldType.dict,
- suboptions=spec_filter,
- description=["A list of filters to apply to the resulting events."],
- ),
- "count": SpecField(
- type=FieldType.integer,
- description=[
- "The number of results to return.",
- "If undefined, all results will be returned.",
- ],
- ),
-}
-
-SPECDOC_META = SpecDocMeta(
- description=["List and filter on Linode events."],
- requirements=global_requirements,
- author=global_authors,
- options=spec,
+module = ListModule(
+ result_display_name="Events",
+ result_field_name="events",
+ endpoint_template="/account/events",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-events",
examples=docs.specdoc_examples,
- return_values={
- "events": SpecReturnValue(
- description="The returned events.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-events",
- type=FieldType.list,
- elements=FieldType.dict,
- sample=docs.result_events_samples,
- )
- },
+ result_samples=docs.result_events_samples,
)
+SPECDOC_META = module.spec
+
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
@@ -99,34 +28,5 @@
RETURN = r"""
"""
-
-class Module(LinodeModuleBase):
- """Module for getting info about a Linode events"""
-
- def __init__(self) -> None:
- self.module_arg_spec = SPECDOC_META.ansible_spec
- self.results: Dict[str, Any] = {"events": []}
-
- super().__init__(module_arg_spec=self.module_arg_spec)
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for event list module"""
-
- filter_dict = construct_api_filter(self.module.params)
-
- self.results["events"] = get_all_paginated(
- self.client,
- "/account/events",
- filter_dict,
- num_results=self.module.params["count"],
- )
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the module"""
- Module()
-
-
if __name__ == "__main__":
- main()
+ module.run()
From 419ed81118d2d89505b66f6d48d7c246d6d26451 Mon Sep 17 00:00:00 2001
From: Erik Zilber
Date: Tue, 20 Aug 2024 12:01:46 -0400
Subject: [PATCH 15/17] Migrated region list (#564)
---
README.md | 2 +-
docs/modules/region_list.md | 14 ++--
plugins/modules/region_list.py | 123 +++------------------------------
3 files changed, 19 insertions(+), 120 deletions(-)
diff --git a/README.md b/README.md
index 04a5e0ae..54759259 100644
--- a/README.md
+++ b/README.md
@@ -104,7 +104,7 @@ Name | Description |
[linode.cloud.nodebalancer_list](./docs/modules/nodebalancer_list.md)|List and filter on Nodebalancers.|
[linode.cloud.object_cluster_list](./docs/modules/object_cluster_list.md)|**NOTE: This module has been deprecated because it relies on deprecated API endpoints. Going forward, `region` will be the preferred way to designate where Object Storage resources should be created.**|
[linode.cloud.placement_group_list](./docs/modules/placement_group_list.md)|List and filter on Placement Groups.|
-[linode.cloud.region_list](./docs/modules/region_list.md)|List and filter on Linode Regions.|
+[linode.cloud.region_list](./docs/modules/region_list.md)|List and filter on Regions.|
[linode.cloud.ssh_key_list](./docs/modules/ssh_key_list.md)|List and filter on SSH keys in the Linode profile.|
[linode.cloud.stackscript_list](./docs/modules/stackscript_list.md)|List and filter on Linode stackscripts.|
[linode.cloud.token_list](./docs/modules/token_list.md)|List and filter on Linode Account tokens.|
diff --git a/docs/modules/region_list.md b/docs/modules/region_list.md
index c014b8ed..641f0515 100644
--- a/docs/modules/region_list.md
+++ b/docs/modules/region_list.md
@@ -1,6 +1,6 @@
# region_list
-List and filter on Linode Regions.
+List and filter on Regions.
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
@@ -32,21 +32,21 @@ List and filter on Linode Regions.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `order` | `str` | Optional | The order to list regions in. **(Choices: `desc`, `asc`; Default: `asc`)** |
-| `order_by` | `str` | Optional | The attribute to order regions by. |
-| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting regions. |
-| `count` | `int` | Optional | The number of results to return. If undefined, all results will be returned. |
+| `order` | `str` | Optional | The order to list Regions in. **(Choices: `desc`, `asc`; Default: `asc`)** |
+| `order_by` | `str` | Optional | The attribute to order Regions by. |
+| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting Regions. |
+| `count` | `int` | Optional | The number of Regions to return. If undefined, all results will be returned. |
### filters
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable attributes can be found here: https://techdocs.akamai.com/linode-api/reference/get-regions |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-regions). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
-- `regions` - The returned regions.
+- `regions` - The returned Regions.
- Sample Response:
```json
diff --git a/plugins/modules/region_list.py b/plugins/modules/region_list.py
index 21c8a054..40328cbf 100644
--- a/plugins/modules/region_list.py
+++ b/plugins/modules/region_list.py
@@ -4,94 +4,22 @@
"""This module allows users to list Linode regions."""
from __future__ import absolute_import, division, print_function
-from typing import Any, Dict, Optional
-
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.region_list 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 (
- construct_api_filter,
- get_all_paginated,
+from ansible_collections.linode.cloud.plugins.module_utils.linode_common_list import (
+ ListModule,
)
-from ansible_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
-)
-
-spec_filter = {
- "name": SpecField(
- type=FieldType.string,
- required=True,
- description=[
- "The name of the field to filter on.",
- "Valid filterable attributes can be found here: "
- "https://techdocs.akamai.com/linode-api/reference/get-regions",
- ],
- ),
- "values": SpecField(
- type=FieldType.list,
- element_type=FieldType.string,
- required=True,
- description=[
- "A list of values to allow for this field.",
- "Fields will pass this filter if at least one of these values matches.",
- ],
- ),
-}
-spec = {
- # Disable the default values
- "state": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "label": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "order": SpecField(
- type=FieldType.string,
- description=["The order to list regions in."],
- default="asc",
- choices=["desc", "asc"],
- ),
- "order_by": SpecField(
- type=FieldType.string,
- description=["The attribute to order regions by."],
- ),
- "filters": SpecField(
- type=FieldType.list,
- element_type=FieldType.dict,
- suboptions=spec_filter,
- description=["A list of filters to apply to the resulting regions."],
- ),
- "count": SpecField(
- type=FieldType.integer,
- description=[
- "The number of results to return.",
- "If undefined, all results will be returned.",
- ],
- ),
-}
-
-SPECDOC_META = SpecDocMeta(
- description=["List and filter on Linode Regions."],
- requirements=global_requirements,
- author=global_authors,
- options=spec,
+module = ListModule(
+ result_display_name="Regions",
+ result_field_name="regions",
+ endpoint_template="/regions",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-regions",
examples=docs.specdoc_examples,
- return_values={
- "regions": SpecReturnValue(
- description="The returned regions.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-regions",
- type=FieldType.list,
- elements=FieldType.dict,
- sample=docs.result_regions_samples,
- )
- },
+ result_samples=docs.result_regions_samples,
)
+SPECDOC_META = module.spec
+
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
@@ -99,34 +27,5 @@
RETURN = r"""
"""
-
-class Module(LinodeModuleBase):
- """Module for getting a list of Linode regions"""
-
- def __init__(self) -> None:
- self.module_arg_spec = SPECDOC_META.ansible_spec
- self.results: Dict[str, Any] = {"regions": []}
-
- super().__init__(module_arg_spec=self.module_arg_spec)
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for region list module"""
-
- filter_dict = construct_api_filter(self.module.params)
-
- self.results["regions"] = get_all_paginated(
- self.client,
- "/regions",
- filter_dict,
- num_results=self.module.params["count"],
- )
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the module"""
- Module()
-
-
if __name__ == "__main__":
- main()
+ module.run()
From 0a16ba0c3dffed02aa7e5ae9840b4dcde1d6bd99 Mon Sep 17 00:00:00 2001
From: Lena Garber <114949949+lgarber-akamai@users.noreply.github.com>
Date: Tue, 20 Aug 2024 12:51:30 -0400
Subject: [PATCH 16/17] fix: Respect existing `zone_file` result structure
(#569)
* Fix structure of zone_file result
* Fix zone file example
---
docs/modules/domain.md | 26 +++++++++-----------
docs/modules/domain_info.md | 26 +++++++++-----------
plugins/module_utils/doc_fragments/domain.py | 26 +++++++++-----------
plugins/modules/domain.py | 2 +-
plugins/modules/domain_info.py | 8 +++---
5 files changed, 41 insertions(+), 47 deletions(-)
diff --git a/docs/modules/domain.md b/docs/modules/domain.md
index 35173768..89e7b445 100644
--- a/docs/modules/domain.md
+++ b/docs/modules/domain.md
@@ -106,20 +106,18 @@ Manage Linode Domains.
- Sample Response:
```json
- [
- {
- "zone_file": [
- "; example.com [123]",
- "$TTL 864000",
- "@ IN SOA ns1.linode.com. user.example.com. 2021000066 14400 14400 1209600 86400",
- "@ NS ns1.linode.com.",
- "@ NS ns2.linode.com.",
- "@ NS ns3.linode.com.",
- "@ NS ns4.linode.com.",
- "@ NS ns5.linode.com."
- ]
- }
- ]
+ {
+ "zone_file": [
+ "; example.com [123]",
+ "$TTL 864000",
+ "@ IN SOA ns1.linode.com. user.example.com. 2021000066 14400 14400 1209600 86400",
+ "@ NS ns1.linode.com.",
+ "@ NS ns2.linode.com.",
+ "@ NS ns3.linode.com.",
+ "@ NS ns4.linode.com.",
+ "@ NS ns5.linode.com."
+ ]
+ }
```
- See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-domain-zone) for a list of returned fields
diff --git a/docs/modules/domain_info.md b/docs/modules/domain_info.md
index fc43724a..b511d381 100644
--- a/docs/modules/domain_info.md
+++ b/docs/modules/domain_info.md
@@ -92,20 +92,18 @@ Get info about a Linode Domain.
- Sample Response:
```json
- [
- {
- "zone_file": [
- "; example.com [123]",
- "$TTL 864000",
- "@ IN SOA ns1.linode.com. user.example.com. 2021000066 14400 14400 1209600 86400",
- "@ NS ns1.linode.com.",
- "@ NS ns2.linode.com.",
- "@ NS ns3.linode.com.",
- "@ NS ns4.linode.com.",
- "@ NS ns5.linode.com."
- ]
- }
- ]
+ {
+ "zone_file": [
+ "; example.com [123]",
+ "$TTL 864000",
+ "@ IN SOA ns1.linode.com. user.example.com. 2021000066 14400 14400 1209600 86400",
+ "@ NS ns1.linode.com.",
+ "@ NS ns2.linode.com.",
+ "@ NS ns3.linode.com.",
+ "@ NS ns4.linode.com.",
+ "@ NS ns5.linode.com."
+ ]
+ }
```
- See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-domain-zone) for a list of returned fields
diff --git a/plugins/module_utils/doc_fragments/domain.py b/plugins/module_utils/doc_fragments/domain.py
index b0f6ce55..18d1298c 100644
--- a/plugins/module_utils/doc_fragments/domain.py
+++ b/plugins/module_utils/doc_fragments/domain.py
@@ -49,17 +49,15 @@
}
]''']
-result_zone_file_samples = ['''[
- {
- "zone_file": [
- "; example.com [123]",
- "$TTL 864000",
- "@ IN SOA ns1.linode.com. user.example.com. 2021000066 14400 14400 1209600 86400",
- "@ NS ns1.linode.com.",
- "@ NS ns2.linode.com.",
- "@ NS ns3.linode.com.",
- "@ NS ns4.linode.com.",
- "@ NS ns5.linode.com."
- ]
- }
-]''']
+result_zone_file_samples = ['''{
+ "zone_file": [
+ "; example.com [123]",
+ "$TTL 864000",
+ "@ IN SOA ns1.linode.com. user.example.com. 2021000066 14400 14400 1209600 86400",
+ "@ NS ns1.linode.com.",
+ "@ NS ns2.linode.com.",
+ "@ NS ns3.linode.com.",
+ "@ NS ns4.linode.com.",
+ "@ NS ns5.linode.com."
+ ]
+}''']
diff --git a/plugins/modules/domain.py b/plugins/modules/domain.py
index 90a89239..ca46b6ac 100644
--- a/plugins/modules/domain.py
+++ b/plugins/modules/domain.py
@@ -154,7 +154,7 @@
"zone_file": SpecReturnValue(
description="The zone file for the last rendered zone for the specified domain.",
docs_url="https://techdocs.akamai.com/linode-api/reference/get-domain-zone",
- type=FieldType.list,
+ type=FieldType.dict,
sample=docs.result_zone_file_samples,
),
},
diff --git a/plugins/modules/domain_info.py b/plugins/modules/domain_info.py
index 4e3c07ae..010622e4 100644
--- a/plugins/modules/domain_info.py
+++ b/plugins/modules/domain_info.py
@@ -40,13 +40,13 @@
),
InfoModuleResult(
field_name="zone_file",
- field_type=FieldType.list,
+ field_type=FieldType.dict,
display_name="zone file",
docs_url="https://techdocs.akamai.com/linode-api/reference/get-domain-zone",
samples=docs_parent.result_zone_file_samples,
- get=lambda client, domain, params: Domain(
- client, domain["id"]
- ).zone_file_view(),
+ get=lambda client, domain, params: {
+ "zone_file": Domain(client, domain["id"]).zone_file_view()
+ },
),
],
attributes=[
From 6af01049a14eeb003007354d61dfb8a431024144 Mon Sep 17 00:00:00 2001
From: Erik Zilber
Date: Tue, 20 Aug 2024 13:35:08 -0400
Subject: [PATCH 17/17] Migrated nb info and list modules (#566)
---
README.md | 6 +-
docs/modules/nodebalancer_info.md | 52 +++-
docs/modules/nodebalancer_list.md | 14 +-
docs/modules/nodebalancer_stats.md | 8 +-
.../doc_fragments/nodebalancer.py | 27 ++
plugins/modules/nodebalancer_info.py | 262 +++++++-----------
plugins/modules/nodebalancer_list.py | 128 +--------
plugins/modules/nodebalancer_stats.py | 146 +++-------
.../nodebalancer_populated/tasks/main.yaml | 22 +-
.../nodebalancer_stats/tasks/main.yaml | 2 -
10 files changed, 254 insertions(+), 413 deletions(-)
diff --git a/README.md b/README.md
index 54759259..1c04e31d 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ Name | Description |
[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.|
[linode.cloud.nodebalancer_node](./docs/modules/nodebalancer_node.md)|Manage Linode NodeBalancer Nodes.|
-[linode.cloud.nodebalancer_stats](./docs/modules/nodebalancer_stats.md)|View a Linode NodeBalancers Stats.|
+[linode.cloud.nodebalancer_stats](./docs/modules/nodebalancer_stats.md)|Get info about a Linode Node Balancer Stats.|
[linode.cloud.object_keys](./docs/modules/object_keys.md)|Manage Linode Object Storage Keys.|
[linode.cloud.placement_group](./docs/modules/placement_group.md)|Manage a Linode Placement Group.|
[linode.cloud.placement_group_assign](./docs/modules/placement_group_assign.md)|Manages a single assignment between a Linode and a Placement Group.|
@@ -69,7 +69,7 @@ Name | Description |
[linode.cloud.ip_info](./docs/modules/ip_info.md)|Get info about a Linode IP.|
[linode.cloud.ipv6_range_info](./docs/modules/ipv6_range_info.md)|Get info about a Linode IPv6 range.|
[linode.cloud.lke_cluster_info](./docs/modules/lke_cluster_info.md)|Get info about a Linode LKE cluster.|
-[linode.cloud.nodebalancer_info](./docs/modules/nodebalancer_info.md)|Get info about a Linode NodeBalancer.|
+[linode.cloud.nodebalancer_info](./docs/modules/nodebalancer_info.md)|Get info about a Linode Node Balancer.|
[linode.cloud.object_cluster_info](./docs/modules/object_cluster_info.md)|**NOTE: This module has been deprecated because it relies on deprecated API endpoints. Going forward, `region` will be the preferred way to designate where Object Storage resources should be created.**|
[linode.cloud.placement_group_info](./docs/modules/placement_group_info.md)|Get info about a Linode Placement Group.|
[linode.cloud.profile_info](./docs/modules/profile_info.md)|Get info about a Linode Profile.|
@@ -101,7 +101,7 @@ Name | Description |
[linode.cloud.instance_list](./docs/modules/instance_list.md)|List and filter on Instances.|
[linode.cloud.instance_type_list](./docs/modules/instance_type_list.md)|**NOTE: This module has been deprecated in favor of `type_list`.|
[linode.cloud.lke_version_list](./docs/modules/lke_version_list.md)|List Kubernetes versions available for deployment to a Kubernetes cluster.|
-[linode.cloud.nodebalancer_list](./docs/modules/nodebalancer_list.md)|List and filter on Nodebalancers.|
+[linode.cloud.nodebalancer_list](./docs/modules/nodebalancer_list.md)|List and filter on Node Balancers.|
[linode.cloud.object_cluster_list](./docs/modules/object_cluster_list.md)|**NOTE: This module has been deprecated because it relies on deprecated API endpoints. Going forward, `region` will be the preferred way to designate where Object Storage resources should be created.**|
[linode.cloud.placement_group_list](./docs/modules/placement_group_list.md)|List and filter on Placement Groups.|
[linode.cloud.region_list](./docs/modules/region_list.md)|List and filter on Regions.|
diff --git a/docs/modules/nodebalancer_info.md b/docs/modules/nodebalancer_info.md
index 1060595b..ba0e4e2a 100644
--- a/docs/modules/nodebalancer_info.md
+++ b/docs/modules/nodebalancer_info.md
@@ -1,6 +1,6 @@
# nodebalancer_info
-Get info about a Linode NodeBalancer.
+Get info about a Linode Node Balancer.
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
@@ -31,12 +31,12 @@ Get info about a Linode NodeBalancer.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `id` | `int` | Optional | The ID of this NodeBalancer. Optional if `label` is defined. **(Conflicts With: `label`)** |
-| `label` | `str` | Optional | The label of this NodeBalancer. Optional if `id` is defined. **(Conflicts With: `id`)** |
+| `label` | `str` | Optional | The label of the Node Balancer to resolve. **(Conflicts With: `id`)** |
+| `id` | `int` | Optional | The ID of the Node Balancer to resolve. **(Conflicts With: `label`)** |
## Return Values
-- `node_balancer` - The NodeBalancer in JSON serialized form.
+- `node_balancer` - The returned Node Balancer.
- Sample Response:
```json
@@ -64,7 +64,7 @@ Get info about a Linode NodeBalancer.
- See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-node-balancer) for a list of returned fields
-- `configs` - A list of configs applied to the NodeBalancer.
+- `configs` - The returned configs.
- Sample Response:
```json
@@ -96,10 +96,10 @@ Get info about a Linode NodeBalancer.
}
]
```
- - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-node-balancer-config) for a list of returned fields
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-node-balancer-configs) for a list of returned fields
-- `nodes` - A list of configs applied to the NodeBalancer.
+- `nodes` - The returned nodes.
- Sample Response:
```json
@@ -116,10 +116,10 @@ Get info about a Linode NodeBalancer.
}
]
```
- - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-node-balancer-node) for a list of returned fields
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-node-balancer-config-nodes) for a list of returned fields
-- `firewalls` - A list IDs for firewalls attached to this NodeBalancer.
+- `firewalls` - The returned firewalls.
- Sample Response:
```json
@@ -131,3 +131,37 @@ Get info about a Linode NodeBalancer.
- See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-node-balancer-firewalls) for a list of returned fields
+- `firewalls_data` - The returned firewalls_data.
+
+ - Sample Response:
+ ```json
+ [
+ {
+ "created": "2020-04-10T13:34:00",
+ "entities": [
+ {
+ "id": 1234,
+ "label": "example-label",
+ "type": "nodebalancer",
+ "url": "/v4/nodebalancers/1234"
+ }
+ ],
+ "id": 45678,
+ "label": "very-cool-label",
+ "rules": {
+ "fingerprint": "abcdefg",
+ "inbound": [],
+ "inbound_policy": "DROP",
+ "outbound": [],
+ "outbound_policy": "DROP",
+ "version": 1
+ },
+ "status": "enabled",
+ "tags": [],
+ "updated": "2020-04-10T13:34:01"
+ }
+ ]
+ ```
+ - See the [Linode API response documentation](https://techdocs.akamai.com/linode-api/reference/get-node-balancer-firewalls) for a list of returned fields
+
+
diff --git a/docs/modules/nodebalancer_list.md b/docs/modules/nodebalancer_list.md
index 60853355..67cb2a28 100644
--- a/docs/modules/nodebalancer_list.md
+++ b/docs/modules/nodebalancer_list.md
@@ -1,6 +1,6 @@
# nodebalancer_list
-List and filter on Nodebalancers.
+List and filter on Node Balancers.
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
@@ -32,21 +32,21 @@ List and filter on Nodebalancers.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `order` | `str` | Optional | The order to list nodebalancers in. **(Choices: `desc`, `asc`; Default: `asc`)** |
-| `order_by` | `str` | Optional | The attribute to order nodebalancers by. |
-| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting nodebalancers. |
-| `count` | `int` | Optional | The number of results to return. If undefined, all results will be returned. |
+| `order` | `str` | Optional | The order to list Node Balancers in. **(Choices: `desc`, `asc`; Default: `asc`)** |
+| `order_by` | `str` | Optional | The attribute to order Node Balancers by. |
+| [`filters` (sub-options)](#filters) | `list` | Optional | A list of filters to apply to the resulting Node Balancers. |
+| `count` | `int` | Optional | The number of Node Balancers to return. If undefined, all results will be returned. |
### filters
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable attributes can be found here: https://techdocs.akamai.com/linode-api/reference/get-node-balancers |
+| `name` | `str` | **Required** | The name of the field to filter on. Valid filterable fields can be found [here](https://techdocs.akamai.com/linode-api/reference/get-node-balancers). |
| `values` | `list` | **Required** | A list of values to allow for this field. Fields will pass this filter if at least one of these values matches. |
## Return Values
-- `nodebalancers` - The returned nodebalancers.
+- `nodebalancers` - The returned Node Balancers.
- Sample Response:
```json
diff --git a/docs/modules/nodebalancer_stats.md b/docs/modules/nodebalancer_stats.md
index 7a1ae565..fcd344da 100644
--- a/docs/modules/nodebalancer_stats.md
+++ b/docs/modules/nodebalancer_stats.md
@@ -1,6 +1,6 @@
# nodebalancer_stats
-View a Linode NodeBalancers Stats.
+Get info about a Linode Node Balancer Stats.
- [Minimum Required Fields](#minimum-required-fields)
- [Examples](#examples)
@@ -28,12 +28,12 @@ View a Linode NodeBalancers Stats.
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
-| `id` | `int` | Optional | The id of the nodebalancer for which the statistics apply to. **(Conflicts With: `label`)** |
-| `label` | `str` | Optional | The label of the nodebalancer for which the statistics apply to. **(Conflicts With: `id`)** |
+| `label` | `str` | Optional | The label of the Node Balancer Stats to resolve. **(Conflicts With: `id`)** |
+| `id` | `int` | Optional | The ID of the Node Balancer Stats to resolve. **(Conflicts With: `label`)** |
## Return Values
-- `node_balancer_stats` - The NodeBalancer Stats in JSON serialized form.
+- `node_balancer_stats` - The returned Node Balancer Stats.
- Sample Response:
```json
diff --git a/plugins/module_utils/doc_fragments/nodebalancer.py b/plugins/module_utils/doc_fragments/nodebalancer.py
index e0bf2dcc..aa6350e5 100644
--- a/plugins/module_utils/doc_fragments/nodebalancer.py
+++ b/plugins/module_utils/doc_fragments/nodebalancer.py
@@ -86,3 +86,30 @@
1234,
5678
]''']
+
+result_firewalls_data_samples = ['''[
+ {
+ "created": "2020-04-10T13:34:00",
+ "entities": [
+ {
+ "id": 1234,
+ "label": "example-label",
+ "type": "nodebalancer",
+ "url": "/v4/nodebalancers/1234"
+ }
+ ],
+ "id": 45678,
+ "label": "very-cool-label",
+ "rules": {
+ "fingerprint": "abcdefg",
+ "inbound": [],
+ "inbound_policy": "DROP",
+ "outbound": [],
+ "outbound_policy": "DROP",
+ "version": 1
+ },
+ "status": "enabled",
+ "tags": [],
+ "updated": "2020-04-10T13:34:01"
+ }
+]''']
diff --git a/plugins/modules/nodebalancer_info.py b/plugins/modules/nodebalancer_info.py
index b5cc230d..4f3aed8d 100644
--- a/plugins/modules/nodebalancer_info.py
+++ b/plugins/modules/nodebalancer_info.py
@@ -1,11 +1,11 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-"""This module contains all of the functionality for Linode NodeBalancers."""
+"""This module allows users to retrieve information about a Linode NodeBalancer."""
from __future__ import absolute_import, division, print_function
-from typing import Any, List, Optional
+from typing import Any, Dict, List
from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import (
nodebalancer as docs_parent,
@@ -13,83 +13,118 @@
from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import (
nodebalancer_info 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_common_info import (
+ InfoModule,
+ InfoModuleAttr,
+ InfoModuleResult,
)
from ansible_collections.linode.cloud.plugins.module_utils.linode_helper import (
- create_filter_and,
-)
-from ansible_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
+ paginated_list_to_json,
+ safe_find,
)
-from linode_api4 import NodeBalancer, NodeBalancerConfig, NodeBalancerNode
-
-linode_nodebalancer_info_spec = {
- # We need to overwrite attributes to exclude them as requirements
- "state": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "id": SpecField(
- type=FieldType.integer,
- required=False,
- conflicts_with=["label"],
- description=[
- "The ID of this NodeBalancer.",
- "Optional if `label` is defined.",
- ],
+from ansible_specdoc.objects import FieldType
+from linode_api4 import LinodeClient, NodeBalancer
+
+
+def _get_firewalls_data(
+ client: LinodeClient, nodebalancer: NodeBalancer, params: Dict[str, Any]
+) -> List[Any]:
+ firewalls = NodeBalancer(client, nodebalancer["id"]).firewalls()
+ firewalls_json = []
+ for firewall in firewalls:
+ firewall._api_get()
+ firewalls_json.append(firewall._raw_json)
+ return firewalls_json
+
+
+def _get_nodes(
+ client: LinodeClient, nodebalancer: NodeBalancer, params: Dict[str, Any]
+) -> List[Any]:
+ configs = NodeBalancer(client, nodebalancer["id"]).configs
+ nodes_json = []
+ for config in configs:
+ for node in config.nodes:
+ node._api_get()
+ nodes_json.append(node._raw_json)
+ return nodes_json
+
+
+module = InfoModule(
+ primary_result=InfoModuleResult(
+ field_name="node_balancer",
+ field_type=FieldType.dict,
+ display_name="Node Balancer",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-node-balancer",
+ samples=docs_parent.result_node_balancer_samples,
),
- "label": SpecField(
- type=FieldType.string,
- required=False,
- conflicts_with=["id"],
- description=[
- "The label of this NodeBalancer.",
- "Optional if `id` is defined.",
- ],
- ),
-}
-
-SPECDOC_META = SpecDocMeta(
- description=["Get info about a Linode NodeBalancer."],
- requirements=global_requirements,
- author=global_authors,
- options=linode_nodebalancer_info_spec,
- examples=docs.specdoc_examples,
- return_values={
- "node_balancer": SpecReturnValue(
- description="The NodeBalancer in JSON serialized form.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-node-balancer",
- type="dict",
- sample=docs_parent.result_node_balancer_samples,
+ secondary_results=[
+ InfoModuleResult(
+ field_name="configs",
+ field_type=FieldType.list,
+ display_name="configs",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-node-balancer-configs",
+ samples=docs_parent.result_configs_samples,
+ get=lambda client, nodebalancer, params: paginated_list_to_json(
+ NodeBalancer(client, nodebalancer["id"]).configs
+ ),
),
- "configs": SpecReturnValue(
- description="A list of configs applied to the NodeBalancer.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-node-balancer-config",
- type=FieldType.list,
- sample=docs_parent.result_configs_samples,
+ InfoModuleResult(
+ field_name="nodes",
+ field_type=FieldType.list,
+ display_name="nodes",
+ docs_url="https://techdocs.akamai.com/linode-api/"
+ + "reference/get-node-balancer-config-nodes",
+ samples=docs_parent.result_nodes_samples,
+ get=_get_nodes,
),
- "nodes": SpecReturnValue(
- description="A list of configs applied to the NodeBalancer.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-node-balancer-node",
- type=FieldType.list,
- sample=docs_parent.result_nodes_samples,
+ InfoModuleResult(
+ field_name="firewalls",
+ field_type=FieldType.list,
+ display_name="firewalls",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-node-balancer-firewalls",
+ samples=docs_parent.result_firewalls_samples,
+ get=lambda client, nodebalancer, params: [
+ firewall.id
+ for firewall in NodeBalancer(
+ client, nodebalancer["id"]
+ ).firewalls()
+ ],
),
- "firewalls": SpecReturnValue(
- description="A list IDs for firewalls attached to this NodeBalancer.",
+ InfoModuleResult(
+ field_name="firewalls_data",
+ field_type=FieldType.list,
+ display_name="firewalls_data",
docs_url="https://techdocs.akamai.com/linode-api/reference/get-node-balancer-firewalls",
- type=FieldType.list,
- elements=FieldType.integer,
- sample=docs_parent.result_firewalls_samples,
+ samples=docs_parent.result_firewalls_data_samples,
+ get=_get_firewalls_data,
+ ),
+ ],
+ attributes=[
+ InfoModuleAttr(
+ display_name="ID",
+ name="id",
+ type=FieldType.integer,
+ get=lambda client, params: client.load(
+ NodeBalancer,
+ params.get("id"),
+ )._raw_json,
+ ),
+ InfoModuleAttr(
+ display_name="label",
+ name="label",
+ type=FieldType.string,
+ get=lambda client, params: safe_find(
+ client.nodebalancers,
+ NodeBalancer.label == params.get("label"),
+ raise_not_found=True,
+ )._raw_json,
),
- },
+ ],
+ examples=docs.specdoc_examples,
)
-linode_nodebalancer_valid_filters = ["id", "label"]
+
+SPECDOC_META = module.spec
DOCUMENTATION = r"""
"""
@@ -98,94 +133,5 @@
RETURN = r"""
"""
-
-class LinodeNodeBalancerInfo(LinodeModuleBase):
- """Module for getting info about a Linode NodeBalancer"""
-
- def __init__(self) -> None:
- self.module_arg_spec = SPECDOC_META.ansible_spec
- self.required_one_of: List[str] = []
- self.results: dict = {
- "node_balancer": None,
- "configs": [],
- "nodes": [],
- "firewalls": [],
- }
-
- super().__init__(
- module_arg_spec=self.module_arg_spec,
- required_one_of=self.required_one_of,
- )
-
- def _get_matching_nodebalancer(self) -> Optional[NodeBalancer]:
- filter_items = {
- k: v
- for k, v in self.module.params.items()
- if k in linode_nodebalancer_valid_filters and v is not None
- }
-
- filter_statement = create_filter_and(NodeBalancer, filter_items)
-
- try:
- # Special case because ID is not filterable
- if "id" in filter_items.keys():
- result = NodeBalancer(self.client, self.module.params.get("id"))
- result._api_get() # Force lazy-loading
-
- return result
-
- return self.client.nodebalancers(filter_statement)[0]
- except IndexError:
- return None
- except Exception as exception:
- return self.fail(
- msg="failed to get nodebalancer {0}".format(exception)
- )
-
- def _get_node_by_label(
- self, config: NodeBalancerConfig, label: str
- ) -> Optional[NodeBalancerNode]:
- try:
- return config.nodes(NodeBalancerNode.label == label)[0]
- except IndexError:
- return None
- except Exception as exception:
- return self.fail(
- msg="failed to get nodebalancer node {0}, {1}".format(
- label, exception
- )
- )
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for NodeBalancer Info module"""
-
- node_balancer = self._get_matching_nodebalancer()
-
- if node_balancer is None:
- return self.fail("failed to get nodebalancer")
-
- self.results["node_balancer"] = node_balancer._raw_json
-
- for config in node_balancer.configs:
- self.results["configs"].append(config._raw_json)
-
- for node in config.nodes:
- node._api_get()
-
- self.results["nodes"].append(node._raw_json)
-
- # NOTE: Only the Firewall IDs are used here to reduce the
- # number of API requests made by this module and to simplify
- # the module result.
- self.results["firewalls"] = [v.id for v in node_balancer.firewalls()]
-
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the Linode NodeBalancer Info module"""
- LinodeNodeBalancerInfo()
-
-
if __name__ == "__main__":
- main()
+ module.run()
diff --git a/plugins/modules/nodebalancer_list.py b/plugins/modules/nodebalancer_list.py
index 8e9f30fb..8417f3c3 100644
--- a/plugins/modules/nodebalancer_list.py
+++ b/plugins/modules/nodebalancer_list.py
@@ -1,99 +1,26 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-"""This module allows users to list Nodebalancers."""
-from __future__ import absolute_import, division, print_function
+"""This module contains all of the functionality for listing Linode Node Balancers."""
-from typing import Any, Dict, Optional
+from __future__ import absolute_import, division, print_function
import ansible_collections.linode.cloud.plugins.module_utils.doc_fragments.nodebalancer_list 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 (
- construct_api_filter,
- get_all_paginated,
-)
-from ansible_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
+from ansible_collections.linode.cloud.plugins.module_utils.linode_common_list import (
+ ListModule,
)
-spec_filter = {
- "name": SpecField(
- type=FieldType.string,
- required=True,
- description=[
- "The name of the field to filter on.",
- "Valid filterable attributes can be found here: "
- "https://techdocs.akamai.com/linode-api/reference/get-node-balancers",
- ],
- ),
- "values": SpecField(
- type=FieldType.list,
- element_type=FieldType.string,
- required=True,
- description=[
- "A list of values to allow for this field.",
- "Fields will pass this filter if at least one of these values matches.",
- ],
- ),
-}
-
-spec = {
- # Disable the default values
- "state": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "label": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "order": SpecField(
- type=FieldType.string,
- description=["The order to list nodebalancers in."],
- default="asc",
- choices=["desc", "asc"],
- ),
- "order_by": SpecField(
- type=FieldType.string,
- description=["The attribute to order nodebalancers by."],
- ),
- "filters": SpecField(
- type=FieldType.list,
- element_type=FieldType.dict,
- suboptions=spec_filter,
- description=[
- "A list of filters to apply to the resulting nodebalancers."
- ],
- ),
- "count": SpecField(
- type=FieldType.integer,
- description=[
- "The number of results to return.",
- "If undefined, all results will be returned.",
- ],
- ),
-}
-
-SPECDOC_META = SpecDocMeta(
- description=["List and filter on Nodebalancers."],
- requirements=global_requirements,
- author=global_authors,
- options=spec,
+module = ListModule(
+ result_display_name="Node Balancers",
+ result_field_name="nodebalancers",
+ endpoint_template="/nodebalancers",
+ result_docs_url="https://techdocs.akamai.com/linode-api/reference/get-node-balancers",
examples=docs.specdoc_examples,
- return_values={
- "nodebalancers": SpecReturnValue(
- description="The returned nodebalancers.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-node-balancers",
- type=FieldType.list,
- elements=FieldType.dict,
- sample=docs.result_nodebalancers_samples,
- )
- },
+ result_samples=docs.result_nodebalancers_samples,
)
+SPECDOC_META = module.spec
+
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
@@ -101,34 +28,5 @@
RETURN = r"""
"""
-
-class Module(LinodeModuleBase):
- """Module for getting a list of Linode Nodebalancers"""
-
- def __init__(self) -> None:
- self.module_arg_spec = SPECDOC_META.ansible_spec
- self.results: Dict[str, Any] = {"nodebalancers": []}
-
- super().__init__(module_arg_spec=self.module_arg_spec)
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for nodebalancers list module"""
-
- filter_dict = construct_api_filter(self.module.params)
-
- self.results["nodebalancers"] = get_all_paginated(
- self.client,
- "/nodebalancers",
- filter_dict,
- num_results=self.module.params["count"],
- )
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the module"""
- Module()
-
-
if __name__ == "__main__":
- main()
+ module.run()
diff --git a/plugins/modules/nodebalancer_stats.py b/plugins/modules/nodebalancer_stats.py
index ecad435c..c7b6d778 100644
--- a/plugins/modules/nodebalancer_stats.py
+++ b/plugins/modules/nodebalancer_stats.py
@@ -5,60 +5,54 @@
from __future__ import absolute_import, division, print_function
-from typing import Any, Dict, List, Optional
-
from ansible_collections.linode.cloud.plugins.module_utils.doc_fragments import (
nodebalancer_stats 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_common_info import (
+ InfoModule,
+ InfoModuleAttr,
+ InfoModuleResult,
)
-from ansible_specdoc.objects import (
- FieldType,
- SpecDocMeta,
- SpecField,
- SpecReturnValue,
+from ansible_collections.linode.cloud.plugins.module_utils.linode_helper import (
+ safe_find,
)
+from ansible_specdoc.objects import FieldType
from linode_api4 import NodeBalancer
-linode_nodebalancer_stats_spec = {
- "state": SpecField(type=FieldType.string, required=False, doc_hide=True),
- "id": SpecField(
- type=FieldType.integer,
- description=[
- "The id of the nodebalancer for which the statistics apply to."
- ],
- conflicts_with=["label"],
- ),
- "label": SpecField(
- type=FieldType.string,
- description=[
- "The label of the nodebalancer for which the statistics apply to."
- ],
- conflicts_with=["id"],
+module = InfoModule(
+ primary_result=InfoModuleResult(
+ field_name="node_balancer_stats",
+ field_type=FieldType.dict,
+ display_name="Node Balancer Stats",
+ docs_url="https://techdocs.akamai.com/linode-api/reference/get-node-balancer-stats",
+ samples=docs.result_nodebalancer_stats_samples,
),
-}
-
-SPECDOC_META = SpecDocMeta(
- description=["View a Linode NodeBalancers Stats."],
- requirements=global_requirements,
- author=global_authors,
- options=linode_nodebalancer_stats_spec,
- examples=docs.specdoc_examples,
- return_values={
- "node_balancer_stats": SpecReturnValue(
- description="The NodeBalancer Stats in JSON serialized form.",
- docs_url="https://techdocs.akamai.com/linode-api/reference/get-node-balancer-stats",
- type=FieldType.dict,
- sample=docs.result_nodebalancer_stats_samples,
+ attributes=[
+ InfoModuleAttr(
+ display_name="ID",
+ name="id",
+ type=FieldType.integer,
+ get=lambda client, params: client.load(
+ NodeBalancer,
+ params.get("id"),
+ )._raw_json,
+ ),
+ InfoModuleAttr(
+ display_name="label",
+ name="label",
+ type=FieldType.string,
+ get=lambda client, params: safe_find(
+ client.nodebalancers,
+ NodeBalancer.label == params.get("label"),
+ raise_not_found=True,
+ )._raw_json,
),
- },
+ ],
+ examples=docs.specdoc_examples,
)
+SPECDOC_META = module.spec
+
DOCUMENTATION = r"""
"""
EXAMPLES = r"""
@@ -66,69 +60,5 @@
RETURN = r"""
"""
-
-class Module(LinodeModuleBase):
- """Module for getting info about a NodeBalancer's Statistics"""
-
- def __init__(self) -> None:
- self.required_one_of: List[str] = []
- self.results: Dict[str, Any] = {"node_balancer_stats": {}}
-
- self.module_arg_spec = SPECDOC_META.ansible_spec
-
- super().__init__(
- module_arg_spec=self.module_arg_spec,
- required_one_of=self.required_one_of,
- )
-
- def _get_stats_by_label(self, label: str) -> Optional[dict]:
- try:
- nodebalancer = self.client.nodebalancers(
- NodeBalancer.label == label
- )[0]
- return self.client.get(
- "/nodebalancers/{}/stats".format(nodebalancer.id)
- )
- except IndexError:
- return self.fail(
- msg="failed to find nodebalancer with label {0}: "
- "nodebalancer does not exist".format(label)
- )
- except Exception as exception:
- return self.fail(
- msg="failed to get nodebalancer {0}: {1}".format(
- label, exception
- )
- )
-
- def exec_module(self, **kwargs: Any) -> Optional[dict]:
- """Entrypoint for NodeBalancer Statistics module"""
-
- if (kwargs["id"] is None and kwargs["label"] is None) or (
- kwargs["id"] is not None and kwargs["label"] is not None
- ):
- return self.fail(
- msg="Label and ID are mutually exclusive and one "
- + "must be used to resolve Nodebalancer statistics."
- )
-
- if kwargs["id"] is not None:
- self.results["node_balancer_stats"] = self.client.get(
- "/nodebalancers/{}/stats".format(kwargs["id"])
- )
-
- if kwargs["label"] is not None:
- self.results["node_balancer_stats"] = self._get_stats_by_label(
- kwargs["label"]
- )
-
- return self.results
-
-
-def main() -> None:
- """Constructs and calls the nodebalancer_stats module"""
- Module()
-
-
if __name__ == "__main__":
- main()
+ module.run()
diff --git a/tests/integration/targets/nodebalancer_populated/tasks/main.yaml b/tests/integration/targets/nodebalancer_populated/tasks/main.yaml
index 19958f1f..12b41757 100644
--- a/tests/integration/targets/nodebalancer_populated/tasks/main.yaml
+++ b/tests/integration/targets/nodebalancer_populated/tasks/main.yaml
@@ -248,26 +248,34 @@
- rm_config.configs|length == 1
- rm_config.nodes|length == 1
- - name: Get info about the NodeBalancer
+ - name: Get info about the NodeBalancer by label
linode.cloud.nodebalancer_info:
label: '{{ create_populated_nodebalancer.node_balancer.label }}'
+ register: nb_info_label
+
+ - name: Get info about the NodeBalancer by id
+ linode.cloud.nodebalancer_info:
id: '{{ create_populated_nodebalancer.node_balancer.id }}'
- register: nb_info
+ register: nb_info_id
- name: Assert that info is valid
assert:
that:
- - nb_info.node_balancer.label == rm_config.node_balancer.label
- - nb_info.configs|length == 1
- - nb_info.nodes|length == 1
- - nb_info.nodes[0] != None
+ - nb_info_label.node_balancer.label == rm_config.node_balancer.label
+ - nb_info_label.configs|length == 1
+ - nb_info_label.nodes|length == 1
+ - nb_info_label.nodes[0] != None
+ - nb_info_id.node_balancer.id == rm_config.node_balancer.id
+ - nb_info_id.configs|length == 1
+ - nb_info_id.nodes|length == 1
+ - nb_info_id.nodes[0] != None
- name: Get info about a NodeBalancer that doesn't exist
linode.cloud.nodebalancer_info:
label: 'fake_nodebalancer-{{ r }}'
register: fake_nb_info
failed_when:
- - "'failed' not in fake_nb_info.msg"
+ - "'Failed' not in fake_nb_info.msg"
always:
- ignore_errors: yes
diff --git a/tests/integration/targets/nodebalancer_stats/tasks/main.yaml b/tests/integration/targets/nodebalancer_stats/tasks/main.yaml
index b51b77cd..0d002d69 100644
--- a/tests/integration/targets/nodebalancer_stats/tasks/main.yaml
+++ b/tests/integration/targets/nodebalancer_stats/tasks/main.yaml
@@ -76,13 +76,11 @@
linode.cloud.nodebalancer_stats:
id: '{{ create_populated_nodebalancer.node_balancer.id }}'
register: nodebalancer_stats_id
- failed_when: '"Stats are unavailable at this time" not in nodebalancer_stats_id.msg'
- name: Get stats about the Nodebalancer by label
linode.cloud.nodebalancer_stats:
label: '{{ create_populated_nodebalancer.node_balancer.label }}'
register: nodebalancer_stats_label
- failed_when: '"Stats are unavailable at this time" not in nodebalancer_stats_label.msg'
always:
- ignore_errors: yes