VLAN management and LAG assignment
The below is available as an example role in the repository.
1. Item-level declarative VLAN resources, add VLANs to their desired LAG
# Task utilizing the role and assigning config parameters, typically used in a playbook
- name: 'Create VLANs and assign to LAGs'
vars:
my_vlans: # item-by-item declarative, the list absolute per item but not in total
- name: vlan-explict-present
id: 1
lag: lag-portchannel1
state: present # explicit state
- name: vlan-implict-present
id: 2
lag: lag-portchannel1
# no state key, implict state is present by default.
- name: vlan-explict-absent
id: 3
lag: lag-portchannel1
state: absent # state is absent, vlan will be removed from LAG and VLANs
include_role:
name: f5os_vlan
loop: '{{ my_vlans }}'
loop_control:
loop_var: f5os_vlan_config
The above implementation iterates over the provided VLAN list my_vlans
. It will only work on an item-by-item (VLAN by VLAN) basis, hence it will allow additional VLANs to co-exist (eg. created/maintained by other automation systems).
As we create VLANs and plan to assign them to a LAG (or interface), we need to take the order of operation into account.
Creation
- Create VLAN
- Assign to LAG/Interface
Removal
- Remove VLAN from LAG/Interface
- Delete VLAN
When dealing with removing a VLAN in practice one needs to consider removing them from Tenants as well. This could be achieved by creating a playbook that first deals with VLAN removal from a Tenant before continuing to remove the VLAN from F5OS, similar to the below approach.
Focusing on VLAN and LAG, the below role task would allow creation and removal in order.
# roles/f5os_vlan/tasks/main.yaml
---
- name: 'Create VLAN and add to LAG'
when: (f5os_vlan_config.state is not defined or f5os_vlan_config.state == 'present')
block:
- name: 'Create VLAN'
f5_ps_ansible.f5os.f5os_restconf_config:
uri: "{{ f5os_api_prefix | default('/restconf' if ansible_httpapi_port == '8888' else '/api') }}/data/openconfig-vlan:vlans/vlan={{ f5os_vlan_config.id }}"
state: "{{ f5os_vlan_config.state | default('present') }}"
config:
openconfig-vlan:vlan:
- vlan-id: "{{ f5os_vlan_config.id }}"
config:
vlan-id: "{{ f5os_vlan_config.id }}"
name: "{{ f5os_vlan_config.name }}"
keys_ignore:
- members
- name: 'Configure trunked VLAN on LAG'
f5_ps_ansible.f5os.f5os_restconf_config:
uri: "{{ f5os_api_prefix | default('/restconf' if ansible_httpapi_port == '8888' else '/api') }}/data/openconfig-interfaces:interfaces/interface={{ f5os_vlan_config.lag }}/openconfig-if-aggregate:aggregation/openconfig-vlan:switched-vlan/config/trunk-vlans={{ f5os_vlan_config.id }}"
state: "{{ f5os_vlan_config.state | default('present') }}"
config:
openconfig-vlan:trunk-vlans: ['{{ f5os_vlan_config.id }}']
- name: 'Delete VLAN and remove from LAG'
when: f5os_vlan_config.state == 'absent'
block:
- name: 'Remove VLAN from LAG'
f5_ps_ansible.f5os.f5os_restconf_config:
uri: "{{ f5os_api_prefix | default('/restconf' if ansible_httpapi_port == '8888' else '/api') }}/data/openconfig-interfaces:interfaces/interface={{ f5os_vlan_config.lag }}/openconfig-if-aggregate:aggregation/openconfig-vlan:switched-vlan/config/trunk-vlans={{ f5os_vlan_config.id }}"
state: absent
- name: 'Delete VLAN'
f5_ps_ansible.f5os.f5os_restconf_config:
uri: "{{ f5os_api_prefix | default('/restconf' if ansible_httpapi_port == '8888' else '/api') }}/data/openconfig-vlan:vlans/vlan={{ f5os_vlan_config.id }}"
state: absent
The F5OS RESTCONF API adds a members
object to a VLAN that has been assigned to an interface/LAG that contains state
about the usage of the VLAN. For an example response of the /data/openconfig-vlan:vlans
resource illustrating the difference, see below.
/data/openconfig-vlan:vlans example response
{
"openconfig-vlan:vlans": {
"vlan": [
{
"vlan-id": 1,
"config": { "vlan-id": 1, "name": "my-assigned-vlan-1" },
---> "members": {
---> "member": [ { "state": { "interface": "lag-portchannel1" } } ]
---> }
},
{
"vlan-id": 2,
"config": { "vlan-id": 2, "name": "my-unassigned-vlan-2" }
}
]
}
}
2. Fully declarative VLAN resources, add VLANs to their desired LAG
What is our desired goal?
- Assume complete control and authority of all VLANs
- Assign VLANs to a single LAG
The below task illustrates all desired VLANs.
Note that while the VLAN resource is fully declared (the below is the complete configuration desired on the system, no additional VLANs), we still require an ansible state
setting as we want to make sure that VLANs are removed from LAGs when the are removed.
# Task utilizing the role and assigning config parameters, typically used in a playbook
- name: 'Create VLANs and assign to LAGs'
vars:
f5os_vlan_declarative_config:
- name: vlan-explict-present
id: 1
lag: lag-portchannel1
state: present # explicit state
- name: vlan-implict-present
id: 2
lag: lag-portchannel1
# no state key, implict state is present by default.
- name: vlan-explict-absent
id: 3
lag: lag-portchannel1
state: absent # Note state absent is absent!
include_role:
name: f5os_vlan_declarative
Below is the tasks file of the role.
Note the task order and filtering of desired and undesired vlans.
# roles/f5os_vlan_declarative/tasks/main.yaml
---
# remove any undesired VLANs
- name: 'Remove VLAN from LAG'
vars:
# extract vlans to remove (state=absent)
undesired_vlans: "{{ f5os_vlan_declarative_config | selectattr('state', 'defined') | selectattr('state', 'eq', 'absent') | list }}"
f5_ps_ansible.f5os.f5os_restconf_config:
uri: "{{ f5os_api_prefix | default('/restconf' if ansible_httpapi_port == '8888' else '/api') }}/data/openconfig-interfaces:interfaces/interface={{ item.lag }}/openconfig-if-aggregate:aggregation/openconfig-vlan:switched-vlan/config/trunk-vlans={{ item.id }}"
state: absent
with_items: "{{ undesired_vlans }}"
# fully declare the VLAN resource
- name: 'Create VLAN'
vars:
# extract desired vlans, either state=present or state not defined (present is the default)
desired_vlans: "{{ f5os_vlan_declarative_config | selectattr('state', 'undefined') | list + f5os_vlan_declarative_config | selectattr('state', 'defined') | selectattr('state', 'eq', 'present') | list }}"
f5_ps_ansible.f5os.f5os_restconf_config:
uri: "{{ f5os_api_prefix | default('/restconf' if ansible_httpapi_port == '8888' else '/api') }}/data/openconfig-vlan:vlans"
config: "{{ lookup('ansible.builtin.template', './templates/vlans.yaml.j2') | from_yaml }}"
keys_ignore:
- members
# add VLANs one-by-one to the desired LAG
- name: 'Add VLAN to LAG'
vars:
# extract desired vlans, either state=present or state not defined (present is the default)
desired_vlans: "{{ f5os_vlan_declarative_config | selectattr('state', 'undefined') | list + f5os_vlan_declarative_config | selectattr('state', 'defined') | selectattr('state', 'eq', 'present') | list }}"
f5_ps_ansible.f5os.f5os_restconf_config:
uri: "{{ f5os_api_prefix | default('/restconf' if ansible_httpapi_port == '8888' else '/api') }}/data/openconfig-interfaces:interfaces/interface={{ item.lag }}/openconfig-if-aggregate:aggregation/openconfig-vlan:switched-vlan/config/trunk-vlans={{ item.id }}"
config:
openconfig-vlan:trunk-vlans: ['{{ item.id }}']
with_items: "{{ desired_vlans }}"