Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add Layer 2 example including interconnection and VRF #232

Merged
merged 16 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions examples/layer2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Layer 2 networking with Equinix Metal

This example demonstrates the use of the `equinix.cloud.metal_connection`, `equinix.cloud.metal_device`, and `equinix.cloud.metal_port` modules--as well as a variety of AWS modules--to configure Layer 2 connectivity from an Equinix Metal device to AWS S3 over a Metal-billed Fabric interconnection.

## Overview

The [Metal playbook](metal.yml) creates a new project, a VLAN, a VRF, a VRF Metal Gateway, and a device, converts the device to [hybrid bonded mode](https://deploy.equinix.com/developers/docs/metal/layer2-networking/overview/#network-configuration-types), and then creates a Metal-billed VRF interconnection and configures BGP peering settings on the interconnection's virtual circuit.

Manual intervention is needed in order to finish setting up the interconnection and accept the Direct Connect request in AWS.

The [AWS playbook](aws.yml) creates a new VPC, a VPC endpoint for S3, and a Virtual Private Gateway attached to the specified Direct Connect.

## Prerequisites

Before running the playbook, you will need to have the following:

- [Ansible installed on your local machine.](https://docs.ansible.com/ansible/latest/installation_guide/installation_distros.html)
- The Community.Aws, and Equinix Ansible Collections installed. You can install them using the following commands:
```bash
ansible-galaxy collection install equinix.cloud
ctreatma marked this conversation as resolved.
Show resolved Hide resolved
ansible-galaxy collection install community.aws
```
- You will also need to ensure that the necessary Python libraries are installed:
```bash
# Install Equinix Ansible collection dependencies
pip install -r https://raw.githubusercontent.com/equinix/ansible-collection-equinix/v0.11.1/requirements.txt
# Install AWS collection and Ansible IP function dependencies
pip install boto3 netaddr
```
- An [Equinix Metal API token](https://deploy.equinix.com/developers/docs/metal/identity-access-management/api-keys/). You can obtain an API token from the Equinix Metal Portal. Set the environment variable METAL_AUTH_TOKEN to your API token:
```bash
export METAL_AUTH_TOKEN=your_api_token_here
```

## Variables

You can customize some variables, such as Equinix Metal device hostname and IP ranges for Equinix Metal and AWS, from [vars/vars.yml](vars/vars.yml).

## Running the Playbooks

This example contains multiple playbooks and requires manual intervention between the playbooks.

To create the Equinix Metal infrastructure for this example, navigate to the directory containing the playbook file `metal.yml` and run the following command:

```bash
ansible-playbook metal.yml -extra-vars "bgp_md5_password=<some_value>"
```

*NOTE:* The API performs some validation on the md5 for BGP. For the latest rules refer to [the VRF virtual circuit API docs](https://deploy.equinix.com/developers/api/metal/#tag/Interconnections/operation/updateVirtualCircuit). As of this writing, the md5:
* must be 10-20 characters long
* may not include punctuation
* must be a combination of numbers and letters
* must contain at least one lowercase, uppercase, and digit character

The last task in the `metal.yml` playbook will print out the service token for your Metal connection:

```bash
TASK [print service token to redeem in Fabric portal] **************************************************************************
ok: [localhost] => {
"connection.service_tokens[0].id": "<service_token_id>"
}
```

After the Equinix Metal infrastructure is created, you will need to redeem the service token for your connection in the [Fabric portal](https://fabric.equinix.com).
cprivitere marked this conversation as resolved.
Show resolved Hide resolved
After the Equinix Metal infrastructure is created, you will need to redeem the service token for your connection in the [Equinix portal](https://portal.equinix.com). Navigate to Fabric -> Connect to Provider, choose AWS, and finally AWS Direct Connect. Choose Primary, put in your account number, choose the metro, click next, then choose "Service Token" from the drop down, and put in the service token.

ctreatma marked this conversation as resolved.
Show resolved Hide resolved
Once the service token is redeemed, you will need to accept the Direct Connect request in the [AWS console](https://console.aws.amazon.com). Take note of the Direct Connect ID and the Direct Connect VLAN when you accept the connection. You will need the ID and VLAN for the next playbook.

To finish setting up the AWS infrastructure, wait for the connection to be available, then run the following command:

```bash
ansible-playbook aws.yml --extra-vars "bgp_md5_password=<some_value>" --extra-vars "aws_connection_id=<your_direct_connect_id>" --extra-vars "aws_connection_vlan=<your_direct_connect_vlan>"
```
ctreatma marked this conversation as resolved.
Show resolved Hide resolved
56 changes: 56 additions & 0 deletions examples/layer2/aws.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
# NOTE: this playbook should be run _after_:
# 1. Running the metal.yml playbook
# 2. Redeeming the Fabric service token in the Fabric portal
# 3. Accepting the Direct Connect request in the AWS console
- name: Equinix Layer 2 example -- AWS resources
hosts: localhost
gather_facts: no
tasks:
- name: Include the required variables
include_vars: "vars/vars.yml"

- name: create a VPC
amazon.aws.ec2_vpc_net:
region: "{{ aws_region }}"
name: ansible-equinix-layer2-example
cidr_block: "{{ aws_network_cidr }}"
ctreatma marked this conversation as resolved.
Show resolved Hide resolved
register: created_vpc

- name: Gather information about any VPC route table within VPC with ID "{{ created_vpc.vpc.id }}"
amazon.aws.ec2_vpc_route_table_info:
region: "{{ aws_region }}"
filters:
ctreatma marked this conversation as resolved.
Show resolved Hide resolved
vpc-id: "{{ created_vpc.vpc.id }}"
register: route_table_info

- name: Create new vpc endpoint with the default policy
amazon.aws.ec2_vpc_endpoint:
region: "{{ aws_region }}"
vpc_id: "{{ created_vpc.vpc.id }}"
service: "com.amazonaws.{{ aws_region }}.s3"
route_table_ids: "{{ route_table_info.route_tables | map(attribute='id') }}"
register: new_vpc_endpoint

- name: Create a new VGW attached to the VPC
community.aws.ec2_vpc_vgw:
region: "{{ aws_region }}"
vpc_id: "{{ created_vpc.vpc.id }}"
name: ansible-equinix-layer2-example
register: created_vgw
ctreatma marked this conversation as resolved.
Show resolved Hide resolved

# TODO this is failing, saying that the virtual gateway
# does not exist, but I can see it...so?
- name: Create an association between VGW and connection
community.aws.directconnect_virtual_interface:
region: "{{ aws_region }}"
state: present
name: "ansible-equinix-layer2-example"
public: false
ctreatma marked this conversation as resolved.
Show resolved Hide resolved
connection_id: "{{ aws_connection_id }}"
vlan: "{{ aws_connection_vlan }}"
virtual_gateway_id: "{{ created_vgw.vgw.id }}"
customer_address: "{{ metal_peering_ip }}/30"
amazon_address: "{{ aws_peering_ip }}/30"
bgp_asn: "{{ vrf_peering_asn }}"
authentication_key: "{{ bgp_md5_password }}"
ctreatma marked this conversation as resolved.
Show resolved Hide resolved
121 changes: 121 additions & 0 deletions examples/layer2/metal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
---
- name: Equinix Metal Example Playbook
hosts: localhost
gather_facts: no
tasks:
- name: Include the required variables
include_vars: "vars/vars.yml"

# Equinix resources
- name: Create a project
equinix.cloud.metal_project:
name: "{{ project_name }}"
register: project

- name: Enable BGP for the project
equinix.cloud.metal_project_bgp_config:
project_id: "{{ project.id }}"
deployment_type: local
asn: 65000

- name: create a vlan
equinix.cloud.metal_vlan:
project_id: "{{ project.id }}"
metro: "{{ metro }}"
vxlan: "1234"
register: vlan

- name: create a VRF
equinix.cloud.metal_vrf:
name: example-vrf
metro: "{{ metro }}"
local_asn: "65000"
ip_ranges:
ctreatma marked this conversation as resolved.
Show resolved Hide resolved
- "{{ vrf_peering_ip_range }}"
- "{{ vrf_gateway_ip_range }}"
project_id: "{{ project.id }}"
register: vrf

- name: create a VRF IP reservation
equinix.cloud.metal_reserved_ip_block:
project_id: "{{ project.id }}"
vrf_id: "{{ vrf.id }}"
type: "vrf"
metro: "{{ metro }}"
network: "{{ vrf_gateway_ip_range | split('/') | first }}"
cidr: "{{ vrf_gateway_ip_range | split('/') | last }}"
register: vrf_ip_reservation

- name: create a VRF Metal Gateway
equinix.cloud.metal_gateway:
project_id: "{{ project.id }}"
ip_reservation_id: "{{ vrf_ip_reservation.id }}"
virtual_network_id: "{{ vlan.id }}"
register: gateway

# Create a device
- name: Create a device
equinix.cloud.metal_device:
project_id: "{{ project.id }}"
metro: "{{ metro }}"
hostname: "{{ device_hostname }}"
operating_system: "{{ operating_system }}"
plan: "{{ plan }}"
state: present
userdata: |2
#!/bin/bash

cat <<EOF >> /etc/network/interfaces
auto bond0.{{vlan.vxlan}}
iface bond0.{{vlan.vxlan}} inet static
address {{ vrf_gateway_ip_range | ansible.utils.nthhost(2) }}
netmask {{ vrf_ip_reservation.netmask }}
post-up route add -net {{ vrf_gateway_ip_range }} gw {{ vrf_ip_reservation.gateway }}
post-up route add -net {{ aws_network_cidr }} gw {{ vrf_ip_reservation.gateway }}
EOF

systemctl restart networking
register: device

- name: capture port ids for device
set_fact:
bond_port_id: "{{ device.network_ports | selectattr('name', 'match', 'bond0') | map(attribute='id') | first }}"
eth1_port_id: "{{ device.network_ports | selectattr('name', 'match', 'eth1') | map(attribute='id') | first }}"

- name: convert bond port to hybrid bonded mode
equinix.cloud.metal_port:
id: "{{ bond_port_id }}"
bonded: true
layer2: false
vlan_ids:
- "{{ vlan.id }}"

- name: create a Metal-billed VRF interconnection
equinix.cloud.metal_connection:
project_id: "{{ project.id }}"
metro: "{{ metro }}"
name: "{{ interconnection_name }}"
type: "shared"
speed: "50Mbps"
service_token_type: a_side
redundancy: primary
vrfs:
- "{{ vrf.id }}"
register: connection

- name: configure BGP for interconnection virtual circuit
equinix.cloud.metal_virtual_circuit:
id: "{{ connection.ports[0].virtual_circuits[0].id }}"
peer_asn: "{{ vrf_peering_asn }}"
customer_ip: "{{ aws_peering_ip }}"
metal_ip: "{{ metal_peering_ip }}"
subnet: "{{ vrf_vc_peering_ip_range }}"
md5: "{{ bgp_md5_password }}"
# The metal_virtual_circuit module requires this parameter
# in order to know that the circuit is a VRF circuit and
# not a VLAN circuit
vrf: "{{ vrf.id }}"

- name: print service token to redeem in Fabric portal
debug:
var: connection.service_tokens[0].id
18 changes: 18 additions & 0 deletions examples/layer2/vars/vars.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Equinix variables
project_name: my_metal_layer2_project
device_hostname: layer2-device
metro: sv
operating_system: ubuntu_20_04
plan: c3.small.x86
interconnection_name: "ansible-layer2-example"
vrf_gateway_ip_range: 192.168.200.0/25
vrf_peering_ip_network: 169.254.0.0
vrf_peering_ip_range: "{{ vrf_peering_ip_network }}/29"
vrf_vc_peering_ip_range: "{{ vrf_peering_ip_network }}/30"
metal_peering_ip: "{{ vrf_vc_peering_ip_range | ansible.utils.nthhost(2) }}"
aws_peering_ip: "{{ vrf_vc_peering_ip_range | ansible.utils.nthhost(1) }}"
vrf_peering_asn: 65100
fabric_connection_name: ansible-layer2-example
# AWS variables
aws_network_cidr: 172.16.0.0/16
ctreatma marked this conversation as resolved.
Show resolved Hide resolved
aws_region: us-west-1