diff --git a/README.md b/README.md
index c3ca647..39c586b 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,7 @@ Name | Description |
[equinix.cloud.metal_project](./docs/modules/metal_project.md)|Manage Projects in Equinix Metal|
[equinix.cloud.metal_reserved_ip_block](./docs/modules/metal_reserved_ip_block.md)|Create/delete blocks of reserved IP addresses in a project.|
[equinix.cloud.metal_ssh_key](./docs/modules/metal_ssh_key.md)|Manage personal SSH keys in Equinix Metal|
+[equinix.cloud.metal_vlan](./docs/modules/metal_vlan.md)|Manage a VLAN resource in Equinix Metal|
### Info Modules
@@ -46,6 +47,7 @@ Name | Description |
[equinix.cloud.metal_project_info](./docs/modules/metal_project_info.md)|Gather information about Equinix Metal projects|
[equinix.cloud.metal_reserved_ip_block_info](./docs/modules/metal_reserved_ip_block_info.md)|Gather list of reserved IP blocks|
[equinix.cloud.metal_ssh_key_info](./docs/modules/metal_ssh_key_info.md)|Gather personal SSH keys|
+[equinix.cloud.metal_vlan_info](./docs/modules/metal_vlan_info.md)|Gather VLANs.|
### Inventory Plugins
@@ -142,4 +144,4 @@ Verify that new version of [equinix.cloud](https://galaxy.ansible.com/equinix/cl
GNU General Public License v3.0.
-See [COPYING](COPYING) to see the full text.
+See [COPYING](COPYING) to see the full text.
\ No newline at end of file
diff --git a/docs/modules/metal_organization.md b/docs/modules/metal_organization.md
index a0ca13d..66ee6fe 100644
--- a/docs/modules/metal_organization.md
+++ b/docs/modules/metal_organization.md
@@ -2,7 +2,7 @@
Lookup a single organization by ID in Equinix Metal.
-This resource only fetches a single organization resource ID.
+This resource only fetches a single organization by resource ID.
It doesn't allow to create or update organizations.
diff --git a/docs/modules/metal_vlan.md b/docs/modules/metal_vlan.md
new file mode 100644
index 0000000..08f3853
--- /dev/null
+++ b/docs/modules/metal_vlan.md
@@ -0,0 +1,66 @@
+# metal_vlan
+
+Manage the VLAN in Equinix Metal. You can use *id* or *vxlan* to lookup the resource. If you want to create new resource, you must provide *metro*.
+
+
+- [Examples](#examples)
+- [Parameters](#parameters)
+- [Return Values](#return-values)
+
+## Examples
+
+```yaml
+- name: Create new VLAN
+ hosts: localhost
+ tasks:
+ - equinix.cloud.metal_vlan:
+ description: "This is my new VLAN."
+ metro: "se"
+ vxlan: 1234
+ project_id: "778h50f7-75b6-4271-bc64-632b80f87de2"
+
+```
+
+
+
+
+
+
+
+
+
+
+## Parameters
+
+| Field | Type | Required | Description |
+|-----------|------|----------|------------------------------------------------------------------------------|
+| `id` |
`str` | Optional | ID of parent project" |
+| `project_id` | `str` | Optional | ID of parent project" |
+| `description` | `str` | Optional | Description of the VLAN **(Updatable)** |
+| `metro` | `str` | Optional | Metro in which to create the VLAN **(Updatable)** |
+| `vxlan` | `int` | Optional | VLAN ID, must be unique in metro **(Updatable)** |
+
+
+
+
+
+
+## Return Values
+
+- `metal_vlan` - The module object
+
+ - Sample Response:
+ ```json
+
+ {
+ "changed": false,
+ "id": "7624f0f7-75b6-4271-bc64-632b80f87de2",
+ "description": "This is my new VLAN.",
+ "metro": "se",
+ "vxlan": 1234,
+ "project_id": "778h50f7-75b6-4271-bc64-632b80f87de2"
+ }
+
+ ```
+
+
diff --git a/docs/modules/metal_vlan_info.md b/docs/modules/metal_vlan_info.md
new file mode 100644
index 0000000..7be8dca
--- /dev/null
+++ b/docs/modules/metal_vlan_info.md
@@ -0,0 +1,56 @@
+# metal_vlan_info
+
+Gather information about Equinix Metal VLAN resources
+
+
+- [Examples](#examples)
+- [Parameters](#parameters)
+- [Return Values](#return-values)
+
+## Examples
+
+```yaml
+- name: list vlans
+ equinix.cloud.metal_vlan_info:
+ register: listed_vlan
+
+```
+
+
+
+
+
+
+
+
+
+
+## Parameters
+
+| Field | Type | Required | Description |
+|-----------|------|----------|------------------------------------------------------------------------------|
+| `project_id` | `str` | Optional | Filter vlans by Project UUID. |
+
+
+
+
+
+
+## Return Values
+
+- `resources` - Found resources
+
+ - Sample Response:
+ ```json
+
+ [
+ {
+ "vxlan": 1234,
+ "metro": "se",
+ "id": "845b45a3-c565-47e5-b9b6-a86204a73d29",
+ "description": "My VLAN."
+ }
+ ]
+ ```
+
+
diff --git a/plugins/module_utils/metal/api_routes.py b/plugins/module_utils/metal/api_routes.py
index 0a64b62..1b1d1cb 100644
--- a/plugins/module_utils/metal/api_routes.py
+++ b/plugins/module_utils/metal/api_routes.py
@@ -56,6 +56,9 @@ def get_routes(mpc):
("metal_organization", action.GET): spec_types.Specs(
equinix_metal.OrganizationsApi(mpc).find_organization_by_id,
),
+ ("metal_vlan", action.GET): spec_types.Specs(
+ equinix_metal.VLANsApi(mpc).get_virtual_network,
+ ),
# LISTERS
('metal_project_device', action.LIST): spec_types.Specs(
@@ -102,6 +105,10 @@ def get_routes(mpc):
equinix_metal.HardwareReservationsApi(mpc).find_project_hardware_reservations,
{'id': 'project_id'},
),
+ ('metal_vlan', action.LIST): spec_types.Specs(
+ equinix_metal.VLANsApi(mpc).find_virtual_networks,
+ {'id': 'project_id'},
+ ),
# DELETERS
('metal_device', action.DELETE): spec_types.Specs(
@@ -119,6 +126,9 @@ def get_routes(mpc):
('metal_ssh_key', action.DELETE): spec_types.Specs(
equinix_metal.SSHKeysApi(mpc).delete_ssh_key,
),
+ ('metal_vlan', action.DELETE): spec_types.Specs(
+ equinix_metal.VLANsApi(mpc).delete_virtual_network,
+ ),
# CREATORS
@@ -152,6 +162,11 @@ def get_routes(mpc):
{},
equinix_metal.SSHKeyCreateInput,
),
+ ('metal_vlan', action.CREATE): spec_types.Specs(
+ equinix_metal.VLANsApi(mpc).create_virtual_network,
+ {'id': 'project_id'},
+ equinix_metal.VirtualNetworkCreateInput,
+ ),
# UPDATERS
('metal_device', action.UPDATE): spec_types.Specs(
diff --git a/plugins/module_utils/metal/metal_api.py b/plugins/module_utils/metal/metal_api.py
index 2e4bb0a..c43b296 100644
--- a/plugins/module_utils/metal/metal_api.py
+++ b/plugins/module_utils/metal/metal_api.py
@@ -137,6 +137,7 @@ def extract_ids_from_projects_hrefs(resource: dict):
'operating_systems',
'hardware_reservations',
'organizations',
+ 'virtual_networks'
]
@@ -191,6 +192,14 @@ def get_assignment_address(resource: dict):
}
+VLAN_RESPONSE_ATTRIBUTE_MAP = {
+ "id": "id",
+ "description": optional_str('description'),
+ "metro": "metro",
+ "vxlan": "vxlan",
+}
+
+
def get_attribute_mapper(resource_type):
"""
Returns attribute mapper for the given resource type.
@@ -201,6 +210,7 @@ def get_attribute_mapper(resource_type):
ip_assignment_resources = set(['metal_ip_assignment'])
ssh_key_resources = set(['metal_ssh_key', 'metal_project_ssh_key'])
hardware_reservation_resources = set(['metal_project_hardware_reservation', 'metal_hardware_reservation'])
+ vlan_resources = set(["metal_vlan"])
if resource_type in device_resources:
return METAL_DEVICE_RESPONSE_ATTRIBUTE_MAP
elif resource_type in project_resources:
@@ -219,6 +229,8 @@ def get_attribute_mapper(resource_type):
return METAL_HARDWARE_RESERVATION_RESPONSE_ATTRIBUTE_MAP
elif resource_type == 'metal_organization':
return METAL_ORGANIZATION_RESPONSE_ATTRIBUTE_MAP
+ elif resource_type in vlan_resources:
+ return VLAN_RESPONSE_ATTRIBUTE_MAP
else:
raise NotImplementedError("No mapper for resource type %s" % resource_type)
@@ -235,7 +247,7 @@ def call(resource_type, action, equinix_metal_client, params={}):
call = api_routes.build_api_call(conf, params)
response = call.do()
# uncomment to check response in /tmp/q
- #import q; q(response)
+ # import q; q(response)
if action == action.DELETE:
return None
attribute_mapper = get_attribute_mapper(resource_type)
diff --git a/plugins/modules/metal_organization.py b/plugins/modules/metal_organization.py
index 609e7a5..b72d5dc 100644
--- a/plugins/modules/metal_organization.py
+++ b/plugins/modules/metal_organization.py
@@ -10,7 +10,7 @@
author: Equinix DevRel Team (@equinix)
description: !!python/tuple
- 'Lookup a single organization by ID in Equinix Metal. '
-- 'This resource only fetches a single organization resource ID. '
+- 'This resource only fetches a single organization by resource ID. '
- It doesn't allow to create or update organizations.
module: metal_organization
notes: []
diff --git a/plugins/modules/metal_vlan.py b/plugins/modules/metal_vlan.py
new file mode 100644
index 0000000..852cefc
--- /dev/null
+++ b/plugins/modules/metal_vlan.py
@@ -0,0 +1,205 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# DOCUMENTATION, EXAMPLES, and RETURN are generated by
+# ansible_specdoc. Do not edit them directly.
+
+DOCUMENTATION = '''
+author: Equinix DevRel Team (@equinix)
+description: Manage the VLAN in Equinix Metal. You can use *id* or *vxlan* to lookup
+ the resource. If you want to create new resource, you must provide *metro*.
+module: metal_vlan
+notes: []
+options:
+ description:
+ description:
+ - Description of the VLAN
+ required: false
+ type: str
+ id:
+ description:
+ - ID of parent project
+ required: false
+ type: str
+ metro:
+ description:
+ - Metro in which to create the VLAN
+ required: false
+ type: str
+ project_id:
+ description:
+ - ID of parent project
+ required: false
+ type: str
+ vxlan:
+ description:
+ - VLAN ID, must be unique in metro
+ required: false
+ type: int
+requirements: null
+short_description: Manage a VLAN resource in Equinix Metal
+'''
+EXAMPLES = '''
+- name: Create new VLAN
+ hosts: localhost
+ tasks:
+ - equinix.cloud.metal_vlan:
+ description: This is my new VLAN.
+ metro: se
+ vxlan: 1234
+ project_id: 778h50f7-75b6-4271-bc64-632b80f87de2
+'''
+RETURN = '''
+metal_vlan:
+ description: The module object
+ returned: always
+ sample:
+ - "\n{\n \"changed\": false,\n \"id\": \"7624f0f7-75b6-4271-bc64-632b80f87de2\"\
+ ,\n \"description\": \"This is my new VLAN.\",\n \"metro\": \"se\",\n \"vxlan\"\
+ : 1234,\n \"project_id\": \"778h50f7-75b6-4271-bc64-632b80f87de2\"\n}\n"
+ type: dict
+'''
+
+# End of generated documentation
+
+# This is a template for a new module. It is not meant to be used as is.
+# It is meant to be copied and modified to create a new module.
+# Replace all occurrences of "metal_resource" with the name of the new
+# module, for example "metal_vlan".
+
+
+from ansible.module_utils._text import to_native
+from ansible_specdoc.objects import (
+ SpecField,
+ FieldType,
+ SpecReturnValue,
+)
+import traceback
+
+from ansible_collections.equinix.cloud.plugins.module_utils.equinix import (
+ EquinixModule,
+ get_diff,
+ getSpecDocMeta,
+)
+
+MODULE_NAME = "metal_vlan"
+
+module_spec = dict(
+ id=SpecField(
+ type=FieldType.string,
+ description=['UUID of vlan"'],
+ ),
+ project_id=SpecField(
+ type=FieldType.string,
+ description=['ID of parent project"'],
+ ),
+ description=SpecField(
+ type=FieldType.string,
+ description=["Description of the VLAN"],
+ ),
+ metro=SpecField(
+ type=FieldType.string,
+ description=["Metro in which to create the VLAN"],
+ ),
+ vxlan=SpecField(
+ type=FieldType.integer,
+ description=["VLAN ID, must be unique in metro"],
+ ),
+)
+
+
+specdoc_examples = [
+ """
+- name: Create new VLAN
+ hosts: localhost
+ tasks:
+ - equinix.cloud.metal_vlan:
+ description: "This is my new VLAN."
+ metro: "se"
+ vxlan: 1234
+ project_id: "778h50f7-75b6-4271-bc64-632b80f87de2"
+""",
+]
+
+result_sample = [
+ """
+{
+ "changed": false,
+ "id": "7624f0f7-75b6-4271-bc64-632b80f87de2",
+ "description": "This is my new VLAN.",
+ "metro": "se",
+ "vxlan": 1234,
+ "project_id": "778h50f7-75b6-4271-bc64-632b80f87de2"
+}
+"""
+]
+
+MUTABLE_ATTRIBUTES = [k for k, v in module_spec.items() if v.editable]
+
+SPECDOC_META = getSpecDocMeta(
+ short_description="Manage a VLAN resource in Equinix Metal",
+ description=(
+ "Manage the VLAN in Equinix Metal. "
+ "You can use *id* or *vxlan* to lookup the resource. "
+ "If you want to create new resource, you must provide *metro*."
+ ),
+ examples=specdoc_examples,
+ options=module_spec,
+ return_values={
+ "metal_vlan": SpecReturnValue(
+ description="The module object",
+ type=FieldType.dict,
+ sample=result_sample,
+ ),
+ },
+)
+
+
+def main():
+ module = EquinixModule(
+ argument_spec=SPECDOC_META.ansible_spec,
+ )
+
+ state = module.params.get("state")
+ changed = False
+ try:
+ module.params_syntax_check()
+ if module.params.get("id"):
+ tolerate_not_found = state == "absent"
+ fetched = module.get_by_id(MODULE_NAME, tolerate_not_found)
+ else:
+ fetched = module.get_one_from_list(
+ MODULE_NAME,
+ ["vxlan"],
+ )
+
+ if fetched:
+ module.params["id"] = fetched["id"]
+ if state == "present":
+ diff = get_diff(module.params, fetched, MUTABLE_ATTRIBUTES)
+ if diff:
+ module.fail_json(msg="Resource metal_vlan is not mutable.")
+
+ else:
+ module.delete_by_id(MODULE_NAME)
+ changed = True
+ else:
+ if state == "present":
+ fetched = module.create(MODULE_NAME)
+ if "id" not in fetched:
+ module.fail_json(msg="UUID not found in resource creation response")
+ changed = True
+ else:
+ fetched = {}
+ except Exception as e:
+ tb = traceback.format_exc()
+ module.fail_json(msg=f"Error in {MODULE_NAME}: {to_native(e)}", exception=tb)
+
+ fetched.update({"changed": changed})
+ module.exit_json(**fetched)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/metal_vlan_info.py b/plugins/modules/metal_vlan_info.py
new file mode 100644
index 0000000..c911811
--- /dev/null
+++ b/plugins/modules/metal_vlan_info.py
@@ -0,0 +1,107 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# DOCUMENTATION, EXAMPLES, and RETURN are generated by
+# ansible_specdoc. Do not edit them directly.
+
+DOCUMENTATION = '''
+author: Equinix DevRel Team (@equinix)
+description: Gather information about Equinix Metal VLAN resources
+module: metal_vlan_info
+notes: []
+options:
+ project_id:
+ description:
+ - Filter vlans by Project UUID.
+ required: false
+ type: str
+requirements: null
+short_description: Gather VLANs.
+'''
+EXAMPLES = '''
+- name: list vlans
+ equinix.cloud.metal_vlan_info: null
+ register: listed_vlan
+'''
+RETURN = '''
+resources:
+ description: Found resources
+ returned: always
+ sample:
+ - "\n[\n {\n \"vxlan\": 1234,\n \"metro\": \"se\",\n \"id\": \"845b45a3-c565-47e5-b9b6-a86204a73d29\"\
+ ,\n \"description\": \"My VLAN.\"\n }\n]"
+ type: dict
+'''
+
+# End
+
+from ansible.module_utils._text import to_native
+from ansible_specdoc.objects import SpecField, FieldType, SpecReturnValue
+import traceback
+
+from ansible_collections.equinix.cloud.plugins.module_utils.equinix import (
+ EquinixModule,
+ getSpecDocMeta,
+)
+
+module_spec = dict(
+ project_id=SpecField(
+ type=FieldType.string,
+ description=['Filter vlans by Project UUID.'],
+ required=False,
+ ),
+)
+
+specdoc_examples = ['''
+- name: list vlans
+ equinix.cloud.metal_vlan_info:
+ register: listed_vlan
+''',
+]
+
+result_sample = ['''
+[
+ {
+ "vxlan": 1234,
+ "metro": "se",
+ "id": "845b45a3-c565-47e5-b9b6-a86204a73d29",
+ "description": "My VLAN"
+ }
+]''',
+]
+
+SPECDOC_META = getSpecDocMeta(
+ short_description="Gather VLANs.",
+ description=(
+ 'Gather information about Equinix Metal VLAN resources'
+ ),
+ examples=specdoc_examples,
+ options=module_spec,
+ return_values={
+ "resources": SpecReturnValue(
+ description='Found resources',
+ type=FieldType.dict,
+ sample=result_sample,
+ ),
+ },
+)
+
+
+def main():
+ module = EquinixModule(
+ argument_spec=SPECDOC_META.ansible_spec,
+ is_info=True,
+ )
+ try:
+ module.params_syntax_check()
+ return_value = {'resources': module.get_list("metal_vlan")}
+ except Exception as e:
+ tr = traceback.format_exc()
+ module.fail_json(msg=to_native(e), exception=tr)
+ module.exit_json(**return_value)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/integration/targets/metal_vlan/tasks/main.yml b/tests/integration/targets/metal_vlan/tasks/main.yml
new file mode 100644
index 0000000..e894407
--- /dev/null
+++ b/tests/integration/targets/metal_vlan/tasks/main.yml
@@ -0,0 +1,87 @@
+- name: metal_vlan
+ module_defaults:
+ equinix.cloud.metal_vlan:
+ metal_api_token: '{{ metal_api_token }}'
+ equinix.cloud.metal_vlan_info:
+ metal_api_token: '{{ metal_api_token }}'
+ equinix.cloud.metal_project:
+ metal_api_token: '{{ metal_api_token }}'
+ equinix.cloud.metal_project_info:
+ metal_api_token: '{{ metal_api_token }}'
+ metal_ua_prefix: '{{ metal_ua_prefix }}'
+ block:
+ - set_fact:
+ test_resource_name_prefix: 'ansible-integration-test-vlan'
+ - set_fact:
+ unique_id: "{{ lookup('community.general.random_string', upper=false, numbers=false, special=false) }}"
+ - set_fact:
+ test_prefix: "{{ test_resource_name_prefix }}-{{ unique_id }}"
+ - set_fact:
+ test_metro: 'am'
+ - set_fact:
+ test_description: 'My new VLAN'
+ - set_fact:
+ test_vxlan: 123
+
+ - name: create project for test
+ equinix.cloud.metal_project:
+ name: "{{ test_prefix }}-project"
+ register: project
+
+ - assert:
+ that:
+ - project.name == '{{ test_prefix }}-project'
+
+ - name: create first vlan for test
+ equinix.cloud.metal_vlan:
+ project_id: "{{ project.id }}"
+ metro: "{{ test_metro }}"
+ description: "{{ test_description }}"
+ vxlan: "{{ test_vxlan }}"
+ register: first_vlan
+
+ - assert:
+ that:
+ - first_vlan.vxlan == {{ test_vxlan }}
+
+ - name: get vlan info
+ equinix.cloud.metal_vlan_info:
+ project_id: "{{ project.id }}"
+ register: vlan_info_listed
+
+ - assert:
+ that:
+ - "vlan_info_listed.resources | length == 1"
+ - vlan_info_listed.resources[0].id == "{{ first_vlan.id }}"
+
+ - name: fetch existing vlan
+ equinix.cloud.metal_vlan:
+ id: "{{ first_vlan.id }}"
+ register: first_vlan_fetched
+
+ - name: delete vlan
+ equinix.cloud.metal_vlan:
+ id: "{{ first_vlan.id }}"
+ state: absent
+
+ - name: delete vlan again to check indempotence
+ equinix.cloud.metal_vlan:
+ id: "{{ first_vlan.id }}"
+ state: absent
+
+ always:
+ - name: Announce teardown start
+ debug:
+ msg: "***** TESTING COMPLETE. COMMENCE TEARDOWN *****"
+
+ - name: list test projects
+ equinix.cloud.metal_project_info:
+ name: "{{ test_prefix }}"
+ register: test_projects_listed
+
+ - name: delete test projects
+ equinix.cloud.metal_project:
+ id: "{{ item.id }}"
+ state: absent
+ loop: "{{ test_projects_listed.resources }}"
+ ignore_errors: yes
\ No newline at end of file