access: fix VLAN database idempotency
Do not try and match the global VLAN list as printed by the switch. Instead, only try to realize the truth: there may be some VLANs added and some removed. We keep the compact_numlist filter and use it instead of the built-in vlan_parser when listing VLANs for tagged ports. This is because some switches compact 1,2,4,5,6 as 1-2,4-6 and others as 1,2,4-6 (see next commit). All of this should reduce the number of cases where Ansible reports a change in configuration where there was in fact no change.
This commit is contained in:
parent
bd4299732d
commit
6ade4f2f8a
5 changed files with 43 additions and 31 deletions
|
@ -14,36 +14,30 @@ class FilterModule(object):
|
|||
'iface_vlans': self.iface_vlans
|
||||
}
|
||||
|
||||
def compact_numlist(self, nums, sort=True, delimiter=',', range_delimiter='-', max_per_line=None):
|
||||
def compact_numlist(self, nums, sort=True, delimiter=',', range_delimiter='-', min_join=2):
|
||||
'''Transform [1,2,3,5,7,8,9] into "1-3,5,7-9".
|
||||
|
||||
If max_per_line is given, return a list of such strings where each string contains at most max_per_line+1 numbers.
|
||||
This emulates how VLAN ranges are displayed by FS switches so we can make ansible check mode work correctly.
|
||||
Do not create a range from fewer than min_join consecutive numbers.
|
||||
'''
|
||||
if sort:
|
||||
nums = sorted(nums)
|
||||
i = 0
|
||||
lines = []
|
||||
line = []
|
||||
nums_in_line = 0
|
||||
while i < len(nums):
|
||||
j = i + 1
|
||||
while j < len(nums) and nums[j]-nums[i] == j-i:
|
||||
j += 1
|
||||
if j > i+1:
|
||||
line += [f'{nums[i]}{range_delimiter}{nums[j-1]}']
|
||||
nums_in_line += 2
|
||||
ranges = []
|
||||
r = []
|
||||
for num in nums:
|
||||
if r and num > r[-1]+1:
|
||||
ranges += [r]
|
||||
r = []
|
||||
r += [num]
|
||||
if r:
|
||||
ranges += [r]
|
||||
|
||||
def format_range(r):
|
||||
if len(r) < min_join:
|
||||
return delimiter.join(str(n) for n in r)
|
||||
else:
|
||||
line += [f'{nums[i]}']
|
||||
nums_in_line += 1
|
||||
if max_per_line and nums_in_line >= max_per_line:
|
||||
lines += [delimiter.join(line)]
|
||||
line = []
|
||||
nums_in_line = 0
|
||||
i = j
|
||||
if line:
|
||||
lines += [delimiter.join(line)]
|
||||
return lines if max_per_line else lines[0]
|
||||
return f'{r[0]}{range_delimiter}{r[-1]}'
|
||||
|
||||
return delimiter.join(format_range(r) for r in ranges)
|
||||
|
||||
def device_address(self, device):
|
||||
'''Return loopback IP addresses for an L3 attached device'''
|
||||
|
|
|
@ -13,10 +13,6 @@
|
|||
set_fact:
|
||||
snmp_hashes: '{{ (snmp_config.stdout | from_yaml).snmpv3.hashes }}'
|
||||
|
||||
- name: Get switch facts
|
||||
cisco.ios.ios_facts:
|
||||
gather_subset: config
|
||||
|
||||
- name: Get SNMP users
|
||||
set_fact:
|
||||
snmp_current: "{{ ansible_net_config | split('\n') | select('match', '^snmp-server user '+manager.snmp_user+' public v3') }}"
|
||||
|
|
|
@ -11,6 +11,20 @@
|
|||
set_fact:
|
||||
snmp_engine_id: '{{ (serial | sha1)[:24] }}'
|
||||
|
||||
- name: Get switch facts
|
||||
cisco.ios.ios_facts:
|
||||
gather_subset: config
|
||||
|
||||
# Determine VLANs to add and remove from switch.
|
||||
- set_fact:
|
||||
actual_vlans: "{{ vlans | map(attribute='vid') }}"
|
||||
switch_vlans: "{{ ansible_net_config | split('\n') | select('match', '^ *vlan (range )?[0-9]')
|
||||
| map('regex_search', '[0-9,-]+') | join(',') | ansible.netcommon.vlan_expander }}"
|
||||
|
||||
- set_fact:
|
||||
add_vlans: "{{ actual_vlans | difference(switch_vlans) }}"
|
||||
del_vlans: "{{ switch_vlans | difference(actual_vlans) }}"
|
||||
|
||||
- name: Set configuration
|
||||
ansible.netcommon.cli_config:
|
||||
config: '{{ lookup("template", "config-"~manufacturer~"-"~device_type~".j2") }}'
|
||||
|
|
|
@ -10,7 +10,12 @@ port-channel load-balance src-dst-ip
|
|||
|
||||
ip ssh server
|
||||
|
||||
vlan {{ vlans | map(attribute='vid') | compact_numlist }}
|
||||
{% for vlan in add_vlans %}
|
||||
vlan {{ vlan }}
|
||||
{% endfor %}
|
||||
{% for vlan in del_vlans %}
|
||||
no vlan {{ vlan }}
|
||||
{% endfor %}
|
||||
|
||||
{# bond members #}
|
||||
{% for iface in interfaces | selectattr('lag') %}
|
||||
|
|
|
@ -6,8 +6,11 @@ no enable service telnet-server
|
|||
no enable service web-server http
|
||||
no enable service web-server https
|
||||
|
||||
{% for vlan_range in vlans | map(attribute='vid') | union([1]) | compact_numlist(max_per_line=19) %}
|
||||
vlan range {{ vlan_range }}
|
||||
{% for vlan in add_vlans %}
|
||||
vlan {{ vlan }}
|
||||
{% endfor %}
|
||||
{% for vlan in del_vlans | difference([1]) %} {# VLAN 1 can not be deleted #}
|
||||
no vlan {{ vlan }}
|
||||
{% endfor %}
|
||||
|
||||
{% for iface in interfaces %}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue