community.general/lib/ansible/modules/network/nxos/nxos_bgp_af.py
Peter Sprygada 21d993a4b8 refactors nxos module to use persistent connections (#21470)
This completes the refactor of the nxos modules to use the persistent
connection.  It also updates all of the nxos modules to use the
new connection module and preserves use of nxapi as well.
2017-02-15 11:43:09 -05:00

971 lines
35 KiB
Python

#!/usr/bin/python
#
# 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/>.
#
ANSIBLE_METADATA = {'status': ['preview'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: nxos_bgp_af
version_added: "2.2"
short_description: Manages BGP Address-family configuration.
description:
- Manages BGP Address-family configurations on NX-OS switches.
author: Gabriele Gerbino (@GGabriele)
notes:
- C(state=absent) removes the whole BGP ASN configuration
- Default, where supported, restores params default value.
options:
asn:
description:
- BGP autonomous system number. Valid values are String,
Integer in ASPLAIN or ASDOT notation.
required: true
vrf:
description:
- Name of the VRF. The name 'default' is a valid VRF representing
the global bgp.
required: true
afi:
description:
- Address Family Identifier.
required: true
choices: ['ipv4','ipv6', 'vpnv4', 'vpnv6', 'l2vpn']
safi:
description:
- Sub Address Family Identifier.
required: true
choices: ['unicast','multicast', 'evpn']
additional_paths_install:
description:
- Install a backup path into the forwarding table and provide
prefix independent convergence (PIC) in case of a PE-CE link
failure.
required: false
choices: ['true','false']
default: null
additional_paths_receive:
description:
- Enables the receive capability of additional paths for all of
the neighbors under this address family for which the capability
has not been disabled.
required: false
choices: ['true','false']
default: null
additional_paths_selection:
description:
- Configures the capability of selecting additional paths for
a prefix. Valid values are a string defining the name of
the route-map.
required: false
default: null
additional_paths_send:
description:
- Enables the send capability of additional paths for all of
the neighbors under this address family for which the capability
has not been disabled.
required: false
choices: ['true','false']
default: null
advertise_l2vpn_evpn:
description:
- Advertise evpn routes.
required: false
choices: ['true','false']
default: null
client_to_client:
description:
- Configure client-to-client route reflection.
required: false
choices: ['true','false']
default: null
dampen_igp_metric:
description:
- Specify dampen value for IGP metric-related changes, in seconds.
Valid values are integer and keyword 'default'.
required: false
default: null
dampening_state:
description:
- Enable/disable route-flap dampening.
required: false
choices: ['true','false']
default: null
dampening_half_time:
description:
- Specify decay half-life in minutes for route-flap dampening.
Valid values are integer and keyword 'default'.
required: false
default: null
dampening_max_suppress_time:
description:
- Specify max suppress time for route-flap dampening stable route.
Valid values are integer and keyword 'default'.
required: false
default: null
dampening_reuse_time:
description:
- Specify route reuse time for route-flap dampening.
Valid values are integer and keyword 'default'.
required: false
dampening_routemap:
description:
- Specify route-map for route-flap dampening. Valid values are a
string defining the name of the route-map.
required: false
default: null
dampening_suppress_time:
description:
- Specify route suppress time for route-flap dampening.
Valid values are integer and keyword 'default'.
required: false
default: null
default_information_originate:
description:
- Default information originate.
required: false
choices: ['true','false']
default: null
default_metric:
description:
- Sets default metrics for routes redistributed into BGP.
Valid values are Integer or keyword 'default'
required: false
default: null
distance_ebgp:
description:
- Sets the administrative distance for eBGP routes.
Valid values are Integer or keyword 'default'.
required: false
default: null
distance_ibgp:
description:
- Sets the administrative distance for iBGP routes.
Valid values are Integer or keyword 'default'.
required: false
default: null
distance_local:
description:
- Sets the administrative distance for local BGP routes.
Valid values are Integer or keyword 'default'.
required: false
default: null
inject_map:
description:
- An array of route-map names which will specify prefixes to
inject. Each array entry must first specify the inject-map name,
secondly an exist-map name, and optionally the copy-attributes
keyword which indicates that attributes should be copied from
the aggregate. For example [['lax_inject_map', 'lax_exist_map'],
['nyc_inject_map', 'nyc_exist_map', 'copy-attributes'],
['fsd_inject_map', 'fsd_exist_map']].
required: false
default: null
maximum_paths:
description:
- Configures the maximum number of equal-cost paths for
load sharing. Valid value is an integer in the range 1-64.
default: null
maximum_paths_ibgp:
description:
- Configures the maximum number of ibgp equal-cost paths for
load sharing. Valid value is an integer in the range 1-64.
required: false
default: null
networks:
description:
- Networks to configure. Valid value is a list of network
prefixes to advertise. The list must be in the form of an array.
Each entry in the array must include a prefix address and an
optional route-map. For example [['10.0.0.0/16', 'routemap_LA'],
['192.168.1.1', 'Chicago'], ['192.168.2.0/24],
['192.168.3.0/24', 'routemap_NYC']].
required: false
default: null
next_hop_route_map:
description:
- Configure a route-map for valid nexthops. Valid values are a
string defining the name of the route-map.
required: false
default: null
redistribute:
description:
- A list of redistribute directives. Multiple redistribute entries
are allowed. The list must be in the form of a nested array.
the first entry of each array defines the source-protocol to
redistribute from; the second entry defines a route-map name.
A route-map is highly advised but may be optional on some
platforms, in which case it may be omitted from the array list.
For example [['direct', 'rm_direct'], ['lisp', 'rm_lisp']].
required: false
default: null
suppress_inactive:
description:
- Advertises only active routes to peers.
required: false
choices: ['true','false']
default: null
table_map:
description:
- Apply table-map to filter routes downloaded into URIB.
Valid values are a string.
required: false
default: null
table_map_filter:
description:
- Filters routes rejected by the route-map and does not download
them to the RIB.
required: false
choices: ['true','false']
default: null
state:
description:
- Determines whether the config should be present or not
on the device.
required: false
default: present
choices: ['present','absent']
'''
EXAMPLES = '''
# configure a simple address-family
- nxos_bgp_af:
asn: 65535
vrf: TESTING
afi: ipv4
safi: unicast
advertise_l2vpn_evpn: true
state: present
'''
RETURN = '''
proposed:
description: k/v pairs of parameters passed into module
returned: verbose mode
type: dict
sample: {"advertise_l2vpn_evpn": true, "afi": "ipv4",
"asn": "65535", "safi": "unicast", "vrf": "TESTING"}
existing:
description: k/v pairs of existing BGP AF configuration
returned: verbose mode
type: dict
sample: {}
end_state:
description: k/v pairs of BGP AF configuration after module execution
returned: verbose mode
type: dict
sample: {"additional_paths_install": false,
"additional_paths_receive": false,
"additional_paths_selection": "",
"additional_paths_send": false,
"advertise_l2vpn_evpn": true, "afi": "ipv4",
"asn": "65535", "client_to_client": true,
"dampen_igp_metric": "600", "dampening_half_time": "",
"dampening_max_suppress_time": "", "dampening_reuse_time": "",
"dampening_routemap": "", "dampening_state": false,
"dampening_suppress_time": "",
"default_information_originate": false, "default_metric": "",
"distance_ebgp": "20", "distance_ibgp": "200",
"distance_local": "220", "inject_map": [], "maximum_paths": "1",
"maximum_paths_ibgp": "1", "networks": [],
"next_hop_route_map": "", "redistribute": [], "safi": "unicast",
"suppress_inactive": false, "table_map": "",
"table_map_filter": false, "vrf": "TESTING"}
updates:
description: commands sent to the device
returned: always
type: list
sample: ["router bgp 65535", "vrf TESTING",
"address-family ipv4 unicast", "advertise l2vpn evpn"]
changed:
description: check to see if a change was made on the device
returned: always
type: boolean
sample: true
'''
import re
from ansible.module_utils.nxos import get_config, load_config, run_commands
from ansible.module_utils.nxos import nxos_argument_spec, check_args
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.netcfg import CustomNetworkConfig
WARNINGS = []
BOOL_PARAMS = [
'additional_paths_install',
'additional_paths_receive',
'additional_paths_send',
'advertise_l2vpn_evpn',
'client_to_client',
'dampening_state',
'default_information_originate',
'suppress_inactive',
]
PARAM_TO_DEFAULT_KEYMAP = {
'maximum_paths': '1',
'maximum_paths_ibgp': '1',
'client_to_client': True,
'distance_ebgp': '20',
'distance_ibgp': '200',
'distance_local': '220',
'dampen_igp_metric': '600'
}
PARAM_TO_COMMAND_KEYMAP = {
'asn': 'router bgp',
'afi': 'address-family',
'safi': 'address-family',
'additional_paths_install': 'additional-paths install backup',
'additional_paths_receive': 'additional-paths receive',
'additional_paths_selection': 'additional-paths selection route-map',
'additional_paths_send': 'additional-paths send',
'advertise_l2vpn_evpn': 'advertise l2vpn evpn',
'client_to_client': 'client-to-client reflection',
'dampen_igp_metric': 'dampen-igp-metric',
'dampening_state': 'dampening',
'dampening_half_time': 'dampening',
'dampening_max_suppress_time': 'dampening',
'dampening_reuse_time': 'dampening',
'dampening_routemap': 'dampening route-map',
'dampening_suppress_time': 'dampening',
'default_information_originate': 'default-information originate',
'default_metric': 'default-metric',
'distance_ebgp': 'distance',
'distance_ibgp': 'distance',
'distance_local': 'distance',
'inject_map': 'inject-map',
'maximum_paths': 'maximum-paths',
'maximum_paths_ibgp': 'maximum-paths ibgp',
'networks': 'network',
'redistribute': 'redistribute',
'next_hop_route_map': 'nexthop route-map',
'suppress_inactive': 'suppress-inactive',
'table_map': 'table-map',
'table_map_filter': 'table-map-filter',
'vrf': 'vrf'
}
DAMPENING_PARAMS = [
'dampening_half_time',
'dampening_suppress_time',
'dampening_reuse_time',
'dampening_max_suppress_time'
]
def invoke(name, *args, **kwargs):
func = globals().get(name)
if func:
return func(*args, **kwargs)
def get_custom_list_value(config, arg, module):
value_list = []
splitted_config = config.splitlines()
if arg == 'inject_map':
REGEX_INJECT = ('.*inject-map\s(?P<inject_map>\S+)'
'\sexist-map\s(?P<exist_map>\S+)-*')
for line in splitted_config:
value = []
inject_group = {}
try:
match_inject = re.match(REGEX_INJECT, line, re.DOTALL)
inject_group = match_inject.groupdict()
inject_map = inject_group['inject_map']
exist_map = inject_group['exist_map']
value.append(inject_map)
value.append(exist_map)
except AttributeError:
value = []
if value:
copy_attributes = False
inject_map_command = ('inject-map {0} exist-map {1} '
'copy-attributes'.format(
inject_group['inject_map'],
inject_group['exist_map']))
REGEX = re.compile(r'\s+{0}\s*$'.format(
inject_map_command), re.M)
try:
if REGEX.search(config):
copy_attributes = True
except TypeError:
copy_attributes = False
if copy_attributes:
value.append('copy_attributes')
value_list.append(value)
elif arg == 'networks':
REGEX_NETWORK = re.compile(r'(?:network\s)(?P<value>.*)$')
for line in splitted_config:
value = []
network_group = {}
if 'network' in line:
value = REGEX_NETWORK.search(line).group('value').split()
if value:
if len(value) == 3:
value.pop(1)
value_list.append(value)
elif arg == 'redistribute':
RED_REGEX = re.compile(r'(?:{0}\s)(?P<value>.*)$'.format(
PARAM_TO_COMMAND_KEYMAP[arg]), re.M)
for line in splitted_config:
value = []
redistribute_group = {}
if 'redistribute' in line:
value = RED_REGEX.search(line).group('value').split()
if value:
if len(value) == 3:
value.pop(1)
elif len(value) == 4:
value = ['{0} {1}'.format(
value[0], value[1]), value[3]]
value_list.append(value)
return value_list
def get_custom_string_value(config, arg, module):
value = ''
if arg.startswith('distance'):
REGEX_DISTANCE = ('.*distance\s(?P<d_ebgp>\w+)\s(?P<d_ibgp>\w+)'
'\s(?P<d_local>\w+)')
try:
match_distance = re.match(REGEX_DISTANCE, config, re.DOTALL)
distance_group = match_distance.groupdict()
except AttributeError:
distance_group = {}
if distance_group:
if arg == 'distance_ebgp':
value = distance_group['d_ebgp']
elif arg == 'distance_ibgp':
value = distance_group['d_ibgp']
elif arg == 'distance_local':
value = distance_group['d_local']
elif arg.startswith('dampening'):
REGEX = re.compile(r'(?:{0}\s)(?P<value>.*)$'.format(
PARAM_TO_COMMAND_KEYMAP[arg]), re.M)
if arg == 'dampen_igp_metric' or arg == 'dampening_routemap':
value = ''
if PARAM_TO_COMMAND_KEYMAP[arg] in config:
value = REGEX.search(config).group('value')
else:
REGEX_DAMPENING = ('.*dampening\s(?P<half>\w+)\s(?P<reuse>\w+)'
'\s(?P<suppress>\w+)\s(?P<max_suppress>\w+)')
try:
match_dampening = re.match(REGEX_DAMPENING, config, re.DOTALL)
dampening_group = match_dampening.groupdict()
except AttributeError:
dampening_group = {}
if dampening_group:
if arg == 'dampening_half_time':
value = dampening_group['half']
elif arg == 'dampening_reuse_time':
value = dampening_group['reuse']
elif arg == 'dampening_suppress_time':
value = dampening_group['suppress']
elif arg == 'dampening_max_suppress_time':
value = dampening_group['max_suppress']
elif arg == 'table_map_filter':
TMF_REGEX = re.compile(r'\s+table-map.*filter$', re.M)
value = False
try:
if TMF_REGEX.search(config):
value = True
except TypeError:
value = False
elif arg == 'table_map':
TM_REGEX = re.compile(r'(?:table-map\s)(?P<value>\S+)(\sfilter)?$', re.M)
value = ''
if PARAM_TO_COMMAND_KEYMAP[arg] in config:
value = TM_REGEX.search(config).group('value')
return value
def get_value(arg, config, module):
custom = [
'inject_map',
'networks',
'redistribute'
]
if arg in BOOL_PARAMS:
REGEX = re.compile(r'\s+{0}\s*$'.format(PARAM_TO_COMMAND_KEYMAP[arg]), re.M)
value = False
try:
if REGEX.search(config):
value = True
except TypeError:
value = False
elif arg in custom:
value = get_custom_list_value(config, arg, module)
elif (arg.startswith('distance') or arg.startswith('dampening') or
arg.startswith('table_map')):
value = get_custom_string_value(config, arg, module)
else:
REGEX = re.compile(r'(?:{0}\s)(?P<value>.*)$'.format(PARAM_TO_COMMAND_KEYMAP[arg]), re.M)
value = ''
if PARAM_TO_COMMAND_KEYMAP[arg] in config:
value = REGEX.search(config).group('value')
return value
def get_existing(module, args):
existing = {}
netcfg = get_config(module)
try:
asn_regex = '.*router\sbgp\s(?P<existing_asn>\d+).*'
match_asn = re.match(asn_regex, str(netcfg), re.DOTALL)
existing_asn_group = match_asn.groupdict()
existing_asn = existing_asn_group['existing_asn']
except AttributeError:
existing_asn = ''
if existing_asn:
parents = ["router bgp {0}".format(existing_asn)]
if module.params['vrf'] != 'default':
parents.append('vrf {0}'.format(module.params['vrf']))
parents.append('address-family {0} {1}'.format(module.params['afi'],
module.params['safi']))
config = netcfg.get_section(parents)
if config:
for arg in args:
if arg not in ['asn', 'afi', 'safi', 'vrf']:
existing[arg] = get_value(arg, config, module)
existing['asn'] = existing_asn
existing['afi'] = module.params['afi']
existing['safi'] = module.params['safi']
existing['vrf'] = module.params['vrf']
else:
WARNINGS.append("The BGP process {0} didn't exist but the task"
" just created it.".format(module.params['asn']))
return existing
def apply_key_map(key_map, table):
new_dict = {}
for key, value in table.items():
new_key = key_map.get(key)
if new_key:
value = table.get(key)
if value:
new_dict[new_key] = value
else:
new_dict[new_key] = value
return new_dict
def fix_proposed(module, proposed, existing):
commands = list()
command = ''
fixed_proposed = {}
for key, value in proposed.items():
if key in DAMPENING_PARAMS:
if value != 'default':
command = 'dampening {0} {1} {2} {3}'.format(
proposed.get('dampening_half_time'),
proposed.get('dampening_reuse_time'),
proposed.get('dampening_suppress_time'),
proposed.get('dampening_max_suppress_time'))
else:
if existing.get(key):
command = ('no dampening {0} {1} {2} {3}'.format(
existing['dampening_half_time'],
existing['dampening_reuse_time'],
existing['dampening_suppress_time'],
existing['dampening_max_suppress_time']))
if 'default' in command:
command = ''
elif key.startswith('distance'):
command = 'distance {0} {1} {2}'.format(
proposed.get('distance_ebgp'),
proposed.get('distance_ibgp'),
proposed.get('distance_local'))
else:
fixed_proposed[key] = value
if command:
if command not in commands:
commands.append(command)
return fixed_proposed, commands
def default_existing(existing_value, key, value):
commands = []
if key == 'network':
for network in existing_value:
if len(network) == 2:
commands.append('no network {0} route-map {1}'.format(
network[0], network[1]))
elif len(network) == 1:
commands.append('no network {0}'.format(
network[0]))
elif key == 'inject-map':
for maps in existing_value:
if len(maps) == 2:
commands.append('no inject-map {0} exist-map {1}'.format(
maps[0], maps[1]))
elif len(maps) == 3:
commands.append('no inject-map {0} exist-map {1} '
'copy-attributes'.format(
maps[0], maps[1]))
else:
commands.append('no {0} {1}'.format(key, existing_value))
return commands
def get_network_command(existing, key, value):
commands = []
existing_networks = existing.get('networks', [])
for inet in value:
if not isinstance(inet, list):
inet = [inet]
if inet not in existing_networks:
if len(inet) == 1:
command = '{0} {1}'.format(key, inet[0])
elif len(inet) == 2:
command = '{0} {1} route-map {2}'.format(key,
inet[0], inet[1])
commands.append(command)
return commands
def get_inject_map_command(existing, key, value):
commands = []
existing_maps = existing.get('inject_map', [])
for maps in value:
if not isinstance(maps, list):
maps = [maps]
if maps not in existing_maps:
if len(maps) == 2:
command = ('inject-map {0} exist-map {1}'.format(
maps[0], maps[1]))
elif len(maps) == 3:
command = ('inject-map {0} exist-map {1} '
'copy-attributes'.format(maps[0],
maps[1]))
commands.append(command)
return commands
def get_redistribute_command(existing, key, value):
commands = []
for rule in value:
if rule[1] == 'default':
existing_rule = existing.get('redistribute', [])
for each_rule in existing_rule:
if rule[0] in each_rule:
command = 'no {0} {1} route-map {2}'.format(
key, each_rule[0], each_rule[1])
commands.append(command)
else:
command = '{0} {1} route-map {2}'.format(key, rule[0], rule[1])
commands.append(command)
return commands
def get_table_map_command(module, existing, key, value):
commands = []
if key == 'table-map':
if value != 'default':
command = '{0} {1}'.format(key, module.params['table_map'])
if (module.params['table_map_filter'] is not None and
module.params['table_map_filter'] != 'default'):
command += ' filter'
commands.append(command)
else:
if existing.get('table_map'):
command = 'no {0} {1}'.format(key, existing.get('table_map'))
commands.append(command)
return commands
def get_default_table_map_filter(existing):
commands = []
existing_table_map_filter = existing.get('table_map_filter')
if existing_table_map_filter:
existing_table_map = existing.get('table_map')
if existing_table_map:
command = 'table-map {0}'.format(existing_table_map)
commands.append(command)
return commands
def state_present(module, existing, proposed, candidate):
fixed_proposed, commands = fix_proposed(module, proposed, existing)
proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, fixed_proposed)
existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
for key, value in proposed_commands.items():
if key == 'address-family':
addr_family_command = "address-family {0} {1}".format(
module.params['afi'], module.params['safi'])
if addr_family_command not in commands:
commands.append(addr_family_command)
elif key.startswith('table-map'):
table_map_commands = get_table_map_command(module, existing, key, value)
if table_map_commands:
commands.extend(table_map_commands)
elif value is True:
commands.append(key)
elif value is False:
commands.append('no {0}'.format(key))
elif value == 'default':
if key in PARAM_TO_DEFAULT_KEYMAP:
commands.append('{0} {1}'.format(key, PARAM_TO_DEFAULT_KEYMAP[key]))
elif existing_commands.get(key):
if key == 'table-map-filter':
default_tmf_command = get_default_table_map_filter(existing)
if default_tmf_command:
commands.extend(default_tmf_command)
else:
existing_value = existing_commands.get(key)
default_command = default_existing(existing_value, key, value)
if default_command:
commands.extend(default_command)
else:
if key == 'network':
network_commands = get_network_command(existing, key, value)
if network_commands:
commands.extend(network_commands)
elif key == 'inject-map':
inject_map_commands = get_inject_map_command(existing, key, value)
if inject_map_commands:
commands.extend(inject_map_commands)
elif key == 'redistribute':
redistribute_commands = get_redistribute_command(existing, key, value)
if redistribute_commands:
commands.extend(redistribute_commands)
else:
command = '{0} {1}'.format(key, value)
commands.append(command)
if commands:
parents = ["router bgp {0}".format(module.params['asn'])]
if module.params['vrf'] != 'default':
parents.append('vrf {0}'.format(module.params['vrf']))
if len(commands) == 1:
candidate.add(commands, parents=parents)
elif len(commands) > 1:
parents.append('address-family {0} {1}'.format(module.params['afi'],
module.params['safi']))
if addr_family_command in commands:
commands.remove(addr_family_command)
candidate.add(commands, parents=parents)
def state_absent(module, existing, proposed, candidate):
commands = []
parents = ["router bgp {0}".format(module.params['asn'])]
if module.params['vrf'] != 'default':
parents.append('vrf {0}'.format(module.params['vrf']))
commands.append('no address-family {0} {1}'.format(
module.params['afi'], module.params['safi']))
candidate.add(commands, parents=parents)
def main():
argument_spec = dict(
asn=dict(required=True, type='str'),
vrf=dict(required=False, type='str', default='default'),
safi=dict(required=True, type='str', choices=['unicast','multicast', 'evpn']),
afi=dict(required=True, type='str', choices=['ipv4','ipv6', 'vpnv4', 'vpnv6', 'l2vpn']),
additional_paths_install=dict(required=False, type='bool'),
additional_paths_receive=dict(required=False, type='bool'),
additional_paths_selection=dict(required=False, type='str'),
additional_paths_send=dict(required=False, type='bool'),
advertise_l2vpn_evpn=dict(required=False, type='bool'),
client_to_client=dict(required=False, type='bool'),
dampen_igp_metric=dict(required=False, type='str'),
dampening_state=dict(required=False, type='bool'),
dampening_half_time=dict(required=False, type='str'),
dampening_max_suppress_time=dict(required=False, type='str'),
dampening_reuse_time=dict(required=False, type='str'),
dampening_routemap=dict(required=False, type='str'),
dampening_suppress_time=dict(required=False, type='str'),
default_information_originate=dict(required=False, type='bool'),
default_metric=dict(required=False, type='str'),
distance_ebgp=dict(required=False, type='str'),
distance_ibgp=dict(required=False, type='str'),
distance_local=dict(required=False, type='str'),
inject_map=dict(required=False, type='list'),
maximum_paths=dict(required=False, type='str'),
maximum_paths_ibgp=dict(required=False, type='str'),
networks=dict(required=False, type='list'),
next_hop_route_map=dict(required=False, type='str'),
redistribute=dict(required=False, type='list'),
suppress_inactive=dict(required=False, type='bool'),
table_map=dict(required=False, type='str'),
table_map_filter=dict(required=False, type='bool'),
state=dict(choices=['present', 'absent'], default='present',
required=False),
include_defaults=dict(default=True),
config=dict(),
save=dict(type='bool', default=False)
)
argument_spec.update(nxos_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
required_together=[DAMPENING_PARAMS,
['distance_ibgp',
'distance_ebgp',
'distance_local']],
supports_check_mode=True)
warnings = list()
check_args(module, warnings)
state = module.params['state']
if module.params['dampening_routemap']:
for param in DAMPENING_PARAMS:
if module.params[param]:
module.fail_json(msg='dampening_routemap cannot be used with'
' the {0} param'.format(param))
if module.params['advertise_l2vpn_evpn']:
if module.params['vrf'] == 'default':
module.fail_json(msg='It is not possible to advertise L2VPN '
'EVPN in the default VRF. Please specify '
'another one.', vrf=module.params['vrf'])
if module.params['table_map_filter'] and not module.params['table_map']:
module.fail_json(msg='table_map param is needed when using'
' table_map_filter filter.')
args = [
"additional_paths_install",
"additional_paths_receive",
"additional_paths_selection",
"additional_paths_send",
"advertise_l2vpn_evpn",
"afi",
"asn",
"client_to_client",
"dampen_igp_metric",
"dampening_half_time",
"dampening_max_suppress_time",
"dampening_reuse_time",
"dampening_suppress_time",
"dampening_routemap",
"dampening_state",
"default_information_originate",
"default_metric",
"distance_ebgp",
"distance_ibgp",
"distance_local",
"inject_map",
"maximum_paths",
"maximum_paths_ibgp",
"networks",
"next_hop_route_map",
"redistribute",
"safi",
"suppress_inactive",
"table_map",
"table_map_filter",
"vrf"
]
existing = invoke('get_existing', module, args)
if existing.get('asn'):
if (existing.get('asn') != module.params['asn'] and
state == 'present'):
module.fail_json(msg='Another BGP ASN already exists.',
proposed_asn=module.params['asn'],
existing_asn=existing.get('asn'))
end_state = existing
proposed_args = dict((k, v) for k, v in module.params.items()
if v is not None and k in args)
if proposed_args.get('networks'):
if proposed_args['networks'][0] == 'default':
proposed_args['networks'] = 'default'
if proposed_args.get('inject_map'):
if proposed_args['inject_map'][0] == 'default':
proposed_args['inject_map'] = 'default'
proposed = {}
for key, value in proposed_args.items():
if key not in ['asn', 'vrf']:
if str(value).lower() == 'default':
value = PARAM_TO_DEFAULT_KEYMAP.get(key)
if value is None:
value = 'default'
if existing.get(key) or (not existing.get(key) and value):
proposed[key] = value
result = {}
if state == 'present' or (state == 'absent' and existing):
candidate = CustomNetworkConfig(indent=3)
invoke('state_%s' % state, module, existing, proposed, candidate)
try:
response = load_config(module, candidate)
result.update(response)
except ShellError:
exc = get_exception()
module.fail_json(msg=str(exc))
else:
result['updates'] = []
result['connected'] = module.connected
if module._verbosity > 0:
end_state = invoke('get_existing', module, args)
result['end_state'] = end_state
result['existing'] = existing
result['proposed'] = proposed_args
if WARNINGS:
result['warnings'] = WARNINGS
module.exit_json(**result)
if __name__ == '__main__':
main()