Init: mediaserver

This commit is contained in:
2023-02-08 12:13:28 +01:00
parent 848bc9739c
commit f7c23d4ba9
31914 changed files with 6175775 additions and 0 deletions

View File

@@ -0,0 +1,336 @@
#!/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/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: nxos_aaa_server
extends_documentation_fragment:
- cisco.nxos.nxos
short_description: Manages AAA server global configuration.
description:
- Manages AAA server global configuration
version_added: 1.0.0
author:
- Jason Edelman (@jedelman8)
notes:
- Tested against NXOSv 7.3.(0)D1(1) on VIRL
- Limited Support for Cisco MDS
- The server_type parameter is always required.
- If encrypt_type is not supplied, the global AAA server key will be stored as encrypted
(type 7).
- Changes to the global AAA server key with encrypt_type=0 are not idempotent.
- state=default will set the supplied parameters to their default values. The parameters
that you want to default must also be set to default. If global_key=default, the
global key will be removed.
options:
server_type:
description:
- The server type is either radius or tacacs.
required: true
choices:
- radius
- tacacs
type: str
global_key:
description:
- Global AAA shared secret or keyword 'default'.
type: str
encrypt_type:
description:
- The state of encryption applied to the entered global key. O clear text, 7 encrypted.
Type-6 encryption is not supported.
choices:
- '0'
- '7'
type: str
deadtime:
description:
- Duration for which a non-reachable AAA server is skipped, in minutes or keyword
'default. Range is 1-1440. Device default is 0.
type: str
server_timeout:
description:
- Global AAA server timeout period, in seconds or keyword 'default. Range is 1-60.
Device default is 5.
type: str
directed_request:
description:
- Enables direct authentication requests to AAA server or keyword 'default' Device
default is disabled.
choices:
- enabled
- disabled
- default
type: str
state:
description:
- Manage the state of the resource.
default: present
choices:
- present
- default
type: str
"""
EXAMPLES = """
# Radius Server Basic settings
- name: Radius Server Basic settings
cisco.nxos.nxos_aaa_server:
server_type: radius
server_timeout: 9
deadtime: 20
directed_request: enabled
# Tacacs Server Basic settings
- name: Tacacs Server Basic settings
cisco.nxos.nxos_aaa_server:
server_type: tacacs
server_timeout: 8
deadtime: 19
directed_request: disabled
# Setting Global Key
- name: AAA Server Global Key
cisco.nxos.nxos_aaa_server:
server_type: radius
global_key: test_key
"""
RETURN = """
commands:
description: command sent to the device
returned: always
type: list
sample: ["radius-server deadtime 22", "radius-server timeout 11",
"radius-server directed-request"]
"""
import re
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
load_config,
run_commands,
)
PARAM_TO_DEFAULT_KEYMAP = {
"server_timeout": "5",
"deadtime": "0",
"directed_request": "disabled",
}
def execute_show_command(command, module):
command = {"command": command, "output": "text"}
return run_commands(module, command)
def flatten_list(command_lists):
flat_command_list = []
for command in command_lists:
if isinstance(command, list):
flat_command_list.extend(command)
else:
flat_command_list.append(command)
return flat_command_list
def get_aaa_server_info(server_type, module):
aaa_server_info = {}
server_command = "show {0}-server".format(server_type)
request_command = "show {0}-server directed-request".format(server_type)
global_key_command = "show run | sec {0}".format(server_type)
aaa_regex = r".*{0}-server\skey\s\d\s+(?P<key>\S+).*".format(server_type)
server_body = execute_show_command(server_command, module)[0]
split_server = server_body.splitlines()
for line in split_server:
if line.startswith("timeout"):
aaa_server_info["server_timeout"] = line.split(":")[1]
elif line.startswith("deadtime"):
aaa_server_info["deadtime"] = line.split(":")[1]
request_body = execute_show_command(request_command, module)[0]
if bool(request_body):
aaa_server_info["directed_request"] = request_body.replace("\n", "")
else:
aaa_server_info["directed_request"] = "disabled"
key_body = execute_show_command(global_key_command, module)[0]
try:
match_global_key = re.match(aaa_regex, key_body, re.DOTALL)
group_key = match_global_key.groupdict()
aaa_server_info["global_key"] = group_key["key"].replace('"', "")
except (AttributeError, TypeError):
aaa_server_info["global_key"] = None
return aaa_server_info
def config_aaa_server(params, server_type):
cmds = []
deadtime = params.get("deadtime")
server_timeout = params.get("server_timeout")
directed_request = params.get("directed_request")
encrypt_type = params.get("encrypt_type", "7")
global_key = params.get("global_key")
if deadtime is not None:
cmds.append("{0}-server deadtime {1}".format(server_type, deadtime))
if server_timeout is not None:
cmds.append("{0}-server timeout {1}".format(server_type, server_timeout))
if directed_request is not None:
if directed_request == "enabled":
cmds.append("{0}-server directed-request".format(server_type))
elif directed_request == "disabled":
cmds.append("no {0}-server directed-request".format(server_type))
if global_key is not None:
cmds.append("{0}-server key {1} {2}".format(server_type, encrypt_type, global_key))
return cmds
def default_aaa_server(existing, params, server_type):
cmds = []
deadtime = params.get("deadtime")
server_timeout = params.get("server_timeout")
directed_request = params.get("directed_request")
global_key = params.get("global_key")
existing_key = existing.get("global_key")
if deadtime is not None and existing.get("deadtime") != PARAM_TO_DEFAULT_KEYMAP["deadtime"]:
cmds.append("no {0}-server deadtime 1".format(server_type))
if (
server_timeout is not None
and existing.get("server_timeout") != PARAM_TO_DEFAULT_KEYMAP["server_timeout"]
):
cmds.append("no {0}-server timeout 1".format(server_type))
if (
directed_request is not None
and existing.get("directed_request") != PARAM_TO_DEFAULT_KEYMAP["directed_request"]
):
cmds.append("no {0}-server directed-request".format(server_type))
if global_key is not None and existing_key is not None:
cmds.append("no {0}-server key 7 {1}".format(server_type, existing_key))
return cmds
def main():
argument_spec = dict(
server_type=dict(type="str", choices=["radius", "tacacs"], required=True),
global_key=dict(type="str", no_log=True),
encrypt_type=dict(type="str", choices=["0", "7"]),
deadtime=dict(type="str"),
server_timeout=dict(type="str"),
directed_request=dict(type="str", choices=["enabled", "disabled", "default"]),
state=dict(choices=["default", "present"], default="present"),
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
warnings = list()
results = {"changed": False, "commands": [], "warnings": warnings}
server_type = module.params["server_type"]
global_key = module.params["global_key"]
encrypt_type = module.params["encrypt_type"]
deadtime = module.params["deadtime"]
server_timeout = module.params["server_timeout"]
directed_request = module.params["directed_request"]
state = module.params["state"]
if encrypt_type and not global_key:
module.fail_json(msg="encrypt_type must be used with global_key.")
args = dict(
server_type=server_type,
global_key=global_key,
encrypt_type=encrypt_type,
deadtime=deadtime,
server_timeout=server_timeout,
directed_request=directed_request,
)
proposed = dict((k, v) for k, v in args.items() if v is not None)
existing = get_aaa_server_info(server_type, module)
commands = []
if state == "present":
if deadtime:
try:
if int(deadtime) < 0 or int(deadtime) > 1440:
raise ValueError
except ValueError:
module.fail_json(msg="deadtime must be an integer between 0 and 1440")
if server_timeout:
try:
if int(server_timeout) < 1 or int(server_timeout) > 60:
raise ValueError
except ValueError:
module.fail_json(msg="server_timeout must be an integer between 1 and 60")
delta = dict(set(proposed.items()).difference(existing.items()))
if delta:
command = config_aaa_server(delta, server_type)
if command:
commands.append(command)
elif state == "default":
for key, value in proposed.items():
if key != "server_type" and value != "default":
module.fail_json(msg='Parameters must be set to "default"' "when state=default")
command = default_aaa_server(existing, proposed, server_type)
if command:
commands.append(command)
cmds = flatten_list(commands)
if cmds:
results["changed"] = True
if not module.check_mode:
load_config(module, cmds)
if "configure" in cmds:
cmds.pop(0)
results["commands"] = cmds
module.exit_json(**results)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,370 @@
#!/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/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: nxos_aaa_server_host
extends_documentation_fragment:
- cisco.nxos.nxos
short_description: Manages AAA server host-specific configuration.
description:
- Manages AAA server host-specific configuration.
version_added: 1.0.0
author: Jason Edelman (@jedelman8)
notes:
- Tested against NXOSv 7.3.(0)D1(1) on VIRL
- Limited Support for Cisco MDS
- Changes to the host key (shared secret) are not idempotent for type 0.
- If C(state=absent) removes the whole host configuration.
options:
server_type:
description:
- The server type is either radius or tacacs.
required: true
choices:
- radius
- tacacs
type: str
address:
description:
- Address or name of the radius or tacacs host.
required: true
type: str
key:
description:
- Shared secret for the specified host or keyword 'default'.
type: str
encrypt_type:
description:
- The state of encryption applied to the entered key. O for clear text, 7 for
encrypted. Type-6 encryption is not supported.
choices:
- '0'
- '7'
type: str
host_timeout:
description:
- Timeout period for specified host, in seconds or keyword 'default. Range is
1-60.
type: str
auth_port:
description:
- Alternate UDP port for RADIUS authentication or keyword 'default'.
type: str
acct_port:
description:
- Alternate UDP port for RADIUS accounting or keyword 'default'.
type: str
tacacs_port:
description:
- Alternate TCP port TACACS Server or keyword 'default'.
type: str
state:
description:
- Manage the state of the resource.
default: present
choices:
- present
- absent
type: str
"""
EXAMPLES = """
# Radius Server Host Basic settings
- name: Radius Server Host Basic settings
cisco.nxos.nxos_aaa_server_host:
state: present
server_type: radius
address: 1.2.3.4
acct_port: 2084
host_timeout: 10
# Radius Server Host Key Configuration
- name: Radius Server Host Key Configuration
cisco.nxos.nxos_aaa_server_host:
state: present
server_type: radius
address: 1.2.3.4
key: hello
encrypt_type: 7
# TACACS Server Host Configuration
- name: Tacacs Server Host Configuration
cisco.nxos.nxos_aaa_server_host:
state: present
server_type: tacacs
tacacs_port: 89
host_timeout: 10
address: 5.6.7.8
"""
RETURN = """
proposed:
description: k/v pairs of parameters passed into module
returned: always
type: dict
sample: {"address": "1.2.3.4", "auth_port": "2084",
"host_timeout": "10", "server_type": "radius"}
existing:
description:
- k/v pairs of existing configuration
returned: always
type: dict
sample: {}
end_state:
description: k/v pairs of configuration after module execution
returned: always
type: dict
sample: {"address": "1.2.3.4", "auth_port": "2084",
"host_timeout": "10", "server_type": "radius"}
updates:
description: command sent to the device
returned: always
type: list
sample: ["radius-server host 1.2.3.4 auth-port 2084 timeout 10"]
changed:
description: check to see if a change was made on the device
returned: always
type: bool
sample: true
"""
import re
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
get_capabilities,
load_config,
run_commands,
)
def execute_show_command(command, module):
device_info = get_capabilities(module)
network_api = device_info.get("network_api", "nxapi")
if network_api == "cliconf":
cmds = [command]
body = run_commands(module, cmds)
elif network_api == "nxapi":
cmds = {"command": command, "output": "text"}
body = run_commands(module, cmds)
return body
def flatten_list(command_lists):
flat_command_list = []
for command in command_lists:
if isinstance(command, list):
flat_command_list.extend(command)
else:
flat_command_list.append(command)
return flat_command_list
def get_aaa_host_info(module, server_type, address):
aaa_host_info = {}
command = "show run | inc {0}-server.host.{1}".format(server_type, address)
body = execute_show_command(command, module)[0]
if body:
try:
if "radius" in body:
pattern = (
r"\S+ host \S+(?:\s+key 7\s+(\S+))?(?:\s+auth-port (\d+))?"
r"(?:\s+acct-port (\d+))?(?:\s+authentication)?"
r"(?:\s+accounting)?(?:\s+timeout (\d+))?"
)
match = re.search(pattern, body)
aaa_host_info["key"] = match.group(1)
if aaa_host_info["key"]:
aaa_host_info["key"] = aaa_host_info["key"].replace('"', "")
aaa_host_info["encrypt_type"] = "7"
aaa_host_info["auth_port"] = match.group(2)
aaa_host_info["acct_port"] = match.group(3)
aaa_host_info["host_timeout"] = match.group(4)
elif "tacacs" in body:
pattern = (
r"\S+ host \S+(?:\s+key 7\s+(\S+))?(?:\s+port (\d+))?(?:\s+timeout (\d+))?"
)
match = re.search(pattern, body)
aaa_host_info["key"] = match.group(1)
if aaa_host_info["key"]:
aaa_host_info["key"] = aaa_host_info["key"].replace('"', "")
aaa_host_info["encrypt_type"] = "7"
aaa_host_info["tacacs_port"] = match.group(2)
aaa_host_info["host_timeout"] = match.group(3)
aaa_host_info["server_type"] = server_type
aaa_host_info["address"] = address
except TypeError:
return {}
else:
return {}
return aaa_host_info
def config_aaa_host(server_type, address, params, existing):
cmds = []
cmd_str = "{0}-server host {1}".format(server_type, address)
cmd_no_str = "no " + cmd_str
key = params.get("key")
enc_type = params.get("encrypt_type", "")
defval = False
nondef = False
if key:
if key != "default":
cmds.append(cmd_str + " key {0} {1}".format(enc_type, key))
else:
cmds.append(cmd_no_str + " key 7 {0}".format(existing.get("key")))
locdict = {
"auth_port": "auth-port",
"acct_port": "acct-port",
"tacacs_port": "port",
"host_timeout": "timeout",
}
# platform CLI needs the keywords in the following order
for key in ["auth_port", "acct_port", "tacacs_port", "host_timeout"]:
item = params.get(key)
if item:
if item != "default":
cmd_str += " {0} {1}".format(locdict.get(key), item)
nondef = True
else:
cmd_no_str += " {0} 1".format(locdict.get(key))
defval = True
if defval:
cmds.append(cmd_no_str)
if nondef or not existing:
cmds.append(cmd_str)
return cmds
def main():
argument_spec = dict(
server_type=dict(choices=["radius", "tacacs"], required=True),
address=dict(type="str", required=True),
key=dict(type="str", no_log=False),
encrypt_type=dict(type="str", choices=["0", "7"]),
host_timeout=dict(type="str"),
auth_port=dict(type="str"),
acct_port=dict(type="str"),
tacacs_port=dict(type="str"),
state=dict(choices=["absent", "present"], default="present"),
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
warnings = list()
server_type = module.params["server_type"]
address = module.params["address"]
key = module.params["key"]
encrypt_type = module.params["encrypt_type"]
host_timeout = module.params["host_timeout"]
auth_port = module.params["auth_port"]
acct_port = module.params["acct_port"]
tacacs_port = module.params["tacacs_port"]
state = module.params["state"]
args = dict(
server_type=server_type,
address=address,
key=key,
encrypt_type=encrypt_type,
host_timeout=host_timeout,
auth_port=auth_port,
acct_port=acct_port,
tacacs_port=tacacs_port,
)
proposed = dict((k, v) for k, v in args.items() if v is not None)
changed = False
if encrypt_type and not key:
module.fail_json(msg="encrypt_type must be used with key")
if tacacs_port and server_type != "tacacs":
module.fail_json(msg="tacacs_port can only be used with server_type=tacacs")
if (auth_port or acct_port) and server_type != "radius":
module.fail_json(msg="auth_port and acct_port can only be used" "when server_type=radius")
existing = get_aaa_host_info(module, server_type, address)
end_state = existing
commands = []
delta = {}
if state == "present":
if not existing:
delta = proposed
else:
for key, value in proposed.items():
if key == "encrypt_type":
delta[key] = value
if value != existing.get(key):
if value != "default" or existing.get(key):
delta[key] = value
command = config_aaa_host(server_type, address, delta, existing)
if command:
commands.append(command)
elif state == "absent":
intersect = dict(set(proposed.items()).intersection(existing.items()))
if intersect.get("address") and intersect.get("server_type"):
command = "no {0}-server host {1}".format(
intersect.get("server_type"),
intersect.get("address"),
)
commands.append(command)
cmds = flatten_list(commands)
if cmds:
if module.check_mode:
module.exit_json(changed=True, commands=cmds)
else:
changed = True
load_config(module, cmds)
end_state = get_aaa_host_info(module, server_type, address)
results = {}
results["proposed"] = proposed
results["existing"] = existing
results["updates"] = cmds
results["changed"] = changed
results["warnings"] = warnings
results["end_state"] = end_state
module.exit_json(**results)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,440 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The module file for nxos_acl_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: nxos_acl_interfaces
short_description: ACL interfaces resource module
description: Add and remove Access Control Lists on interfaces in NX-OS platform
version_added: 1.0.0
author: Adharsh Srivats Rangarajan (@adharshsrivatsr)
notes:
- Tested against NX-OS 7.3.(0)D1(1) on VIRL
- Unsupported for Cisco MDS
options:
running_config:
description:
- This option is used only with state I(parsed).
- The value of this option should be the output received from the NX-OS device
by executing the command B(show running-config | section '^interface').
- The state I(parsed) reads the configuration from C(running_config) option and
transforms it into Ansible structured data as per the resource module's argspec
and the value is then returned in the I(parsed) key within the result.
type: str
config:
description: A list of interfaces to be configured with ACLs
type: list
elements: dict
suboptions:
name:
description: Name of the interface
type: str
required: true
access_groups:
description: List of address family indicators with ACLs to be configured
on the interface
type: list
elements: dict
suboptions:
afi:
description: Address Family Indicator of the ACLs to be configured
type: str
required: true
choices:
- ipv4
- ipv6
acls:
description: List of Access Control Lists for the interface
type: list
elements: dict
suboptions:
name:
description: Name of the ACL to be added/removed
type: str
required: true
direction:
description: Direction to be applied for the ACL
type: str
required: true
choices:
- in
- out
port:
description: Use ACL as port policy.
type: bool
state:
description: The state the configuration should be left in
type: str
choices:
- deleted
- gathered
- merged
- overridden
- rendered
- replaced
- parsed
default: merged
"""
EXAMPLES = """
# Using merged
# Before state:
# ------------
#
- name: Merge ACL interfaces configuration
cisco.nxos.nxos_acl_interfaces:
config:
- name: Ethernet1/2
access_groups:
- afi: ipv6
acls:
- name: ACL1v6
direction: in
- name: Eth1/5
access_groups:
- afi: ipv4
acls:
- name: PortACL
direction: in
port: true
- name: ACL1v4
direction: out
- afi: ipv6
acls:
- name: ACL1v6
direction: in
state: merged
# After state:
# ------------
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/5
# ip port access-group PortACL in
# ip access-group ACL1v4 out
# ipv6 traffic-filter ACL1v6 in
# Using replaced
# Before state:
# ------------
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/5
# ip port access-group PortACL in
# ip access-group ACL1v4 out
# ipv6 traffic-filter ACL1v6 in
- name: Replace interface configuration with given configuration
cisco.nxos.nxos_acl_interfaces:
config:
- name: Eth1/5
access_groups:
- afi: ipv4
acls:
- name: NewACLv4
direction: out
- name: Ethernet1/3
access_groups:
- afi: ipv6
acls:
- name: NewACLv6
direction: in
port: true
state: replaced
# After state:
# ------------
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/3
# ipv6 port traffic-filter NewACLv6 in
# interface Ethernet1/5
# ip access-group NewACLv4 out
# Using overridden
# Before state:
# ------------
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/5
# ip port access-group PortACL in
# ip access-group ACL1v4 out
# ipv6 traffic-filter ACL1v6 in
- name: Override interface configuration with given configuration
cisco.nxos.nxos_acl_interfaces:
config:
- name: Ethernet1/3
access_groups:
- afi: ipv4
acls:
- name: ACL1v4
direction: out
- name: PortACL
port: true
direction: in
- afi: ipv6
acls:
- name: NewACLv6
direction: in
port: true
state: overridden
# After state:
# ------------
# interface Ethernet1/3
# ip access-group ACL1v4 out
# ip port access-group PortACL in
# ipv6 port traffic-filter NewACLv6 in
# Using deleted to remove ACL config from specified interfaces
# Before state:
# -------------
# interface Ethernet1/1
# ip access-group ACL2v4 in
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/5
# ip port access-group PortACL in
# ip access-group ACL1v4 out
# ipv6 traffic-filter ACL1v6 in
- name: Delete ACL configuration on interfaces
cisco.nxos.nxos_acl_interfaces:
config:
- name: Ethernet1/5
- name: Ethernet1/2
state: deleted
# After state:
# -------------
# interface Ethernet1/1
# ip access-group ACL2v4 in
# interface Ethernet1/2
# interface Ethernet1/5
# Using deleted to remove ACL config from all interfaces
# Before state:
# -------------
# interface Ethernet1/1
# ip access-group ACL2v4 in
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/5
# ip port access-group PortACL in
# ip access-group ACL1v4 out
# ipv6 traffic-filter ACL1v6 in
- name: Delete ACL configuration from all interfaces
cisco.nxos.nxos_acl_interfaces:
state: deleted
# After state:
# -------------
# interface Ethernet1/1
# interface Ethernet1/2
# interface Ethernet1/5
# Using parsed
- name: Parse given configuration into structured format
cisco.nxos.nxos_acl_interfaces:
running_config: |
interface Ethernet1/2
ipv6 traffic-filter ACL1v6 in
interface Ethernet1/5
ipv6 traffic-filter ACL1v6 in
ip access-group ACL1v4 out
ip port access-group PortACL in
state: parsed
# returns
# parsed:
# - name: Ethernet1/2
# access_groups:
# - afi: ipv6
# acls:
# - name: ACL1v6
# direction: in
# - name: Ethernet1/5
# access_groups:
# - afi: ipv4
# acls:
# - name: PortACL
# direction: in
# port: True
# - name: ACL1v4
# direction: out
# - afi: ipv6
# acls:
# - name: ACL1v6
# direction: in
# Using gathered:
# Before state:
# ------------
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/5
# ipv6 traffic-filter ACL1v6 in
# ip access-group ACL1v4 out
# ip port access-group PortACL in
- name: Gather existing configuration from device
cisco.nxos.nxos_acl_interfaces:
config:
state: gathered
# returns
# gathered:
# - name: Ethernet1/2
# access_groups:
# - afi: ipv6
# acls:
# - name: ACL1v6
# direction: in
# - name: Ethernet1/5
# access_groups:
# - afi: ipv4
# acls:
# - name: PortACL
# direction: in
# port: True
# - name: ACL1v4
# direction: out
# - afi: ipv6
# acls:
# - name: ACL1v6
# direction: in
# Using rendered
- name: Render required configuration to be pushed to the device
cisco.nxos.nxos_acl_interfaces:
config:
- name: Ethernet1/2
access_groups:
- afi: ipv6
acls:
- name: ACL1v6
direction: in
- name: Ethernet1/5
access_groups:
- afi: ipv4
acls:
- name: PortACL
direction: in
port: true
- name: ACL1v4
direction: out
- afi: ipv6
acls:
- name: ACL1v6
direction: in
state: rendered
# returns
# rendered:
# interface Ethernet1/2
# ipv6 traffic-filter ACL1v6 in
# interface Ethernet1/5
# ipv6 traffic-filter ACL1v6 in
# ip access-group ACL1v4 out
# ip port access-group PortACL in
"""
RETURN = """
before:
description: The configuration prior to the model invocation.
returned: always
type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The resulting configuration model invocation.
returned: when changed
type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample: ['interface Ethernet1/2', 'ipv6 traffic-filter ACL1v6 out', 'ip port access-group PortACL in']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.acl_interfaces.acl_interfaces import (
Acl_interfacesArgs,
)
from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.acl_interfaces.acl_interfaces import (
Acl_interfaces,
)
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
module = AnsibleModule(
argument_spec=Acl_interfacesArgs.argument_spec,
supports_check_mode=True,
)
result = Acl_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,818 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The module file for nxos_acls
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: nxos_acls
short_description: ACLs resource module
description: Manage named IP ACLs on the Cisco NX-OS platform
version_added: 1.0.0
author: Adharsh Srivats Rangarajan (@adharshsrivatsr)
notes:
- Tested against NX-OS 7.3.(0)D1(1) on VIRL
- Unsupported for Cisco MDS
- As NX-OS allows configuring a rule again with different sequence numbers, the user
is expected to provide sequence numbers for the access control entries to preserve
idempotency. If no sequence number is given, the rule will be added as a new rule
by the device.
options:
running_config:
description:
- This option is used only with state I(parsed).
- The value of this option should be the output received from the NX-OS device
by executing the command B(show running-config | section 'ip(v6)* access-list).
- The state I(parsed) reads the configuration from C(running_config) option and
transforms it into Ansible structured data as per the resource module's argspec
and the value is then returned in the I(parsed) key within the result.
type: str
config:
description: A dictionary of ACL options.
type: list
elements: dict
suboptions:
afi:
description: The Address Family Indicator (AFI) for the ACL.
type: str
required: true
choices:
- ipv4
- ipv6
acls:
description: A list of the ACLs.
type: list
elements: dict
suboptions:
name:
description: Name of the ACL.
type: str
required: true
aces:
description: The entries within the ACL.
type: list
elements: dict
suboptions:
grant:
description: Action to be applied on the rule.
type: str
choices:
- permit
- deny
destination:
description: Specify the packet destination.
type: dict
suboptions:
address:
description: Destination network address.
type: str
any:
description: Any destination address.
type: bool
host:
description: Host IP address.
type: str
port_protocol:
description: Specify the destination port or protocol (only for
TCP and UDP).
type: dict
suboptions:
eq:
description: Match only packets on a given port number.
type: str
gt:
description: Match only packets with a greater port number.
type: str
lt:
description: Match only packets with a lower port number.
type: str
neq:
description: Match only packets not on a given port number.
type: str
range:
description: Match only packets in the range of port numbers.
type: dict
suboptions:
start:
description: Specify the start of the port range.
type: str
end:
description: Specify the end of the port range.
type: str
prefix:
description: Destination network prefix. Only for prefixes of
value less than 31 for ipv4 and 127 for ipv6. Prefixes of 32
(ipv4) and 128 (ipv6) should be given in the 'host' key.
type: str
wildcard_bits:
description: Destination wildcard bits.
type: str
dscp:
description: Match packets with given DSCP value.
type: str
fragments:
description: Check non-initial fragments.
type: bool
remark:
description: Access list entry comment.
type: str
sequence:
description: Sequence number.
type: int
source:
description: Specify the packet source.
type: dict
suboptions:
address:
description: Source network address.
type: str
any:
description: Any source address.
type: bool
host:
description: Host IP address.
type: str
port_protocol:
description: Specify the destination port or protocol (only for
TCP and UDP).
type: dict
suboptions:
eq:
description: Match only packets on a given port number.
type: str
gt:
description: Match only packets with a greater port number.
type: str
lt:
description: Match only packets with a lower port number.
type: str
neq:
description: Match only packets not on a given port number.
type: str
range:
description: Match only packets in the range of port numbers.
type: dict
suboptions:
start:
description: Specify the start of the port range.
type: str
end:
description: Specify the end of the port range.
type: str
prefix:
description: Source network prefix. Only for prefixes of mask
value less than 31 for ipv4 and 127 for ipv6. Prefixes of mask
32 (ipv4) and 128 (ipv6) should be given in the 'host' key.
type: str
wildcard_bits:
description: Source wildcard bits.
type: str
log:
description: Log matches against this entry.
type: bool
precedence:
description: Match packets with given precedence value.
type: str
protocol:
description: Specify the protocol.
type: str
protocol_options:
description: All possible suboptions for the protocol chosen.
type: dict
suboptions:
icmp:
description: ICMP protocol options.
type: dict
suboptions:
administratively_prohibited:
description: Administratively prohibited
type: bool
alternate_address:
description: Alternate address
type: bool
conversion_error:
description: Datagram conversion
type: bool
dod_host_prohibited:
description: Host prohibited
type: bool
dod_net_prohibited:
description: Net prohibited
type: bool
echo:
description: Echo (ping)
type: bool
echo_reply:
description: Echo reply
type: bool
echo_request:
description: Echo request (ping)
type: bool
general_parameter_problem:
description: Parameter problem
type: bool
host_isolated:
description: Host isolated
type: bool
host_precedence_unreachable:
description: Host unreachable for precedence
type: bool
host_redirect:
description: Host redirect
type: bool
host_tos_redirect:
description: Host redirect for TOS
type: bool
host_tos_unreachable:
description: Host unreachable for TOS
type: bool
host_unknown:
description: Host unknown
type: bool
host_unreachable:
description: Host unreachable
type: bool
information_reply:
description: Information replies
type: bool
information_request:
description: Information requests
type: bool
mask_reply:
description: Mask replies
type: bool
mask_request:
description: Mask requests
type: bool
message_code:
description: ICMP message code
type: int
message_type:
description: ICMP message type
type: int
mobile_redirect:
description: Mobile host redirect
type: bool
net_redirect:
description: Network redirect
type: bool
net_tos_redirect:
description: Net redirect for TOS
type: bool
net_tos_unreachable:
description: Network unreachable for TOS
type: bool
net_unreachable:
description: Net unreachable
type: bool
network_unknown:
description: Network unknown
type: bool
no_room_for_option:
description: Parameter required but no room
type: bool
option_missing:
description: Parameter required but not present
type: bool
packet_too_big:
description: Fragmentation needed and DF set
type: bool
parameter_problem:
description: All parameter problems
type: bool
port_unreachable:
description: Port unreachable
type: bool
precedence_unreachable:
description: Precedence cutoff
type: bool
protocol_unreachable:
description: Protocol unreachable
type: bool
reassembly_timeout:
description: Reassembly timeout
type: bool
redirect:
description: All redirects
type: bool
router_advertisement:
description: Router discovery advertisements
type: bool
router_solicitation:
description: Router discovery solicitations
type: bool
source_quench:
description: Source quenches
type: bool
source_route_failed:
description: Source route failed
type: bool
time_exceeded:
description: All time exceeded.
type: bool
timestamp_reply:
description: Timestamp replies
type: bool
timestamp_request:
description: Timestamp requests
type: bool
traceroute:
description: Traceroute
type: bool
ttl_exceeded:
description: TTL exceeded
type: bool
unreachable:
description: All unreachables
type: bool
tcp:
description: TCP flags.
type: dict
suboptions:
ack:
description: Match on the ACK bit
type: bool
established:
description: Match established connections
type: bool
fin:
description: Match on the FIN bit
type: bool
psh:
description: Match on the PSH bit
type: bool
rst:
description: Match on the RST bit
type: bool
syn:
description: Match on the SYN bit
type: bool
urg:
description: Match on the URG bit
type: bool
igmp:
description: IGMP protocol options.
type: dict
suboptions:
dvmrp:
description: Distance Vector Multicast Routing Protocol
type: bool
host_query:
description: Host Query
type: bool
host_report:
description: Host Report
type: bool
state:
description:
- The state the configuration should be left in
type: str
choices:
- deleted
- gathered
- merged
- overridden
- rendered
- replaced
- parsed
default: merged
"""
EXAMPLES = """
# Using merged
# Before state:
# -------------
#
- name: Merge new ACLs configuration
cisco.nxos.nxos_acls:
config:
- afi: ipv4
acls:
- name: ACL1v4
aces:
- grant: deny
destination:
address: 192.0.2.64
wildcard_bits: 0.0.0.255
source:
any: true
port_protocol:
lt: 55
protocol: tcp
protocol_options:
tcp:
ack: true
fin: true
sequence: 50
- afi: ipv6
acls:
- name: ACL1v6
aces:
- grant: permit
sequence: 10
source:
any: true
destination:
prefix: 2001:db8:12::/32
protocol: sctp
state: merged
# After state:
# ------------
#
# ip access-list ACL1v4
# 50 deny tcp any lt 55 192.0.2.64 0.0.0.255 ack fin
# ipv6 access-list ACL1v6
# 10 permit sctp any any
# Using replaced
# Before state:
# ----------------
#
# ip access-list ACL1v4
# 10 permit ip any any
# 20 deny udp any any
# ip access-list ACL2v4
# 10 permit ahp 192.0.2.0 0.0.0.255 any
# ip access-list ACL1v6
# 10 permit sctp any any
# 20 remark IPv6 ACL
# ip access-list ACL2v6
# 10 deny ipv6 any 2001:db8:3000::/36
# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
- name: Replace existing ACL configuration with provided configuration
cisco.nxos.nxos_acls:
config:
- afi: ipv4
- afi: ipv6
acls:
- name: ACL1v6
aces:
- sequence: 20
grant: permit
source:
any: true
destination:
any: true
protocol: pip
- remark: Replaced ACE
- name: ACL2v6
state: replaced
# After state:
# ---------------
#
# ipv6 access-list ACL1v6
# 20 permit pip any any
# 30 remark Replaced ACE
# ipv6 access-list ACL2v6
# Using overridden
# Before state:
# ----------------
#
# ip access-list ACL1v4
# 10 permit ip any any
# 20 deny udp any any
# ip access-list ACL2v4
# 10 permit ahp 192.0.2.0 0.0.0.255 any
# ip access-list ACL1v6
# 10 permit sctp any any
# 20 remark IPv6 ACL
# ip access-list ACL2v6
# 10 deny ipv6 any 2001:db8:3000::/36
# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
- name: Override existing configuration with provided configuration
cisco.nxos.nxos_acls:
config:
- afi: ipv4
acls:
- name: NewACL
aces:
- grant: deny
source:
address: 192.0.2.0
wildcard_bits: 0.0.255.255
destination:
any: true
protocol: eigrp
- remark: Example for overridden state
state: overridden
# After state:
# ------------
#
# ip access-list NewACL
# 10 deny eigrp 192.0.2.0 0.0.255.255 any
# 20 remark Example for overridden state
# Using deleted:
#
# Before state:
# -------------
#
# ip access-list ACL1v4
# 10 permit ip any any
# 20 deny udp any any
# ip access-list ACL2v4
# 10 permit ahp 192.0.2.0 0.0.0.255 any
# ip access-list ACL1v6
# 10 permit sctp any any
# 20 remark IPv6 ACL
# ip access-list ACL2v6
# 10 deny ipv6 any 2001:db8:3000::/36
# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
- name: Delete all ACLs
cisco.nxos.nxos_acls:
config:
state: deleted
# After state:
# -----------
#
# Before state:
# -------------
#
# ip access-list ACL1v4
# 10 permit ip any any
# 20 deny udp any any
# ip access-list ACL2v4
# 10 permit ahp 192.0.2.0 0.0.0.255 any
# ip access-list ACL1v6
# 10 permit sctp any any
# 20 remark IPv6 ACL
# ip access-list ACL2v6
# 10 deny ipv6 any 2001:db8:3000::/36
# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
- name: Delete all ACLs in given AFI
cisco.nxos.nxos_acls:
config:
- afi: ipv4
state: deleted
# After state:
# ------------
#
# ip access-list ACL1v6
# 10 permit sctp any any
# 20 remark IPv6 ACL
# ip access-list ACL2v6
# 10 deny ipv6 any 2001:db8:3000::/36
# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
# Before state:
# -------------
#
# ip access-list ACL1v4
# 10 permit ip any any
# 20 deny udp any any
# ip access-list ACL2v4
# 10 permit ahp 192.0.2.0 0.0.0.255 any
# ipv6 access-list ACL1v6
# 10 permit sctp any any
# 20 remark IPv6 ACL
# ipv6 access-list ACL2v6
# 10 deny ipv6 any 2001:db8:3000::/36
# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
- name: Delete specific ACLs
cisco.nxos.nxos_acls:
config:
- afi: ipv4
acls:
- name: ACL1v4
- name: ACL2v4
- afi: ipv6
acls:
- name: ACL1v6
state: deleted
# After state:
# ------------
# ipv6 access-list ACL2v6
# 10 deny ipv6 any 2001:db8:3000::/36
# 20 permit tcp 2001:db8:2000:2::2/128 2001:db8:2000:ab::2/128
# Using parsed
- name: Parse given config to structured data
cisco.nxos.nxos_acls:
running_config: |
ip access-list ACL1v4
50 deny tcp any lt 55 192.0.2.64 0.0.0.255 ack fin
ipv6 access-list ACL1v6
10 permit sctp any any
state: parsed
# returns:
# parsed:
# - afi: ipv4
# acls:
# - name: ACL1v4
# aces:
# - grant: deny
# destination:
# address: 192.0.2.64
# wildcard_bits: 0.0.0.255
# source:
# any: true
# port_protocol:
# lt: 55
# protocol: tcp
# protocol_options:
# tcp:
# ack: true
# fin: true
# sequence: 50
#
# - afi: ipv6
# acls:
# - name: ACL1v6
# aces:
# - grant: permit
# sequence: 10
# source:
# any: true
# destination:
# prefix: 2001:db8:12::/32
# protocol: sctp
# Using gathered:
# Before state:
# ------------
#
# ip access-list ACL1v4
# 50 deny tcp any lt 55 192.0.2.64 0.0.0.255 ack fin
# ipv6 access-list ACL1v6
# 10 permit sctp any any
- name: Gather existing configuration
cisco.nxos.nxos_acls:
state: gathered
# returns:
# gathered:
# - afi: ipv4
# acls:
# - name: ACL1v4
# aces:
# - grant: deny
# destination:
# address: 192.0.2.64
# wildcard_bits: 0.0.0.255
# source:
# any: true
# port_protocol:
# lt: 55
# protocol: tcp
# protocol_options:
# tcp:
# ack: true
# fin: true
# sequence: 50
# - afi: ipv6
# acls:
# - name: ACL1v6
# aces:
# - grant: permit
# sequence: 10
# source:
# any: true
# destination:
# prefix: 2001:db8:12::/32
# protocol: sctp
# Using rendered
- name: Render required configuration to be pushed to the device
cisco.nxos.nxos_acls:
config:
- afi: ipv4
acls:
- name: ACL1v4
aces:
- grant: deny
destination:
address: 192.0.2.64
wildcard_bits: 0.0.0.255
source:
any: true
port_protocol:
lt: 55
protocol: tcp
protocol_options:
tcp:
ack: true
fin: true
sequence: 50
- afi: ipv6
acls:
- name: ACL1v6
aces:
- grant: permit
sequence: 10
source:
any: true
destination:
prefix: 2001:db8:12::/32
protocol: sctp
state: rendered
# returns:
# rendered:
# ip access-list ACL1v4
# 50 deny tcp any lt 55 192.0.2.64 0.0.0.255 ack fin
# ipv6 access-list ACL1v6
# 10 permit sctp any any
"""
RETURN = """
before:
description: The configuration prior to the model invocation.
returned: always
type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The resulting configuration model invocation.
returned: when changed
type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample: ['ip access-list ACL1v4', '10 permit ip any any precedence critical log', '20 deny tcp any lt smtp host 192.0.2.64 ack fin']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.acls.acls import (
AclsArgs,
)
from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.acls.acls import Acls
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
module = AnsibleModule(argument_spec=AclsArgs.argument_spec, supports_check_mode=True)
result = Acls(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,224 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
__metaclass__ = type
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# 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/>.
#
DOCUMENTATION = """
module: nxos_banner
author: Trishna Guha (@trishnaguha)
short_description: Manage multiline banners on Cisco NXOS devices
description:
- This will configure both exec and motd banners on remote devices running Cisco NXOS.
It allows playbooks to add or remove banner text from the active running configuration.
notes:
- Since responses from the device are always read with surrounding whitespaces stripped,
tasks that configure banners with preceeding or trailing whitespaces will not be idempotent.
- Limited Support for Cisco MDS
version_added: 1.0.0
options:
banner:
description:
- Specifies which banner that should be configured on the remote device.
required: true
choices:
- exec
- motd
type: str
text:
description:
- The banner text that should be present in the remote device running configuration.
This argument accepts a multiline string, with no empty lines. Requires I(state=present).
type: str
state:
description:
- Specifies whether or not the configuration is present in the current devices
active running configuration.
default: present
choices:
- present
- absent
type: str
extends_documentation_fragment:
- cisco.nxos.nxos
"""
EXAMPLES = """
- name: configure the exec banner
cisco.nxos.nxos_banner:
banner: exec
text: |
this is my exec banner
that contains a multiline
string
state: present
- name: remove the motd banner
cisco.nxos.nxos_banner:
banner: motd
state: absent
- name: Configure banner from file
cisco.nxos.nxos_banner:
banner: motd
text: "{{ lookup('file', './config_partial/raw_banner.cfg') }}"
state: present
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- banner exec
- this is my exec banner
- that contains a multiline
- string
"""
import re
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
load_config,
run_commands,
)
def execute_show_command(module, command):
format = "text"
cmds = [{"command": command, "output": format}]
output = run_commands(module, cmds)
return output
def map_obj_to_commands(want, have, module):
commands = list()
state = module.params["state"]
platform_regex = "Nexus.*Switch"
if state == "absent":
if have.get("text") and not (
(have.get("text") == "User Access Verification")
or re.match(platform_regex, have.get("text"))
):
commands.append("no banner %s" % module.params["banner"])
elif state == "present" and want.get("text") != have.get("text"):
banner_cmd = "banner %s @\n%s\n@" % (
module.params["banner"],
want["text"],
)
commands.append(banner_cmd)
return commands
def map_config_to_obj(module):
command = "show banner %s" % module.params["banner"]
output = execute_show_command(module, command)[0]
if "Invalid command" in output:
module.fail_json(
msg="banner: %s may not be supported on this platform. Possible values are : exec | motd"
% module.params["banner"],
)
if isinstance(output, dict):
output = list(output.values())
if output != []:
output = output[0]
else:
output = ""
if isinstance(output, dict):
output = list(output.values())
if output != []:
output = output[0]
else:
output = ""
else:
output = output.rstrip()
obj = {"banner": module.params["banner"], "state": "absent"}
if output:
obj["text"] = output
obj["state"] = "present"
return obj
def map_params_to_obj(module):
text = module.params["text"]
return {
"banner": module.params["banner"],
"text": to_text(text) if text else None,
"state": module.params["state"],
}
def main():
"""main entry point for module execution"""
argument_spec = dict(
banner=dict(required=True, choices=["exec", "motd"]),
text=dict(),
state=dict(default="present", choices=["present", "absent"]),
)
required_if = [("state", "present", ("text",))]
module = AnsibleModule(
argument_spec=argument_spec,
required_if=required_if,
supports_check_mode=True,
)
warnings = list()
result = {"changed": False}
if warnings:
result["warnings"] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands(want, have, module)
result["commands"] = commands
if commands:
if not module.check_mode:
msgs = load_config(module, commands, True)
if msgs:
for item in msgs:
if item:
if isinstance(item, dict):
err_str = item["clierror"]
else:
err_str = item
if "more than 40 lines" in err_str or "buffer overflowed" in err_str:
load_config(module, commands)
result["changed"] = True
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,333 @@
#!/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/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: nxos_bfd_global
extends_documentation_fragment:
- cisco.nxos.nxos
short_description: Bidirectional Forwarding Detection (BFD) global-level configuration
description:
- Manages Bidirectional Forwarding Detection (BFD) global-level configuration.
version_added: 1.0.0
author:
- Chris Van Heuveln (@chrisvanheuveln)
notes:
- Tested against NXOSv 9.2(2)
- Unsupported for Cisco MDS
- BFD global will automatically enable 'feature bfd' if it is disabled.
- BFD global does not have a 'state' parameter. All of the BFD commands are unique
and are defined if 'feature bfd' is enabled.
options:
echo_interface:
description:
- Loopback interface used for echo frames.
- Valid values are loopback interface name or 'deleted'.
- Not supported on N5K/N6K
required: false
type: str
echo_rx_interval:
description:
- BFD Echo receive interval in milliseconds.
required: false
type: int
interval:
description:
- BFD interval timer values.
- Value must be a dict defining values for keys (tx, min_rx, and multiplier)
required: false
type: dict
slow_timer:
description:
- BFD slow rate timer in milliseconds.
required: false
type: int
startup_timer:
description:
- BFD delayed startup timer in seconds.
- Not supported on N5K/N6K/N7K
required: false
type: int
ipv4_echo_rx_interval:
description:
- BFD IPv4 session echo receive interval in milliseconds.
required: false
type: int
ipv4_interval:
description:
- BFD IPv4 interval timer values.
- Value must be a dict defining values for keys (tx, min_rx, and multiplier).
required: false
type: dict
ipv4_slow_timer:
description:
- BFD IPv4 slow rate timer in milliseconds.
required: false
type: int
ipv6_echo_rx_interval:
description:
- BFD IPv6 session echo receive interval in milliseconds.
required: false
type: int
ipv6_interval:
description:
- BFD IPv6 interval timer values.
- Value must be a dict defining values for keys (tx, min_rx, and multiplier).
required: false
type: dict
ipv6_slow_timer:
description:
- BFD IPv6 slow rate timer in milliseconds.
required: false
type: int
fabricpath_interval:
description:
- BFD fabricpath interval timer values.
- Value must be a dict defining values for keys (tx, min_rx, and multiplier).
required: false
type: dict
fabricpath_slow_timer:
description:
- BFD fabricpath slow rate timer in milliseconds.
required: false
type: int
fabricpath_vlan:
description:
- BFD fabricpath control vlan.
required: false
type: int
"""
EXAMPLES = """
- cisco.nxos.nxos_bfd_global:
echo_interface: Ethernet1/2
echo_rx_interval: 50
interval:
tx: 50
min_rx: 50
multiplier: 4
"""
RETURN = """
cmds:
description: commands sent to the device
returned: always
type: list
sample: ["bfd echo-interface loopback1", "bfd slow-timer 2000"]
"""
import re
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
NxosCmdRef,
load_config,
)
BFD_CMD_REF = """
# The cmd_ref is a yaml formatted list of module commands.
# A leading underscore denotes a non-command variable; e.g. _template.
# BFD does not have convenient json data so this cmd_ref uses raw cli configs.
---
_template: # _template holds common settings for all commands
# Enable feature bfd if disabled
feature: bfd
# Common get syntax for BFD commands
get_command: show run bfd all | incl '^(no )*bfd'
echo_interface:
kind: str
getval: (no )*bfd echo-interface *(\\S+)*$
setval: 'bfd echo-interface {0}'
default: ~
echo_rx_interval:
_exclude: ['N5K', 'N6K']
kind: int
getval: bfd echo-rx-interval (\\d+)$
setval: bfd echo-rx-interval {0}
default: 50
N3K:
default: 250
interval:
kind: dict
getval: bfd interval (?P<tx>\\d+) min_rx (?P<min_rx>\\d+) multiplier (?P<multiplier>\\d+)
setval: bfd interval {tx} min_rx {min_rx} multiplier {multiplier}
default: &def_interval
tx: 50
min_rx: 50
multiplier: 3
N3K:
default: &n3k_def_interval
tx: 250
min_rx: 250
multiplier: 3
slow_timer:
kind: int
getval: bfd slow-timer (\\d+)$
setval: bfd slow-timer {0}
default: 2000
startup_timer:
_exclude: ['N5K', 'N6K', 'N7K']
kind: int
getval: bfd startup-timer (\\d+)$
setval: bfd startup-timer {0}
default: 5
# IPv4/IPv6 specific commands
ipv4_echo_rx_interval:
_exclude: ['N5K', 'N6K']
kind: int
getval: bfd ipv4 echo-rx-interval (\\d+)$
setval: bfd ipv4 echo-rx-interval {0}
default: 50
N3K:
default: 250
ipv4_interval:
_exclude: ['N5K', 'N6K']
kind: dict
getval: bfd ipv4 interval (?P<tx>\\d+) min_rx (?P<min_rx>\\d+) multiplier (?P<multiplier>\\d+)
setval: bfd ipv4 interval {tx} min_rx {min_rx} multiplier {multiplier}
default: *def_interval
N3K:
default: *n3k_def_interval
ipv4_slow_timer:
_exclude: ['N5K', 'N6K']
kind: int
getval: bfd ipv4 slow-timer (\\d+)$
setval: bfd ipv4 slow-timer {0}
default: 2000
ipv6_echo_rx_interval:
_exclude: ['N35', 'N5K', 'N6K']
kind: int
getval: bfd ipv6 echo-rx-interval (\\d+)$
setval: bfd ipv6 echo-rx-interval {0}
default: 50
N3K:
default: 250
ipv6_interval:
_exclude: ['N35', 'N5K', 'N6K']
kind: dict
getval: bfd ipv6 interval (?P<tx>\\d+) min_rx (?P<min_rx>\\d+) multiplier (?P<multiplier>\\d+)
setval: bfd ipv6 interval {tx} min_rx {min_rx} multiplier {multiplier}
default: *def_interval
N3K:
default: *n3k_def_interval
ipv6_slow_timer:
_exclude: ['N35', 'N5K', 'N6K']
kind: int
getval: bfd ipv6 slow-timer (\\d+)$
setval: bfd ipv6 slow-timer {0}
default: 2000
# Fabricpath Commands
fabricpath_interval:
_exclude: ['N35', 'N3K', 'N9K']
kind: dict
getval: bfd fabricpath interval (?P<tx>\\d+) min_rx (?P<min_rx>\\d+) multiplier (?P<multiplier>\\d+)
setval: bfd fabricpath interval {tx} min_rx {min_rx} multiplier {multiplier}
default: *def_interval
fabricpath_slow_timer:
_exclude: ['N35', 'N3K', 'N9K']
kind: int
getval: bfd fabricpath slow-timer (\\d+)$
setval: bfd fabricpath slow-timer {0}
default: 2000
fabricpath_vlan:
_exclude: ['N35', 'N3K', 'N9K']
kind: int
getval: bfd fabricpath vlan (\\d+)$
setval: bfd fabricpath vlan {0}
default: 1
"""
def reorder_cmds(cmds):
"""
There is a bug in some image versions where bfd echo-interface and
bfd echo-rx-interval need to be applied last for them to nvgen properly.
"""
regex1 = re.compile(r"^bfd echo-interface")
regex2 = re.compile(r"^bfd echo-rx-interval")
filtered_cmds = [i for i in cmds if not regex1.match(i)]
filtered_cmds = [i for i in filtered_cmds if not regex2.match(i)]
echo_int_cmd = [i for i in cmds if regex1.match(i)]
echo_rx_cmd = [i for i in cmds if regex2.match(i)]
filtered_cmds.extend(echo_int_cmd)
filtered_cmds.extend(echo_rx_cmd)
return filtered_cmds
def main():
argument_spec = dict(
echo_interface=dict(required=False, type="str"),
echo_rx_interval=dict(required=False, type="int"),
interval=dict(required=False, type="dict"),
slow_timer=dict(required=False, type="int"),
startup_timer=dict(required=False, type="int"),
ipv4_echo_rx_interval=dict(required=False, type="int"),
ipv4_interval=dict(required=False, type="dict"),
ipv4_slow_timer=dict(required=False, type="int"),
ipv6_echo_rx_interval=dict(required=False, type="int"),
ipv6_interval=dict(required=False, type="dict"),
ipv6_slow_timer=dict(required=False, type="int"),
fabricpath_interval=dict(required=False, type="dict"),
fabricpath_slow_timer=dict(required=False, type="int"),
fabricpath_vlan=dict(required=False, type="int"),
)
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
warnings = list()
cmd_ref = NxosCmdRef(module, BFD_CMD_REF)
cmd_ref.get_existing()
cmd_ref.get_playvals()
cmds = reorder_cmds(cmd_ref.get_proposed())
result = {
"changed": False,
"commands": cmds,
"warnings": warnings,
"check_mode": module.check_mode,
}
if cmds:
result["changed"] = True
if not module.check_mode:
load_config(module, cmds)
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,302 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright 2019 Cisco and/or its affiliates.
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The module file for nxos_bfd_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: nxos_bfd_interfaces
short_description: BFD interfaces resource module
description: Manages attributes of Bidirectional Forwarding Detection (BFD) on the
interface.
version_added: 1.0.0
author: Chris Van Heuveln (@chrisvanheuveln)
notes:
- Tested against NX-OS 7.0(3)I5(1).
- Unsupported for Cisco MDS
- Feature bfd should be enabled for this module.
options:
running_config:
description:
- This option is used only with state I(parsed).
- The value of this option should be the output received from the NX-OS device
by executing the command B(show running-config | section '^interface|^feature
bfd').
- The state I(parsed) reads the configuration from C(running_config) option and
transforms it into Ansible structured data as per the resource module's argspec
and the value is then returned in the I(parsed) key within the result.
type: str
config:
description: The provided configuration
type: list
elements: dict
suboptions:
name:
type: str
description: The name of the interface.
bfd:
type: str
description:
- Enable/Disable Bidirectional Forwarding Detection (BFD) on the interface.
choices:
- enable
- disable
echo:
type: str
description:
- Enable/Disable BFD Echo functionality on the interface.
choices:
- enable
- disable
state:
description:
- The state of the configuration after module completion
type: str
choices:
- merged
- replaced
- overridden
- deleted
- gathered
- rendered
- parsed
default: merged
"""
EXAMPLES = """
# Using deleted
- name: Configure interfaces
cisco.nxos.nxos_bfd_interfaces:
state: deleted
# Using merged
- name: Configure interfaces
cisco.nxos.nxos_bfd_interfaces:
config:
- name: Ethernet1/1
bfd: enable
echo: enable
- name: Ethernet1/2
bfd: disable
echo: disable
state: merged
# Using overridden
- name: Configure interfaces
cisco.nxos.nxos_bfd_interfaces:
config:
- name: Ethernet1/1
bfd: enable
echo: enable
- name: Ethernet1/2
bfd: disable
echo: disable
state: overridden
# Using replaced
- name: Configure interfaces
cisco.nxos.nxos_bfd_interfaces:
config:
- name: Ethernet1/1
bfd: enable
echo: enable
- name: Ethernet1/2
bfd: disable
echo: disable
state: replaced
# Using rendered
- name: Use rendered state to convert task input to device specific commands
cisco.nxos.nxos_bfd_interfaces:
config:
- name: Ethernet1/800
bfd: enable
echo: enable
- name: Ethernet1/801
bfd: disable
echo: disable
state: rendered
# Task Output (redacted)
# -----------------------
# rendered:
# - "interface Ethernet1/800"
# - "bfd"
# - "bfd echo"
# - "interface Ethernet1/801"
# - "no bfd"
# - "no bfd echo"
# Using parsed
# parsed.cfg
# ------------
# feature bfd
# interface Ethernet1/800
# no switchport
# no bfd
# no bfd echo
# interface Ethernet1/801
# no switchport
# no bfd
# interface Ethernet1/802
# no switchport
# no bfd echo
# interface mgmt0
# ip address dhcp
# vrf member management
- name: Use parsed state to convert externally supplied config to structured format
cisco.nxos.nxos_bfd_interfaces:
running_config: "{{ lookup('file', 'parsed.cfg') }}"
state: parsed
# Task output (redacted)
# -----------------------
# parsed:
# - bfd: disable
# echo: disable
# name: Ethernet1/800
# - bfd: disable
# echo: enable
# name: Ethernet1/801
# - bfd: enable
# echo: disable
# name: Ethernet1/802
# - bfd: enable
# echo: enable
# name: mgmt0
# Using gathered
# Existing device config state
# -------------------------------
# feature bfd
# interface Ethernet1/1
# no switchport
# no bfd
# interface Ethernet1/2
# no switchport
# no bfd echo
# interface mgmt0
# ip address dhcp
# vrf member management
- name: Gather bfd_interfaces facts from the device using nxos_bfd_interfaces
cisco.nxos.nxos_bfd_interfaces:
state: gathered
# Task output (redacted)
# -----------------------
# gathered:
# - name: Ethernet1/1
# bfd: disable
# echo: enable
# - name: Ethernet1/3
# echo: disable
# bfd: enable
# - name: mgmt0
# bfd: enable
# echo: enable
"""
RETURN = """
before:
description: The configuration as structured data prior to module invocation.
returned: always
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The configuration as structured data after module completion.
returned: when changed
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample: ['interface Ethernet1/1', 'no bfd', 'no bfd echo']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.argspec.bfd_interfaces.bfd_interfaces import (
Bfd_interfacesArgs,
)
from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.config.bfd_interfaces.bfd_interfaces import (
Bfd_interfaces,
)
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
required_if = [
("state", "merged", ("config",)),
("state", "replaced", ("config",)),
("state", "overridden", ("config",)),
("state", "rendered", ("config",)),
("state", "parsed", ("running_config",)),
]
mutually_exclusive = [("config", "running_config")]
module = AnsibleModule(
argument_spec=Bfd_interfacesArgs.argument_spec,
required_if=required_if,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True,
)
result = Bfd_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,761 @@
#!/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/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: nxos_bgp
extends_documentation_fragment:
- cisco.nxos.nxos
short_description: (deprecated, removed after 2023-01-27) Manages BGP configuration.
description:
- Manages BGP configurations on NX-OS switches.
version_added: 1.0.0
author:
- Jason Edelman (@jedelman8)
- Gabriele Gerbino (@GGabriele)
deprecated:
alternative: nxos_bgp_global
why: Updated module released with more functionality.
removed_at_date: '2023-01-27'
notes:
- Tested against NXOSv 7.3.(0)D1(1) on VIRL
- Unsupported for Cisco MDS
- C(state=absent) removes the whole BGP ASN configuration when C(vrf=default) or the
whole VRF instance within the BGP process when using a different VRF.
- Default when supported restores params default value.
- Configuring global params is only permitted if C(vrf=default).
options:
asn:
description:
- BGP autonomous system number. Valid values are String, Integer in ASPLAIN or
ASDOT notation.
required: true
type: str
vrf:
description:
- Name of the VRF. The name 'default' is a valid VRF representing the global BGP.
default: 'default'
type: str
bestpath_always_compare_med:
description:
- Enable/Disable MED comparison on paths from different autonomous systems.
type: bool
bestpath_aspath_multipath_relax:
description:
- Enable/Disable load sharing across the providers with different (but equal-length)
AS paths.
type: bool
bestpath_compare_routerid:
description:
- Enable/Disable comparison of router IDs for identical eBGP paths.
type: bool
bestpath_compare_neighborid:
description:
- Enable/Disable neighborid. Use this when more paths available than max path
config.
type: bool
bestpath_cost_community_ignore:
description:
- Enable/Disable Ignores the cost community for BGP best-path calculations.
type: bool
bestpath_med_confed:
description:
- Enable/Disable enforcement of bestpath to do a MED comparison only between paths
originated within a confederation.
type: bool
bestpath_med_missing_as_worst:
description:
- Enable/Disable assigns the value of infinity to received routes that do not
carry the MED attribute, making these routes the least desirable.
type: bool
bestpath_med_non_deterministic:
description:
- Enable/Disable deterministic selection of the best MED pat from among the paths
from the same autonomous system.
type: bool
cluster_id:
description:
- Route Reflector Cluster-ID.
type: str
confederation_id:
description:
- Routing domain confederation AS.
type: str
confederation_peers:
description:
- AS confederation parameters.
type: list
elements: str
disable_policy_batching:
description:
- Enable/Disable the batching evaluation of prefix advertisement to all peers.
type: bool
disable_policy_batching_ipv4_prefix_list:
description:
- Enable/Disable the batching evaluation of prefix advertisements to all peers
with prefix list.
type: str
disable_policy_batching_ipv6_prefix_list:
description:
- Enable/Disable the batching evaluation of prefix advertisements to all peers
with prefix list.
type: str
enforce_first_as:
description:
- Enable/Disable enforces the neighbor autonomous system to be the first AS number
listed in the AS path attribute for eBGP. On NX-OS, this property is only supported
in the global BGP context.
type: bool
event_history_cli:
description:
- Enable/Disable cli event history buffer.
choices:
- size_small
- size_medium
- size_large
- size_disable
- default
- 'true'
- 'false'
type: str
event_history_detail:
description:
- Enable/Disable detail event history buffer.
choices:
- size_small
- size_medium
- size_large
- size_disable
- default
- 'true'
- 'false'
type: str
event_history_events:
description:
- Enable/Disable event history buffer.
choices:
- size_small
- size_medium
- size_large
- size_disable
- default
- 'true'
- 'false'
type: str
event_history_periodic:
description:
- Enable/Disable periodic event history buffer.
choices:
- size_small
- size_medium
- size_large
- size_disable
- default
- 'true'
- 'false'
type: str
fast_external_fallover:
description:
- Enable/Disable immediately reset the session if the link to a directly connected
BGP peer goes down. Only supported in the global BGP context.
type: bool
flush_routes:
description:
- Enable/Disable flush routes in RIB upon controlled restart. On NX-OS, this property
is only supported in the global BGP context.
type: bool
graceful_restart:
description:
- Enable/Disable graceful restart.
type: bool
graceful_restart_helper:
description:
- Enable/Disable graceful restart helper mode.
type: bool
graceful_restart_timers_restart:
description:
- Set maximum time for a restart sent to the BGP peer.
type: str
graceful_restart_timers_stalepath_time:
description:
- Set maximum time that BGP keeps the stale routes from the restarting BGP peer.
type: str
isolate:
description:
- Enable/Disable isolate this router from BGP perspective.
type: bool
local_as:
description:
- Local AS number to be used within a VRF instance.
type: str
log_neighbor_changes:
description:
- Enable/Disable message logging for neighbor up/down event.
type: bool
maxas_limit:
description:
- Specify Maximum number of AS numbers allowed in the AS-path attribute. Valid
values are between 1 and 512.
type: str
neighbor_down_fib_accelerate:
description:
- Enable/Disable handle BGP neighbor down event, due to various reasons.
type: bool
reconnect_interval:
description:
- The BGP reconnection interval for dropped sessions. Valid values are between
1 and 60.
type: str
router_id:
description:
- Router Identifier (ID) of the BGP router VRF instance.
type: str
shutdown:
description:
- Administratively shutdown the BGP protocol.
type: bool
suppress_fib_pending:
description:
- Enable/Disable advertise only routes programmed in hardware to peers.
type: bool
timer_bestpath_limit:
description:
- Specify timeout for the first best path after a restart, in seconds.
type: str
timer_bgp_hold:
description:
- Set BGP hold timer.
type: str
timer_bgp_keepalive:
description:
- Set BGP keepalive timer.
type: str
state:
description:
- Determines whether the config should be present or not on the device.
default: present
choices:
- present
- absent
type: str
"""
EXAMPLES = """
- name: Configure a simple ASN
cisco.nxos.nxos_bgp:
asn: 65535
vrf: test
router_id: 192.0.2.1
state: present
"""
RETURN = """
commands:
description: commands sent to the device
returned: always
type: list
sample: ["router bgp 65535", "vrf test", "router-id 192.0.2.1"]
"""
import re
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
CustomNetworkConfig,
)
from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
get_config,
load_config,
)
BOOL_PARAMS = [
"bestpath_always_compare_med",
"bestpath_aspath_multipath_relax",
"bestpath_compare_neighborid",
"bestpath_compare_routerid",
"bestpath_cost_community_ignore",
"bestpath_med_confed",
"bestpath_med_missing_as_worst",
"bestpath_med_non_deterministic",
"disable_policy_batching",
"enforce_first_as",
"fast_external_fallover",
"flush_routes",
"graceful_restart",
"graceful_restart_helper",
"isolate",
"log_neighbor_changes",
"neighbor_down_fib_accelerate",
"shutdown",
"suppress_fib_pending",
]
GLOBAL_PARAMS = [
"disable_policy_batching",
"disable_policy_batching_ipv4_prefix_list",
"disable_policy_batching_ipv6_prefix_list",
"enforce_first_as",
"event_history_cli",
"event_history_detail",
"event_history_events",
"event_history_periodic",
"fast_external_fallover",
"flush_routes",
"isolate",
"suppress_fib_pending",
"shutdown",
]
PARAM_TO_DEFAULT_KEYMAP = {
"timer_bgp_keepalive": "60",
"timer_bgp_hold": "180",
"timer_bestpath_limit": "300",
"graceful_restart": True,
"graceful_restart_timers_restart": "120",
"graceful_restart_timers_stalepath_time": "300",
"reconnect_interval": "60",
"suppress_fib_pending": True,
"fast_external_fallover": True,
"enforce_first_as": True,
"event_history_cli": True,
"event_history_detail": False,
"event_history_events": True,
"event_history_periodic": True,
"maxas_limit": "",
"router_id": "",
"cluster_id": "",
"disable_policy_batching_ipv4_prefix_list": "",
"disable_policy_batching_ipv6_prefix_list": "",
"local_as": "",
"confederation_id": "",
}
PARAM_TO_COMMAND_KEYMAP = {
"asn": "router bgp",
"bestpath_always_compare_med": "bestpath always-compare-med",
"bestpath_aspath_multipath_relax": "bestpath as-path multipath-relax",
"bestpath_compare_neighborid": "bestpath compare-neighborid",
"bestpath_compare_routerid": "bestpath compare-routerid",
"bestpath_cost_community_ignore": "bestpath cost-community ignore",
"bestpath_med_confed": "bestpath med confed",
"bestpath_med_missing_as_worst": "bestpath med missing-as-worst",
"bestpath_med_non_deterministic": "bestpath med non-deterministic",
"cluster_id": "cluster-id",
"confederation_id": "confederation identifier",
"confederation_peers": "confederation peers",
"disable_policy_batching": "disable-policy-batching",
"disable_policy_batching_ipv4_prefix_list": "disable-policy-batching ipv4 prefix-list",
"disable_policy_batching_ipv6_prefix_list": "disable-policy-batching ipv6 prefix-list",
"enforce_first_as": "enforce-first-as",
"event_history_cli": "event-history cli",
"event_history_detail": "event-history detail",
"event_history_events": "event-history events",
"event_history_periodic": "event-history periodic",
"fast_external_fallover": "fast-external-fallover",
"flush_routes": "flush-routes",
"graceful_restart": "graceful-restart",
"graceful_restart_helper": "graceful-restart-helper",
"graceful_restart_timers_restart": "graceful-restart restart-time",
"graceful_restart_timers_stalepath_time": "graceful-restart stalepath-time",
"isolate": "isolate",
"local_as": "local-as",
"log_neighbor_changes": "log-neighbor-changes",
"maxas_limit": "maxas-limit",
"neighbor_down_fib_accelerate": "neighbor-down fib-accelerate",
"reconnect_interval": "reconnect-interval",
"router_id": "router-id",
"shutdown": "shutdown",
"suppress_fib_pending": "suppress-fib-pending",
"timer_bestpath_limit": "timers bestpath-limit",
"timer_bgp_hold": "timers bgp",
"timer_bgp_keepalive": "timers bgp",
"vrf": "vrf",
}
def get_value(arg, config):
command = PARAM_TO_COMMAND_KEYMAP.get(arg)
if command.split()[0] == "event-history":
has_size = re.search(r"^\s+{0} size\s(?P<value>.*)$".format(command), config, re.M)
if command == "event-history detail":
value = False
else:
value = "size_small"
if has_size:
value = "size_%s" % has_size.group("value")
elif arg in ["enforce_first_as", "fast_external_fallover"]:
no_command_re = re.compile(r"no\s+{0}\s*".format(command), re.M)
value = True
if no_command_re.search(config):
value = False
elif arg in BOOL_PARAMS:
has_command = re.search(r"^\s+{0}\s*$".format(command), config, re.M)
value = False
if has_command:
value = True
else:
command_val_re = re.compile(r"(?:{0}\s)(?P<value>.*)".format(command), re.M)
value = ""
has_command = command_val_re.search(config)
if has_command:
found_value = has_command.group("value")
if arg == "confederation_peers":
value = found_value.split()
elif arg == "timer_bgp_keepalive":
value = found_value.split()[0]
elif arg == "timer_bgp_hold":
split_values = found_value.split()
if len(split_values) == 2:
value = split_values[1]
elif found_value:
value = found_value
return value
def get_existing(module, args, warnings):
existing = {}
netcfg = CustomNetworkConfig(indent=2, contents=get_config(module, flags=["bgp all"]))
asn_re = re.compile(r".*router\sbgp\s(?P<existing_asn>\d+(\.\d+)?).*", re.S)
asn_match = asn_re.match(str(netcfg))
if asn_match:
existing_asn = asn_match.group("existing_asn")
bgp_parent = "router bgp {0}".format(existing_asn)
if module.params["vrf"] != "default":
parents = [bgp_parent, "vrf {0}".format(module.params["vrf"])]
else:
parents = [bgp_parent]
config = netcfg.get_section(parents)
if config:
for arg in args:
if arg != "asn" and (module.params["vrf"] == "default" or arg not in GLOBAL_PARAMS):
existing[arg] = get_value(arg, config)
existing["asn"] = existing_asn
if module.params["vrf"] == "default":
existing["vrf"] = "default"
if not existing and module.params["vrf"] != "default" and module.params["state"] == "present":
msg = "VRF {0} doesn't exist.".format(module.params["vrf"])
warnings.append(msg)
return existing
def apply_key_map(key_map, table):
new_dict = {}
for key in table:
new_key = key_map.get(key)
if new_key:
new_dict[new_key] = table.get(key)
return new_dict
def state_present(module, existing, proposed, candidate):
commands = list()
proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed)
existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing)
for key, value in proposed_commands.items():
if value is True:
commands.append(key)
elif value is False:
commands.append("no {0}".format(key))
elif value == "default":
default_value = PARAM_TO_DEFAULT_KEYMAP.get(key)
existing_value = existing_commands.get(key)
if default_value:
commands.append("{0} {1}".format(key, default_value))
elif existing_value:
if key == "confederation peers":
existing_value = " ".join(existing_value)
commands.append("no {0} {1}".format(key, existing_value))
elif not value:
existing_value = existing_commands.get(key)
if existing_value:
commands.append("no {0} {1}".format(key, existing_value))
elif key == "confederation peers":
commands.append("{0} {1}".format(key, value))
elif key.startswith("timers bgp"):
command = "timers bgp {0} {1}".format(
proposed["timer_bgp_keepalive"],
proposed["timer_bgp_hold"],
)
if command not in commands:
commands.append(command)
else:
if value.startswith("size"):
value = value.replace("_", " ")
command = "{0} {1}".format(key, value)
commands.append(command)
parents = []
if commands:
commands = fix_commands(commands)
parents = ["router bgp {0}".format(module.params["asn"])]
if module.params["vrf"] != "default":
parents.append("vrf {0}".format(module.params["vrf"]))
elif proposed:
if module.params["vrf"] != "default":
commands.append("vrf {0}".format(module.params["vrf"]))
parents = ["router bgp {0}".format(module.params["asn"])]
else:
commands.append("router bgp {0}".format(module.params["asn"]))
candidate.add(commands, parents=parents)
def state_absent(module, existing, candidate):
commands = []
parents = []
if module.params["vrf"] == "default":
commands.append("no router bgp {0}".format(module.params["asn"]))
elif existing.get("vrf") == module.params["vrf"]:
commands.append("no vrf {0}".format(module.params["vrf"]))
parents = ["router bgp {0}".format(module.params["asn"])]
candidate.add(commands, parents=parents)
def fix_commands(commands):
local_as_command = ""
confederation_id_command = ""
confederation_peers_command = ""
for command in commands:
if "local-as" in command:
local_as_command = command
elif "confederation identifier" in command:
confederation_id_command = command
elif "confederation peers" in command:
confederation_peers_command = command
if local_as_command and confederation_id_command:
if "no" in confederation_id_command:
commands.pop(commands.index(local_as_command))
commands.pop(commands.index(confederation_id_command))
commands.append(confederation_id_command)
commands.append(local_as_command)
else:
commands.pop(commands.index(local_as_command))
commands.pop(commands.index(confederation_id_command))
commands.append(local_as_command)
commands.append(confederation_id_command)
if confederation_peers_command and confederation_id_command:
if local_as_command:
if "no" in local_as_command:
commands.pop(commands.index(local_as_command))
commands.pop(commands.index(confederation_id_command))
commands.pop(commands.index(confederation_peers_command))
commands.append(confederation_id_command)
commands.append(confederation_peers_command)
commands.append(local_as_command)
else:
commands.pop(commands.index(local_as_command))
commands.pop(commands.index(confederation_id_command))
commands.pop(commands.index(confederation_peers_command))
commands.append(local_as_command)
commands.append(confederation_id_command)
commands.append(confederation_peers_command)
else:
commands.pop(commands.index(confederation_peers_command))
commands.pop(commands.index(confederation_id_command))
commands.append(confederation_id_command)
commands.append(confederation_peers_command)
return commands
def main():
argument_spec = dict(
asn=dict(required=True, type="str"),
vrf=dict(required=False, type="str", default="default"),
bestpath_always_compare_med=dict(required=False, type="bool"),
bestpath_aspath_multipath_relax=dict(required=False, type="bool"),
bestpath_compare_neighborid=dict(required=False, type="bool"),
bestpath_compare_routerid=dict(required=False, type="bool"),
bestpath_cost_community_ignore=dict(required=False, type="bool"),
bestpath_med_confed=dict(required=False, type="bool"),
bestpath_med_missing_as_worst=dict(required=False, type="bool"),
bestpath_med_non_deterministic=dict(required=False, type="bool"),
cluster_id=dict(required=False, type="str"),
confederation_id=dict(required=False, type="str"),
confederation_peers=dict(required=False, type="list", elements="str"),
disable_policy_batching=dict(required=False, type="bool"),
disable_policy_batching_ipv4_prefix_list=dict(required=False, type="str"),
disable_policy_batching_ipv6_prefix_list=dict(required=False, type="str"),
enforce_first_as=dict(required=False, type="bool"),
event_history_cli=dict(
required=False,
choices=[
"true",
"false",
"default",
"size_small",
"size_medium",
"size_large",
"size_disable",
],
),
event_history_detail=dict(
required=False,
choices=[
"true",
"false",
"default",
"size_small",
"size_medium",
"size_large",
"size_disable",
],
),
event_history_events=dict(
required=False,
choices=[
"true",
"false",
"default",
"size_small",
"size_medium",
"size_large",
"size_disable",
],
),
event_history_periodic=dict(
required=False,
choices=[
"true",
"false",
"default",
"size_small",
"size_medium",
"size_large",
"size_disable",
],
),
fast_external_fallover=dict(required=False, type="bool"),
flush_routes=dict(required=False, type="bool"),
graceful_restart=dict(required=False, type="bool"),
graceful_restart_helper=dict(required=False, type="bool"),
graceful_restart_timers_restart=dict(required=False, type="str"),
graceful_restart_timers_stalepath_time=dict(required=False, type="str"),
isolate=dict(required=False, type="bool"),
local_as=dict(required=False, type="str"),
log_neighbor_changes=dict(required=False, type="bool"),
maxas_limit=dict(required=False, type="str"),
neighbor_down_fib_accelerate=dict(required=False, type="bool"),
reconnect_interval=dict(required=False, type="str"),
router_id=dict(required=False, type="str"),
shutdown=dict(required=False, type="bool"),
suppress_fib_pending=dict(required=False, type="bool"),
timer_bestpath_limit=dict(required=False, type="str"),
timer_bgp_hold=dict(required=False, type="str"),
timer_bgp_keepalive=dict(required=False, type="str"),
state=dict(choices=["present", "absent"], default="present", required=False),
)
module = AnsibleModule(
argument_spec=argument_spec,
required_together=[["timer_bgp_hold", "timer_bgp_keepalive"]],
supports_check_mode=True,
)
warnings = list()
result = dict(changed=False, warnings=warnings)
state = module.params["state"]
if module.params["vrf"] != "default":
for param in GLOBAL_PARAMS:
if module.params[param]:
module.fail_json(
msg='Global params can be modified only under "default" VRF.',
vrf=module.params["vrf"],
global_param=param,
)
args = PARAM_TO_COMMAND_KEYMAP.keys()
existing = get_existing(module, args, warnings)
if existing.get("asn") and state == "present":
if existing.get("asn") != module.params["asn"]:
module.fail_json(
msg="Another BGP ASN already exists.",
proposed_asn=module.params["asn"],
existing_asn=existing.get("asn"),
)
proposed_args = dict((k, v) for k, v in module.params.items() if v is not None and k in args)
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, "default")
if key == "confederation_peers":
if value[0] == "default":
if existing.get(key):
proposed[key] = "default"
else:
v = set([int(i) for i in value])
ex = set([int(i) for i in existing.get(key)])
if v != ex:
proposed[key] = " ".join(str(s) for s in v)
else:
if existing.get(key) != value:
proposed[key] = value
candidate = CustomNetworkConfig(indent=3)
if state == "present":
state_present(module, existing, proposed, candidate)
elif existing.get("asn") == module.params["asn"]:
state_absent(module, existing, candidate)
if candidate:
candidate = candidate.items_text()
if not module.check_mode:
load_config(module, candidate)
result["changed"] = True
result["commands"] = candidate
else:
result["commands"] = []
module.exit_json(**result)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,877 @@
#!/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/>.
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: nxos_bgp_af
extends_documentation_fragment:
- cisco.nxos.nxos
short_description: (deprecated, removed after 2023-02-24) Manages BGP Address-family configuration.
description:
- Manages BGP Address-family configurations on NX-OS switches.
version_added: 1.0.0
author: Gabriele Gerbino (@GGabriele)
deprecated:
alternative: nxos_bgp_address_family
why: Updated module released with more functionality.
removed_at_date: '2023-02-24'
notes:
- Tested against NXOSv 7.3.(0)D1(1) on VIRL
- Unsupported for Cisco MDS
- 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
type: str
vrf:
description:
- Name of the VRF. The name 'default' is a valid VRF representing the global bgp.
default: 'default'
type: str
afi:
description:
- Address Family Identifier.
required: true
choices:
- ipv4
- ipv6
- vpnv4
- vpnv6
- l2vpn
type: str
safi:
description:
- Sub Address Family Identifier.
required: true
choices:
- unicast
- multicast
- evpn
type: str
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.
type: bool
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.
type: bool
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.
type: str
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.
type: bool
advertise_l2vpn_evpn:
description:
- Advertise evpn routes.
type: bool
client_to_client:
description:
- Configure client-to-client route reflection.
type: bool
dampen_igp_metric:
description:
- Specify dampen value for IGP metric-related changes, in seconds. Valid values
are integer and keyword 'default'.
type: str
dampening_state:
description:
- Enable/disable route-flap dampening.
type: bool
dampening_half_time:
description:
- Specify decay half-life in minutes for route-flap dampening. Valid values are
integer and keyword 'default'.
type: str
dampening_max_suppress_time:
description:
- Specify max suppress time for route-flap dampening stable route. Valid values
are integer and keyword 'default'.
type: str
dampening_reuse_time:
description:
- Specify route reuse time for route-flap dampening. Valid values are integer
and keyword 'default'.
type: str
dampening_routemap:
description:
- Specify route-map for route-flap dampening. Valid values are a string defining
the name of the route-map.
type: str
dampening_suppress_time:
description:
- Specify route suppress time for route-flap dampening. Valid values are integer
and keyword 'default'.
type: str
default_information_originate:
description:
- Default information originate.
type: bool
default_metric:
description:
- Sets default metrics for routes redistributed into BGP. Valid values are Integer
or keyword 'default'
type: str
distance_ebgp:
description:
- Sets the administrative distance for eBGP routes. Valid values are Integer or
keyword 'default'.
type: str
distance_ibgp:
description:
- Sets the administrative distance for iBGP routes. Valid values are Integer or
keyword 'default'.
type: str
distance_local:
description:
- Sets the administrative distance for local BGP routes. Valid values are Integer
or keyword 'default'.
type: str
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']].
type: list
elements: list
maximum_paths:
description:
- Configures the maximum number of equal-cost paths for load sharing. Valid value
is an integer in the range 1-64.
type: str
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.
type: str
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']].
type: list
elements: list
next_hop_route_map:
description:
- Configure a route-map for valid nexthops. Valid values are a string defining
the name of the route-map.
type: str
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']].
type: list
elements: list
suppress_inactive:
description:
- Advertises only active routes to peers.
type: bool
table_map:
description:
- Apply table-map to filter routes downloaded into URIB. Valid values are a string.
type: str
table_map_filter:
description:
- Filters routes rejected by the route-map and does not download them to the RIB.
type: bool
state:
description:
- Determines whether the config should be present or not on the device.
default: present
choices:
- present
- absent
type: str
retain_route_target:
description:
- Retains all of the routes or the routes which are part of configured route-map.
Valid values are route-map names or keyword C(all) or keyword C(default). C(all)
retains all the routes regardless of Target-VPN community. C(default) will disable
the retain route target option. If you are using route-map name please ensure
that the name is not same as C(all) and C(default).
type: str
version_added: 1.1.0
"""
EXAMPLES = """
# configure a simple address-family
- cisco.nxos.nxos_bgp_af:
asn: 65535
vrf: TESTING
afi: ipv4
safi: unicast
advertise_l2vpn_evpn: true
state: present
retain_route_target: all
"""
RETURN = """
commands:
description: commands sent to the device
returned: always
type: list
sample: ["router bgp 65535", "vrf TESTING",
"address-family ipv4 unicast", "advertise l2vpn evpn",
"retain route-target all"]
"""
import re
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
CustomNetworkConfig,
)
from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import (
get_config,
load_config,
)
BOOL_PARAMS = [
"additional_paths_install",
"additional_paths_receive",
"additional_paths_send",
"advertise_l2vpn_evpn",
"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",
"retain_route_target": "retain route-target",
}
DAMPENING_PARAMS = [
"dampening_half_time",
"dampening_suppress_time",
"dampening_reuse_time",
"dampening_max_suppress_time",
]
def get_value(arg, config, module):
command = PARAM_TO_COMMAND_KEYMAP[arg]
command_val_re = re.compile(r"(?:{0}\s)(?P<value>.*)$".format(command), re.M)
has_command_val = command_val_re.search(config)
if arg in ["networks", "redistribute", "inject_map"]:
value = []
for ele in command_val_re.findall(config):
tl = ele.split()
if "exist-map" in tl:
tl.remove("exist-map")
elif "route-map" in tl:
tl.remove("route-map")
value.append(tl)
elif command == "distance":
distance_re = r".*distance\s(?P<d_ebgp>\w+)\s(?P<d_ibgp>\w+)\s(?P<d_local>\w+)"
match_distance = re.match(distance_re, config, re.DOTALL)
value = ""
if match_distance:
distance_group = match_distance.groupdict()
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 command.split()[0] == "dampening":
value = ""
if arg == "dampen_igp_metric" or arg == "dampening_routemap":
if command in config:
value = has_command_val.group("value")
else:
dampening_re = r".*dampening\s(?P<half>\w+)\s(?P<reuse>\w+)\s(?P<suppress>\w+)\s(?P<max_suppress>\w+)"
match_dampening = re.match(dampening_re, config, re.DOTALL)
if match_dampening:
dampening_group = match_dampening.groupdict()
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"]
else:
if arg == "dampening_state":
value = True if "dampening" in config else False
elif arg == "table_map_filter":
tmf_regex = re.compile(r"\s+table-map.*filter$", re.M)
value = False
if tmf_regex.search(config):
value = True
elif arg == "table_map":
tm_regex = re.compile(r"(?:table-map\s)(?P<value>\S+)(\sfilter)?$", re.M)
has_tablemap = tm_regex.search(config)
value = ""
if has_tablemap:
value = has_tablemap.group("value")
elif arg == "client_to_client":
no_command_re = re.compile(r"^\s+no\s{0}\s*$".format(command), re.M)
value = True
if no_command_re.search(config):
value = False
elif arg == "retain_route_target":
value = ""
if command in config:
route_target = has_command_val.group("value")
if route_target:
if route_target == "all":
value = "all"
elif "route-map" in route_target:
value = route_target.replace("route-map ", "")
elif arg in BOOL_PARAMS:
command_re = re.compile(r"^\s+{0}\s*$".format(command), re.M)
value = False
if command_re.search(config):
value = True
else:
value = ""
if has_command_val:
value = has_command_val.group("value")
return value
def get_existing(module, args, warnings):
existing = {}
netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
asn_regex = re.compile(r".*router\sbgp\s(?P<existing_asn>\d+(\.\d+)?).*", re.DOTALL)
match_asn = asn_regex.match(str(netcfg))
if match_asn:
existing_asn = match_asn.group("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"]:
gv = get_value(arg, config, module)
if gv:
existing[arg] = gv
else:
if arg != "client_to_client" and arg in PARAM_TO_DEFAULT_KEYMAP.keys():
existing[arg] = PARAM_TO_DEFAULT_KEYMAP.get(arg)
else:
existing[arg] = gv
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:
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]),
)
elif key == "redistribute":
for maps in existing_value:
commands.append("no redistribute {0} route-map {1}".format(maps[0], maps[1]))
elif key == "retain route-target":
if existing_value == "all":
commands.append("no {0} {1}".format(key, existing_value))
elif existing_value != "default":
commands.append("no {0} route-map {1}".format(key, existing_value))
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])
if command:
commands.append(command)
for enet in existing_networks:
if enet not in value:
if len(enet) == 1:
command = "no {0} {1}".format(key, enet[0])
elif len(enet) == 2:
command = "no {0} {1} route-map {2}".format(key, enet[0], enet[1])
if command:
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])
if command:
commands.append(command)
for emaps in existing_maps:
if emaps not in value:
if len(emaps) == 2:
command = "no inject-map {0} exist-map {1}".format(emaps[0], emaps[1])
elif len(emaps) == 3:
command = "no inject-map {0} exist-map {1} " "copy-attributes".format(
emaps[0],
emaps[1],
)
if command:
commands.append(command)
return commands
def get_redistribute_command(existing, key, value):
commands = []
existing_rules = existing.get("redistribute", [])
for rule in value:
if not isinstance(rule, list):
rule = [rule]
if rule not in existing_rules:
command = "redistribute {0} route-map {1}".format(rule[0], rule[1])
commands.append(command)
for erule in existing_rules:
if erule not in value:
command = "no redistribute {0} route-map {1}".format(erule[0], erule[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_retain_route_target_command(existing, key, value):
commands = []
if key == "retain route-target":
if value != "default":
if value == "all":
command = "{0} {1}".format(key, value)
else:
command = "{0} route-map {1}".format(key, value)
else:
existing_value = existing.get("retain_route_target")
if existing_value == "all":
command = "no {0} {1}".format(key, existing_value)
else:
command = "no {0} route-map {1}".format(key, existing_value)
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)
elif key == "retain route-target":
retain_route_target_commands = get_retain_route_target_command(existing, key, value)
if retain_route_target_commands:
commands.extend(retain_route_target_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"]))
addr_family_command = "address-family {0} {1}".format(
module.params["afi"],
module.params["safi"],
)
parents.append(addr_family_command)
if addr_family_command in commands:
commands.remove(addr_family_command)
candidate.add(commands, parents=parents)
def state_absent(module, 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", elements="list"),
maximum_paths=dict(required=False, type="str"),
maximum_paths_ibgp=dict(required=False, type="str"),
networks=dict(required=False, type="list", elements="list"),
next_hop_route_map=dict(required=False, type="str"),
redistribute=dict(required=False, type="list", elements="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),
retain_route_target=dict(required=False, type="str"),
)
mutually_exclusive = [
("dampening_state", "dampening_routemap"),
("dampening_state", "dampening_half_time"),
("dampening_state", "dampening_suppress_time"),
("dampening_state", "dampening_reuse_time"),
("dampening_state", "dampening_max_suppress_time"),
("dampening_routemap", "dampening_half_time"),
("dampening_routemap", "dampening_suppress_time"),
("dampening_routemap", "dampening_reuse_time"),
("dampening_routemap", "dampening_max_suppress_time"),
]
module = AnsibleModule(
argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive,
required_together=[
DAMPENING_PARAMS,
["distance_ibgp", "distance_ebgp", "distance_local"],
],
supports_check_mode=True,
)
warnings = list()
result = dict(changed=False, warnings=warnings)
state = module.params["state"]
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 = PARAM_TO_COMMAND_KEYMAP.keys()
existing = get_existing(module, args, warnings)
if existing.get("asn") and state == "present":
if existing.get("asn") != module.params["asn"]:
module.fail_json(
msg="Another BGP ASN already exists.",
proposed_asn=module.params["asn"],
existing_asn=existing.get("asn"),
)
proposed_args = dict((k, v) for k, v in module.params.items() if v is not None and k in args)
for arg in ["networks", "inject_map", "redistribute"]:
if proposed_args.get(arg):
if proposed_args[arg][0] == "default":
proposed_args[arg] = "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, "default")
if existing.get(key) != value:
proposed[key] = value
candidate = CustomNetworkConfig(indent=3)
if state == "present":
state_present(module, existing, proposed, candidate)
elif state == "absent" and existing:
state_absent(module, candidate)
if candidate:
candidate = candidate.items_text()
if not module.check_mode:
load_config(module, candidate)
result["changed"] = True
result["commands"] = candidate
else:
result["commands"] = []
module.exit_json(**result)
if __name__ == "__main__":
main()

Some files were not shown because too many files have changed in this diff Show More