nxos_pim_interface: Add 'bfd' support (#57133)

* nxos_pim_interface: Add 'bfd' support

* PEP fixes

* fix copyright date

* bfd states changed from T/F to enable/disable/default
This commit is contained in:
Chris Van Heuveln 2019-06-12 00:17:41 -04:00 committed by Trishna Guha
parent 7880be240f
commit 205f6b1699
3 changed files with 240 additions and 11 deletions

View file

@ -40,6 +40,14 @@ options:
- Enable/disable sparse-mode on the interface. - Enable/disable sparse-mode on the interface.
type: bool type: bool
default: no default: no
bfd:
description:
- Enables BFD for PIM at the interface level. This overrides the bfd variable set at the pim global level.
- Valid values are 'enable', 'disable' or 'default'.
- "Dependency: 'feature bfd'"
version_added: "2.9"
type: str
choices: ['enable', 'disable', 'default']
dr_prio: dr_prio:
description: description:
- Configures priority for PIM DR election on interface. - Configures priority for PIM DR election on interface.
@ -112,6 +120,11 @@ EXAMPLES = r'''
jp_type_in: routemap jp_type_in: routemap
jp_type_out: routemap jp_type_out: routemap
- name: disable bfd on the interface
nxos_pim_interface:
interface: eth1/33
bfd: disable
- name: Ensure defaults are in place - name: Ensure defaults are in place
nxos_pim_interface: nxos_pim_interface:
interface: eth1/33 interface: eth1/33
@ -123,8 +136,11 @@ commands:
description: command sent to the device description: command sent to the device
returned: always returned: always
type: list type: list
sample: ["interface eth1/33", "ip pim neighbor-policy test", sample: ["interface eth1/33",
"ip pim neighbor-policy test"] "ip pim neighbor-policy test",
"ip pim bfd-instance disable",
"ip pim neighbor-policy test"
]
''' '''
import re import re
@ -137,6 +153,7 @@ from ansible.module_utils.six import string_types
PARAM_TO_COMMAND_KEYMAP = { PARAM_TO_COMMAND_KEYMAP = {
'interface': '', 'interface': '',
'bfd': 'ip pim bfd-instance',
'sparse': 'ip pim sparse-mode', 'sparse': 'ip pim sparse-mode',
'dr_prio': 'ip pim dr-priority {0}', 'dr_prio': 'ip pim dr-priority {0}',
'hello_interval': 'ip pim hello-interval {0}', 'hello_interval': 'ip pim hello-interval {0}',
@ -151,6 +168,7 @@ PARAM_TO_COMMAND_KEYMAP = {
} }
PARAM_TO_DEFAULT_KEYMAP = { PARAM_TO_DEFAULT_KEYMAP = {
'bfd': 'default',
'dr_prio': '1', 'dr_prio': '1',
'hello_interval': '30000', 'hello_interval': '30000',
'sparse': False, 'sparse': False,
@ -158,6 +176,13 @@ PARAM_TO_DEFAULT_KEYMAP = {
'hello_auth_key': False, 'hello_auth_key': False,
} }
BFD_KEYMAP = {
None: None,
'default': 'no ip pim bfd-instance',
'disable': 'ip pim bfd-instance disable',
'enable': 'ip pim bfd-instance',
}
def execute_show_command(command, module, text=False): def execute_show_command(command, module, text=False):
if text: if text:
@ -222,6 +247,7 @@ def get_pim_interface(module, interface):
pim_interface = {} pim_interface = {}
body = get_config(module, flags=['interface {0}'.format(interface)]) body = get_config(module, flags=['interface {0}'.format(interface)])
pim_interface['bfd'] = 'default'
pim_interface['neighbor_type'] = None pim_interface['neighbor_type'] = None
pim_interface['neighbor_policy'] = None pim_interface['neighbor_policy'] = None
pim_interface['jp_policy_in'] = None pim_interface['jp_policy_in'] = None
@ -263,6 +289,11 @@ def get_pim_interface(module, interface):
pim_interface['isauth'] = True pim_interface['isauth'] = True
elif 'sparse-mode' in each: elif 'sparse-mode' in each:
pim_interface['sparse'] = True pim_interface['sparse'] = True
elif 'bfd-instance' in each:
value = 'default'
m = re.search(r'ip pim bfd-instance(?P<disable> disable)?', each)
if m:
pim_interface['bfd'] = 'disable' if m.group('disable') else 'enable'
elif 'border' in each: elif 'border' in each:
pim_interface['border'] = True pim_interface['border'] = True
elif 'hello-interval' in each: elif 'hello-interval' in each:
@ -299,9 +330,11 @@ def config_pim_interface(delta, existing, jp_bidir, isauth):
commands.append(command) commands.append(command)
for k, v in delta.items(): for k, v in delta.items():
if k in ['dr_prio', 'hello_interval', 'hello_auth_key', 'border', if k in ['bfd', 'dr_prio', 'hello_interval', 'hello_auth_key', 'border',
'sparse']: 'sparse']:
if v: if k == 'bfd':
command = BFD_KEYMAP[v]
elif v:
command = PARAM_TO_COMMAND_KEYMAP.get(k).format(v) command = PARAM_TO_COMMAND_KEYMAP.get(k).format(v)
elif k == 'hello_auth_key': elif k == 'hello_auth_key':
if isauth: if isauth:
@ -350,12 +383,17 @@ def config_pim_interface(delta, existing, jp_bidir, isauth):
commands.append(command) commands.append(command)
command = None command = None
if 'no ip pim sparse-mode' in commands:
# sparse is long-running on some platforms, process it last
commands.remove('no ip pim sparse-mode')
commands.append('no ip pim sparse-mode')
return commands return commands
def get_pim_interface_defaults(): def get_pim_interface_defaults():
args = dict(dr_prio=PARAM_TO_DEFAULT_KEYMAP.get('dr_prio'), args = dict(dr_prio=PARAM_TO_DEFAULT_KEYMAP.get('dr_prio'),
bfd=PARAM_TO_DEFAULT_KEYMAP.get('bfd'),
border=PARAM_TO_DEFAULT_KEYMAP.get('border'), border=PARAM_TO_DEFAULT_KEYMAP.get('border'),
sparse=PARAM_TO_DEFAULT_KEYMAP.get('sparse'), sparse=PARAM_TO_DEFAULT_KEYMAP.get('sparse'),
hello_interval=PARAM_TO_DEFAULT_KEYMAP.get('hello_interval'), hello_interval=PARAM_TO_DEFAULT_KEYMAP.get('hello_interval'),
@ -430,6 +468,15 @@ def config_pim_interface_defaults(existing, jp_bidir, isauth):
return command return command
def normalize_proposed_values(proposed):
keys = proposed.keys()
if 'bfd' in keys:
# bfd is a tri-state string: enable, disable, default
proposed['bfd'] = proposed['bfd'].lower()
if 'hello_interval' in keys:
proposed['hello_interval'] = str(proposed['hello_interval'] * 1000)
def main(): def main():
argument_spec = dict( argument_spec = dict(
interface=dict(type='str', required=True), interface=dict(type='str', required=True),
@ -441,6 +488,7 @@ def main():
jp_policy_in=dict(type='str'), jp_policy_in=dict(type='str'),
jp_type_out=dict(type='str', choices=['prefix', 'routemap']), jp_type_out=dict(type='str', choices=['prefix', 'routemap']),
jp_type_in=dict(type='str', choices=['prefix', 'routemap']), jp_type_in=dict(type='str', choices=['prefix', 'routemap']),
bfd=dict(type='str', choices=['enable', 'disable', 'default']),
border=dict(type='bool', default=False), border=dict(type='bool', default=False),
neighbor_policy=dict(type='str'), neighbor_policy=dict(type='str'),
neighbor_type=dict(type='str', choices=['prefix', 'routemap']), neighbor_type=dict(type='str', choices=['prefix', 'routemap']),
@ -484,9 +532,7 @@ def main():
args = PARAM_TO_COMMAND_KEYMAP.keys() args = PARAM_TO_COMMAND_KEYMAP.keys()
proposed = dict((k, v) for k, v in module.params.items() proposed = dict((k, v) for k, v in module.params.items()
if v is not None and k in args) if v is not None and k in args)
normalize_proposed_values(proposed)
if hello_interval:
proposed['hello_interval'] = str(proposed['hello_interval'] * 1000)
delta = dict(set(proposed.items()).difference(existing.items())) delta = dict(set(proposed.items()).difference(existing.items()))

View file

@ -3,17 +3,20 @@
- debug: msg="Using provider={{ connection.transport }}" - debug: msg="Using provider={{ connection.transport }}"
when: ansible_connection == "local" when: ansible_connection == "local"
- name: "Disable feature PIM" - name: "Setup: Disable features"
nxos_feature: &disable_feature nxos_feature: &disable_feature
feature: pim feature: "{{ item }}"
provider: "{{ connection }}" provider: "{{ connection }}"
state: disabled state: disabled
loop: ['pim', 'bfd']
ignore_errors: yes
- name: "Enable feature PIM" - name: "Setup: Enable features"
nxos_feature: nxos_feature:
feature: pim feature: "{{ item }}"
provider: "{{ connection }}" provider: "{{ connection }}"
state: enabled state: enabled
loop: ['pim', 'bfd']
- set_fact: testint="{{ nxos_int1 }}" - set_fact: testint="{{ nxos_int1 }}"
@ -115,6 +118,7 @@
hello_interval: 40 hello_interval: 40
sparse: True sparse: True
border: True border: True
bfd: enable
provider: "{{ connection }}" provider: "{{ connection }}"
state: present state: present
register: result register: result
@ -132,6 +136,7 @@
interface: "{{ testint }}" interface: "{{ testint }}"
sparse: False sparse: False
border: False border: False
bfd: disable
provider: "{{ connection }}" provider: "{{ connection }}"
state: present state: present
register: result register: result
@ -193,5 +198,6 @@
always: always:
- name: "Disable feature PIM" - name: "Disable feature PIM"
nxos_feature: *disable_feature nxos_feature: *disable_feature
loop: ['pim', 'bfd']
- debug: msg="END connection={{ ansible_connection }} nxos_pim_interface sanity test" - debug: msg="END connection={{ ansible_connection }} nxos_pim_interface sanity test"

View file

@ -0,0 +1,177 @@
# (c) 2019 Red Hat Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from units.compat.mock import patch
from ansible.modules.network.nxos import nxos_pim_interface
from .nxos_module import TestNxosModule, load_fixture, set_module_args
class TestNxosPimInterfaceBfdModule(TestNxosModule):
module = nxos_pim_interface
def setUp(self):
super(TestNxosPimInterfaceBfdModule, self).setUp()
self.mock_get_interface_mode = patch('ansible.modules.network.nxos.nxos_pim_interface.get_interface_mode')
self.get_interface_mode = self.mock_get_interface_mode.start()
self.mock_get_config = patch('ansible.modules.network.nxos.nxos_pim_interface.get_config')
self.get_config = self.mock_get_config.start()
self.mock_load_config = patch('ansible.modules.network.nxos.nxos_pim_interface.load_config')
self.load_config = self.mock_load_config.start()
self.mock_run_commands = patch('ansible.modules.network.nxos.nxos_pim_interface.run_commands')
self.run_commands = self.mock_run_commands.start()
def tearDown(self):
super(TestNxosPimInterfaceBfdModule, self).tearDown()
self.mock_get_interface_mode.stop()
self.mock_get_config.stop()
self.mock_load_config.stop()
self.mock_run_commands.stop()
def load_fixtures(self, commands=None, device=''):
self.load_config.return_value = None
def test_bfd_1(self):
# default (None) -> enable
self.get_config.return_value = None
set_module_args(dict(interface='eth2/1', bfd='enable'))
self.execute_module(
changed=True,
commands=[
'interface eth2/1',
'ip pim bfd-instance',
])
# default (None) -> disable
set_module_args(dict(interface='eth2/1', bfd='disable'))
self.execute_module(
changed=True,
commands=[
'interface eth2/1',
'ip pim bfd-instance disable',
])
# default (None) -> default (None) (idempotence)
set_module_args(dict(interface='eth2/1', bfd='default'))
self.execute_module(changed=False,)
# default (None) -> interface state 'default'
set_module_args(dict(interface='Ethernet9/3', state='default'))
self.execute_module(changed=False,)
# default (None) -> interface state 'absent'
set_module_args(dict(interface='Ethernet9/3', state='absent'))
self.execute_module(changed=False,)
def test_bfd_2(self):
# From disable
self.get_config.return_value = '''
interface Ethernet9/2
ip pim bfd-instance disable
'''
# disable -> enable
set_module_args(dict(interface='Ethernet9/2', bfd='enable'))
self.execute_module(
changed=True,
commands=[
'interface Ethernet9/2',
'ip pim bfd-instance',
])
# disable -> disable (idempotence)
set_module_args(dict(interface='Ethernet9/2', bfd='disable'))
self.execute_module(changed=False,)
# disable -> default (None)
set_module_args(dict(interface='Ethernet9/2', bfd='default'))
self.execute_module(
changed=True,
commands=[
'interface Ethernet9/2',
'no ip pim bfd-instance',
])
# disable -> interface state 'default'
set_module_args(dict(interface='Ethernet9/3', state='default'))
self.execute_module(
changed=True,
commands=[
'interface Ethernet9/3',
'no ip pim bfd-instance',
])
# disable -> interface state 'absent'
set_module_args(dict(interface='Ethernet9/3', state='absent'))
self.execute_module(
changed=True,
commands=[
'interface Ethernet9/3',
'no ip pim bfd-instance',
])
def test_bfd_3(self):
# From enable
self.get_config.return_value = '''
interface Ethernet9/2
ip pim bfd-instance
'''
# enable -> disabled
set_module_args(dict(interface='Ethernet9/3', bfd='disable'))
self.execute_module(
changed=True,
commands=[
'interface Ethernet9/3',
'ip pim bfd-instance disable',
])
# enable -> enable (idempotence)
set_module_args(dict(interface='Ethernet9/3', bfd='enable'))
self.execute_module(changed=False,)
# enable -> default (None)
set_module_args(dict(interface='Ethernet9/3', bfd='default'))
self.execute_module(
changed=True,
commands=[
'interface Ethernet9/3',
'no ip pim bfd-instance',
])
# enable -> interface state 'default'
set_module_args(dict(interface='Ethernet9/3', state='default'))
self.execute_module(
changed=True,
commands=[
'interface Ethernet9/3',
'no ip pim bfd-instance',
])
# enable -> interface state 'absent'
set_module_args(dict(interface='Ethernet9/3', state='absent'))
self.execute_module(
changed=True,
commands=[
'interface Ethernet9/3',
'no ip pim bfd-instance',
])