diff --git a/README.md b/README.md index 61f9719..e0c2215 100755 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This is repository for Ansible collection registered in Ansible Galaxy as [equinix.cloud](https://galaxy.ansible.com/ui/repo/published/equinix/cloud/). The collection contains various plugins for managing Equinix services. -For users transitioning from the [equinix.metal collection](https://github.com/equinix/ansible-collection-metal), please refer to our [Migration Guide](MIGRATION.md) for detailed instructions on migrating to this collection (`equinix.cloud`). +For users transitioning from the [equinix.metal collection](https://github.com/equinix/ansible-collection-metal), please refer to our [Migration Guide](MIGRATION.MD) for detailed instructions on migrating to this collection (`equinix.cloud`). ## Ansible version compatibility @@ -23,18 +23,20 @@ Modules for managing Equinix infrastructure. Name | Description | --- | ------------ | -[equinix.cloud.metal_connection](./docs/modules/metal_connection.md)|Manage an Interconnection in Equinix Metal| -[equinix.cloud.metal_device](./docs/modules/metal_device.md)|Create, update, or delete Equinix Metal devices| -[equinix.cloud.metal_gateway](./docs/modules/metal_gateway.md)|Manage Metal Gateway in Equinix Metal| -[equinix.cloud.metal_hardware_reservation](./docs/modules/metal_hardware_reservation.md)|Lookup a single hardware_reservation by ID in Equinix Metal| -[equinix.cloud.metal_ip_assignment](./docs/modules/metal_ip_assignment.md)|Manage Equinix Metal IP assignments| -[equinix.cloud.metal_organization](./docs/modules/metal_organization.md)|Lookup a single organization by ID in Equinix Metal| -[equinix.cloud.metal_project](./docs/modules/metal_project.md)|Manage Projects in Equinix Metal| -[equinix.cloud.metal_project_ssh_key](./docs/modules/metal_project_ssh_key.md)|Manage a project ssh key 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| -[equinix.cloud.metal_vrf](./docs/modules/metal_vrf.md)|Manage a VRF resource in Equinix Metal| +[equinix.cloud.metal_bgp_session](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_bgp_session.md)|Manage BGP sessions in Equinix Metal| +[equinix.cloud.metal_connection](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_connection.md)|Manage an Interconnection in Equinix Metal| +[equinix.cloud.metal_device](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_device.md)|Create, update, or delete Equinix Metal devices| +[equinix.cloud.metal_gateway](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_gateway.md)|Manage Metal Gateway in Equinix Metal| +[equinix.cloud.metal_hardware_reservation](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_hardware_reservation.md)|Lookup a single hardware_reservation by ID in Equinix Metal| +[equinix.cloud.metal_ip_assignment](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_ip_assignment.md)|Manage Equinix Metal IP assignments| +[equinix.cloud.metal_organization](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_organization.md)|Lookup a single organization by ID in Equinix Metal| +[equinix.cloud.metal_project](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_project.md)|Manage Projects in Equinix Metal| +[equinix.cloud.metal_project_bgp_config](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_project_bgp_config.md)|Manage BGP Config for Equinix Metal Project| +[equinix.cloud.metal_project_ssh_key](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_project_ssh_key.md)|Manage a project ssh key in Equinix Metal| +[equinix.cloud.metal_reserved_ip_block](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_reserved_ip_block.md)|Create/delete blocks of reserved IP addresses in a project.| +[equinix.cloud.metal_ssh_key](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_ssh_key.md)|Manage personal SSH keys in Equinix Metal| +[equinix.cloud.metal_vlan](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_vlan.md)|Manage a VLAN resource in Equinix Metal| +[equinix.cloud.metal_vrf](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_vrf.md)|Manage a VRF resource in Equinix Metal| ### Info Modules @@ -43,21 +45,22 @@ Modules for retrieving information about existing Equinix infrastructure. Name | Description | --- | ------------ | -[equinix.cloud.metal_available_ips_info](./docs/modules/metal_available_ips_info.md)|Get list of avialable IP addresses from a reserved IP block| -[equinix.cloud.metal_connection_info](./docs/modules/metal_connection_info.md)|Gather information about Interconnections| -[equinix.cloud.metal_device_info](./docs/modules/metal_device_info.md)|Select list of Equinix Metal devices| -[equinix.cloud.metal_gateway_info](./docs/modules/metal_gateway_info.md)|Gather information about Metal Gateways| -[equinix.cloud.metal_hardware_reservation_info](./docs/modules/metal_hardware_reservation_info.md)|Gather information about Equinix Metal hardware_reservations| -[equinix.cloud.metal_ip_assignment_info](./docs/modules/metal_ip_assignment_info.md)|Gather IP address assignments for a device| -[equinix.cloud.metal_metro_info](./docs/modules/metal_metro_info.md)|Gather information about Equinix Metal metros| -[equinix.cloud.metal_operating_system_info](./docs/modules/metal_operating_system_info.md)|Gather information about Operating Systems available for devices in Equinix Metal| -[equinix.cloud.metal_organization_info](./docs/modules/metal_organization_info.md)|Gather information about Equinix Metal organizations| -[equinix.cloud.metal_project_info](./docs/modules/metal_project_info.md)|Gather information about Equinix Metal projects| -[equinix.cloud.metal_project_ssh_key_info](./docs/modules/metal_project_ssh_key_info.md)|Gather project SSH keys.| -[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.| -[equinix.cloud.metal_vrf_info](./docs/modules/metal_vrf_info.md)|Gather VRFs| +[equinix.cloud.metal_available_ips_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_available_ips_info.md)|Get list of avialable IP addresses from a reserved IP block| +[equinix.cloud.metal_bgp_session_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_bgp_session_info.md)|Gather information BGP sessions in Equinix Metal| +[equinix.cloud.metal_connection_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_connection_info.md)|Gather information about Interconnections| +[equinix.cloud.metal_device_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_device_info.md)|Select list of Equinix Metal devices| +[equinix.cloud.metal_gateway_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_gateway_info.md)|Gather information about Metal Gateways| +[equinix.cloud.metal_hardware_reservation_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_hardware_reservation_info.md)|Gather information about Equinix Metal hardware_reservations| +[equinix.cloud.metal_ip_assignment_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_ip_assignment_info.md)|Gather IP address assignments for a device| +[equinix.cloud.metal_metro_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_metro_info.md)|Gather information about Equinix Metal metros| +[equinix.cloud.metal_operating_system_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_operating_system_info.md)|Gather information about Operating Systems available for devices in Equinix Metal| +[equinix.cloud.metal_organization_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_organization_info.md)|Gather information about Equinix Metal organizations| +[equinix.cloud.metal_project_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_project_info.md)|Gather information about Equinix Metal projects| +[equinix.cloud.metal_project_ssh_key_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_project_ssh_key_info.md)|Gather project SSH keys.| +[equinix.cloud.metal_reserved_ip_block_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_reserved_ip_block_info.md)|Gather list of reserved IP blocks| +[equinix.cloud.metal_ssh_key_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_ssh_key_info.md)|Gather personal SSH keys| +[equinix.cloud.metal_vlan_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_vlan_info.md)|Gather VLANs.| +[equinix.cloud.metal_vrf_info](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/modules/metal_vrf_info.md)|Gather VRFs| ### Inventory Plugins @@ -66,7 +69,7 @@ Dynamically add Equinix infrastructure to an Ansible inventory. Name | --- | -[equinix.cloud.metal_device](./docs/inventory/metal_device.rst)| +[equinix.cloud.metal_device](https://github.com/equinix-labs/ansible-collection-equinix/blob/0.3.0/docs/inventory/metal_device.md)| @@ -83,7 +86,7 @@ The Python module dependencies are not installed by `ansible-galaxy`. They can be manually installed using pip: ```shell -pip install -r https://raw.githubusercontent.com/equinix-labs/ansible-collection-equinix/main/requirements.txt +pip install -r https://raw.githubusercontent.com/equinix-labs/ansible-collection-equinix/0.3.0/requirements.txt ``` ## Usage diff --git a/docs/modules/metal_bgp_session.md b/docs/modules/metal_bgp_session.md new file mode 100644 index 0000000..1db1ee0 --- /dev/null +++ b/docs/modules/metal_bgp_session.md @@ -0,0 +1,80 @@ +# metal_bgp_session + +Manage BGP sessions in Equinix Metal.Create, update or delete BGP session. To look up an existing session, pass only the *id* attribute. + + +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Examples + +```yaml +- name: Start first test bgp session + hosts: localhost + tasks: + - equinix.cloud.metal_bgp_session: + device_id: 8ea9837a-6d19-4607-b166-f7f7bb04b022 + address_family: ipv6 + default_route: true + +``` + +```yaml +- name: Delete bgp session + hosts: localhost + tasks: + - equinix.cloud.metal_bgp_session: + id: 1273edef-39af-4df0-85bb-02a847a484d1 + state: absent + +``` + + + + + + + + + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `id` |
`str`
|
Optional
| UUID of the BGP session to look up | +| `device_id` |
`str`
|
Optional
| Device ID for the BGP session | +| `address_family` |
`str`
|
Optional
| BGP session address family, "ipv4" or "ipv6" | +| `default_route` |
`bool`
|
Optional
| Boolean flag to set the default route policy. False by default. | + + + + + + +## Return Values + +- `metal_bgp_session` - The module object + + - Sample Response: + ```json + + [ + { + "address_family": "ipv4", + "default_route": true, + "device_id": "2066d33e-7c43-4d78-87a3-aaa434913f7f", + "id": "fc2d43e6-d606-47f7-9611-9d77aee443b5" + }, + { + "address_family": "ipv6", + "default_route": true, + "device_id": "bfab58c0-0723-49aa-a64e-6caf1b8ea2e2", + "id": "277d4a7a-82dd-4e7c-bf79-8a1de6882982" + } + ] + + ``` + + diff --git a/docs/modules/metal_bgp_session_info.md b/docs/modules/metal_bgp_session_info.md new file mode 100644 index 0000000..b384557 --- /dev/null +++ b/docs/modules/metal_bgp_session_info.md @@ -0,0 +1,62 @@ +# metal_bgp_session_info + +Gather information BGP sessions in Equinix Metal. You can fetch it by device ID or project ID. + + +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Examples + +```yaml +- name: Gather information about all BGP sessions in a project + hosts: localhost + tasks: + - equinix.cloud.metal_bgp_session_info: + project_id: 2a5122b9-c323-4d5c-b53c-9ad3f54273e7 + +``` + +```yaml + +``` + + + + + + + + + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `device_id` |
`str`
|
Optional
| Find BGP sessions by device ID. | +| `project_id` |
`str`
|
Optional
| Find BGP sessions by project ID. | + + + + + + +## Return Values + +- `resources` - Found resources + + - Sample Response: + ```json + [ + { + "address_family": "ipv6", + "default_route": true, + "device_id": "b068984f-f7d9-43a2-aa45-de04dcf4fe06", + "id": "03912bd6-a158-47ad-8bc7-c93df338fe0d" + } + ] + ``` + + diff --git a/docs/modules/metal_project_bgp_config.md b/docs/modules/metal_project_bgp_config.md new file mode 100644 index 0000000..5fb1ca6 --- /dev/null +++ b/docs/modules/metal_project_bgp_config.md @@ -0,0 +1,62 @@ +# metal_project_bgp_config + +You can use this module to enable BGP Config for a project. To lookup BGP Config of an existing project, call the module only with `project_id`. + + +- [Examples](#examples) +- [Parameters](#parameters) +- [Return Values](#return-values) + +## Examples + +```yaml +- name: Enable local BGP Config in Equinix Metal project + hosts: localhost + tasks: + - equinix.cloud.metal_project_bgp_config: + deployment_type: local + asn: 65000 + md5: null + use_case: "ansible test" + project_id: "{{ test_project.id }}" + +``` + + + + + + + + + + +## Parameters + +| Field | Type | Required | Description | +|-----------|------|----------|------------------------------------------------------------------------------| +| `project_id` |
`str`
|
**Required**
| UUID of the project where BGP Config should be enabled | +| `asn` |
`int`
|
**Required**
| Autonomous System Number for local BGP deployment | +| `deployment_type` |
`str`
|
**Required**
| "local" or "global". Local deployment type is likely to be usable immediately, "global" will need to be reviewed by Equinix Metal support. | +| `md5` |
`str`
|
Optional
| Password for BGP session in plaintext (not a checksum) | +| `use_case` |
`str`
|
Optional
| Description of your BGP use-case for Equinix Metal support | + + + + + + +## Return Values + +- `metal_project_bgp_config` - The module object + + - Sample Response: + ```json + + { + "changed": true + } + + ``` + + diff --git a/plugins/module_utils/metal/api_routes.py b/plugins/module_utils/metal/api_routes.py index 172b7c1..62d076b 100644 --- a/plugins/module_utils/metal/api_routes.py +++ b/plugins/module_utils/metal/api_routes.py @@ -69,6 +69,12 @@ def get_routes(mpc): equinix_metal.MetalGatewaysApi(mpc).find_metal_gateway_by_id, extra_kwargs={"include": ["ip_reservation"]}, ), + ("metal_bgp_session", action.GET): spec_types.Specs( + equinix_metal.BGPApi(mpc).find_bgp_session_by_id, + ), + ('metal_project_bgp_config', action.GET): spec_types.Specs( + equinix_metal.BGPApi(mpc).find_bgp_config_by_project, + ), # LISTERS ('metal_project_device', action.LIST): spec_types.Specs( @@ -140,6 +146,14 @@ def get_routes(mpc): {'project_id': 'project_id'}, extra_kwargs={"include": ["ip_reservation"]}, ), + ('metal_bgp_session', action.LIST): spec_types.Specs( + equinix_metal.DevicesApi(mpc).find_bgp_sessions, + {'id': 'device_id'}, + ), + ('metal_bgp_session_by_project', action.LIST): spec_types.Specs( + equinix_metal.BGPApi(mpc).find_bgp_config_by_project, + {'id': 'project_id'}, + ), # DELETERS ('metal_device', action.DELETE): spec_types.Specs( @@ -170,6 +184,10 @@ def get_routes(mpc): equinix_metal.MetalGatewaysApi(mpc).delete_metal_gateway, extra_kwargs={"include": ["ip_reservation"]}, ), + ('metal_bgp_session', action.DELETE): spec_types.Specs( + equinix_metal.BGPApi(mpc).delete_bgp_session, + ), + # CREATORS ('metal_device', action.CREATE): spec_types.Specs( @@ -249,6 +267,16 @@ def get_routes(mpc): {"include": ["ip_reservation"]}, ), + ('metal_bgp_session', action.CREATE): spec_types.Specs( + equinix_metal.DevicesApi(mpc).create_bgp_session, + {'id': 'device_id'}, + equinix_metal.BGPSessionInput, + ), + ('metal_project_bgp_config', action.CREATE): spec_types.Specs( + equinix_metal.BGPApi(mpc).request_bgp_config, + {'id': 'project_id'}, + equinix_metal.BgpConfigRequestInput, + ), # UPDATERS ('metal_device', action.UPDATE): spec_types.Specs( @@ -286,4 +314,9 @@ def get_routes(mpc): {}, equinix_metal.VrfUpdateInput, ), + ('metal_bgp_session', action.UPDATE): spec_types.Specs( + equinix_metal.BGPApi(mpc).update_bgp_session, + {}, + equinix_metal.BGPSessionInput, + ), } diff --git a/plugins/module_utils/metal/metal_api.py b/plugins/module_utils/metal/metal_api.py index d6a6615..828c844 100644 --- a/plugins/module_utils/metal/metal_api.py +++ b/plugins/module_utils/metal/metal_api.py @@ -149,6 +149,8 @@ def extract_ids_from_projects_hrefs(resource: dict): 'interconnections', 'vrfs', 'metal_gateways', + 'bgp_sessions', # metal_bgp_session + 'sessions', # metal_bgp_session_info ] @@ -209,7 +211,6 @@ def get_assignment_address(resource: dict): "vxlan": "vxlan", } - METAL_CONNECTION_RESPONSE_ATTRIBUTE_MAP = { 'id': 'id', 'name': 'name', @@ -253,6 +254,23 @@ def private_ipv4_subnet_size(resource: dict): 'metal_state': optional_str('state'), } +METAL_BGP_SESSION_RESPONSE_ATTRIBUTE_MAP = { + 'id': 'id', + 'address_family': 'address_family', + 'device_id': 'device.id', + 'default_route': 'default_route', +} + +METAL_PROJECT_BGP_CONFIG_RESPONSE_ATTRIBUTE_MAP = { + 'project_id': optional('project.id'), + 'asn': optional('asn'), + 'deployment_type': optional('deployment_type'), + 'md5': 'md5', + 'max_prefix': 'max_prefix', + 'id': optional('id'), + 'status': optional('status'), + +} def get_attribute_mapper(resource_type): """ @@ -270,6 +288,8 @@ def get_attribute_mapper(resource_type): 'metal_connection_project_vlanfabric', 'metal_connection_project_vrf']) vrf_resources = set(['metal_vrf']) gateway_resources = set(["metal_gateway", "metal_gateway_vrf"]) + bgp_resources = {'metal_bgp_session', 'metal_bgp_session_by_project'} + project_bgp_config_resources = {'metal_project_bgp_config'} if resource_type in device_resources: return METAL_DEVICE_RESPONSE_ATTRIBUTE_MAP elif resource_type in project_resources: @@ -296,6 +316,10 @@ def get_attribute_mapper(resource_type): return METAL_VRF_RESPONSE_ATTRIBUTE_MAP elif resource_type in gateway_resources: return METAL_GATEWAY_RESPONSE_ATTRIBUTE_MAP + elif resource_type in bgp_resources: + return METAL_BGP_SESSION_RESPONSE_ATTRIBUTE_MAP + elif resource_type in project_bgp_config_resources: + return METAL_PROJECT_BGP_CONFIG_RESPONSE_ATTRIBUTE_MAP else: raise NotImplementedError("No mapper for resource type %s" % resource_type) @@ -311,6 +335,8 @@ 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) if action == action.DELETE: return None attribute_mapper = get_attribute_mapper(resource_type) diff --git a/plugins/modules/metal_bgp_session.py b/plugins/modules/metal_bgp_session.py new file mode 100644 index 0000000..ebe3bfc --- /dev/null +++ b/plugins/modules/metal_bgp_session.py @@ -0,0 +1,215 @@ +#!/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 BGP sessions in Equinix Metal.Create, update or delete BGP session. + To look up an existing session, pass only the *id* attribute. +module: metal_bgp_session +notes: [] +options: + address_family: + description: + - BGP session address family, "ipv4" or "ipv6" + required: false + type: str + default_route: + description: + - Boolean flag to set the default route policy. False by default. + required: false + type: bool + device_id: + description: + - Device ID for the BGP session + required: false + type: str + id: + description: + - UUID of the BGP session to look up + required: false + type: str +requirements: null +short_description: Manage BGP sessions in Equinix Metal +''' +EXAMPLES = ''' +- name: Start first test bgp session + hosts: localhost + tasks: + - equinix.cloud.metal_bgp_session: + device_id: 8ea9837a-6d19-4607-b166-f7f7bb04b022 + address_family: ipv6 + default_route: true +- name: Delete bgp session + hosts: localhost + tasks: + - equinix.cloud.metal_bgp_session: + id: 1273edef-39af-4df0-85bb-02a847a484d1 + state: absent +''' +RETURN = ''' +metal_bgp_session: + description: The module object + returned: always + sample: + - "\n [\n {\n \"address_family\": \"ipv4\",\n \"default_route\"\ + : true,\n \"device_id\": \"2066d33e-7c43-4d78-87a3-aaa434913f7f\",\n\ + \ \"id\": \"fc2d43e6-d606-47f7-9611-9d77aee443b5\"\n },\n \ + \ {\n \"address_family\": \"ipv6\",\n \"default_route\"\ + : true,\n \"device_id\": \"bfab58c0-0723-49aa-a64e-6caf1b8ea2e2\",\n\ + \ \"id\": \"277d4a7a-82dd-4e7c-bf79-8a1de6882982\"\n }\n \ + \ ]\n" + type: dict +''' + +# End of generated documentation + + +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_spec = dict( + id=SpecField( + type=FieldType.string, + description="UUID of the BGP session to look up", + ), + device_id=SpecField( + type=FieldType.string, + description="Device ID for the BGP session", + ), + address_family=SpecField( + type=FieldType.string, + description="BGP session address family, \"ipv4\" or \"ipv6\"", + editable=False, + ), + default_route=SpecField( + type=FieldType.bool, + description="Boolean flag to set the default route policy. False by default.", + editable=False, + ), +) + + +specdoc_examples = [''' +- name: Start first test bgp session + hosts: localhost + tasks: + - equinix.cloud.metal_bgp_session: + device_id: 8ea9837a-6d19-4607-b166-f7f7bb04b022 + address_family: ipv6 + default_route: true +''', ''' +- name: Delete bgp session + hosts: localhost + tasks: + - equinix.cloud.metal_bgp_session: + id: 1273edef-39af-4df0-85bb-02a847a484d1 + state: absent +''', +] + +result_sample = [''' + [ + { + "address_family": "ipv4", + "default_route": true, + "device_id": "2066d33e-7c43-4d78-87a3-aaa434913f7f", + "id": "fc2d43e6-d606-47f7-9611-9d77aee443b5" + }, + { + "address_family": "ipv6", + "default_route": true, + "device_id": "bfab58c0-0723-49aa-a64e-6caf1b8ea2e2", + "id": "277d4a7a-82dd-4e7c-bf79-8a1de6882982" + } + ] +'''] + +MUTABLE_ATTRIBUTES = [ + k for k, v in module_spec.items() if v.editable +] + +SPECDOC_META = getSpecDocMeta( + short_description='Manage BGP sessions in Equinix Metal', + description=( + 'Manage BGP sessions in Equinix Metal.' + 'Create, update or delete BGP session. To look up an existing session, pass only the *id* attribute.' + ), + examples=specdoc_examples, + options=module_spec, + return_values={ + "metal_bgp_session": SpecReturnValue( + description='The module object', + type=FieldType.dict, + sample=result_sample, + ), + }, +) + + +def main(): + module = EquinixModule( + argument_spec=SPECDOC_META.ansible_spec, + required_one_of=[("id", "address_family"), ("id", "default_route"), ("id", "device_id")], + ) + + state = module.params.get("state") + changed = False + + fetched = {} + try: + module.params_syntax_check() + if module.params.get("id"): + tolerate_not_found = state == "absent" + fetched = module.get_by_id("metal_bgp_session", tolerate_not_found) + else: + fetched = module.get_one_from_list( + "metal_bgp_session", + ["device_id"], + ) + + if fetched: + module.params['id'] = fetched['id'] + if state == "present": + diff = get_diff(module.params, fetched, MUTABLE_ATTRIBUTES) + if diff: + fetched = module.update_by_id(diff, "metal_bgp_session") + changed = True + + else: + module.delete_by_id("metal_bgp_session") + changed = True + elif state == "present": + fetched = module.create("metal_bgp_session") + if 'id' not in fetched: + module.fail_json(msg="UUID not found in resource creation response") + changed = True + + except Exception as e: + tb = traceback.format_exc() + module.fail_json(msg="Error in metal_bgp_session: {0}".format(to_native(e)), + exception=tb) + + fetched = {} if not fetched else fetched + fetched.update({'changed': changed}) + module.exit_json(**fetched) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/metal_bgp_session_info.py b/plugins/modules/metal_bgp_session_info.py new file mode 100644 index 0000000..162e9e7 --- /dev/null +++ b/plugins/modules/metal_bgp_session_info.py @@ -0,0 +1,126 @@ +#!/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 BGP sessions in Equinix Metal. You can fetch it by + device ID or project ID. +module: metal_bgp_session_info +notes: [] +options: + device_id: + description: + - Find BGP sessions by device ID. + required: false + type: str + project_id: + description: + - Find BGP sessions by project ID. + required: false + type: str +requirements: null +short_description: Gather information BGP sessions in Equinix Metal +''' +EXAMPLES = ''' +- name: Gather information about all BGP sessions in a project + hosts: localhost + tasks: + - equinix.cloud.metal_bgp_session_info: + project_id: 2a5122b9-c323-4d5c-b53c-9ad3f54273e7 +''' +RETURN = ''' +resources: + description: Found resources + returned: always + sample: + - "[\n {\n \"address_family\": \"ipv6\",\n \"default_route\": true,\n \ + \ \"device_id\": \"b068984f-f7d9-43a2-aa45-de04dcf4fe06\",\n \"id\": \"03912bd6-a158-47ad-8bc7-c93df338fe0d\"\ + \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( + device_id=SpecField( + type=FieldType.string, + description=['Find BGP sessions by device ID.'], + ), + project_id=SpecField( + type=FieldType.string, + description=['Find BGP sessions by project ID.'], + ), +) + +specdoc_examples = [''' +- name: Gather information about all BGP sessions in a project + hosts: localhost + tasks: + - equinix.cloud.metal_bgp_session_info: + project_id: 2a5122b9-c323-4d5c-b53c-9ad3f54273e7 +''', ''' +''', + ] + +result_sample = ['''[ + { + "address_family": "ipv6", + "default_route": true, + "device_id": "b068984f-f7d9-43a2-aa45-de04dcf4fe06", + "id": "03912bd6-a158-47ad-8bc7-c93df338fe0d" + } +]''', +] + +SPECDOC_META = getSpecDocMeta( + short_description="Gather information BGP sessions in Equinix Metal", + description=( + 'Gather information BGP sessions in Equinix Metal. You can fetch it by device ID or project ID.' + ), + 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() + + if 'project_id' in module.params: + return_value = {'resources': module.get_list("metal_bgp_session_by_project")} + else: + return_value = {'resources': module.get_list("metal_bgp_session")} + + 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/plugins/modules/metal_project.py b/plugins/modules/metal_project.py index 9cc4883..efe7757 100644 --- a/plugins/modules/metal_project.py +++ b/plugins/modules/metal_project.py @@ -255,7 +255,6 @@ def main(): module.params['id'] = fetched['id'] fetched = module.update_by_id({"backend_transfer_enabled": True}, "metal_project") - # TODO: add support for bgp_config once we have a module else: fetched = {} except Exception as e: diff --git a/plugins/modules/metal_project_bgp_config.py b/plugins/modules/metal_project_bgp_config.py new file mode 100644 index 0000000..0a04318 --- /dev/null +++ b/plugins/modules/metal_project_bgp_config.py @@ -0,0 +1,200 @@ +#!/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 METAL_PROJECT_ARGS are generated by +# ansible_specdoc. Do not edit them directly. + +DOCUMENTATION = ''' +author: Equinix DevRel Team (@equinix) +description: 'You can use this module to enable BGP Config for a project. To lookup + BGP Config of an existing project, call the module only with `project_id`. ' +module: metal_project_bgp_config +notes: [] +options: + asn: + description: + - Autonomous System Number for local BGP deployment + required: true + type: int + deployment_type: + description: + - '"local" or "global". Local deployment type is likely to be usable immediately, ' + - '"global" will need to be reviewed by Equinix Metal support.' + required: true + type: str + md5: + description: + - Password for BGP session in plaintext (not a checksum) + required: false + type: str + project_id: + description: + - UUID of the project where BGP Config should be enabled + required: true + type: str + use_case: + description: + - Description of your BGP use-case for Equinix Metal support + required: false + type: str +requirements: null +short_description: Manage BGP Config for Equinix Metal Project +''' +EXAMPLES = ''' +- name: Enable local BGP Config in Equinix Metal project + hosts: localhost + tasks: + - equinix.cloud.metal_project_bgp_config: + deployment_type: local + asn: 65000 + md5: null + use_case: ansible test + project_id: '{{ test_project.id }}' +''' +RETURN = ''' +metal_project_bgp_config: + description: The module object + returned: always + sample: + - "\n{\n \"changed\": true\n}\n" + type: dict +''' + +# End of generated documentation + +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_spec = dict( + project_id=SpecField( + type=FieldType.string, + description=['UUID of the project where BGP Config should be enabled'], + required=True, + editable=False, + ), + asn=SpecField( + type=FieldType.integer, + required=True, + description=['Autonomous System Number for local BGP deployment'], + editable=False, + ), + deployment_type=SpecField( + type=FieldType.string, + required=True, + description=[ + '"local" or "global". Local deployment type is likely to be usable immediately, ', + '"global" will need to be reviewed by Equinix Metal support.', + ], + editable=False, + ), + md5=SpecField( + type=FieldType.string, + description=['Password for BGP session in plaintext (not a checksum)'], + editable=False, + ), + use_case=SpecField( + type=FieldType.string, + description=['Description of your BGP use-case for Equinix Metal support'], + editable=False, + ), +) + + +specdoc_examples = [ + ''' +- name: Enable local BGP Config in Equinix Metal project + hosts: localhost + tasks: + - equinix.cloud.metal_project_bgp_config: + deployment_type: local + asn: 65000 + md5: null + use_case: "ansible test" + project_id: "{{ test_project.id }}" +''', +] + +result_sample = [''' +{ + "changed": true +} +'''] + +MUTABLE_ATTRIBUTES = [ + k for k, v in module_spec.items() if v.editable +] + +SPECDOC_META = getSpecDocMeta( + short_description='Manage BGP Config for Equinix Metal Project', + description=( + 'You can use this module to enable BGP Config for a project. To lookup BGP Config of an existing project, call the module only with `project_id`. ' + ), + examples=specdoc_examples, + options=module_spec, + return_values={ + "metal_project_bgp_config": 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() + + module.params['id'] = module.params.get("project_id") + tolerate_not_found = state == "absent" + fetched = module.get_by_id("metal_project_bgp_config", tolerate_not_found) + + if not fetched.get('id'): + # api returns {max_prefix: 10, 'md5': None} even if config not exists + # optional RESPONSE_ATTRIBUTE_MAP puts other attributes as Nones + fetched = {} + + if fetched: + if state == "present": + diff = get_diff(module.params, fetched, MUTABLE_ATTRIBUTES) + if diff: + module.fail_json(msg="BGP config cannot be updated") + + else: + module.fail_json(msg="BGP config cannot be deleted. Please delete project") + elif state == "present": + fetched = module.create("metal_project_bgp_config") + changed = True + + except Exception as e: + tb = traceback.format_exc() + module.fail_json(msg="Error in metal_project_bgp_config: {0}".format(to_native(e)), + exception=tb) + + fetched.update({'changed': changed}) + module.exit_json(**fetched) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/inventory b/tests/integration/inventory index 7c937f8..c1ff27c 100644 --- a/tests/integration/inventory +++ b/tests/integration/inventory @@ -1,2 +1,2 @@ [testgroup] -testhost ansible_connection="local" ansible_pipelining="yes" ansible_python_interpreter="/usr/bin/python3" +testhost ansible_connection="local" ansible_pipelining="yes" ansible_python_interpreter="/usr/bin/python3.8" diff --git a/tests/integration/targets/metal_bgp_session/tasks/main.yml b/tests/integration/targets/metal_bgp_session/tasks/main.yml new file mode 100644 index 0000000..ea2d012 --- /dev/null +++ b/tests/integration/targets/metal_bgp_session/tasks/main.yml @@ -0,0 +1,166 @@ +- name: metal_project + module_defaults: + equinix.cloud.metal_project: + metal_api_token: '{{ metal_api_token }}' + equinix.cloud.metal_bgp_session: + metal_api_token: '{{ metal_api_token }}' + equinix.cloud.metal_project_info: + metal_api_token: '{{ metal_api_token }}' + equinix.cloud.metal_bgp_session_info: + metal_api_token: '{{ metal_api_token }}' + equinix.cloud.metal_device: + metal_api_token: '{{ metal_api_token }}' + equinix.cloud.metal_device_info: + metal_api_token: '{{ metal_api_token }}' + equinix.cloud.metal_project_bgp_config: + metal_api_token: '{{ metal_api_token }}' + block: + - set_fact: + test_resource_name_prefix: 'ansible-integration-test-bgp-session' + - set_fact: + unique_id: "{{ lookup('password', '/dev/null chars=ascii_lowercase,digits length=8') }}" + - set_fact: + test_prefix: "{{ test_resource_name_prefix }}-{{ unique_id }}" + - set_fact: + test_os: ubuntu_22_04 + - set_fact: + test_plan: c3.small.x86 + - set_fact: + wait_seconds: 1200 + + - name: create project for test + equinix.cloud.metal_project: + name: "{{ test_prefix }}" + register: test_project + + - name: enable project bgp config + equinix.cloud.metal_project_bgp_config: + deployment_type: local + asn: 65000 + md5: null + use_case: "ansible test" + project_id: "{{ test_project.id }}" + + - name: start first test device + equinix.cloud.metal_device: + project_id: "{{ test_project.id }}" + hostname: "{{ test_prefix }}-dev1" + operating_system: "{{ test_os }}" + plan: "{{ test_plan }}" + metro: "{{ metal_test_metro }}" + state: present + provisioning_wait_seconds: "{{ wait_seconds }}" + register: test_device_1 + + - name: start first test bgp session + equinix.cloud.metal_bgp_session: + device_id: "{{ test_device_1.id }}" + address_family: ipv6 + default_route: true + register: test_bgp_session_1 + + - debug: + var: test_bgp_session_1 + + - name: start first test bgp session again (idempotence test) + equinix.cloud.metal_bgp_session: + device_id: "{{ test_device_1.id }}" + address_family: ipv6 + default_route: true + register: test_bgp_session_1d + + - name: start second test device + equinix.cloud.metal_device: + project_id: "{{ test_project.id }}" + hostname: "{{ test_prefix }}-dev2" + operating_system: "{{ test_os }}" + plan: "{{ test_plan }}" + metro: "{{ metal_test_metro }}" + state: present + provisioning_wait_seconds: "{{ wait_seconds }}" + register: test_device_2 + + + - name: start second test bgp session again + equinix.cloud.metal_bgp_session: + device_id: "{{ test_device_2.id }}" + address_family: ipv4 + default_route: true + register: test_bgp_session_2 + + - name: assert bgp sessions + assert: + that: + - "test_bgp_session_1.address_family == 'ipv6'" + - "test_bgp_session_1d.changed == false" + + - name: list bgp sessions + equinix.cloud.metal_bgp_session_info: + project_id: "{{ test_project.id }}" + register: both_bgp_sessions + + - debug: + var: both_bgp_sessions + + - name: assert both bgp sessions selected + assert: + that: + - "both_bgp_sessions.resources|length == 2" + + - name: delete first bgp session + equinix.cloud.metal_bgp_session: + id: "{{ test_bgp_session_1.id }}" + state: absent + + - name: delete first bgp session again (idempotence check) + equinix.cloud.metal_bgp_session: + id: "{{ test_bgp_session_1.id }}" + state: absent + register: test_deletion + + - assert: + that: + - "test_deletion.changed == false" + + 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 + + - name: list devices in test projects + equinix.cloud.metal_device_info: + project_id: "{{ item.id }}" + register: "test_devices" + loop: "{{ test_projects.resources }}" + + - name: list bgp sessions in test projects + equinix.cloud.metal_bgp_session_info: + project_id: "{{ item.id }}" + register: "test_bgp_sessions" + loop: "{{ test_projects.resources }}" + + - name: delete bgp sessions + equinix.cloud.metal_bgp_session: + id: "{{ item.id }}" + state: absent + ignore_errors: yes + loop: "{{ test_bgp_sessions.results | map(attribute='resources') | flatten }}" + + - name: delete test devices + equinix.cloud.metal_device: + id: "{{ item.id }}" + state: absent + ignore_errors: yes + loop: "{{ test_devices.results | map(attribute='resources') | flatten }}" + + - name: delete test projects + equinix.cloud.metal_project: + id: "{{ item.id }}" + state: absent + ignore_errors: yes + loop: "{{ test_projects.resources }}" diff --git a/tests/integration/targets/metal_project_bgp_config/tasks/main.yml b/tests/integration/targets/metal_project_bgp_config/tasks/main.yml new file mode 100644 index 0000000..5c0d034 --- /dev/null +++ b/tests/integration/targets/metal_project_bgp_config/tasks/main.yml @@ -0,0 +1,59 @@ +- name: metal_project + module_defaults: + equinix.cloud.metal_project: + metal_api_token: '{{ metal_api_token }}' + equinix.cloud.metal_project_info: + metal_api_token: '{{ metal_api_token }}' + equinix.cloud.metal_project_bgp_config: + metal_api_token: '{{ metal_api_token }}' + block: + - set_fact: + test_resource_name_prefix: 'ansible-integration-test-bgp-config' + - set_fact: + unique_id: "{{ lookup('password', '/dev/null chars=ascii_lowercase,digits length=8') }}" + - set_fact: + test_prefix: "{{ test_resource_name_prefix }}-{{ unique_id }}" + + - name: create project for test + equinix.cloud.metal_project: + name: "{{ test_prefix }}" + register: test_project + + - name: enable project bgp config + equinix.cloud.metal_project_bgp_config: + deployment_type: local + asn: 65000 + md5: null + use_case: "ansible test" + project_id: "{{ test_project.id }}" + register: test_bgp_config1 + + - name: enable project bgp config again (idempotence) + equinix.cloud.metal_project_bgp_config: + deployment_type: local + asn: 65000 + md5: null + use_case: "ansible test" + project_id: "{{ test_project.id }}" + register: test_bgp_config2 + + - assert: + that: + - "test_bgp_config2.changed == False" + + 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 + + - name: delete test projects + equinix.cloud.metal_project: + id: "{{ item.id }}" + state: absent + ignore_errors: yes + loop: "{{ test_projects.resources }}"