nxos_interface_ospf: Add bfd support (#56807)
* nxos_interface_ospf: Add bfd support Add support for `bfd` state in `nxos_interface_ospf` - Feature Pull Request `nxos_interface_ospf` * Fix pep issues * sanity loop: syntax * bfd states changed from T/F to enable/disable/default * doc hdr fixes
This commit is contained in:
parent
b9a0086aac
commit
bd844bc11c
4 changed files with 122 additions and 19 deletions
|
@ -53,6 +53,14 @@ options:
|
||||||
Valid values are a string, formatted as an IP address
|
Valid values are a string, formatted as an IP address
|
||||||
(i.e. "0.0.0.0") or as an integer.
|
(i.e. "0.0.0.0") or as an integer.
|
||||||
required: true
|
required: true
|
||||||
|
bfd:
|
||||||
|
description:
|
||||||
|
- Enables bfd at interface level. This overrides the bfd variable set at the ospf router level.
|
||||||
|
- Valid values are 'enable', 'disable' or 'default'.
|
||||||
|
- "Dependency: 'feature bfd'"
|
||||||
|
version_added: "2.9"
|
||||||
|
type: str
|
||||||
|
choices: ['enable', 'disable', 'default']
|
||||||
cost:
|
cost:
|
||||||
description:
|
description:
|
||||||
- The cost associated with this cisco_interface_ospf instance.
|
- The cost associated with this cisco_interface_ospf instance.
|
||||||
|
@ -112,12 +120,14 @@ EXAMPLES = '''
|
||||||
interface: ethernet1/32
|
interface: ethernet1/32
|
||||||
ospf: 1
|
ospf: 1
|
||||||
area: 1
|
area: 1
|
||||||
|
bfd: disable
|
||||||
cost: default
|
cost: default
|
||||||
|
|
||||||
- nxos_interface_ospf:
|
- nxos_interface_ospf:
|
||||||
interface: loopback0
|
interface: loopback0
|
||||||
ospf: prod
|
ospf: prod
|
||||||
area: 0.0.0.0
|
area: 0.0.0.0
|
||||||
|
bfd: enable
|
||||||
network: point-to-point
|
network: point-to-point
|
||||||
state: present
|
state: present
|
||||||
'''
|
'''
|
||||||
|
@ -127,7 +137,7 @@ commands:
|
||||||
description: commands sent to the device
|
description: commands sent to the device
|
||||||
returned: always
|
returned: always
|
||||||
type: list
|
type: list
|
||||||
sample: ["interface Ethernet1/32", "ip router ospf 1 area 0.0.0.1"]
|
sample: ["interface Ethernet1/32", "ip router ospf 1 area 0.0.0.1", "ip ospf bfd disable"]
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
@ -148,6 +158,7 @@ PARAM_TO_COMMAND_KEYMAP = {
|
||||||
'cost': 'ip ospf cost',
|
'cost': 'ip ospf cost',
|
||||||
'ospf': 'ip router ospf',
|
'ospf': 'ip router ospf',
|
||||||
'area': 'ip router ospf',
|
'area': 'ip router ospf',
|
||||||
|
'bfd': 'ip ospf bfd',
|
||||||
'hello_interval': 'ip ospf hello-interval',
|
'hello_interval': 'ip ospf hello-interval',
|
||||||
'dead_interval': 'ip ospf dead-interval',
|
'dead_interval': 'ip ospf dead-interval',
|
||||||
'passive_interface': 'ip ospf passive-interface',
|
'passive_interface': 'ip ospf passive-interface',
|
||||||
|
@ -198,6 +209,12 @@ def get_value(arg, config, module):
|
||||||
value = True
|
value = True
|
||||||
else:
|
else:
|
||||||
value = None
|
value = None
|
||||||
|
elif arg == 'bfd':
|
||||||
|
m = re.search(r'\s*ip ospf bfd(?P<disable> disable)?', config)
|
||||||
|
if m:
|
||||||
|
value = 'disable' if m.group('disable') else 'enable'
|
||||||
|
else:
|
||||||
|
value = 'default'
|
||||||
elif arg in BOOL_PARAMS:
|
elif arg in BOOL_PARAMS:
|
||||||
value = bool(has_command)
|
value = bool(has_command)
|
||||||
else:
|
else:
|
||||||
|
@ -254,6 +271,8 @@ def get_default_commands(existing, proposed, existing_commands, key, module):
|
||||||
encryption_type,
|
encryption_type,
|
||||||
existing['message_digest_password'])
|
existing['message_digest_password'])
|
||||||
commands.append(command)
|
commands.append(command)
|
||||||
|
elif 'ip ospf bfd' in key:
|
||||||
|
commands.append('no {0}'.format(key))
|
||||||
elif 'passive-interface' in key:
|
elif 'passive-interface' in key:
|
||||||
commands.append('default ip ospf passive-interface')
|
commands.append('default ip ospf passive-interface')
|
||||||
else:
|
else:
|
||||||
|
@ -310,6 +329,16 @@ def state_present(module, existing, proposed, candidate):
|
||||||
module.fail_json(msg='loopback interface does not support passive_interface')
|
module.fail_json(msg='loopback interface does not support passive_interface')
|
||||||
if key == 'ip ospf network' and value == 'broadcast' and module.params.get('interface').upper().startswith('LO'):
|
if key == 'ip ospf network' and value == 'broadcast' and module.params.get('interface').upper().startswith('LO'):
|
||||||
module.fail_json(msg='loopback interface does not support ospf network type broadcast')
|
module.fail_json(msg='loopback interface does not support ospf network type broadcast')
|
||||||
|
|
||||||
|
if key == 'ip ospf bfd':
|
||||||
|
cmd = key
|
||||||
|
if 'disable' in value:
|
||||||
|
cmd += ' disable'
|
||||||
|
elif 'default' in value and existing.get('bfd') is not None:
|
||||||
|
cmd = 'no ' + cmd
|
||||||
|
commands.append(cmd)
|
||||||
|
continue
|
||||||
|
|
||||||
if value is True:
|
if value is True:
|
||||||
commands.append(key)
|
commands.append(key)
|
||||||
elif value is False:
|
elif value is False:
|
||||||
|
@ -339,7 +368,12 @@ def state_absent(module, existing, proposed, candidate):
|
||||||
existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
|
existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
|
||||||
|
|
||||||
for key, value in existing_commands.items():
|
for key, value in existing_commands.items():
|
||||||
if 'ip ospf passive-interface' in key:
|
if 'ip ospf bfd' in key:
|
||||||
|
if 'default' not in value:
|
||||||
|
# cli is present when enabled or disabled; this removes either case
|
||||||
|
commands.append('no ip ospf bfd')
|
||||||
|
continue
|
||||||
|
if 'ip ospf passive-interface' in key and value is not None:
|
||||||
# cli is present for both enabled or disabled; 'no' will not remove
|
# cli is present for both enabled or disabled; 'no' will not remove
|
||||||
commands.append('default ip ospf passive-interface')
|
commands.append('default ip ospf passive-interface')
|
||||||
continue
|
continue
|
||||||
|
@ -388,6 +422,7 @@ def main():
|
||||||
interface=dict(required=True, type='str'),
|
interface=dict(required=True, type='str'),
|
||||||
ospf=dict(required=True, type='str'),
|
ospf=dict(required=True, type='str'),
|
||||||
area=dict(required=True, type='str'),
|
area=dict(required=True, type='str'),
|
||||||
|
bfd=dict(choices=['enable', 'disable', 'default'], required=False, type='str'),
|
||||||
cost=dict(required=False, type='str'),
|
cost=dict(required=False, type='str'),
|
||||||
hello_interval=dict(required=False, type='str'),
|
hello_interval=dict(required=False, type='str'),
|
||||||
dead_interval=dict(required=False, type='str'),
|
dead_interval=dict(required=False, type='str'),
|
||||||
|
@ -447,6 +482,8 @@ def main():
|
||||||
value = False
|
value = False
|
||||||
elif str(value).lower() == 'default':
|
elif str(value).lower() == 'default':
|
||||||
value = 'default'
|
value = 'default'
|
||||||
|
elif key == 'bfd':
|
||||||
|
value = str(value).lower()
|
||||||
if existing.get(key) or (not existing.get(key) and value):
|
if existing.get(key) or (not existing.get(key) and value):
|
||||||
proposed[key] = value
|
proposed[key] = value
|
||||||
elif 'passive_interface' in key and existing.get(key) is None and value is False:
|
elif 'passive_interface' in key and existing.get(key) is None and value is False:
|
||||||
|
|
|
@ -5,19 +5,20 @@
|
||||||
|
|
||||||
- set_fact: testint="{{ nxos_int1 }}"
|
- set_fact: testint="{{ nxos_int1 }}"
|
||||||
|
|
||||||
- name: "Setup - Disable feature OSPF"
|
- name: Setup - Disable features
|
||||||
nxos_feature: &disable
|
nxos_feature:
|
||||||
feature: ospf
|
feature: "{{ item }}"
|
||||||
provider: "{{ connection }}"
|
provider: "{{ connection }}"
|
||||||
state: disabled
|
state: disabled
|
||||||
|
loop: ['ospf', 'bfd']
|
||||||
ignore_errors: yes
|
ignore_errors: yes
|
||||||
|
|
||||||
- name: "Setup - Enable feature OSPF"
|
- name: Setup - Enable features
|
||||||
nxos_feature: &enable
|
nxos_feature:
|
||||||
feature: ospf
|
feature: "{{ item }}"
|
||||||
provider: "{{ connection }}"
|
provider: "{{ connection }}"
|
||||||
state: enabled
|
state: enabled
|
||||||
ignore_errors: yes
|
loop: ['ospf', 'bfd']
|
||||||
|
|
||||||
- name: "Put interface into default state"
|
- name: "Put interface into default state"
|
||||||
nxos_config: &intdefault
|
nxos_config: &intdefault
|
||||||
|
@ -51,6 +52,7 @@
|
||||||
interface: "{{ nxos_int1|upper }}"
|
interface: "{{ nxos_int1|upper }}"
|
||||||
ospf: 1
|
ospf: 1
|
||||||
area: 12345678
|
area: 12345678
|
||||||
|
bfd: enable
|
||||||
cost: 55
|
cost: 55
|
||||||
passive_interface: true
|
passive_interface: true
|
||||||
hello_interval: 15
|
hello_interval: 15
|
||||||
|
@ -99,6 +101,7 @@
|
||||||
interface: "{{ testint }}"
|
interface: "{{ testint }}"
|
||||||
ospf: 1
|
ospf: 1
|
||||||
area: 12345678
|
area: 12345678
|
||||||
|
bfd: default
|
||||||
cost: default
|
cost: default
|
||||||
hello_interval: 10
|
hello_interval: 10
|
||||||
dead_interval: default
|
dead_interval: default
|
||||||
|
@ -266,6 +269,7 @@
|
||||||
interface: "{{ testint }}"
|
interface: "{{ testint }}"
|
||||||
ospf: 1
|
ospf: 1
|
||||||
area: 12345678
|
area: 12345678
|
||||||
|
bfd: disable
|
||||||
cost: 55
|
cost: 55
|
||||||
passive_interface: true
|
passive_interface: true
|
||||||
hello_interval: 15
|
hello_interval: 15
|
||||||
|
@ -282,22 +286,21 @@
|
||||||
|
|
||||||
- assert: *false
|
- assert: *false
|
||||||
|
|
||||||
- name: "Disable feature OSPF"
|
always:
|
||||||
nxos_feature: *disable
|
- name: Disable features
|
||||||
|
nxos_feature:
|
||||||
|
feature: "{{ item }}"
|
||||||
|
provider: "{{ connection }}"
|
||||||
|
state: disabled
|
||||||
|
loop: ['ospf', 'bfd']
|
||||||
ignore_errors: yes
|
ignore_errors: yes
|
||||||
|
|
||||||
- name: "Interface cleanup"
|
- name: "Interface cleanup"
|
||||||
nxos_config: *intdefault
|
nxos_config: *intdefault
|
||||||
|
ignore_errors: yes
|
||||||
rescue:
|
|
||||||
- name: "Disable feature OSPF"
|
|
||||||
nxos_feature: *disable
|
|
||||||
|
|
||||||
- name: "Interface cleanup"
|
|
||||||
nxos_config: *intdefault
|
|
||||||
|
|
||||||
- name: "Remove port-channel and loopback ints"
|
- name: "Remove port-channel and loopback ints"
|
||||||
nxos_config: *removepcandlb
|
nxos_config: *removepcandlb
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
always:
|
|
||||||
- debug: msg="END connection={{ ansible_connection }} nxos_interface_ospf sanity test"
|
- debug: msg="END connection={{ ansible_connection }} nxos_interface_ospf sanity test"
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
interface Ethernet1/33
|
interface Ethernet1/33
|
||||||
|
interface Ethernet1/33.101
|
||||||
|
ip router ospf 1 area 0.0.0.1
|
||||||
interface Ethernet1/34
|
interface Ethernet1/34
|
||||||
ip router ospf 1 area 0.0.0.1
|
ip router ospf 1 area 0.0.0.1
|
||||||
ip ospf passive-interface
|
ip ospf passive-interface
|
||||||
interface Ethernet1/35
|
interface Ethernet1/35
|
||||||
ip router ospf 1 area 0.0.0.1
|
ip router ospf 1 area 0.0.0.1
|
||||||
no ip ospf passive-interface
|
no ip ospf passive-interface
|
||||||
|
|
||||||
|
interface Ethernet1/36
|
||||||
|
ip router ospf 1 area 0.0.0.1
|
||||||
|
ip ospf bfd
|
||||||
|
|
||||||
|
interface Ethernet1/37
|
||||||
|
ip router ospf 1 area 0.0.0.1
|
||||||
|
ip ospf bfd disable
|
||||||
|
|
|
@ -51,6 +51,59 @@ class TestNxosInterfaceOspfModule(TestNxosModule):
|
||||||
set_module_args(dict(interface='ethernet1/32', ospf=1, area=1))
|
set_module_args(dict(interface='ethernet1/32', ospf=1, area=1))
|
||||||
self.execute_module(changed=True, commands=['interface Ethernet1/32', 'ip router ospf 1 area 0.0.0.1'])
|
self.execute_module(changed=True, commands=['interface Ethernet1/32', 'ip router ospf 1 area 0.0.0.1'])
|
||||||
|
|
||||||
|
def test_bfd_1(self):
|
||||||
|
# default -> enable
|
||||||
|
set_module_args(dict(interface='ethernet1/33', ospf=1, area=1, bfd='enable'))
|
||||||
|
self.execute_module(changed=True, commands=['interface Ethernet1/33', 'ip router ospf 1 area 0.0.0.1', 'ip ospf bfd'])
|
||||||
|
|
||||||
|
# default -> disable
|
||||||
|
set_module_args(dict(interface='ethernet1/33', ospf=1, area=1, bfd='disable'))
|
||||||
|
self.execute_module(changed=True, commands=['interface Ethernet1/33', 'ip router ospf 1 area 0.0.0.1', 'ip ospf bfd disable'])
|
||||||
|
|
||||||
|
def test_bfd_2(self):
|
||||||
|
# default -> default
|
||||||
|
set_module_args(dict(interface='ethernet1/33.101', ospf=1, area=1, bfd='default'))
|
||||||
|
self.execute_module(changed=False)
|
||||||
|
|
||||||
|
# enable -> default
|
||||||
|
set_module_args(dict(interface='ethernet1/36', ospf=1, area=1, bfd='default'))
|
||||||
|
self.execute_module(changed=True, commands=['interface Ethernet1/36', 'no ip ospf bfd'])
|
||||||
|
|
||||||
|
# disable -> default
|
||||||
|
set_module_args(dict(interface='ethernet1/37', ospf=1, area=1, bfd='default'))
|
||||||
|
self.execute_module(changed=True, commands=['interface Ethernet1/37', 'no ip ospf bfd'])
|
||||||
|
|
||||||
|
def test_bfd_3(self):
|
||||||
|
# enable -> idempotence
|
||||||
|
set_module_args(dict(interface='ethernet1/36', ospf=1, area=1, bfd='enable'))
|
||||||
|
self.execute_module(changed=False)
|
||||||
|
|
||||||
|
# disable -> idempotence
|
||||||
|
set_module_args(dict(interface='ethernet1/37', ospf=1, area=1, bfd='disable'))
|
||||||
|
self.execute_module(changed=False)
|
||||||
|
|
||||||
|
def test_bfd_4(self):
|
||||||
|
# None -> absent
|
||||||
|
set_module_args(dict(interface='ethernet1/33.101', ospf=1, area=1, state='absent'))
|
||||||
|
self.execute_module(changed=True, commands=['interface Ethernet1/33.101', 'no ip router ospf 1 area 0.0.0.1'])
|
||||||
|
|
||||||
|
# enable -> absent
|
||||||
|
set_module_args(dict(interface='ethernet1/36', ospf=1, area=1, bfd='enable', state='absent'))
|
||||||
|
self.execute_module(changed=True, commands=['interface Ethernet1/36', 'no ip router ospf 1 area 0.0.0.1', 'no ip ospf bfd'])
|
||||||
|
|
||||||
|
# disable -> absent
|
||||||
|
set_module_args(dict(interface='ethernet1/37', ospf=1, area=1, bfd='disable', state='absent'))
|
||||||
|
self.execute_module(changed=True, commands=['interface Ethernet1/37', 'no ip router ospf 1 area 0.0.0.1', 'no ip ospf bfd'])
|
||||||
|
|
||||||
|
def test_absent_1(self):
|
||||||
|
# area only -> absent
|
||||||
|
set_module_args(dict(interface='ethernet1/33.101', ospf=1, area=1, state='absent'))
|
||||||
|
self.execute_module(changed=True, commands=['interface Ethernet1/33.101', 'no ip router ospf 1 area 0.0.0.1'])
|
||||||
|
|
||||||
|
# None -> absent
|
||||||
|
set_module_args(dict(interface='ethernet1/33', ospf=1, area=1, state='absent'))
|
||||||
|
self.execute_module(changed=False)
|
||||||
|
|
||||||
def test_loopback_interface_failed(self):
|
def test_loopback_interface_failed(self):
|
||||||
set_module_args(dict(interface='loopback0', ospf=1, area=0, passive_interface=True))
|
set_module_args(dict(interface='loopback0', ospf=1, area=0, passive_interface=True))
|
||||||
self.execute_module(failed=True, changed=False)
|
self.execute_module(failed=True, changed=False)
|
||||||
|
|
Loading…
Reference in a new issue