nxos_pim: Add bfd support (#56908)
* nxos_pim: Add bfd support * Add integration sanity * minor cleanup * bfd T/F now bfd enable/disable
This commit is contained in:
parent
12d656901f
commit
30830a4482
4 changed files with 139 additions and 48 deletions
|
@ -31,6 +31,14 @@ description:
|
||||||
- Manages configuration of a Protocol Independent Multicast (PIM) instance.
|
- Manages configuration of a Protocol Independent Multicast (PIM) instance.
|
||||||
author: Gabriele Gerbino (@GGabriele)
|
author: Gabriele Gerbino (@GGabriele)
|
||||||
options:
|
options:
|
||||||
|
bfd:
|
||||||
|
description:
|
||||||
|
- Enables BFD on all PIM interfaces.
|
||||||
|
- "Dependency: 'feature bfd'"
|
||||||
|
version_added: "2.9"
|
||||||
|
type: str
|
||||||
|
choices: ['enable', 'disable']
|
||||||
|
|
||||||
ssm_range:
|
ssm_range:
|
||||||
description:
|
description:
|
||||||
- Configure group ranges for Source Specific Multicast (SSM).
|
- Configure group ranges for Source Specific Multicast (SSM).
|
||||||
|
@ -42,8 +50,9 @@ options:
|
||||||
required: true
|
required: true
|
||||||
'''
|
'''
|
||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
- name: Configure ssm_range
|
- name: Configure ssm_range, enable bfd
|
||||||
nxos_pim:
|
nxos_pim:
|
||||||
|
bfd: enable
|
||||||
ssm_range: "224.0.0.0/8"
|
ssm_range: "224.0.0.0/8"
|
||||||
|
|
||||||
- name: Set to default
|
- name: Set to default
|
||||||
|
@ -60,7 +69,9 @@ commands:
|
||||||
description: commands sent to the device
|
description: commands sent to the device
|
||||||
returned: always
|
returned: always
|
||||||
type: list
|
type: list
|
||||||
sample: ["ip pim ssm range 224.0.0.0/8"]
|
sample:
|
||||||
|
- ip pim bfd
|
||||||
|
- ip pim ssm range 224.0.0.0/8
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,7 +84,8 @@ from ansible.module_utils.network.common.config import CustomNetworkConfig
|
||||||
|
|
||||||
|
|
||||||
PARAM_TO_COMMAND_KEYMAP = {
|
PARAM_TO_COMMAND_KEYMAP = {
|
||||||
'ssm_range': 'ip pim ssm range'
|
'bfd': 'ip pim bfd',
|
||||||
|
'ssm_range': 'ip pim ssm range',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,15 +94,17 @@ def get_existing(module, args):
|
||||||
config = str(get_config(module))
|
config = str(get_config(module))
|
||||||
|
|
||||||
for arg in args:
|
for arg in args:
|
||||||
command = PARAM_TO_COMMAND_KEYMAP[arg]
|
if 'ssm_range' in arg:
|
||||||
has_command = re.search(r'^{0}\s(?P<value>.*)$'.format(command), config, re.M)
|
# <value> may be 'n.n.n.n/s', 'none', or 'default'
|
||||||
|
m = re.search(r'ssm range (?P<value>(?:[\s\d.\/]+|none|default))?$', config, re.M)
|
||||||
value = ''
|
if m:
|
||||||
if has_command:
|
# Remove rsvd SSM range
|
||||||
value = has_command.group('value')
|
value = m.group('value').replace('232.0.0.0/8', '')
|
||||||
if value == '232.0.0.0/8':
|
|
||||||
value = '' # remove the reserved value
|
|
||||||
existing[arg] = value.split()
|
existing[arg] = value.split()
|
||||||
|
|
||||||
|
elif 'bfd' in arg and 'ip pim bfd' in config:
|
||||||
|
existing[arg] = 'enable'
|
||||||
|
|
||||||
return existing
|
return existing
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,7 +112,7 @@ def apply_key_map(key_map, table):
|
||||||
new_dict = {}
|
new_dict = {}
|
||||||
for key, value in table.items():
|
for key, value in table.items():
|
||||||
new_key = key_map.get(key)
|
new_key = key_map.get(key)
|
||||||
if value:
|
if value is not None:
|
||||||
new_dict[new_key] = value
|
new_dict[new_key] = value
|
||||||
return new_dict
|
return new_dict
|
||||||
|
|
||||||
|
@ -108,12 +122,20 @@ def get_commands(module, existing, proposed, candidate):
|
||||||
proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed)
|
proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed)
|
||||||
|
|
||||||
for key, value in proposed_commands.items():
|
for key, value in proposed_commands.items():
|
||||||
if key == 'ip pim ssm range' and value == 'default':
|
command = ''
|
||||||
|
if key == 'ip pim ssm range':
|
||||||
|
if value == 'default':
|
||||||
# no cmd needs a value but the actual value does not matter
|
# no cmd needs a value but the actual value does not matter
|
||||||
command = 'no ip pim ssm range none'
|
command = 'no ip pim ssm range none'
|
||||||
commands.append(command)
|
elif value == 'none':
|
||||||
else:
|
command = 'ip pim ssm range none'
|
||||||
command = '{0} {1}'.format(key, value)
|
elif value:
|
||||||
|
command = 'ip pim ssm range {0}'.format(value)
|
||||||
|
elif key == 'ip pim bfd':
|
||||||
|
no_cmd = 'no ' if value == 'disable' else ''
|
||||||
|
command = no_cmd + key
|
||||||
|
|
||||||
|
if command:
|
||||||
commands.append(command)
|
commands.append(command)
|
||||||
|
|
||||||
if commands:
|
if commands:
|
||||||
|
@ -122,42 +144,52 @@ def get_commands(module, existing, proposed, candidate):
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
ssm_range=dict(required=True, type='list'),
|
bfd=dict(required=False, type='str', choices=['enable', 'disable']),
|
||||||
|
ssm_range=dict(required=False, type='list', default=[]),
|
||||||
)
|
)
|
||||||
|
|
||||||
argument_spec.update(nxos_argument_spec)
|
argument_spec.update(nxos_argument_spec)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
warnings = list()
|
warnings = list()
|
||||||
check_args(module, warnings)
|
check_args(module, warnings)
|
||||||
result = {'changed': False, 'commands': [], 'warnings': warnings}
|
result = {'changed': False, 'commands': [], 'warnings': warnings}
|
||||||
|
|
||||||
ssm_range_list = module.params['ssm_range']
|
params = module.params
|
||||||
for item in ssm_range_list:
|
args = [k for k in PARAM_TO_COMMAND_KEYMAP.keys() if params[k] is not None]
|
||||||
splitted_ssm_range = item.split('.')
|
|
||||||
if len(splitted_ssm_range) != 4 and item != 'none' and item != 'default':
|
# SSM syntax check
|
||||||
|
if 'ssm_range' in args:
|
||||||
|
for item in params['ssm_range']:
|
||||||
|
if re.search('none|default', item):
|
||||||
|
break
|
||||||
|
if len(item.split('.')) != 4:
|
||||||
module.fail_json(msg="Valid ssm_range values are multicast addresses "
|
module.fail_json(msg="Valid ssm_range values are multicast addresses "
|
||||||
"or the keyword 'none' or the keyword 'default'.")
|
"or the keyword 'none' or the keyword 'default'.")
|
||||||
|
|
||||||
args = PARAM_TO_COMMAND_KEYMAP.keys()
|
|
||||||
|
|
||||||
existing = get_existing(module, args)
|
existing = get_existing(module, args)
|
||||||
proposed_args = dict((k, v) for k, v in module.params.items() if k in args)
|
proposed_args = dict((k, v) for k, v in params.items() if k in args)
|
||||||
|
|
||||||
proposed = {}
|
proposed = {}
|
||||||
for key, value in proposed_args.items():
|
for key, value in proposed_args.items():
|
||||||
if key == 'ssm_range':
|
if key == 'ssm_range':
|
||||||
if value[0] == 'default':
|
if value and value[0] == 'default':
|
||||||
if existing.get(key):
|
if existing.get(key):
|
||||||
proposed[key] = 'default'
|
proposed[key] = 'default'
|
||||||
else:
|
else:
|
||||||
v = sorted(set([str(i) for i in value]))
|
v = sorted(set([str(i) for i in value]))
|
||||||
ex = sorted(set([str(i) for i in existing.get(key)]))
|
ex = sorted(set([str(i) for i in existing.get(key, [])]))
|
||||||
if v != ex:
|
if v != ex:
|
||||||
proposed[key] = ' '.join(str(s) for s in v)
|
proposed[key] = ' '.join(str(s) for s in v)
|
||||||
|
|
||||||
|
elif key == 'bfd':
|
||||||
|
if value != existing.get('bfd', 'disable'):
|
||||||
|
proposed[key] = value
|
||||||
|
|
||||||
|
elif value != existing.get(key):
|
||||||
|
proposed[key] = value
|
||||||
|
|
||||||
candidate = CustomNetworkConfig(indent=3)
|
candidate = CustomNetworkConfig(indent=3)
|
||||||
get_commands(module, existing, proposed, candidate)
|
get_commands(module, existing, proposed, candidate)
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,24 @@
|
||||||
- debug: msg="Using provider={{ connection.transport }}"
|
- debug: msg="Using provider={{ connection.transport }}"
|
||||||
when: ansible_connection == "local"
|
when: ansible_connection == "local"
|
||||||
|
|
||||||
- name: "Setup: 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
|
||||||
|
ignore_errors: yes
|
||||||
|
loop:
|
||||||
|
- pim
|
||||||
|
- bfd
|
||||||
|
|
||||||
- name: "Setup: 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
|
||||||
|
|
||||||
- name: "Setup: Configure ssm_range none"
|
- name: "Setup: Configure ssm_range none"
|
||||||
nxos_pim: &none
|
nxos_pim: &none
|
||||||
|
@ -21,8 +28,9 @@
|
||||||
provider: "{{ connection }}"
|
provider: "{{ connection }}"
|
||||||
|
|
||||||
- block:
|
- block:
|
||||||
- name: Configure ssm_range
|
- name: Initial config from none
|
||||||
nxos_pim: &configure
|
nxos_pim: &configure
|
||||||
|
bfd: enable
|
||||||
ssm_range:
|
ssm_range:
|
||||||
- "239.128.1.0/24"
|
- "239.128.1.0/24"
|
||||||
- "224.0.0.0/8"
|
- "224.0.0.0/8"
|
||||||
|
@ -33,7 +41,7 @@
|
||||||
that:
|
that:
|
||||||
- "result.changed == true"
|
- "result.changed == true"
|
||||||
|
|
||||||
- name: Check idempotence
|
- name: Initial config idempotence
|
||||||
nxos_pim: *configure
|
nxos_pim: *configure
|
||||||
register: result
|
register: result
|
||||||
|
|
||||||
|
@ -43,13 +51,14 @@
|
||||||
|
|
||||||
- name: Configure ssm_range default
|
- name: Configure ssm_range default
|
||||||
nxos_pim: &conf_default
|
nxos_pim: &conf_default
|
||||||
|
bfd: disable
|
||||||
ssm_range: "default"
|
ssm_range: "default"
|
||||||
provider: "{{ connection }}"
|
provider: "{{ connection }}"
|
||||||
register: result
|
register: result
|
||||||
|
|
||||||
- assert: *true
|
- assert: *true
|
||||||
|
|
||||||
- name: Check idempotence
|
- name: ssm_range default idempotence
|
||||||
nxos_pim: *conf_default
|
nxos_pim: *conf_default
|
||||||
register: result
|
register: result
|
||||||
|
|
||||||
|
@ -61,14 +70,22 @@
|
||||||
|
|
||||||
- assert: *true
|
- assert: *true
|
||||||
|
|
||||||
- name: Check idempotence
|
- meta: end_play
|
||||||
|
- name: ssm_range none idempotence
|
||||||
nxos_pim: *none
|
nxos_pim: *none
|
||||||
register: result
|
register: result
|
||||||
|
|
||||||
- assert: *false
|
- assert: *false
|
||||||
|
|
||||||
always:
|
always:
|
||||||
- name: "Disable feature PIM"
|
- name: "Teardown: Disable features"
|
||||||
nxos_feature: *disable_feature
|
nxos_feature:
|
||||||
|
feature: "{{ item }}"
|
||||||
|
provider: "{{ connection }}"
|
||||||
|
state: disabled
|
||||||
|
ignore_errors: yes
|
||||||
|
loop:
|
||||||
|
- pim
|
||||||
|
- bfd
|
||||||
|
|
||||||
- debug: msg="END connection={{ ansible_connection }} nxos_pim sanity test"
|
- debug: msg="END connection={{ ansible_connection }} nxos_pim sanity test"
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
ip pim bfd
|
||||||
ip pim ssm range 127.0.0.0/31
|
ip pim ssm range 127.0.0.0/31
|
||||||
|
|
|
@ -43,17 +43,58 @@ class TestNxosPimModule(TestNxosModule):
|
||||||
self.mock_load_config.stop()
|
self.mock_load_config.stop()
|
||||||
|
|
||||||
def load_fixtures(self, commands=None, device=''):
|
def load_fixtures(self, commands=None, device=''):
|
||||||
self.get_config.return_value = load_fixture('nxos_pim', 'config.cfg')
|
|
||||||
self.load_config.return_value = None
|
self.load_config.return_value = None
|
||||||
|
|
||||||
def test_nxos_pim(self):
|
def test_nxos_pim_1(self):
|
||||||
set_module_args(dict(ssm_range='232.0.0.0/8'))
|
# Add/ Modify
|
||||||
self.execute_module(changed=True, commands=['ip pim ssm range 232.0.0.0/8'])
|
self.get_config.return_value = load_fixture('nxos_pim', 'config.cfg')
|
||||||
|
set_module_args(dict(ssm_range='233.0.0.0/8'))
|
||||||
|
self.execute_module(changed=True, commands=[
|
||||||
|
'ip pim ssm range 233.0.0.0/8',
|
||||||
|
])
|
||||||
|
|
||||||
def test_nxos_pim_none(self):
|
def test_nxos_pim_2(self):
|
||||||
|
# Remove existing values
|
||||||
|
self.get_config.return_value = load_fixture('nxos_pim', 'config.cfg')
|
||||||
|
set_module_args(dict(bfd='disable', ssm_range='none'))
|
||||||
|
self.execute_module(changed=True, commands=[
|
||||||
|
'no ip pim bfd',
|
||||||
|
'ip pim ssm range none',
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_nxos_pim_3(self):
|
||||||
|
# bfd None (disable)-> enable
|
||||||
|
self.get_config.return_value = None
|
||||||
|
set_module_args(dict(bfd='enable'))
|
||||||
|
self.execute_module(changed=True, commands=['ip pim bfd'])
|
||||||
|
|
||||||
|
# bfd None (disable) -> disable
|
||||||
|
set_module_args(dict(bfd='disable'))
|
||||||
|
self.execute_module(changed=False)
|
||||||
|
|
||||||
|
# ssm None to 'default'
|
||||||
|
set_module_args(dict(ssm_range='default'))
|
||||||
|
self.execute_module(changed=False)
|
||||||
|
|
||||||
|
def test_nxos_pim_4(self):
|
||||||
|
# SSM 'none'
|
||||||
|
self.get_config.return_value = load_fixture('nxos_pim', 'config.cfg')
|
||||||
set_module_args(dict(ssm_range='none'))
|
set_module_args(dict(ssm_range='none'))
|
||||||
self.execute_module(changed=True, commands=['ip pim ssm range none'])
|
self.execute_module(changed=True, commands=['ip pim ssm range none'])
|
||||||
|
|
||||||
def test_nxos_pim_no_change(self):
|
def test_nxos_pim_5(self):
|
||||||
set_module_args(dict(ssm_range='127.0.0.0/31'))
|
# SSM 'default'
|
||||||
|
self.get_config.return_value = load_fixture('nxos_pim', 'config.cfg')
|
||||||
|
set_module_args(dict(ssm_range='default'))
|
||||||
|
self.execute_module(changed=True, commands=['no ip pim ssm range none'])
|
||||||
|
|
||||||
|
# SSM 'default' idempotence
|
||||||
|
self.get_config.return_value = None
|
||||||
|
set_module_args(dict(ssm_range='default'))
|
||||||
|
self.execute_module(changed=False)
|
||||||
|
|
||||||
|
def test_nxos_pim_6(self):
|
||||||
|
# Idempotence
|
||||||
|
self.get_config.return_value = load_fixture('nxos_pim', 'config.cfg')
|
||||||
|
set_module_args(dict(bfd='enable', ssm_range='127.0.0.0/31'))
|
||||||
self.execute_module(changed=False, commands=[])
|
self.execute_module(changed=False, commands=[])
|
||||||
|
|
Loading…
Reference in a new issue