Compare commits
4 commits
48eee50203
...
343fd0daad
Author | SHA1 | Date | |
---|---|---|---|
343fd0daad | |||
9ec6241e4a | |||
6ade4f2f8a | |||
bd4299732d |
8 changed files with 125 additions and 56 deletions
|
@ -14,36 +14,30 @@ class FilterModule(object):
|
||||||
'iface_vlans': self.iface_vlans
|
'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".
|
'''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.
|
Do not create a range from fewer than min_join consecutive numbers.
|
||||||
This emulates how VLAN ranges are displayed by FS switches so we can make ansible check mode work correctly.
|
|
||||||
'''
|
'''
|
||||||
if sort:
|
if sort:
|
||||||
nums = sorted(nums)
|
nums = sorted(nums)
|
||||||
i = 0
|
ranges = []
|
||||||
lines = []
|
r = []
|
||||||
line = []
|
for num in nums:
|
||||||
nums_in_line = 0
|
if r and num > r[-1]+1:
|
||||||
while i < len(nums):
|
ranges += [r]
|
||||||
j = i + 1
|
r = []
|
||||||
while j < len(nums) and nums[j]-nums[i] == j-i:
|
r += [num]
|
||||||
j += 1
|
if r:
|
||||||
if j > i+1:
|
ranges += [r]
|
||||||
line += [f'{nums[i]}{range_delimiter}{nums[j-1]}']
|
|
||||||
nums_in_line += 2
|
def format_range(r):
|
||||||
|
if len(r) < min_join:
|
||||||
|
return delimiter.join(str(n) for n in r)
|
||||||
else:
|
else:
|
||||||
line += [f'{nums[i]}']
|
return f'{r[0]}{range_delimiter}{r[-1]}'
|
||||||
nums_in_line += 1
|
|
||||||
if max_per_line and nums_in_line >= max_per_line:
|
return delimiter.join(format_range(r) for r in ranges)
|
||||||
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]
|
|
||||||
|
|
||||||
def device_address(self, device):
|
def device_address(self, device):
|
||||||
'''Return loopback IP addresses for an L3 attached device'''
|
'''Return loopback IP addresses for an L3 attached device'''
|
||||||
|
|
|
@ -13,10 +13,6 @@
|
||||||
set_fact:
|
set_fact:
|
||||||
snmp_hashes: '{{ (snmp_config.stdout | from_yaml).snmpv3.hashes }}'
|
snmp_hashes: '{{ (snmp_config.stdout | from_yaml).snmpv3.hashes }}'
|
||||||
|
|
||||||
- name: Get switch facts
|
|
||||||
cisco.ios.ios_facts:
|
|
||||||
gather_subset: config
|
|
||||||
|
|
||||||
- name: Get SNMP users
|
- name: Get SNMP users
|
||||||
set_fact:
|
set_fact:
|
||||||
snmp_current: "{{ ansible_net_config | split('\n') | select('match', '^snmp-server user '+manager.snmp_user+' public v3') }}"
|
snmp_current: "{{ ansible_net_config | split('\n') | select('match', '^snmp-server user '+manager.snmp_user+' public v3') }}"
|
||||||
|
|
1
roles/access/tasks/fs-s5800-48t4s.yml
Symbolic link
1
roles/access/tasks/fs-s5800-48t4s.yml
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
fs.yml
|
|
@ -11,6 +11,21 @@
|
||||||
set_fact:
|
set_fact:
|
||||||
snmp_engine_id: '{{ (serial | sha1)[:24] }}'
|
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(',')
|
||||||
|
| default('0', true) | ansible.netcommon.vlan_expander | reject('eq', 0) }}" # vlan_expander barfs on empty string so add/remove a fake VLAN 0
|
||||||
|
|
||||||
|
- set_fact:
|
||||||
|
add_vlans: "{{ actual_vlans | difference(switch_vlans) }}"
|
||||||
|
del_vlans: "{{ switch_vlans | difference(actual_vlans) }}"
|
||||||
|
|
||||||
- name: Set configuration
|
- name: Set configuration
|
||||||
ansible.netcommon.cli_config:
|
ansible.netcommon.cli_config:
|
||||||
config: '{{ lookup("template", "config-"~manufacturer~"-"~device_type~".j2") }}'
|
config: '{{ lookup("template", "config-"~manufacturer~"-"~device_type~".j2") }}'
|
||||||
|
@ -19,7 +34,7 @@
|
||||||
ansible_terminal_stderr_re: [] # some errors are not actually errors
|
ansible_terminal_stderr_re: [] # some errors are not actually errors
|
||||||
register: result
|
register: result
|
||||||
# These lines are not displayed by 'sho ru' and always reported as different, so ignore them.
|
# These lines are not displayed by 'sho ru' and always reported as different, so ignore them.
|
||||||
changed_when: result.commands | reject('match', '^(no shutdown|no switchport access vlan|no switchport trunk native vlan|no voice vlan.*|switchport mode access|switchport mode hybrid|interface .*|no enable service web-server https?|no ip dhcp snooping|no ip dhcp snooping trust|no switchport port-security.*)$')
|
changed_when: result.commands | reject('match', '^(no shutdown|no switchport access vlan|no switchport trunk native vlan|no voice vlan.*|switchport mode access|switchport mode hybrid|interface .*|service http disable|no enable service web-server https?|no ip dhcp snooping|no ip dhcp snooping trust|no switchport port-security.*)$')
|
||||||
notify: write config
|
notify: write config
|
||||||
|
|
||||||
- name: Run model-specific tasks
|
- name: Run model-specific tasks
|
||||||
|
|
|
@ -10,7 +10,12 @@ port-channel load-balance src-dst-ip
|
||||||
|
|
||||||
ip ssh server
|
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 #}
|
{# bond members #}
|
||||||
{% for iface in interfaces | selectattr('lag') %}
|
{% for iface in interfaces | selectattr('lag') %}
|
||||||
|
|
|
@ -1,40 +1,41 @@
|
||||||
hostname {{ inventory_hostname }}
|
hostname {{ inventory_hostname }}
|
||||||
|
|
||||||
no netconf enable
|
service http disable
|
||||||
|
service telnet disable
|
||||||
|
|
||||||
no enable service telnet-server
|
vlan database
|
||||||
no enable service web-server http
|
{% for vlan in add_vlans %}
|
||||||
no enable service web-server https
|
vlan {{ vlan }}
|
||||||
|
|
||||||
{% for vlan_range in vlans | map(attribute='vid') | union([1]) | compact_numlist(max_per_line=19) %}
|
|
||||||
vlan range {{ vlan_range }}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% for vlan in del_vlans %}
|
||||||
|
no vlan {{ vlan }}
|
||||||
|
{% endfor %}
|
||||||
|
exit
|
||||||
|
|
||||||
{% for iface in interfaces %}
|
{# sort to ensure LAG interfaces are added last #}
|
||||||
interface {{ iface.name }}
|
{% for iface in interfaces | sort(attribute="type.value") | sort(attribute="mgmt_only") %}
|
||||||
{% if iface.enabled %} no{% endif %} shutdown
|
{% if iface.mgmt_only %}
|
||||||
{% if iface.lag %}
|
|
||||||
port-group {{ iface.lag.name | select('in', '0123456789') | join('') }} mode active
|
|
||||||
|
|
||||||
{% elif iface.mgmt_only %}
|
|
||||||
{% for address in iface.ip_addresses %}
|
{% for address in iface.ip_addresses %}
|
||||||
{% set subnet = address.address | ipaddr('subnet') %}
|
{% set subnet = address.address | ipaddr('subnet') %}
|
||||||
{% set prefix = prefixes | selectattr('prefix', '==', subnet) | first %}
|
{% set prefix = prefixes | selectattr('prefix', '==', subnet) | first %}
|
||||||
{% if address.family.value == 4 %}
|
{% if address.family.value == 4 %}
|
||||||
ip address {{ address.address | ipaddr('address') }} {{ address.address | ipaddr('netmask') }}
|
management ip address {{ address.address }}
|
||||||
{% if prefix.custom_fields.gateway %}
|
{% if prefix.custom_fields.gateway %}
|
||||||
gateway {{ prefix.custom_fields.gateway.address | ipaddr('address') }}
|
management route add gateway {{ prefix.custom_fields.gateway.address | ipaddr('address') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
ipv6 address {{ address.address | upper }}
|
management ipv6 address {{ address.address }}
|
||||||
{% if prefix.custom_fields.gateway %}
|
|
||||||
ipv6 gateway {{ prefix.custom_fields.gateway.address | ipaddr('address') | upper }}
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
mtu {{ iface.mtu | default('9216', true) }}
|
|
||||||
|
interface {{ iface.name }}
|
||||||
|
{% if iface.enabled %} no{% endif %} shutdown
|
||||||
|
{% if iface.lag %}
|
||||||
|
channel-group {{ iface.lag.name | select('in', '0123456789') | join('') }} mode active
|
||||||
|
|
||||||
|
{% else %}
|
||||||
{% if iface.mode and iface.mode.value == 'access' %}
|
{% if iface.mode and iface.mode.value == 'access' %}
|
||||||
switchport mode access
|
switchport mode access
|
||||||
{% if iface.untagged_vlan and iface.untagged_vlan.vid != 1 %}
|
{% if iface.untagged_vlan and iface.untagged_vlan.vid != 1 %}
|
||||||
|
@ -46,9 +47,10 @@ interface {{ iface.name }}
|
||||||
switchport mode trunk
|
switchport mode trunk
|
||||||
switchport trunk allowed vlan only {{ (iface.tagged_vlans or vlans) | map(attribute='vid') | compact_numlist }}
|
switchport trunk allowed vlan only {{ (iface.tagged_vlans or vlans) | map(attribute='vid') | compact_numlist }}
|
||||||
{%- elif iface.mode and iface.mode.value == 'tagged-all' %}
|
{%- elif iface.mode and iface.mode.value == 'tagged-all' %}
|
||||||
switchport mode uplink
|
switchport mode trunk
|
||||||
switchport trunk allowed vlan only 2-4094
|
switchport trunk allowed vlan all
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
|
@ -1 +0,0 @@
|
||||||
config-fs.j2
|
|
57
roles/access/templates/config-fs-s5860-48xmg-u.j2
Normal file
57
roles/access/templates/config-fs-s5860-48xmg-u.j2
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
hostname {{ inventory_hostname }}
|
||||||
|
|
||||||
|
no netconf enable
|
||||||
|
|
||||||
|
no enable service telnet-server
|
||||||
|
no enable service web-server http
|
||||||
|
no enable service web-server https
|
||||||
|
|
||||||
|
{% 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 %}
|
||||||
|
interface {{ iface.name }}
|
||||||
|
{% if iface.enabled %} no{% endif %} shutdown
|
||||||
|
{% if iface.lag %}
|
||||||
|
port-group {{ iface.lag.name | select('in', '0123456789') | join('') }} mode active
|
||||||
|
|
||||||
|
{% elif iface.mgmt_only %}
|
||||||
|
{% for address in iface.ip_addresses %}
|
||||||
|
{% set subnet = address.address | ipaddr('subnet') %}
|
||||||
|
{% set prefix = prefixes | selectattr('prefix', '==', subnet) | first %}
|
||||||
|
{% if address.family.value == 4 %}
|
||||||
|
ip address {{ address.address | ipaddr('address') }} {{ address.address | ipaddr('netmask') }}
|
||||||
|
{% if prefix.custom_fields.gateway %}
|
||||||
|
gateway {{ prefix.custom_fields.gateway.address | ipaddr('address') }}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
ipv6 address {{ address.address | upper }}
|
||||||
|
{% if prefix.custom_fields.gateway %}
|
||||||
|
ipv6 gateway {{ prefix.custom_fields.gateway.address | ipaddr('address') | upper }}
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
mtu {{ iface.mtu | default('9216', true) }}
|
||||||
|
{% if iface.mode and iface.mode.value == 'access' %}
|
||||||
|
switchport mode access
|
||||||
|
{% if iface.untagged_vlan and iface.untagged_vlan.vid != 1 %}
|
||||||
|
switchport access vlan {{ iface.untagged_vlan.vid }}
|
||||||
|
{% else %}
|
||||||
|
no switchport access vlan
|
||||||
|
{% endif %}
|
||||||
|
{%- elif iface.mode and iface.mode.value == 'tagged' %}
|
||||||
|
switchport mode trunk
|
||||||
|
switchport trunk allowed vlan only {{ (iface.tagged_vlans or vlans) | map(attribute='vid') | compact_numlist }}
|
||||||
|
{%- elif iface.mode and iface.mode.value == 'tagged-all' %}
|
||||||
|
switchport mode uplink
|
||||||
|
switchport trunk allowed vlan only 2-4094
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
|
@ -55,8 +55,8 @@ router bgp {{ asn.asn }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
address-family l2vpn evpn
|
address-family l2vpn evpn
|
||||||
neighbor fabric activate
|
neighbor fabric activate
|
||||||
{% for iface in ifaces_evpn|default([]) %}
|
{% for iface in interfaces | selectattr('enabled') | selectattr('name', 'in', ifaces_evpn|default([])) %}
|
||||||
neighbor {{ iface }} activate
|
neighbor {{ iface.name }} activate
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if peer is defined and interfaces | selectattr('mode') %}
|
{% if peer is defined and interfaces | selectattr('mode') %}
|
||||||
advertise-all-vni
|
advertise-all-vni
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue